AI智能
改变未来

Linux USB ECM Gadget 驱动介绍

​1USBECM介绍

USB ECM,属于USB-IF定义的CDC(Communication Device Class)下的一个子类:Ethernet Networking Control Model,用于Host和Device之间交换以太网帧。下图是从USB ECM规范中截取:

2 关键描述符解析

用USB tool抓取ECM设备的描述符,部分关键描述符如下。

首先是IAD描述符。

IAD Descriptor : Interface AssociationDescriptor,接口关联描述符,将多个接口组合在一起。

bDescriptorType : 0x0B 表示描述符类型是IAD描述符。

bInterfaceCount : 0x02表示组合的接口数目是2个。

bFunctionClass : 0x02表示CDC class。

bFunctionSubClass : 0x06表示ECM subclass.

接下来是接口0的描述符,接口0用作ECM的control接口。

Interface Descriptor : 接口描述符

bInterfaceNumber : 0x00 标识该接口为接口0

bAlternateSetting : 0x00 如果同一个接口有多个描述符设置,那该值就用来区分是哪个

bNumEndpoints: 0x01表示该接口使用1个端点

bInterfaceClass : 0x02 表示CDC class

bInterfaceSubClass: 0x06 表示ECM subclass

bInterfaceProtocol : 0x00 表示使用标准协议

以下三个CDC Interface Descriptor属于functional descriptor,functional descriptor用来描述class-specific的信息,从属于某个标准接口描述符下。

HeaderFunctional Descriptor,CDC class-specific的描述符必须以这个描述符作为开头。

UnionFunctional Descriptor,包含控制接口信息。

EthernetNetworking Functional Descriptor,包含网卡的信息,比如MAC地址、统计能力等。其中MAC地址是通过字符串index来间接表示的,位于该描述符第4个字节,这里是06,表示String Descriptor 6中存放了MAC地址。

接口0的端点描述符,使用IN-2端点,端点方向为IN(Device->Host),中断传输方式。

接下来是接口1的描述符,大部分字段的意义和接口0的描述符类似,因此不再重复解释。接口1用作ECM的data接口。

接口1用做ECM的data接口。分配了两个端点。

接口1的端点描述符,使用了IN-1端点,传输类型为Bulk。

接口1的另一个端点描述符,使用了OUT-1端点,传输类型为Bulk。

接下来是字串描述符,这里只截取了字串6,也就是存放MAC地址的字串。

3 数据通路

Device ->Host:

在ECM Gadget驱动中,USB角色是device,在本地注册一个以太网卡设备,网络协议栈发送数据到该网卡,该网卡驱动会将数据以USB传输的方式发送到主机。Host端有ECM Host驱动,也会在Host端注册一个以太网卡,收到USB传输过来的数据后,网卡会将数据上报给Host端的网络协议栈。

Host -> Devcie:

Host端网络协议栈把数据发给ECM Host驱动,ECM Host驱动以USB传输的方式将数据发送给Device,Device端网卡收到数据后,上报给Device端网络协议栈。

4 驱动流程

源码位置在:

drivers\\usb\\gadget\\function\\f_ecm.c

drivers\\usb\\gadget\\function\\u_ether.c

4.1驱动的注册

注册function到USB gadget驱动框架中。

DECLARE_USB_FUNCTION_INIT(ecm, ecm_alloc_inst, ecm_alloc);

  

4.2USB function的实现

  • ecm_alloc_inst函数主要是调用gether_setup_default创建一个net设备。

opts->net = gether_setup_default();

  

  • ecm_alloc函数主要做两件事:

一是从net设备中获取该网卡host mac地址并记录下来,后续bind时会添加到描述符中。

status = gether_get_host_addr_cdc(opts->net, ecm->ethaddr,sizeof(ecm->ethaddr));

  

二是按USB function driver框架注册各回调函数。

ecm->port.func.name = \"cdc_ethernet\";/* descriptors are per-instance copies */ecm->port.func.bind = ecm_bind;ecm->port.func.unbind = ecm_unbind;ecm->port.func.set_alt = ecm_set_alt;ecm->port.func.get_alt = ecm_get_alt;ecm->port.func.setup = ecm_setup;ecm->port.func.disable = ecm_disable;ecm->port.func.free_func = ecm_free;

  

  • ecm_bind函数主要完成USB bind的过程:

一是将net设备和gadget关联,并注册net设备。

if (!ecm_opts->bound) {mutex_lock(&ecm_opts->lock);gether_set_gadget(ecm_opts->net, cdev->gadget);status = gether_register_netdev(ecm_opts->net);mutex_unlock(&ecm_opts->lock);if (status)return status;ecm_opts->bound = true;}

  

二是处理字符串描述符。

ecm_string_defs[1].s = ecm->ethaddr;​us = usb_gstrings_attach(cdev, ecm_strings,ARRAY_SIZE(ecm_string_defs));if (IS_ERR(us))return PTR_ERR(us);ecm_control_intf.iInterface = us[0].id;ecm_data_intf.iInterface = us[2].id;ecm_desc.iMACAddress = us[1].id;ecm_iad_descriptor.iFunction = us[3].id;

  

三是分配interface,并将interface信息更新到描述符中。

/* allocate instance-specific interface IDs */status = usb_interface_id(c, f);if (status < 0)goto fail;ecm->ctrl_id = status;ecm_iad_descriptor.bFirstInterface = status;​ecm_control_intf.bInterfaceNumber = status;ecm_union_desc.bMasterInterface0 = status;​status = usb_interface_id(c, f);if (status < 0)goto fail;ecm->data_id = status;​ecm_data_nop_intf.bInterfaceNumber = status;ecm_data_intf.bInterfaceNumber = status;ecm_union_desc.bSlaveInterface0 = status;

  

四是分配端点。

/* allocate instance-specific endpoints */ep = usb_ep_autoconfig(cdev->gadget, &fs_ecm_in_desc);if (!ep)goto fail;ecm->port.in_ep = ep;​ep = usb_ep_autoconfig(cdev->gadget, &fs_ecm_out_desc);if (!ep)goto fail;ecm->port.out_ep = ep;ep = usb_ep_autoconfig(cdev->gadget, &fs_ecm_notify_desc);if (!ep)goto fail;ecm->notify = ep;

  

五是分配描述符。

status = usb_assign_descriptors(f, ecm_fs_function, ecm_hs_function,ecm_ss_function, NULL);

  

4.3网卡部分的实现

  • 网卡设备操作函数集

static const struct net_device_ops eth_netdev_ops = {.ndo_open    = eth_open,.ndo_stop    = eth_stop,.ndo_start_xmit    = eth_start_xmit,.ndo_set_mac_address   = eth_mac_addr,.ndo_validate_addr  = eth_validate_addr,};

  

  • 分配网卡设备

static inline struct net_device *gether_setup_default(void){return gether_setup_name_default(\"usb\");}

  

  • 注册网卡设备

int gether_register_netdev(struct net_device *net)

  

  • 数据的收发

static netdev_tx_t eth_start_xmit(struct sk_buff *skb,struct net_device *net)static void rx_fill(struct eth_dev *dev, gfp_t gfp_flags)static void process_rx_w(struct work_struct *work)static void process_tx_w(struct work_struct *w)

  

以上就是对Linux USB ECM Gadget驱动的介绍,谢谢阅读。

文章会在公众号“大鱼嵌入式”同步发布,欢迎关注,一起交流。

赞(0) 打赏
未经允许不得转载:爱站程序员基地 » Linux USB ECM Gadget 驱动介绍