免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
楼主: projl
打印 上一主题 下一主题

《Linux 那些事儿之我是U盘》 [复制链接]

论坛徽章:
0
51 [报告]
发表于 2008-06-06 14:00 |只看该作者
607 #endif

608

609 #ifdef CONFIG_USB_STORAGE_FREECOM

610

case US_PR_FREECOM:
611


us->transport_name = "Freecom"
;
612


us->transport = freecom_transport;
613


us->transport_reset = usb_stor_freecom_reset;
614


us->max_lun = 0;
615 break;
616 #endif
617
618 #ifdef CONFIG_USB_STORAGE_DATAFAB
619 case US_PR_DATAFAB:
620 us->transport_name = "Datafab Bulk-Only"
;
621 us->transport = datafab_transport;
622 us->transport_reset = usb_stor_Bulk_reset;
623 us->max_lun = 1;
624 break;
625 #endif
626
627 #ifdef CONFIG_USB_STORAGE_JUMPSHOT
628 case US_PR_JUMPSHOT:
629 us->transport_name = "Lexar Jumpshot Control/Bulk"
;
630 us->transport = jumpshot_transport;
631 us->transport_reset = usb_stor_Bulk_reset;
632 us->max_lun = 1;
633 break;
634 #endif
635
636 default:
637 return -EIO;
638
}
639 US_DEBUGP("Transport: %s\n", us->transport_name)
;
640
641 /* fix for single-lun devices *
/
642 if (us->flags & US_FL_SINGLE_LUN)
643


us->max_lun = 0;
644 return 0;
645
}


咋一看,这么长一段,用长沙话讲,这叫非洲老头子跳高—吓(黑)老子一跳.(长沙话”黑”和”吓”一个音)不
过明眼人一看,就知道了,主要就是一个switch, 选择语句,语法上来说很简单,谭浩强大哥的书里边介绍的很
清楚.所以我们看懂这段代码不难,只是,我想说的是,虽然这里做出一个选择不难,但是不同选择就意味着后
来整个故事会有千差万别的结局,当鸟儿选择在两翼上系上黄金,就意味着它放弃展翅高飞;选择云天搏击,

论坛徽章:
0
52 [报告]
发表于 2008-06-06 14:01 |只看该作者
就意味着放弃身外的负累.所以,此处,我们需要仔细的看清楚我们究竟选择了怎样一条路.很显然,前面我们
已经说过,对于U 盘,spec 规定了,它就属于Bulk-only 的传输方式,即它的us->protocol 就是
US_PR_BULK. 这是我们刚刚在get_device_info 中确定下来的.于是,在整个switch 段落中,我们所执行
的只是US_PR_BULK 这一段,即,

us 的transport_name 被赋值为”Bulk”,transport 被赋值为
usb_stor_Bulk_transport,transport_reset 被赋值为usb_stor_Bulk_reset. 其中我们最需要记住的
是,us 的成员transport 和transport_reset 是两个函数指针.程序员们把这个称作钩子.这两个赋值我们
需要牢记,日后我们定会用到它们的,因为这正是我们真正的数据传输的时候调用的冬冬.关于
usb_stor_Bulk_* 的这两个函数,咱们到时候调用了再来看.现在只需知道,日后我们一定会回过来看这个
赋值的.

573 行到634 行,不用多说了,这里就全是与各种特定产品相关的一些编译开关,它们有些自己定义一
些传输函数,有些则共用那些通用的函数.

641 行,判断us->flags, 还记得我们在讲unusual_devs.h 文件的时候说的那个flags 吧,这里第一次
用上了.有些设备设置了US_FL_SINGLE_LUN 这么一个flag,就表明它是只有一个LUN 的.像这样的设备
挺多的,随便从unusual_devs.h 中抓一个出来:

338 UNUSUAL_DEV( 0x054c, 0x002d, 0x0100, 0x0100,
339 "Sony"
,
340 "Memorystick MSAC-US1"
,
341 US_SC_DEVICE, US_PR_DEVICE, NULL,
342 US_FL_SINGLE_LUN )
,


比如这个Sony 的Memorystick. 中文名叫记忆棒,大小就跟箭牌口香糖似的,也是一种存储芯片,是Sony
公司推出的,广泛用于Sony 的各种数码产品中.比如数码相机,数码摄影机.

有人问了,啥是LUN 啊?logical unit number. 通常在谈到scsi 设备的时候不可避免的要说起LUN.关于
LUN,曾几何时,一位来自Novell(SUSE) 的参与开发Linux 内核中usb 子系统的工程师这样对我说,一个
lun 就是一个device 中的一个drive. 换言之,usb 中引入lun 的目的在于,举例来说,有些读卡器可以有多
个插槽,比如就是两个,其中一个支持CF 卡,另一个支持SD 卡,那么这种情况要区分这两个插槽里的冬冬,
就得引入lun 这么一个词.这叫逻辑单元.很显然,像U 盘这样简单的设备其LUN 必然是一个.有时候,人们常
把U 盘中一个分区当作一个LUN,这样说可能对小学三年级以下的朋友是可以接受的,但是作为一个成年人,
不应该这么理解.

知道了LUN 以后,自然就可以知道US_FL_SINGLE_LUN 是干嘛了,这个flag 的意义很明显,直截了当的
告诉你,我这个设备只有一个LUN,它不支持多个LUN.而max_lun 又是什么意思?us 中的成员max_lun
等于一个设备所支持的最大的lun 号.即如果一个设备支持四个LUNs,那么这四个LUN 的编号就是
0,1,2,3, 而max_lun 就是3.如果一个设备不用支持多个LUN,那么它的max_lun 就是0.所以这里
max_lun 就是设为了0.

另外一个需要注意的地方是,比较一下各个case 语句,发现, US_PR_BULK 和别的case 不一样,别的case
下面都设置了us->max_lun, 而对应于Bulk-Only 协议的这个case,它没有设置us->max_lun, 这是为
何?别急,后来我们会专门有一个函数去读取这个值的,之所以不设,是因为这个值由设备说了算,必须向设备

论坛徽章:
0
53 [报告]
发表于 2008-06-06 14:01 |只看该作者
查询,这是Bulk-Only 协议规定的.所以我们之后会遇见usb_stor_Bulk_max_lun() 函数,它将负责获取
这个max lun. 而我依然要声明一次,这个函数对我们U 盘是没啥意义的,咱们这个值肯定是0.

至此,get_transport() 也结束了,和get_device_info 一样.我们目前所看到的这些函数都不得不面对现实,
对它们来说,凋谢是最终的结果,盛开只是一个过程...而对我们来说,要到达终点,那么和这些函数狭路相逢,
终不能幸免.然而,不管这部分代码有多么重要,也不过是我们整个长途旅程中,来去匆匆的转机站,无论停留
多久,始终要离去坐另一班机.

冬天来了,春天还会远吗?(五)

道不尽红尘舍恋诉不完人间恩恩怨怨.

看完了get_transport() 继续看get_protocol() 和get_pipes(). 仍然是来自
drivers/usb/storage/usb.c 中:

647 /* Get the protocol settings */

648 static int get_protocol(struct us_data *us)

649 {

650 switch (us->subclass) {

651 case US_SC_RBC:

652 us->protocol_name = "Reduced Block Commands (RBC)";

653 us->proto_handler = usb_stor_transparent_scsi_command;

654 break;

655

656 case US_SC_8020:

657 us->protocol_name = "8020i";

658 us->proto_handler = usb_stor_ATAPI_command;

659 us->max_lun = 0;

660 break;

661

662 case US_SC_QIC:

663 us->protocol_name = "QIC-157";

664 us->proto_handler = usb_stor_qic157_command;

665 us->max_lun = 0;

666 break;

667

668

case US_SC_8070:
669


us->protocol_name = "8070i"
;
670


us->proto_handler = usb_stor_ATAPI_command;
671


us->max_lun = 0;
672 break;
673
674


case US_SC_SCSI:
675


us->protocol_name = "Transparent SCSI";

论坛徽章:
0
54 [报告]
发表于 2008-06-06 14:02 |只看该作者
676

us->proto_handler = usb_stor_transparent_scsi_command;
677 break;
678
679

case US_SC_UFI:
680


us->protocol_name = "Uniform Floppy Interface (UFI)"
;
681


us->proto_handler = usb_stor_ufi_command;
682 break;
683
684 #ifdef CONFIG_USB_STORAGE_ISD200
685 case US_SC_ISD200:
686 us->protocol_name = "ISD200 ATA/ATAPI"
;
687 us->proto_handler = isd200_ata_command;
688 break;
689 #endif
690
691 default:
692 return -EIO;
693
}
694 US_DEBUGP("Protocol: %s\n", us->protocol_name)
;
695 return 0;
696
}


这段代码非常的浅显易懂.我相信即使去问上海火车站附近那些卖黑车的哥们儿,他们也能告诉你这段代码
做了什么.就一件事,根据us->subclass 来判断.对于U 盘来说,spec 里边规定了,它的subclass 是
US_SC_SCSI, 所以这里就是两句赋值语句.一个是令us 的protocol_name 为"Transparent SCSI", 另
一个是令us 的proto_handler 为usb_stor_transparent_scsi_command. 后者又是一个函数指针,我
们日后必将不可避免的遇到这个函数,暂且不表.

然后是get_pipes().drivers/usb/storage/usb.c:
698 /* Get the pipe settings */
699 static int get_pipes(struct us_data *us)
700 {
701 struct usb_host_interface *altsetting =
702 us->pusb_intf->cur_altsetting;
703 int i;
704 struct usb_endpoint_descriptor *ep;
705

struct usb_endpoint_descriptor *ep_in = NULL;
706


struct usb_endpoint_descriptor *ep_out = NULL;
707


struct usb_endpoint_descriptor *ep_int = NULL;
708
709 /
*
710


* Find the endpoints we need.
711
* We are expecting a minimum of 2 endpoints - in and out (bulk).
712
* An optional interrupt is OK (necessary for CBI protocol)
.
713
* We will ignore any others.

论坛徽章:
0
55 [报告]
发表于 2008-06-06 14:02 |只看该作者
714 */

715 for (i = 0; i < altsetting->desc.bNumEndpoints; i++) {

716 ep = &altsetting->endpoint.desc;

717

718 /* Is it a BULK endpoint? */

719 if ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)

720 == USB_ENDPOINT_XFER_BULK) {

721 /* BULK in or out? */

722 if (ep->bEndpointAddress & USB_DIR_IN)

723 ep_in = ep;

724 else

725 ep_out = ep;

726 }

727

728 /* Is it an interrupt endpoint? */

729 else if ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)

730 == USB_ENDPOINT_XFER_INT) {

731 ep_int = ep;

732 }

733 }

734

735 if (!ep_in || !ep_out || (us->protocol == US_PR_CBI && !ep_int)) {

736 US_DEBUGP("Endpoint sanity check failed! Rejecting dev.\n");

737 return -EIO;

738 }

739

740 /* Calculate and store the pipe values */

741 us->send_ctrl_pipe = usb_sndctrlpipe(us->pusb_dev, 0);

742 us->recv_ctrl_pipe = usb_rcvctrlpipe(us->pusb_dev, 0);

743 us->send_bulk_pipe = usb_sndbulkpipe(us->pusb_dev,

744 ep_out->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);

745 us->recv_bulk_pipe = usb_rcvbulkpipe(us->pusb_dev,

746 ep_in->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);

747 if (ep_int) {

748 us->recv_intr_pipe = usb_rcvintpipe(us->pusb_dev,

749 ep_int->bEndpointAddress &
USB_ENDPOINT_NUMBER_MASK);

750 us->ep_bInterval = ep_int->bInterval;

751 }

752 return 0;

753 }

这个函数应该可以说是我们这几个无聊的函数中最后一个了,但它也是相对来说最复杂的一个.请容我
慢慢给您道来.702 行,us->pusb_intf, 可还记得,在associate_dev 中赋得值,如不记得请回过去查一下.

论坛徽章:
0
56 [报告]
发表于 2008-06-06 14:03 |只看该作者
没错,us->pusb_intf 就是我们故事中最开始一再提到的那个interface( 指针).而它的成员
cur_altsetting, 就是当前的setting, 或者说设置.在讲associate_dev 的时候也已经遇到过,是一个
struct usb_host_interface 的结构体指针.现在这里用另一个指针临时代替一下, altsetting. 接下来会用
到它的成员,desc 和endpoint. 回顾struct usb_host_interface, 可以看到,它这两个成员,struct
usb_interface_descriptor desc, 和struct usb_host_endpoint *endpoint. 其中,desc 不用多说,正是
这个interface 的接口描述符,而endpoint 这个指针记录的是几个endpoint, 它们以数组的形式被存储,
而endpoint 指向数组头.这些冬冬都是在usb core 枚举的时候就设置好了,我们无需操任何心,只需拿来
用就是了.这里给出struct usb_host_endpoint 的定义,来自include/linux/usb.h:

43 /* host-side wrapper for parsed endpoint descriptors */

44 struct usb_host_endpoint {

45 struct usb_endpoint_descriptor desc;

46

47 unsigned char *extra; /* Extra descriptors */

48 int extralen;

49 };

接着定义了几个struct usb_endpoint_descriptor 的结构体指针.顾名思义,这就是对应endpoint 的
描述符的.其定义来自于include/linux/usb_ch9.h:

260 /* USB_DT_ENDPOINT: Endpoint descriptor *
/
261 struct usb_endpoint_descriptor
{
262 __u8 bLength;
263 __u8 bDescriptorType;
264
265 __u8 bEndpointAddress;
266 __u8 bmAttributes;
267 __u16 wMaxPacketSize;
268 __u8 bInterval;
269
270 // NOTE: these two are _only_ in audio endpoints.
271 // use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof.
272 __u8 bRefresh;
273 __u8 bSynchAddress;
274 } __attribute__ ((packed))
;


至此,四大描述符一一亮相,在继续讲之前,我们先来小结一下:究竟什么是描述符?每个USB 设备都有
这四大描述符,不过我们拿U 盘来说.听说过Flash Memory 吗?Intel,三星,这些都是做Flash Memory 的,
当然通常人们就简称Flash. Flash 在U 盘中扮演什么角色?Flash 是用来给用户存储数据的,而U 盘中的
Flash 就相当于PC 机中的硬盘,存储数据主要就靠它.那么除了给用户存储数据以外,设备自己还需要存储
一些设备本身固有的东西,比如设备姓甚名谁?谁生产的?还有一些信息,比如该设备有几种配置,有几个接
口,等等许多特性,这些东西怎么办?复旦大学97 电工四大才子之一,我在Intel 的老师加师兄曾经这样对我
说:这个世界上,除了Flash memory 外,还有一个咚咚叫做EEPROM,也是用来存储的,它是EEPROM 的
前身, 而Flash 是基于EEPROM 技术发展起来的一种低成本的ROM 产品. EEPROM 和Flash 相同,都是
需要电擦除,但EEPROM 可以按字节擦除,而不向Flash 那样一次擦除一个block, 这样在只需改动很少数

论坛徽章:
0
57 [报告]
发表于 2008-06-06 14:04 |只看该作者
据的情况下使用EEPROM 就很方便了.因此EEPROM 的这一特性,它的电路要复杂些,集成度不高,一个bit
需要两个管子,一个用来储存电荷信息,一个充当开关.所以EEPROM 的成本高,Flash 简化了一些电路,成本
降低了很多.因此,通常,USB 设备里边,会有一个Flash 芯片,会有一个EEPROM 芯片,Flash 给客户存储数
据,而EEPROM 用来存储设备本身的信息.这就是为什么当我们把Flash 芯片卖给Motorola 之后,客户看
到的手机厂商是摩托罗拉而不是我们Intel,因为我们虽然在做Flash 的时候把我们的厂商ID 写在了Flash
上,但是最终的成品对外来看,提供的信息都是来自EEPROM, 所以当你把USB 设备通过USB 接口连到电
脑上去,那么电脑上如果能显示厂家,那么一定是最终的包装厂家,而不可能是里边那块Flash 的厂家.而
EEPROM 里边写什么?按什么格式写?这正是usb spec 规定的,这种格式就是一个个的描述符的格式.设备
描述符,配置描述符,接口描述符,端点描述符,以及其它一些某一些类别的设备特有的描述符,比如hub 描述
符.这些东西都是很规范的,尤其对于这四种标准的描述符,每个usb 设备都是规规矩矩的支持的,所以usb
core 层可以用一段相同的代码把它们都给读出来,而不用再让我们设备驱动程序去自己读了,这就是权力集
中的好处,反正大家都要做的事情,干脆让上头一起做了好了,这样的领导真是好啊!

715 到733 行,循环, bNumEndpoints 就是接口描述符中的成员,表示这个接口有多少个端点,不过这
其中不包括0 号端点,0 号端点是任何一个usb 设备都必须是提供的,这个端点专门用于进行控制传输,即它
是一个控制端点.正因为如此,所以即使一个设备没有进行任何设置,usb 主机也可以开始跟它进行一些通信,
因为即使不知道其它的端点,但至少知道它一定有一个0号端点,或者说一个控制端点.此外,通常usb mass
storage 会有两个bulk 端点,用于bulk 传输,即所谓的批量传输.我们日常的读写U 盘里的文件,就是属于
批量传输,所以毫无疑问,对于mass storage 设备来说,bulk 传输是它的主要工作方式,道理很简单,我们使
用U 盘就是用来读写文件的,谁没事天天去读它的这种描述符那种描述符呢,吃错药了?和这些描述符打交
道无非就是为了帮助我们最终实现读写文件的工作,这才是每一个usb 存储设备真正的使命.

于是我们来这段循环到底在干嘛, altsetting->endpoint.desc, 对照struct usb_host_endpoint
这个结构体的定义,可知,desc 正是一个struct usb_endpoint_descriptor 的变量.咱们刚刚定义了四个
这种结构体的指针,ep,ep_in,ep_out,ep_int, 很简单,就是用来记录端点描述符的,ep_in 用于
bulk-in,ep_out 用于bulk-out,ep_int 用于记录中断端点(如果有的话).而ep,只是一个临时指针.

我们看struct usb_endpoint_descriptor, 它的成员中, bmAttributes 表示属性,总共8 位,其中bit1
和bit0 共同称为Transfer Type, 即传输类型,即00 表示控制,01 表示等时,10 表示批量,11 表示中断.而
719 行我们看到, USB_ENDPOINT_XFERTYPE_MASK 这个宏定义于include/linux/usb_ch9.h 中:

286 #define USB_ENDPOINT_XFERTYPE_MASK 0x03 /* in bmAttributes */
287 #define USB_ENDPOINT_XFER_CONTROL 0
288 #define USB_ENDPOINT_XFER_ISOC 1
289 #define USB_ENDPOINT_XFER_BULK 2
290 #define USB_ENDPOINT_XFER_INT 3

懂一点C 语言的人就不难理解,719 行就是判断这个端点描述符描述的是不是一个Bulk 端点,如果是,
继续比较,我们先看bEndpointAddress, 这个struct usb_endpoint_descriptor 中的另一个成员,也是8
个bit, 或者说1 个byte,其中bit7 表示的是这个端点的方向,0 表示OUT,1 表示IN,OUT 与IN 是对主机
而言.OUT 就是从主机到设备,IN 就是从设备到主机.而宏USB_DIR_IN 仍然来自
include/linux/usb_ch9.h

25 /
*
26 * USB directions

论坛徽章:
0
58 [报告]
发表于 2008-06-06 14:04 |只看该作者
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 */

所以这里意思很明显,就是为了让ep_in 和ep_out 指向该指的endpoint descriptor.

729就不用再说了,如果这还看不懂的话,可以考虑去复旦的绝情谷找到那棵老树上吊自杀了,可惜如今
学校里不断搞建设,绝情谷可能已经不复存在了.729 这一个else if 的作用就是如果这个端点是中断端点,
那么就让ep_int 指向它.我们说了,每一类usb 其上面有多少端点有何种端点都是不确定的,都得遵守该类
设备的规范,而usb mass storage 的规范说了,一个usb mass storage 设备至少应该有两个bulk 端点,
除此之外,那个控制端点显然是必须的,毋庸置疑,另外,可能会有一个中断端点,这种设备支持CBI 协议,即
Control/Bulk/Interrupt 协议.我们也说过了,U 盘遵守的是Bulk-only 协议,它不需要有中断端点.

735 到738 这段代码,没啥好说的,就是判断是否ep_in 或者ep_out 不存在,或者是遵守CBI 协议但
是没有中断端点,这些都是不合理的,当然就会出错啰!

剩下一小段代码,我们下节再看.需要说的是,这个函数结束之后我们将开始最精彩的部分,它就是伟大
的usb_stor_acquire_resources(). 黑暗即将过去,黎明已经带我们上路.让我们共同期待吧.同时,我们小
结一下,此前我们花了很大的篇幅来为usb_stor_acquire_resources() 做铺垫,那我们来回顾一下,究竟
做了哪些事情?

首先我们从storage_probe 出发,一共调用了五个函数,它们是
assocaite_dev,get_device_info,get_transport,get_protocol,get_pipes. 我们这样做的目的是什么?
很简单,就是为了建立一个数据结构,它就是传说中的struct us_data,它的名字叫做us.我们把她建立了起
来,为她申请了内存,为她的各个元素赋了值,目的就是为了让以后我们可以很好的利用她.这五个函数都不
难,你一定也会写.难的是如何去定义struct us_data,别忘了这个数据结构是写代码的同志们专门为
usb-storage 模块而设计的.子曾经曰过,所谓编程,无非就是数据结构加上算法.没错,这个定义于
drivers/usb/storage/usb.h 中的数据结构长达60 行,她正是整部戏的主角.关于她的成员,我们还有很多
没遇到,不过别急,后面会遇到的.好了,虽然get_pipes 还有一小段没讲,但是我们可以提前和这5 个函数说
再见了,席慕蓉说过,若不得不分离,也要好好的说声再见,也要在心里存着一份感谢,谢谢她给你一份记忆.

通往春天的管道

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

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

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

论坛徽章:
0
59 [报告]
发表于 2008-06-06 14:05 |只看该作者
输油管道,更没有输卵管道.但是相同的是,所有的管道都是用来传输东西的,只不过有些管道传输的是实实
在在的物质,而有些管道传输的是数据.

眼下我们在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 << | (endpoint << 15);

1107 }

论坛徽章:
0
60 [报告]
发表于 2008-06-06 14:05 |只看该作者
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)
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP