Linux-USB Gadget : Part 4: 最简单的 gadget驱动:g_zero

作者: zjujoe 转载请注明出处

Email : zjujoe@yahoo.com

BLOG : http://blog.csdn.net/zjujoe

前言

前面讲过, 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->dma , GFP_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_NAME , EP_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_desc , value );

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_qualifier , value );

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, (u16 ) value );

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 * buf , u8 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                          buf , USB_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, (u16 ) value );

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_MANUFACTURER , manufacturer , },

402          { STRING_PRODUCT , longname , },

403          { STRING_SERIAL , serial , },

404          { STRING_LOOPBACK , loopback , },

405          { STRING_SOURCE_SINK , source_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 (dev , config , GFP_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 number , gfp_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" , speed , number ,

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 *dev , gfp_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->name , EP_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->name , EP_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->name , result );

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->name , result );

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" , qlen , buflen );

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->name , dev ->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 回调函数只是一个起点。

参考

USB 2.0 Spec

http://www.usb.org/developers/docs/

用 KFI 和 Graphviz 跟踪 / 优化内核代码

http://blog.csdn.net/colorant/archive/2008/07/09/2627493.aspx

Linux-USB Gadget : Part 4: 最简单的 gadget驱动:g_zero相关推荐

  1. win7无法识别linux usb设备,win7无法识别U盘,驱动信息:该设备的驱动程序未被安装。 (代码 28)...

    POJ 2452 Sticks Problem RMQ+二分....枚举 i  ,找比 i 小的第一个元素,再找之间的第一个最大元素.....                   Sticks Pro ...

  2. Linux usb 6. HC/UDC 测试

    文章目录 1. 背景介绍 2. Device (gadget zero) 2.1 `gadget zero` 创建 2.2 SourceSink Function 2.3 Loopback Funct ...

  3. 嵌入式Linux设备驱动程序开发指南20(Linux USB设备驱动)——读书笔记

    Linux USB设备驱动 二十.Linux USB设备驱动 20.1 USB简介 20.1.1 USB2.0总线拓扑 20.1.2 USB总线枚举和设备布局 20.1.3 USB数据传输 20.1. ...

  4. linux usb gadget 日志

    1,USB 协议入门 几种USB控制器类型:OHCI,UHCI,EHCI,XHCI 遇到过一些关于USB的东西(如下),一直没搞明白什么USB1.0/1.1/2.0/3.0之类的,当然我知道它们的各自 ...

  5. linux usb gadget驱动详解(三)

    本文将对linux4.4.19版本usb gadget源码进行简单分析.鉴于前文反复测试U盘设备驱动,现从linux-4.4.19/drivers/usb/gadget/legacy/mass_sto ...

  6. linux下gadget复合设备,Linux USB Gadget--设备枚举

    前面介绍了Linux USB Gadget的软件结构与各软件层的整合过程.经过各种注册函数,Gadget功能驱动层,USB设备层与UDC底层结合在了一起形成了一个完整的USB设备.而这个设备已经准备好 ...

  7. linux usb gadget驱动详解(二)

    在上篇<linux usb gadget驱动详解(一)>中,我们了解到gadget的测试方法,但在最后,我们留下一个问题,就是怎样使用新的方法进行usb gadget驱动测试. 我们发现l ...

  8. Linux USB 驱动开发(五)—— USB驱动程序开发过程简单总结

    http://blog.csdn.net/zqixiao_09/article/details/51057086 设备驱动程序是操作系统内核和机器硬件之间的接口,由一组函数和一些私有数据组成,是应用程 ...

  9. linux usb gadget驱动详解(一)

    由于PC的推广,USB(通用串行总线)是我们最熟知的通信总线规范之一,其他的还有诸如以太网.PCIE总线和RS232串口等.这里我们主要讨论USB. USB是一个主从通信架构,但只能一主多从.其中us ...

最新文章

  1. android相机截取矩形框,Android自定义照相机实现只拍摄矩形区域(重传)
  2. 刀模图是什么意思_“吃鸡”光子公布神秘图,海岛图上有44个坐标,暗示信号值取消?...
  3. ajax中怎么验证data,我应该在jQuery的ajax成功处理程序中验证响应数据吗?
  4. android数据库文件是否加密存储,详解Android数据存储之SQLCipher数据库加密
  5. 成都睿铂盘点无人机航测三个极端恶劣环境的人员与设备防护指南
  6. 00.Maven简介
  7. 神奇的数学:牛津教授给青少年的讲座
  8. 密码生成 算法编程题
  9. 智慧校园人脸识别门禁系统设计方案
  10. linux pipe2函数,pipe()函数 Unix/Linux
  11. android 视频上传网络异常,App上传视频(或大文件)失败怎么办?
  12. 计算机笔记Excel,秦路天善智能EXCEL学习笔记1-文本清洗函数
  13. Gmail:如何撤回发出的邮件?
  14. 利用 perf4j 做服务监控
  15. ::ng-deep 与 :host ::ng-deep
  16. add_days oracle_oracle 日期时间函数使用总结
  17. 你们要的水性粘合剂乳胶漆消泡剂已经出来了
  18. 图片怎么在线转换成PDF格式
  19. 618战局天猫聚焦“商家体验”,创造确定性增长是核心目标
  20. 求解多元一次方程解的个数(参考内容)

热门文章

  1. 蓝桥杯第八届决赛B组
  2. Java基础四——面向对象思想
  3. wsappx是什么进程
  4. Iterm2保存服务器账密
  5. 解决chrome浏览器中鼠标滚轮滚动事件失效的问题
  6. 真正的美剧字幕组翻译高手教你如何学好英语!心得经验之谈啊!
  7. 单向拉伸试验有限元模拟(ABAQUS)
  8. No.2 STM32F429IGT6 固件库 CMSIS标准及库和STM32官方文档资料总结 (STM32F429/F767/H743)
  9. linux执行命令后日志打印输出到文件
  10. 使用 JavaScript 和 CSS 的随机颜色生成器