`
javatome
  • 浏览: 844479 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

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这一行,基本上你就知道这个模块是怎么工作的了.

分享到:
评论

相关推荐

    Linux那些事儿之我是U盘

    根据给定的信息,“Linux那些事儿之我是U盘”这一标题及描述主要聚焦于USB技术在Linux环境下的工作原理和技术细节。接下来,我们将基于这个主题展开深入探讨,涵盖USB技术的基本概念、USB在Linux系统中的实现机制...

    Linux那些事儿

    Linux那些事儿之我是U盘 Linux那些事儿之我是Hub Linux那些事儿之我是USB Core Linux那些事儿之我是UHCI Linux那些事儿之我是EHCI控制器 Linux那些事儿之我是PCI Linux那些事儿之我是SCSI硬盘 Linux那些事儿之我是...

    Linux那些事儿1-9合集

    读过《linux那些事儿之我是U盘》的人,都知道其风格,我就不多说了。 导读: linux那些事儿之我是U盘 linux那些事儿之我是HUB linux那些事儿之我是USB Core linux那些事儿之我是UHCI Linux那些事儿之我是EHCI主机控制...

    Linux那些事儿之我是USB(第2版)

    ### Linux那些事儿之我是USB(第2版)关键知识点概览 #### 一、书籍概述 - **核心主题**:本书主要围绕Linux内核中的USB子系统展开,深入剖析其工作原理和技术细节。 - **目标读者**:面向Linux初学者、驱动开发者...

    linux那些事儿之我是USB.zip

    本压缩包文件"linux那些事儿之我是USB.zip"包含了深入理解Linux USB驱动及内核相关知识的九个文档,包括Block层、EHCI主机控制器、HUB、PCI、SCSI硬盘、Sysfs、UHCI、USB core以及U盘。这些文档旨在提供一个系统性的...

    LINUX\Linux那些事儿系列

    3. **Linux那些事儿之我是U盘.pdf**: USB闪存驱动器,通常称为U盘,是常见的便携式存储设备。这部分可能讲述Linux如何识别、挂载和管理U盘,包括使用ums(USB Mass Storage)驱动程序,以及如何处理文件系统的读写...

    Linux那些事儿之全集

    导读.doc Linux那些事儿之我是Block层.pdf Linux那些事儿之我是EHCI主机控制器.pdf Linux那些事儿之我是Hub.pdf Linux那些事儿之我是USB_core.pdf Linux那些事儿之我是U盘.pdf等等 Linux那些事儿系列全在这里了

    Linux那些事儿系列.rar

    》包括《Linux那些事儿之我是Hub》、《Linux那些事儿之我是Sysfs》《Linux那些事儿之我是UHCI》、《Linux那些事儿之我是USB core》、《Linux那些事儿之我是U盘》,令人叹为观止的一个linux系列书籍。只能说,江山代...

    linux 那些事儿全集

    “Linux那些事儿之我是U盘.pdf”将关注通用串行总线(USB)闪存驱动器。这部分会解释Linux如何识别和处理USB闪存设备,包括文件系统的挂载和数据交换过程。 “Linux那些事儿之我是USB Core.pdf”涉及Linux内核的USB...

    linux的那些事儿全集

    Linux那些事儿之我是Block层 Linux那些事儿之我是EHCI主机控制器 Linux那些事儿之我是Hub Linux那些事儿之我是PCI Linux那些事儿之我是SCSI硬盘 Linux那些事儿之我是Sysfs ...Linux那些事儿之我是U盘

    linux那些事儿之我是U盘

    ### Linux与USB存储设备——《Linux那些事儿之我是U盘》知识点提炼 #### 引言 本文基于一篇幽默且深入的教程《Linux那些事儿之我是U盘》,该文章通过作者的亲身经历和深入浅出的讲解,介绍了Linux操作系统中USB存储...

Global site tag (gtag.js) - Google Analytics