Linux-USB Gadget : Part 1: 简介

简介之简介

Linux-USB Gadget 驱动框架(以下简称 Gadget)实现了USB 协议定义的设备端的软件功能。相对于 Linux USB主机端(Host) 驱动而言, Gadget 驱动出现较晚,它出现在2.4.23 以后, (作者的第一次 announce:http://lwn.net/Articles/27352/或 http://kerneltrap.org/node/621), 如果需要在老的 2.4 内核上支持 Gadget, 可以移植 2.4.31 代码,工作量较小。另外,2.6 内核标准版本里的可能比最新版本要老一些。 “mm” 补丁中的版本会比较新。

 

Gadget 框架提出了一套标准 API, 在底层, USB 设备控制器(USB Device Controller, UDC)驱动则实现这一套 API,不同的 UDC (通常是 SOC 的一部分) 需要不同的驱动, 甚至基于同样的 UDC 的不同板子也需要进行代码修改。这一层我们可以称之为平台相关层。

 

基于 API, Gadget 驱动实现了一套硬件无关的功能,这基本上可以对应到 USB 协议里 的各种 USB Class, 也有比如 USB Gadget Generic Serial 驱动,没有对应的 Class。当然,Gadget 驱动还是受限于底层提供的功能的。比如某些 Class 需要 USB Isochronous  端点,这时我们就不能支持该 Class。

 

普通的 Gadget 驱动只实现一个功能(比如, u 盘,usb 网卡)。复合设备可以支持多个功能,后面将仔细研究一下复合设备的实现。像智能手机, PDA这样的设备,硬件支持较丰富的端点、DMA Buffer, 给软件提了支持复合功能的基础。

 

有两点值得注意,第一是 usb gaget 驱动框架不象 usb 主机端有 usb core 的概念,usb 主机可能支持成百类型的外设,把通用功能抽象出来很有意义。Usb device 端则通常没有这个需求,一些通用功能抽象在一些 Helper 函数里就可以了。第二是 usb 2.0 里提出了 OTG 的概念,可以在同一接口上支持 host 以及 device 功能。OTG 是一个比较复杂的规范,以后有时间再研究。

 

控制器驱动

       常见的 usb device 有 U 盘, usb 鼠标、键盘,usb 蓝牙模块,usb 读卡器,等等。这些设备比较简单,通常不会运行Linux。运行Linux Gadget 的通常是一些集成 CPU 以及很多外设接口的 SOC (System-on-Chip), 其中 CPU通常为 32 bit 的 CPU, 并且 udc 也是该 SOC 的一部分( 顺带还有 DMA 通道,FIFO)。

       Linux 标准内核里支持各主流 SOC 的 udc 驱动,make menuconfig 一下可以看到具体列表,其中值得一提的是 dummy_hcd, 它是一个软件模拟的 udc, 在开发新的 gadget 驱动时很有帮助。

       控制器驱动处理很少的 USB 控制请求(主要由硬件负责的部分)。其它所有的控制请求,比如返回描述符,设置当前配置,由 Gadget Driver 完成。控制器驱动一个主要责任就是负责管理各个端点的 I/O 队列,在 Gadget Driver 的 buffer 和硬件buffer 之间传输数据(通常是通过 DMA)。

       我们前面提过,上层 Gadget 驱动能够实现什么功能要依赖底层提供的硬件条件。比如一个复合设备需要至少5 个端点,这些硬件特性通过一组 gadget_is_*()函数实现。

 

Gadget 驱动

基于底层提供的资源, Gadget 驱动可以运行在各种硬件平台上。重要的驱动有:

Ø         Gadget Zero, 类似于 dummy hcd, 该驱动用于测试 udc 驱动。它会帮助您通过 USB-IF 测试。

Ø         Ethernet over USB, 该驱动模拟以太网网口,它支持多种运行方式:

²        CDC Ethernet: usb 规范规定的 Communications Device Class “Ethernet Model” protocol。

²        CDC Subset: 对硬件要求最低的一种方式,主要是 Linux 主机支持该方式。

²        RNDIS: 微软公司对 CDC Ethernet 的变种实现。

Ø         File-backed Storage Gadget最常见的 U 盘功能实现。

Ø         Serial Gadget 实现,包括:

²        Generic Serial 实现(只需要Bulk-in/Bulk-out端点+ep0)

²        CDC ACM 规范实现。

Ø         Gadget Filesystem, 将 Gadget API 接口暴露给应用层,以便在应用层实现user mode driver。

Ø         MIDI: 暴露ALSA接口,提供 recording 以及 playback 功能。

前面讲过,gadget api 提供了usb device controller 驱动和上层gadget驱动交互的接口。 UDC 驱动是服务提供者,而各种 gadget 驱动则是服务的使用者。其实还有一些通用代码,因为功能比较简单,我们称之为 helpe 函数。在阅读了 Gadget API 文档后,让我们开始阅读代码, udb 驱动代码比较复杂,我们先从 gadget 驱动看起。各种gadget 驱动中, 最简单的要数 g_zero 驱动。

 

g_zero 驱动简介

作为最简单的 gadget 驱动,g_zero 的功能基于两个 BULK 端点实现了简单的输入输出功能, 它可以用作写新的gadget 驱动的一个实例。 g_zero 驱动还有一个重要角色, 即配合 host 端的 usbtest (内核模块及用户层代码),用于测试底层 udc 驱动。当然,也可以是测试主机的控制器驱动。

 

两个 BULK 端点为一个 IN 端点,  一个 OUT端点。基于这两个(由底层提供的)端点,g_zero 驱动实现了两个configuration。 第一个 configuration 提供了 sink/source功能:两个端点一个负责输入,一个负责输出,其中输出的内容根据设置可以是全0,也可以是按照某种算法生成的数据。另一个 configuration 提供了 loopback 接口, IN端点负责把从 OUT 端点收到的数据反馈给 Host.

 

根据系统配置,g_zero 驱动提供了全速及高速功能,从而稍微增加了代码复杂度。另外,它还支持 otg 接口,从usb2.0 协议我们知道, otg 其实是usb device实现的一个补充功能。它增加了一套接口,使得同一设备可以在设备角色以及有限主机角色之切换。上层gadget驱动主要是在描述符方面提供配合支持。下面我们开始看代码。

模块初始化

1309 static int __init init (void)

1310 {

1311         /* a real value would likely come through some id prom

1312          * or module option.  this one takes at least two packets.

1313          */

1314         strlcpy (serial"0123456789.0123456789.0123456789", sizeof serial);

1315

1316         return usb_gadget_register_driver (&zero_driver);

1317 }

1318 module_init (init);

1320 static void __exit cleanup (void)

1321 {

1322         (&zero_driver);

1323 }

1324 module_exit (cleanup); usb_gadget_unregister_driver

 

Serial 变量存储的是设备序列号,我们是一个非正式设备,随便填充一下。模块初始化函数调用 usb_gadget_register_driver 来向udc driver 注册一个 gadget 驱动, 我们这里是 zero_driver。而退出函数则会做相反的操作:调用 usb_gadget_unregister_driver取消原来的注册。像所有的模块初始化、退出函数一样,现在还看不出什么花头。我们再看一下 zero_driver 的定义:

 

1283 static struct usb_gadget_driver zero_driver = {

1284 #ifdef CONFIG_USB_GADGET_DUALSPEED

1285         .speed          = USB_SPEED_HIGH,

1286 #else

1287         .speed          = USB_SPEED_FULL,

1288 #endif

1289         .function       = (char *) longname,

1290         .bind           = zero_bind,

1291         .unbind         = __exit_p(zero_unbind),

1293         .setup          = zero_setup,

1294         .disconnect     = zero_disconnect,

1296         .suspend        = zero_suspend,

1297         .resume         = zero_resume,

1299         .driver         = {

1300                 .name           = (char *) shortname,

1301                 .owner          = THIS_MODULE,

1302         },

1303 };

 

根据 CONFIG_USB_GADGET_DUALSPEED,代码选择是支持高速还是全速,我们使用的 PXA 平台支持高速传输,所以我们假定该配置变量为真。根据 Gadget API 文档(以及下面的代码调用图),在初始化阶段 usb_gadget_register_driver 函数会调用 bind 函数,而 setup 函数是用于处理 udc 驱动没有处理的控制传输部分。这两个函数是整个 zero gadget 驱动的精华部分。其它函数则只是为了完整性而提供,有兴趣者可以对照 Gadget API 文档及代码,自行研究。至于 .driver 成员变量,那主要是为LDM(linux device module)服务的。现在关于 LDM 的文档满天飞,这里就不多说了。

 

简单起见,我们目前不去深究 udc 驱动代码(那比我们的 g_zero 驱动要复杂很多, 而且很多代码硬件相关,需要阅读硬件 spec 才能理解),而是使用 kft及graphviz(见参考,colorant 大侠提供的文档)工具得到函数调用关系图:(我们这里使用 pxa udc驱动,如果使用 dummy_hcd 会得到类似但更简单的关系图)

 

 

 

从上图中,我们可以看到在初始化阶段, udc 驱动会调用 zero 驱动的 bind 函数,也会调用 zero 驱动的 setup 函数 (主要是得到一些描述符), setup 函数主要是在后面我们的 device 和主机连接后用于处理控制传输的响应(大部分)。在初始阶段只是顺便帮忙提供点信息,进行的是假传输,真提供信息给 udc 驱动。下面我们重点分析 bind 函数。

函数 zero_bind

1140 static int __init

1141 zero_bind (struct usb_gadget *gadget)

1142 {

1143         struct zero_dev         *dev;

1144         struct usb_ep           *ep;

1145         int                     gcnum;

 

首先映入眼帘的是zero_bind 函数的参数,根据 Gadget API,一个 gadget 代表一个 usb slave设备。这个数据结构是在底层控制器驱动中静态分配的。Udc 驱动在调用 gadget 驱动各接口函数时都会提供这个数据结构。

 

1147         /* FIXME this can't yet work right with SH ... it has only

1148          * one configuration, numbered one.

1149          */

1150         if (gadget_is_sh(gadget))

1151                 return -ENODEV;

 

注意我们以前说过 gadget_is_* 系列函数提供了查询硬件能力的接口,这里用于判断是否是 SH 平台的 udc, 如果是, 直接出错返回:g_zero 驱动目前还不支持该平台。

 

1153         /* Bulk-only drivers like this one SHOULD be able to

1154          * autoconfigure on any sane usb controller driver,

1155          * but there may also be important quirks to address.

1156          */

1157         usb_ep_autoconfig_reset (gadget);

 

注意函数 usb_ep_autoconfig_reset 不是由底层 udc 驱动实现,而是我们以前提过的 helper 函数的一部分。该函数功能很简单:用于清空 gadget 的 端点列表。

 

1158         ep = usb_ep_autoconfig (gadget, &fs_source_desc);

1159         if (!ep) {

1160 autoconf_fail:

1161                 printk (KERN_ERR "%s: can't autoconfigure on %s/n",

1162                         shortname, gadget->name);

1163                 return -ENODEV;

1164         }

1165         EP_IN_NAME = ep->name;

1166         ep->driver_data = ep;   /* claim */

1167        

1168         ep = usb_ep_autoconfig (gadget, &fs_sink_desc);

1169         if (!ep)

1170                 goto autoconf_fail;

1171         EP_OUT_NAME = ep->name;

1172         ep->driver_data = ep;   /* claim */

 

函数 usb_ep_autoconfig 根据第二个参数所描述的限制条件,自动寻找适合条件的端点,并插入 gadget的端点列表。这里 ep 是普通的数据端点,它的 driver_data 不需要存放特殊数据,那就保存一下自己的地址吧。(后面我们将看到 ep0 的 driver_data 放的是zero_driver 的特殊数据)。我们看一下 fs_source_desc:

 

296 static struct usb_endpoint_descriptor

297 fs_source_desc = {

298         .bLength =              USB_DT_ENDPOINT_SIZE,

299         .bDescriptorType =      USB_DT_ENDPOINT,

300

301         .bEndpointAddress =     USB_DIR_IN,

302         .bmAttributes =         USB_ENDPOINT_XFER_BULK,

303 };

 

可见该描述符描述的是一个类型为 BULK,方向为 IN 的端点。 fs_sink_desc 的定义类似,描述一个类型为 BULK, 方向为 OUT 的端点。下面继续看 zero_bind 的代码。

 

1174         gcnum = usb_gadget_controller_number (gadget);

1175         if (gcnum >= 0)

1176                 device_desc.bcdDevice = cpu_to_le16 (0x0200 + gcnum);

1177         else {

1178                 /* gadget zero is so simple (for now, no altsettings) that

1179                  * it SHOULD NOT have problems with bulk-capable hardware.

1180                  * so warn about unrcognized controllers, don't panic.

1181                  *

1182                  * things like configuration and altsetting numbering

1183                  * can need hardware-specific attention though.

1184                  */

1185                 printk (KERN_WARNING "%s: controller '%s' not recognized/n",

1186                         shortname, gadget->name);

1187                 device_desc.bcdDevice = __constant_cpu_to_le16 (0x9999);

1188         }

 

每一个 udc 驱动被分配了一个编号,用作该设备描述符里的 bcd 码。 如果没有分配,没办法,就将就着用 0x9999 吧。

 

1191         /* ok, we made sense of the hardware ... */

1192         dev = kzalloc(sizeof(*dev), GFP_KERNEL);

1193         if (!dev)

1194                 return -ENOMEM;

1195         spin_lock_init (&dev->lock);

1196         dev->gadget = gadget;

1197         set_gadget_data (gadget, dev);

1198

 

stuct gadget 维护所有 gadget 驱动共性的内容,个性的数据则由各 gadget 驱动各自定义,对于 zero, 它定义了 zero_dev. 分配后存放在 gadget 结构的某个角落里: gadget.dev.driver_data。 zero_dev 定义如下:

 

119 struct zero_dev {

120         spinlock_t              lock;

121         struct usb_gadget       *gadget;

122         struct usb_request      *req;           /* for control responses */

124         /* when configured, we have one of two configs:

125          * - source data (in to host) and sink it (out from host)

126          * - or loop it back (out from host back in to host)

127          */

128         u8                      config;

129         struct usb_ep           *in_ep, *out_ep;

131         /* autoresume timer */

132         struct timer_list       resume;

133 };

 

这里 resume是用于唤醒 host 的 timer 的列表, config 表示我们当前使用第几个 configuration. 其它含义自明。下面继续看 zero bind 代码。

 

1199         /* preallocate control response and buffer */

1200         dev->req = usb_ep_alloc_request (gadget->ep0, GFP_KERNEL);

1201         if (!dev->req)

1202                 goto enomem;

1203         dev->req->buf = usb_ep_alloc_buffer (gadget->ep0, USB_BUFSIZ,

1204                                 &dev->req->dmaGFP_KERNEL);

1205         if (!dev->req->buf)

1206                 goto enomem;

1207

1208         dev->req->complete = zero_setup_complete;

1209

 

这几行代码分配用于控制传输的请求/数据缓冲以及结束函数。控制传输是每个 gadget 驱动要使用的传输方式,这里及早分配。结束函数 zero_setup_complete 只是打印一下状态,我们就不贴出了。

 

1210         device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket;

 

这里根据底层的数据初始化设备描述符里端点 0 (控制端点)的最大包大小。

 

1212 #ifdef CONFIG_USB_GADGET_DUALSPEED

1213         /* assume ep0 uses the same value for both speeds ... */

1214         dev_qualifier.bMaxPacketSize0 = device_desc.bMaxPacketSize0;

1215

1216         /* and that all endpoints are dual-speed */

1217         hs_source_desc.bEndpointAddress = fs_source_desc.bEndpointAddress;

1218         hs_sink_desc.bEndpointAddress = fs_sink_desc.bEndpointAddress;

1219 #endif

 

高速设备需要的额外的描述符,我们对某些字段进行初始化。

 

1221         if (gadget->is_otg) {

1222                 otg_descriptor.bmAttributes |= USB_OTG_HNP,

1223                 source_sink_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP;

1224                 loopback_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP;

1225         }

 

如果是 otg 设备,则需要在描述符里设置相关特性。

 

1227         usb_gadget_set_selfpowered (gadget);

 

能运行 Linux Gadget 驱动的设备一般电池供电,也就是 selfpowered。

 

1229         init_timer (&dev->resume);

1230         dev->resume.function = zero_autoresume;

1231         dev->resume.data = (unsigned long) dev;

1232         if (autoresume) {

1233                 source_sink_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP;

1234                 loopback_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP;

1235         }

 

这段代码跟自动唤醒 host 有关, 不深究。

 

1237         gadget->ep0->driver_data = dev;

 

多记一份 zero_dev 的地址, 方便使用。

 

1239         INFO (dev"%s, version: " DRIVER_VERSION "/n"longname);

1240         INFO (dev"using %s, OUT %s IN %s/n", gadget->name,

1241                 EP_OUT_NAMEEP_IN_NAME);

1242

1243         snprintf (manufacturer, sizeof manufacturer"%s %s with %s",

1244                 init_utsname()->sysname, init_utsname()->release,

1245                 gadget->name);

1246

1247         return 0;

1248

1249 enomem:

1250         zero_unbind (gadget);

1251         return -ENOMEM;

1252 }

 

自此  zero_bind 分析完毕。它主要是为 gadget 驱动找到了合适的端点,并且初始化了设备相关结构: zero_dev.从而把 gadget 驱动和  udc 驱动仅仅地绑定在一起。 看到现在,我们还没有感受到 gadget 驱动的真正意义, 前面的函数就像一座座桥梁,走过这些桥梁,我们终于来到美丽的湖心小岛:zero_setup。

 

函数 zero_setup

zero_setup 完成控制传输的大部分功能。比如获取各种描述符、设置配置等。Host 首先通过控制传输和设备进行通信,告诉设备它底下要干什么。 Zero gadget驱动比较简单,在主机进行set configuration后,就会在 IN/OUT 端点上准备好数据,供主机去用。并且通过call函数,在主机使用完前面准备好的数据后,继续插入请求,这样,主机就可以源源不断的对我们这个设备进行读写操作。以下开始看代码。

 

917 static int

918 zero_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)

919 {

 

照例,我们得到 usb_gadget 结构,同时,我们的第二个参数是 usb_ctrlrequest 结构:

 
140 struct usb_ctrlrequest {
141         __u8 bRequestType;
142         __u8 bRequest;
143         __le16 wValue;
144         __le16 wIndex;
145         __le16 wLength;
146 } __attribute__ ((packed));

 

具体含义请参考 Usb spec Ch9。 这里结构名字有点误导, usb_ctrlrequest 代表的是主机传过来的控制请求。和后面的usb_request 有较大区别。 usb_request 代表放到端点的队列里等待主机过来读写的一个个数据包。下面我们继续看 zero_setup 函数代码。

 

920         struct zero_dev         *dev = get_gadget_data (gadget);

921         struct usb_request      *req = dev->req;

922         int                     value = -EOPNOTSUPP;

923         u16                     w_index = le16_to_cpu(ctrl->wIndex);

924         u16                     w_value = le16_to_cpu(ctrl->wValue);

925         u16                     w_length = le16_to_cpu(ctrl->wLength);

 

获得我们在 bind 函数分配的 zero_dev, usb_request, 以及由主机传过来的“请求”的各字段。

 

927         /* usually this stores reply data in the pre-allocated ep0 buffer,

928          * but config change events will reconfigure hardware.*/

930         req->zero = 0;

931         switch (ctrl->bRequest) {

932

933         case USB_REQ_GET_DESCRIPTOR:

934                 if (ctrl->bRequestType != USB_DIR_IN)

935                         goto unknown;

 

请求各种描述符,当然需要是 IN 类型的请求。

 

936                 switch (w_value >> 8) {

938                 case USB_DT_DEVICE:

939                         value = min (w_length, (u16) sizeof device_desc);

940                         memcpy (req->buf, &device_descvalue);

941                         break;

942 #ifdef CONFIG_USB_GADGET_DUALSPEED

943                 case USB_DT_DEVICE_QUALIFIER:

944                         if (!gadget->is_dualspeed)

945                                 break;

946                         value = min (w_length, (u16) sizeof dev_qualifier);

947                         memcpy (req->buf, &dev_qualifiervalue);

948                         break;

 

对应 USB 2.0 Spec CH9, 以上代码很容易理解。 每一个描述符使用 struct usb_device_descriptor 描述,比如, 设备描述符:

 

222 static struct usb_device_descriptor

223 device_desc = {

224         .bLength =              sizeof device_desc,

225         .bDescriptorType =      USB_DT_DEVICE,

226

227         .bcdUSB =               __constant_cpu_to_le16 (0x0200),

228         .bDeviceClass =         USB_CLASS_VENDOR_SPEC, 0xff

229

230         .idVendor =             __constant_cpu_to_le16 (DRIVER_VENDOR_NUM),

231         .idProduct =            __constant_cpu_to_le16 (DRIVER_PRODUCT_NUM),

232         .iManufacturer =        STRING_MANUFACTURER, 25, 厂商描述符

233         .iProduct =             STRING_PRODUCT,   42,厂品描述符

234         .iSerialNumber =        STRING_SERIAL,    101, 序列号

235         .bNumConfigurations =   2,

236 };

 

950                 case USB_DT_OTHER_SPEED_CONFIG:

951                         if (!gadget->is_dualspeed)

952                                 break;

953                         // FALLTHROUGH

954 #endif /* CONFIG_USB_GADGET_DUALSPEED */

955                 case USB_DT_CONFIG:

956                         value = config_buf (gadget, req->buf,

957                                         w_value >> 8,

958                                         w_value & 0xff);

959                         if (value >= 0)

960                                 value = min (w_length, (u16value);

961                         break;

 

配置描述符比较复杂,会返回该配置里的接口,端点等信息。配置描述符由:struct usb_descriptor_header[] 表达, 而且高速/全速设备的配置描述符是不一样。比如,高速 loopback 配置的配置描述符为:

 

378 static const struct usb_descriptor_header *hs_loopback_function [] = {

379         (struct usb_descriptor_header *) &otg_descriptor,

380         (struct usb_descriptor_header *) &loopback_intf,

381         (struct usb_descriptor_header *) &hs_source_desc,

382         (struct usb_descriptor_header *) &hs_sink_desc,

383         NULL,

384 };

 

可见,本质上,配置描述符是返回一组描述符。下面看一下配置描述符是如何生成的。

 

432 static int

433 config_buf (struct usb_gadget *gadget,

434                 u8 *bufu8 type, unsigned index)

435 {

436         int                             is_source_sink;

437         int                             len;

438         const struct usb_descriptor_header **function;

439 #ifdef CONFIG_USB_GADGET_DUALSPEED

440         int                             hs = (gadget->speed == USB_SPEED_HIGH);

441 #endif

442

443         /* two configurations will always be index 0 and index 1 */

444         if (index > 1)

445                 return -EINVAL;

446         is_source_sink = loopdefault ? (index == 1) : (index == 0);

447

448 #ifdef CONFIG_USB_GADGET_DUALSPEED

449         if (type == USB_DT_OTHER_SPEED_CONFIG)

450                 hs = !hs;

451         if (hs)

452                 function = is_source_sink

453                         ? hs_source_sink_function

454                         : hs_loopback_function;

455         else

456 #endif

457                 function = is_source_sink

458                         ? fs_source_sink_function

459                         : fs_loopback_function;

460

461         /* for now, don't advertise srp-only devices */

462         if (!gadget->is_otg)

463                 function++;

464

465         len = usb_gadget_config_buf (is_source_sink

466                                         ? &source_sink_config

467                                         : &loopback_config,

468                         bufUSB_BUFSIZ, function);

469         if (len < 0)

470                 return len;

471         ((struct usb_config_descriptor *) buf)->bDescriptorType = type;

472         return len;

473 }

 

代码很简单, config_buf 函数根据当前是否是高速设备,以及是否是 otg 设备,选择合适的 configuration( souce sink config or loopback config), 调用 usb_gadget_config_buf 生成最终的配置描述符。可以想象 usb_gadget_config_buf 的实现非常简单: 根据传过来的 描述符列表( 以 NULL 指针结束),使用 memcpy  之类见每个描述符的内容拷贝到 buf 里。 下面我们继续看  zero_setup函数。

 

963                 case USB_DT_STRING:

964                         /* wIndex == language code.

965                          * this driver only handles one language, you can

966                          * add string tables for other languages, using

967                          * any UTF-8 characters

968                          */

969                         value = usb_gadget_get_string (&stringtab,

970                                         w_value & 0xff, req->buf);

971                         if (value >= 0)

972                                 value = min (w_length, (u16value);

973                         break;

974                 }

975                 break;

976

 

根据 host 传递过来的索引,响应相应的字符串。Zero驱动的字符串描述符则只支持一种语言(0409, en-us):

 

409 static struct usb_gadget_strings        stringtab = {

410         .language       = 0x0409,       /* en-us */

411         .strings        = strings,

412 };

 

399 /* static strings, in UTF-8 */

400 static struct usb_string                strings [] = {

401         { STRING_MANUFACTURERmanufacturer, },

402         { STRING_PRODUCTlongname, },

403         { STRING_SERIALserial, },

404         { STRING_LOOPBACKloopback, },

405         { STRING_SOURCE_SINKsource_sink, },

406         {  }                    /* end of list */

407 };

 

有点像应用层(比如 vc)为了支持多语言而独立出来的字符串资源。事实上就是这样!我们可以很容易再增加一种语言。下面我们继续看  zero_setup 函数。

 

977         /* currently two configs, two speeds */

978         case USB_REQ_SET_CONFIGURATION:

979                 if (ctrl->bRequestType != 0)

980                         goto unknown;

981                 if (gadget->a_hnp_support)

982                         DBG (dev"HNP available/n");

983                 else if (gadget->a_alt_hnp_support)

984                         DBG (dev"HNP needs a different root port/n");

985                 else

986                         VDBG (dev"HNP inactive/n");

987                 spin_lock (&dev->lock);

988                 value = zero_set_config (dev, w_value, GFP_ATOMIC);

989                 spin_unlock (&dev->lock);

990                 break;

 

设置设备的当前配置,到这里,才凌空一脚,将设备带入数据传输状态,我们先把zero_setup 看完,再仔细看函数zero_set_config

 

991         case USB_REQ_GET_CONFIGURATION:

992                 if (ctrl->bRequestType != USB_DIR_IN)

993                         goto unknown;

994                 *(u8 *)req->buf = dev->config;

995                 value = min (w_length, (u16) 1);

996                 break;

 

获取设备的当前配置

 

998         /* until we add altsetting support, or other interfaces,

999          * only 0/0 are possible.  pxa2xx only supports 0/0 (poorly)

1000          * and already killed pending endpoint I/O.

1001          */

1002         case USB_REQ_SET_INTERFACE:

1003                 if (ctrl->bRequestType != USB_RECIP_INTERFACE)

1004                         goto unknown;

1005                 spin_lock (&dev->lock);

1006                 if (dev->config && w_index == 0 && w_value == 0) {

1007                         u8              config = dev->config;

1008

1009                         /* resets interface configuration, forgets about

1010                          * previous transaction state (queued bufs, etc)

1011                          * and re-inits endpoint state (toggle etc)

1012                          * no response queued, just zero status == success.

1013                          * if we had more than one interface we couldn't

1014                          * use this "reset the config" shortcut.

1015                          */

1016                         zero_reset_config (dev);

1017                         zero_set_config (devconfigGFP_ATOMIC);

1018                         value = 0;

1019                 }

1020                 spin_unlock (&dev->lock);

1021                 break;

 

设置接口,由于我们每个configuration只有一个接口,所以这里的效果跟前面设置配置类似。

由于 zero_set_config 函数会调用 zero_reset_config, 所以这里应该可以不调用 zero_reset_config.

 

1022         case USB_REQ_GET_INTERFACE:

1023                 if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE))

1024                         goto unknown;

1025                 if (!dev->config)

1026                         break;

1027                 if (w_index != 0) {

1028                         value = -EDOM;

1029                         break;

1030                 }

1031                 *(u8 *)req->buf = 0;

1032                 value = min (w_length, (u16) 1);

1033                 break;

1034

 

获取设备的当前配置的当前接口。

 

1035         /*

1036          * These are the same vendor-specific requests supported by

1037          * Intel's USB 2.0 compliance test devices.  We exceed that

1038          * device spec by allowing multiple-packet requests.

1039          */

1040         case 0x5b:      /* control WRITE test -- fill the buffer */

1041                 if (ctrl->bRequestType != (USB_DIR_OUT|USB_TYPE_VENDOR))

1042                         goto unknown;

1043                 if (w_value || w_index)

1044                         break;

1045                 /* just read that many bytes into the buffer */

1046                 if (w_length > USB_BUFSIZ)

1047                         break;

1048                 value = w_length;

1049                 break;

1050         case 0x5c:      /* control READ test -- return the buffer */

1051                 if (ctrl->bRequestType != (USB_DIR_IN|USB_TYPE_VENDOR))

1052                         goto unknown;

1053                 if (w_value || w_index)

1054                         break;

1055                 /* expect those bytes are still in the buffer; send back */

1056                 if (w_length > USB_BUFSIZ

1057                                 || w_length != req->length)

1058                         break;

1059                 value = w_length;

1060                 break;

1061

 

根据协议,我们可以定制私有的类型,这里是 Intel 定义的测试类型,用于测试端点0的数据收发。端点0通常用于控制传输, 用它进行数据传输完全是为了测试目的。

 

1062         default:

1063 unknown:

1064                 VDBG (dev,

1065                         "unknown control req%02x.%02x v%04x i%04x l%d/n",

1066                         ctrl->bRequestType, ctrl->bRequest,

1067                         w_value, w_index, w_length);

1068         }

1069

1070         /* respond with data transfer before status phase? */

1071         if (value >= 0) {

1072                 req->length = value;

1073                 req->zero = value < w_length;

1074                 value = usb_ep_queue (gadget->ep0, req, GFP_ATOMIC);

1075                 if (value < 0) {

1076                         DBG (dev"ep_queue --> %d/n"value);

1077                         req->status = 0;

1078                         zero_setup_complete (gadget->ep0, req);

1079                 }

1080         }

 

如果有数据需要传给 Host, 则将其放到端点0的传送队列。底层 udc 驱动会负责将其发给 host.

 

1082         /* device either stalls (value < 0) or reports success */

1083         return value;

1084 }

 

函数zero_setup 完成了usb spec ch9 定义的很多功能。而我们前面介绍的 bulk-in/bulk-out 数据端点开始工作则是在 set configuration (或者 set interface)后,由zero_set_config函数触发。下面开始分析该函数。

 

函数zero_set_config

848 static int

849 zero_set_config (struct zero_dev *dev, unsigned numbergfp_t gfp_flags)

850 {

851         int                     result = 0;

852         struct usb_gadget       *gadget = dev->gadget;

853

854         if (number == dev->config)

855                 return 0;

862         zero_reset_config (dev);

863

 

函数 zero_reset_config 把所有的 端点置于 disable 状态。

 

864         switch (number) {

865         case CONFIG_SOURCE_SINK:

866                 result = set_source_sink_config (dev, gfp_flags);

867                 break;

868         case CONFIG_LOOPBACK:

869                 result = set_loopback_config (dev, gfp_flags);

870                 break;

871         default:

872                 result = -EINVAL;

873                 /* FALL THROUGH */

874         case 0:

875                 return result;

876         }

 

根据当前的配置,设置两种不同的传送方式。我们假定 host 设置的是 loopback 方式。另一种方式是类似的(数据内容不同)。

 

878         if (!result && (!dev->in_ep || !dev->out_ep))

879                 result = -ENODEV;

880         if (result)

881                 zero_reset_config (dev);

882         else {

883                 char *speed;

884

885                 switch (gadget->speed) {

886                 case USB_SPEED_LOW:     speed = "low"; break;

887                 case USB_SPEED_FULL:    speed = "full"; break;

888                 case USB_SPEED_HIGH:    speed = "high"; break;

889                 default:                speed = "?"; break;

890                 }

891

892                 dev->config = number;

893                 INFO (dev"%s speed config #%d: %s/n"speednumber,

894                                 (number == CONFIG_SOURCE_SINK)

895                                         ? source_sink : loopback);

896         }

897         return result;

898 }

一些善后处理。 下面我们看函数 set_loopback_config

函数 set_loopback_config

747 static int

748 set_loopback_config (struct zero_dev *devgfp_t gfp_flags)

749 {

750         int                     result = 0;

751         struct usb_ep           *ep;

752         struct usb_gadget       *gadget = dev->gadget;

753

754         gadget_for_each_ep (ep, gadget) {

 

针对 gadget 端点列表的每一个端点进行操作。

 

755                 const struct usb_endpoint_descriptor    *d;

756

757                 /* one endpoint writes data back IN to the host */

758                 if (strcmp (ep->nameEP_IN_NAME) == 0) {

759                         d = ep_desc (gadget, &hs_source_desc, &fs_source_desc);

760                         result = usb_ep_enable (ep, d);

761                         if (result == 0) {

762                                 ep->driver_data = dev;

763                                 dev->in_ep = ep;

764                                 continue;

765                         }

766

767                 /* one endpoint just reads OUT packets */

768                 } else if (strcmp (ep->nameEP_OUT_NAME) == 0) {

769                         d = ep_desc (gadget, &hs_sink_desc, &fs_sink_desc);

770                         result = usb_ep_enable (ep, d);

771                         if (result == 0) {

772                                 ep->driver_data = dev;

773                                 dev->out_ep = ep;

774                                 continue;

775                         }

776

777                 /* ignore any other endpoints */

778                 } else

779                         continue;

780

781                 /* stop on error */

782                 ERROR (dev"can't enable %s, result %d/n", ep->nameresult);

783                 break;

784         }

 

激活端点。并设置速度(高速或者全速)。

 

786         /* allocate a bunch of read buffers and queue them all at once.

787          * we buffer at most 'qlen' transfers; fewer if any need more

788          * than 'buflen' bytes each.

789          */

790         if (result == 0) {

791                 struct usb_request      *req;

792                 unsigned                i;

793

794                 ep = dev->out_ep;

795                 for (i = 0; i < qlen && result == 0; i++) {

796                         req = alloc_ep_req (ep, buflen);

797                         if (req) {

798                                 req->complete = loopback_complete;

799                                 result = usb_ep_queue (ep, req, GFP_ATOMIC);

800                                 if (result)

801                                         DBG (dev"%s queue req --> %d/n",

802                                                         ep->nameresult);

803                         } else

804                                 result = -ENOMEM;

805                 }

806         }

 

首先在 OUT 端点上挂一堆请求(usb_request), 等待主机向我们发送数据。等主机真正对我们进行OUT数据传输并且数据传完后,会调用 loopback_complete 回调函数。

 

807         if (result == 0)

808                 DBG (dev"qlen %d, buflen %d/n"qlenbuflen);

809

810         /* caller is responsible for cleanup on error */

811         return result;

812 }

 

下面看 函数 loopback_complete

 

函数 loopback_complete

698 static void loopback_complete (struct usb_ep *ep, struct usb_request *req)

699 {

700         struct zero_dev *dev = ep->driver_data;

701         int             status = req->status;

702

703         switch (status) {

704

705         case 0:                         /* normal completion? */

706                 if (ep == dev->out_ep) {

707                         /* loop this OUT packet back IN to the host */

708                         req->zero = (req->actual < req->length);

709                         req->length = req->actual;

710                         status = usb_ep_queue (dev->in_ep, req, GFP_ATOMIC);

711                         if (status == 0)

712                                 return;

713

714                         /* "should never get here" */

715                         ERROR (dev"can't loop %s to %s: %d/n",

716                                 ep->namedev->in_ep->name,

717                                 status);

718                 }

719

720                 /* queue the buffer for some later OUT packet */

721                 req->length = buflen;

722                 status = usb_ep_queue (dev->out_ep, req, GFP_ATOMIC);

723                 if (status == 0)

724                         return;

725

726                 /* "should never get here" */

727                 /* FALLTHROUGH */

728

729         default:

730                 ERROR (dev"%s loop complete --> %d, %d/%d/n", ep->name,

731                                 status, req->actual, req->length);

732                 /* FALLTHROUGH */

733

734         /* NOTE:  since this driver doesn't maintain an explicit record

735          * of requests it submitted (just maintains qlen count), we

736          * rely on the hardware driver to clean up on disconnect or

737          * endpoint disable.

738          */

739         case -ECONNABORTED:             /* hardware forced ep reset */

740         case -ECONNRESET:               /* request dequeued */

741         case -ESHUTDOWN:                /* disconnect from host */

742                 free_ep_req (ep, req);

743                 return;

744         }

745 }

 

如果 OUT 传输正常结束,则会将其放到IN 端点的传输队列。

如果 IN 传输正常结束,则会将其放到 OUT 端点的传输队列。

这样,通过回调函数不断在两个队列(IN/OUT)之间切换这些请求(usb_request),就实现了在主机看来的 loopback 设备。

总结

Gadget 驱动的特殊性在于它是 host 端对等驱动的 slave, 而不是上层某个应用的 slave. 响应的,它是实现是很有意思的。我们没有看到 read/write 函数,也没有看到我们最常实现的 ioctl 函数, 而是把重点放在回调函数zero_setup 上。 g_zero gadget 驱动实现了一个最简单的 bulk-in/bulk-out 功能,向我们展示了 gadget 驱动如果利用 gadget API来完成数据传输功能。对于复杂的 gadget 驱动, setup 回调函数只是一个起点。

posted on 2016-08-24 15:31  jamboo  阅读(1824)  评论(0编辑  收藏  举报

导航