Linux USB ECM Gadget 驱动介绍
1 USB ECM介绍
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.2 USB 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驱动的介绍,谢谢阅读。
文章会在公众号“大鱼嵌入式”同步发布,欢迎关注,一起交流。

Linux USB ECM Gadget 驱动介绍的更多相关文章
- Linux USB 鼠标输入驱动具体解释
平台:mini2440 内核:linux 2.6.32.2 USB设备插入时.内核会读取设备信息,接着就把id_table里的信息与读取到的信息做比較.看是否匹配,假设匹配.就调用probe函数. U ...
- [置顶] 自娱自乐6之Linux gadget驱动5(自编gadget驱动,包涵与之通讯的主机usb驱动,已调试通过)
这个代码调试,你首先要保证你的udc驱动没用问题,这个有些矛盾,应为我本来要用gadget驱动来调试udc驱动,结果反过来了. 这是在zero基础改的,大概的改动 1. 去掉loop. 2. sink ...
- Linux usb gadget框架概述
很幸运,在公司开发了gadget相关驱动,总结下来,大大小小开发了四个与gadget相关的驱动,字符驱动.g_multi.g_ether.g_zero,在这里把自己对gadget的开发中自己的感悟记录 ...
- Linux主机USB RNDIS网卡驱动实现不完整导致的一例问题
某通信模块设备,通过USB提供RDNIS和ECM网卡功能.在实际应用中发现,USB RNDIS网卡模式下,当使用AT指令以不同的CID拨号的时候,在Windows主机上能正常拨号成功,但在Linux主 ...
- Linux USB驱动
linux usb 驱动详解 一 http://blog.163.com/cl2006ky@126/blog/static/87195173201131245557340/ USB设备驱动开发-USB ...
- Linux USB摄像头驱动【转】
本文转载自:http://www.itdadao.com/articles/c15a509940p0.html 在 cortex-a8 中,可接入摄像头的接口通常可以分为两种, CAMERA 接口和 ...
- Linux usb子系统(三):通过usbfs操作设备的用户空间驱动
内核中提供了USB设备文件系统(usbdevfs,Linux 2.6改为usbfs,即USB文件系统),它和/proc类似,都是动态产生的.通过在/etc/fstab文件中添加如下一行:none /p ...
- Linux usb子系统(二):USB设备驱动usb-skeleton.c
usb驱动分为通过usbfs操作设备的用户空间驱动,内核空间的内核驱动.两者不能同时进行,否则容易引发对共享资源访问的问题,死锁!使用了内核驱动,就不能在usbfs里驱动该设备. 下面转载的一篇分析u ...
- Linux usb子系统(一) _写一个usb鼠标驱动
USB总线是一种典型的热插拔的总线标准,由于其优异的性能几乎成为了当下大小设备中的标配. USB的驱动可以分为3类:SoC的USB控制器的驱动,主机端USB设备的驱动,设备上的USB Gadget驱动 ...
随机推荐
- 使用C# (.NET Core) 实现装饰模式 (Decorator Pattern) 并介绍 .NET/Core的Stream
该文章综合了几本书的内容. 某咖啡店项目的解决方案 某咖啡店供应咖啡, 客户买咖啡的时候可以添加若干调味料, 最后要求算出总价钱. Beverage是所有咖啡饮料的抽象类, 里面的cost方法是抽象的 ...
- 从I/O多路复用到Netty,还要跨过Java NIO包
本文是Netty系列第4篇 上一篇文章我们深入了解了I/O多路复用的三种实现形式,select/poll/epoll. 那Netty是使用哪种实现的I/O多路复用呢?这个问题,得从Java NIO包说 ...
- 一文读懂MySql主从复制机制
作为一个关系型数据库,MySQL内建地提供数据复制机制,这使得在使用时,可以基于其复制机制实现高可用架构等高级特性,从而使得MySQL无需借助额外的插件或其他工具就具备适用于生产环境.这是MySQL得 ...
- kubernetes中有状态应用的优雅缩容
将有状态的应用程序部署到Kubernetes是棘手的. StatefulSet使它变得容易得多,但是它们仍然不能解决所有问题.最大的挑战之一是如何缩小StatefulSet而不将数据留在断开连接的Pe ...
- 自学PHP笔记(五) PHP运算符
本文转发来自:自学PHP笔记(五) PHP运算符 首先我们需要了解运算符是什么,运算符是指的对变量.常量或者数据进行计算的一个符号,比如数学中学到的加减乘除等用来运算的代表符号,PHP中的运算符也是这 ...
- YARP实现Dapr服务调用的反向代理
楔子 公司即将新开项目,打算用点时髦的技术,需要探探路.之前没做过微服务项目,没有技术栈方面的积(负)累(债), 干脆就上微软的分布式运行时Dapr......嗯......用来服务发现,然后等测试用 ...
- zipfile tarfile模块
zipfile --- 使用ZIP存档 这个模块提供了创建.读取.写入.添加及列出 ZIP 文件的工具 # 创建一个ZipFile对象, 可使用上下文管理 with class zipfile.Zip ...
- IOS Widget(1):概述
引言 本系列文章作者是安卓开发,以安卓开发的视角学习IOS小组件,记录一下踩坑记录,如有讲得不对的地方,路过大佬多包涵.如果你是想深入学习小组件,建议您顺着笔者的编号顺序阅读本系列文章.如果曾经了 ...
- 基于MATLAB的手写公式识别(10)
2公式分割 2.1投影分割法 12:23:00 完成水平和垂直两个方向上的投影 15:32:23 水平投影只投到水平方向的影,垂直投影只投到垂直方向上的影. 16:51:38 17:37:08 终其 ...
- hdu4876 深搜+(随机枚举剪枝)
题意: 给你n个数,让你从选择k个数,然后排成一个环(k个数的顺序随意,但是排成一个环后就不能变了),然后可以在这个环上任意的找连续w个数(w<=k),可以找多次,得到一个值等于当前 ...