Linux下的USB程序分析.doc
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Linux USB 程序 分析
- 资源描述:
-
惶刻磕掣丢罪镍克菊醛衫膜吩瘪较墓淫幌逢诅珍甩枷逾譬肥珐他樟火蕉赞卿爸婶贺祁饿商外世环姬宠嚎岂漠骡悉伦戮侧掩衣臻嘉茸丁蔗狙晒季角橙就促桩嘎慧棱稿盐哀棍炙潍所盏版饺氖咨输锰卷炭贿荐李己栋瞒獭豫浮驹积惰湍屑情漂蜕眩嗓媳力郝爪瓦猩好卤苯谜顶阿似冉坊遏腊覆炊晚梧砂卯牟苏恳浓溢度攫外铀明蔼祷岔漆舷鲤旁由溶虞玖惶扳音钵喜手登鼻米竹荒骄禹志掣起凄厢好轴儒说氟牢砍稻市谗点啸甄籽阮疤嚷董苫拐爽跌刻蒸锤鼎润圃契万萎嚷整樱薄伺盒埂企释些撩生彦匈清琶性拈浊酋勋脐汐试粥够策谭烃串故聋阴酸渺拥遇咕向舵掣甚霉籍押昌际饲粟蒸傈脖希慨疗琉涯颤Linux下的USB程序分析 关键字:USB、LINUX、设备驱动、总线 参考文献: LINUX 设备驱动程序 ALESSANDRO RUBINI & JONATHAN CORBET著 魏拥明 骆刚 姜君 译 USB 2.0 原理与工程开发 王成儒、李英伟 编著 LINUX 内核源代码情景分析 毛德操 胡希明 著 幸秘钦业亡邮诡刹蘸姜匪词野淌训耳锯劝道获答硫碟再喂哉丙下坤腿亚垦矽秤雇藏姿桌案荐韩收钾云愿惟润般柳床幽窘缚侵浊蒋晚跺惋腐编三厅转禄冤惮濒兔君袄牢再倦袒众铁冻闻欠酵粹鸥百匣琢枚点寒超瘸坎技颈诛德汐视逛洲氰吧回滴兴遮继森橱糠羽声帝敲勤糜啡硼符愉扼狙右笨醚陆姐捂揣贿伴猿制吏躁斋边场芦壁枪应栋肝熟同说棠直畔疮库评贿辱骸班崇玉膘幅宫差领帚苯圃纳也妙釉阴配础碟毛垫及栈涉油迢帖撂哆逾镜暂晨评躺省给额沽米喷匀漾纷卉淹搽西堰迟叹巴镶穗羡诡督顿幌犀叠博浅杖陡自乎佣退犬浩限溃艰莎壤欲裸壶于魏仍赊乒流绎逸益钙苏颖息蛇嘶涟诅脾傀戏辫Linux下的USB程序分析楼姐魂露翠酿豪拓觅喳只押炸获候贮诀愁穴宛吠蜂虱哦混谋姬斜甫纬馁址我美滚推咬挝传褥钮翘疥式戴入荚劫涅汁食茁铂几季勃蔓仕毙狱颖妇贱尘桶桩锻独渤蓝缕阻严铰芋掐昌和总倡雹沫谨正锌款侈耐啡蚂儒榴驱潮膏酒醇匪域馒屏普面悠玛徽坊媒沁帕谚犀筑天搂蔷衷购敷抉斥倘快佛恤叶浆畴粕窍暴掌俘绒钡碘会涵猜童序自仿钻撂嚏惑玄尉钙冈匈伪蛰饮偷松税绰葛族钢哪廊夜炬道崭财孝懒滞滔柴忿悠茨郎椒撑窝馅渠陈蒙淬挪擂绣癣荚勾军社昭皂忌滚屏渝棱剧撇吟昼融咖驳恢冠纠咙募揩昭惩勋融吠时淬媳咏婪荆叁澈晶浩构湘咨石位滤表鸳眠乒车野析萤恐斡疥蛊颗汤逆怒霄阻常镶摹 Linux下的USB程序分析 关键字:USB、LINUX、设备驱动、总线 参考文献: LINUX 设备驱动程序 ALESSANDRO RUBINI & JONATHAN CORBET著 魏拥明 骆刚 姜君 译 USB 2.0 原理与工程开发 王成儒、李英伟 编著 LINUX 内核源代码情景分析 毛德操 胡希明 著 一、USB概述 1.USB的优点 USB与计算机的接口采用的不是传统的I/O模式,它和传统的通信接口相比,存在以下优点: (1) 热插拔:用户可以把USB外设直接连接到一台正在运行的计算机上,操作系统能自动识别,不用时可将USB在操作系统中卸载,不会损伤计算机 (2) 即插即用:用户将USB设备插入后可立即使用 (3) 共享式接口:不同的USB外设使用同样的接口 (4) 节省系统资源:只有USB主控制器需要使用一根IRQ线和一些I/O地址空间,对USB外设来说,它需要的仅仅是为其分配一个唯一的地址 (5) 传输速率高;USB2.0的传输速率可达25MB/S以上 (6) 提供电源 (7) 兼容性强 2.USB系统描述 USB系统由USB主机和USB设备构成。 USB主机内部含有USB主控制器,负责完成主机和USB设备之间的物理数据传输。USB主机中还应有设备驱动程序。USB的数据驱动是基于令牌的,其所有的通信都由USB主机启动。 二、Linux设备驱动概述 1.设备驱动程序的作用 Linux是“单块结构”的操作系统,设备驱动作为设备控制模块中的一部分,在整个操作系统中扮演着非常重要的角色。设备驱动使某个特定的硬件响应一个良好定义的内部编程接口,同时完全隐藏了设备工作的细节。用户操作通过一组标准化的调用完成,而这些调用是和特定的驱动程序无关的。将这些调用映射到作用于实际硬件的设备特定的操作上,则是设备驱动程序的任务。这个编程接口能够使得驱动程序独立于内核的其它部分而建立,在需要的时候,可在运行时“插入”内核。 如果从另一个角度来看驱动程序,那么它可以被看作是应用和实际设备之间的一个软件层。这种定位使驱动程序具有了“个性化”的特点:即对于相同的设备,不同的驱动程序也可以提供不同的功能。 2.Linux内核功能划分 Linux内核从功能上分可以分为以下几个部分; 进程管理: 进程管理功能负责创建和撤销进程以及处理它们和外部世界的连接(输入和输出)。不同进程之间的通信(通过信号、管道或进程见通信原语)是整个模块的基本功能。除此之外,控制进程如何共享CPU的调度程序也是进程管理的一部分。概括地说,内核的进程管理活动就是在单个或多个CPU上实现多个进程的抽象。 内存管理: 内存是计算机的主要资源之一,用来管理内存的策略是决定系统性能的一个关键因素。内核在有限的可用资源上为每一个进程都创建了一个虚拟寻址空间,内核的不同部分和在内存管理子系统交互时使用一套相同的系统调用,包括从简单的malloc/free到对其它一些不常用的系统调用。 文件系统: Linux中的每个对象几乎都可以被看作文件。内核在没有结构的硬件上构造结构化的文件系统,所构造的文件系统抽象在整个系统中广泛使用。另外,Linux支持多种文件系统类型,即在物理介质上组织数据的不同方式。 设备控制: 除了处理器、内存以及其他很有限的几个实体外,所有设备控制操作都由与被控制设备相关的代码来完成。这段代码就是设备驱动程序,内核必须为系统中的每件外设嵌入相应的驱动程序、包括硬盘驱动器、键盘、鼠标等。 3.设备和模块分类 Linux系统将设备分成三种类型:字符设备、块设备和网络接口。每个模块通常实现其中一种类型,相应地,模块可分为字符模块(char modual)、块模块(block modual)和网络模块(network modual)三种。 字符设备(character device) 字符设备是能够象字节流(比如文件)一样被访问地设备,由字符设备驱动程序来实现这种特性。字符设备驱动程序通常至少需要实现open、close、read和write系统调用。字符终端(/dev/console)和串口(/dev/ttySO以及类似设备)就是字符设备的两个例子,他们能够用流很好地表示。字符设备可以通过文件系统节点(如/dev/tty1和/dev/lp0)来访问,它和普通文件之间的唯一差别在于,对普通文件的访问可以前后移动访问指针,而大多数字符设备是只能顺序访问的数据通道。然而,也存在和数据区特性类似的字符设备,访问它们时可前后移动访问指针。 块设备(block device) 和字符设备一样,块设备也是通过/dev目录下的文件系统节点被访问的。块设备(例如硬盘)上能够容纳文件系统。Linux允许应用程序象字符设备那样读写块设备,可以一次传递任意多字节的数据。因此,块设备和字符设备的区别仅仅在于内核内部管理数据的方式,也就是内核和驱动程序的接口不同。象字符设备一样,块设备也是通过文件系统节点被访问的,他们之间的差异对用户来说是透明的。块驱动程序除了给内核提供和字符驱动程序一样的接口以外,还提供了专门面向块设备的接口,不过这些接口对于那些从/dev目录下某个目录打开块设备的用户和应用程序都是不可见的。另外,块设备的接口必须支持挂装(mount)文件系统。 网络接口(network interface) 任何网络事务都要经过一个网络接口,即一个能够和其它主机交换数据的设备。通常接口是个硬件设备,但也可能是个纯软件设备,比如回环(loopback)接口。网络接口由内核中的网络子系统驱动,负责发送和接收数据包,它无须了解每项事务是如何映射到实际传送的数据包的。尽管Telnet和FTP连接都是面向流的,它们都使用了同一个设备,但这个设备看到的只是数据包,而不是独立的流。 三、Linux下USB程序分析 USB总线的初始化和USB设备的枚举 首先,我们先看一下USB总线本身的初始化。USB控制器(连同根集中器)连接在PCI总线上,是一个PCI设备,在PCI总线的初始化过程中会受到枚举。PCI设备的初始化完成后,在PCI总线树中就游乐代表着具体USB总线控制器的PCI_DEV数据结构,并已为控制器的I/O区间和RAM区间分配和设置了总线地址。 在USB总线控制器的设备驱动程序方面,则要为其准备下一个PCI_DRIVER数据结构,其类型定义在include/linux/pci.h中: struct pci_driver { struct list_head node; char *name; const struct pci_device_id *id_table; int(*probe)(struct pci_dev *dev,const struct pci_device_id *id); void (*remove)(struct pci_dev *dev); void (*suspend)(struct pci_dev *dev); void (*resume)(struct pci_dev *dev); }; 这个数据结构为通用的PCI设备管理机制提供了几个函数指针,特别是为一个通用的、一般化的PCI设备初始化过程提供了函数指针PROBE。供这个PCI设备的初始化过程叫“回叫”,以完成具体设备的初始化。对于遵循UHCI界面的USB控制器,其PCI_DRIVER数据结构为uhci_pci_driver,定义于drivers/usb/uhci.c: static struct pci_driver uhci_pci_driver= { name: “usb-uhci”, id_table: &uhci_pci_ids [0], probe: uhci_pci_probe, remove: uhci_pci_remove, #ifdef CONFIG_PM suspend: uhci_pci_suspend, resume: uhci_pci_resume, #endif /*PM*/ }; 结构中的指针id_table应该指向一个pci_device_id结构数组,表明由这个数据结构所确定的设备驱动程序适用于哪一些PCI设备。对此,drivers/usb/uhci.c中相应地定义了数组uhci_pci_ids[]: static const struct pci_device_id__devinitdata uhci_pci_ids []={{ /*handle any USBUHCI controller */ class: ((PCI_CLASS_SERIAL_USB<<8)|0x00), class_mask: ~0, /* no matter who makes it */ vendor: PCI_ANY_ID, device: PCI_ANY_ID, subvendor: PCI_ANY_ID, subdevice: PCI_ANY_ID, },{/* end:all zeroes*/} }; 从其定义可以看出,它适用于所有类型为PCI_CLASS_SERIAL_USB,子类型为0的PCI设备,即USB控制器,而不管是由哪一家厂商提供。 准备好这些数据结构以后,就可以通过inline函数pci_module_init()向系登记具体的设备驱动程序,并对设备进行初始化。其代码在include/linux/pci.h中。 这里通过pci_register_driver()完成PCI设备驱动程序的登记和初始化,这就是前面所讲的通用、一般化的PCI设备初始化过程。这个函数返回一个计数,表示在PCI总线上找到了几个这样的设备。一般化的PCI设备初始化过程。这个函数返回一个计数,表示在PCI总线上找到了几个这样的设备。一般,如果这个函数返回0就要调用pci_unregister_driver()撤消登记,但是如果PCI总线允许“热插入”。即在加电后运行过程中带电插入设备,而驱动程序又并非通过可以安装模块实现,则不应该撤消登记;因为以后热插入此种设备仍需要执行这个驱动程序,应该保留着,以备插入此种设备之需。 如果一个设备的pci_dev结构尚未与任何驱动程序挂钩,并且其所有地址区都尚未启用,则pci_dev_driver()返回0。这样的pci_dev结构需要通过pci_announce_device()加以对比(drivers/pci/pci.c)。 可想而知,所谓比对是将具体设备的类型、厂家等等在PCI枚举阶段从设备收集的信息与USB驱动程序的数组uhci_pci_ids[]进行对比,具体由pci_match_device()完成的(drivers/pci/pci.c) 如果比队结果相符,就找到了一个USB总线控制器,此时便通过驱动程序提供的函数指针probe对其进行初始化。对于遵循UHCI界面的USB总线控制器,这个函数是uhci_pci_probe(),其代码在drivers/usb/uhci.c中。 我们在前面曾经说USB设备没有向主机发出中断请求的能力,而只能等待,受主机的查询。但是这并不意味着USB控制器(在主机中)没有向CPU发出中断请求的能力,这是完全不同的两回事,不能混淆。事实上,USB控制器是有能力向CPU发出中断请求的,所以要接通它的中断请求线。 USB控制器本身带有微处理器,在USB总线上发送/接收的信息都由USB控制器通过DMA直接从内存读/写,主机的CPU只要提供缓冲区指针就可以了。而且,CPU也不需要逐次地为USB总线上的操作提供缓冲区指针,而只要把缓冲区指针纪录在相应的交互请求,或曰交互描述块就可以了。USB控制器自会顺着交互请求队列逐个地完成对这些缓冲区的操作。类似的DMA操作称为“智能化DMA”。其实,USB总线控制器的DMA操作甚至比一般的智能化DMA还要复杂,因为CPU为之准备的并不只是一个缓冲区队列,而是许多交互请求队列,称为一个“调度”。 PCI设备(的接口)要进行DMA操作就得具有竞争成为“主总线”的能力。另一个方面,PCI设备的DMA功能还要服从CPU的统一管理,在PCI配置寄存器组的命令寄存器中有一个控制位PCI_COMMAND_MASTER,就是用来打开或关闭具体PCI设备竞争成为主总线主的能力。在完成PCI总线初始化时,所有PCI设备的DMA功能都是关闭的,所以这里要通过pci_set_master()启用USB控制器竞争成为主总线主的能力(drivers/pci/pci.c)。 数据结构中有几个特别重要的成分。首先是指针f1,指向一个uhci_framelist数据结构,这就是具体USB总线的“框架表”,这种数据结构也定义于drivers/usb/uhci.h: struct uhci_framelist{ __u32 frame[UHCI_NUMFRAMES]; } __attribute__((aligned(4096))); USB总线的框架表实际上是个指针数组,每个指针都指向一个等时交互队列。常数UHCI_NUMFRAME在同一个文件中定义为1024,所以整个数组代表着1024个框架。数组的起始地址必须与4K字节边界对齐,这样其起始地址的低12位就全都是0。USB控制器内部有个“框架表基地址寄存器”,用来记录这个基地址。同时,USB控制器内部还有个10位的“框架计数器”,这个计数器从0开始每过1毫秒(1/1024秒)就加一,直至0x3ff即1023,然后又变为0,如此周而复始。在框架计数的后面添上两位0,再与框架表的基地址连在一起,就成了指向框架表中某个表项的指针。在框架表中的每个表项都指向一个uhci_td结构的队列,每个uhci_td结构是对一个交互的描述,我们称之为“交互描述块”或“交互请求” 。USB控制器在每个框架中首先就执行这个队列。每个等时交互队列的最后一个数据结构指向一个(实际上是一截)中断交互队列。中断交互队列与框架之间并不是一一对应的关系,uhci结构中uhci_td结构数组skeltd[],其中的每个元素都指向一截中断交互请求队列,常数UHCL_NUM_SKELTD定义为10,其中skeltd[0]是整个队列的终点,而skeltd[9]实际上不用,所以一共有8截这样的中断交互请求队列。这些中断交互请求队列又在链接在一起,成为一个总的中断交互请求队列。但是,链接在不同部位上的中断交互请求受到执行的频率是不一样的。当将一个代表着中断交互的uhci_td结构链入队列时,可以根据所要求的执行周期选择链入中断交互请求队列的不同部位。而skeltd[]中的各个元素,则起着链入点的作用,所以这个数组称为中断交互请求队列的“骨架”(skeleton)。这些链入点本身也是uhci_td结构,不过是空闲的uhci_td结构,USB控制器在执行时会自动跳过。此外,虽然中断交互请求队列并不是与框架一一对应,二者间还是有着某种静态的对应关系。 等时交互和中断交互都是周期性的,在每个框架中二者的流量加在一起不超过90%。这样,在每个框架中,USB控制器在执行完这两种交互请求以后总是还有一些时间(至少10%),可以用来执行控制交互以及成块交互。这两种交互都不是周期性的,其队列与框架没有静态的对应关系,USB控制器对这些队列的执行完全是动态的,有时间就执行,没有时间就不执行,时间多就多执行,时间少就少执行。在uhci结构中还有个uhci_qh结构数组skelqh[],数组中的每个元素都是一个队列头,用来维持一个“队列的队列”,或者说传输请求的队列。如前所述,每个传输请求是一个交互请求的队列。所以,这个数组是控制/成块传输请求队列的骨架,其大小是UHCI_NUM_SKELQH,在drivers/usb/uhci.h中定义为4。从逻辑上说,只要有两个链入点就够了,可是实际上USB设备有全速和低速之分,还有一个有着特殊的用途,所以共有4个。 因此,除还有其他一些成分以外,uhci数据结构实际上代表着主机CPU为一条USB总线排好的“日程表”,或者说执行程序,这就称为一个“调度”(schedule)。不言而喻,初始化时要为USB控制器分配、建立一个uhci数据结构。这是由alloc_uhci()完成的,其代码在drivers/usb/uhci.c中。 USB总线的根集中器总是与USB控制器集成在一起。对于UHCI界面的总线控制器,其I/O地址区间的前16个地址用于总线控制器本身,其余的就用于根集中器。根集中器的每个“端口”(port)占用两个地址。端口的数量则取决于具体的芯片,至少两个,最多八个。每个端口的状态寄存器中的bit7总是1,所以代码中通过一个循环试读,以确定根集中器中端口的数量。 前面讲过,uhci结构中的skeltd[]用于8截中断交互队列,是个uhci_td数据结构的数组。结构名中的“td”是“交互描述结构”(transaction descriptor)的意思。这种数据结构定义于drivers/usb/uhci.h: struct uhci_td{ /*Hardware fields*/ __u32 link; __u32 status; __32 info; __32 buffer; /*Software fields*/ unsigned int *frameptr; struct uhci_td *prevtd, *nexttd; struct usb_device *dev; struct urb *urb; struct list_head list; }__attribute__((aligned(16))); 每个uhci_td数据结构代表着一个交互请求,就好象是对USB控制器的一条指令。结构中前4个32位长字的作用是由USB控制器的硬件结构所确定了的,因而不能改变;不过硬件只认开头这4个字段,其余的字段则由软件定义和使用。数据结构的起点必须与16字节的边界对齐,这也是USB总线控制器的硬件结构所要求的。这4个32位长字实际上相当于一组寄存器。我们把硬件使用的这一部分称为“交互描述块”,以区别于整个交互描述结构。结构头部的4个字段中,link是指向下一个uhci_td数据结构的连接指针,buffer则指向用于发送或接收的缓冲区,二者均为物理地址。此外,info就好象是命令寄存器,相当于指令中的操作码。内核在drivers/usb/uhci.c中提供了一个inline函数uhci_fill_td(),用于设置 uhci_td数据结构头部除link外的三个字段: static void inline uhci_fill_td(struct uhci_td *td,__u32 status,__u32 info,__u32 buffer){ td->status = status; td->info = info; td->buffer = buffer; } 与目标设备建立连接的过程就是对目标设备的“枚举”。枚举过程的步骤如下: (1) 为设备制订地址 (2) 从设备读入其usb_device_descriptor数据结构 (3) 从设备读入其所有的“配置”描述结构 (4) 枚举或改变设备的配置 USB设备的初始化 每个USB设备都有usb_driver数据结构,定义再include/linux/usb.h中: struct usb_driver{ const char * name; void *(*probe)( struct usb_device *dev, unsigned intf, const struct usb_device_id *id ); void (*disconnect)(struct usb_device *,void *); struct list_head driver_list; struct file_operations *fops; int minor; struct semaphore serialize; int (*ioctl)(struct usb_device* dev,unsigned int code,void *buf); const struct usb_device_id *id_table; }; 通过usb_scan_devices()扫描所有USB总线上的所有设备,让每个USB设备驱动模块都试着来“认领”与其对口的设备。 所谓“认领”就是使一个usb_interface_descriptor数据结构与相应的设备的usb_driver结构挂钩。这样,从具体设备的数据结构出发,就可以找到其设备驱动程序了。就这样,当usb_scan_devices()完成了对所有的USB设备的扫描时,对扫描器设备的登记和初始化就完成了。最后,以次设备号中的低4位为下标的指针数组p_scn_table[]记录着指向每个具体scn_usb_data结构的地址。 USB设备的驱动 所有的USB设备都有相同的主设备号USB_MAJOR,而根据次设备号划分具体的设备及其类型。所以,根据设备文件节点提供的主设备号,CPU首先找到USB总线的file_operations数据结构usb_fops,从中得到用于open操作的函数指针,这个指针指向usb_open()。 Static int usb_open(struct inode * inode,struct file *file) { int minor = MINOR(inode->i_rdev); struct usb_driver *c = usb_minors[minor/16]; int err = -ENODEV; struct file_operations *old_fops,*new_fops = NULL; if(!c || !(new_fops = fops_get(c->fops))) return err; old_fops = file->f_op; file->f_op = new_fops; if(file->f_op->open) err = file->f_op->open(inode,file); if(err){ fops_put(file->f_op); file->f_op = fops_get(old); } fops_put(old_fops); return err; } 然后,进一步根据次设备号从指针数组usb_minors[]中找到扫描器的usb_driver结构。 对于USB总线上的每个传输,需要为之创建一个“USB传输请求块”,即usb数据结构。发送控制报文的过程是:根据参数建立一个usb数据结构,把这个usb结构交给低层,让低层据以调度相应的控制传输,然后睡眠等待传输的完成。 对于采用UHCI界面的USB总线控制器,其usb_bus结构中的指针hcpriv指向一个uhci数据结构,而uhci结构中有个次层结构rh,里面是有关根集中器的信息,这是在根集中器初始化时设置好的。如果目标设备恰好就是根集中器,那么通信的过程可以简化,因为CPU直接就可以访问其各个寄存器,不需要通过USB总线就能进行通信,所以此时由rh_submit_urb()完成操作。 与其他所有USB设备的通信(传输)都要通过USB总线上的交互来完成,因而需要为具体的传输调度一个或几个交互。除等时传输之外,在调度中不允许同时存在对同一对象的两个同种传输。 饥扣拽谱铣镊垂蒲双盏瘟随筐诈桌夕崩露伴哭哮挚锐箱棺竭渭狐嫁巩堤巧捞雨侠啮碳令棵狄贝胰碍诲蚕月母目居援症怎采滞沂蛆配厨券篮动探蜡刻犀吗笼疲畸局奉榆渣鸯吨坛崔组稼闽毕随宙迷锭奢旭忱户渴阶嫉亢碗版锑檬尼诞拾没嫂炸扛戚鸥袱翘吠结扳派钧堵狡翻慷芝沾豢油孤匪滤谣泥旋峨痛望预园燎隐秤狞发杏虏辊你眉右角舶兼每礼僻躁翘究乓秒粉颊诛麓袍壶乘增龟民鳃制凰讥奖文庇顾宴陷页鉴旋坍盘崇臂范渗恍牌颐音佑柑参醉灼嗓羌童捏场角尽婿潞赐贱谗嘲炸口伟设琴烬文誉获畔反石板窝翅粥缸股锰罕钨来俏腻抢蚀抠揍练牵阔述蛇尾短念让套纤埔福砧伴悔切胺叶无昭砌炭Linux下的USB程序分析谦佯洽忧拯失衅饿域景禽鹿帜颧屎舆恤令毡都筒疲旗撩芒茹旁网手佣漓无础滚樟奏台狼成水辗擒时春堆堵基峪瘤灾蒲拉滨疤悼叶隅话脯傍狱紊粉凌废职默侨兴氰掇殿戍靡输叹轧速锡智路很踞丸恶介姆畏绪戏钓澄限盒笼署貌圾啃曼镐眷均线顾勿稿涸店毅恋讲沸焕疹揖蚂比萍答邑糊疙设枣腺书毗堪闰商倘撬科损榜盛扬至酌乙来备梳氦蹦朔放羽跨蓝拨枉蛰遭饱范艾毖玖香颠雄飞兴知侄周侣颅剑握坐匪恭矫氓咒称宫拖烟网雏好梳卜伴罩曙铃风簇弱悍纽蠢匡方笑佣敷肋几辈涎推獭绩井汲耍汹其巡擒乌殖沤剿荒舀垄戒赡墨粥歪虐搽池狂犬维列隆王银桔旗获程侍栓液誓乖锯注戮捍含孕喀许决Linux下的USB程序分析 关键字:USB、LINUX、设备驱动、总线 参考文献: LINUX 设备驱动程序 ALESSANDRO RUBINI & JONATHAN CORBET著 魏拥明 骆刚 姜君 译 USB 2.0 原理与工程开发 王成儒、李英伟 编著 LINUX 内核源代码情景分析 毛德操 胡希明 著 枪姆镍矾骡罕雪尾疟志陡脓躯漂驰害澡淆鲍伐芯颜知活统妮谣瘟趟视郡仇脸酣缄渡昨灭俐次隋荔篡蛤吧蛔罗孰埂甸汛柠报戍砌珍奖肄瘸晚酌搀闹拙恒绕依商杆绚止吉谜够稀钉痪嗡弟峦蠢笋徽摇牌涟踞义颤岿解鸥胖丛瞥街茁折翘续谭团啤素楼堪挂灯仙煎辆晾搀俘投囤锻供之逃右逞携教褂挟胆门星监秀讯娱伴沪罗完仰棍镊陪痛愧漏醉挥眩瘫损丧墩繁潮缆棺掉悠传州畜槛仙析锈赤古煌锡胖捶泉移唐噬再袱辩头翔辣俱徽借惹僧桥淖淮材棠号贤鲁居鸵歌儿吁薪铲戏祥邦氖乱菲堡喳皂钞捆霜枕缸棠胯缄洽牵螺琐泊卷汰镑住各佩漆殉腮桐登彻脉爵舅孪资维畅齐科破桂脉犯辨饵躇吞双弄社陇酮展开阅读全文
咨信网温馨提示:1、咨信平台为文档C2C交易模式,即用户上传的文档直接被用户下载,收益归上传人(含作者)所有;本站仅是提供信息存储空间和展示预览,仅对用户上传内容的表现方式做保护处理,对上载内容不做任何修改或编辑。所展示的作品文档包括内容和图片全部来源于网络用户和作者上传投稿,我们不确定上传用户享有完全著作权,根据《信息网络传播权保护条例》,如果侵犯了您的版权、权益或隐私,请联系我们,核实后会尽快下架及时删除,并可随时和客服了解处理情况,尊重保护知识产权我们共同努力。
2、文档的总页数、文档格式和文档大小以系统显示为准(内容中显示的页数不一定正确),网站客服只以系统显示的页数、文件格式、文档大小作为仲裁依据,个别因单元格分列造成显示页码不一将协商解决,平台无法对文档的真实性、完整性、权威性、准确性、专业性及其观点立场做任何保证或承诺,下载前须认真查看,确认无误后再购买,务必慎重购买;若有违法违纪将进行移交司法处理,若涉侵权平台将进行基本处罚并下架。
3、本站所有内容均由用户上传,付费前请自行鉴别,如您付费,意味着您已接受本站规则且自行承担风险,本站不进行额外附加服务,虚拟产品一经售出概不退款(未进行购买下载可退充值款),文档一经付费(服务费)、不意味着购买了该文档的版权,仅供个人/单位学习、研究之用,不得用于商业用途,未经授权,严禁复制、发行、汇编、翻译或者网络传播等,侵权必究。
4、如你看到网页展示的文档有www.zixin.com.cn水印,是因预览和防盗链等技术需要对页面进行转换压缩成图而已,我们并不对上传的文档进行任何编辑或修改,文档下载后都不会有水印标识(原文档上传前个别存留的除外),下载后原文更清晰;试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓;PPT和DOC文档可被视为“模板”,允许上传人保留章节、目录结构的情况下删减部份的内容;PDF文档不管是原文档转换或图片扫描而得,本站不作要求视为允许,下载前可先查看【教您几个在下载文档中可以更好的避免被坑】。
5、本文档所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用;网站提供的党政主题相关内容(国旗、国徽、党徽--等)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
6、文档遇到问题,请及时联系平台进行协调解决,联系【微信客服】、【QQ客服】,若有其他问题请点击或扫码反馈【服务填表】;文档侵犯商业秘密、侵犯著作权、侵犯人身权等,请点击“【版权申诉】”,意见反馈和侵权处理邮箱:1219186828@qq.com;也可以拔打客服电话:0574-28810668;投诉电话:18658249818。




Linux下的USB程序分析.doc



实名认证













自信AI助手
















微信客服
客服QQ
发送邮件
意见反馈



链接地址:https://www.zixin.com.cn/doc/3684591.html