八、USB驱动分析
学习目标:分析USB驱动源码结构。
一、Windows下USB驱动理论问题
1. 当usb设备接入PC时,右下角弹出"发现AAA",并弹出对话框,提示安装驱动程序。没有驱动程序,Windows是怎样知道是AAA设备?
--> Windows有USB的总线驱动程序,接入USB设备后,"总线驱动程序"就会知道该设备是"AAA",提示安装的是”AAA的设备驱动程序"。这里USB总线驱动程序负责:识别USB设备, 给USB设备找到对应的驱动程序。
USB总线驱动程序的作用:(1) 识别USB设备 (2)查找并安装对应的设备驱动程序 (3)提供USB读写函数。
2. Windows是怎样识别出该USB设备的种类/名称?
--> PC和USB设备都得遵守一些规范。USB总线驱动程序会发出某些命令想获取设备信息(描述符),USB设备必须返回"描述符"给PC。
3. PC机是怎样分辨USB设备?
--> 每一个USB设备接入PC时,USB总线驱动程序都会给它分配一个编号(地址),PC机想访问某个USB设备时,发出的命令都含有对应的编号(地址);新接入的USB设备的默认编号是0,在未分配新编号前,PC使用0编号和它通信。
4. 为什么一接入USB设备,PC机就能发现它?
--> 由于硬件接口连接问题提示的。USB接口只有4条线: 5V,GND,D-,D+。USB设备接入PC,会把PC USB口的D-或D+拉高,从而通知有新设备接入。
注意:
(1)USB是主从结构的,所有的传输都是由USB主机方发起的。例如,USB键盘按下立刻产生数据,但是它没有能力通知PC机来读数据,只能等待PC机来读。
(2)USB在数据传输中,以端点为对象的,其有传输类型,传输方向。每个端点只有一个方向的功能(除了端点0控制传输,既能输出也能输入外),例如把数据从端点1读出,向端点2写入数据。


usb 1-1: configuration #1 chosen from 1 choice
scsi0 : SCSI emulation for USB Mass Storage devices
scsi 0:0:0:0: Direct-Access HTC Android Phone 0100 PQ: 0 ANSI: 2
sd 0:0:0:0: [sda] Attached SCSI removable disk
拔掉后会打印:
usb 1-1: USB disconnect, address 2
drivers/usb/core/hub.c:2186: "%s %s speed %sUSB device using %s and address %d\n",
- dev_info (&udev->dev,
- "%s %s speed %sUSB device using %s and address %d\n",
- (udev->config) ? "reset" : "new", speed, type,
- udev->bus->controller->driver->name, udev->devnum);
2) 接下来在source insight中查看源码调用:
hub_thread-->
hub_events()-->
hub_port_connect_change()-->
调用 hub_port_init()
在hub_thread()函数中:可知hub_events() 的调用,需要等待中断khubd_wait被唤醒;
- static int hub_thread(void *__unused)
- {
- do {
- hub_events();
- wait_event_interruptible(khubd_wait,
- !list_empty(&hub_event_list) ||
- kthread_should_stop());
- try_to_freeze();
- } while (!kthread_should_stop() || !list_empty(&hub_event_list));
- pr_debug("%s: khubd exiting\n", usbcore_name);
- return ;
- }
3) 搜索“中断khubd_wait”,可以得到它是在drivers/usb/core/Hub.c --> kick_khubd()函数中:
- static void kick_khubd(struct usb_hub *hub)
- {
- unsigned long flags;
- /* Suppress autosuspend until khubd runs */
- to_usb_interface(hub->intfdev)->pm_usage_cnt = ;
- spin_lock_irqsave(&hub_event_lock, flags);
- if (list_empty(&hub->event_list)) {
- list_add_tail(&hub->event_list, &hub_event_list);
- wake_up(&khubd_wait);
- }
- spin_unlock_irqrestore(&hub_event_lock, flags);
- }
4) 继续搜索kick_khubd(),可知该函数被hub_irq()调用,因此当USB设备插入后,D+ 或者D-会被拉高,从而使USB控制器产生usb_irq中断。
5) 在hub_port_connect_change()实现了设备注册、端口连接、创建USB设备等功能。
- static void hub_port_connect_change(struct usb_hub *hub, int port1, u16 portstatus, u16 portchange)
- {
- udev = usb_alloc_dev(hdev, hdev->bus, port1); //注册usb_device,放在usb总线上
- device_initialize(&dev->dev); // 初始化usb_device
dev->dev.bus = &usb_bus_type; // 设置usb_device的成员device->bus等于usb_bus总线
dev->dev.type = &usb_device_type; // 设置usb_device的成员device->type等于usb_device_type
return dev; // 返回一个usb_device结构体
usb_set_device_state(udev, USB_STATE_POWERED); //设置注册的USB设备的状态标识- choose_address(udev); /*分配一个地址编号 */
- /* usb 1-1: new full speed USB device using s3c2410-ohci and address 3 **初始化端口,与USB设备建立连接*/
- hub_port_init(hub, udev, port1, i)
retval = hub_set_address(udev); //(1)设置地址,告诉USB设备新的地址编号
retval = usb_get_device_descriptor(udev, 8); //(2)获得USB设备描述符前8个字节 status = usb_new_device(udev); //创建USB设备,与USB驱动连接
err = usb_get_configuration(udev); // 把所有的描述符都读出来,并解析- -->usb_parse_configuration
- -->device_add // 把device放入usb_bus_type的dev链表,
- // 从usb_bus_type的driver链表里取出usb_driver,
- // 把usb_interface和usb_driver的id_table比较
- // 如果能匹配,调用usb_driver的probe
- }
小结
1)USB驱动程序源码的执行流程为:
2)usb_bus_type是一个全局变量, 和我们之前学的platform平台总线原理、结构体、设备与驱动节点、函数调用等相似, 属于USB总线, 是Linux中bus的一种. 每当创建一个USB设备,或者USB设备驱动时,USB总线都会调用match成员来匹配一次,使USB设备和USB设备驱动联系起来.
3)在分配一个地址编号时,USB接口最大能接127个设备,连续插拔两次USB键盘,也可以看出,如下图所示:
4)usb_get_device_descriptor()函数主要是获取目标设备描述符前8个字节,为什么先只开始读取8个字节?是因为开始时还不知道对方所支持的信包容量,这8个字节是每个设备都有的,后面再根据设备的数据,通过usb_get_device_descriptor()重读一次目标设备的设备描述结构.
5)那么USB驱动的id_table又该如何定义?
例如:参考/drivers/hid/usbhid/usbmouse.c (鼠标驱动)
- static struct usb_device_id usb_mouse_id_table [] = {
- { USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
- USB_INTERFACE_PROTOCOL_MOUSE) },
//(1)USB_INTERFACE_CLASS_HID 为设置匹配USB的接口类型为HID类, 因为USB_INTERFACE_CLASS_HID=0x03,HID类是属于人机交互的设备,比如:USB键盘,USB鼠标,USB触摸板,USB游戏操作杆都要填入0X03
//(2)USB_INTERFACE_SUBCLASS_BOOT 为设置匹配USB的接口子类型为启动设备
//(3)USB_INTERFACE_PROTOCOL_MOUSE 设置匹配USB的接口协议为USB鼠标的协议,等于2;当.bInterfaceProtocol=1也就是USB_INTERFACE_PROTOCOL_KEYBOARD时,表示USB键盘的协议
{ } /* Terminating entry */- };
- MODULE_DEVICE_TABLE (usb, usb_mouse_id_table);
- static struct usb_driver usb_mouse_driver = {
- .name = "usbmouse",
- .probe = usb_mouse_probe,
- .disconnect = usb_mouse_disconnect,
- .id_table = usb_mouse_id_table,
- };
源码分析完后,就可以自己写USB设备驱动程序了。
参考:https://www.cnblogs.com/lifexy/p/7631900.html
八、USB驱动分析的更多相关文章
- USB驱动分析
INIT函数: 这是内核模块的初始化函数,其所作的工作只有注册定义好的USB驱动结构体. USB驱动结构体如下: Usb_driver中的probe函数是驱动和设备匹配成功后调用. Usb_drive ...
- usb键鼠标驱动分析
一.鼠标 linux下的usb鼠标驱动在/drivers/hid/usbhid/usbmouse.c中实现 1.加载初始化过程 1.1模块入口 module_init(usb_mouse_init); ...
- Linux 串口、usb转串口驱动分析(2-2) 【转】
转自:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=26807463&id=4186852 Linux 串口.usb转 ...
- Linux 串口、usb转串口驱动分析(2-1) 【转】
转自:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=26807463&id=4186851 Linux 串口.usb转 ...
- linux下usb转串口驱动分析【转】
转自:http://blog.csdn.net/txxm520/article/details/8934706 首先说一下linux的风格,个人理解 1. linux大小结构体其实是面向对象的方法,( ...
- linux设备驱动之USB主机控制器驱动分析 【转】
转自:http://blog.chinaunix.net/uid-20543183-id-1930831.html ---------------------------------------- ...
- Linux USB驱动框架分析 【转】
转自:http://blog.chinaunix.net/uid-11848011-id-96188.html 初次接触与OS相关的设备驱动编写,感觉还挺有意思的,为了不至于忘掉看过的东西,笔记跟总结 ...
- linux驱动基础系列--Linux 串口、usb转串口驱动分析
前言 主要是想对Linux 串口.usb转串口驱动框架有一个整体的把控,因此会忽略某些细节,同时里面涉及到的一些驱动基础,比如字符设备驱动.平台驱动等也不进行详细说明原理.如果有任何错误地方,请指出, ...
- linux驱动学习(八) i2c驱动架构(史上最全) davinc dm368 i2c驱动分析【转】
转自:http://blog.csdn.net/ghostyu/article/details/8094049 版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[-] 预备知识 lin ...
随机推荐
- JSTL核心库
1 out和set(重点) out <c:out value=”aaa”/> 输出aaa字符串常量 <c:out value=”${aaa}”/> 与${aaa}相同 < ...
- Java—集合框架 Collections.sort()、Comparable接口和Comparator接口
Collentions工具类--java.util.Collections Collentions是Java集合框架中,用来操作集合对象的工具类,也是Java集合框架的成员,与List.Map和Set ...
- Java—集合框架Set
Set接口及其实现类——HashSet Set是元素无序并且不可以重复的集合,被称作集. HashSet—哈希集,是Set的一个重要实现类. Set的使用 HashSet没有像List一样的set ...
- C# 轻松读取、改变文件的创建、修改、访问时间 z
// 读取文件的创建.修改.访问时间FileInfo fi = new FileInfo("C://test.txt");Console.WriteLine(fi.Creation ...
- 使用SAPGUI画图
国内80后上的编程课应该都学过Logo这门编程语言: Logo语言是一门专门设计用来进行编程教学的语言,于1967年由Wally Feurzeig, Seymour Papert和Cynthia So ...
- Django运行SQL语句
1.Manager.raw(raw_query, params=None, translations=None) >>> for p in Person.objects.raw('S ...
- swift 协议(结合扩展)的特点
协议的传统实现: 定义接口+实现协议 由抽象到具体: 协议的逆向实现(使用扩展): 由已存在的类型抽离部分功能作为协议,并让原体符合协议: 由具体到抽象: 向上抽离: 向上生成: 协议的缺省 ...
- 【LOJ6041】「雅礼集训 2017 Day7」事情的相似度(用LCT维护SAM的parent树)
点此看题面 大致题意: 给你一个\(01\)串,每次询问前缀编号在一段区间内的两个前缀的最长公共后缀的长度. 离线存储询问 考虑将询问离线,按右端点大小用邻接表存下来(直接排序当然也可以啦). 这样的 ...
- ACM-ICPC(10/21)
写一发后缀数组套路题,看起来简单,写起来要人命哦~~~ 总共13题. 分两天debug吧,有点累了~~~ suffix(后缀数组的应用) sa[i] :排名第 i 的后缀在哪(i 从 1 开始) ra ...
- Chrome,本地页面和插件
今天测试一款Chrome插件,这款插件提供了一些本地页面做测试用,在解决一些技术问题之后,在插件的官网上可以测试成功了,但是在本地页面上测试时Chrome始终会拦截插件,即使在右上角的地址栏中允许该本 ...