iPXE
Functions | Variables
acm.c File Reference

USB RNDIS driver. More...

#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <byteswap.h>
#include <ipxe/profile.h>
#include <ipxe/usb.h>
#include <ipxe/usbnet.h>
#include <ipxe/rndis.h>
#include "acm.h"

Go to the source code of this file.

Functions

 FILE_LICENCE (GPL2_OR_LATER_OR_UBDL)
static void acm_intr_complete (struct usb_endpoint *ep, struct io_buffer *iobuf, int rc)
 Complete interrupt transfer.
static void acm_in_complete (struct usb_endpoint *ep, struct io_buffer *iobuf, int rc)
 Complete bulk IN transfer.
static int acm_out_transmit (struct acm_device *acm, struct io_buffer *iobuf)
 Transmit packet.
static void acm_out_complete (struct usb_endpoint *ep, struct io_buffer *iobuf, int rc)
 Complete bulk OUT transfer.
static int acm_control_transmit (struct acm_device *acm, struct io_buffer *iobuf)
 Send control packet.
static int acm_control_receive (struct acm_device *acm)
 Receive control packet.
static int acm_open (struct rndis_device *rndis)
 Open RNDIS device.
static void acm_close (struct rndis_device *rndis)
 Close RNDIS device.
static int acm_transmit (struct rndis_device *rndis, struct io_buffer *iobuf)
 Transmit packet.
static void acm_poll (struct rndis_device *rndis)
 Poll for completed and received packets.
static int acm_probe (struct usb_function *func, struct usb_configuration_descriptor *config)
 Probe device.
static void acm_remove (struct usb_function *func)
 Remove device.

Variables

static struct profiler
acm_intr_profiler 
__profiler
 Interrupt completion profiler.
static struct
usb_endpoint_driver_operations 
acm_intr_operations
 Interrupt endpoint operations.
static struct
usb_endpoint_driver_operations 
acm_in_operations
 Bulk IN endpoint operations.
static struct
usb_endpoint_driver_operations 
acm_out_operations
 Bulk OUT endpoint operations.
static struct rndis_operations acm_operations
 USB RNDIS operations.
static struct usb_device_id cdc_acm_ids []
 USB CDC-ACM device IDs.
struct usb_driver cdc_acm_driver __usb_driver
 USB CDC-ACM driver.
static struct usb_device_id rf_rndis_ids []
 USB RF-RNDIS device IDs.

Detailed Description

USB RNDIS driver.

Definition in file acm.c.


Function Documentation

FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL  )
static void acm_intr_complete ( struct usb_endpoint ep,
struct io_buffer iobuf,
int  rc 
) [static]

Complete interrupt transfer.

Parameters:
epUSB endpoint
iobufI/O buffer
rcCompletion status code

Definition at line 68 of file acm.c.

References CDC_RESPONSE_AVAILABLE, container_of, cpu_to_le16, io_buffer::data, DBGC, DBGC_HDA, EINVAL, ENOTSUP, error, free_iob(), usbnet_device::intr, iob_disown, iob_len(), message, usb_endpoint::open, profile_start(), profile_stop(), usb_setup_packet::request, acm_device::responded, acm_device::rndis, rndis_rx_err(), strerror(), and acm_device::usbnet.

                                                                  {
        struct acm_device *acm = container_of ( ep, struct acm_device,
                                                usbnet.intr );
        struct rndis_device *rndis = acm->rndis;
        struct usb_setup_packet *message;

        /* Profile completions */
        profile_start ( &acm_intr_profiler );

        /* Ignore packets cancelled when the endpoint closes */
        if ( ! ep->open )
                goto ignore;

        /* Drop packets with errors */
        if ( rc != 0 ) {
                DBGC ( acm, "ACM %p interrupt failed: %s\n",
                       acm, strerror ( rc ) );
                DBGC_HDA ( acm, 0, iobuf->data, iob_len ( iobuf ) );
                goto error;
        }

        /* Extract message header */
        if ( iob_len ( iobuf ) < sizeof ( *message ) ) {
                DBGC ( acm, "ACM %p underlength interrupt:\n", acm );
                DBGC_HDA ( acm, 0, iobuf->data, iob_len ( iobuf ) );
                rc = -EINVAL;
                goto error;
        }
        message = iobuf->data;

        /* Parse message header */
        switch ( message->request ) {

        case cpu_to_le16 ( CDC_RESPONSE_AVAILABLE ) :
        case cpu_to_le16 ( 0x0001 ) : /* qemu seems to use this value */
                acm->responded = 1;
                break;

        default:
                DBGC ( acm, "ACM %p unrecognised interrupt:\n", acm );
                DBGC_HDA ( acm, 0, iobuf->data, iob_len ( iobuf ) );
                rc = -ENOTSUP;
                goto error;
        }

        /* Free I/O buffer */
        free_iob ( iobuf );
        profile_stop ( &acm_intr_profiler );

        return;

 error:
        rndis_rx_err ( rndis, iob_disown ( iobuf ), rc );
 ignore:
        free_iob ( iobuf );
        return;
}
static void acm_in_complete ( struct usb_endpoint ep,
struct io_buffer iobuf,
int  rc 
) [static]

Complete bulk IN transfer.

Parameters:
epUSB endpoint
iobufI/O buffer
rcCompletion status code

Definition at line 146 of file acm.c.

References container_of, DBGC, error, free_iob(), usbnet_device::in, iob_disown, usb_endpoint::open, profile_start(), profile_stop(), acm_device::rndis, rndis_rx(), rndis_rx_err(), strerror(), and acm_device::usbnet.

                                       {
        struct acm_device *acm = container_of ( ep, struct acm_device,
                                                usbnet.in );
        struct rndis_device *rndis = acm->rndis;

        /* Profile receive completions */
        profile_start ( &acm_in_profiler );

        /* Ignore packets cancelled when the endpoint closes */
        if ( ! ep->open )
                goto ignore;

        /* Record USB errors against the RNDIS device */
        if ( rc != 0 ) {
                DBGC ( acm, "ACM %p bulk IN failed: %s\n",
                       acm, strerror ( rc ) );
                goto error;
        }

        /* Hand off to RNDIS */
        rndis_rx ( rndis, iob_disown ( iobuf ) );

        profile_stop ( &acm_in_profiler );
        return;

 error:
        rndis_rx_err ( rndis, iob_disown ( iobuf ), rc );
 ignore:
        free_iob ( iobuf );
}
static int acm_out_transmit ( struct acm_device acm,
struct io_buffer iobuf 
) [static]

Transmit packet.

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

Definition at line 190 of file acm.c.

References usbnet_device::out, profile_start(), profile_stop(), rc, usb_stream(), and acm_device::usbnet.

Referenced by acm_transmit().

                                                        {
        int rc;

        /* Profile transmissions */
        profile_start ( &acm_out_profiler );

        /* Enqueue I/O buffer */
        if ( ( rc = usb_stream ( &acm->usbnet.out, iobuf, 0 ) ) != 0 )
                return rc;

        profile_stop ( &acm_out_profiler );
        return 0;
}
static void acm_out_complete ( struct usb_endpoint ep,
struct io_buffer iobuf,
int  rc 
) [static]

Complete bulk OUT transfer.

Parameters:
epUSB endpoint
iobufI/O buffer
rcCompletion status code

Definition at line 212 of file acm.c.

References container_of, usbnet_device::out, acm_device::rndis, rndis_tx_complete_err(), and acm_device::usbnet.

                                        {
        struct acm_device *acm = container_of ( ep, struct acm_device,
                                                usbnet.out );
        struct rndis_device *rndis = acm->rndis;

        /* Report TX completion */
        rndis_tx_complete_err ( rndis, iobuf, rc );
}
static int acm_control_transmit ( struct acm_device acm,
struct io_buffer iobuf 
) [static]

Send control packet.

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

Definition at line 241 of file acm.c.

References cdc_send_encapsulated_command(), usbnet_device::comms, io_buffer::data, DBGC, iob_len(), rc, acm_device::rndis, rndis_tx_complete(), strerror(), acm_device::usb, and acm_device::usbnet.

Referenced by acm_transmit().

                                                            {
        struct rndis_device *rndis = acm->rndis;
        struct usb_device *usb = acm->usb;
        int rc;

        /* Send packet as an encapsulated command */
        if ( ( rc = cdc_send_encapsulated_command ( usb, acm->usbnet.comms,
                                                    iobuf->data,
                                                    iob_len ( iobuf ) ) ) != 0){
                DBGC ( acm, "ACM %p could not send encapsulated command: %s\n",
                       acm, strerror ( rc ) );
                return rc;
        }

        /* Complete packet immediately */
        rndis_tx_complete ( rndis, iobuf );

        return 0;
}
static int acm_control_receive ( struct acm_device acm) [static]

Receive control packet.

Parameters:
acmUSB RNDIS device
Return values:
rcReturn status code

Definition at line 268 of file acm.c.

References ACM_RESPONSE_MTU, alloc_iob(), cdc_get_encapsulated_response(), usbnet_device::comms, io_buffer::data, DBGC, DBGC_HDA, ENOMEM, EPROTO, free_iob(), header, iob_disown, iob_put, le32_to_cpu, rndis_header::len, len, mtu, rc, acm_device::rndis, rndis_rx(), strerror(), acm_device::usb, and acm_device::usbnet.

Referenced by acm_poll().

                                                          {
        struct rndis_device *rndis = acm->rndis;
        struct usb_device *usb = acm->usb;
        struct io_buffer *iobuf;
        struct rndis_header *header;
        size_t mtu = ACM_RESPONSE_MTU;
        size_t len;
        int rc;

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

        /* Get encapsulated response */
        if ( ( rc = cdc_get_encapsulated_response ( usb, acm->usbnet.comms,
                                                    iobuf->data, mtu ) ) != 0 ){
                DBGC ( acm, "ACM %p could not get encapsulated response: %s\n",
                       acm, strerror ( rc ) );
                goto err_get_response;
        }

        /* Fix up buffer length */
        header = iobuf->data;
        len = le32_to_cpu ( header->len );
        if ( len > mtu ) {
                DBGC ( acm, "ACM %p overlength encapsulated response\n", acm );
                DBGC_HDA ( acm, 0, iobuf->data, mtu );
                rc = -EPROTO;
                goto err_len;
        }
        iob_put ( iobuf, len );

        /* Hand off to RNDIS */
        rndis_rx ( rndis, iob_disown ( iobuf ) );

        return 0;

 err_len:
 err_get_response:
        free_iob ( iobuf );
 err_alloc:
        return rc;
}
static int acm_open ( struct rndis_device rndis) [static]

Open RNDIS device.

Parameters:
rndisRNDIS device
Return values:
rcReturn status code

Definition at line 328 of file acm.c.

References rndis_device::priv, rc, acm_device::usbnet, usbnet_close(), and usbnet_open().

                                                   {
        struct acm_device *acm = rndis->priv;
        int rc;

        /* Open USB network device */
        if ( ( rc = usbnet_open ( &acm->usbnet ) ) != 0 )
                goto err_open;

        return 0;

        usbnet_close ( &acm->usbnet );
 err_open:
        return rc;
}
static void acm_close ( struct rndis_device rndis) [static]

Close RNDIS device.

Parameters:
rndisRNDIS device

Definition at line 348 of file acm.c.

References rndis_device::priv, acm_device::usbnet, and usbnet_close().

                                                     {
        struct acm_device *acm = rndis->priv;

        /* Close USB network device */
        usbnet_close ( &acm->usbnet );
}
static int acm_transmit ( struct rndis_device rndis,
struct io_buffer iobuf 
) [static]

Transmit packet.

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

Definition at line 362 of file acm.c.

References acm_control_transmit(), acm_out_transmit(), assert, cpu_to_le32, io_buffer::data, header, iob_len(), le32_to_cpu, rndis_header::len, rndis_device::priv, RNDIS_PACKET_MSG, and rndis_header::type.

                                                    {
        struct acm_device *acm = rndis->priv;
        struct rndis_header *header = iobuf->data;

        /* Sanity check */
        assert ( iob_len ( iobuf ) >= sizeof ( *header ) );
        assert ( iob_len ( iobuf ) == le32_to_cpu ( header->len ) );

        /* Transmit packet via appropriate mechanism */
        if ( header->type == cpu_to_le32 ( RNDIS_PACKET_MSG ) ) {
                return acm_out_transmit ( acm, iobuf );
        } else {
                return acm_control_transmit ( acm, iobuf );
        }
}
static void acm_poll ( struct rndis_device rndis) [static]

Poll for completed and received packets.

Parameters:
rndisRNDIS device

Definition at line 384 of file acm.c.

References acm_control_receive(), acm_device::bus, NULL, rndis_device::priv, rc, acm_device::responded, rndis_rx_err(), usb_poll(), acm_device::usbnet, and usbnet_refill().

                                                    {
        struct acm_device *acm = rndis->priv;
        int rc;

        /* Poll USB bus */
        usb_poll ( acm->bus );

        /* Refill rings */
        if ( ( rc = usbnet_refill ( &acm->usbnet ) ) != 0 )
                rndis_rx_err ( rndis, NULL, rc );

        /* Retrieve encapsulated response, if applicable */
        if ( acm->responded ) {

                /* Clear flag */
                acm->responded = 0;

                /* Get encapsulated response */
                if ( ( rc = acm_control_receive ( acm ) ) != 0 )
                        rndis_rx_err ( rndis, NULL, rc );
        }
}
static int acm_probe ( struct usb_function func,
struct usb_configuration_descriptor config 
) [static]

Probe device.

Parameters:
funcUSB function
configConfiguration descriptor
Return values:
rcReturn status code

Definition at line 429 of file acm.c.

References ACM_IN_MAX_FILL, ACM_IN_MTU, ACM_INTR_MAX_FILL, alloc_rndis(), acm_device::bus, usb_hub::bus, DBGC, net_device::dev, usb_function::dev, ENOMEM, free_rndis(), usb_port::hub, usbnet_device::in, usbnet_device::intr, rndis_device::netdev, usb_device::port, rndis_device::priv, rc, register_rndis(), acm_device::rndis, rndis_init(), strerror(), unregister_rndis(), acm_device::usb, usb_function::usb, usb_func_set_drvdata(), usb_refill_init(), acm_device::usbnet, usbnet_describe(), and usbnet_init().

                                                                     {
        struct usb_device *usb = func->usb;
        struct rndis_device *rndis;
        struct acm_device *acm;
        int rc;

        /* Allocate and initialise structure */
        rndis = alloc_rndis ( sizeof ( *acm ) );
        if ( ! rndis ) {
                rc = -ENOMEM;
                goto err_alloc;
        }
        rndis_init ( rndis, &acm_operations );
        rndis->netdev->dev = &func->dev;
        acm = rndis->priv;
        acm->usb = usb;
        acm->bus = usb->port->hub->bus;
        acm->rndis = rndis;
        usbnet_init ( &acm->usbnet, func, &acm_intr_operations,
                      &acm_in_operations, &acm_out_operations );
        usb_refill_init ( &acm->usbnet.intr, 0, 0, ACM_INTR_MAX_FILL );
        usb_refill_init ( &acm->usbnet.in, 0, ACM_IN_MTU, ACM_IN_MAX_FILL );

        /* Describe USB network device */
        if ( ( rc = usbnet_describe ( &acm->usbnet, config ) ) != 0 ) {
                DBGC ( acm, "ACM %p could not describe: %s\n",
                       acm, strerror ( rc ) );
                goto err_describe;
        }

        /* Register RNDIS device */
        if ( ( rc = register_rndis ( rndis ) ) != 0 )
                goto err_register;

        usb_func_set_drvdata ( func, acm );
        return 0;

        unregister_rndis ( rndis );
 err_register:
 err_describe:
        free_rndis ( rndis );
 err_alloc:
        return rc;
}
static void acm_remove ( struct usb_function func) [static]

Remove device.

Parameters:
funcUSB function

Definition at line 480 of file acm.c.

References free_rndis(), acm_device::rndis, unregister_rndis(), and usb_func_get_drvdata().

                                                     {
        struct acm_device *acm = usb_func_get_drvdata ( func );
        struct rndis_device *rndis = acm->rndis;

        /* Unregister RNDIS device */
        unregister_rndis ( rndis );

        /* Free RNDIS device */
        free_rndis ( rndis );
}

Variable Documentation

struct profiler acm_out_profiler __profiler [static]
Initial value:
        { .name = "acm.intr" }

Interrupt completion profiler.

Bulk OUT profiler.

Bulk IN completion profiler.

Definition at line 43 of file acm.c.

Initial value:
 {
        .complete = acm_intr_complete,
}

Interrupt endpoint operations.

Definition at line 128 of file acm.c.

Initial value:
 {
        .complete = acm_in_complete,
}

Bulk IN endpoint operations.

Definition at line 179 of file acm.c.

Initial value:
 {
        .complete = acm_out_complete,
}

Bulk OUT endpoint operations.

Definition at line 223 of file acm.c.

Initial value:
 {
        .open           = acm_open,
        .close          = acm_close,
        .transmit       = acm_transmit,
        .poll           = acm_poll,
}

USB RNDIS operations.

Definition at line 408 of file acm.c.

struct usb_device_id cdc_acm_ids[] [static]
Initial value:
 {
        {
                .name = "cdc-acm",
                .vendor = USB_ANY_ID,
                .product = USB_ANY_ID,
        },
}

USB CDC-ACM device IDs.

Definition at line 492 of file acm.c.

struct usb_driver rf_rndis_driver __usb_driver
Initial value:
 {
        .ids = cdc_acm_ids,
        .id_count = ( sizeof ( cdc_acm_ids ) / sizeof ( cdc_acm_ids[0] ) ),
        .class = USB_CLASS_ID ( USB_CLASS_CDC, USB_SUBCLASS_CDC_ACM,
                                USB_PROTOCOL_ACM_RNDIS ),
        .score = USB_SCORE_DEPRECATED,
        .probe = acm_probe,
        .remove = acm_remove,
}

USB CDC-ACM driver.

USB RF-RNDIS driver.

Definition at line 501 of file acm.c.

struct usb_device_id rf_rndis_ids[] [static]
Initial value:
 {
        {
                .name = "rf-rndis",
                .vendor = USB_ANY_ID,
                .product = USB_ANY_ID,
        },
}

USB RF-RNDIS device IDs.

Definition at line 512 of file acm.c.