linux usb初始化 謹以此文紀念過往的歲月 一.前言 對于usb的普通驅(qū)動,我們了解了不少,但是對于usb的真正核心還是不是太理解。該文中對于usb的初始化進行一定的學習,如有不對之處,請各位多多指教。
二.usb子系統(tǒng)初始化。 話說在linux啟動之初,就會將usb子系統(tǒng)初始化完成,亦如input子系統(tǒng)和V4L2一樣。usb_init就完成了初始化以及啟動usb hub守護進程。那來看usb_init中的各個函數(shù)的實現(xiàn)。 2.1 ksuspend_usb_init static int ksuspend_usb_init(void) { ksuspend_usb_wq = create_freezeable_workqueue("ksuspend_usbd"); if (!ksuspend_usb_wq) return -ENOMEM; return 0; } 創(chuàng)建一個名為ksuspend_usbd的工作隊列。不過在該函數(shù)說明中也說明了,該函數(shù)在多CPU中并不能很好的工作。也許大家對這個函數(shù)會很奇怪,不知道該工作隊列的作用。 這個工作隊列如何使用的,將在看usbsuspend和resume時再具體看這個東東具體有什么用。 2.2 bus_register 在usb_init中第二個調(diào)用的函數(shù)是bus_register(&usb_bus_type);這個應(yīng)該很熟悉吧,注冊一個usb總線,其會在/sys/bus下創(chuàng)建一個usb的文件夾,在其中會創(chuàng)建bus屬性文件和drivers和devices文件夾,以后所有的usb設(shè)備和usb驅(qū)動都會在這兩個文件夾下。bus總線需要指明device和driver匹配的規(guī)則,例如platform bus以name進行匹配,而usb則以id進行匹配。對于bus_register在前文中講述多,這里不多此一舉了。那主要來看usb bus的特征。 struct bus_type usb_bus_type = { .name = "usb", --定義bus的name,這個也會是在/sys/bus下看見的文件夾得名稱。 .match = usb_device_match, --定義bus的device和driver的匹配規(guī)則 .uevent = usb_uevent, --usb的觸發(fā)事件處理。 }; 下面來看usb的匹配規(guī)則函數(shù)。不過有一點需要說明的是一般usb的驅(qū)動為usb的interface的驅(qū)動,而不是usb設(shè)備的驅(qū)動。為了方便描述,并沒有加以說明,要注意了。 static int usb_device_match(struct device *dev, struct device_driver *drv) { if (is_usb_device(dev)) { --判斷是否為usb device if (!is_usb_device_driver(drv)) --判斷是否為為usb device的驅(qū)動。interface 驅(qū)動不能與device匹配。 return 0; return 1; --這個不是主要的,下面才是真正的驅(qū)動和設(shè)備的匹配。 } else { struct usb_interface *intf; struct usb_driver *usb_drv; const struct usb_device_id *id; if (is_usb_device_driver(drv)) --usb device driver不能驅(qū)動interface。 return 0; intf = to_usb_interface(dev); --取得設(shè)備的當前interface usb_drv = to_usb_driver(drv); --取得驅(qū)動。 id = usb_match_id(intf, usb_drv->id_table); --真正的匹配規(guī)則。對于這個函數(shù)我們就不加描述。感興趣的可以去看具體的實現(xiàn),其實這個函數(shù)的額實現(xiàn)也很簡單。就是對usb interface中的id如idVendor ,idProduct, bDeviceClass等參數(shù)進行匹配。 if (id) return 1; id = usb_match_dynamic_id(intf, usb_drv); if (id) return 1; } return 0; } 對于usb match比較好理解,而usb_uevent中,則定義了usb的熱插拔的處理函數(shù)。這個的作用就是將設(shè)備的信息存儲到env中,可以供用戶空間讀。對于設(shè)備的信息,我們可以通過在用戶空間的程序中讀取/proc/bus/usb/下的文件取得這些設(shè)備的信息。 static int usb_uevent(struct device *dev, struct kobj_uevent_env *env) { struct usb_device *usb_dev; if (is_usb_device(dev)) usb_dev = to_usb_device(dev); else { struct usb_interface *intf = to_usb_interface(dev); usb_dev = interface_to_usbdev(intf); }
if (usb_dev->devnum < 0) { return -ENODEV; } if (!usb_dev->bus) { return -ENODEV; }
#ifdef CONFIG_USB_DEVICEFS --添加設(shè)備名稱 if (add_uevent_var(env, "DEVICE=/proc/bus/usb/%03d/%03d", usb_dev->bus->busnum, usb_dev->devnum)) return -ENOMEM; #endif if (add_uevent_var(env, "PRODUCT=%x/%x/%x", --添加設(shè)備id le16_to_cpu(usb_dev->descriptor.idVendor), le16_to_cpu(usb_dev->descriptor.idProduct), le16_to_cpu(usb_dev->descriptor.bcdDevice))) return -ENOMEM; if (add_uevent_var(env, "TYPE=%d/%d/%d", --添加設(shè)備類型。 usb_dev->descriptor.bDeviceClass, usb_dev->descriptor.bDeviceSubClass, usb_dev->descriptor.bDeviceProtocol)) return -ENOMEM;
return 0; } 對于uevent的理解可能比較難懂一點,不過你可以把uevent的操作看成將一些設(shè)備和驅(qū)動的信息寫到/proc下對應(yīng)的文件中,讓usrspcae的用戶程序可以讀取從而獲取設(shè)備的信息。 2.3 usb_host_init 也許從這個函數(shù)名上看不出這個函數(shù)的具體應(yīng)用是什么,既然看函數(shù)名不知道干什么,那我們來看該函數(shù)的具體實現(xiàn)。你會發(fā)現(xiàn)這個函數(shù)好像挺簡單的,就是創(chuàng)建一個usb host 類。 int usb_host_init(void) { int retval = 0; usb_host_class = class_create(THIS_MODULE, "usb_host"); if (IS_ERR(usb_host_class)) retval = PTR_ERR(usb_host_class); return retval; } 關(guān)于這個設(shè)備類有什么用,以后再說,咱們還是繼續(xù)usb init的主線來看usb。 2.4 usb_major_init 注冊usb major其實是注冊一個以USB_MAJOR為主設(shè)備號的cdev。不過其還分配了256個從設(shè)備號。提供一個公共的open函數(shù)。 int usb_major_init(void) { int error;
error = register_chrdev(USB_MAJOR, "usb", &usb_fops); if (error) printk(KERN_ERR "Unable to get major %d for usb devices\n", USB_MAJOR);
return error; } 對于每一個usb設(shè)備的打開都會先調(diào)用主設(shè)備的open,而后根據(jù)不同的設(shè)備調(diào)用不同的open函數(shù)。下面就是cdev的ops操作集。 static const struct file_operations usb_fops = { .owner = THIS_MODULE, .open = usb_open, }; static int usb_open(struct inode * inode, struct file * file) { int minor = iminor(inode); const struct file_operations *c; int err = -ENODEV; const struct file_operations *old_fops, *new_fops = NULL;
lock_kernel(); down_read(&minor_rwsem); c = usb_minors[minor]; --關(guān)于這個全局變量在以后的usb設(shè)備注冊時會看到,這里先有個底。 if (!c || !(new_fops = fops_get(c))) goto done;
old_fops = file->f_op; file->f_op = new_fops; /* Curiouser and curiouser... NULL ->open() as "no device" ? */ if (file->f_op->open) err = file->f_op->open(inode,file); --調(diào)用真正的open if (err) { fops_put(file->f_op); file->f_op = fops_get(old_fops); } fops_put(old_fops); done: up_read(&minor_rwsem); unlock_kernel(); return err; } 其實上面的公共的open就是提供一個對于所有的usb 設(shè)備的公共接口,真正的open其實是usb_minors中存儲的fops->open. 2.5 usb_register 該函數(shù)是注冊usbfs_driver驅(qū)動。不過注冊該驅(qū)動不知是為什么。 2.6 usb_devio_init 對于這個函數(shù)就更加困惑了,這個同樣是注冊一個以USB_DEVICE_DEV為主設(shè)備號的cdev設(shè)備。 int __init usb_devio_init(void) { int retval; retval = register_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX, "usb_device"); if (retval) { goto out; } cdev_init(&usb_device_cdev, &usbdev_file_operations); retval = cdev_add(&usb_device_cdev, USB_DEVICE_DEV, USB_DEVICE_MAX); if (retval) { goto error_cdev; } usb_classdev_class = class_create(THIS_MODULE, "usb_device"); if (IS_ERR(usb_classdev_class)) { goto out; } usb_classdev_class->dev_kobj = NULL; usb_register_notify(&usbdev_nb); return retval; } 關(guān)于這個函數(shù)具體有什么用,以后再看,先跳過。 2.7 usbfs_init int __init usbfs_init(void) { int retval; retval = register_filesystem(&usb_fs_type); --注冊filesystem if (retval) return retval; usb_register_notify(&usbfs_nb); usbdir = proc_mkdir("bus/usb", NULL); --為usbfs創(chuàng)建掛載點 return 0; } 2.8 usb_hub_init int usb_hub_init(void) { if (usb_register(&hub_driver) < 0) { --注冊hub驅(qū)動 printk(KERN_ERR "%s: can't register hub driver\n", usbcore_name); return -1; } khubd_task = kthread_run(hub_thread, NULL, "khubd"); --創(chuàng)建hub守護進程 if (!IS_ERR(khubd_task)) return 0; usb_deregister(&hub_driver); return -1; } 2.9 usb_register_device_driver 注冊一個通用usb驅(qū)動。usb_register_device_driver(&usb_generic_driver, THIS_MODULE); OK,到此usb init是看完了,不過這里面卻有種種的迷霧,讓人不解困惑。比如為什么要注冊兩個usb major,還注冊了兩個古怪的驅(qū)動,這些種種讓人不解。不過這些都將會在后面的學習中來了解這些。 三.總結(jié) 對于大牛們而言,也許這些東西很簡單,不過對于我這個菜鳥而言,卻是那么的難弄。在此本文也僅僅是對自己學習的一個記錄。 |
|
來自: lchjczw > 《USB設(shè)備》