iPXE
Functions | Variables
ecm.c File Reference

CDC-ECM USB Ethernet driver. More...

#include <stdint.h>
#include <errno.h>
#include <ipxe/netdevice.h>
#include <ipxe/ethernet.h>
#include <ipxe/if_ether.h>
#include <ipxe/base16.h>
#include <ipxe/profile.h>
#include <ipxe/usb.h>
#include "ecm.h"

Go to the source code of this file.

Functions

 FILE_LICENCE (GPL2_OR_LATER_OR_UBDL)
struct ecm_ethernet_descriptorecm_ethernet_descriptor (struct usb_configuration_descriptor *config, struct usb_interface_descriptor *interface)
 Locate Ethernet functional descriptor.
int ecm_fetch_mac (struct usb_device *usb, struct ecm_ethernet_descriptor *desc, uint8_t *hw_addr)
 Get hardware MAC address.
static void ecm_intr_complete (struct usb_endpoint *ep, struct io_buffer *iobuf, int rc)
 Complete interrupt transfer.
static void ecm_in_complete (struct usb_endpoint *ep, struct io_buffer *iobuf, int rc)
 Complete bulk IN transfer.
static int ecm_out_transmit (struct ecm_device *ecm, struct io_buffer *iobuf)
 Transmit packet.
static void ecm_out_complete (struct usb_endpoint *ep, struct io_buffer *iobuf, int rc)
 Complete bulk OUT transfer.
static int ecm_open (struct net_device *netdev)
 Open network device.
static void ecm_close (struct net_device *netdev)
 Close network device.
static int ecm_transmit (struct net_device *netdev, struct io_buffer *iobuf)
 Transmit packet.
static void ecm_poll (struct net_device *netdev)
 Poll for completed and received packets.
static int ecm_probe (struct usb_function *func, struct usb_configuration_descriptor *config)
 Probe device.
static void ecm_remove (struct usb_function *func)
 Remove device.

Variables

static struct profiler
ecm_intr_profiler 
__profiler
 Interrupt completion profiler.
static struct
usb_endpoint_driver_operations 
ecm_intr_operations
 Interrupt endpoint operations.
static struct
usb_endpoint_driver_operations 
ecm_in_operations
 Bulk IN endpoint operations.
static struct
usb_endpoint_driver_operations 
ecm_out_operations
 Bulk OUT endpoint operations.
static struct net_device_operations ecm_operations
 CDC-ECM network device operations.
static struct usb_device_id ecm_ids []
 CDC-ECM device IDs.
struct usb_driver ecm_driver __usb_driver
 CDC-ECM driver.

Detailed Description

CDC-ECM USB Ethernet driver.

Definition in file ecm.c.


Function Documentation

FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL  )
struct ecm_ethernet_descriptor* ecm_ethernet_descriptor ( struct usb_configuration_descriptor config,
struct usb_interface_descriptor interface 
) [read]

Locate Ethernet functional descriptor.

Parameters:
configConfiguration descriptor
interfaceInterface descriptor
Return values:
descDescriptor, or NULL if not found

Definition at line 69 of file ecm.c.

References CDC_SUBTYPE_ETHERNET, for_each_interface_descriptor, ecm_ethernet_descriptor::header, NULL, ecm_ethernet_descriptor::subtype, usb_descriptor_header::type, and USB_CS_INTERFACE_DESCRIPTOR.

Referenced by ecm_probe(), and ncm_probe().

                                                                       {
        struct ecm_ethernet_descriptor *desc;

        for_each_interface_descriptor ( desc, config, interface ) {
                if ( ( desc->header.type == USB_CS_INTERFACE_DESCRIPTOR ) &&
                     ( desc->subtype == CDC_SUBTYPE_ETHERNET ) )
                        return desc;
        }
        return NULL;
}
int ecm_fetch_mac ( struct usb_device usb,
struct ecm_ethernet_descriptor desc,
uint8_t hw_addr 
)

Get hardware MAC address.

Parameters:
usbUSB device
descEthernet functional descriptor
hw_addrHardware address to fill in
Return values:
rcReturn status code

Definition at line 89 of file ecm.c.

References base16_encoded_len(), DBGC, EINVAL, ETH_ALEN, len, ecm_ethernet_descriptor::mac, usb_device::name, rc, strerror(), and usb_get_string_descriptor().

Referenced by ecm_probe(), and ncm_probe().

                                                                             {
        char buf[ base16_encoded_len ( ETH_ALEN ) + 1 /* NUL */ ];
        int len;
        int rc;

        /* Fetch MAC address string */
        len = usb_get_string_descriptor ( usb, desc->mac, 0, buf,
                                          sizeof ( buf ) );
        if ( len < 0 ) {
                rc = len;
                return rc;
        }

        /* Sanity check */
        if ( len != ( ( int ) ( sizeof ( buf ) - 1 /* NUL */ ) ) ) {
                DBGC ( usb, "USB %s has invalid ECM MAC \"%s\"\n",
                       usb->name, buf );
                return -EINVAL;
        }

        /* Decode MAC address */
        len = base16_decode ( buf, hw_addr, ETH_ALEN );
        if ( len < 0 ) {
                rc = len;
                DBGC ( usb, "USB %s could not decode ECM MAC \"%s\": %s\n",
                       usb->name, buf, strerror ( rc ) );
                return rc;
        }

        return 0;
}
static void ecm_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 136 of file ecm.c.

References CDC_CONNECTION_SPEED_CHANGE, CDC_NETWORK_CONNECTION, container_of, cpu_to_le16, io_buffer::data, DBGC, DBGC_HDA, EINVAL, ENOTSUP, error, free_iob(), usbnet_device::intr, iob_disown, iob_len(), len, message, netdev, ecm_device::netdev, netdev_link_down(), netdev_link_ok(), netdev_link_up(), netdev_rx_err(), usb_endpoint::open, profile_start(), profile_stop(), usb_setup_packet::request, strerror(), ecm_device::usbnet, and usb_setup_packet::value.

                                                                  {
        struct ecm_device *ecm = container_of ( ep, struct ecm_device,
                                                usbnet.intr );
        struct net_device *netdev = ecm->netdev;
        struct usb_setup_packet *message;
        size_t len = iob_len ( iobuf );

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

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

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

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

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

        case cpu_to_le16 ( CDC_NETWORK_CONNECTION ) :
                if ( message->value && ! netdev_link_ok ( netdev ) ) {
                        DBGC ( ecm, "ECM %p link up\n", ecm );
                        netdev_link_up ( netdev );
                } else if ( netdev_link_ok ( netdev ) && ! message->value ) {
                        DBGC ( ecm, "ECM %p link down\n", ecm );
                        netdev_link_down ( netdev );
                }
                break;

        case cpu_to_le16 ( CDC_CONNECTION_SPEED_CHANGE ) :
                /* Ignore */
                break;

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

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

        return;

 error:
        netdev_rx_err ( netdev, iob_disown ( iobuf ), rc );
 ignore:
        free_iob ( iobuf );
        return;
}
static void ecm_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 224 of file ecm.c.

References container_of, DBGC, error, free_iob(), usbnet_device::in, iob_disown, netdev, ecm_device::netdev, netdev_rx(), netdev_rx_err(), usb_endpoint::open, profile_start(), profile_stop(), strerror(), and ecm_device::usbnet.

                                       {
        struct ecm_device *ecm = container_of ( ep, struct ecm_device,
                                                usbnet.in );
        struct net_device *netdev = ecm->netdev;

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

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

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

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

        profile_stop ( &ecm_in_profiler );
        return;

 error:
        netdev_rx_err ( netdev, iob_disown ( iobuf ), rc );
 ignore:
        free_iob ( iobuf );
}
static int ecm_out_transmit ( struct ecm_device ecm,
struct io_buffer iobuf 
) [static]

Transmit packet.

Parameters:
ecmCDC-ECM device
iobufI/O buffer
Return values:
rcReturn status code

Definition at line 268 of file ecm.c.

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

Referenced by ecm_transmit().

                                                        {
        int rc;

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

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

        profile_stop ( &ecm_out_profiler );
        return 0;
}
static void ecm_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 290 of file ecm.c.

References container_of, netdev, ecm_device::netdev, netdev_tx_complete_err(), usbnet_device::out, and ecm_device::usbnet.

                                        {
        struct ecm_device *ecm = container_of ( ep, struct ecm_device,
                                                usbnet.out );
        struct net_device *netdev = ecm->netdev;

        /* Report TX completion */
        netdev_tx_complete_err ( netdev, iobuf, rc );
}
static int ecm_open ( struct net_device netdev) [static]

Open network device.

Parameters:
netdevNetwork device
Return values:
rcReturn status code

Definition at line 318 of file ecm.c.

References usbnet_device::comms, DBGC, ECM_PACKET_TYPE_ALL_MULTICAST, ECM_PACKET_TYPE_BROADCAST, ECM_PACKET_TYPE_DIRECTED, ECM_PACKET_TYPE_PROMISCUOUS, ECM_SET_ETHERNET_PACKET_FILTER, filter, NULL, net_device::priv, rc, strerror(), ecm_device::usb, usb_control(), ecm_device::usbnet, usbnet_close(), and usbnet_open().

                                                  {
        struct ecm_device *ecm = netdev->priv;
        struct usb_device *usb = ecm->usb;
        unsigned int filter;
        int rc;

        /* Open USB network device */
        if ( ( rc = usbnet_open ( &ecm->usbnet ) ) != 0 ) {
                DBGC ( ecm, "ECM %p could not open: %s\n",
                       ecm, strerror ( rc ) );
                goto err_open;
        }

        /* Set packet filter */
        filter = ( ECM_PACKET_TYPE_PROMISCUOUS |
                   ECM_PACKET_TYPE_ALL_MULTICAST |
                   ECM_PACKET_TYPE_DIRECTED |
                   ECM_PACKET_TYPE_BROADCAST );
        if ( ( rc = usb_control ( usb, ECM_SET_ETHERNET_PACKET_FILTER,
                                  filter, ecm->usbnet.comms, NULL, 0 ) ) != 0 ){
                DBGC ( ecm, "ECM %p could not set packet filter: %s\n",
                       ecm, strerror ( rc ) );
                goto err_set_filter;
        }

        return 0;

 err_set_filter:
        usbnet_close ( &ecm->usbnet );
 err_open:
        return rc;
}
static void ecm_close ( struct net_device netdev) [static]

Close network device.

Parameters:
netdevNetwork device

Definition at line 356 of file ecm.c.

References net_device::priv, ecm_device::usbnet, and usbnet_close().

                                                    {
        struct ecm_device *ecm = netdev->priv;

        /* Close USB network device */
        usbnet_close ( &ecm->usbnet );
}
static int ecm_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 370 of file ecm.c.

References ecm_out_transmit(), net_device::priv, and rc.

                                                    {
        struct ecm_device *ecm = netdev->priv;
        int rc;

        /* Transmit packet */
        if ( ( rc = ecm_out_transmit ( ecm, iobuf ) ) != 0 )
                return rc;

        return 0;
}
static void ecm_poll ( struct net_device netdev) [static]

Poll for completed and received packets.

Parameters:
netdevNetwork device

Definition at line 387 of file ecm.c.

References ecm_device::bus, netdev_rx_err(), NULL, net_device::priv, rc, usb_poll(), ecm_device::usbnet, and usbnet_refill().

                                                   {
        struct ecm_device *ecm = netdev->priv;
        int rc;

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

        /* Refill endpoints */
        if ( ( rc = usbnet_refill ( &ecm->usbnet ) ) != 0 )
                netdev_rx_err ( netdev, NULL, rc );
}
static int ecm_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 421 of file ecm.c.

References alloc_etherdev(), assert, ecm_device::bus, usb_hub::bus, usbnet_device::comms, DBGC, net_device::dev, usb_function::dev, ecm_ethernet_descriptor(), ecm_fetch_mac(), ECM_IN_MAX_FILL, ECM_IN_MTU, ECM_INTR_MAX_FILL, EINVAL, ENOMEM, usb_port::hub, net_device::hw_addr, usbnet_device::in, usbnet_device::intr, memset(), usb_function::name, netdev, ecm_device::netdev, netdev_init(), netdev_nullify(), netdev_put(), NULL, usb_device::port, net_device::priv, rc, register_netdev(), strerror(), unregister_netdev(), ecm_device::usb, usb_function::usb, usb_func_set_drvdata(), usb_interface_descriptor(), usb_refill_init(), ecm_device::usbnet, usbnet_describe(), and usbnet_init().

                                                                     {
        struct usb_device *usb = func->usb;
        struct net_device *netdev;
        struct ecm_device *ecm;
        struct usb_interface_descriptor *comms;
        struct ecm_ethernet_descriptor *ethernet;
        int rc;

        /* Allocate and initialise structure */
        netdev = alloc_etherdev ( sizeof ( *ecm ) );
        if ( ! netdev ) {
                rc = -ENOMEM;
                goto err_alloc;
        }
        netdev_init ( netdev, &ecm_operations );
        netdev->dev = &func->dev;
        ecm = netdev->priv;
        memset ( ecm, 0, sizeof ( *ecm ) );
        ecm->usb = usb;
        ecm->bus = usb->port->hub->bus;
        ecm->netdev = netdev;
        usbnet_init ( &ecm->usbnet, func, &ecm_intr_operations,
                      &ecm_in_operations, &ecm_out_operations );
        usb_refill_init ( &ecm->usbnet.intr, 0, 0, ECM_INTR_MAX_FILL );
        usb_refill_init ( &ecm->usbnet.in, 0, ECM_IN_MTU, ECM_IN_MAX_FILL );
        DBGC ( ecm, "ECM %p on %s\n", ecm, func->name );

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

        /* Locate Ethernet descriptor */
        comms = usb_interface_descriptor ( config, ecm->usbnet.comms, 0 );
        assert ( comms != NULL );
        ethernet = ecm_ethernet_descriptor ( config, comms );
        if ( ! ethernet ) {
                DBGC ( ecm, "ECM %p has no Ethernet descriptor\n", ecm );
                rc = -EINVAL;
                goto err_ethernet;
        }

        /* Fetch MAC address */
        if ( ( rc = ecm_fetch_mac ( usb, ethernet, netdev->hw_addr ) ) != 0 ) {
                DBGC ( ecm, "ECM %p could not fetch MAC address: %s\n",
                       ecm, strerror ( rc ) );
                goto err_fetch_mac;
        }

        /* Register network device */
        if ( ( rc = register_netdev ( netdev ) ) != 0 )
                goto err_register;

        usb_func_set_drvdata ( func, ecm );
        return 0;

        unregister_netdev ( netdev );
 err_register:
 err_fetch_mac:
 err_ethernet:
 err_describe:
        netdev_nullify ( netdev );
        netdev_put ( netdev );
 err_alloc:
        return rc;
}
static void ecm_remove ( struct usb_function func) [static]

Remove device.

Parameters:
funcUSB function

Definition at line 496 of file ecm.c.

References netdev, ecm_device::netdev, netdev_nullify(), netdev_put(), unregister_netdev(), and usb_func_get_drvdata().

                                                     {
        struct ecm_device *ecm = usb_func_get_drvdata ( func );
        struct net_device *netdev = ecm->netdev;

        unregister_netdev ( netdev );
        netdev_nullify ( netdev );
        netdev_put ( netdev );
}

Variable Documentation

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

Interrupt completion profiler.

Bulk OUT profiler.

Bulk IN completion profiler.

Definition at line 43 of file ecm.c.

Initial value:
 {
        .complete = ecm_intr_complete,
}

Interrupt endpoint operations.

Definition at line 206 of file ecm.c.

Initial value:
 {
        .complete = ecm_in_complete,
}

Bulk IN endpoint operations.

Definition at line 257 of file ecm.c.

Initial value:
 {
        .complete = ecm_out_complete,
}

Bulk OUT endpoint operations.

Definition at line 301 of file ecm.c.

Initial value:
 {
        .open           = ecm_open,
        .close          = ecm_close,
        .transmit       = ecm_transmit,
        .poll           = ecm_poll,
}

CDC-ECM network device operations.

Definition at line 400 of file ecm.c.

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

CDC-ECM device IDs.

Definition at line 506 of file ecm.c.

struct usb_driver ecm_driver __usb_driver
Initial value:
 {
        .ids = ecm_ids,
        .id_count = ( sizeof ( ecm_ids ) / sizeof ( ecm_ids[0] ) ),
        .class = USB_CLASS_ID ( USB_CLASS_CDC, USB_SUBCLASS_CDC_ECM, 0 ),
        .score = USB_SCORE_NORMAL,
        .probe = ecm_probe,
        .remove = ecm_remove,
}

CDC-ECM driver.

Definition at line 515 of file ecm.c.