【转】 Linux那些事儿之我是U盘(22)通往春天的管道

1990,两伊战争,电台里报道早间新闻,播音员说:各位听众朋友,昨天伊拉克截断了科威特的两条输卵管道.(输油管道)

此时,树无语天无语人无语.

一年后,公元1991,一个芬兰人写了一个叫做Linux的操作系统,他也觉得这位播音员很有趣,给听众朋友们带来了欢乐.于是为了纪念这件经典的口误,这个芬兰人在Linux中引入了管道这么一个概念,并且他把管道用在很多地方,文件系统中,设备驱动中,于是后来我们看到在Linux中有了各种各样的管道,不过,没有输油管道,更没有输卵管道.但是相同的是,所有的管道都是用来传输东西的,只不过有些管道传输的是实实在在的物质,而有些管道传输的是数据.

眼下我们在usb代码中看到的管道就是用来传输数据,用来通信. 通信是双方的,不可能自言自语对吧.而在usb的通信中,一方肯定是主机,host,另一方是什么?是设备吗?在你少不更事的岁月里,你可以说是设备,你可以说主机和设备进行通信.但是,现在,当你已经长大,当你已经成熟,在这样一个激情燃烧的岁月里,你应该说得更确切一点.真正和主机进行通信的是设备内的端点.关于端点,我们也可以专业一点说,从硬件上来看它是实实在在存在的,它被实现为一种FIFO,支持多少个端点是接口芯片的一个重要指标.而从概念上来说,端点是主机和usb设备之间通信流的终点.主机和设备可以进行不同种类的通信,或者说数据传输,首先,设备连接在usb总线上,usb总线为了分辨每一个设备,给每一个设备编上号,然后为了实现多种通信,设备方于是提供了端点,端点多了,自然也要编上号,而让主机最终和端点去联系.

鲁迅先生说得好,世上本没有管道,端点多了,也就有了管道.

这句话说得相当好,非常有道理,两个字,犀利!没错,我们知道,usb的世界里,有四种管道,它们对应usb世界中的四种传输方式,控制传输对应控制管道,中断传输对应中断管道,批量传输对应批量管道,等时传输对应等时管道.每一个usb世界中的设备要在usb世界里生存,就得有它生存的管道.而管道的出现,正是为了让我们分辨端点,或者说连接端点.记得网友唐伯虎点_蚊_香曾经说过,主机与端点之间的数据链接就称为管道.

比如说,复旦大学,有一个主校区,也可以说是教学区,(当然也包括一些实验室),上课什么的都得去教学区,把教学区看作主机,那么与其相对的是,另外有很多学生宿舍楼,宿舍楼多了,就给每个楼编上号,比如1号楼,2号楼,...,36号楼,..,每幢楼算一个设备,比如说你在淘宝网上买了一套阿玛尼外套(当然,肯定是假的,也就一两百的那种),你让人家给你快递过来,人家问你你住哪?你说你住复旦大学,但如果你只说你在复旦大学,那么送快递那哥们可能先得赶到复旦大学正门,或者学生宿舍区的正门,然后人家肯定就得问,你是哪幢楼哪间房?比如你说你是36号楼201,,那么像201这么一个数字呢,就对应端点号,最终那套外套要到达的就是端点201,而不仅仅是36号楼,对吧,假如人家要是送到36号楼下就把外套给扔地上了你肯定得跟他急.那么在这个例子里,复旦主校区是主机,每幢宿舍楼算一个设备,你住的那间宿舍就算端点.那么pipe?pipe很难与现实中的某一实物对应,不能说她是复旦正门通往宿舍的某条路,而应该按别的方式理解.她包含很多东西,你可以把她比作快递的货物上面贴得那张标签,比如她上面写了收货人的地址,包括多少号楼多少号房,usb里面,就是设备号和端点号,知道了这两个号,货物就能确定它的目的地,usb主机就能知道和她通信的是哪个端点.pipe除了包含着两个号以外,还包含其它一些信息,比如她包含了通信的方向,也就是说,你能从那张标签上得知36号楼201这个是收件人呢还是寄件人,虽然现实中不需要写明,因为快递员肯定知道你是收件人,他没事才不会和寄件人联系呢.再比如,pipe里还包含了pipe的类型,比如你快递是从深圳递过来,那么怎么递就得看了,快递公司肯定提供不同类型的服务,有的快有的慢,有的可能还有保险什么的,看你出多少钱,让你选择不同的服务类型,同样pipe也如此,因为usb设备的端点有不同的类型,所以pipe里就包含了一个字段来记录这一点,这个字段称为pipe type.,让我们来看看实际的pipe.

  来看这些宏,头一个闪亮登场的是usb_sndctrlpipe,定义于include/linux/usb.h,咱们先把这一堆冬冬相关的代码给列出来.

   1104 static inline unsigned int __create_pipe(struct usb_device *dev, unsigned int endpoint)
   1105 {
   1106         return (dev->devnum << 8) | (endpoint << 15);
   1107 }
   1108
   1109 /* Create various pipes... */
   1110 #define usb_sndctrlpipe(dev,endpoint)   ((PIPE_CONTROL << 30) | __create_pipe(dev,endpoint))
   1111 #define usb_rcvctrlpipe(dev,endpoint)   ((PIPE_CONTROL << 30) | __create_pipe(dev,endpoint) | USB_DIR_IN)
   1112 #define usb_sndisocpipe(dev,endpoint)   ((PIPE_ISOCHRONOUS << 30) | __create_pipe(dev,endpoint))
   1113 #define usb_rcvisocpipe(dev,endpoint)   ((PIPE_ISOCHRONOUS << 30) | __create_pipe(dev,endpoint) | USB_DIR_IN)
   1114 #define usb_sndbulkpipe(dev,endpoint)   ((PIPE_BULK << 30) | __create_pipe(dev,endpoint))
   1115 #define usb_rcvbulkpipe(dev,endpoint)   ((PIPE_BULK << 30) | __create_pipe(dev,endpoint) | USB_DIR_IN)
   1116 #define usb_sndintpipe(dev,endpoint)    ((PIPE_INTERRUPT << 30) | __create_pipe(dev,endpoint))
   1117 #define usb_rcvintpipe(dev,endpoint)    ((PIPE_INTERRUPT << 30) | __create_pipe(dev,endpoint) | USB_DIR_IN)
  先看1110,把这个宏展开,PIPE_CONTROL也是宏,也是定义于同一文件中,include/linux/usb.h,

   1040 /* -------------------------------------------------------------------------- */
   1041
   1042 /*
   1043  * Calling this entity a "pipe" is glorifying it. A USB pipe
   1044  * is something embarrassingly simple: it basically consists
   1045  * of the following information:
   1046  *  - device number (7 bits)
   1047  *  - endpoint number (4 bits)
   1048  *  - current Data0/1 state (1 bit) [Historical; now gone]
   1049  *  - direction (1 bit)
   1050  *  - speed (1 bit) [Historical and specific to USB 1.1; now gone.]
   1051  *  - max packet size (2 bits: 8, 16, 32 or 64) [Historical; now gone.]
   1052  *  - pipe type (2 bits: control, interrupt, bulk, isochronous)
   1053  *
   1054  * That's 18 bits. Really. Nothing more. And the USB people have
   1055  * documented these eighteen bits as some kind of glorious
   1056  * virtual data structure.
   1057  *
   1058  * Let's not fall in that trap. We'll just encode it as a simple
   1059  * unsigned int. The encoding is:
   1060  *
   1061  *  - max size:         bits 0-1        [Historical; now gone.]
   1062  *  - direction:        bit 7           (0 = Host-to-Device [Out],
   1063  *                                       1 = Device-to-Host [In] ...
   1064  *                                      like endpoint bEndpointAddress)
   1065  *  - device:           bits 8-14       ... bit positions known to uhci-hcd
   1066  *  - endpoint:         bits 15-18      ... bit positions known to uhci-hcd
   1067  *  - Data0/1:          bit 19          [Historical; now gone. ]
   1068  *  - lowspeed:         bit 26          [Historical; now gone. ]
   1069  *  - pipe type:        bits 30-31      (00 = isochronous, 01 = interrupt,
   1070  *                                       10 = control, 11 = bulk)
   1071  *
   1072  * Why? Because it's arbitrary, and whatever encoding we select is really
   1073  * up to us. This one happens to share a lot of bit positions with the UHCI
   1074  * specification, so that much of the uhci driver can just mask the bits
   1075  * appropriately.
   1076  */
   1077
   1078 /* NOTE:  these are not the standard USB_ENDPOINT_XFER_* values!! */
   1079 #define PIPE_ISOCHRONOUS                0
   1080 #define PIPE_INTERRUPT                  1
   1081 #define PIPE_CONTROL                    2
   1082 #define PIPE_BULK                       3
  咱们知道usb有四种传输方式,等时传输,中断传输,控制传输,批量传输.一个设备能支持这四种传输中的哪一种或者哪几种是设备本身的属性,在硬件设计的时候就确定了,比如一个纯粹的u,她肯定是支持Bulk传输和Control传输的.不同的传输要求有不同的端点,所以对于u盘来说,她一定会有Bulk端点和Control端点,于是就得使用相应的pipe来跟不同的端点联系.在这里我们看到了四个宏,其中,PIPE_ISOCHRONOUS就是标志等时通道,PIPE_INTERRUPT就是中断通道,PIPE_CONTROL就是控制通道,PIPE_BULK就是BULK通道.

  另外__create_pipe也是一个宏,由上面她的定义可以看出她为构造一个宏提供了设备号和端点号.在内核里使用一个unsigned int类型的变量来表征一个pipe,其中8~14位是设备号,devnum,15~18位是端点号,endpoint.而咱们还看到有这么一个宏,USB_DIR_IN,她是用来在pipe里面标志数据传输方向的,一个管道要么是只能输入要么是只能输出,鱼和熊掌不可兼得也.include/linux/usb_ch9.h中有的:

     24
     25 /*
     26  * USB directions
     27  *
     28  * This bit flag is used in endpoint descriptors' bEndpointAddress field.
     29  * It's also one of three fields in control requests bRequestType.
     30  */
     31 #define USB_DIR_OUT                     0               /* to device */
     32 #define USB_DIR_IN                      0x80            /* to host */
  pipe里面,7(bit 7)是表征方向的.所以这里0x80也就是说让bit 71,这就表示传输方向是由设备向主机的,也就是所谓的in,而如果这一位是0,就表示传输方向是由主机向设备的,也就是所谓的out.而正是因为USB_DIR_OUT0,USB_DIR_IN1,所以我们看到定义管道的时候只有用到了USB_DIR_IN,而没有用到USB_DIR_OUT,因为她是0,任何数和0相或都没有意义.

  这样,咱们就知道了,get_pipes函数里741,742行就是为us的控制输入和控制输出管道赋了值,管道是单向的,但是有一个例外,那就是控制端点,控制端点是双向的,比如你36号楼201这个端点既可以是往外寄东西,也可以是别人给你寄东西而作为收件人地址.usb规范规定了,每一个usb设备至少得有一个控制端点,其端点号为0.其它端点有没有得看具体设备而定,但这个端点是放之四海而皆准的,不管你是什么设备,只要你是usb这条道上的,那你就得遵守这么一个规矩,没得商量.所以我们看到741,742行里传递的endpoint变量值为0.显然其构造的两个管道就是对应这个0号控制端点的.而接下来几行,就是构造bulk管道和中断管道(如果有中断端点的话).

  对于bulk端点和中断端点(如果有的话),在她们的端点描述符里有这么一个字段,bEndpointAddress,这个字段共八位,但是她包含了挺多信息的,比如这个端点是输入端点还是输出端点,比如这个端点的地址,(总线枚举的时候给她分配的),以及这个端点的端点号,不过要取得她的端点号得用一个掩码USB_ENDPOINT_NUMBER_MASK,bEndpointAddressUSB_ENDPOINT_NUMBER_MASK相与就能得到她的端点号.(就好比一份藏头诗,你得按着特定的方法才能读懂她,而这里特定的方法就是和USB_ENDPOINT_NUMBER_MASK这个掩码相与就行了.)

  750,对于中断端点,您还得使用端点描述符中的bInterval字段,表示端点的中断请求间隔时间.

  至此,get_pipes函数结束了,信息都保存到了us里面.下面us该发挥她的作用了.回到storage_probe()函数,998,us作为参数传递给了usb_stor_acquire_resources()函数.而这个函数才是故事的高潮.每一个有识之士在看过这个函数之后就会豁然开朗,都会感慨,一下子就看到了春天,看到了光明,原来Linux中的设备驱动程序就是这么工作的啊!

996
    997         /* Acquire all the other resources and add the host */
    998         result = usb_stor_acquire_resources(us);
    999         if (result)
   1000                 goto BadDevice;
   1001         result = scsi_add_host(us->host, &intf->dev);
   1002         if (result) {
   1003                 printk(KERN_WARNING USB_STORAGE
   1004                         "Unable to add the scsi host/n");
   1005                 goto BadDevice;
   1006         }

我们来看usb_stor_acquire_resources函数.它被定义于drivers/usb/storage/usb.c:

755 /* Initialize all the dynamic resources we need */
    756 static int usb_stor_acquire_resources(struct us_data *us)
    757 {
    758         int p;
    759
    760         us->current_urb = usb_alloc_urb(0, GFP_KERNEL);
    761         if (!us->current_urb) {
    762                 US_DEBUGP("URB allocation failed/n");
    763                 return -ENOMEM;
    764         }
    765
    766         /* Lock the device while we carry out the next two operations */
    767         down(&us->dev_semaphore);
    768
    769         /* For bulk-only devices, determine the max LUN value */
    770         if (us->protocol == US_PR_BULK) {
    771                 p = usb_stor_Bulk_max_lun(us);
    772                 if (p < 0) {
    773                         up(&us->dev_semaphore);
    774                         return p;
    775                 }
    776                 us->max_lun = p;
    777         }
    778
    779         /* Just before we start our control thread, initialize
    780          * the device if it needs initialization */
    781         if (us->unusual_dev->initFunction)
    782                 us->unusual_dev->initFunction(us);
    783
    784         up(&us->dev_semaphore);
    785
    786         /*
    787          * Since this is a new device, we need to register a SCSI
    788          * host definition with the higher SCSI layers.
    789          */
    790         us->host = scsi_host_alloc(&usb_stor_host_template, sizeof(us));
    791         if (!us->host) {
    792                 printk(KERN_WARNING USB_STORAGE
    793                         "Unable to allocate the scsi host/n");
    794                 return -EBUSY;
    795         }
    796
    797         /* Set the hostdata to prepare for scanning */
    798         us->host->hostdata[0] = (unsigned long) us;
    799
    800         /* Start up our control thread */
    801         p = kernel_thread(usb_stor_control_thread, us, CLONE_VM);
    802         if (p < 0) {
    803                 printk(KERN_WARNING USB_STORAGE
    804                        "Unable to start control thread/n");
    805                 return p;
    806         }
    807         us->pid = p;
    808
    809         /* Wait for the thread to start */
    810         wait_for_completion(&(us->notify));
    811
    812         return 0;
    813 }

待到山花浪漫时,她在丛中笑.一个悟性高的人应该一眼就能从这个函数中找出那行在丛中笑的代码来,没错,她就是801,kernel_thread,这个函数造就了许多的经典的Linux内核模块,正是因为她的存在,Linux中某些设备驱动程序的编写变得非常简单.可以说,对某些设备驱动程序来说,kernel_thread几乎是整个driver的灵魂,或者说是该Linux内核模块的灵魂.不管她隐藏的多么深,她总像漆黑中的萤火虫,那样的鲜明,那样的出众.甚至不夸张的说,对于很多模块来说,只要找到kernel_thread这一行,基本上你就知道这个模块是怎么工作的了. 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值