USB设备驱动开发深度解析
Linux 下wifi 驱动开发—— USB接口WiFi驱动浅析
Linux 下wifi 驱动开发(四)——USB接口WiFi驱动浅析前面学习了SDIO接口的WiFi驱动,现在我们来学习一下USB接口的WiFi驱动,二者的区别在于接口不同。
而USB接口的设备驱动,我们前面也有学习,比如USB摄像头驱动、USB鼠标驱动,同样都符合LinuxUSB驱动结构:USB设备驱动(字符设备、块设备、网络设备)|USB 核心|USB主机控制器驱动不同之处只是在于USB摄像头驱动是字符设备,而我们今天要学习的WiFi驱动是网络设备;当然由我们编写的部分还是USB设备驱动部分,下面进入USB接口WiFi驱动的分析,如何分析呢?我们下面从这几个方面入手:从硬件层面上看,WIFI设备与CPU通信是通过USB接口的,与其他WIFI设备之间的通信是通过无线射频(RF)。
从软件层面上看,Linux操作系统要管理WIFI设备,那么就要将WIFI设备挂载到USB总线上,通过USB子系统实现管理。
而同时为了对接网络,又将WIFI设备封装成一个网络设备。
我们以USB接口的WIFI模块进行分析:a -- 从USB总线的角度去看,它是USB设备;b -- 从Linux设备的分类上看,它又是网络设备;c -- 从WIFI本身的角度去看,它又有自己独特的功能及属性,因此它又是一个私有的设备;通过上述的分析,我们只要抓住这三条线索深入去分析它的驱动源码,整个WIFI驱动框架就会浮现在你眼前。
一、框架整理1、USB设备驱动现在我们先从USB设备开始,要写一个USB设备驱动,那么大致步骤如下:a -- 需要针对该设备定义一个USB驱动,对应到代码中即定义一个usb_driver结构体变量代码如下:[cpp]view plain copyb -- 填充该设备的usb_driver结构体成员变量代码如下:[cpp]view plain copyc -- 将该驱动注册到USB子系统代码如下:[cpp]view plain copy以上步骤只是一个大致的USB驱动框架流程,而最大和最复杂的工作是填充usb_driver结构体成员变量。
USBgadget设备驱动解析
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:253 errors:0 dropped:0 overruns:0 frame:0
};
static struct platform_device *smdk2410_devices[] __initdata = {
…,
&s3c_device_usbgadget, /*USB gadget device设备登记*/
};
static void __init sdmk2410_init(void)
};
printk("smdk2410_udc: %s\\n",s3c2410_pullup_info[cmd]);
s3c2410_gpio_cfgpin(S3C2410_GPG9, S3C2410_GPG9_OUTP);
switch (cmd)
{
case S3C2410_UDC_P_ENABLE :
#make zImage
#make modules
在名目drivers/usb/gadget下生成g_ether.ko
3、加载驱动,测试功能
利用前面的生成的内核,启动系统后,加载g_ether.ko
#insmod g_ether.ko
#ifconfig usb0 192.168.1.120
……
usb0 Link encap:Ethernet HWaddr 5E:C5:F6:D4:2B:91
USB(Host)唯独通过描述符了解设备的有关信息,按照这些信息,建立起通信,在这些描述符中,规定了设备所使用的协议、端点情形等。因此,正确地提供描述符,是USB设备正常工作的先决条件。
USB设备开发过程解析_前台VB+驱动VC+固件GCC_
边干边写之——USB设备开发过程解析(前台VB+驱动VC+固件GCC)[声明:]1、本文为开发工作过程中的心得体会,认识粗浅表述不周之处请见谅;2、本文内容供广大爱好者学习交流之用,如需转载请注明出处并告知本人。
KKND 08年11月21日清晨发表于VBGood论坛Dreamon-II Labs.******************************************************************************[正文]其实弄清楚USB的工作流程后开发USB设备是很简单的事情。
简单来说无非是这样的过程:开发设备硬件-> 编写设备固件程序-> 编写驱动程序-> 开发应用程序其中后3项主要是软件编程工作,也是本文讨论的重点。
应用程序与USB硬件设备通信自底向上需要完成三部分程序的开发:固件程序 <-USB总线驱动程序-> 设备驱动程序 <-系统I/O管理器-> 用户应用程序以下分别将这三部分程序中用于实现通信的核心代码加以介绍。
固件程序:固件程序也就是所谓的“下位机”程序,它运行于设备上,由设备上的单片机执行,用于控制USB接口芯片与主机进行通信。
开发环境:GCC + AVR Studio……/******************************************************************* USB中断处理,该中断由USB接口芯片产生,由下位机CPU处理******************************************************************/void usb_isr(void){……if ( D12_int_flags & D12_INT_ENDP0OUT ) control_out_handler(); // 产生USB控制端点接收中断时调用该函数……}/******************************************************************* 处理USB控制端点接收事件******************************************************************/ void control_out_handler( void ){……control_dispatcher(); // 调用分发处理函数}/******************************************************************* 分发处理,根据URB 的请求类型和请求号选择相应的处理函数******************************************************************/ void control_dispatcher( void ){uchar type, req;type = ControlData.DeviceRequest.bmRequestType & USB_REQUEST_TYPE_MASK;req = ControlData.DeviceRequest.bRequest;……if ( type == USB_VENDOR_REQUEST ) // 处理用户自定义请求{// 根据请求号选择请求处理函数表入口if ( req < NUMBER_VENDOR_REQ ) (*VendorDeviceRequest[req])();}……}/******************************************************************* 用于处理用户请求的函数入口表******************************************************************/ const pfnvoid VendorDeviceRequest[] ={fun0,fun1,fun2,……};/******************************************************************* 用户请求处理函数实现******************************************************************/void fun0(void){uint8_t txdata[LEN];……single_transmit(txdata, LEN);}驱动程序:目前Windows系统下的驱动程序通常采用WDM模型(Win32 Driver Model,Win32驱动模型),采用Microsoft提供的DDK开发,借助第三方驱动程序开发工具比如DriverStudio 可以大大简化开发过程。
USB 驱动开发技术彻底解密
PDIUSBD12 USB 器件原理 固件编程及其驱动开发
广州周立功单片机发展有限公司 地址 广州市天河区天河北路 689 号光大银行大厦 16 楼 D2 邮编 510630 电话 (020) 38730916 38730917 38730976 38730977 传真 (020) 38730925 电子邮箱 info@ 网址
十分抱歉 这本书在还没有出版之前是不单独对外销售的 仅仅作为我们销售 Keil C51 高级语言的 DP 51 下载仿真实验仪 和 D12 SMART USB 开发套件 的辅助资料 作为一个技术驱动型的企业 我 们投入了大量的人力 物力 资金 服务及其心血做出一个产品 设计一个软件或者写作一本好书确实不 是一件容易的事情 总有一小撮人不顾他人的心血和利益――将资料扫描 源码通过网络散发 更可恶的 是有些人经常为了一点绳头小利 仿造 以至于很多人还是到我们这里来寻求服务 但面对那么多热心的 客户我们也不能不服务 此时此刻看来服务并非 产品 无论怎样我们希望大家能够尊重我们的劳动成果 和知识产权 这也是我们可持续发展的动力
技术支持 电话 020) 85520995 85539796 85547386
85541621 85541773 85571663 电子邮箱 mcutools@ pmcu@
各地分公司 北京周立功 地址 北京市海淀区黄庄知春路 132 号中发电子大厦 701 室 电话 (010)82628073 82614433(Fax) 成都周立功 地址 成都市一环路南一段 57 号金城大厦 618 室 电话 (028)85499320 85439505(Fax) 深圳周立功 地址 深圳市深南中路 2070 号电子科技大厦 C 座 18 楼 D2 电话 (0755)83287588 83274266(Fax) 杭州周立功 地址 浙江省杭州市教工路 2 号杭州电子市场仪器仪表城二楼 555 室 电话 (0571)88271834 88271326 (Fax) 上海周立功 地址 上海市延安西路 1882 号东华大学上海喜天游大酒店 4 楼 C31 室 电话 (021)62199015 62199016(Fax) 南京周立功 地址 南京市珠江路 280 号珠江大厦 2006 室 电话 (025)3613221 3641103(Fax) 广州周立功 地址 广州市天河区新赛格电子城 203--204 室 邮编 510630 电话 (020)87578634 87578842(Fax)
嵌入式USB设备驱动的研究与设计开发
嵌入式USB设备驱动的研究与设计开发随着科技的飞速发展,嵌入式系统已经成为现代生活中不可或缺的一部分。
而作为嵌入式系统中最常见的外设之一,USB设备的驱动程序对于系统的正常运行起着至关重要的作用。
因此,对嵌入式USB设备驱动的研究与设计开发具有极高的实用价值和深远的意义。
嵌入式USB设备驱动的研究主要涉及到硬件和软件两个方面。
在硬件方面,需要对USB接口的工作原理进行深入研究,了解USB设备的接口规范以及各种USB设备的特性和使用场景。
同时,还需要研究与开发USB设备的驱动电路,确保其能够与嵌入式系统正常通信,并能实现数据的传输和交互。
在软件方面,嵌入式USB设备驱动的设计开发主要包括驱动程序的编写和优化。
首先,需要针对不同的USB设备类型和功能,编写相应的驱动程序。
例如,对于USB存储设备,需要编写文件系统的驱动程序,实现对存储设备的读写操作;对于USB打印机,需要编写打印机控制命令的驱动程序,实现打印功能等。
其次,还需要优化驱动程序的性能,提高系统的响应速度和稳定性。
这需要通过对驱动程序进行调试和测试,发现并修复可能存在的问题,确保驱动程序能够在不同的嵌入式平台上正常运行。
嵌入式USB设备驱动的研究与设计开发还涉及到与操作系统的兼容性和互通性。
不同的嵌入式系统可能采用不同的操作系统,如Linux、Windows等。
因此,在进行驱动程序的设计和开发时,需要考虑到不同操作系统的特点和要求,确保驱动程序能够在不同的操作系统环境下正常运行和兼容。
总之,嵌入式USB设备驱动的研究与设计开发是一个涉及到硬件和软件的综合性工作。
通过对USB接口的研究和了解,编写和优化驱动程序,考虑与操作系统的兼容性和互通性,可以实现嵌入式系统与USB设备之间的良好交互和数据传输,为现代生活中的各种嵌入式系统提供稳定可靠的外设支持。
这将进一步推动嵌入式系统的发展和应用,为人们的生活带来更多便利和创新。
USB驱动开发
• 初始化请求
• 为了创建一个URB,你首先应该为URB分配内存,然后调用初始化例程把URB 结构中的各个域填入请求要求的内容,例如,当你为响应IRP_START_DEVICE 请求而配置设备时,首要的任务就是读取该设备的设备描述符。
• • • • • • • • • • •
USB_DEVICE_DESCRIPTOR dd; URB urb; UsbBuildGetDescriptorRequest(&urb, sizeof(_URB_CONTROL_DESCRIPTOR_REQUEST), USB_DEVICE_DESCRIPTOR_TYPE, 0, 0, &dd, NULL, sizeof(dd), NULL);
•
•
•
设备的端点模式器的结构决定了它的查询频率,从1到255ms之间。这种传 输方式典型的应用在少量的分散的、不可预测数据的传输。键盘、操纵杆 和鼠标就属于这一类型。 ·大量(bulk)传送:主要应用在数据大量传送传送和接受数据上,同时又没有带宽
和间隔时间要求的情况下,要求保证传输。打印机和扫描仪属于这种类型。这种类 型的设备适合于传输非常慢和大量被延迟的传输,可以等到所有其它类型的数据的 传送完成之后再传送和接收数据。
• 应用软件设计
• 应用软件设计由两部分组成:链接库程序和应用程序。 链接库负责与USB功能驱动程序通信并接受应用程序的 各种操作请求,而应用程序负责对所采集的数据进行 分析处理。
• 我们在这里就可以总结一下:
• 一套完整的嵌入式系统,由硬件和软件组合而成。应用程序要控 制硬件系统的具体工作,但是现在可以看到,它并不是直接参与 硬件的控制,在中间层,还有一套具体的驱动程序来执行应用层 传递过来的I/O请求,同样,硬件系统要返回给应用程序的数据也 要经过驱动程序的处理。
USB设备驱动程序的研究与开发
种 例 程 的指 针 等 。其 相关 代码 如下 :
H ̄插 即用例 程人 口 l l J D i r be t > r eE tni > d D v e : r eO jc - D i r xe s n- A d ei v v o c
口。而要 实 现U B 件 设备 与 主机 间 稳定 的数 据 S硬
传 输 ,则 不 可避 免 的要 编 写适 合U B 件要 求 的 S硬
驱 动 程序 。为此 ,本 文结 合 实 例 ,论 述 了U B S 设
备 驱动 程序 的具 体实 现机 制和 开发 方法 。
示 是其 体 系结构 图。
/ R 发送 给下 层 的U B / 将U B S 总线 驱动 程序
D i r bet > aoF n t n【R _ _ R — r eO jc 一 M jru c o I P MJC E v i
A E =D v rae T] rC et;
nSa s sC l S D fo ub tt u =U b a U B I( , r); t l d
4 U B 备 驱 动 程 序 的 实现 S设
驱 动程 序 是一 些例 程 的集 合 ,它 一般 被 动地 存 在 ,以等 待 主 机 系统 软 件 (n 管 理 器 、I PP / O管 理 器 、 电源管 理器 1来 调用 。典 型 的U B 备 驱 S设 动 程序 主要 包 括若 干 例程 ,其 中有 驱 动程 序入 口
31 I P 求包 . R 请
个U B S 系统 的分 层 结 构 。 图 中 ,功 能层 负 责 实 现
U B 备 的特 定 功能 。该 层 不 需 要 理 解U B 体 S设 S具
USB驱动开发
第17章USB设备驱动USB设备驱动和PCI设备驱动是PC中最主要的两种设备驱动程序。
与PCI协议相比,USB协议更复杂,涉及面较多。
本章将介绍USB设备驱动开发。
首先介绍USB协议,使读者对USB协议有个整体认识。
然后介绍USB设备在WDM中的开发框架。
由于操作系统的USB总线驱动程序提供了丰富的功能调用,因此开发USB驱动开发变得相对简单,只需要调用USB总线驱动接口。
17.1 USB总线协议USB总线协议比PCI协议复杂的多,涉及USB物理层协议,又涉及USB传输层协议等。
对于USB驱动程序开发者来说,不需要对USB协议的每个细节都很清楚。
本节概要地介绍USB总线协议,并对驱动开发者需要了解的地方进行详细介绍。
17.1.1 USB设备简介USB即通用串行总线(Universal Serial Bus),是一种支持即插即用的新型串行接口。
也有人称之为“菊链(daisy-chaining)”,是因为在一条“线缆”上有链接127 个设备的能力。
USB要比标准串行口快得多,其数据传输率可达每秒4Mb~12Mb(而老式的串行口最多是每秒115Kb)。
除了具有较高的传输率外,它还能给外围设备提供支持。
需要注意的是,这不是一种新的总线标准,而是计算机系统连接外围设备(如键盘、鼠标、打印机等)的输入/输出接口标准。
到现在为止,计算机系统连接外围设备的接口还没有统一的标准,例如,键盘的插口是圆的、连接打印机要用9针或25针的并行接口、鼠标则要用9针或25针的串行接口。
USB能把这些不同的接口统一起来,仅用一个4针插头作为标准插头,如图17-1所示。
通过这个标准插头,采用菊花链形式可以把所有的外设连接起来,并且不会损失带宽。
USB正在取代当前PC上的串口和并口。
第17章 USB 设备驱动431图17-1 USB 的四条传输线以USB 方式连接设备时,所有的外设都在机箱外连接,连接外设不必再打开机箱;允许外设热插拔,而不必关闭主机电源。
linux设备驱动(28)usb驱动开发过程总结
linux设备驱动(28)usb驱动开发过程总结设备驱动程序是操作系统内核和机器硬件之间的接⼝,由⼀组函数和⼀些私有数据组成,是应⽤程序和硬件设备之间的桥梁。
在应⽤程序看来,硬件设备只是⼀个设备⽂件,应⽤程序可以像操作普通⽂件⼀样对硬件设备进⾏操作。
设备驱动程序是内核的⼀部分,主要完成以下功能:对设备的初始化和释放;把数据从内核传送到硬件设备和从硬件设备读取数据;读取应⽤程序数据传送给设备⽂件和回送应⽤程序请求的数据;检测和处理硬件设备出现的错误。
1 Linux USB⼦系统分析在Linux系统中,USB主机驱动程序由3部分组成:USB主机控制器驱动(HCD)、USB核⼼驱动(USBD)和不同种类的USB设备类驱动,如下所⽰。
其中HCD和USBD被称为协议软件或者协议栈,这两部分共同处理与协议相关的操作。
USB设备类驱动可以包含多个,不同的功能接⼝对应不同的驱动程序,它们不直接与USB设备硬件打交道,⽽是通过协议软件的抽象处理来完成与设备的不同功能接⼝之间的通信。
在Linux USB⼦系统中,HCD是直接和硬件进⾏交互的软件模块,是USB协议栈的最底层部分,是USB主机控制器硬件和数据传输的⼀种抽象。
HCD向上仅对USB总线驱动程序服务,HCD提供了⼀个软件接⼝,即HCDI,使得各种USB主机控制器的硬件特性都被软件化,并受USB总线驱动程序的调⽤和管理。
HCD向下则直接管理和检测主控制器硬件的各种⾏为。
HCD提供的功能主要有:主机控制器硬件初始化;为USBD层提供相应的接⼝函数;提供根HUB(ROOT HUB)设备配置、控制功能;完成4种类型的数据传输等。
USBD部分是整个USB主机驱动的核⼼,主要实现的功能有:USB总线管理;USB总线设备管理、USB总线带宽管理、USB的4种类型数据传输、USB HUB驱动、为USB设备驱动提供相关接⼝、提供应⽤程序访问USB系统的⽂件接⼝等。
其中USB HUB作为⼀类特殊的USB设备,其驱动程序被包含在USBD层。
USB驱动分析1
USB驱动分析一这个故事中使用的是2.6.10的内核代码.Linux内核代码目录中, 所有去设备驱动程序有关的代码都在drivers/目录下面,在这个目录中我们用ls命令可以看到很多子目录. localhost:/usr/src/linux-2.6.10/drivers # lsKconfig atm cdrom eisa ide macintosh message net parport s390 tc w1Makefile base char fc4 ieee1394 mca misc nubuspci sbus telephony zorroacorn block cpufreq firmware input md mmc oprofile pcmcia scsi usbacpi bluetooth dio i2c isdn media mtd parisc pn p serial video其中usb目录包含了所有usb设备的驱动,而usb目录下面又有它自己的子目录,进去看一下,localhost:/usr/src/linux-2.6.10/drivers # cd usb/locahost:/usr/src/linux-2.6.10/drivers/usb # lsKconfig Makefile README atm class core gadget host image input me dia misc net serial storage usb-skeleton.c注意到每一个目录下面都有一个Kconfig文件和一个Makefile,这很重要.稍后会有介绍. 而我们的故事其实是围绕着drivers/usb/storage这个目录来展开的.实际上这里边的代码清清楚楚地展示了我们日常频繁接触的U盘是如何工作的,是如何被驱动起来的.但是这个目录里边的冬冬并不是生活在世外桃源,他们总是和外面的世界有着千丝万缕的瓜葛.可以继续进来看一下,localhost:/usr/src/linux-2.6.10/drivers/usb # cd storage/localhost:/usr/src/linux-2.6.10/drivers/usb/storage # lsKconfig debug.c freecom.c isd200.c protocol.c sddr09.c shuttle_ usbat.c unusual_devs.hMakefile debug.h freecom.h isd200.h protocol.h sddr09.h shuttle _usbat.h usb.cdatafab.c dpcm.c initializers.c jumpshot.c scsiglue.c sddr55.c transport.c usb.hdatafab.h dpcm.h initializers.h jumpshot.h scsiglue.h sddr55.h transpor t.h咋一看,着实吓了一跳,用`wc -l *`这个命令统计一下,12076行,晕死...wc [ -c | -m ] [ -l ] [ -w ] [文件]或者wc -k [ -c ] [ -l ] [ -w ] [文件]注:缺省情况下,wc 命令对File 参数指定的文件中的行数、字数和字节数进行计数。
05-USB驱动程序开发
2、USB设备类型 USB设备类型
八、USB设备列举 USB设备列举
在USB规范中有一个非常重要的“动作” USB规范中有一个非常重要的 动作” 规范中有一个非常重要的“ 过程” 这个动作将会让PC PC机知道何 或“过程”。这个动作将会让PC机知道何 USB设备刚接上以及其所含的各种信息 设备刚接上以及其所含的各种信息。 种USB设备刚接上以及其所含的各种信息。 这样,PC机就可以与这个USB设备开始进行 机就可以与这个USB 这样,PC机就可以与这个USB设备开始进行 数据传输的工作了。 数据传输的工作了。这个动作称之为设备 列举(enumeration) (enumeration)。 列举(enumeration)。
7 . USB2.0 的 High-speed 模式支持音频和视频设备 , 可 USB2 High-speed模式支持音频和视频设备 模式支持音频和视频设备, 以保证其固定带宽; 以保证其固定带宽; 8 . 为了适应各种不同类型外围设备的要求 USB 提供了 为了适应各种不同类型外围设备的要求USB 四种不同的数据传输类型:控制传输,Bulk数据传输 数据传输, 四种不同的数据传输类型:控制传输, Bulk数据传输, 中断数据传输,同步数据传输。 中断数据传输, 同步数据传输。 同步数据传输可为音 频和视频等实时设备的实时数据传输提供固定带宽。 频和视频等实时设备的实时数据传输提供固定带宽。 的端口具有很灵活的扩展性。 一个USB 9 . USB 的端口具有很灵活的扩展性 。 一个 USB 端口串 接上一个USB 就可以扩展为多个USB 端口。 USB端口 接上一个 USB Hub 就可以扩展为多个 USB 端口 。 规范 中说,USB可以扩展到127个外设端口 可以扩展到127个外设端口。 中说,USB可以扩展到127个外设端口。
(简易USB驱动)开发指导
实验七(2)设备驱动开发指导块设备种类多,使用广泛,其驱动程序的开发也比字符设备复杂。
通过本实验,大家要开发一个实际块设备(U盘)的驱动程序,将能够更深入地掌握块设备驱动程序的开发方法。
Linux下已经有一个通用的U盘驱动程序usb-storage.o,其源程序放在目录drivers\usb\storage下(相对于内核源码根目录)。
但这个驱动的实现相当复杂,本实验希望开发一个相对简单些的U盘驱动程序,不求高性能,只求结构明朗、清晰易懂,主要是让大家掌握一个实际块设备的驱动方式,从而加深理解。
事实上,本实验开发的驱动程序应该能够适用于所有基于Bulkonly传输协议的USB大容量存储设备(USB Mass Storage),比如USB移动硬盘和USB外置光驱,USB闪存盘(U 盘)只是其中的一种。
由于USB大容量存储设备具有容量大、速度快、连接灵活、即插即用、总线供电等优点,它们得到了广泛使用,掌握这类设备驱动程序的开发技术无疑具有很强的实用性。
实验内容编写一个U盘驱动程序myudisk,只要求能够驱动某个型号的U盘,能够支持U盘的常规操作,如命令hexdump、mke2fs和mount等。
同时,要求在系统内核日志中显示出U盘的容量。
若有余力,可增加多分区支持功能。
实验基础和思路在教材中P130,讲解了如何编写一个Ramdisk块设备驱动程序(sbull.c),称为radimo;在文献《Linux Device Drivers》讲解了如何编写一个USB设备驱动程序,并以Linux源代码中的usb-skeleton.c为例。
虽然前者驱动的并不是一个实际的块设备,且后者又只是针对usb字符设备,但是它们提供了一个不错的基础,通过合并我们就能基本得到一个支持usb块设备的驱动程序。
之所以说基本得到,是因为合并后只是有了块设备、USB设备的驱动支持框架,但还缺一样:对U盘(USB块设备)的实际访问操作。
USB设备驱动开发-USBGadgetDriver
USB设备驱动开发-USBGadgetDriver一、Linux USB Gadget Driver功能为了与主机端驱动设备的USB Device Driver概念进行区别,将在外围器件中运行的驱动程序称为USB Gadget Driver。
其中,Host 端驱动设备的驱动程序是master或者client driver,设备端gadget driver是slave或者function driver。
Gadget Driver和USB Host端驱动程序类似,都是使用请求队列来对I/O包进行缓冲,这些请求可以被提交和取消。
它们的结构、消息和常量的定义也和USB技术规范第九章的内容一致。
同时也是通过bind和unbind将driver与device建立关系。
二、Linux USB Gadget Driver核心数据结构1. USB_Gadget对象struct usb_gadget {/* readonly to gadget driver */const struct usb_gadget_ops *ops; //Gadget设备操作函数集struct usb_ep *ep0; //控制端点,只对setup包响应struct list_head ep_list;//将设备的所有端点连成链表,ep0不在其中enum usb_device_speed speed;//高速、全速和低速unsigned is_dualspeed:1; //是否同时支持高速和全速unsigned is_otg:1; //是否支持OTG(On-To-Go)unsigned is_a_peripheral:1;unsigned b_hnp_enable:1;unsigned a_hnp_support:1;unsigned a_alt_hnp_support:1;const char *name; //器件名称struct device dev; //内核设备模型使用};2. Gadget器件操作函数集操作UDC硬件的API,但操作端点的函数由端点操作函数集完成struct usb_gadget_ops {int (*get_frame)(struct usb_gadget *);int (*wakeup)(struct usb_gadget *);int (*set_selfpowered) (struct usb_gadget *, int is_selfpowered);int (*vbus_session) (struct usb_gadget *, int is_active);int (*vbus_draw) (struct usb_gadget *, unsigned mA);int (*pullup) (struct usb_gadget *, int is_on);int (*ioctl)(struct usb_gadget *, unsigned code, unsigned long param);};3. USB Gadget driver对象struct usb_gadget_driver {char *function; //驱动名称enum usb_device_speed speed; //USB设备速度类型int (*bind)(struct usb_gadget *); //将驱动和设备绑定,一般在驱动注册时调用void (*unbind)(struct usb_gadget *);//卸载驱动时调用,rmmod时调用int (*setup)(struct usb_gadget *, const struct usb_ctrlrequest *); //处理ep0的控制请求,在中断中调用,不能睡眠void (*disconnect)(struct usb_gadget *); //可能在中断中调用不能睡眠void (*suspend)(struct usb_gadget *); //电源管理模式相关,设备挂起void (*resume)(struct usb_gadget *);//电源管理模式相关,设备恢复/* FIXME support safe rmmod */struct device_driver driver; //内核设备管理使用};4.描述一个I/O请求struct usb_request {void *buf; //数据缓存区unsigned length; //数据长度dma_addr_t dma; //与buf关联的DMA地址,DMA传输时使用unsigned no_interrupt:1;//当为true时,表示没有完成函数,则通过中断通知传输完成,这个由DMA控制器直接控制unsigned zero:1; //当输出的最后一个数据包不够长度是是否填充0unsigned short_not_ok:1; //当接收的数据不够指定长度时,是否报错void (*complete)(struct usb_ep *ep, struct usb_request *req);//请求完成函数void *context;//被completion回调函数使用struct list_head list; //被Gadget Driver使用,插入队列int status;//返回完成结果,0表示成功unsigned actual;//实际传输的数据长度};5.端点struct usb_ep {void *driver_data; //端点私有数据const char *name; //端点名称const struct usb_ep_ops *ops; //端点操作函数集struct list_head ep_list; //Gadget设备建立所有端点的链表unsigned maxpacket:16;//这个端点使用的最大包长度};6.端点操作函数集struct usb_ep_ops {int (*enable) (struct usb_ep *ep, const struct usb_endpoint_descriptor *desc);int (*disable) (struct usb_ep *ep);struct usb_request *(*alloc_request) (struct usb_ep *ep, gfp_t gfp_flags);void (*free_request) (struct usb_ep *ep, struct usb_request *req);int (*queue) (struct usb_ep *ep, struct usb_request *req, gfp_t gfp_flags);int (*dequeue) (struct usb_ep *ep, struct usb_request *req);int (*set_halt) (struct usb_ep *ep, int value);int (*set_wedge) (struct usb_ep *ep);int (*fifo_status) (struct usb_ep *ep);void (*fifo_flush) (struct usb_ep *ep);};7.字符串结构struct usb_gadget_strings {u16 language; /* 0x0409 for en-us */struct usb_string *strings;};struct usb_string {u8 id; //索引const char *s;};8. UDC驱动程序需要实现的上层调用接口int usb_gadget_register_driver(struct usb_gadget_driver *driver);int usb_gadget_unregister_driver(struct usb_gadget_driver *driver);三、UDC驱动程序1.UDC层主要数据结构,以S3C2410为例,在driver/usb/gadget/s3c2410_udc.c和s3c2410_udc.h 文件中。
usb 驱动开发原理
usb 驱动开发原理USB驱动开发原理USB(Universal Serial Bus,通用串行总线)是一种用于连接计算机与外部设备的通信接口标准。
USB驱动开发是为了实现计算机与USB设备之间的数据传输而进行的软件编程。
本文将介绍USB驱动开发的原理和步骤。
一、USB驱动开发的基本原理USB驱动开发的基本原理是通过驱动程序与USB设备之间的通信来实现数据的传输。
USB驱动程序负责管理和控制USB设备,将计算机的请求传递给USB设备,并将USB设备的响应传递给计算机。
USB驱动开发的基本流程如下:1. 初始化USB驱动程序:驱动程序需要初始化USB控制器和USB设备。
这包括初始化数据结构、分配内存空间、设置中断处理程序等操作。
2. 建立通信连接:驱动程序需要与USB设备建立通信连接。
这包括检测和识别USB设备、分配端点和接口、设置传输模式等操作。
3. 数据传输:驱动程序通过读取和写入USB设备的寄存器来实现数据的传输。
这包括发送和接收数据包、处理中断和错误等操作。
4. 终止通信连接:在完成数据传输后,驱动程序需要关闭通信连接。
这包括释放端点和接口、清除中断和错误等操作。
二、USB驱动开发的步骤USB驱动开发的步骤如下:1. 确定USB设备的功能和特性:USB设备可以具有多种功能和特性,例如存储设备、打印机、摄像头等。
驱动程序需要了解USB设备的功能和特性,以便正确地管理和控制USB设备。
2. 编写驱动程序:驱动程序是实现USB驱动开发的核心部分。
驱动程序需要根据USB设备的功能和特性编写相应的代码,以实现数据的传输和设备的控制。
3. 进行调试和测试:在编写驱动程序后,需要进行调试和测试来验证驱动程序的正确性和稳定性。
这包括检查驱动程序的功能、性能和兼容性等方面。
4. 发布和维护驱动程序:在通过调试和测试后,可以将驱动程序发布给用户使用。
同时,还需要对驱动程序进行维护,以修复bug和提升性能。
三、USB驱动开发的挑战和解决方案USB驱动开发面临一些挑战,例如设备的兼容性、驱动程序的稳定性、传输性能的优化等。
《嵌入式Linux下USB设备驱动开发深度解析》PDF
嵌入式Linux下USB设备驱动开发解析华清远见Copyright 2007-2008 Farsight.All rights reserved.LINUX的USB设备驱动程序开发}USB及驱动框架简介}USB主机端驱动}USB设备端驱动usb 驱动程序功能演示}步骤1: 插入MMC卡到fs2410开发板,出现设备/dev/mmcblk0}步骤2: 插入4GB Kingston优盘到fs2410开发板的usb host接口.fs2410将识别这个插入过程并出现设备/dev/uba1(或者/dev/sda1)}步骤3: 将fs2410开发板的usb device接口插入windows USB口,使得fs2410的本机nandflash/dev/mtdblock3和上述两个设备(mmc卡/4GB优盘)都能在电脑上通过优盘形式来访问(出现3个盘符).usb 驱动程序功能演示}/dev/mtdblock3=> fs2410开发板上nandflash}/dev/mmcblk0=> 接在fs2410开发板上的MMC卡}/dev/uba1=> 接在fs2410开发板上的Kingston 优盘}=> 这个演示,涉及了usb host和usb device功能(也涉及了sd卡驱动的功能).usb 驱动程序功能演示: 解释}fs2410 usb host: 插入优盘到fs2410 usb主机端,fs2410 usb 主机端检测到插入优盘设备并完成枚举和初始化过程.然后调用一个具体的设备驱动(如storage设备驱动)并产生一个设备节点/dev/sda1}fs2410 usb device: usb设备端驱动在用户的要求下将3个设备(mmcblk0/sda1/mtdblock3)作为优盘设备接入windows usb主机端.并对windows发起的枚举过程作出正确的响应,返回三个设备的相关信息,使得最终windows能正确识别出这三个设备,并出现3个优盘盘符供用户方便的访问这些存储介质.}Linux-USB 子系统}Linux-USB 子系统Linux对USB规范的支持}USB-通用串行总线是目前使用最广泛的外部总线}USB是采用单一的主从设备通信模式。
STM32的USB开发教程详解
Sept. 14~25, 2009
23
STM32互联型产品OTG_FS控制器
USB协议简介
USB协议特性 USB系统和架构 USB设备类和供电 USB传输类型 USB OTG协议
STM32互联型产品OTG_FS控制器
OTG_FS控制器特性 OTG_FS控制器不同模式的连接图 OTG_FS软件解决方案和应用实例
Sept. 14~25, 2009
12
USB 供电
按供电类型分:
主HUB:直接连接到USB主机控制器,和主机控制器从同一个源 取电。向高功耗端口和低功耗端口提供一个单位(100mA)到五个单 位的供电。 总线供电类HUB:从上游端口的VBUS 取电,为自身功能及其下游 端口供电。 自供电HUB:从外部电源取电,为自身功能及其下游端口供电。 低功耗总线供电设备:从上游端口的VBUS取电,在任何时刻,耗电 都不能超过一个单位。 高功耗总线供电设备:从上游端口的VBUS取电,在上电时,耗电不 能超过一个单位,在配置后,耗电可以达到五个单位。 自供电设备:可以从上游端口获得不超过一个单位的电流,其他的 耗电需要从外部电源获取。
10
USB系统和架构
USB协议简介
USB协议特性 USB系统和架构 USB设备类和供电
STM32互联型产品OTG_FS控制器
STMicroelectronics
Sept. 14~25, 2009
11
USB 设备类
音频类(Audio) 通信设备类 (CDC) 设备固件升级类 (DFU) 人机接口类 (HID) 智能卡接口设备类 (CCID) 影像类(Imaging) 红外线桥接设备类 (IrDA Bridge) 大容量存储设备类 (Mass Storage)
USBgadget设备驱动解析
u32 upll_value;
set_s3c2410fb_info(&smdk2410_lcdcfg);
s3c24xx_udc_set_platdata(&smdk2410_udc_cfg); /*初始化*/
s3c_device_sdi.dev.platform_data = &smdk2410_mmc_cfg;
Microsoft Windows中提供对Mass Storage协议的支持,因此USB移动设备只需要遵循Mass Storage协议来组织数据和处理命令,即可实现与PC机交换数据。而Flash的储备单元组织形式采纳FAT16文件系统,如此,就能够直截了当在Windows的扫瞄器中通过可移动磁盘来交换数据了,Windows负责对FAT16文件系统的治理,USB设备不需要干预FAT16文件系统操作的具体细节。
Linux USB gadget设备驱动解析(2)---驱动调试
这一节要紧把在实现“linux模拟U盘功能”过程中的一些调试过程记录下来,并加以解析。
背景知识
1、USB Mass Storage类规范概述
USB组织在universal Serial Bus Mass Storage Class Spaceification 1.1版本中定义了海量储备设备类(Mass Storage Class)的规范,那个类规范包括四个独立的子类规范,即:
加载驱动,测试功能
利用前面的生成的内核,启动系统后,加载g_file_storage.ko
#insmod g_file_storage.ko
# insmod g_file_storage.ko file=/dev/mtdblock2 stall=0 removable=1
通用USB设备驱动源码分析
通用USB设备驱动源码分析Author:aaron前段时间写了篇<qualcomm usb modem驱动小结>的文章, 描述了自己如何为高通的一个usb modem设备写驱动的过程, 最近发现实际上可以使用linux自带的一个叫usbserial的模块作为这个modem的驱动并能良好的工作, 所以写了这片文章来详细的分析下usbserial 模块的源码(2.6.16.3).应该来说, 对于那些仅仅是用USB来通信, 在上层可看作tty设备, 不属于任何USB设备类型, 没有什么流控等的普通USB设备来说都可以使用这个驱动来作为设备驱动程序.下面就来对这样一种通用的驱动程序来进行详细的分析. 不对之处敬请指正!为了能让usbserail模块支持我的设备, 我必须在命令行上输入如下命令:sudo modprobe usbserial vendor=0x12d1 product=0x1003该命令用特权用户来加载usbserial模块,并把该模块依赖的模块一并加载进系统, 同时它还设置了usbserial的两个参数: vendor, product, 很显然这两个参数是厂商ID和设备ID, 而作用就是用于匹配设备.首先, 当然是要知道usbserial模块由哪些文件编译而成, 这样才能有目的性的去分析其代码. 而要知道其组成当然是去其目录下看Makefile了, 它位于内核源码目录下的./drivers/usb/serial/下./drivers/usb/serial/Makefile:## Makefile for the USB serial device drivers.## Object file lists.obj-$(CONFIG_USB_SERIAL) +=usbserial.o #编译内核时如何编译该模块usbserial-obj-$(CONFIG_USB_SERIAL_CONSOLE) += console.ousbserial-obj-$(CONFIG_USB_EZUSB) += ezusb.ousbserial-objs := usb-serial.o generic.o bus.o $(usbserial-obj-y) #OK, 就是usbserial模块的组成了.obj-$(CONFIG_USB_SERIAL_AIRPRIME) += airprime.oobj-$(CONFIG_USB_SERIAL_ANYDATA) += anydata.o .......我们重点看的是usb-serial.c, generic.c, bus.c在看源码之前我们先说说该模块的原理及整体结构:很简单跟应用层交互的是一个tty设备, 也就是说该模块把USB 设备映射成一个tty设备(即在/dev/目录下为该USB设备创建一个tty设备文件), 然后用于可以用minicom之类的串口工具来打开这个设备, 并同设备端的设备通信.对于发送过程: tty设备文件在获取了用户要求发送的数据之后传递到下层usbserial模块的核心层,而该核心层就是将数据打包成USB格式的数据并由USB通信发送到设备端去,对于接收过程: usbserial模块会在该设备打开时就启动一个urb 在那等待设备端发数据过来, 收到数据后就push到上层tty设备的缓冲中去, 而tty设备在收到数据后就会给用户,或直接显示在minicom之类的工具上.usb-serial.c 就是usbserial模块的核心, 它主要用来接收设备端发来的数据并传送到上层, 同时也接收来自上层应用的数据,并组装成urb包发送给设备.generic.c 对特定设备单独的操作,相当于是设备自己的驱动程序, 由于很多设备具有通用性, 所以对于没有特殊要求的设备都可以使用这个驱动来作为自己设备的驱动程序. 它有两个参数vendor 和product,上面提过了.bus.c 每个usb驱动和设备都必须要归入某一条总线上, 即都是归属于某条总线的,只有这样系统才能从特定一条总线开始找到每个驱动和设备并为他们匹配. 这个文件就是用来模拟一条总线, 而usbserial的每个驱动和设备都会注册到这条总线上来.好了,是时候分析usbserial模块了.我们知道当把一个模块加载进系统时会调用这个模块里的一个由module_init()声明的一个初始化函数. usbserial当然也不另外, usb-serial.c:module_init(usb_serial_init);module_exit(usb_serial_exit);没错加载时调用的就是: usb_serial_init().usb-serial.c:struct tty_driver *usb_serial_tty_driver;static int __init usb_serial_init(void){int i;int result;//创建一个tty_driver对象, 对应的就是tty设备的驱动.usb_serial_tty_driver =alloc_tty_driver(SERIAL_TTY_MINORS);if (!usb_serial_tty_driver)return -ENOMEM;/* Initialize our global data */for (i = 0; i < SERIAL_TTY_MINORS; ++i) {serial_table[i] = NULL; //该模块共支持SERIAL_TTY_MINORS个该类型设备.}result = bus_register(&usb_serial_bus_type); //注册这条serial bus.if (result) {err("%s - registering bus driver failed", __FUNCTION__);goto exit_bus;}//初始化tty_driver对象usb_serial_tty_driver->owner = THIS_MODULE;usb_serial_tty_driver->driver_name = "usbserial";usb_serial_tty_driver->devfs_name = "usb/tts/";usb_serial_tty_driver->name= "ttyUSB"; //tty设备文件名以这个开头,后加0,1,2,3,....usb_serial_tty_driver->major =SERIAL_TTY_MAJOR; //主设备号usb_serial_tty_driver->minor_start = 0;usb_serial_tty_driver->type =TTY_DRIVER_TYPE_SERIAL; //设备类型usb_serial_tty_driver->subtype =SERIAL_TYPE_NORMAL;usb_serial_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS;usb_serial_tty_driver->init_termios =tty_std_termios;usb_serial_tty_driver->init_termios.c_cflag =B9600 | CS8 | CREAD | HUPCL | CLOCAL;//赋值tty设备的操作集合,即应用层调用open时最终会调到serial_ops->open里面tty_set_operations(usb_serial_tty_driver,&serial_ops);result =tty_register_driver(usb_serial_tty_driver); //注册这个tty 驱动if (result) {err("%s - tty_register_driver failed",__FUNCTION__);goto exit_reg_driver;}/* register the USB driver */result = usb_register(&usb_serial_driver); //注册一个usb驱动if (result < 0) {err("%s - usb_register failed",__FUNCTION__);goto exit_tty;}/* register the generic driver, if we should */result = usb_serial_generic_register(debug); //注册generic驱动程序if (result < 0) {err("%s - registering generic driver failed", __FUNCTION__);goto exit_generic;}info(DRIVER_DESC);return result;//失败时候的一些反向操作exit_generic:usb_deregister(&usb_serial_driver);exit_tty:tty_unregister_driver(usb_serial_tty_driver);exit_reg_driver:bus_unregister(&usb_serial_bus_type);exit_bus:err ("%s - returning with error %d", __FUNCTION__, result);put_tty_driver(usb_serial_tty_driver);return result;}该函数先创建并初始化好了一个tty_driver的对象, 并把该对象注册进系统, 该对象就是tty设备的驱动程序, 后面我们会看到他是如何于具体tty设备绑定在一起的.usb_serial.c:static struct tty_operations serial_ops = {.open = serial_open,.close = serial_close,.write = serial_write,.write_room = serial_write_room,.ioctl = serial_ioctl,.set_termios = serial_set_termios,.throttle = serial_throttle,.unthrottle = serial_unthrottle,.break_ctl = serial_break,.chars_in_buffer = serial_chars_in_buffer,.read_proc = serial_read_proc,.tiocmget = serial_tiocmget,.tiocmset = serial_tiocmset,};这个就是tty设备文件对应的操作方法集合, 例如, 应用层调用open函数来打开该设备文件时将最终会走到serial_open里面.usb_serial_init() 还注册了一条总线:usb_serial_bus_type, 这样当有设备连上系统时, 该总线上的驱动就有机会去匹配这个设备. 后面我们会看到generic的驱动就是注册在该总线上的.bus.c:struct bus_type usb_serial_bus_type = {.name = "usb-serial",.match = usb_serial_device_match, //在设备匹配时会调用.probe = usb_serial_device_probe,.remove = usb_serial_device_remove,};关于设备匹配过程(probe)可以参考我的另一篇文章.usb_serial_init() 在最后usb_serial_generic_register(debug)来注册generic驱动.generic.c:int usb_serial_generic_register (int _debug){int retval = 0;debug = _debug;#ifdef CONFIG_USB_SERIAL_GENERICgeneric_device_ids[0].idVendor = vendor; //保存厂商IDgeneric_device_ids[0].idProduct = product; //保存产品IDgeneric_device_ids[0].match_flags =USB_DEVICE_ID_MATCH_VENDOR |USB_DEVICE_ID_MATCH_PRODUCT; //匹配类型/* register our generic driver with ourselves */ retval = usb_serial_register(&usb_serial_generic_device); //注册驱动if (retval)goto exit;retval = usb_register(&generic_driver); //注册驱动if (retval)usb_serial_deregister(&usb_serial_generic_ device);exit:#endifreturn retval;}该函数首先保存了命令通过命令行设备的vendor,product 用于以后设备匹配, 由此我们知道该驱动可以动态支持设备匹配. 接着该函数注册了usb_serial_generic_device驱动.generic.c:struct usb_serial_driver usb_serial_generic_device = { .driver = {.owner = THIS_MODULE,.name = "generic",},.id_table = generic_device_ids, //匹配用的设备列表, 支持动态匹配.num_interrupt_in = NUM_DONT_CARE,.num_bulk_in = NUM_DONT_CARE,.num_bulk_out = NUM_DONT_CARE,.num_ports = 1,.shutdown = usb_serial_generic_shutdown, };Usb-serial.c:int usb_serial_register(struct usb_serial_driver *driver) {int retval;fixup_generic(driver); //为driver赋上默认的操作函数if (!driver->description)driver->description = driver->;/* Add this device to our list of devices */list_add(&driver->driver_list,&usb_serial_driver_list); //加入驱动列表retval = usb_serial_bus_register(driver); //把该驱动注册进usb serial bus下if (retval) {err("problem %d when registering driver %s", retval, driver->description);list_del(&driver->driver_list);}elseinfo("USB Serial support registered for %s", driver->description);return retval;}其中的fixup_generic()函数仅仅是为driver赋上默认的操作函数.Usb-serial.c:#define set_to_generic_if_null(type,function) \do{ \ if (!type->function){ \type->function =usb_serial_generic_##function; \dbg("Had to override the "#function \" usb serial operation with the generic one.");\} \} while (0)static void fixup_generic(struct usb_serial_driver*device){set_to_generic_if_null(device, open);set_to_generic_if_null(device, write);set_to_generic_if_null(device, close);set_to_generic_if_null(device, write_room);set_to_generic_if_null(device, chars_in_buffer);set_to_generic_if_null(device,read_bulk_callback);set_to_generic_if_null(device,write_bulk_callback);set_to_generic_if_null(device, shutdown);}即通过上面的usb_serial_register()函数后usb_serial_generic_device的函数集为:usb_serial_generic_device.open = usb_serial_generic_open;usb_serial_generic_device.close =usb_serial_generic_close......驱动usb_serial_generic_device将是以后操作tty设备的主要函数.我们会在后面分析.bus.c:int usb_serial_bus_register(struct usb_serial_driver *driver){int retval;driver->driver.bus = &usb_serial_bus_type; //注册到该bus下retval = driver_register(&driver->driver);return retval;}最后usb_serial_generic_register()函数注册了一个generic_driver驱动.generic.c:static struct usb_driver generic_driver = {.name = "usbserial_generic",.probe = generic_probe, //匹配函数.disconnect = usb_serial_disconnect,.id_table = generic_serial_ids, //匹配用的设备列表.no_dynamic_id = 1, //不支持动态匹配};整个初始化过程, 乍一看一下子注册了几个驱动程序, 几个驱动列表, 有的支持动态匹配有的不支持, 感觉很复杂, 其实注册generic_driver驱动主要是为了注册一个generic_probe函数, 而该函数将会在设备连上系统后被调用以来匹配设备. 除此之外该驱动没什么用, 而在这个初始化函数中把vendor,product都保存在了generic_device_ids里, 因此可以肯定以后的匹配将用这个设备列表,而不是generic_serial_ids, 说的更直白些generic_serial_ids其实根本也没什么用. 真正有用的是usb_serial_generic_device驱动,generic.c:static int generic_probe(struct usb_interface *interface, const struct usb_device_id *id){const struct usb_device_id *id_pattern;id_pattern =usb_match_id(interface, generic_device_ids); //设备匹配 if (id_pattern != NULL)return usb_serial_probe(interface, id); //进一步匹配return -ENODEV;}如果接入系统的设备的vendor和product与我们驱动支持的设备列表匹配则调用usb_serial_probe来进一步匹配.usb_serial_probe函数比较长, 我们一段段的来看usb-serial.c:int usb_serial_probe(struct usb_interface *interface, const struct usb_device_id *id){struct usb_device *dev = interface_to_usbdev (interface);struct usb_serial *serial = NULL;struct usb_serial_port *port;struct usb_host_interface *iface_desc;struct usb_endpoint_descriptor *endpoint;struct usb_endpoint_descriptor*interrupt_in_endpoint[MAX_NUM_PORTS];struct usb_endpoint_descriptor*interrupt_out_endpoint[MAX_NUM_PORTS];struct usb_endpoint_descriptor*bulk_in_endpoint[MAX_NUM_PORTS];struct usb_endpoint_descriptor*bulk_out_endpoint[MAX_NUM_PORTS];struct usb_serial_driver *type = NULL;int retval;int minor;int buffer_size;int i;int num_interrupt_in = 0;int num_interrupt_out = 0;int num_bulk_in = 0;int num_bulk_out = 0;int num_ports = 0;int max_endpoints;type = search_serial_device(interface); //获取该设备匹配的驱动if (!type) {dbg("none matched");return -ENODEV;}......}首先是找到合适的驱动程序.usb-serial.c:static struct usb_serial_driver*search_serial_device(struct usb_interface *iface) {struct list_head *p;const struct usb_device_id *id;struct usb_serial_driver *t;/* Check if the usb id matches a known device */ list_for_each(p, &usb_serial_driver_list) {t = list_entry(p, struct usb_serial_driver, driver_list);id = usb_match_id(iface, t->id_table); //看设备列表是否匹配if (id != NULL) {dbg("descriptor matches");return t; //返回匹配的驱动}}return NULL;}实际上这边的匹配和generic_probe里的匹配重复了, 因为他们的匹配的设备列表是同一个, 这边主要是为了得到匹配的驱动程序, 根据上面的代码分析我们可以知道这里匹配的驱动是usb_serial_generic_device.接着看usb_serial_probe()usb-serial.c:....serial = create_serial (dev, interface, type); //为该设备创建一个usb_serial对象if (!serial) {dev_err(&interface->dev, "%s - out of memory\n", __FUNCTION__);return -ENOMEM;}/* if this device type has a probe function, call it */if (type->probe) { //从上面分析的代码可知这里的probe函数没有赋值const struct usb_device_id *id;if (!try_module_get(type->driver.owner)) { dev_err(&interface->dev, "module get failed, exiting\n");kfree (serial);return -EIO;}id = usb_match_id(interface,type->id_table);retval = type->probe(serial, id);module_put(type->driver.owner);if (retval) {dbg ("sub driver rejected device");kfree (serial);return retval;}}....这段代码可知, 主要是创建一个usb_serial的对象, 用于保存该设备的详细信息, 一般的驱动程序都会为自己匹配的设备创建一个描用于描述该设备的对象. 在以后的所有操作中如读写等都会直接从这个对象里获取相应的信息.usb-serial.c:static struct usb_serial * create_serial (struct usb_device *dev,structusb_interface *interface,structusb_serial_driver *driver){struct usb_serial *serial;serial = kmalloc (sizeof (*serial),GFP_KERNEL); //闯将该对象if (!serial) {dev_err(&dev->dev, "%s - out of memory\n", __FUNCTION__);return NULL;}//初始化该对象memset (serial, 0, sizeof(*serial));serial->dev = usb_get_dev(dev); //增加dev的引用计数serial->type = driver;serial->interface = interface;kref_init(&serial->kref);return serial;}这个函数就是用来创建usb_serial对象的,并把相关信息保存在里面.继续看usb_serial_probe()usb-serial.c:..../* descriptor matches, let's find the endpoints needed *//* check out the endpoints *///查找该设备使用的endpoint的描述符, 并检查是否正确 iface_desc = interface->cur_altsetting; //接口描述符for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {endpoint =&iface_desc->endpoint[i].desc; //端点描述符if ((endpoint->bEndpointAddress & 0x80) && ((endpoint->bmAttributes & 3) == 0x02)) {/* we found a bulk in endpoint*/ //bulk in 的端点dbg("found bulk in on endpoint %d", i);bulk_in_endpoint[num_bulk_in] = endpoint;++num_bulk_in;}if (((endpoint->bEndpointAddress & 0x80) == 0x00) &&((endpoint->bmAttributes & 3) == 0x02)) {/* we found a bulk out endpoint*/ //bulk out的端点dbg("found bulk out on endpoint %d", i);bulk_out_endpoint[num_bulk_out] = endpoint;++num_bulk_out;}if ((endpoint->bEndpointAddress & 0x80) && ((endpoint->bmAttributes & 3) == 0x03)) {/* we found a interrupt in endpoint */ //中断 in 端点dbg("found interrupt in onendpoint %d", i);interrupt_in_endpoint[num_interrupt _in] = endpoint;++num_interrupt_in;}if (((endpoint->bEndpointAddress & 0x80) == 0x00) &&((endpoint->bmAttributes & 3) == 0x03)) {/* we found an interrupt out endpoint */ //中断 out 端点dbg("found interrupt out on endpoint %d", i);interrupt_out_endpoint[num_interrup t_out] = endpoint;++num_interrupt_out;}}.....该段代码主要是获取该设备使用的各个类型及方向的端点描述府, 并保存起来, 关于端点的类型与方向可以参考USB的规范.继续看usb_serial_probe()usb-serial.c:....#if defined(CONFIG_USB_SERIAL_PL2303) ||defined(CONFIG_USB_SERIAL_PL2303_MODULE)/* BEGIN HORRIBLE HACK FOR PL2303 *//* this is needed due to the looney way its endpoints are set up */if (((le16_to_cpu(dev->descriptor.idVendor) ==PL2303_VENDOR_ID) &&(le16_to_cpu(dev->descriptor.idProduct) == PL2303_PRODUCT_ID)) ||((le16_to_cpu(dev->descriptor.idVendor) == ATEN_VENDOR_ID) &&(le16_to_cpu(dev->descriptor.idProduct) == ATEN_PRODUCT_ID))) {if (interface !=dev->actconfig->interface[0]) {/* check out the endpoints of the other interface*/iface_desc =dev->actconfig->interface[0]->cur_altsetting;for (i = 0; i <iface_desc->desc.bNumEndpoints; ++i) {endpoint =&iface_desc->endpoint[i].desc;if((endpoint->bEndpointAddress & 0x80) &&((endpoint->bmAttributes & 3) == 0x03)) {/* we found a interrupt in endpoint */dbg("found interrupt in for Prolific device on separate interface");interrupt_in_endpoint [num_interrupt_in] = endpoint;++num_interrupt_in;}}}/* Now make sure the PL-2303 is configured correctly.* If not, give up now and hope this hack will work* properly during a later invocation of usb_serial_probe*/if (num_bulk_in == 0 || num_bulk_out == 0) { dev_info(&interface->dev, "PL-2303 hack: descriptors matched but endpoints did not\n");kfree (serial);return -ENODEV;}}/* END HORRIBLE HACK FOR PL2303 */#endif上面这段代码主要是用于特定类型设备的(PL2303), 这里我们不用管他.接着看usb_serial_probe()usb-serial.c:..../* found all that we need */dev_info(&interface->dev, "%s converterdetected\n", type->description);#ifdef CONFIG_USB_SERIAL_GENERIC //这个宏定义了, 因为我们使用的是通用USB驱动.if (type == &usb_serial_generic_device) { //这个if为TRUE(上面分析过了)num_ports = num_bulk_out;if (num_ports == 0) { //bulk out端点必须要有dev_err(&interface->dev, "Generic device with no bulk out, not allowed.\n");kfree (serial);return -EIO;}}#endifif (!num_ports) { //由于走到了上面那个if段,因此这里的num_ports肯定不为0/* if this device type has a calc_num_ports function, call it */if (type->calc_num_ports) {if(!try_module_get(type->driver.owner)) {dev_err(&interface->dev, "module get failed, exiting\n");kfree (serial);return -EIO;}num_ports = type->calc_num_ports (serial);module_put(type->driver.owner);}if (!num_ports)num_ports = type->num_ports;}//获取一个空闲的serial_table项if (get_free_serial (serial, num_ports, &minor) == NULL) {dev_err(&interface->dev, "No more free serial devices\n");kfree (serial);return -ENOMEM;}usbserial模块总共支持SERIAL_TTY_MINORS个设备, 它为每个设备都分配了一个serial_table项, 用于保存usb_serial对象, 方便以后直接通过minor号获取usb_serial对象.usb-serial.c:static struct usb_serial *get_free_serial (structusb_serial *serial, int num_ports, unsigned int *minor) {unsigned int i, j;int good_spot;dbg("%s %d", __FUNCTION__, num_ports);*minor = 0;for (i = 0; i < SERIAL_TTY_MINORS; ++i) {if (serial_table[i]) //查找一个空闲的serial_table项, serial_table是个usb_serial的指针数组.continue;good_spot = 1;//从上面代码可知, 对于generic的驱动, num_ports 就等于num_bulk_out, 而一般的设备仅有//一个bulkout 的端点, 因此这个for循环不会执行.for (j = 1; j <= num_ports-1; ++j)if ((i+j >= SERIAL_TTY_MINORS) || (serial_table[i+j])) {good_spot = 0;i += j;break;}if (good_spot == 0)continue;*minor = i; //获取minor号dbg("%s - minor base = %d", __FUNCTION__, *minor);for (i = *minor; (i < (*minor + num_ports)) && (i < SERIAL_TTY_MINORS); ++i)serial_table[i] = serial; //获取空闲的serial_table项, 并把我们的usb_serial对象地址保//存在其中.return serial;}return NULL;}通过这个函数我们找到了一个空闲的serial_table项, 并把描述我们设备的usb_serial对象保存在其中, 在以后对设备的使用中, 我们可以轻易的通过minor号来找到这个usb_serial.接着看usb_serial_probe()usb-serial.c:....//保存设备信息到usb_serial对象中,serial->minor = minor;serial->num_ports = num_ports; //这里的port数量不是endpoint的数量,serial->num_bulk_in = num_bulk_in;serial->num_bulk_out = num_bulk_out;serial->num_interrupt_in = num_interrupt_in;serial->num_interrupt_out = num_interrupt_out;/* create our ports, we need as many as the max endpoints *//* we don't use num_ports here cauz some devices have more endpoint pairs than ports *///对于generic的驱动来说一般都只有一个bulk in,一个bulk out,一个interrupt in,一个interrupt outmax_endpoints = max(num_bulk_in, num_bulk_out);max_endpoints = max(max_endpoints,num_interrupt_in);max_endpoints = max(max_endpoints,num_interrupt_out);max_endpoints = max(max_endpoints,(int)serial->num_ports);//到这一步,对于generic来说大多数情况下max_endpoints 还是为1serial->num_port_pointers = max_endpoints;dbg("%s - setting up %d port structures for this device", __FUNCTION__, max_endpoints);for (i = 0; i < max_endpoints; ++i) {port = kmalloc(sizeof(structusb_serial_port), GFP_KERNEL); //分配一个port对象 if (!port)goto probe_error;//初始化port对象memset(port, 0x00, sizeof(structusb_serial_port));port->number = i + serial->minor;port->serial = serial; //保存usb_serial 对象, 便于以后通过port对象访问到usb_serial对象 spin_lock_init(&port->lock);sema_init(&port->sem, 1);INIT_WORK(&port->work,usb_serial_port_softint, port);serial->port[i] = port;}//由上面的对port的初始化可知,每个port都有一套自己的工作机制, port间互不干扰/* set up the endpoint information */for (i = 0; i < num_bulk_in; ++i) {//初始化bulk in 端点, 并把它保存到相应的port里 endpoint = bulk_in_endpoint[i];port = serial->port[i];port->read_urb = usb_alloc_urb (0,GFP_KERNEL); //分配urbif (!port->read_urb) {dev_err(&interface->dev, "No free urbs available\n");goto probe_error;}buffer_size =le16_to_cpu(endpoint->wMaxPacketSize);port->bulk_in_size = buffer_size;port->bulk_in_endpointAddress =endpoint->bEndpointAddress; //保存端点地址port->bulk_in_buffer = kmalloc (buffer_size, GFP_KERNEL); //分配传输缓存if (!port->bulk_in_buffer) {dev_err(&interface->dev, "Couldn't allocate bulk_in_buffer\n");goto probe_error;}//设置好该urb.usb_fill_bulk_urb (port->read_urb, dev, usb_rcvbulkpipe (dev,endpoint->bE ndpointAddress),port->bulk_in_buffer, buffer_size,serial->type->read_bulk_c allback,port);}for (i = 0; i < num_bulk_out; ++i) {//初始化bulk out 端点, 并把它保存到相应的port里 endpoint = bulk_out_endpoint[i];port = serial->port[i];port->write_urb = usb_alloc_urb(0,GFP_KERNEL);if (!port->write_urb) {dev_err(&interface->dev, "No free urbs available\n");goto probe_error;}buffer_size =le16_to_cpu(endpoint->wMaxPacketSize);port->bulk_out_size = buffer_size;port->bulk_out_endpointAddress =endpoint->bEndpointAddress;port->bulk_out_buffer = kmalloc(buffer_size, GFP_KERNEL);if (!port->bulk_out_buffer) {dev_err(&interface->dev, "Couldn't allocate bulk_out_buffer\n");。
通用USB设备驱动源码分析
通用USB设备驱动源码分析Author:aaron前段时间写了篇<qualcomm usb modem驱动小结>的文章, 描述了自己如何为高通的一个usb modem设备写驱动的过程, 最近发现实际上可以使用linux自带的一个叫usbserial的模块作为这个modem的驱动并能良好的工作, 所以写了这片文章来详细的分析下usbserial 模块的源码(2.6.16.3).应该来说, 对于那些仅仅是用USB来通信, 在上层可看作tty设备, 不属于任何USB设备类型, 没有什么流控等的普通USB设备来说都可以使用这个驱动来作为设备驱动程序.下面就来对这样一种通用的驱动程序来进行详细的分析. 不对之处敬请指正!为了能让usbserail模块支持我的设备, 我必须在命令行上输入如下命令:sudo modprobe usbserial vendor=0x12d1 product=0x1003该命令用特权用户来加载usbserial模块,并把该模块依赖的模块一并加载进系统, 同时它还设置了usbserial的两个参数: vendor, product, 很显然这两个参数是厂商ID和设备ID, 而作用就是用于匹配设备.首先, 当然是要知道usbserial模块由哪些文件编译而成, 这样才能有目的性的去分析其代码. 而要知道其组成当然是去其目录下看Makefile了, 它位于内核源码目录下的./drivers/usb/serial/下./drivers/usb/serial/Makefile:## Makefile for the USB serial device drivers.## Object file lists.obj-$(CONFIG_USB_SERIAL) +=usbserial.o #编译内核时如何编译该模块usbserial-obj-$(CONFIG_USB_SERIAL_CONSOLE) += console.ousbserial-obj-$(CONFIG_USB_EZUSB) += ezusb.ousbserial-objs := usb-serial.o generic.o bus.o $(usbserial-obj-y) #OK, 就是usbserial模块的组成了.obj-$(CONFIG_USB_SERIAL_AIRPRIME) += airprime.oobj-$(CONFIG_USB_SERIAL_ANYDATA) += anydata.o .......我们重点看的是usb-serial.c, generic.c, bus.c在看源码之前我们先说说该模块的原理及整体结构:很简单跟应用层交互的是一个tty设备, 也就是说该模块把USB 设备映射成一个tty设备(即在/dev/目录下为该USB设备创建一个tty设备文件), 然后用于可以用minicom之类的串口工具来打开这个设备, 并同设备端的设备通信.对于发送过程: tty设备文件在获取了用户要求发送的数据之后传递到下层usbserial模块的核心层,而该核心层就是将数据打包成USB格式的数据并由USB通信发送到设备端去,对于接收过程: usbserial模块会在该设备打开时就启动一个urb 在那等待设备端发数据过来, 收到数据后就push到上层tty设备的缓冲中去, 而tty设备在收到数据后就会给用户,或直接显示在minicom之类的工具上.usb-serial.c 就是usbserial模块的核心, 它主要用来接收设备端发来的数据并传送到上层, 同时也接收来自上层应用的数据,并组装成urb包发送给设备.generic.c 对特定设备单独的操作,相当于是设备自己的驱动程序, 由于很多设备具有通用性, 所以对于没有特殊要求的设备都可以使用这个驱动来作为自己设备的驱动程序. 它有两个参数vendor 和product,上面提过了.bus.c 每个usb驱动和设备都必须要归入某一条总线上, 即都是归属于某条总线的,只有这样系统才能从特定一条总线开始找到每个驱动和设备并为他们匹配. 这个文件就是用来模拟一条总线, 而usbserial的每个驱动和设备都会注册到这条总线上来.好了,是时候分析usbserial模块了.我们知道当把一个模块加载进系统时会调用这个模块里的一个由module_init()声明的一个初始化函数. usbserial当然也不另外, usb-serial.c:module_init(usb_serial_init);module_exit(usb_serial_exit);没错加载时调用的就是: usb_serial_init().usb-serial.c:struct tty_driver *usb_serial_tty_driver;static int __init usb_serial_init(void){int i;int result;//创建一个tty_driver对象, 对应的就是tty设备的驱动.usb_serial_tty_driver =alloc_tty_driver(SERIAL_TTY_MINORS);if (!usb_serial_tty_driver)return -ENOMEM;/* Initialize our global data */for (i = 0; i < SERIAL_TTY_MINORS; ++i) {serial_table[i] = NULL; //该模块共支持SERIAL_TTY_MINORS个该类型设备.}result = bus_register(&usb_serial_bus_type); //注册这条serial bus.if (result) {err("%s - registering bus driver failed", __FUNCTION__);goto exit_bus;}//初始化tty_driver对象usb_serial_tty_driver->owner = THIS_MODULE;usb_serial_tty_driver->driver_name = "usbserial";usb_serial_tty_driver->devfs_name = "usb/tts/";usb_serial_tty_driver->name= "ttyUSB"; //tty设备文件名以这个开头,后加0,1,2,3,....usb_serial_tty_driver->major =SERIAL_TTY_MAJOR; //主设备号usb_serial_tty_driver->minor_start = 0;usb_serial_tty_driver->type =TTY_DRIVER_TYPE_SERIAL; //设备类型usb_serial_tty_driver->subtype =SERIAL_TYPE_NORMAL;usb_serial_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS;usb_serial_tty_driver->init_termios =tty_std_termios;usb_serial_tty_driver->init_termios.c_cflag =B9600 | CS8 | CREAD | HUPCL | CLOCAL;//赋值tty设备的操作集合,即应用层调用open时最终会调到serial_ops->open里面tty_set_operations(usb_serial_tty_driver,&serial_ops);result =tty_register_driver(usb_serial_tty_driver); //注册这个tty 驱动if (result) {err("%s - tty_register_driver failed",__FUNCTION__);goto exit_reg_driver;}/* register the USB driver */result = usb_register(&usb_serial_driver); //注册一个usb驱动if (result < 0) {err("%s - usb_register failed",__FUNCTION__);goto exit_tty;}/* register the generic driver, if we should */result = usb_serial_generic_register(debug); //注册generic驱动程序if (result < 0) {err("%s - registering generic driver failed", __FUNCTION__);goto exit_generic;}info(DRIVER_DESC);return result;//失败时候的一些反向操作exit_generic:usb_deregister(&usb_serial_driver);exit_tty:tty_unregister_driver(usb_serial_tty_driver);exit_reg_driver:bus_unregister(&usb_serial_bus_type);exit_bus:err ("%s - returning with error %d", __FUNCTION__, result);put_tty_driver(usb_serial_tty_driver);return result;}该函数先创建并初始化好了一个tty_driver的对象, 并把该对象注册进系统, 该对象就是tty设备的驱动程序, 后面我们会看到他是如何于具体tty设备绑定在一起的.usb_serial.c:static struct tty_operations serial_ops = {.open = serial_open,.close = serial_close,.write = serial_write,.write_room = serial_write_room,.ioctl = serial_ioctl,.set_termios = serial_set_termios,.throttle = serial_throttle,.unthrottle = serial_unthrottle,.break_ctl = serial_break,.chars_in_buffer = serial_chars_in_buffer,.read_proc = serial_read_proc,.tiocmget = serial_tiocmget,.tiocmset = serial_tiocmset,};这个就是tty设备文件对应的操作方法集合, 例如, 应用层调用open函数来打开该设备文件时将最终会走到serial_open里面.usb_serial_init() 还注册了一条总线:usb_serial_bus_type, 这样当有设备连上系统时, 该总线上的驱动就有机会去匹配这个设备. 后面我们会看到generic的驱动就是注册在该总线上的.bus.c:struct bus_type usb_serial_bus_type = {.name = "usb-serial",.match = usb_serial_device_match, //在设备匹配时会调用.probe = usb_serial_device_probe,.remove = usb_serial_device_remove,};关于设备匹配过程(probe)可以参考我的另一篇文章.usb_serial_init() 在最后usb_serial_generic_register(debug)来注册generic驱动.generic.c:int usb_serial_generic_register (int _debug){int retval = 0;debug = _debug;#ifdef CONFIG_USB_SERIAL_GENERICgeneric_device_ids[0].idVendor = vendor; //保存厂商IDgeneric_device_ids[0].idProduct = product; //保存产品IDgeneric_device_ids[0].match_flags =USB_DEVICE_ID_MATCH_VENDOR |USB_DEVICE_ID_MATCH_PRODUCT; //匹配类型/* register our generic driver with ourselves */ retval = usb_serial_register(&usb_serial_generic_device); //注册驱动if (retval)goto exit;retval = usb_register(&generic_driver); //注册驱动if (retval)usb_serial_deregister(&usb_serial_generic_ device);exit:#endifreturn retval;}该函数首先保存了命令通过命令行设备的vendor,product 用于以后设备匹配, 由此我们知道该驱动可以动态支持设备匹配. 接着该函数注册了usb_serial_generic_device驱动.generic.c:struct usb_serial_driver usb_serial_generic_device = { .driver = {.owner = THIS_MODULE,.name = "generic",},.id_table = generic_device_ids, //匹配用的设备列表, 支持动态匹配.num_interrupt_in = NUM_DONT_CARE,.num_bulk_in = NUM_DONT_CARE,.num_bulk_out = NUM_DONT_CARE,.num_ports = 1,.shutdown = usb_serial_generic_shutdown, };Usb-serial.c:int usb_serial_register(struct usb_serial_driver *driver) {int retval;fixup_generic(driver); //为driver赋上默认的操作函数if (!driver->description)driver->description = driver->;/* Add this device to our list of devices */list_add(&driver->driver_list,&usb_serial_driver_list); //加入驱动列表retval = usb_serial_bus_register(driver); //把该驱动注册进usb serial bus下if (retval) {err("problem %d when registering driver %s", retval, driver->description);list_del(&driver->driver_list);}elseinfo("USB Serial support registered for %s", driver->description);return retval;}其中的fixup_generic()函数仅仅是为driver赋上默认的操作函数.Usb-serial.c:#define set_to_generic_if_null(type,function) \do{ \ if (!type->function){ \type->function =usb_serial_generic_##function; \dbg("Had to override the "#function \" usb serial operation with the generic one.");\} \} while (0)static void fixup_generic(struct usb_serial_driver*device){set_to_generic_if_null(device, open);set_to_generic_if_null(device, write);set_to_generic_if_null(device, close);set_to_generic_if_null(device, write_room);set_to_generic_if_null(device, chars_in_buffer);set_to_generic_if_null(device,read_bulk_callback);set_to_generic_if_null(device,write_bulk_callback);set_to_generic_if_null(device, shutdown);}即通过上面的usb_serial_register()函数后usb_serial_generic_device的函数集为:usb_serial_generic_device.open = usb_serial_generic_open;usb_serial_generic_device.close =usb_serial_generic_close......驱动usb_serial_generic_device将是以后操作tty设备的主要函数.我们会在后面分析.bus.c:int usb_serial_bus_register(struct usb_serial_driver *driver){int retval;driver->driver.bus = &usb_serial_bus_type; //注册到该bus下retval = driver_register(&driver->driver);return retval;}最后usb_serial_generic_register()函数注册了一个generic_driver驱动.generic.c:static struct usb_driver generic_driver = {.name = "usbserial_generic",.probe = generic_probe, //匹配函数.disconnect = usb_serial_disconnect,.id_table = generic_serial_ids, //匹配用的设备列表.no_dynamic_id = 1, //不支持动态匹配};整个初始化过程, 乍一看一下子注册了几个驱动程序, 几个驱动列表, 有的支持动态匹配有的不支持, 感觉很复杂, 其实注册generic_driver驱动主要是为了注册一个generic_probe函数, 而该函数将会在设备连上系统后被调用以来匹配设备. 除此之外该驱动没什么用, 而在这个初始化函数中把vendor,product都保存在了generic_device_ids里, 因此可以肯定以后的匹配将用这个设备列表,而不是generic_serial_ids, 说的更直白些generic_serial_ids其实根本也没什么用. 真正有用的是usb_serial_generic_device驱动,generic.c:static int generic_probe(struct usb_interface *interface, const struct usb_device_id *id){const struct usb_device_id *id_pattern;id_pattern =usb_match_id(interface, generic_device_ids); //设备匹配 if (id_pattern != NULL)return usb_serial_probe(interface, id); //进一步匹配return -ENODEV;}如果接入系统的设备的vendor和product与我们驱动支持的设备列表匹配则调用usb_serial_probe来进一步匹配.usb_serial_probe函数比较长, 我们一段段的来看usb-serial.c:int usb_serial_probe(struct usb_interface *interface, const struct usb_device_id *id){struct usb_device *dev = interface_to_usbdev (interface);struct usb_serial *serial = NULL;struct usb_serial_port *port;struct usb_host_interface *iface_desc;struct usb_endpoint_descriptor *endpoint;struct usb_endpoint_descriptor*interrupt_in_endpoint[MAX_NUM_PORTS];struct usb_endpoint_descriptor*interrupt_out_endpoint[MAX_NUM_PORTS];struct usb_endpoint_descriptor*bulk_in_endpoint[MAX_NUM_PORTS];struct usb_endpoint_descriptor*bulk_out_endpoint[MAX_NUM_PORTS];struct usb_serial_driver *type = NULL;int retval;int minor;int buffer_size;int i;int num_interrupt_in = 0;int num_interrupt_out = 0;int num_bulk_in = 0;int num_bulk_out = 0;int num_ports = 0;int max_endpoints;type = search_serial_device(interface); //获取该设备匹配的驱动if (!type) {dbg("none matched");return -ENODEV;}......}首先是找到合适的驱动程序.usb-serial.c:static struct usb_serial_driver*search_serial_device(struct usb_interface *iface) {struct list_head *p;const struct usb_device_id *id;struct usb_serial_driver *t;/* Check if the usb id matches a known device */ list_for_each(p, &usb_serial_driver_list) {t = list_entry(p, struct usb_serial_driver, driver_list);id = usb_match_id(iface, t->id_table); //看设备列表是否匹配if (id != NULL) {dbg("descriptor matches");return t; //返回匹配的驱动}}return NULL;}实际上这边的匹配和generic_probe里的匹配重复了, 因为他们的匹配的设备列表是同一个, 这边主要是为了得到匹配的驱动程序, 根据上面的代码分析我们可以知道这里匹配的驱动是usb_serial_generic_device.接着看usb_serial_probe()usb-serial.c:....serial = create_serial (dev, interface, type); //为该设备创建一个usb_serial对象if (!serial) {dev_err(&interface->dev, "%s - out of memory\n", __FUNCTION__);return -ENOMEM;}/* if this device type has a probe function, call it */if (type->probe) { //从上面分析的代码可知这里的probe函数没有赋值const struct usb_device_id *id;if (!try_module_get(type->driver.owner)) { dev_err(&interface->dev, "module get failed, exiting\n");kfree (serial);return -EIO;}id = usb_match_id(interface,type->id_table);retval = type->probe(serial, id);module_put(type->driver.owner);if (retval) {dbg ("sub driver rejected device");kfree (serial);return retval;}}....这段代码可知, 主要是创建一个usb_serial的对象, 用于保存该设备的详细信息, 一般的驱动程序都会为自己匹配的设备创建一个描用于描述该设备的对象. 在以后的所有操作中如读写等都会直接从这个对象里获取相应的信息.usb-serial.c:static struct usb_serial * create_serial (struct usb_device *dev,structusb_interface *interface,structusb_serial_driver *driver){struct usb_serial *serial;serial = kmalloc (sizeof (*serial),GFP_KERNEL); //闯将该对象if (!serial) {dev_err(&dev->dev, "%s - out of memory\n", __FUNCTION__);return NULL;}//初始化该对象memset (serial, 0, sizeof(*serial));serial->dev = usb_get_dev(dev); //增加dev的引用计数serial->type = driver;serial->interface = interface;kref_init(&serial->kref);return serial;}这个函数就是用来创建usb_serial对象的,并把相关信息保存在里面.继续看usb_serial_probe()usb-serial.c:..../* descriptor matches, let's find the endpoints needed *//* check out the endpoints *///查找该设备使用的endpoint的描述符, 并检查是否正确 iface_desc = interface->cur_altsetting; //接口描述符for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {endpoint =&iface_desc->endpoint[i].desc; //端点描述符if ((endpoint->bEndpointAddress & 0x80) && ((endpoint->bmAttributes & 3) == 0x02)) {/* we found a bulk in endpoint*/ //bulk in 的端点dbg("found bulk in on endpoint %d", i);bulk_in_endpoint[num_bulk_in] = endpoint;++num_bulk_in;}if (((endpoint->bEndpointAddress & 0x80) == 0x00) &&((endpoint->bmAttributes & 3) == 0x02)) {/* we found a bulk out endpoint*/ //bulk out的端点dbg("found bulk out on endpoint %d", i);bulk_out_endpoint[num_bulk_out] = endpoint;++num_bulk_out;}if ((endpoint->bEndpointAddress & 0x80) && ((endpoint->bmAttributes & 3) == 0x03)) {/* we found a interrupt in endpoint */ //中断 in 端点dbg("found interrupt in onendpoint %d", i);interrupt_in_endpoint[num_interrupt _in] = endpoint;++num_interrupt_in;}if (((endpoint->bEndpointAddress & 0x80) == 0x00) &&((endpoint->bmAttributes & 3) == 0x03)) {/* we found an interrupt out endpoint */ //中断 out 端点dbg("found interrupt out on endpoint %d", i);interrupt_out_endpoint[num_interrup t_out] = endpoint;++num_interrupt_out;}}.....该段代码主要是获取该设备使用的各个类型及方向的端点描述府, 并保存起来, 关于端点的类型与方向可以参考USB的规范.继续看usb_serial_probe()usb-serial.c:....#if defined(CONFIG_USB_SERIAL_PL2303) ||defined(CONFIG_USB_SERIAL_PL2303_MODULE)/* BEGIN HORRIBLE HACK FOR PL2303 *//* this is needed due to the looney way its endpoints are set up */if (((le16_to_cpu(dev->descriptor.idVendor) ==PL2303_VENDOR_ID) &&(le16_to_cpu(dev->descriptor.idProduct) == PL2303_PRODUCT_ID)) ||((le16_to_cpu(dev->descriptor.idVendor) == ATEN_VENDOR_ID) &&(le16_to_cpu(dev->descriptor.idProduct) == ATEN_PRODUCT_ID))) {if (interface !=dev->actconfig->interface[0]) {/* check out the endpoints of the other interface*/iface_desc =dev->actconfig->interface[0]->cur_altsetting;for (i = 0; i <iface_desc->desc.bNumEndpoints; ++i) {endpoint =&iface_desc->endpoint[i].desc;if((endpoint->bEndpointAddress & 0x80) &&((endpoint->bmAttributes & 3) == 0x03)) {/* we found a interrupt in endpoint */dbg("found interrupt in for Prolific device on separate interface");interrupt_in_endpoint [num_interrupt_in] = endpoint;++num_interrupt_in;}}}/* Now make sure the PL-2303 is configured correctly.* If not, give up now and hope this hack will work* properly during a later invocation of usb_serial_probe*/if (num_bulk_in == 0 || num_bulk_out == 0) { dev_info(&interface->dev, "PL-2303 hack: descriptors matched but endpoints did not\n");kfree (serial);return -ENODEV;}}/* END HORRIBLE HACK FOR PL2303 */#endif上面这段代码主要是用于特定类型设备的(PL2303), 这里我们不用管他.接着看usb_serial_probe()usb-serial.c:..../* found all that we need */dev_info(&interface->dev, "%s converterdetected\n", type->description);#ifdef CONFIG_USB_SERIAL_GENERIC //这个宏定义了, 因为我们使用的是通用USB驱动.if (type == &usb_serial_generic_device) { //这个if为TRUE(上面分析过了)num_ports = num_bulk_out;if (num_ports == 0) { //bulk out端点必须要有dev_err(&interface->dev, "Generic device with no bulk out, not allowed.\n");kfree (serial);return -EIO;}}#endifif (!num_ports) { //由于走到了上面那个if段,因此这里的num_ports肯定不为0/* if this device type has a calc_num_ports function, call it */if (type->calc_num_ports) {if(!try_module_get(type->driver.owner)) {dev_err(&interface->dev, "module get failed, exiting\n");kfree (serial);return -EIO;}num_ports = type->calc_num_ports (serial);module_put(type->driver.owner);}if (!num_ports)num_ports = type->num_ports;}//获取一个空闲的serial_table项if (get_free_serial (serial, num_ports, &minor) == NULL) {dev_err(&interface->dev, "No more free serial devices\n");kfree (serial);return -ENOMEM;}usbserial模块总共支持SERIAL_TTY_MINORS个设备, 它为每个设备都分配了一个serial_table项, 用于保存usb_serial对象, 方便以后直接通过minor号获取usb_serial对象.usb-serial.c:static struct usb_serial *get_free_serial (structusb_serial *serial, int num_ports, unsigned int *minor) {unsigned int i, j;int good_spot;dbg("%s %d", __FUNCTION__, num_ports);*minor = 0;for (i = 0; i < SERIAL_TTY_MINORS; ++i) {if (serial_table[i]) //查找一个空闲的serial_table项, serial_table是个usb_serial的指针数组.continue;good_spot = 1;//从上面代码可知, 对于generic的驱动, num_ports 就等于num_bulk_out, 而一般的设备仅有//一个bulkout 的端点, 因此这个for循环不会执行.for (j = 1; j <= num_ports-1; ++j)if ((i+j >= SERIAL_TTY_MINORS) || (serial_table[i+j])) {good_spot = 0;i += j;break;}if (good_spot == 0)continue;*minor = i; //获取minor号dbg("%s - minor base = %d", __FUNCTION__, *minor);for (i = *minor; (i < (*minor + num_ports)) && (i < SERIAL_TTY_MINORS); ++i)serial_table[i] = serial; //获取空闲的serial_table项, 并把我们的usb_serial对象地址保//存在其中.return serial;}return NULL;}通过这个函数我们找到了一个空闲的serial_table项, 并把描述我们设备的usb_serial对象保存在其中, 在以后对设备的使用中, 我们可以轻易的通过minor号来找到这个usb_serial.接着看usb_serial_probe()usb-serial.c:....//保存设备信息到usb_serial对象中,serial->minor = minor;serial->num_ports = num_ports; //这里的port数量不是endpoint的数量,serial->num_bulk_in = num_bulk_in;serial->num_bulk_out = num_bulk_out;serial->num_interrupt_in = num_interrupt_in;serial->num_interrupt_out = num_interrupt_out;/* create our ports, we need as many as the max endpoints *//* we don't use num_ports here cauz some devices have more endpoint pairs than ports *///对于generic的驱动来说一般都只有一个bulk in,一个bulk out,一个interrupt in,一个interrupt outmax_endpoints = max(num_bulk_in, num_bulk_out);max_endpoints = max(max_endpoints,num_interrupt_in);max_endpoints = max(max_endpoints,num_interrupt_out);max_endpoints = max(max_endpoints,(int)serial->num_ports);//到这一步,对于generic来说大多数情况下max_endpoints 还是为1serial->num_port_pointers = max_endpoints;dbg("%s - setting up %d port structures for this device", __FUNCTION__, max_endpoints);for (i = 0; i < max_endpoints; ++i) {port = kmalloc(sizeof(structusb_serial_port), GFP_KERNEL); //分配一个port对象 if (!port)goto probe_error;//初始化port对象memset(port, 0x00, sizeof(structusb_serial_port));port->number = i + serial->minor;port->serial = serial; //保存usb_serial 对象, 便于以后通过port对象访问到usb_serial对象 spin_lock_init(&port->lock);sema_init(&port->sem, 1);INIT_WORK(&port->work,usb_serial_port_softint, port);serial->port[i] = port;}//由上面的对port的初始化可知,每个port都有一套自己的工作机制, port间互不干扰/* set up the endpoint information */for (i = 0; i < num_bulk_in; ++i) {//初始化bulk in 端点, 并把它保存到相应的port里 endpoint = bulk_in_endpoint[i];port = serial->port[i];port->read_urb = usb_alloc_urb (0,GFP_KERNEL); //分配urbif (!port->read_urb) {dev_err(&interface->dev, "No free urbs available\n");goto probe_error;}buffer_size =le16_to_cpu(endpoint->wMaxPacketSize);port->bulk_in_size = buffer_size;port->bulk_in_endpointAddress =endpoint->bEndpointAddress; //保存端点地址port->bulk_in_buffer = kmalloc (buffer_size, GFP_KERNEL); //分配传输缓存if (!port->bulk_in_buffer) {dev_err(&interface->dev, "Couldn't allocate bulk_in_buffer\n");goto probe_error;}//设置好该urb.usb_fill_bulk_urb (port->read_urb, dev, usb_rcvbulkpipe (dev,endpoint->bE ndpointAddress),port->bulk_in_buffer, buffer_size,serial->type->read_bulk_c allback,port);}for (i = 0; i < num_bulk_out; ++i) {//初始化bulk out 端点, 并把它保存到相应的port里 endpoint = bulk_out_endpoint[i];port = serial->port[i];port->write_urb = usb_alloc_urb(0,GFP_KERNEL);if (!port->write_urb) {dev_err(&interface->dev, "No free urbs available\n");goto probe_error;}buffer_size =le16_to_cpu(endpoint->wMaxPacketSize);port->bulk_out_size = buffer_size;port->bulk_out_endpointAddress =endpoint->bEndpointAddress;port->bulk_out_buffer = kmalloc(buffer_size, GFP_KERNEL);if (!port->bulk_out_buffer) {dev_err(&interface->dev, "Couldn't allocate bulk_out_buffer\n");。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
USB设备驱动开发
主讲:宋宝华
华清远见
今天的内容
Ø B及驱动框架简介
Ø
1.1 USB协议
Ø
1.2 USB驱动的体系结构
Ø 2.主机端驱动
Ø
2.1 主机控制器驱动
Ø
2.2 设备驱动
Ø 3.设备端驱动
Ø
3.1 设备控制器驱动
Ø
3.2 gadget驱动
... };
static struct usb_config_descriptor source_sink_config = {
... };
static struct usb_config_descriptor source_sink_config = {
... };
static struct usb_endpoint_descriptor fs_source_desc = {
}; MODULE_DEVICE_TABLE (usb, skel_table);
ØUSB字符设备
int usb_register_dev(struct usb_interface *intf, struct usb_class_driver *class_driver);
void usb_deregister_dev(struct usb_interface *intf, struct usb_class_driver *class_driver);
MC SYSTEM, 2008-12-12
华清远见
USB设备驱动体系结构
华清远见
USB设备驱动数据结构和API
Østruct usb_driver
static struct usb_driver skel_driver =
{
.name =
"skeleton",
.probe = skel_probe,
};
int usb_gadget_config_buf(
const struct usb_config_descriptor *config,
void
*buf,
unsigned
usb_alloc_urb()
中断:usb_fill_int_urb() 批量:usb_fill_bulk_urb() 控制:usb_fill_control_urb() 等时:手工初始化iso urb
usb_submit_urb() usb核心、usb主机控制器处理
usb_kill_urb() usb_unlink_urb()
.disable = xxx_ep_disable,
.alloc_request = xxx_alloc_request,
.free_request = xxx_free_request,
.alloc_buffer = xxx_alloc_buffer,
.free_buffer = xxx_free_buffer,
= zero_suspend, = zero_resume,
.driver = {
.name
= (char *) shortname,
.owner = THIS_MODULE,
},
};
华清远见
USB Gadget驱动--组成(2)
Ø设备、配置、接口、端点描述符
static struct usb_device_descriptor device_desc = {
... }; static struct usb_endpoint_descriptor fs_sink_desc = {
..., }; ....
static const struct usb_descriptor_header *fs_source_sink_function [] = { (struct usb_descriptor_header *) &otg_descriptor, (struct usb_descriptor_header *) &source_sink_intf, (struct usb_descriptor_header *) &fs_sink_desc, (struct usb_descriptor_header *) &fs_source_desc, NULL,
...
};
Østruct usb_gadget_ops
static const struct usb_gadget_ops xxx_udc_ops =
{
.get_frame = xxx_udc_get_frame,
.wakeup
= xxx_udc_wakeup,
};
华清远见
USB Gadget驱动--组成(1)
bInterfaceNumber 0
bAlternateSetting 0
bNumEndpoints
2
bInterfaceClass 8 Mass Storage
bInterfaceSubClass 6 SCSI
bInterfaceProtocol 80 Bulk (Zip)
iInterface
0
Endpoint Descriptor:
幻灯片 8 MS1
Starts processing a USB transfer request specified by a USB Request Block (URB). mem_flags indicates the type of memory allocation to use while processing this URB.
.get_frame_number =
xxx_hcd_get_frame_number,
.hub_status_data = .hub_control =
xxx_hcd_hub_status_data, xxx_hcd_hub_control,
};
API:
struct usb_hcd *usb_create_hcd (const struct hc_driver *driver, struct device *dev, char *bus_name);
Østruct usb_gadget_driver
static struct usb_gadget_driver zero_driver = {
#ifdef CONFIG_USB_GADGET_DUALSPEED
.speed = USB_SPEED_HIGH,
#else
.speed = USB_SPEED_FULL,
UDC驱动(omap/pxa2xx...)
USB控制器OHCI/EHCI/UHCI
USB总线
USB控制器OHCI/EHCI/UHCI
华清远见
USB主机控制器驱动
数据结构:
Østruct usb_hcd
Ø struct hc_driver
static const struct hc_driver xxx_hc_driver = {
wTotalLength
32
bNumInterfaces 1
bConfigurationValue 1
iConfiguration 0
bmAttributes 0x80
MaxPower
200mA
直观的例子—U盘
Interface Descriptor:
bLength
9
bDescriptorType 4
xxx_hcd_start,
.stop = .urb_enqueue =
xxx_hcd_stop,
xxx_hcd_urb_enqueue, MS1
.urb_dequeue =
xxx_hcd_urb_dequeue,
.endpoint_disable =
xxx_hcd_endpoint_disable,
bLength
7
bDescriptorType 5
bEndpointAddress 0x81 EP 1 IN
bmAttributes
2
Transfer Type
Bulk
Synch Type
none
wMaxPacketSize 512
bInterval
0
Endpoint Descriptor:
bLength
urb->com ple t e()
华清远见
USB设备驱动实例分析
ØUSB骨架程序 ØUSB串口驱动 ØUSB键盘驱动
华清远见
USB设备控制器驱动
数据结构
Østruct usb_ep_ops
static struct usb_ep_ops xxx_ep_ops = {
.enable = xxx_ep_enable,
Ø 4. USB OTG
华清远见
Ø拓扑结构
主机 root hub
USB协议(1)
设备
设备 hub
设备
hub
设备
设备
设备
设备
Ø设备、配置、接口和端点
设备描述符
配置0
接口0 端点1
接口1
端点2
端点3
配置1
接口0
接口1 端点…
接口2
华清远见
Ø传输方式
Ø控制(Control)传输方式 Ø同步(Synchronization)传输方式 Ø中断(Interrupt)传输方式 Ø批量(Bulk)传输方式
.description =
xxx_hcd_name,
.product_desc =
"xxx OTG Controller",