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_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函数