USB驱动程序之USB总线驱动程序

来源:互联网 发布:中国人性格 知乎 编辑:程序博客网 时间:2024/04/27 16:25

USB主机控制器分为三种,一种是UHCI  OHCI  EHCI

UHCI规范:intel阵营  告诉你怎么做USB这些东西 适用于低速和全速USB设备  低速一般称为USB1.1 全速一般称为USB2.0 inter做硬件比较牛 所以他的硬件比较复杂点,软件比较简单点 

OHCI规范:microsoft阵营做出来的东西 适用于低速和全速 微软软件牛逼点,所以它的软件复杂点,硬件简单点 

EHCI规范是后来出现的东西  支持高速


低速是1.5Mbps 全速是12Mbps  高速是480Mbps


2410用的OHCI 我们在内核下面搜索 就会发现有个文件叫OHCI-s3c2410.c


我们USB总线驱动程序的作用

1:识别设备

1.1分配地址,并告诉USB设备(设置地址)

1.2获得发出命令获取描述符

2:查找并安装相应的驱动程序

3:提供USB读写函数


我们在开发板上接入一个USB设备,看输出信息


拔出来之后会看到下面这个信息

在插上会看到如下信息


地址变成了3


我们可以从内核中搜,看一下这行代码从哪里输出处理

grep "USB device using" * -nR

得到如图


是在内核的/drivers/usb/core/hub.c里面

hub是什么意思呢,2440里面有个USB主机控制器,里面有个hub 上面就接有USB设备。每个USB主机控制器里面都自带一个HUB,

在hub_port_init函数里面打印的这句话

在 hub_port_connect_change函数里调用hub_port_init这个函数

hub_port_connect_change这个函数又在hub_events这里面被调用

hub_events又被hub_thread给调用


static int hub_thread(void *__unused)
{
/* khubd needs to be freezable to avoid intefering with USB-PERSIST
* port handover.  Otherwise it might see that a full-speed device
* was gone before the EHCI controller had handed its port over to
* the companion full-speed controller.
*/
set_freezable();


do {
hub_events();
wait_event_freezable(khubd_wait,
!list_empty(&hub_event_list) ||
kthread_should_stop());
} while (!kthread_should_stop() || !list_empty(&hub_event_list));


pr_debug("%s: khubd exiting\n", usbcore_name);
return 0;
}这是一个内核线程,平时是休眠的,但有事件发生的时候把它唤醒,谁来把它唤醒呢,

wait_event_freezable(khubd_wait,!list_empty(&hub_event_list) ||kthread_should_stop()); 这里休息,在khubd_wait队列里面休眠


谁来唤醒

在static void kick_khubd(struct usb_hub *hub)这个函数里

有一句wake_up(&khubd_wait);唤醒

这个函数又被hub_irq给调用,这里是主机控制器里面注册的中断,不是USB设备的中断。当我们接上一个USB设备之后,D+或者D-就会有一个由低电平变为高电平,硬件上就感知到了有USB设备接入,里面就会产生某个中断,然后按上面分析的一套一套,最后打应那就话


我们来看看他是怎么分配地址的,进入这个函数hub_port_connect_change

choose_devnum(udev);这里是分配地址

看看这个函数怎么分配的

static void choose_devnum(struct usb_device *udev)
{
int devnum;
struct usb_bus*bus = udev->bus;


/* If khubd ever becomes multithreaded, this will need a lock */
if (udev->wusb) {
devnum = udev->portnum + 1;
BUG_ON(test_bit(devnum, bus->devmap.devicemap));
} else {
/* Try to allocate the next devnum beginning at
* bus->devnum_next. */
devnum = find_next_zero_bit(bus->devmap.devicemap, 128,
   bus->devnum_next);                                      查找下一个0位,某个地址被用了,对应的某一位就会被设置成1.就表示你这个地址正在被使用,找不到的话找到128,如果大于等于128的话又重头开始找。所以说一个USB主机控制器里面最多接1-127个设备
if (devnum >= 128)
devnum = find_next_zero_bit(bus->devmap.devicemap,
   128, 1);
bus->devnum_next = ( devnum >= 127 ? 1 : devnum + 1);
}
if (devnum < 128) {
set_bit(devnum, bus->devmap.devicemap);
udev->devnum = devnum;
}
}


这里只是分配了还没有告诉USB设备呢,我们跳出这个函数接着往下看

hub_port_init这里面

r = usb_control_msg(udev, usb_rcvaddr0pipe(),USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,USB_DT_DEVICE << 8, 0,buf, GET_DESCRIPTOR_BUFSIZE,initial_descriptor_timeout); 这里USB控制消息,就是用USB总线驱动提供的一些函数发一些控制消息

我们前面说有四种传输,这就是控制传输。他有什么命令SB_REQ_GET_DESCRIPTOR 这里 获得描述符,当我们没有到获得描述符的那部分,现在还只是告诉地址部分,在这个函数后面有个hub_set_address这个函数 设置地址,把地址告诉USB设备,然后马上retval = usb_get_device_descriptor(udev, 8);调用这个获得文件描述符。

int usb_get_device_descriptor(struct usb_device *dev, unsigned int size)
{
struct usb_device_descriptor *desc;

.................................................................

}

这里有个文件描述符的结构体,我们可以进去看一下这里面是什么东西,在C9.h里

struct usb_device_descriptor {
__u8  bLength;
__u8  bDescriptorType;
__u16 bcdUSB;                          //USB版本号
__u8  bDeviceClass;     //类
__u8  bDeviceSubClass; //子类
__u8  bDeviceProtocol;//协议
__u8  bMaxPacketSize0;//最大包大小0 0是什么意思端点0
__u16 idVendor;//厂家ID
__u16 idProduct;//产品ID
__u16 bcdDevice;
__u8  iManufacturer;
__u8  iProduct;
__u8  iSerialNumber;
__u8  bNumConfigurations;  配置的个数,这个设备他有多少种配置,
} __attribute__ ((packed));

除了有设备描述符还有配置描述符

struct usb_config_descriptor {
__u8  bLength;
__u8  bDescriptorType;


__le16 wTotalLength;
__u8  bNumInterfaces;接口的个数 接口就是逻辑上的设备 比如上USB声卡,硬件上只有一个设备,但是有两个功能一个是录音一个是播放,我们就可以把它分为两个接口,两个逻辑设备
__u8  bConfigurationValue;
__u8  iConfiguration;
__u8  bmAttributes;属性
__u8  bMaxPower;消耗的电源
} __attribute__ ((packed));

显然下面又有接口描述符

struct usb_interface_descriptor {
__u8  bLength;
__u8  bDescriptorType;


__u8  bInterfaceNumber;
__u8  bAlternateSetting;
__u8  bNumEndpoints;    端点 USB传输的对象是端点
__u8  bInterfaceClass;
__u8  bInterfaceSubClass;子类
__u8  bInterfaceProtocol;协议
__u8  iInterface;
} __attribute__ ((packed));

然后又有端点描述符

struct usb_endpoint_descriptor {
__u8  bLength;
__u8  bDescriptorType;


__u8  bEndpointAddress;端点地址
__u8  bmAttributes;属性
__le16 wMaxPacketSize;最大包的大小
__u8  bInterval;查询有多频繁的意思,因为我们鼠标移动都是靠查询来检测的


/* NOTE:  these two are _only_ in audio endpoints. */
/* use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof. */
__u8  bRefresh;
__u8  bSynchAddress;
} __attribute__ ((packed));

我们写驱动程序的时候是给逻辑上的设备写的,一个USB设备可能安装多个USB设备,因为他可能有多个逻辑设备,多个接口


//但是很奇怪为什么这里只获得8个字节呢,是因为它还不知道你这个端点0一次传多少个数据

读8个字节

u8  bLength;
__u8  bDescriptorType;
__u16 bcdUSB;                          //USB版本号
__u8  bDeviceClass;     //类
__u8  bDeviceSubClass; //子类
__u8  bDeviceProtocol;//协议
__u8  bMaxPacketSize0;//最大包大小0 0是什么意思端点0

刚好读到这里,从最后一个我们就知道你一个端点0最大可以传输多少的数据,我先得到你这个数据,以后就可以尽快的得到数据了

retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE) 这里又会再次获得设备描述符

然后我们在来看看连接hub_port_connect_change

看看里面有什么东西

status = usb_new_device(udev);有个这个,新建一个USB设备

看一看怎么new_device

err = usb_enumerate_device(udev);这个函数 有个err = usb_get_configuration(udev);这个,获得配置

进去看一看这个函数

result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno,
   desc, USB_DT_CONFIG_SIZE);获得描述符

result = usb_parse_configuration(dev, cfgno,
   &dev->config[cfgno], bigbuffer, length); 解析描述符

在回过头来看usb_new_device里面除了解析之外,还会device_add添加设备

device_add就会把设备放入总线的device链表,从总线的driver链表里取出driver,一一比较,如果能匹配,调用driver的probe函数

usb_new_device(udev) 这个参数是udev udev在哪里呢 在udev = usb_alloc_dev(hdev, hdev->bus, port1);这里

分配一个udev结构体,dev->dev.bus = &usb_bus_type;这个结构体里有总线 USB总线

另外一条总线处理了,就是usb_bus_type,这个总线是干嘛呢,它里面也有

struct bus_type usb_bus_type = {
.name = "usb",
.match = usb_device_match,
.uevent = usb_uevent,
};match函数 

match函数干嘛勒  他是跟id = usb_match_id(intf, usb_drv->id_table); id_table做比较

usb_driver就是通过usb_register注册的,usb_interface就是通过usb_new_device注册的。

然后driver里面就有个probe函数










0 0
原创粉丝点击