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總線驅動程序框架已經分析完了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章