iPXE
Functions | Variables
rndis.c File Reference

Remote Network Driver Interface Specification. More...

#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <byteswap.h>
#include <ipxe/iobuf.h>
#include <ipxe/netdevice.h>
#include <ipxe/ethernet.h>
#include <ipxe/device.h>
#include <ipxe/rndis.h>

Go to the source code of this file.

Functions

 FILE_LICENCE (GPL2_OR_LATER_OR_UBDL)
static struct io_bufferrndis_alloc_iob (size_t len)
 Allocate I/O buffer.
static int rndis_wait (struct rndis_device *rndis, unsigned int wait_id)
 Wait for completion.
static int rndis_tx_message (struct rndis_device *rndis, struct io_buffer *iobuf, unsigned int type)
 Transmit message.
void rndis_tx_complete_err (struct rndis_device *rndis, struct io_buffer *iobuf, int rc)
 Complete message transmission.
static int rndis_tx_data (struct rndis_device *rndis, struct io_buffer *iobuf)
 Transmit data packet.
int rndis_tx_defer (struct rndis_device *rndis, struct io_buffer *iobuf)
 Defer transmitted packet.
static void rndis_rx_data (struct rndis_device *rndis, struct io_buffer *iobuf)
 Receive data packet.
static int rndis_tx_initialise (struct rndis_device *rndis, unsigned int id)
 Transmit initialisation message.
static void rndis_rx_initialise (struct rndis_device *rndis, struct io_buffer *iobuf)
 Receive initialisation completion.
static int rndis_initialise (struct rndis_device *rndis)
 Initialise RNDIS.
static int rndis_tx_halt (struct rndis_device *rndis)
 Transmit halt message.
static int rndis_halt (struct rndis_device *rndis)
 Halt RNDIS.
static int rndis_tx_oid (struct rndis_device *rndis, unsigned int oid, const void *data, size_t len)
 Transmit OID message.
static void rndis_rx_query_oid (struct rndis_device *rndis, struct io_buffer *iobuf)
 Receive query OID completion.
static void rndis_rx_set_oid (struct rndis_device *rndis, struct io_buffer *iobuf)
 Receive set OID completion.
static int rndis_oid (struct rndis_device *rndis, unsigned int oid, const void *data, size_t len)
 Query or set OID.
static int rndis_describe (struct rndis_device *rndis)
 Describe RNDIS device.
static void rndis_rx_status (struct rndis_device *rndis, struct io_buffer *iobuf)
 Receive indicate status message.
static void rndis_rx_message (struct rndis_device *rndis, struct io_buffer *iobuf, unsigned int type)
 Receive RNDIS message.
void rndis_rx (struct rndis_device *rndis, struct io_buffer *iobuf)
 Receive packet from underlying transport layer.
void rndis_rx_err (struct rndis_device *rndis, struct io_buffer *iobuf, int rc)
 Discard packet from underlying transport layer.
static int rndis_filter (struct rndis_device *rndis, unsigned int filter)
 Set receive filter.
static int rndis_open (struct net_device *netdev)
 Open network device.
static void rndis_close (struct net_device *netdev)
 Close network device.
static int rndis_transmit (struct net_device *netdev, struct io_buffer *iobuf)
 Transmit packet.
static void rndis_poll (struct net_device *netdev)
 Poll for completed and received packets.
struct rndis_devicealloc_rndis (size_t priv_len)
 Allocate RNDIS device.
int register_rndis (struct rndis_device *rndis)
 Register RNDIS device.
void unregister_rndis (struct rndis_device *rndis)
 Unregister RNDIS device.
void free_rndis (struct rndis_device *rndis)
 Free RNDIS device.

Variables

static struct net_device_operations rndis_operations
 Network device operations.

Detailed Description

Remote Network Driver Interface Specification.

Definition in file rndis.c.


Function Documentation

FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL  )
static struct io_buffer* rndis_alloc_iob ( size_t  len) [static, read]

Allocate I/O buffer.

Parameters:
lenLength
Return values:
iobufI/O buffer, or NULL

Definition at line 48 of file rndis.c.

References alloc_iob(), header, and iob_reserve.

Referenced by rndis_tx_halt(), rndis_tx_initialise(), and rndis_tx_oid().

                                                         {
        struct rndis_header *header;
        struct io_buffer *iobuf;

        /* Allocate I/O buffer and reserve space */
        iobuf = alloc_iob ( sizeof ( *header ) + len );
        if ( iobuf )
                iob_reserve ( iobuf, sizeof ( *header ) );

        return iobuf;
}
static int rndis_wait ( struct rndis_device rndis,
unsigned int  wait_id 
) [static]

Wait for completion.

Parameters:
rndisRNDIS device
wait_idRequest ID
Return values:
rcReturn status code

Definition at line 67 of file rndis.c.

References DBGC, ETIMEDOUT, mdelay(), rndis_device::name, rndis_device::op, rndis_operations::poll, RNDIS_MAX_WAIT_MS, rndis_device::wait_id, and rndis_device::wait_rc.

Referenced by rndis_initialise(), and rndis_oid().

                                                                           {
        unsigned int i;

        /* Record query ID */
        rndis->wait_id = wait_id;

        /* Wait for operation to complete */
        for ( i = 0 ; i < RNDIS_MAX_WAIT_MS ; i++ ) {

                /* Check for completion */
                if ( ! rndis->wait_id )
                        return rndis->wait_rc;

                /* Poll RNDIS device */
                rndis->op->poll ( rndis );

                /* Delay for 1ms */
                mdelay ( 1 );
        }

        DBGC ( rndis, "RNDIS %s timed out waiting for ID %#08x\n",
               rndis->name, wait_id );
        return -ETIMEDOUT;
}
static int rndis_tx_message ( struct rndis_device rndis,
struct io_buffer iobuf,
unsigned int  type 
) [static]

Transmit message.

Parameters:
rndisRNDIS device
iobufI/O buffer
typeMessage type
Return values:
rcReturn status code

Definition at line 100 of file rndis.c.

References cpu_to_le32, DBGC, header, iob_len(), iob_push, rndis_header::len, rndis_device::name, rndis_device::op, rc, strerror(), rndis_operations::transmit, and rndis_header::type.

Referenced by rndis_tx_data(), rndis_tx_halt(), rndis_tx_initialise(), and rndis_tx_oid().

                                                                           {
        struct rndis_header *header;
        int rc;

        /* Prepend RNDIS header */
        header = iob_push ( iobuf, sizeof ( *header ) );
        header->type = cpu_to_le32 ( type );
        header->len = cpu_to_le32 ( iob_len ( iobuf ) );

        /* Transmit message */
        if ( ( rc = rndis->op->transmit ( rndis, iobuf ) ) != 0 ) {
                DBGC ( rndis, "RNDIS %s could not transmit: %s\n",
                       rndis->name, strerror ( rc ) );
                return rc;
        }

        return 0;
}
void rndis_tx_complete_err ( struct rndis_device rndis,
struct io_buffer iobuf,
int  rc 
)

Complete message transmission.

Parameters:
rndisRNDIS device
iobufI/O buffer
rcPacket status code

Definition at line 127 of file rndis.c.

References cpu_to_le32, io_buffer::data, DBGC, DBGC_HDA, EINVAL, free_iob(), header, iob_len(), len, rndis_device::name, netdev, rndis_device::netdev, netdev_tx_complete_err(), netdev_tx_err(), NULL, RNDIS_PACKET_MSG, and rndis_header::type.

Referenced by acm_out_complete(), netvsc_cancel_transmit(), and rndis_tx_complete().

                                                               {
        struct net_device *netdev = rndis->netdev;
        struct rndis_header *header;
        size_t len = iob_len ( iobuf );

        /* Sanity check */
        if ( len < sizeof ( *header ) ) {
                DBGC ( rndis, "RNDIS %s completed underlength transmission:\n",
                       rndis->name );
                DBGC_HDA ( rndis, 0, iobuf->data, len );
                netdev_tx_err ( netdev, NULL, -EINVAL );
                return;
        }
        header = iobuf->data;

        /* Complete buffer */
        if ( header->type == cpu_to_le32 ( RNDIS_PACKET_MSG ) ) {
                netdev_tx_complete_err ( netdev, iobuf, rc );
        } else {
                free_iob ( iobuf );
        }
}
static int rndis_tx_data ( struct rndis_device rndis,
struct io_buffer iobuf 
) [static]

Transmit data packet.

Parameters:
rndisRNDIS device
iobufI/O buffer
Return values:
rcReturn status code

Definition at line 158 of file rndis.c.

References cpu_to_le32, rndis_packet_message::data, iob_len(), iob_push, rndis_packet_field::len, len, memset(), msg(), rndis_packet_field::offset, rc, RNDIS_PACKET_MSG, and rndis_tx_message().

Referenced by rndis_transmit().

                                                     {
        struct rndis_packet_message *msg;
        size_t len = iob_len ( iobuf );
        int rc;

        /* Prepend packet message header */
        msg = iob_push ( iobuf, sizeof ( *msg ) );
        memset ( msg, 0, sizeof ( *msg ) );
        msg->data.offset = cpu_to_le32 ( sizeof ( *msg ) );
        msg->data.len = cpu_to_le32 ( len );

        /* Transmit message */
        if ( ( rc = rndis_tx_message ( rndis, iobuf, RNDIS_PACKET_MSG ) ) != 0 )
                return rc;

        return 0;
}
int rndis_tx_defer ( struct rndis_device rndis,
struct io_buffer iobuf 
)

Defer transmitted packet.

Parameters:
rndisRNDIS device
iobufI/O buffer
Return values:
rcReturn status code

As with netdev_tx_defer(), the caller must ensure that space in the transmit descriptor ring is freed up before calling rndis_tx_complete().

Unlike netdev_tx_defer(), this call may fail.

Definition at line 190 of file rndis.c.

References assert, cpu_to_le32, io_buffer::data, ENOTSUP, header, iob_len(), iob_pull, msg(), netdev, rndis_device::netdev, netdev_tx_defer(), RNDIS_PACKET_MSG, and rndis_header::type.

Referenced by netvsc_transmit().

                                                                           {
        struct net_device *netdev = rndis->netdev;
        struct rndis_header *header;
        struct rndis_packet_message *msg;

        /* Fail unless this was a packet message.  Only packet
         * messages correspond to I/O buffers in the network device's
         * TX queue; other messages cannot be deferred in this way.
         */
        assert ( iob_len ( iobuf ) >= sizeof ( *header ) );
        header = iobuf->data;
        if ( header->type != cpu_to_le32 ( RNDIS_PACKET_MSG ) )
                return -ENOTSUP;

        /* Strip RNDIS header and packet message header, to return
         * this packet to the state in which we received it.
         */
        iob_pull ( iobuf, ( sizeof ( *header ) + sizeof ( *msg ) ) );

        /* Defer packet */
        netdev_tx_defer ( netdev, iobuf );

        return 0;
}
static void rndis_rx_data ( struct rndis_device rndis,
struct io_buffer iobuf 
) [static]

Receive data packet.

Parameters:
rndisRNDIS device
iobufI/O buffer

Definition at line 221 of file rndis.c.

References io_buffer::data, rndis_packet_message::data, DBGC, DBGC_HDA, EINVAL, iob_disown, iob_len(), iob_pull, iob_unput, le32_to_cpu, rndis_packet_field::len, len, msg(), rndis_device::name, netdev, rndis_device::netdev, netdev_rx(), netdev_rx_err(), rndis_packet_field::offset, and rc.

Referenced by rndis_rx_message().

                                                      {
        struct net_device *netdev = rndis->netdev;
        struct rndis_packet_message *msg;
        size_t len = iob_len ( iobuf );
        size_t data_offset;
        size_t data_len;
        int rc;

        /* Sanity check */
        if ( len < sizeof ( *msg ) ) {
                DBGC ( rndis, "RNDIS %s received underlength data packet:\n",
                       rndis->name );
                DBGC_HDA ( rndis, 0, iobuf->data, len );
                rc = -EINVAL;
                goto err_len;
        }
        msg = iobuf->data;

        /* Locate and sanity check data buffer */
        data_offset = le32_to_cpu ( msg->data.offset );
        data_len = le32_to_cpu ( msg->data.len );
        if ( ( data_offset > len ) || ( data_len > ( len - data_offset ) ) ) {
                DBGC ( rndis, "RNDIS %s data packet data exceeds packet:\n",
                       rndis->name );
                DBGC_HDA ( rndis, 0, iobuf->data, len );
                rc = -EINVAL;
                goto err_data;
        }

        /* Strip non-data portions */
        iob_pull ( iobuf, data_offset );
        iob_unput ( iobuf, ( iob_len ( iobuf ) - data_len ) );

        /* Hand off to network stack */
        netdev_rx ( netdev, iob_disown ( iobuf ) );

        return;

 err_data:
 err_len:
        /* Report error to network stack */
        netdev_rx_err ( netdev, iob_disown ( iobuf ), rc );
}
static int rndis_tx_initialise ( struct rndis_device rndis,
unsigned int  id 
) [static]

Transmit initialisation message.

Parameters:
rndisRNDIS device
idRequest ID
Return values:
rcReturn status code

Definition at line 273 of file rndis.c.

References cpu_to_le32, ENOMEM, free_iob(), rndis_initialise_message::id, id, iob_put, rndis_initialise_message::major, memset(), rndis_initialise_message::minor, msg(), rndis_initialise_message::mtu, rc, rndis_alloc_iob(), RNDIS_INITIALISE_MSG, RNDIS_MTU, rndis_tx_message(), RNDIS_VERSION_MAJOR, and RNDIS_VERSION_MINOR.

Referenced by rndis_initialise().

                                                                               {
        struct io_buffer *iobuf;
        struct rndis_initialise_message *msg;
        int rc;

        /* Allocate I/O buffer */
        iobuf = rndis_alloc_iob ( sizeof ( *msg ) );
        if ( ! iobuf ) {
                rc = -ENOMEM;
                goto err_alloc;
        }

        /* Construct message */
        msg = iob_put ( iobuf, sizeof ( *msg ) );
        memset ( msg, 0, sizeof ( *msg ) );
        msg->id = id; /* Non-endian */
        msg->major = cpu_to_le32 ( RNDIS_VERSION_MAJOR );
        msg->minor = cpu_to_le32 ( RNDIS_VERSION_MINOR );
        msg->mtu = cpu_to_le32 ( RNDIS_MTU );

        /* Transmit message */
        if ( ( rc = rndis_tx_message ( rndis, iobuf,
                                       RNDIS_INITIALISE_MSG ) ) != 0 )
                goto err_tx;

        return 0;

 err_tx:
        free_iob ( iobuf );
 err_alloc:
        return rc;
}
static void rndis_rx_initialise ( struct rndis_device rndis,
struct io_buffer iobuf 
) [static]

Receive initialisation completion.

Parameters:
rndisRNDIS device
iobufI/O buffer

Definition at line 312 of file rndis.c.

References io_buffer::data, DBGC, DBGC_HDA, EINVAL, EIO, free_iob(), rndis_initialise_completion::id, id, iob_len(), le32_to_cpu, len, rndis_device::name, rc, rndis_initialise_completion::status, rndis_device::wait_id, and rndis_device::wait_rc.

Referenced by rndis_rx_message().

                                                            {
        struct rndis_initialise_completion *cmplt;
        size_t len = iob_len ( iobuf );
        unsigned int id;
        int rc;

        /* Sanity check */
        if ( len < sizeof ( *cmplt ) ) {
                DBGC ( rndis, "RNDIS %s received underlength initialisation "
                       "completion:\n", rndis->name );
                DBGC_HDA ( rndis, 0, iobuf->data, len );
                rc = -EINVAL;
                goto err_len;
        }
        cmplt = iobuf->data;

        /* Extract request ID */
        id = cmplt->id; /* Non-endian */

        /* Check status */
        if ( cmplt->status ) {
                DBGC ( rndis, "RNDIS %s received initialisation completion "
                       "failure %#08x\n", rndis->name,
                       le32_to_cpu ( cmplt->status ) );
                rc = -EIO;
                goto err_status;
        }

        /* Success */
        rc = 0;

 err_status:
        /* Record completion result if applicable */
        if ( id == rndis->wait_id ) {
                rndis->wait_id = 0;
                rndis->wait_rc = rc;
        }
 err_len:
        free_iob ( iobuf );
}
static int rndis_initialise ( struct rndis_device rndis) [static]

Initialise RNDIS.

Parameters:
rndisRNDIS device
Return values:
rcReturn status code

Definition at line 360 of file rndis.c.

References rc, RNDIS_INIT_ID, rndis_tx_initialise(), and rndis_wait().

Referenced by rndis_describe(), and rndis_open().

                                                           {
        int rc;

        /* Transmit initialisation message */
        if ( ( rc = rndis_tx_initialise ( rndis, RNDIS_INIT_ID ) ) != 0 )
                return rc;

        /* Wait for response */
        if ( ( rc = rndis_wait ( rndis, RNDIS_INIT_ID ) ) != 0 )
                return rc;

        return 0;
}
static int rndis_tx_halt ( struct rndis_device rndis) [static]

Transmit halt message.

Parameters:
rndisRNDIS device
Return values:
rcReturn status code

Definition at line 380 of file rndis.c.

References ENOMEM, free_iob(), iob_put, memset(), msg(), rc, rndis_alloc_iob(), RNDIS_HALT_MSG, and rndis_tx_message().

Referenced by rndis_halt().

                                                        {
        struct io_buffer *iobuf;
        struct rndis_halt_message *msg;
        int rc;

        /* Allocate I/O buffer */
        iobuf = rndis_alloc_iob ( sizeof ( *msg ) );
        if ( ! iobuf ) {
                rc = -ENOMEM;
                goto err_alloc;
        }

        /* Construct message */
        msg = iob_put ( iobuf, sizeof ( *msg ) );
        memset ( msg, 0, sizeof ( *msg ) );

        /* Transmit message */
        if ( ( rc = rndis_tx_message ( rndis, iobuf, RNDIS_HALT_MSG ) ) != 0 )
                goto err_tx;

        return 0;

 err_tx:
        free_iob ( iobuf );
 err_alloc:
        return rc;
}
static int rndis_halt ( struct rndis_device rndis) [static]

Halt RNDIS.

Parameters:
rndisRNDIS device
Return values:
rcReturn status code

Definition at line 414 of file rndis.c.

References rc, and rndis_tx_halt().

Referenced by rndis_close(), rndis_describe(), and rndis_open().

                                                     {
        int rc;

        /* Transmit halt message */
        if ( ( rc = rndis_tx_halt ( rndis ) ) != 0 )
                return rc;

        return 0;
}
static int rndis_tx_oid ( struct rndis_device rndis,
unsigned int  oid,
const void *  data,
size_t  len 
) [static]

Transmit OID message.

Parameters:
rndisRNDIS device
oidObject ID
dataNew OID value (or NULL to query current value)
lenLength of new OID value
Return values:
rcReturn status code

Definition at line 433 of file rndis.c.

References cpu_to_le32, ENOMEM, free_iob(), rndis_oid_message::id, iob_put, rndis_oid_message::len, memcpy(), memset(), msg(), rndis_oid_message::offset, rndis_oid_message::oid, rc, rndis_alloc_iob(), RNDIS_QUERY_MSG, RNDIS_SET_MSG, rndis_tx_message(), and type.

Referenced by rndis_oid().

                                                         {
        struct io_buffer *iobuf;
        struct rndis_oid_message *msg;
        unsigned int type;
        int rc;

        /* Allocate I/O buffer */
        iobuf = rndis_alloc_iob ( sizeof ( *msg ) + len );
        if ( ! iobuf ) {
                rc = -ENOMEM;
                goto err_alloc;
        }

        /* Construct message.  We use the OID as the request ID. */
        msg = iob_put ( iobuf, sizeof ( *msg ) );
        memset ( msg, 0, sizeof ( *msg ) );
        msg->id = oid; /* Non-endian */
        msg->oid = cpu_to_le32 ( oid );
        msg->offset = cpu_to_le32 ( sizeof ( *msg ) );
        msg->len = cpu_to_le32 ( len );
        memcpy ( iob_put ( iobuf, len ), data, len );

        /* Transmit message */
        type = ( data ? RNDIS_SET_MSG : RNDIS_QUERY_MSG );
        if ( ( rc = rndis_tx_message ( rndis, iobuf, type ) ) != 0 )
                goto err_tx;

        return 0;

 err_tx:
        free_iob ( iobuf );
 err_alloc:
        return rc;
}
static void rndis_rx_query_oid ( struct rndis_device rndis,
struct io_buffer iobuf 
) [static]

Receive query OID completion.

Parameters:
rndisRNDIS device
iobufI/O buffer

Definition at line 475 of file rndis.c.

References io_buffer::data, DBGC, DBGC_HDA, EINVAL, EIO, EPROTO, free_iob(), net_device::hw_addr, id, rndis_query_completion::id, info, info_len, iob_len(), le32_to_cpu, rndis_query_completion::len, len, net_device::ll_addr, memcpy(), rndis_device::name, netdev, rndis_device::netdev, netdev_link_down(), netdev_link_up(), rndis_query_completion::offset, rc, RNDIS_OID_802_3_CURRENT_ADDRESS, RNDIS_OID_802_3_PERMANENT_ADDRESS, RNDIS_OID_GEN_MEDIA_CONNECT_STATUS, rndis_query_completion::status, rndis_device::wait_id, and rndis_device::wait_rc.

Referenced by rndis_rx_message().

                                                           {
        struct net_device *netdev = rndis->netdev;
        struct rndis_query_completion *cmplt;
        size_t len = iob_len ( iobuf );
        size_t info_offset;
        size_t info_len;
        unsigned int id;
        void *info;
        uint32_t *link_status;
        int rc;

        /* Sanity check */
        if ( len < sizeof ( *cmplt ) ) {
                DBGC ( rndis, "RNDIS %s received underlength query "
                       "completion:\n", rndis->name );
                DBGC_HDA ( rndis, 0, iobuf->data, len );
                rc = -EINVAL;
                goto err_len;
        }
        cmplt = iobuf->data;

        /* Extract request ID */
        id = cmplt->id; /* Non-endian */

        /* Check status */
        if ( cmplt->status ) {
                DBGC ( rndis, "RNDIS %s received query completion failure "
                       "%#08x\n", rndis->name, le32_to_cpu ( cmplt->status ) );
                DBGC_HDA ( rndis, 0, iobuf->data, len );
                rc = -EIO;
                goto err_status;
        }

        /* Locate and sanity check information buffer */
        info_offset = le32_to_cpu ( cmplt->offset );
        info_len = le32_to_cpu ( cmplt->len );
        if ( ( info_offset > len ) || ( info_len > ( len - info_offset ) ) ) {
                DBGC ( rndis, "RNDIS %s query completion information exceeds "
                       "packet:\n", rndis->name );
                DBGC_HDA ( rndis, 0, iobuf->data, len );
                rc = -EINVAL;
                goto err_info;
        }
        info = ( ( ( void * ) cmplt ) + info_offset );

        /* Handle OID */
        switch ( id ) {

        case RNDIS_OID_802_3_PERMANENT_ADDRESS:
                if ( info_len > sizeof ( netdev->hw_addr ) )
                        info_len = sizeof ( netdev->hw_addr );
                memcpy ( netdev->hw_addr, info, info_len );
                break;

        case RNDIS_OID_802_3_CURRENT_ADDRESS:
                if ( info_len > sizeof ( netdev->ll_addr ) )
                        info_len = sizeof ( netdev->ll_addr );
                memcpy ( netdev->ll_addr, info, info_len );
                break;

        case RNDIS_OID_GEN_MEDIA_CONNECT_STATUS:
                if ( info_len != sizeof ( *link_status ) ) {
                        DBGC ( rndis, "RNDIS %s invalid link status:\n",
                               rndis->name );
                        DBGC_HDA ( rndis, 0, iobuf->data, len );
                        rc = -EPROTO;
                        goto err_link_status;
                }
                link_status = info;
                if ( *link_status == 0 ) {
                        DBGC ( rndis, "RNDIS %s link is up\n", rndis->name );
                        netdev_link_up ( netdev );
                } else {
                        DBGC ( rndis, "RNDIS %s link is down: %#08x\n",
                               rndis->name, le32_to_cpu ( *link_status ) );
                        netdev_link_down ( netdev );
                }
                break;

        default:
                DBGC ( rndis, "RNDIS %s unexpected query completion ID %#08x\n",
                       rndis->name, id );
                DBGC_HDA ( rndis, 0, iobuf->data, len );
                rc = -EPROTO;
                goto err_id;
        }

        /* Success */
        rc = 0;

 err_id:
 err_link_status:
 err_info:
 err_status:
        /* Record completion result if applicable */
        if ( id == rndis->wait_id ) {
                rndis->wait_id = 0;
                rndis->wait_rc = rc;
        }
 err_len:
        /* Free I/O buffer */
        free_iob ( iobuf );
}
static void rndis_rx_set_oid ( struct rndis_device rndis,
struct io_buffer iobuf 
) [static]

Receive set OID completion.

Parameters:
rndisRNDIS device
iobufI/O buffer

Definition at line 586 of file rndis.c.

References io_buffer::data, DBGC, DBGC_HDA, EINVAL, EIO, free_iob(), id, rndis_set_completion::id, iob_len(), le32_to_cpu, len, rndis_device::name, rc, rndis_set_completion::status, rndis_device::wait_id, and rndis_device::wait_rc.

Referenced by rndis_rx_message().

                                                         {
        struct rndis_set_completion *cmplt;
        size_t len = iob_len ( iobuf );
        unsigned int id;
        int rc;

        /* Sanity check */
        if ( len < sizeof ( *cmplt ) ) {
                DBGC ( rndis, "RNDIS %s received underlength set completion:\n",
                       rndis->name );
                DBGC_HDA ( rndis, 0, iobuf->data, len );
                rc = -EINVAL;
                goto err_len;
        }
        cmplt = iobuf->data;

        /* Extract request ID */
        id = cmplt->id; /* Non-endian */

        /* Check status */
        if ( cmplt->status ) {
                DBGC ( rndis, "RNDIS %s received set completion failure "
                       "%#08x\n", rndis->name, le32_to_cpu ( cmplt->status ) );
                DBGC_HDA ( rndis, 0, iobuf->data, len );
                rc = -EIO;
                goto err_status;
        }

        /* Success */
        rc = 0;

 err_status:
        /* Record completion result if applicable */
        if ( id == rndis->wait_id ) {
                rndis->wait_id = 0;
                rndis->wait_rc = rc;
        }
 err_len:
        /* Free I/O buffer */
        free_iob ( iobuf );
}
static int rndis_oid ( struct rndis_device rndis,
unsigned int  oid,
const void *  data,
size_t  len 
) [static]

Query or set OID.

Parameters:
rndisRNDIS device
oidObject ID
dataNew OID value (or NULL to query current value)
lenLength of new OID value
Return values:
rcReturn status code

Definition at line 638 of file rndis.c.

References rc, rndis_tx_oid(), and rndis_wait().

Referenced by rndis_describe(), rndis_filter(), and rndis_open().

                                                      {
        int rc;

        /* Transmit query */
        if ( ( rc = rndis_tx_oid ( rndis, oid, data, len ) ) != 0 )
                return rc;

        /* Wait for response */
        if ( ( rc = rndis_wait ( rndis, oid ) ) != 0 )
                return rc;

        return 0;
}
static int rndis_describe ( struct rndis_device rndis) [static]

Describe RNDIS device.

Parameters:
rndisRNDIS device
Return values:
rcReturn status code

Definition at line 659 of file rndis.c.

References rndis_operations::close, DBGC, net_device::dev, device::name, rndis_device::name, netdev, rndis_device::netdev, NULL, rndis_device::op, rndis_operations::open, rc, rndis_halt(), rndis_initialise(), rndis_oid(), RNDIS_OID_802_3_CURRENT_ADDRESS, RNDIS_OID_802_3_PERMANENT_ADDRESS, RNDIS_OID_GEN_MEDIA_CONNECT_STATUS, and strerror().

Referenced by register_rndis().

                                                         {
        struct net_device *netdev = rndis->netdev;
        int rc;

        /* Assign device name (for debugging) */
        rndis->name = netdev->dev->name;

        /* Open RNDIS device to read MAC addresses */
        if ( ( rc = rndis->op->open ( rndis ) ) != 0 ) {
                DBGC ( rndis, "RNDIS %s could not open: %s\n",
                       rndis->name, strerror ( rc ) );
                goto err_open;
        }

        /* Initialise RNDIS */
        if ( ( rc = rndis_initialise ( rndis ) ) != 0 )
                goto err_initialise;

        /* Query permanent MAC address */
        if ( ( rc = rndis_oid ( rndis, RNDIS_OID_802_3_PERMANENT_ADDRESS,
                                NULL, 0 ) ) != 0 )
                goto err_query_permanent;

        /* Query current MAC address */
        if ( ( rc = rndis_oid ( rndis, RNDIS_OID_802_3_CURRENT_ADDRESS,
                                NULL, 0 ) ) != 0 )
                goto err_query_current;

        /* Get link status */
        if ( ( rc = rndis_oid ( rndis, RNDIS_OID_GEN_MEDIA_CONNECT_STATUS,
                                NULL, 0 ) ) != 0 )
                goto err_query_link;

        /* Halt RNDIS device */
        rndis_halt ( rndis );

        /* Close RNDIS device */
        rndis->op->close ( rndis );

        return 0;

 err_query_link:
 err_query_current:
 err_query_permanent:
        rndis_halt ( rndis );
 err_initialise:
        rndis->op->close ( rndis );
 err_open:
        return rc;
}
static void rndis_rx_status ( struct rndis_device rndis,
struct io_buffer iobuf 
) [static]

Receive indicate status message.

Parameters:
rndisRNDIS device
iobufI/O buffer

Definition at line 716 of file rndis.c.

References io_buffer::data, DBGC, DBGC_HDA, EINVAL, ENOTSUP, free_iob(), iob_len(), le32_to_cpu, len, msg(), rndis_device::name, netdev, rndis_device::netdev, netdev_link_down(), netdev_link_up(), netdev_rx_err(), rc, RNDIS_STATUS_MEDIA_CONNECT, RNDIS_STATUS_MEDIA_DISCONNECT, RNDIS_STATUS_WTF_WORLD, status, and rndis_indicate_status_message::status.

Referenced by rndis_rx_message().

                                                        {
        struct net_device *netdev = rndis->netdev;
        struct rndis_indicate_status_message *msg;
        size_t len = iob_len ( iobuf );
        unsigned int status;
        int rc;

        /* Sanity check */
        if ( len < sizeof ( *msg ) ) {
                DBGC ( rndis, "RNDIS %s received underlength status message:\n",
                       rndis->name );
                DBGC_HDA ( rndis, 0, iobuf->data, len );
                rc = -EINVAL;
                goto err_len;
        }
        msg = iobuf->data;

        /* Extract status */
        status = le32_to_cpu ( msg->status );

        /* Handle status */
        switch ( msg->status ) {

        case RNDIS_STATUS_MEDIA_CONNECT:
                DBGC ( rndis, "RNDIS %s link is up\n", rndis->name );
                netdev_link_up ( netdev );
                break;

        case RNDIS_STATUS_MEDIA_DISCONNECT:
                DBGC ( rndis, "RNDIS %s link is down\n", rndis->name );
                netdev_link_down ( netdev );
                break;

        case RNDIS_STATUS_WTF_WORLD:
                /* Ignore */
                break;

        default:
                DBGC ( rndis, "RNDIS %s unexpected status %#08x:\n",
                       rndis->name, status );
                DBGC_HDA ( rndis, 0, iobuf->data, len );
                rc = -ENOTSUP;
                goto err_status;
        }

        /* Free I/O buffer */
        free_iob ( iobuf );

        return;

 err_status:
 err_len:
        /* Report error via network device statistics */
        netdev_rx_err ( netdev, iobuf, rc );
}
static void rndis_rx_message ( struct rndis_device rndis,
struct io_buffer iobuf,
unsigned int  type 
) [static]

Receive RNDIS message.

Parameters:
rndisRNDIS device
iobufI/O buffer
typeMessage type

Definition at line 780 of file rndis.c.

References io_buffer::data, DBGC, DBGC_HDA, EPROTO, iob_disown, iob_len(), rndis_device::name, netdev, rndis_device::netdev, netdev_rx_err(), rc, RNDIS_INDICATE_STATUS_MSG, RNDIS_INITIALISE_CMPLT, RNDIS_PACKET_MSG, RNDIS_QUERY_CMPLT, rndis_rx_data(), rndis_rx_initialise(), rndis_rx_query_oid(), rndis_rx_set_oid(), rndis_rx_status(), and RNDIS_SET_CMPLT.

Referenced by rndis_rx().

                                                                            {
        struct net_device *netdev = rndis->netdev;
        int rc;

        /* Handle packet */
        switch ( type ) {

        case RNDIS_PACKET_MSG:
                rndis_rx_data ( rndis, iob_disown ( iobuf ) );
                break;

        case RNDIS_INITIALISE_CMPLT:
                rndis_rx_initialise ( rndis, iob_disown ( iobuf ) );
                break;

        case RNDIS_QUERY_CMPLT:
                rndis_rx_query_oid ( rndis, iob_disown ( iobuf ) );
                break;

        case RNDIS_SET_CMPLT:
                rndis_rx_set_oid ( rndis, iob_disown ( iobuf ) );
                break;

        case RNDIS_INDICATE_STATUS_MSG:
                rndis_rx_status ( rndis, iob_disown ( iobuf ) );
                break;

        default:
                DBGC ( rndis, "RNDIS %s received unexpected type %#08x\n",
                       rndis->name, type );
                DBGC_HDA ( rndis, 0, iobuf->data, iob_len ( iobuf ) );
                rc = -EPROTO;
                goto err_type;
        }

        return;

 err_type:
        /* Report error via network device statistics */
        netdev_rx_err ( netdev, iobuf, rc );
}
void rndis_rx ( struct rndis_device rndis,
struct io_buffer iobuf 
)

Receive packet from underlying transport layer.

Parameters:
rndisRNDIS device
iobufI/O buffer

Definition at line 829 of file rndis.c.

References io_buffer::data, DBGC, DBGC_HDA, EINVAL, header, iob_disown, iob_len(), iob_pull, le32_to_cpu, rndis_device::name, netdev, rndis_device::netdev, netdev_rx_err(), rc, rndis_rx_message(), rndis_header::type, and type.

Referenced by acm_control_receive(), acm_in_complete(), and netvsc_recv_data().

                                                                      {
        struct net_device *netdev = rndis->netdev;
        struct rndis_header *header;
        unsigned int type;
        int rc;

        /* Sanity check */
        if ( iob_len ( iobuf ) < sizeof ( *header ) ) {
                DBGC ( rndis, "RNDIS %s received underlength packet:\n",
                       rndis->name );
                DBGC_HDA ( rndis, 0, iobuf->data, iob_len ( iobuf ) );
                rc = -EINVAL;
                goto drop;
        }
        header = iobuf->data;

        /* Parse and strip header */
        type = le32_to_cpu ( header->type );
        iob_pull ( iobuf, sizeof ( *header ) );

        /* Handle message */
        rndis_rx_message ( rndis, iob_disown ( iobuf ), type );

        return;

 drop:
        /* Record error */
        netdev_rx_err ( netdev, iob_disown ( iobuf ), rc );
}
void rndis_rx_err ( struct rndis_device rndis,
struct io_buffer iobuf,
int  rc 
)

Discard packet from underlying transport layer.

Parameters:
rndisRNDIS device
iobufI/O buffer
rcPacket status code

Definition at line 866 of file rndis.c.

References iob_disown, netdev, rndis_device::netdev, and netdev_rx_err().

Referenced by acm_in_complete(), acm_intr_complete(), and acm_poll().

                             {
        struct net_device *netdev = rndis->netdev;

        /* Record error */
        netdev_rx_err ( netdev, iob_disown ( iobuf ), rc );
}
static int rndis_filter ( struct rndis_device rndis,
unsigned int  filter 
) [static]

Set receive filter.

Parameters:
rndisRNDIS device
filterReceive filter
Return values:
rcReturn status code

Definition at line 881 of file rndis.c.

References cpu_to_le32, DBGC, rndis_device::name, rc, rndis_oid(), RNDIS_OID_GEN_CURRENT_PACKET_FILTER, strerror(), and value.

Referenced by rndis_close(), and rndis_open().

                                                                            {
        uint32_t value = cpu_to_le32 ( filter );
        int rc;

        /* Set receive filter */
        if ( ( rc = rndis_oid ( rndis, RNDIS_OID_GEN_CURRENT_PACKET_FILTER,
                                &value, sizeof ( value ) ) ) != 0 ) {
                DBGC ( rndis, "RNDIS %s could not set receive filter to %#08x: "
                       "%s\n", rndis->name, filter, strerror ( rc ) );
                return rc;
        }

        return 0;
}
static int rndis_open ( struct net_device netdev) [static]

Open network device.

Parameters:
netdevNetwork device
Return values:
rcReturn status code

Definition at line 902 of file rndis.c.

References rndis_operations::close, DBGC, rndis_device::name, NULL, rndis_device::op, rndis_operations::open, net_device::priv, rc, rndis_filter(), RNDIS_FILTER_ALL_MULTICAST, RNDIS_FILTER_BROADCAST, RNDIS_FILTER_MULTICAST, RNDIS_FILTER_PROMISCUOUS, RNDIS_FILTER_UNICAST, rndis_halt(), rndis_initialise(), rndis_oid(), RNDIS_OID_GEN_MEDIA_CONNECT_STATUS, and strerror().

                                                    {
        struct rndis_device *rndis = netdev->priv;
        int rc;

        /* Open RNDIS device */
        if ( ( rc = rndis->op->open ( rndis ) ) != 0 ) {
                DBGC ( rndis, "RNDIS %s could not open: %s\n",
                       rndis->name, strerror ( rc ) );
                goto err_open;
        }

        /* Initialise RNDIS */
        if ( ( rc = rndis_initialise ( rndis ) ) != 0 )
                goto err_initialise;

        /* Set receive filter */
        if ( ( rc = rndis_filter ( rndis, ( RNDIS_FILTER_UNICAST |
                                            RNDIS_FILTER_MULTICAST |
                                            RNDIS_FILTER_ALL_MULTICAST |
                                            RNDIS_FILTER_BROADCAST |
                                            RNDIS_FILTER_PROMISCUOUS ) ) ) != 0)
                goto err_set_filter;

        /* Update link status */
        if ( ( rc = rndis_oid ( rndis, RNDIS_OID_GEN_MEDIA_CONNECT_STATUS,
                                NULL, 0 ) ) != 0 )
                goto err_query_link;

        return 0;

 err_query_link:
 err_set_filter:
        rndis_halt ( rndis );
 err_initialise:
        rndis->op->close ( rndis );
 err_open:
        return rc;
}
static void rndis_close ( struct net_device netdev) [static]

Close network device.

Parameters:
netdevNetwork device

Definition at line 946 of file rndis.c.

References rndis_operations::close, rndis_device::op, net_device::priv, rndis_filter(), and rndis_halt().

                                                      {
        struct rndis_device *rndis = netdev->priv;

        /* Clear receive filter */
        rndis_filter ( rndis, 0 );

        /* Halt RNDIS device */
        rndis_halt ( rndis );

        /* Close RNDIS device */
        rndis->op->close ( rndis );
}
static int rndis_transmit ( struct net_device netdev,
struct io_buffer iobuf 
) [static]

Transmit packet.

Parameters:
netdevNetwork device
iobufI/O buffer
Return values:
rcReturn status code

Definition at line 966 of file rndis.c.

References net_device::priv, and rndis_tx_data().

                                                      {
        struct rndis_device *rndis = netdev->priv;

        /* Transmit data packet */
        return rndis_tx_data ( rndis, iobuf );
}
static void rndis_poll ( struct net_device netdev) [static]

Poll for completed and received packets.

Parameters:
netdevNetwork device

Definition at line 979 of file rndis.c.

References rndis_device::op, rndis_operations::poll, and net_device::priv.

                                                     {
        struct rndis_device *rndis = netdev->priv;

        /* Poll RNDIS device */
        rndis->op->poll ( rndis );
}
struct rndis_device* alloc_rndis ( size_t  priv_len) [read]

Allocate RNDIS device.

Parameters:
priv_lenLength of private data
Return values:
rndisRNDIS device, or NULL on allocation failure

Definition at line 1000 of file rndis.c.

References alloc_etherdev(), netdev, rndis_device::netdev, netdev_init(), NULL, rndis_device::priv, and net_device::priv.

Referenced by acm_probe(), and netvsc_probe().

                                                      {
        struct net_device *netdev;
        struct rndis_device *rndis;

        /* Allocate and initialise structure */
        netdev = alloc_etherdev ( sizeof ( *rndis ) + priv_len );
        if ( ! netdev )
                return NULL;
        netdev_init ( netdev, &rndis_operations );
        rndis = netdev->priv;
        rndis->netdev = netdev;
        rndis->priv = ( ( ( void * ) rndis ) + sizeof ( *rndis ) );

        return rndis;
}
int register_rndis ( struct rndis_device rndis)

Register RNDIS device.

Parameters:
rndisRNDIS device
Return values:
rcReturn status code

Note that this routine will open and use the RNDIS device in order to query the MAC address. The device must be immediately ready for use prior to registration.

Definition at line 1026 of file rndis.c.

References DBGC, rndis_device::name, netdev, rndis_device::netdev, rc, register_netdev(), rndis_describe(), strerror(), and unregister_netdev().

Referenced by acm_probe(), and netvsc_probe().

                                                  {
        struct net_device *netdev = rndis->netdev;
        int rc;

        /* Describe RNDIS device */
        if ( ( rc = rndis_describe ( rndis ) ) != 0 )
                goto err_describe;

        /* Register network device */
        if ( ( rc = register_netdev ( netdev ) ) != 0 ) {
                DBGC ( rndis, "RNDIS %s could not register: %s\n",
                       rndis->name, strerror ( rc ) );
                goto err_register;
        }

        return 0;

        unregister_netdev ( netdev );
 err_register:
 err_describe:
        return rc;
}
void unregister_rndis ( struct rndis_device rndis)

Unregister RNDIS device.

Parameters:
rndisRNDIS device

Definition at line 1054 of file rndis.c.

References netdev, rndis_device::netdev, and unregister_netdev().

Referenced by acm_probe(), acm_remove(), netvsc_probe(), and netvsc_remove().

                                                     {
        struct net_device *netdev = rndis->netdev;

        /* Unregister network device */
        unregister_netdev ( netdev );
}
void free_rndis ( struct rndis_device rndis)

Free RNDIS device.

Parameters:
rndisRNDIS device

Definition at line 1066 of file rndis.c.

References netdev, rndis_device::netdev, netdev_nullify(), and netdev_put().

Referenced by acm_probe(), acm_remove(), netvsc_probe(), and netvsc_remove().

                                               {
        struct net_device *netdev = rndis->netdev;

        /* Free network device */
        netdev_nullify ( netdev );
        netdev_put ( netdev );
}

Variable Documentation

Initial value:
 {
        .open           = rndis_open,
        .close          = rndis_close,
        .transmit       = rndis_transmit,
        .poll           = rndis_poll,
}

Network device operations.

Definition at line 987 of file rndis.c.