linux usb总线驱动(一)
title: linux usb总线驱动
tags: linux
date: 2018/12/11/ 17:14:30
toc: true
linux usb总线驱动框架
USB 介绍
- 当插入一个未知的usb设备,电脑也会有相应的提示?
- 插入有反应,是因为电脑的usb作为主机设备,有一个下拉电阻,从机上拉电压通知
- 识别到一些信息,是因为有着标准的 usb 总线协议,有一些简单的标准的交互方式获取信息。
- usb 设备的基本构成
- 控制usb设备,其实也就是控制usb的寄存器来通过usb总线协议来与设备读写来实现具体的功能。
- usb 总线协议一般内核都会自带,我们只需要写总线协议下面的逻辑控制就好了
- usb也是主从结构,读写只能由主机发起
- 新接入的USB设备的默认地址(编号)是0,在未分配新编号前,PC主机使用0地址和它通信
- 所谓热拔插,其实是因为在主机上的D+和D-都接了15k下拉电阻,平时为低电平。从机有上拉电阻
- 全速设备(12M/s)和高速设备(480M/s):D+ 上拉1.5k
- 低俗设备:D-上拉1.5k
传输类型
| 类型 | 可靠性 | 时间性 | 举例 |
|---|---|---|---|
| 控制传输 | 可靠 | 有保证 | usb设备识别 |
| 批量传输 | 可靠 | 没有保证 | U盘 |
| 中断传输 | 可靠 | 实时 | 鼠标 |
| 实时同步传输 | 不可靠 | 实时 | 摄像头 |
USB中传输的对象为端点,端点0是每个设备都有的,作为控制传输,可读可写。其他端点只有一个方向。
控制器接口
USB规范都是从寄存器级别规定好的,不过各个厂商可能有自己的几个专用的寄存器 参考
OHCI(Open Host Controller Interface):微软主导的低速USB1.0(1.5Mbps)和全速USB1.1(12Mbps),OHCI接口的软件简单,硬件复杂 其实这个不止是usb上用,其他接口也有用的
UHCI(Universal Host Controller Interface): Intel主导的低速USB1.0(1.5Mbps)和全速USB1.1(12Mbps), 而UHCI接口的软件复杂,硬件简单
EHCI(Enhanced Host Controller Interface):高速USB2.0(480Mbps),
xHCI(eXtensible Host Controller Interface):USB3.0(5.0Gbps),采用了9针脚设计,同时也支持USB2.0、1.1等
2440接口
2440是兼容OHCI Rev 1.0的在数据手册上有标准,同时我们插入usb的时候也会有指示类似如下:
# usb 1-1: new full speed USB device using s3c2410-ohci and address 2
usb 1-1: configuration #1 chosen from 1 choice
usb 1-1: USB disconnect, address 2
基本流程
- 硬件插入,在D+或者D-上有电平变化,通知主机
- 主机通过端点地址0与usb设备交互,分配地址给usb从设备
- 通过端点0获取从设备的信息
- 安装对应的设备驱动,提供读写设备的函数
当插入usb时候,提示如下,搜索相关字符grep "USB device using" * -nR,可以在drivers\usb\core\hub.c中找到hub_port_init中调用
# usb 1-1: new full speed USB device using s3c2410-ohci and address 2
usb 1-1: configuration #1 chosen from 1 choice
usb 1-1: USB disconnect, address 2
每一个 USB 主机控制器,都自带了一个 USB HUB, HUB 上再接“ USB 设备”。可以认为
"HUB"是一个特殊的"USB 设备"。

继续搜索可以有如下关系
>hub_thread
>hub_events
>hub_port_connect_change(struct usb_hub *hub, int port1,u16 portstatus, u16 portchange)
//分配一个 usb_device 空间
>usb_alloc_dev(hdev, hdev->bus, port1);
>kzalloc(sizeof(*dev), GFP_KERNEL)
>device_initialize(&dev->dev)
>dev->dev.bus = &usb_bus_type;
>dev->dev.type = &usb_device_type
>sprintf(&dev->dev.bus_id[0], "usb%d", bus->busnum); //usb 的编号
//分配地址,最大127
>choose_address(udev)
>if (devnum >= 128) devnum = find_next_zero_bit(bus->devmap.devicemap, 128, 1);
//根据 usb高速低速全速设置一些数据
>hub_port_init(hub, udev, port1, i)
//设置地址
>hub_set_address
>usb_control_msg
//先读取必备的描述符 8
>usb_get_device_descriptor(udev, 8)
>usb_control_msg
//18个长度的描述符
>usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE)
//打印usb信息
>dev_info
>usb_new_device(udev)
//获取配置
>usb_get_configuration
//分配设备号
>dev->dev.devt = MKDEV(USB_DEVICE_MAJOR,(((udev->bus->busnum-1) * 128) + (udev->devnum-1)))
//这个是通用的函数哦,并不是单独给usb用的
>device_add
//创建设备文件
>device_create_file
>bus_add_device
>bus_attach_device
>bus_attach_device
>device_attach(dev)
>bus_for_each_drv(dev->bus, NULL, dev, __device_attach)
>__device_attach //(while ((drv = next_driver(&i)) && !error))
>driver_probe_device(drv, dev)
>if (drv->bus->match && !drv->bus->match(dev, drv))
goto done;
>really_probe(dev, drv);
>dev->bus->probe
>drv->probe //没有dev->bus->probe执行
>list_for_each_entry
//休眠
>wait_event_interruptible
>hub_irq
//唤醒队列
>wake_up(&khubd_wait)


alloc_dev
在usb_alloc_dev中初始化了总线设备类型,这与之前初始化platform的总线类型是类似的
dev->dev.bus = &usb_bus_type;
dev->dev.type = &usb_device_type;
都是属于bus_type,类比如下:
struct bus_type platform_bus_type = {
.name = "platform",
.dev_attrs = platform_dev_attrs,
.match = platform_match,
.uevent = platform_uevent,
.suspend = platform_suspend,
.suspend_late = platform_suspend_late,
.resume_early = platform_resume_early,
.resume = platform_resume,
};
struct bus_type usb_bus_type = {
.name = "usb",
.match = usb_device_match,
.uevent = usb_uevent,
.suspend = usb_suspend,
.resume = usb_resume,
};
这里的match也是同样的作用:设备插入时,总线驱动调用match来匹配
choose_address
分配usb的地址,支持最多127个设备端点
devnum = find_next_zero_bit(bus->devmap.devicemap, 128,
bus->devnum_next);
if (devnum >= 128)
devnum = find_next_zero_bit(bus->devmap.devicemap, 128, 1);
udev->devnum = devnum;
hub_port_init
这里进行usb插入后的信息打印,设置地址,获取描述符等
hub_set_address(udev);
usb_get_device_descriptor(udev, 8);
> memcpy(&dev->descriptor, desc, size);//存储的描述符结构
...
usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE);
usb_get_device_descriptor
在hub_port_init首先获取8字节的描述符,因为所有设备都支持这8字节的描述符,也支持8字节这个最小长度。具体描述符先结构如下:
struct usb_device_descriptor {
__u8 bLength; //本描述符的size
__u8 bDescriptorType; //描述符的类型,这里是设备描述符DEVICE
__u16 bcdUSB; //指明usb的版本,比如usb2.0
__u8 bDeviceClass; //类
__u8 bDeviceSubClass; //子类
__u8 bDeviceProtocol; //指定协议
__u8 bMaxPacketSize0; //端点0对应的最大包大小
__u16 idVendor; //厂家ID
__u16 idProduct; //产品ID
__u16 bcdDevice; //设备的发布号
__u8 iManufacturer; //字符串描述符中厂家ID的索引
__u8 iProduct; //字符串描述符中产品ID的索引
__u8 iSerialNumber; //字符串描述符中设备序列号的索引
__u8 bNumConfigurations; //可能的配置的数目
} __attribute__ ((packed));
usb_control_msg
在hub_set_address和usb_get_device_descriptor中最终也是调用了这个usb_control_msg来与usb设备交互的。
usb_new_device
int usb_new_device(struct usb_device *udev)
{
... ...
err = usb_get_configuration(udev); //(1)获取配置描述块
... ...
err = device_add(&udev->dev); // (2)把device放入bus的dev链表中,并寻找对应的设备驱动
}
usb_get_configuration
//支持最大8种配置
if (ncfg > USB_MAXCONFIG) {
dev_warn(ddev, "too many configurations: %d, "
"using maximum allowed: %d\n", ncfg, USB_MAXCONFIG);
dev->descriptor.bNumConfigurations = ncfg = USB_MAXCONFIG;
}
//分配9字节存储,读取描述符中的各种配置
buffer = kmalloc(USB_DT_CONFIG_SIZE, GFP_KERNEL);
usb_get_descriptor(dev, USB_DT_CONFIG, cfgno,buffer, USB_DT_CONFIG_SIZE);
//分配配置使用实际允许的大小
length = max((int) le16_to_cpu(desc->wTotalLength),USB_DT_CONFIG_SIZE);
bigbuffer = kmalloc(length, GFP_KERNEL);
usb_get_descriptor(dev, USB_DT_CONFIG, cfgno,bigbuffer, length);
//填充
dev->rawdescriptors[cfgno] = bigbuffer;
//解析
usb_parse_configuration
device_add
dev = get_device(dev);
parent = get_device(dev->parent);
error = setup_parent(dev, parent);
//创建类下的文件
error = device_create_file(dev, &dev->uevent_attr);
。。
//把这个设备添加到dev->bus的device表中
bus_add_device
///来匹配对应的驱动程序
bus_attach_device
//链表遍历
list_for_each_entry(class_intf, &dev->class->interfaces, node)
bus_attach_device
device_add中会调用bus_attach_device来运行匹配函数后运行probe
bus_attach_device
>device_attach(dev)
>bus_for_each_drv(dev->bus, NULL, dev, __device_attach)//函数指针在这里
>__device_attach //(while ((drv = next_driver(&i)) && !error))
>driver_probe_device(drv, dev)
>if (drv->bus->match && !drv->bus->match(dev, drv))
goto done;
>really_probe(dev, drv);
>dev->bus->probe
>drv->probe //如果没有dev->bus->probe执行
match
这个时候再来看匹配总线的函数
struct bus_type usb_bus_type = {
.name = "usb",
.match = usb_device_match,
.uevent = usb_uevent,
.suspend = usb_suspend,
.resume = usb_resume,
};
static int usb_device_match(struct device *dev, struct device_driver *drv)
{
if (is_usb_device(dev)) { //判断是不是USB设备
if (!is_usb_device_driver(drv))
return 0;
return 1;
}
else { //否则就是USB驱动或者USB设备的接口
struct usb_interface *intf;
struct usb_driver *usb_drv;
const struct usb_device_id *id;
if (is_usb_device_driver(drv)) //如果是USB驱动,就不需要匹配,直接return
return 0;
intf = to_usb_interface(dev); //获取USB设备的接口
usb_drv = to_usb_driver(drv); //获取USB驱动
id = usb_match_id(intf, usb_drv->id_table); //匹配USB驱动的成员id_table
if (id)
return 1;
id = usb_match_dynamic_id(intf, usb_drv);
if (id)
return 1;
}
return 0;
}
查看下具体的id_table,具体的类型在include\linux\usb.h
struct usb_device_id {
__u16 match_flags; //与usb设备匹配那种类型?比较类型的宏如下:
//USB_DEVICE_ID_MATCH_INT_INFO : 用于匹配设备的接口描述符的3个成员
//USB_DEVICE_ID_MATCH_DEV_INFO: 用于匹配设备描述符的3个成员
//USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION: 用于匹配特定的USB设备的4个成员
//USB_DEVICE_ID_MATCH_DEVICE:用于匹配特定的USB设备的2个成员(idVendor和idProduct)
/* 以下4个用匹配描述特定的USB设备 */
__u16 idVendor; //厂家ID
__u16 idProduct; //产品ID
__u16 bcdDevice_lo; //设备的低版本号
__u16 bcdDevice_hi; //设备的高版本号
/*以下3个就是用于比较设备描述符的*/
__u8 bDeviceClass; //设备类
__u8 bDeviceSubClass; //设备子类
__u8 bDeviceProtocol; //设备协议
/* 以下3个就是用于比较设备的接口描述符的 */
__u8 bInterfaceClass; //接口类型
__u8 bInterfaceSubClass; //接口子类型
__u8 bInterfaceProtocol; //接口所遵循的协议
/* not matched against */
kernel_ulong_t driver_info;
};
参考/drivers/hid/usbhid/usbmouse.c(内核自带的USB鼠标驱动)
static struct usb_device_id usb_mouse_id_table [] = {
{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
USB_INTERFACE_PROTOCOL_MOUSE) },
{ } /* Terminating entry */
};
#define USB_INTERFACE_INFO(cl,sc,pr) \
//设置id_table的.match_flags成员
.match_flags = USB_DEVICE_ID_MATCH_INT_INFO, .bInterfaceClass = (cl), \
//设置id_table的3个成员,用于与匹配USB设备的3个成员
.bInterfaceSubClass = (sc), .bInterfaceProtocol = (pr)
//最终结果如下
.bInterfaceClass =USB_INTERFACE_CLASS_HID;
//设置匹配USB的接口类型为HID类, 因为USB_INTERFACE_CLASS_HID=0x03
//HID类是属于人机交互的设备,比如:USB键盘,USB鼠标,USB触摸板,USB游戏操作杆都要填入0X03
.bInterfaceSubClass =USB_INTERFACE_SUBCLASS_BOOT;
//设置匹配USB的接口子类型为启动设备
.bInterfaceProtocol=USB_INTERFACE_PROTOCOL_MOUSE;
//设置匹配USB的接口协议为USB鼠标的协议,等于2
//当.bInterfaceProtocol=1也就是USB_INTERFACE_PROTOCOL_KEYBOARD时,表示USB键盘的协议
查看下win7系统鼠标的设备信息,VID:表示厂家(vendor)ID,PID:表示产品(Product) ID

linux usb总线驱动(一)的更多相关文章
- EDK II之USB总线驱动的实现框架
本文简单介绍一下UEFI中USB驱动的实现框架: 下图是USBD向上层驱动提供的接口: 1.从图中我们可以看出,USBDI的实现主要通过调用HCDI实现 和 访问USB_INTERFACE结构体(该结 ...
- Hi3559AV100外接UVC/MJPEG相机实时采图设计(一):Linux USB摄像头驱动分析
下面将给出Hi3559AV100外接UVC/MJPEG相机实时采图设计的整体流程,主要实现是通过V4L2接口将UVC/MJPEG相机采集的数据送入至MPP平台,经过VDEC.VPSS.VO最后通过HD ...
- Linux下的USB总线驱动(一)
版权所有,转载请说明转自 http://my.csdn.net/weiqing1981127 一.USB理论 1. USB概念概述 USB1.0版本速度1.5Mbps(低速USB) USB1 ...
- Linux USB摄像头驱动【转】
本文转载自:http://www.itdadao.com/articles/c15a509940p0.html 在 cortex-a8 中,可接入摄像头的接口通常可以分为两种, CAMERA 接口和 ...
- Linux 设备总线驱动模型
尽管LDD3中说对多数程序员掌握设备驱动模型不是必要的,但对于嵌入式Linux的底层程序员而言,对设备驱动模型的学习非常重要. Linux设备模型的目的:为内核建立一个统一的设备模型,从而又一 ...
- Linux USB 摄像头驱动
在 cortex-a8 中,可接入摄像头的接口通常可以分为两种, CAMERA 接口和 USB 接口的摄像头.这一章主要是介绍 USB 摄像头的设备驱动程序.在我们印象中,驱动程序都是一个萝卜一个坑, ...
- linux平台总线驱动设备模型之点亮LED
这一节里,我们来使用平台驱动设备这一套架构来实现我们之前使用简单的字符设备驱动点亮LED,这里并无实际意义,只是告诉大家如果编写平台总线驱动设备. 问:如何编写平台总线驱动设备这一套架构的设备驱动? ...
- 嵌入式Linux USB WIFI驱动的移植
硬件平台:飞思卡尔MX258开发板 操作系统:Linux2.6.31 WIFI: RT2860 USB WIFI模组 交叉编译环境:gcc version 4.1.2 调试步骤: 第一步:测试U ...
- Linux平台总线驱动设备模型
platform总线是一种虚拟的总线,相应的设备则为platform_device,而驱动则为platform_driver.Linux 2.6的设备驱动模型中,把I2C.RTC.LCD等都归纳为pl ...
随机推荐
- C#基础第六天
数组 方法 实现代码的重用 参数 返回值 注释 return 语法:[public] static 返回值类型 方法名([参数列表]){ 方法体;}public:访问修饰符,公开的,公共的s ...
- 我的博客即将入驻“云栖社区”,诚邀技术同仁一同入驻。
我的博客即将入驻"云栖社区",诚邀技术同仁一同入驻. 博客搬家邀请码NXLZV
- 索引-mysql
什么是索引 索引的四大作用 (参考百度百科:百度百科-索引) 索引的优缺点 索引分类 什么地方需要建索引 索引优化 什么是索引? 宏观来说,索引是一种检索工具,目录也是一种检索工具,但是两者是有区是别 ...
- Django--session(登录用)
一.session的原理图 二.Django中session对象的设置/读取/删除及其他方法 三. Django--配置 settings.py中与session有关的参数 一.session的原理图 ...
- 【English Email】CIP payouts now in Workday
simplification简化的[ˌsɪmplɪfɪˈkeɪʃn] quota配额[ˈkwoʊtə] regional区域的[ˈriːdʒənl] mechanics技工[məˈkænɪks] ...
- vue 路由元信息
官方文档:路由meta元信息 前言: 在设置面包屑导航还有菜单栏的时候,路由的meta字段可以自定义我们需要的信息,然后路由跳转的时候,提供我们判断条件 文档: 定义路由的时候可以配置 meta 字段 ...
- Linux 进入 5.0 时代!
Linux 进入 5.0 时代! 为什么 Linux 4.2 之后的版本不再是 4.21 而是 5.0? 如果你非要一个理由,那就是因为 Linux 4.x 的版本如今用手指与脚趾加在一起都要数不过来 ...
- Linux内核入门到放弃-无持久存储的文件系统-《深入Linux内核架构》笔记
proc文件系统 proc文件系统是一种虚拟的文件系统,其信息不能从块设备读取.只有在读取文件内容时,才动态生成相应的信息. /proc的内容 内存管理 系统进程的特征数据 文件系统 设备驱动程序 系 ...
- Python第一天:你必须要知道的Python擅长领域以及各种重点学习框架(包含Python在世界上的应用)
目录 Python5大擅长领域 WEB开发 网络编程 科学运算 GUI图形开发 运维自动化 Python在世界上的知名应用 国外 谷歌 CIA NASA YouTube Dropbox Instagr ...
- 【区块链】【一】Hash 算法【转】
问题导读1.哈希算法在区块链的作用是什么?2.什么是哈希算法?3.哈希算法是否可逆?4.比特币采用的是什么哈希算法? 作用在学习哈希算法前,我们需要知道哈希在区块链的作用哈希算法的作用如下:区块链通过 ...