USB学习之一:USB总线驱动程序
USB总线驱动程序的作用
1. 识别USB设备
1.1 分配地址
1.2 并告诉USB设备(set address)
1.3 发出命令获取描述符
描述符的信息可以在include\linux\usb\Ch9.h看到
2. 查找并安装对应的设备驱动程序
3. 提供USB读写函数
将一个USB设备接到开发板上,看输出信息:
usb 1-1: new full speed USB device using s3c2410-ohci and address 2
usb 1-1: configuration #1 chosen from 1 choi ce
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
再接上:
usb 1-1: new full speed USB device using s3c2410-ohci and address 3
usb 1-1: configuration #1 chosen from 1 choice
scsi1 : SCSI emulation for USB Mass Storage devices
scsi 1:0:0:0: Direct-Access HTC Android Phone 0100 PQ: 0 ANSI: 2
sd 1:0:0:0: [sda] Attached SCSI removable disk
全局搜索 grep "USB device using" * -nR 在2.6的内核中可以搜到相关信息。
我用的是3.0的内核,USB设备插入后,会有如下信息打印出来
[ 465.520759] usb 1-2.3: new high speed USB device number 22 using s5p-ehci
因此可以搜索:grep "USB device number" -nr .
找到在linux-3.0.86/drivers/usb/core/hub.c 中hub_port_init函数中,因此我们可以通过hub_port_init函数找出USB总线驱动程序的调用关系。先上一张图。
接着从代码的角度做一些分析:
drivers/usb/core/usb.c
struct bus_type usb_bus_type = {
.name = "usb",
.match = usb_device_match,
.uevent = usb_uevent,
};
static int __init usb_init(void)
{
retval = bus_register(&usb_bus_type); //注册USB总线,USB总线和平台总线及其他总线是类似的
........ //省略了很多初始化的工作
retval = usb_hub_init(); //usb hub初始化
}
static struct usb_driver hub_driver = {
.name = "hub",
.probe = hub_probe,
.disconnect = hub_disconnect,
.suspend = hub_suspend,
.resume = hub_resume,
.reset_resume = hub_reset_resume,
.pre_reset = hub_pre_reset,
.post_reset = hub_post_reset,
.unlocked_ioctl = hub_ioctl,
.id_table = hub_id_table,
.supports_autosuspend = 1,
};
int usb_hub_init(void)
{
if (usb_register(&hub_driver) < 0) { //注册usb hub driver,在hub_driver中提供了一系列hub相关的操作函数
}
khubd_task = kthread_run(hub_thread, NULL, "khubd");//初始化一个hub 线程,这个线程在usb设备插入后会跑,后面会分析到。
}
//接着分析usb_register函数
usb_register(&hub_driver)
usb_register_driver(driver, THIS_MODULE, KBUILD_MODNAME)
driver_register(&new_driver->drvwrap.driver)
bus_add_driver(drv)
driver_attach(drv)
bus_for_each_dev(drv->bus, NULL, drv, __driver_attach)
__driver_attach
driver_match_device(drv, dev)
return drv->bus->match ? drv->bus->match(dev, drv) : 1;
//因此最后是调用的是上文中提到的usb_bus_type中提供的
usb_device_match(struct device *dev, struct device_driver *drv)函数。该函数是通过比较usb_driver中的id_table来确定的是否匹配并调用probe函数。
这些是跟内核的总线设备框架有关。
设备和驱动匹配到之后就会调用probe函数,即hub_probe
static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
struct usb_host_interface *desc;
.......//省略一些初始化工作
hub = kzalloc(sizeof(*hub), GFP_KERNEL); //分配一个hub结构体
.......
if (hub_configure(hub, endpoint) >= 0) {
}
hub_configure(hub, endpoint)
hub->urb = usb_alloc_urb(0, GFP_KERNEL); //分配hub->urb
usb_fill_int_urb(hub->urb, hdev, pipe, *hub->buffer, maxp, hub_irq,
hub, endpoint->bInterval);//注册hub中断 hub_irq,当usb设备被插入时,hub_irq会被调用
当由USB设备插入hub,hub端口的D+或者D-会由原来的低电平被拉高到3V高电平,触发hub中断,hub_irq
static void hub_irq(struct urb *urb)
{
struct usb_hub *hub = urb->context;
int status = urb->status;
unsigned i;
unsigned long bits;
.........
/* Something happened, let khubd figure it out */
kick_khubd(hub); //唤醒上文中提到的hub_thread
}
static int hub_thread(void *__unused)
{
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));
}
//接着调用到hub_events
hub_events
hub_port_connect_change(hub, i,
portstatus, portchange);
udev = usb_alloc_dev(hdev, hdev->bus, port1);//分配一个USB DEV设备
choose_devnum(udev);//设置这个dev的编号
hub_port_init(hub, udev, port1, i);
在hub_port_init中做了很多事情
hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
int retry_counter)
{
static DEFINE_MUTEX(usb_address0_mutex);
retval = hub_port_reset(hub, port1, udev, delay);//复位hub
oldspeed = udev->speed; //获取usb设备的速度
switch (udev->speed) { //根据不同速度的设备做一些设置
case USB_SPEED_SUPER:
case USB_SPEED_WIRELESS: /* fixed at 512 */
udev->ep0.desc.wMaxPacketSize = cpu_to_le16(512);
break;
case USB_SPEED_HIGH: /* fixed at 64 */
udev->ep0.desc.wMaxPacketSize = cpu_to_le16(64);
break;
case USB_SPEED_FULL: /* 8, 16, 32, or 64 */
/* to determine the ep0 maxpacket size, try to read
* the device descriptor to get bMaxPacketSize0 and
* then correct our initial guess.
*/
udev->ep0.desc.wMaxPacketSize = cpu_to_le16(64);
break;
case USB_SPEED_LOW: /* fixed at 8 */
udev->ep0.desc.wMaxPacketSize = cpu_to_le16(8);
break;
}
if (udev->speed != USB_SPEED_SUPER)
dev_info(&udev->dev,
"%s %s speed %sUSB device number %d using %s\n", //识别到usb设备后就会打印这句信息
(udev->config) ? "reset" : "new", speed, type,
devnum, udev->bus->controller->driver->name);
for (i = 0; i < GET_DESCRIPTOR_TRIES; (++i, msleep(100))) {
if (USE_NEW_SCHEME(retry_counter) && !(hcd->driver->flags & HCD_USB3)) {
struct usb_device_descriptor *buf;
int r = 0;
#undef GET_DESCRIPTOR_BUFSIZE
}
/*
* If device is WUSB, we already assigned an
* unauthorized address in the Connect Ack sequence;
* authorization will assign the final address.
*/
if (udev->wusb == 0) {
for (j = 0; j < SET_ADDRESS_TRIES; ++j) {
retval = hub_set_address(udev, devnum); //设置设备的地址
}
if (udev->speed == USB_SPEED_SUPER) {
devnum = udev->devnum;
dev_info(&udev->dev,
"%s SuperSpeed USB device number %d using %s\n",
(udev->config) ? "reset" : "new",
devnum, udev->bus->controller->driver->name);
}
}
retval = usb_get_device_descriptor(udev, 8);//获取设备描述符,第8个字节是设备描述符的最大包长
i = udev->descriptor.bMaxPacketSize0;
if (le16_to_cpu(udev->ep0.desc.wMaxPacketSize) != i) {
if (udev->speed == USB_SPEED_LOW ||
}
retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE);//在上文中获取到了设备描述符的最大包长,因此这里一次把设备描述符读取
if (retval < (signed)sizeof(udev->descriptor)) {
dev_err(&udev->dev, "device descriptor read/all, error %d\n",
retval);
if (retval >= 0)
retval = -ENOMSG;
goto fail;
}
}
hub_port_init初始化完成以后调用usb_new_device(udev),将usb设备添加到设备链表中去。
int usb_new_device(struct usb_device *udev)
device_add(&udev->dev); // 把device放入usb_bus_type的dev链表,
// 从usb_bus_type的driver链表里取出usb_driver,
// 把usb_interface和usb_driver的id_table比较
// 如果能匹配,调用usb_driver的probe
至此USB总线驱动程序框架已经分析完了。
上一篇: python爬虫——爬取知网《钢结构》期刊所有文章的摘要
下一篇: Linux字符设备驱动