日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

linux usb初始化

 lchjczw 2012-03-15

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ù)會很奇怪,不知道該工作隊列的作用。

這個工作隊列如何使用的,將在看usbsuspendresume時再具體看這個東東具體有什么用。

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屬性文件和driversdevices文件夾,以后所有的usb設(shè)備和usb驅(qū)動都會在這兩個文件夾下。bus總線需要指明devicedriver匹配的規(guī)則,例如platform busname進行匹配,而usb則以id進行匹配。對于bus_register在前文中講述多,這里不多此一舉了。那主要來看usb bus的特征。

struct bus_type usb_bus_type = {

.name =      "usb",              --定義busname,這個也會是在/sys/bus下看見的文件夾得名稱。

    .match =    usb_device_match,   --定義busdevicedriver的匹配規(guī)則

    .uevent =   usb_uevent,         --usb的觸發(fā)事件處理。

};

下面來看usb的匹配規(guī)則函數(shù)。不過有一點需要說明的是一般usb的驅(qū)動為usbinterface的驅(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中的ididVendor ,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ù)。下面就是cdevops操作集。

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é)

對于大牛們而言,也許這些東西很簡單,不過對于我這個菜鳥而言,卻是那么的難弄。在此本文也僅僅是對自己學習的一個記錄。

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多