Linux那些事儿 之 戏说USB(28)设备的生命线(十一)
2015-03-26 17:04
471 查看
现在已经使用GET_DESCRIPTOR请求取到了包含一个配置里所有相关描述符内容的一堆数据,这些数据是raw的,即原始的,所有数据不管是配置描述符、接口描述符还是端点描述符都彼此的挤在一起,所以得想办法将它们给分开,丁是丁卯是卯的,于是usb_parse_configuration()登上了历史舞台,显然它们两个不管是谁想简短几句就搞定是不可能的,不过也没什么可怕的,咱写不会,看还不会么?
drivers/usb/core/config.c
5行,buffer里保存的就是GET_DESCRIPTOR请求获得的那堆数据,要解析这些数据,不可避免的要对buffer指针进行操作,这里先将它备份一下。
17行,config是参数里传递过来的,是设备struct usb_device结构体里的struct usb_host_config结构体数组config中的一员。不出意外的话buffer的前USB_DT_CONFIG_SIZE个字节对应的就是配置描述符,那么这里的意思就很明显了。然后做些检验,看看这USB_DT_CONFIG_SIZE字节的内容究竟是不是正如我们所期待的那样是个配置描述符,如果不是,那buffer里的数据问题可就大了,没什么利用价值了,还是返回吧,不必要再接着解析了。
28行,buffer的前USB_DT_CONFIG_SIZE个字节已经理清了,接下来该解析剩下的数据了,buffer需要紧跟形势的发展,位置和长度都要做相应的修正。
31行,获得这个配置所拥有的接口数目,不能简单一赋值就完事儿了,得知道系统里对这个数目是有个USB_MAXINTERFACES这样的限制的,如果数目比这个限制还大,就改为USB_MAXINTERFACES。
42~124行,这函数真是酷到家了,连里面一个循环都这么长这么酷,不过别看它cool,完成的事情却很单一,就是统计记录一下这个配置里每个接口所拥有的设置数目。提醒你一下,千万别被写代码的哥们儿给迷惑了,这个循环里使用的是buffer2和size2, buffer和size的两个替身,buffer和size就停在42行享受阳光海滩。
46行,这里遇到一个新的结构struct usb_descriptor_header,在include/uapi/linux/usb/ch9.h里定义
usb_descriptor_header的结构体指针,然后就可以使用‘->’来取出bLength和bDescriptorType,这样写的人顺心看的人舒心,你好我好大家好。
那么46行就表示如果GET_DESCRIPTOR请求返回的数据里除了包括一个配置描述符外,连两个字节都没有,那就说明这个配置在进行裸体行为艺术,能看不能用。
61行,如果这是个接口描述符就说明这个配置的某个接口拥有一个设置,是没有什么所谓的设置描述符的,一个接口描述符就代表了存在一个设置,接口描述里的bInterfaceNumber会指出这个设置隶属于哪个接口。那么这里除了是接口描述符还有可能是什么?还有可能是class-和vendor-specific描述符。
65行,既然觉得这是个接口描述符,就把这个指针转化为struct usb_interface_descriptor结构体指针,你可别被C里的这种指针游戏给转晕了,一个地址如果代码不给它赋予什么意义,它除了表示一个地址外就什么都不是。同样一个地址,上面转化为struct usb_descriptor_header结构体指针和这里转化为struct usb_interface_descriptor结构体指针,它就不再仅仅是一个地址,而是代表了不同的含义。
66行,仍然不忘保持革命斗争警惕性。bDescriptorType等于USB_DT_INTERFACE并不说明它就一定是接口描述符了,它的bLength还必须要等于USB_DT_INTERFACE_SIZE。bLength和bDescriptorType一起才能决定一个描述符。
91~102这几行是用来考验咱们的耐心和勇气的。首先要明白n、inums和nalts这几个枯燥的东东是表示什么的,n记录的是接口的数目,数组inums里的每一项都表示一个接口号,数组nalts里的每一项记录的是每个接口拥有的设置数目,inums和nalts两个数组里的元素是一一对应的,inums[0]就对应nalts[0],inums[1]就对应nalts[1]。其次还要谨记一个残酷的事实,发送GET_DESCRIPTOR请求时,设备并不一定会按照接口1,接口2这样的顺序循规蹈矩的返回数据,虽说协议里是这么要求的,但都在江湖行走谁能没点个性。
125行,buffer的最后边儿可能会有些垃圾数据,为了去除这些洋垃圾,这里需要将size和配置描述符里的那个wTotalLength修正一下。
128行,经过上面那个超酷的循环之后,如果统计得到的接口数目和配置描述符里的bNumInterfaces不符,或者干脆就没有发现配置里有什么接口,就警告一下。
137行,又一个for循环,目的是看看是不是遗漏了哪个接口号,比如说配置6个接口,为什么是6那,因为俺的幸运数字是6,呵呵,每个接口号都应该对应数组inums里的一项,如果在inums里面没有发现这个接口号,比如2吧,那2这个接口号就神秘失踪了,你找不到接口2。这个当然也属于违章驾驶,需要警告一下,开票罚600,不开票罚200,你自己选。
148行,再一个for循环,struct usb_interface_caches做嘛用的早就说过了,USB_MAXALTSETTING的定义在config.c里
169行,配置描述符后面紧跟的不一定就是接口描述符,还可能是class-和vendor-specific描述符,如果有的话。不管有没有,先把buffer的地址赋给extra,如果没有扩展的描述符,则170行返回的i就等于0,extralen也就为0。
170行,调用find_next_descriptor()在buffer里寻找配置描述符后面跟着的第一个接口描述符。它也在config.c里定义,进去看看
这个函数结束后,num_skipped里记录的是搜索过程中忽略的dt1和dt2之外其它描述符的数目,返回值表示搜索结束时buffer的位置比搜索开始时前进的字节数。其它没什么好讲的。还是回到usb_parse_configuration函数。
176行,根据find_next_descriptor的结果修正buffer和size。你可能因为受过很多面试的摧残,或者代码里错过很多次,对C里的按引用传递和按值传递已经烂熟于心,看到find_next_descriptor()那里传递的是buffer,一个指针,条件反射的觉得它里面对buffer的修改必定影响了外面的buffer,所以认为buffer已经指向了寻找到的接口描述符。但是你的这种如意算盘此时并不能如意,find_next_descriptor里修改的只是参数里buffer的值,并没有修改它指向的内容,对于地址本身来说仍然只能算是按值传递,怎么修改都影响不到函数外边,所以这里的176行仍然要对buffer的位置进行修正。
180行,事不过三,三个for循环之后轮到了一个while循环,如果size大于0,就说明配置描述符后面找到了一个接口描述符,根据这个接口描述符的长度,已经可以解析出一个完整的接口描述符了,但是仍然没到乐观的时候,这个接口描述符后面还会跟着一群端点描述符,再然后还会有其它的接口描述符。所以我们又迎来了另一个变态函数usb_parse_interface,先不管这个它长什么样子,毕竟usb_parse_configuration()就快到头儿了,暂时只需要知道它返回的时候,buffer的位置已经在下一个接口描述符那里了,还是那个理儿,对buffer地址本身来说是按值传递的,所以186行要对这个位置和长度进行下调整以适应新形势。那么这个while循环的意思就很明显了,对buffer一段一段的解析,直到再也找不到接口描述符了。
191行,最后这个for循环没啥实质性的内容,就是找一下每个接口是不是有哪个设置编号给漏过去了,只要有耐心,你就能看得懂。咱们接下来还是看config.c里的那个usb_parse_interface()
18行,只能是并不说明它就是,只有bLength等于USB_DT_INTERFACE_SIZE才说明开头儿的USB_DT_INTERFACE_SIZE字节确实是个接口描述符。否则就没必要再对这些数据进行什么处理了,直接跳到最后吧。先看看这个函数的最后都发生了什么,从新的位置开始再次调用find_next_descriptor()在buffer里寻找下一个接口描述符。
22行,因为数组inums并不一定是按照接口的顺序来保存接口号的,inums[1]对应的可能是接口1也可能是接口0,所以这里要用for循环来寻找这个接口对应着inums里的哪一项,从而根据在数组里的位置获得接口对应的struct usb_interface_cache结构体。usb_parse_configuration()已经告诉了我们,同一个接口在inums和intf_cache这两个数组里的位置是一样的。
34行,获得这个接口描述符对应的设置编号,然后根据这个编号从接口的cache里搜索看这个设置是不是已经遇到过了,如果已经遇到过,就没必要再对这个接口描述符进行处理,直接跳到最后,否则意味着发现了一个新的设置,要将它添加到cache里,并cache里的设置数目num_altsetting加1。要记住,设置是用struct usb_host_interface结构来表示的,一个接口描述符就对应一个设置。
51行,这段代码好熟悉啊。现在buffer开头儿的那个接口描述符已经理清了,要解析它后面的那些数据了。先把位置赋给这个刚解析出来的接口描述符的extra,然后再从这个位置开始去寻找下一个距离最近的一个接口描述符或端点描述符。如果这个接口描述符后面还跟有class-或vendor-specific描述符,则find_next_descriptor的返回值会大于0,buffer的位置和size也要进行相应的调整,来指向新找到的接口描述符或端点描述符。
这里find_next_descriptor的dt1参数和dt2参数就不再一样了,因为如果一个接口只用到端点0,它的接口描述符后边儿是不会跟有端点描述符的。
62行,获得这个设置使用的端点数目,然后将相应接口描述符里的bNumEndpoints置0,为什么?你要往下看。USB_MAXENDPOINTS在include/linux/usb.h里定义
81行,走到这里,buffer开头儿的那个接口描述符已经理清了,而且也找到了下一个接口描述符或端点描述符的位置,该从这个新的位置开始解析了,于是又遇到了一个似曾相识的while循环。82行先判断一下前面找到的是接口描述符还是端点描述符,如果是接口描述符就中断这个while循环,返回与下一个接口描述符的距离。否则说明在buffer当前的位置上待着的是一个端点描述符,因此就要迎来另一个函数usb_parse_endpoint对面紧接着的数据进行解析。usb_parse_endpoint()返回的时候,buffer的位置已经在下一个端点描述符那里了,91行调整buffer的位置长度,这个while循环的也很明显了,对buffer一段一段的解析,直到遇到下一个接口描述符或者已经走到buffer结尾。现在看看config.c里定义的usb_parse_endpoint函数
10行,buffer开头儿只能是一个端点描述符,所以这里将地址转化为struct usb_endpoint_descriptor结构体指针,然后调整buffer的位置和size。
14行,这里要明白的是端点描述符与配置描述符、接口描述符不一样,它是可能有两种大小的。
25行,得到端点号。这里的端点号不能为0,因为端点0是没有描述符的,也不能大于16,为什么?同样如果你不想蓦然回首,就当成协议里规定的吧。
34行,要知道这个bNumEndpoints在usb_parse_interface()的63行是被赋为0了的。
37行,要知道这个endpoint数组在usb_parse_interface()的74行也是已经申请好内存了的。从这里你应该明白bNumEndpoints是被当成了一个计数器,发现一个端点描述符,它就加1,并把找到的端点描述符copy到设置的endpoint数组里。
41行,初始化端点的urb队列urb_list。
43~98行,这堆代码的目的是处理端点的bInterval,你要想不被它们给忽悠了,得明白几个问题。第一个就是,i,j,n分别表示什么。45~90这么多行就为了给它们选择一个合适的值,i和j限定了bInterval的一个范围,bInterval如果在这里边儿,它就是合法的,如果超出了这个范围,它就是非法的,就要修理修理它,像97行做的那样将n赋给它,那么n表示的就是bInterval的一个默认值。i和j的默认值分别为0和255,也就是说合法的范围默认是0~255,对于批量端点和控制端点,bInterval对你我来说并没有太大的用处,不过协议里还是规定了,这个范围只能为0~255。对于中断端点和等时端点,bInterval表演的舞台就很大了,对这个范围也要做一些调整。
第二个问题就是如何判断端点是中断的还是等时的。这涉及到两个函数usb_endpoint_xfer_int和usb_endpoint_xfer_isoc,它们都在include/uapi/linux/usb/ch9.h里定义
第三个问题是to_usb_device。usb_parse_endpoint()的参数是struct device结构体,要获得设备的速度就需要使用to_usb_device将它转化为struct usb_device结构体,这是个include/linux/usb.h里定义的宏
经过usb_parse_configuration、usb_parse_interface和usb_parse_endpoint这三个函数一步一营的层层推进,通过GET_DESCRIPTOR请求所获得那堆数据现在已经解析的清清白白。现在,设备的各个配置信息已经了然于胸,那接下来设备的那条生命线该怎么去走?它已经可以进入Configured状态了么?事情没这么简单,光是获得设备各个配置信息没用,要进入Configured状态,你还得有选择有目的有步骤有计划的去配置设备,那怎么去有选择有目的有步骤有计划?这好像就不是core能够答复的问题了,毕竟它并不知道你希望你的设备采用哪种配置,只有你的设备的驱动才知道,所以接下来设备要做的是去在设备模型的茫茫人海中寻找属于自己的驱动。
做为一个负责任的男人,绝对不能忘记的是设备的那个struct usb_device结构体在出生的时候就带有usb_bus_type和usb_device_type这样的胎记,Linux设备模型根据总线类型usb_bus_type将设备添加到usb总线的那条有名的设备链表里,然后去轮询usb总线的另外一条有名的驱动链表,针对每个找到的驱动去调用usb总线的match函数,也就是usb_device_match(),去为设备寻找另一个匹配的半圆。match函数会根据设备的自身条件和类型usb_device_type安排设备走设备那条路,从而匹配到那个对所有usb_device_type类型的设备都来者不拒的花心大萝卜,usb世界里唯一的那个usb设备驱动(不是usb接口驱动)struct
device_driver结构体对象usb_generic_driver。
drivers/usb/core/config.c
static int usb_parse_configuration(struct usb_device *dev, int cfgidx, struct usb_host_config *config, unsigned char *buffer, int size) { struct device *ddev = &dev->dev; unsigned char *buffer0 = buffer; int cfgno; int nintf, nintf_orig; int i, j, n; struct usb_interface_cache *intfc; unsigned char *buffer2; int size2; struct usb_descriptor_header *header; int len, retval; u8 inums[USB_MAXINTERFACES], nalts[USB_MAXINTERFACES]; unsigned iad_num = 0; memcpy(&config->desc, buffer, USB_DT_CONFIG_SIZE); if (config->desc.bDescriptorType != USB_DT_CONFIG || config->desc.bLength < USB_DT_CONFIG_SIZE || config->desc.bLength > size) { dev_err(ddev, "invalid descriptor for config index %d: " "type = 0x%X, length = %d\n", cfgidx, config->desc.bDescriptorType, config->desc.bLength); return -EINVAL; } cfgno = config->desc.bConfigurationValue; buffer += config->desc.bLength; size -= config->desc.bLength; nintf = nintf_orig = config->desc.bNumInterfaces; if (nintf > USB_MAXINTERFACES) { dev_warn(ddev, "config %d has too many interfaces: %d, " "using maximum allowed: %d\n", cfgno, nintf, USB_MAXINTERFACES); nintf = USB_MAXINTERFACES; } /* Go through the descriptors, checking their length and counting the * number of altsettings for each interface */ n = 0; for ((buffer2 = buffer, size2 = size); size2 > 0; (buffer2 += header->bLength, size2 -= header->bLength)) { if (size2 < sizeof(struct usb_descriptor_header)) { dev_warn(ddev, "config %d descriptor has %d excess " "byte%s, ignoring\n", cfgno, size2, plural(size2)); break; } header = (struct usb_descriptor_header *) buffer2; if ((header->bLength > size2) || (header->bLength < 2)) { dev_warn(ddev, "config %d has an invalid descriptor " "of length %d, skipping remainder of the config\n", cfgno, header->bLength); break; } if (header->bDescriptorType == USB_DT_INTERFACE) { struct usb_interface_descriptor *d; int inum; d = (struct usb_interface_descriptor *) header; if (d->bLength < USB_DT_INTERFACE_SIZE) { dev_warn(ddev, "config %d has an invalid " "interface descriptor of length %d, " "skipping\n", cfgno, d->bLength); continue; } inum = d->bInterfaceNumber; if ((dev->quirks & USB_QUIRK_HONOR_BNUMINTERFACES) && n >= nintf_orig) { dev_warn(ddev, "config %d has more interface " "descriptors, than it declares in " "bNumInterfaces, ignoring interface " "number: %d\n", cfgno, inum); continue; } if (inum >= nintf_orig) dev_warn(ddev, "config %d has an invalid " "interface number: %d but max is %d\n", cfgno, inum, nintf_orig - 1); /* Have we already encountered this interface? * Count its altsettings */ for (i = 0; i < n; ++i) { if (inums[i] == inum) break; } if (i < n) { if (nalts[i] < 255) ++nalts[i]; } else if (n < USB_MAXINTERFACES) { inums = inum; nalts = 1; ++n; } } else if (header->bDescriptorType == USB_DT_INTERFACE_ASSOCIATION) { if (iad_num == USB_MAXIADS) { dev_warn(ddev, "found more Interface " "Association Descriptors " "than allocated for in " "configuration %d\n", cfgno); } else { config->intf_assoc[iad_num] = (struct usb_interface_assoc_descriptor *)header; iad_num++; } } else if (header->bDescriptorType == USB_DT_DEVICE || header->bDescriptorType == USB_DT_CONFIG) dev_warn(ddev, "config %d contains an unexpected " "descriptor of type 0x%X, skipping\n", cfgno, header->bDescriptorType); } /* for ((buffer2 = buffer, size2 = size); ...) */ size = buffer2 - buffer; config->desc.wTotalLength = cpu_to_le16(buffer2 - buffer0); if (n != nintf) dev_warn(ddev, "config %d has %d interface%s, different from " "the descriptor's value: %d\n", cfgno, n, plural(n), nintf_orig); else if (n == 0) dev_warn(ddev, "config %d has no interfaces?\n", cfgno); config->desc.bNumInterfaces = nintf = n; /* Check for missing interface numbers */ for (i = 0; i < nintf; ++i) { for (j = 0; j < nintf; ++j) { if (inums[j] == i) break; } if (j >= nintf) dev_warn(ddev, "config %d has no interface number " "%d\n", cfgno, i); } /* Allocate the usb_interface_caches and altsetting arrays */ for (i = 0; i < nintf; ++i) { j = nalts[i]; if (j > USB_MAXALTSETTING) { dev_warn(ddev, "too many alternate settings for " "config %d interface %d: %d, " "using maximum allowed: %d\n", cfgno, inums[i], j, USB_MAXALTSETTING); nalts[i] = j = USB_MAXALTSETTING; } len = sizeof(*intfc) + sizeof(struct usb_host_interface) * j; config->intf_cache[i] = intfc = kzalloc(len, GFP_KERNEL); if (!intfc) return -ENOMEM; kref_init(&intfc->ref); } /* FIXME: parse the BOS descriptor */ /* Skip over any Class Specific or Vendor Specific descriptors; * find the first interface descriptor */ config->extra = buffer; i = find_next_descriptor(buffer, size, USB_DT_INTERFACE, USB_DT_INTERFACE, &n); config->extralen = i; if (n > 0) dev_dbg(ddev, "skipped %d descriptor%s after %s\n", n, plural(n), "configuration"); buffer += i; size -= i; /* Parse all the interface/altsetting descriptors */ while (size > 0) { retval = usb_parse_interface(ddev, cfgno, config, buffer, size, inums, nalts); if (retval < 0) return retval; buffer += retval; size -= retval; } /* Check for missing altsettings */ for (i = 0; i < nintf; ++i) { intfc = config->intf_cache[i]; for (j = 0; j < intfc->num_altsetting; ++j) { for (n = 0; n < intfc->num_altsetting; ++n) { if (intfc->altsetting .desc. bAlternateSetting == j) break; } if (n >= intfc->num_altsetting) dev_warn(ddev, "config %d interface %d has no " "altsetting %d\n", cfgno, inums[i], j); } } return 0; }代码太生猛了,还是先说点理论垫垫底儿,其实前面也说到过的,使用GET_DESCRIPTOR请求时,得到的数据并不是杂乱无序的,而是有规可循的,一般来说,配置描述符后面跟的是第一个接口的接口描述符,接着是这个接口里第一个端点的端点描述符,如果有class-和vendor-specific描述符的话,会紧跟在对应的标准描述符后面,不管接口有多少端点有多少都是按照这个规律顺序排列。当然有些厂商会特立独行一些,非要先返回第二个接口然后再返回第一个接口,但配置描述符后面总归先是接口描述符再是端点描述符。
5行,buffer里保存的就是GET_DESCRIPTOR请求获得的那堆数据,要解析这些数据,不可避免的要对buffer指针进行操作,这里先将它备份一下。
17行,config是参数里传递过来的,是设备struct usb_device结构体里的struct usb_host_config结构体数组config中的一员。不出意外的话buffer的前USB_DT_CONFIG_SIZE个字节对应的就是配置描述符,那么这里的意思就很明显了。然后做些检验,看看这USB_DT_CONFIG_SIZE字节的内容究竟是不是正如我们所期待的那样是个配置描述符,如果不是,那buffer里的数据问题可就大了,没什么利用价值了,还是返回吧,不必要再接着解析了。
28行,buffer的前USB_DT_CONFIG_SIZE个字节已经理清了,接下来该解析剩下的数据了,buffer需要紧跟形势的发展,位置和长度都要做相应的修正。
31行,获得这个配置所拥有的接口数目,不能简单一赋值就完事儿了,得知道系统里对这个数目是有个USB_MAXINTERFACES这样的限制的,如果数目比这个限制还大,就改为USB_MAXINTERFACES。
42~124行,这函数真是酷到家了,连里面一个循环都这么长这么酷,不过别看它cool,完成的事情却很单一,就是统计记录一下这个配置里每个接口所拥有的设置数目。提醒你一下,千万别被写代码的哥们儿给迷惑了,这个循环里使用的是buffer2和size2, buffer和size的两个替身,buffer和size就停在42行享受阳光海滩。
46行,这里遇到一个新的结构struct usb_descriptor_header,在include/uapi/linux/usb/ch9.h里定义
struct usb_descriptor_header { __u8 bLength; __u8 bDescriptorType; } __attribute__ ((packed));这个结构比俺还单纯,就包括了两个成员,你研究一下所有的那些标准描述符,会兴奋的发现它们的前两个字节都是一样的,一个表示描述符的长度,一个表示描述符的类型。那么为什么要专门搞这么一个结构?试想一下,有块数据缓冲区,让你判断一下里面保存的是哪个描述符,或者是其它什么东西,你怎么做?你当然可以直接将它的前两个字节内容读出来,判断判断bDescriptorType,再判断判断bLength,不过这样的代码就好像你自己画的一副抽象画,太艺术化了,过个若干年自己都不知道啥意思,更别说别人了。53行做了个很好的示范,把buffer2指针转化为struct
usb_descriptor_header的结构体指针,然后就可以使用‘->’来取出bLength和bDescriptorType,这样写的人顺心看的人舒心,你好我好大家好。
那么46行就表示如果GET_DESCRIPTOR请求返回的数据里除了包括一个配置描述符外,连两个字节都没有,那就说明这个配置在进行裸体行为艺术,能看不能用。
61行,如果这是个接口描述符就说明这个配置的某个接口拥有一个设置,是没有什么所谓的设置描述符的,一个接口描述符就代表了存在一个设置,接口描述里的bInterfaceNumber会指出这个设置隶属于哪个接口。那么这里除了是接口描述符还有可能是什么?还有可能是class-和vendor-specific描述符。
65行,既然觉得这是个接口描述符,就把这个指针转化为struct usb_interface_descriptor结构体指针,你可别被C里的这种指针游戏给转晕了,一个地址如果代码不给它赋予什么意义,它除了表示一个地址外就什么都不是。同样一个地址,上面转化为struct usb_descriptor_header结构体指针和这里转化为struct usb_interface_descriptor结构体指针,它就不再仅仅是一个地址,而是代表了不同的含义。
66行,仍然不忘保持革命斗争警惕性。bDescriptorType等于USB_DT_INTERFACE并不说明它就一定是接口描述符了,它的bLength还必须要等于USB_DT_INTERFACE_SIZE。bLength和bDescriptorType一起才能决定一个描述符。
91~102这几行是用来考验咱们的耐心和勇气的。首先要明白n、inums和nalts这几个枯燥的东东是表示什么的,n记录的是接口的数目,数组inums里的每一项都表示一个接口号,数组nalts里的每一项记录的是每个接口拥有的设置数目,inums和nalts两个数组里的元素是一一对应的,inums[0]就对应nalts[0],inums[1]就对应nalts[1]。其次还要谨记一个残酷的事实,发送GET_DESCRIPTOR请求时,设备并不一定会按照接口1,接口2这样的顺序循规蹈矩的返回数据,虽说协议里是这么要求的,但都在江湖行走谁能没点个性。
125行,buffer的最后边儿可能会有些垃圾数据,为了去除这些洋垃圾,这里需要将size和配置描述符里的那个wTotalLength修正一下。
128行,经过上面那个超酷的循环之后,如果统计得到的接口数目和配置描述符里的bNumInterfaces不符,或者干脆就没有发现配置里有什么接口,就警告一下。
137行,又一个for循环,目的是看看是不是遗漏了哪个接口号,比如说配置6个接口,为什么是6那,因为俺的幸运数字是6,呵呵,每个接口号都应该对应数组inums里的一项,如果在inums里面没有发现这个接口号,比如2吧,那2这个接口号就神秘失踪了,你找不到接口2。这个当然也属于违章驾驶,需要警告一下,开票罚600,不开票罚200,你自己选。
148行,再一个for循环,struct usb_interface_caches做嘛用的早就说过了,USB_MAXALTSETTING的定义在config.c里
#define USB_MAXALTSETTING 128 /* Hard limit */一个接口最多可以有128个设置,足够了。158行根据每个接口拥有的设置数目为对应的intf_cache数组项申请内存。
169行,配置描述符后面紧跟的不一定就是接口描述符,还可能是class-和vendor-specific描述符,如果有的话。不管有没有,先把buffer的地址赋给extra,如果没有扩展的描述符,则170行返回的i就等于0,extralen也就为0。
170行,调用find_next_descriptor()在buffer里寻找配置描述符后面跟着的第一个接口描述符。它也在config.c里定义,进去看看
static int find_next_descriptor(unsigned char *buffer, int size, int dt1, int dt2, int *num_skipped) { struct usb_descriptor_header *h; int n = 0; unsigned char *buffer0 = buffer; /* Find the next descriptor of type dt1 or dt2 */ while (size > 0) { h = (struct usb_descriptor_header *) buffer; if (h->bDescriptorType == dt1 || h->bDescriptorType == dt2) break; buffer += h->bLength; size -= h->bLength; ++n; } /* Store the number of descriptors skipped and return the * number of bytes skipped */ if (num_skipped) *num_skipped = n; return buffer - buffer0; }这个函数需要传递两个描述符类型的参数,11行已经清清楚楚的表明它是脚踩两只船的,一个多情的种。它不是专一的去寻找一种描述符,而是去寻找两种描述符,比如你指定dt1为USB_DT_INTERFACE,dt2为USB_DT_ENDPOINT时,只要能够找到接口描述符或端点描述符中的一个,这个函数就返回。usb_parse_configuration函数的170行只需要寻找下一个接口描述符,所以dt1和dt2都设置为USB_DT_INTERFACE。
这个函数结束后,num_skipped里记录的是搜索过程中忽略的dt1和dt2之外其它描述符的数目,返回值表示搜索结束时buffer的位置比搜索开始时前进的字节数。其它没什么好讲的。还是回到usb_parse_configuration函数。
176行,根据find_next_descriptor的结果修正buffer和size。你可能因为受过很多面试的摧残,或者代码里错过很多次,对C里的按引用传递和按值传递已经烂熟于心,看到find_next_descriptor()那里传递的是buffer,一个指针,条件反射的觉得它里面对buffer的修改必定影响了外面的buffer,所以认为buffer已经指向了寻找到的接口描述符。但是你的这种如意算盘此时并不能如意,find_next_descriptor里修改的只是参数里buffer的值,并没有修改它指向的内容,对于地址本身来说仍然只能算是按值传递,怎么修改都影响不到函数外边,所以这里的176行仍然要对buffer的位置进行修正。
180行,事不过三,三个for循环之后轮到了一个while循环,如果size大于0,就说明配置描述符后面找到了一个接口描述符,根据这个接口描述符的长度,已经可以解析出一个完整的接口描述符了,但是仍然没到乐观的时候,这个接口描述符后面还会跟着一群端点描述符,再然后还会有其它的接口描述符。所以我们又迎来了另一个变态函数usb_parse_interface,先不管这个它长什么样子,毕竟usb_parse_configuration()就快到头儿了,暂时只需要知道它返回的时候,buffer的位置已经在下一个接口描述符那里了,还是那个理儿,对buffer地址本身来说是按值传递的,所以186行要对这个位置和长度进行下调整以适应新形势。那么这个while循环的意思就很明显了,对buffer一段一段的解析,直到再也找不到接口描述符了。
191行,最后这个for循环没啥实质性的内容,就是找一下每个接口是不是有哪个设置编号给漏过去了,只要有耐心,你就能看得懂。咱们接下来还是看config.c里的那个usb_parse_interface()
static int usb_parse_interface(struct device *ddev, int cfgno, struct usb_host_config *config, unsigned char *buffer, int size, u8 inums[], u8 nalts[]) { unsigned char *buffer0 = buffer; struct usb_interface_descriptor *d; int inum, asnum; struct usb_interface_cache *intfc; struct usb_host_interface *alt; int i, n; int len, retval; int num_ep, num_ep_orig; d = (struct usb_interface_descriptor *) buffer; buffer += d->bLength; size -= d->bLength; if (d->bLength < USB_DT_INTERFACE_SIZE) goto skip_to_next_interface_descriptor; /* Which interface entry is this? */ intfc = NULL; inum = d->bInterfaceNumber; for (i = 0; i < config->desc.bNumInterfaces; ++i) { if (inums[i] == inum) { intfc = config->intf_cache[i]; break; } } if (!intfc || intfc->num_altsetting >= nalts[i]) goto skip_to_next_interface_descriptor; /* Check for duplicate altsetting entries */ asnum = d->bAlternateSetting; for ((i = 0, alt = &intfc->altsetting[0]); i < intfc->num_altsetting; (++i, ++alt)) { if (alt->desc.bAlternateSetting == asnum) { dev_warn(ddev, "Duplicate descriptor for config %d " "interface %d altsetting %d, skipping\n", cfgno, inum, asnum); goto skip_to_next_interface_descriptor; } } ++intfc->num_altsetting; memcpy(&alt->desc, d, USB_DT_INTERFACE_SIZE); /* Skip over any Class Specific or Vendor Specific descriptors; * find the first endpoint or interface descriptor */ alt->extra = buffer; i = find_next_descriptor(buffer, size, USB_DT_ENDPOINT, USB_DT_INTERFACE, &n); alt->extralen = i; if (n > 0) dev_dbg(ddev, "skipped %d descriptor%s after %s\n", n, plural(n), "interface"); buffer += i; size -= i; /* Allocate space for the right(?) number of endpoints */ num_ep = num_ep_orig = alt->desc.bNumEndpoints; alt->desc.bNumEndpoints = 0; /* Use as a counter */ if (num_ep > USB_MAXENDPOINTS) { dev_warn(ddev, "too many endpoints for config %d interface %d " "altsetting %d: %d, using maximum allowed: %d\n", cfgno, inum, asnum, num_ep, USB_MAXENDPOINTS); num_ep = USB_MAXENDPOINTS; } if (num_ep > 0) { /* Can't allocate 0 bytes */ len = sizeof(struct usb_host_endpoint) * num_ep; alt->endpoint = kzalloc(len, GFP_KERNEL); if (!alt->endpoint) return -ENOMEM; } /* Parse all the endpoint descriptors */ n = 0; while (size > 0) { if (((struct usb_descriptor_header *) buffer)->bDescriptorType == USB_DT_INTERFACE) break; retval = usb_parse_endpoint(ddev, cfgno, inum, asnum, alt, num_ep, buffer, size); if (retval < 0) return retval; ++n; buffer += retval; size -= retval; } if (n != num_ep_orig) dev_warn(ddev, "config %d interface %d altsetting %d has %d " "endpoint descriptor%s, different from the interface " "descriptor's value: %d\n", cfgno, inum, asnum, n, plural(n), num_ep_orig); return buffer - buffer0; skip_to_next_interface_descriptor: i = find_next_descriptor(buffer, size, USB_DT_INTERFACE, USB_DT_INTERFACE, NULL); return buffer - buffer0 + i; }14行,传递过来的buffer里开头儿那部分只能是一个接口描述符,没有什么可质疑的,所以这里将地址转化为struct usb_interface_descriptor结构体指针,然后调整buffer的位置和size。
18行,只能是并不说明它就是,只有bLength等于USB_DT_INTERFACE_SIZE才说明开头儿的USB_DT_INTERFACE_SIZE字节确实是个接口描述符。否则就没必要再对这些数据进行什么处理了,直接跳到最后吧。先看看这个函数的最后都发生了什么,从新的位置开始再次调用find_next_descriptor()在buffer里寻找下一个接口描述符。
22行,因为数组inums并不一定是按照接口的顺序来保存接口号的,inums[1]对应的可能是接口1也可能是接口0,所以这里要用for循环来寻找这个接口对应着inums里的哪一项,从而根据在数组里的位置获得接口对应的struct usb_interface_cache结构体。usb_parse_configuration()已经告诉了我们,同一个接口在inums和intf_cache这两个数组里的位置是一样的。
34行,获得这个接口描述符对应的设置编号,然后根据这个编号从接口的cache里搜索看这个设置是不是已经遇到过了,如果已经遇到过,就没必要再对这个接口描述符进行处理,直接跳到最后,否则意味着发现了一个新的设置,要将它添加到cache里,并cache里的设置数目num_altsetting加1。要记住,设置是用struct usb_host_interface结构来表示的,一个接口描述符就对应一个设置。
51行,这段代码好熟悉啊。现在buffer开头儿的那个接口描述符已经理清了,要解析它后面的那些数据了。先把位置赋给这个刚解析出来的接口描述符的extra,然后再从这个位置开始去寻找下一个距离最近的一个接口描述符或端点描述符。如果这个接口描述符后面还跟有class-或vendor-specific描述符,则find_next_descriptor的返回值会大于0,buffer的位置和size也要进行相应的调整,来指向新找到的接口描述符或端点描述符。
这里find_next_descriptor的dt1参数和dt2参数就不再一样了,因为如果一个接口只用到端点0,它的接口描述符后边儿是不会跟有端点描述符的。
62行,获得这个设置使用的端点数目,然后将相应接口描述符里的bNumEndpoints置0,为什么?你要往下看。USB_MAXENDPOINTS在include/linux/usb.h里定义
#define USB_MAXENDPOINTS 30为什么这个最大上限为30?前面也提到过,如果你不想频繁的蓦然回首那就简单认为是协议里这么规定的好了。然后根据端点数为接口描述符里的endpoint数组申请内存。
81行,走到这里,buffer开头儿的那个接口描述符已经理清了,而且也找到了下一个接口描述符或端点描述符的位置,该从这个新的位置开始解析了,于是又遇到了一个似曾相识的while循环。82行先判断一下前面找到的是接口描述符还是端点描述符,如果是接口描述符就中断这个while循环,返回与下一个接口描述符的距离。否则说明在buffer当前的位置上待着的是一个端点描述符,因此就要迎来另一个函数usb_parse_endpoint对面紧接着的数据进行解析。usb_parse_endpoint()返回的时候,buffer的位置已经在下一个端点描述符那里了,91行调整buffer的位置长度,这个while循环的也很明显了,对buffer一段一段的解析,直到遇到下一个接口描述符或者已经走到buffer结尾。现在看看config.c里定义的usb_parse_endpoint函数
static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum, int asnum, struct usb_host_interface *ifp, int num_ep, unsigned char *buffer, int size) { unsigned char *buffer0 = buffer; struct usb_endpoint_descriptor *d; struct usb_host_endpoint *endpoint; int n, i, j, retval; d = (struct usb_endpoint_descriptor *) buffer; buffer += d->bLength; size -= d->bLength; if (d->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE) n = USB_DT_ENDPOINT_AUDIO_SIZE; else if (d->bLength >= USB_DT_ENDPOINT_SIZE) n = USB_DT_ENDPOINT_SIZE; else { dev_warn(ddev, "config %d interface %d altsetting %d has an " "invalid endpoint descriptor of length %d, skipping\n", cfgno, inum, asnum, d->bLength); goto skip_to_next_endpoint_or_interface_descriptor; } i = d->bEndpointAddress & ~USB_ENDPOINT_DIR_MASK; if (i >= 16 || i == 0) { dev_warn(ddev, "config %d interface %d altsetting %d has an " "invalid endpoint with address 0x%X, skipping\n", cfgno, inum, asnum, d->bEndpointAddress); goto skip_to_next_endpoint_or_interface_descriptor; } /* Only store as many endpoints as we have room for */ if (ifp->desc.bNumEndpoints >= num_ep) goto skip_to_next_endpoint_or_interface_descriptor; endpoint = &ifp->endpoint[ifp->desc.bNumEndpoints]; ++ifp->desc.bNumEndpoints; memcpy(&endpoint->desc, d, n); INIT_LIST_HEAD(&endpoint->urb_list); /* Fix up bInterval values outside the legal range. Use 32 ms if no * proper value can be guessed. */ i = 0; /* i = min, j = max, n = default */ j = 255; if (usb_endpoint_xfer_int(d)) { i = 1; switch (to_usb_device(ddev)->speed) { case USB_SPEED_SUPER: case USB_SPEED_HIGH: /* Many device manufacturers are using full-speed * bInterval values in high-speed interrupt endpoint * descriptors. Try to fix those and fall back to a * 32 ms default value otherwise. */ n = fls(d->bInterval*8); if (n == 0) n = 9; /* 32 ms = 2^(9-1) uframes */ j = 16; /* * Adjust bInterval for quirked devices. * This quirk fixes bIntervals reported in * linear microframes. */ if (to_usb_device(ddev)->quirks & USB_QUIRK_LINEAR_UFRAME_INTR_BINTERVAL) { n = clamp(fls(d->bInterval), i, j); i = j = n; } break; default: /* USB_SPEED_FULL or _LOW */ /* For low-speed, 10 ms is the official minimum. * But some "overclocked" devices might want faster * polling so we'll allow it. */ n = 32; break; } } else if (usb_endpoint_xfer_isoc(d)) { i = 1; j = 16; switch (to_usb_device(ddev)->speed) { case USB_SPEED_HIGH: n = 9; /* 32 ms = 2^(9-1) uframes */ break; default: /* USB_SPEED_FULL */ n = 6; /* 32 ms = 2^(6-1) frames */ break; } } if (d->bInterval < i || d->bInterval > j) { dev_warn(ddev, "config %d interface %d altsetting %d " "endpoint 0x%X has an invalid bInterval %d, " "changing to %d\n", cfgno, inum, asnum, d->bEndpointAddress, d->bInterval, n); endpoint->desc.bInterval = n; } /* Some buggy low-speed devices have Bulk endpoints, which is * explicitly forbidden by the USB spec. In an attempt to make * them usable, we will try treating them as Interrupt endpoints. */ if (to_usb_device(ddev)->speed == USB_SPEED_LOW && usb_endpoint_xfer_bulk(d)) { dev_warn(ddev, "config %d interface %d altsetting %d " "endpoint 0x%X is Bulk; changing to Interrupt\n", cfgno, inum, asnum, d->bEndpointAddress); endpoint->desc.bmAttributes = USB_ENDPOINT_XFER_INT; endpoint->desc.bInterval = 1; if (usb_endpoint_maxp(&endpoint->desc) > 8) endpoint->desc.wMaxPacketSize = cpu_to_le16(8); } /* * Some buggy high speed devices have bulk endpoints using * maxpacket sizes other than 512. High speed HCDs may not * be able to handle that particular bug, so let's warn... */ if (to_usb_device(ddev)->speed == USB_SPEED_HIGH && usb_endpoint_xfer_bulk(d)) { unsigned maxp; maxp = usb_endpoint_maxp(&endpoint->desc) & 0x07ff; if (maxp != 512) dev_warn(ddev, "config %d interface %d altsetting %d " "bulk endpoint 0x%X has invalid maxpacket %d\n", cfgno, inum, asnum, d->bEndpointAddress, maxp); } /* Parse a possible SuperSpeed endpoint companion descriptor */ if (to_usb_device(ddev)->speed == USB_SPEED_SUPER) usb_parse_ss_endpoint_companion(ddev, cfgno, inum, asnum, endpoint, buffer, size); /* Skip over any Class Specific or Vendor Specific descriptors; * find the next endpoint or interface descriptor */ endpoint->extra = buffer; i = find_next_descriptor(buffer, size, USB_DT_ENDPOINT, USB_DT_INTERFACE, &n); endpoint->extralen = i; retval = buffer - buffer0 + i; if (n > 0) dev_dbg(ddev, "skipped %d descriptor%s after %s\n", n, plural(n), "endpoint"); return retval; skip_to_next_endpoint_or_interface_descriptor: i = find_next_descriptor(buffer, size, USB_DT_ENDPOINT, USB_DT_INTERFACE, NULL); return buffer - buffer0 + i; }一个一个变态的函数看过来,到现在都已经麻木了。但是遇到这种函数,你谁都不能骂,对linus,对Greg,对Alan,你有的只能是崇敬。
10行,buffer开头儿只能是一个端点描述符,所以这里将地址转化为struct usb_endpoint_descriptor结构体指针,然后调整buffer的位置和size。
14行,这里要明白的是端点描述符与配置描述符、接口描述符不一样,它是可能有两种大小的。
25行,得到端点号。这里的端点号不能为0,因为端点0是没有描述符的,也不能大于16,为什么?同样如果你不想蓦然回首,就当成协议里规定的吧。
34行,要知道这个bNumEndpoints在usb_parse_interface()的63行是被赋为0了的。
37行,要知道这个endpoint数组在usb_parse_interface()的74行也是已经申请好内存了的。从这里你应该明白bNumEndpoints是被当成了一个计数器,发现一个端点描述符,它就加1,并把找到的端点描述符copy到设置的endpoint数组里。
41行,初始化端点的urb队列urb_list。
43~98行,这堆代码的目的是处理端点的bInterval,你要想不被它们给忽悠了,得明白几个问题。第一个就是,i,j,n分别表示什么。45~90这么多行就为了给它们选择一个合适的值,i和j限定了bInterval的一个范围,bInterval如果在这里边儿,它就是合法的,如果超出了这个范围,它就是非法的,就要修理修理它,像97行做的那样将n赋给它,那么n表示的就是bInterval的一个默认值。i和j的默认值分别为0和255,也就是说合法的范围默认是0~255,对于批量端点和控制端点,bInterval对你我来说并没有太大的用处,不过协议里还是规定了,这个范围只能为0~255。对于中断端点和等时端点,bInterval表演的舞台就很大了,对这个范围也要做一些调整。
第二个问题就是如何判断端点是中断的还是等时的。这涉及到两个函数usb_endpoint_xfer_int和usb_endpoint_xfer_isoc,它们都在include/uapi/linux/usb/ch9.h里定义
static inline int usb_endpoint_xfer_int( const struct usb_endpoint_descriptor *epd) { return ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT); }
static inline int usb_endpoint_xfer_isoc( const struct usb_endpoint_descriptor *epd) { return ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_ISOC); }这俩函数so直白,一点都不含蓄,你根本不用去猜它们的心思就能明明白白了。一桌麻将还差两个,另外两个就是usb_endpoint_xfer_bulk和usb_endpoint_xfer_control,用来判断批量端点和控制端点的。
第三个问题是to_usb_device。usb_parse_endpoint()的参数是struct device结构体,要获得设备的速度就需要使用to_usb_device将它转化为struct usb_device结构体,这是个include/linux/usb.h里定义的宏
#define to_usb_device(d) container_of(d, struct usb_device, dev)OK,接着继续看usb_parse_endpoint的139行,现在你对这几行玩的把戏应该很明白了。这里接着在buffer里寻找下一个端点描述符或者接口描述符。
经过usb_parse_configuration、usb_parse_interface和usb_parse_endpoint这三个函数一步一营的层层推进,通过GET_DESCRIPTOR请求所获得那堆数据现在已经解析的清清白白。现在,设备的各个配置信息已经了然于胸,那接下来设备的那条生命线该怎么去走?它已经可以进入Configured状态了么?事情没这么简单,光是获得设备各个配置信息没用,要进入Configured状态,你还得有选择有目的有步骤有计划的去配置设备,那怎么去有选择有目的有步骤有计划?这好像就不是core能够答复的问题了,毕竟它并不知道你希望你的设备采用哪种配置,只有你的设备的驱动才知道,所以接下来设备要做的是去在设备模型的茫茫人海中寻找属于自己的驱动。
做为一个负责任的男人,绝对不能忘记的是设备的那个struct usb_device结构体在出生的时候就带有usb_bus_type和usb_device_type这样的胎记,Linux设备模型根据总线类型usb_bus_type将设备添加到usb总线的那条有名的设备链表里,然后去轮询usb总线的另外一条有名的驱动链表,针对每个找到的驱动去调用usb总线的match函数,也就是usb_device_match(),去为设备寻找另一个匹配的半圆。match函数会根据设备的自身条件和类型usb_device_type安排设备走设备那条路,从而匹配到那个对所有usb_device_type类型的设备都来者不拒的花心大萝卜,usb世界里唯一的那个usb设备驱动(不是usb接口驱动)struct
device_driver结构体对象usb_generic_driver。
相关文章推荐
- 【转】Linux那些事儿 之 戏说USB(32)设备的生命线(十一)
- Linux那些事儿 之 戏说USB(32)设备的生命线(十一)
- Linux那些事儿 之 戏说USB(32)设备的生命线(十一)
- 【转】Linux那些事儿 之 戏说USB(28)设备的生命线(七)
- Linux那些事儿 之 戏说USB(28)设备的生命线(七)
- Linux那些事儿 之 戏说USB(28)设备的生命线(七)
- 【转】Linux那些事儿 之 戏说USB(23)设备的生命线(二)
- Linux那些事儿 之 戏说USB(22)设备的生命线(一)
- Linux那些事儿 之 戏说USB(29)设备的生命线(八)
- 【转】Linux那些事儿 之 戏说USB(26)设备的生命线(五)
- 【转】Linux那些事儿 之 戏说USB(24)设备的生命线(三)
- Linux那些事儿 之 戏说USB(30)设备的生命线(九)
- 【转】Linux那些事儿 之 戏说USB(29)设备的生命线(八)
- Linux那些事儿 之 戏说USB(20)设备的生命线(三)
- 【转】Linux那些事儿 之 戏说USB(22)设备的生命线(一)
- Linux那些事儿 之 戏说USB(24)设备的生命线(七)
- Linux那些事儿 之 戏说USB(25)设备的生命线(八)
- Linux那些事儿 之 戏说USB(19)设备的生命线(二)
- 【转】Linux那些事儿 之 戏说USB(27)设备的生命线(六)
- Linux那些事儿 之 戏说USB(31)设备的生命线(十)