<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    posts - 189,comments - 115,trackbacks - 0

    2009-05-25 | 嵌入式Linux下USB驅(qū)動程序的設(shè)計

     
    標(biāo)簽: Linux  嵌入式  USB  驅(qū)動 

    一、引言

        USB(Universal Serial Bus)即通用串行總線,是一種全新的雙向同步傳輸?shù)闹С譄岵灏蔚臄?shù)據(jù)傳輸總線,其目的是為了提供一種兼容不同速度的、可擴充的并且使用方便的外圍設(shè)備接口,同時也是為了解決計算機接口的太多的弊端而設(shè)計的。一個USB系統(tǒng)主要有三部分組成:USB互連、USB主機、USB設(shè)備三部分組成的,其結(jié)構(gòu)如圖1 所示。在編寫USB設(shè)備驅(qū)動程序設(shè)計時,可以分為三部分編寫:主機端設(shè)備驅(qū)動程序、主機控制器驅(qū)動程序設(shè)計和設(shè)備端驅(qū)動程序三部分,在本文中重點介紹主機端驅(qū)動程序的設(shè)計。

        二、USB設(shè)備驅(qū)動程序的設(shè)計

        USB設(shè)備驅(qū)動程序的設(shè)計包括主機端設(shè)備驅(qū)動程序設(shè)計、主機控制器驅(qū)動程序設(shè)計和設(shè)備端驅(qū)動程序設(shè)計三部分組成。主機端設(shè)備驅(qū)動程序就是通常說的設(shè)備驅(qū)動程序,它是主機環(huán)境中為用戶應(yīng)用程序提供一個訪問USB外設(shè)的接口。Linux為這部分驅(qū)動程序提供編程接口,驅(qū)動程序設(shè)計者只要按照需求編寫驅(qū)動程序框架,通過調(diào)用操作系統(tǒng)提供的API接口函數(shù)可以完成對USB外設(shè)的特定訪問。

        主機控制驅(qū)動主要是對USB主機控制器的驅(qū)動,在大多數(shù)PC環(huán)境下,主機控制器都是由操作系統(tǒng)提供。嵌入式設(shè)備一般都沒有USB主機控制器,只是工作在 Slave模式下。如果要使USB具有主機功能,那么設(shè)備中需要選用一個帶主機控制器的USB接口控制芯片,同時自己還要有實現(xiàn)該主機控制器的驅(qū)動程序。目前Linux內(nèi)核中只提供USB主機控制器的開放主機控制器和通用主機控制器接口兩種規(guī)格,而這兩種規(guī)格主要用在PC架構(gòu)中。USB主機端驅(qū)動程序與主機控制器的結(jié)構(gòu)如圖2所示。其中USB核是Linux的一個子模塊,集中定義了一組USB相關(guān)的數(shù)據(jù)結(jié)構(gòu)、宏以及API函數(shù)。

        USB設(shè)備驅(qū)動程序是常說的設(shè)備固件程序的一部分,提供設(shè)備信息與主機的通信接口。設(shè)備端USB驅(qū)動程序設(shè)計由以下幾部分處理程序組成。初始化例程:完成描述符指針、端點、配置改變等操作。數(shù)據(jù)傳輸例程:完成控制傳輸、批量傳輸、中斷傳輸及同步傳輸?shù)葌鬏敺绞较碌臄?shù)據(jù)收發(fā)工作。標(biāo)準(zhǔn)設(shè)備處理請求:處理標(biāo)準(zhǔn)設(shè)備請求。廠商請求處理:處理生產(chǎn)商指定請求。其他操作:處理主機發(fā)出的端口復(fù)位、配置改變等操作。

       1.USB設(shè)備驅(qū)動程序框架

        USB驅(qū)動程序首先要向Linux內(nèi)核注冊自己,并告訴系統(tǒng)它所支持的設(shè)備類型以及它所支持的操作。這些信息通過一個usb_driver結(jié)構(gòu)來傳遞。usb_driver結(jié)構(gòu)如下:

        static struct usb_driver skel_driver = {

        name: "skeleton";/*驅(qū)動程序的名稱*/

        probe: skel_probe; /*設(shè)備列舉時被調(diào)用*/

        disconnect: skel_disconnect; /*設(shè)備被卸載時被調(diào)用*/

        fops: &skel_fops; /*指向一個file_operation結(jié)構(gòu),內(nèi)核通過它來訪問驅(qū)動程序的文件操作函數(shù),與用戶程序的read、write等操作進(jìn)行交互*/

        minor USB_SKEL_MINOR_BASE; /*指向設(shè)備的次設(shè)備號,用于系統(tǒng)識別主設(shè)備號相同的設(shè)備(即一個驅(qū)動程序可以同時支持多個USB設(shè)備*/

        id_table: skel_table; /*保存設(shè)備的廠商ID和產(chǎn)品ID,作為該設(shè)備的唯一標(biāo)識,驅(qū)動程序向系統(tǒng)注冊后,當(dāng)下次插入時,系統(tǒng)根據(jù)這個標(biāo)識查找正確的驅(qū)動程序,實現(xiàn)設(shè)備的即插即用*/

        };

        static struct file_operation skel_fops={

        {

        owner:THIS_MODULE,

        read:skel_read,

        write:skel_write,

        ioctl:skel_ioctl,

        open:skel_open,

        release:skel_release,

        };

        (1)注冊和注銷

        USB驅(qū)動程序注冊,就是把在初始化函數(shù)中填好的use_driver結(jié)構(gòu)作為參數(shù)傳遞給
       
        use_register()函數(shù)即可,函數(shù)的調(diào)用方法為:

        result=usb_register(&skel_driver);

        當(dāng)要從系統(tǒng)卸載驅(qū)動程序時,也是將use_driver結(jié)構(gòu)作為參數(shù)傳遞給usb_deregister 函數(shù)處理。 函數(shù)的調(diào)用格式為:

        static void __exit usb_skel_exit(void)

        { /* deregister this driver with the USB subsystem */

        usb_deregister(&skel_driver);

        }

        module_exit(usb_skel_exit);

        當(dāng)USB設(shè)備插入時,為了使linux-hotplug(Linux中PCI、USB等設(shè)備熱插拔支持)系統(tǒng)自動裝載驅(qū)動程序,需要創(chuàng)建一個MODULE_DEVICE_TABLE。核心代碼如下(這個模塊僅支持某一特定設(shè)備):

        /* table of devices that work with this driver */

        static struct usb_device_id skel_table [] = {

        { USB_DEVICE(USB_SKEL_VENDOR_ID,

        USB_SKEL_PRODUCT_ID) },

        { } /* Terminating entry */

        };

        MODULE_DEVICE_TABLE (usb, skel_table);

        USB_DEVICE宏利用廠商ID和產(chǎn)品ID提供了一個設(shè)備的唯一標(biāo)識。當(dāng)系統(tǒng)插入一個ID匹配的USB設(shè)備到USB總線時,驅(qū)動會在USB core中注冊,驅(qū)動程序中probe 函數(shù)也就會被調(diào)用。usb_device 結(jié)構(gòu)指針、接口號和接口ID都會被傳遞到函數(shù)中。

        (2)probe()函數(shù)

        probe()函數(shù)的編寫格式為:static void * skel_probe(struct usb_device *dev, unsigned int ifnum, const struct usb_device_id *id);驅(qū)動程序需要確認(rèn)插入的設(shè)備是否可以被接受,如果不接受,或者在初始化的過程中發(fā)生任何錯誤,probe()函數(shù)返回一個NULL值。否則返回一個含有設(shè)備驅(qū)動程序狀態(tài)的指針,通過這個指針,就可以訪問所有結(jié)構(gòu)中的回調(diào)函數(shù)。

        在驅(qū)動程序里,最后一點是要注冊devfs(設(shè)備文件系統(tǒng))。首先創(chuàng)建一個緩沖用來保存那些被發(fā)送給USB設(shè)備的數(shù)據(jù)和那些從設(shè)備上接受的數(shù)據(jù),并為設(shè)備傳輸創(chuàng)建一個USB請求塊(URB)以向設(shè)備寫入數(shù)據(jù),同時USB urb 被初始化,然后在devfs子系統(tǒng)中注冊設(shè)備,允許devfs用戶訪問USB的設(shè)備。注冊過程如下:

        /* initialize the devfs node for this device and register it */

        sprintf(name, "skel%d", skel->minor);

        skel->devfs = devfs_register (usb_devfs_handle, name, DEVFS_FL_DEFAULT, USB_MAJOR, USB_SKEL_MINOR_BASE + skel->minor, S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH, &skel_fops, NULL);

        如果devfs_register函數(shù)失敗, devfs子系統(tǒng)會將此情況報告給用戶。如果設(shè)備從USB總線拔掉,設(shè)備指針會調(diào)用disconnect 函數(shù)。驅(qū)動程序就需要清除那些被分配了的所有私有數(shù)據(jù)、關(guān)閉urbs,并且從devfs上注銷調(diào)自己。調(diào)用函數(shù)的格式為:

        /* remove our devfs node */

        devfs_unregister(skel->devfs);

        現(xiàn)在,skeleton驅(qū)動就已經(jīng)和設(shè)備綁定上了,任何用戶態(tài)程序要操作此設(shè)備都可以通過file_operations結(jié)構(gòu)所定義的函數(shù)進(jìn)行了。

        (3)open()、write()和read()函數(shù)

        首先,要打開此設(shè)備。在open()函數(shù)中MODULE_INC_USE_COUNT 宏是一個關(guān)鍵,它起到一個計數(shù)的作用,有一個用戶態(tài)程序打開一個設(shè)備,計數(shù)器就加1。例如,以模塊方式加入一個驅(qū)動,若計數(shù)器不為零,就說明仍然有用戶程序在使用此驅(qū)動,這時候,就不能通過rmmod命令卸載驅(qū)動模塊了。

        /* increment our usage count for the module */

        MOD_INC_USE_COUNT;

        ++skel->open_count;

        /* save our object in the file's private structure */

        file->private_data = skel;

        當(dāng)open完設(shè)備后,read()、write()函數(shù)就可以收、發(fā)數(shù)據(jù)了。

        read()函數(shù)首先從open()函數(shù)中保存的fi。

        Write()函數(shù)和read()函數(shù)是完成驅(qū)動對讀寫等操作的響應(yīng)。在skel_write中,一個FILL_BULK_URB函數(shù),就完成了urb 系統(tǒng)callbak和的skel_write_bulk_callback之間的聯(lián)系。注意skel_write_bulkcallback是中斷方式,所以要注意時間不能太久,本程序中它就只是報告一些urb的狀態(tài)等。 read 函數(shù)與write 函數(shù)稍有不同在于:程序并沒有用urb 將數(shù)據(jù)從設(shè)備傳送到驅(qū)動程序,而是用usb_bulk_msg 函數(shù)代替,這個函數(shù)能夠不需要創(chuàng)建urbs 和操作urb函數(shù)的情況下,來發(fā)送數(shù)據(jù)給設(shè)備,或者從設(shè)備來接收數(shù)據(jù)。調(diào)用usb_bulk_msg函數(shù)并傳到一個存儲空間,用來緩沖和放置驅(qū)動收到的數(shù)據(jù),若沒有收到數(shù)據(jù)表示失敗并返回一個錯誤信息。

        usb_bulk_msg函數(shù):當(dāng)對usb設(shè)備進(jìn)行一次讀或者寫時,usb_bulk_msg 函數(shù)是非常有用的; 然而, 當(dāng)需要連續(xù)地對設(shè)備進(jìn)行讀/寫時,應(yīng)建立一個自己的urbs,同時將urbs 提交給USB子系統(tǒng)。

        skel_disconnect函數(shù):當(dāng)釋放設(shè)備文件句柄時,這個函數(shù)會被調(diào)用。
        MOD_DEC_USE_COUNT宏也會被調(diào)用到(和MOD_INC_USE_COUNT剛好對應(yīng),它減少一個計數(shù)器),首先確認(rèn)當(dāng)前是否有其他的程序正在訪問這個設(shè)備,如果是最后一個用戶在使用,可以關(guān)閉任何正在發(fā)生的寫,操作如下:

        /* decrement our usage count for the device */

        --skel->open_count;

        if (skel->open_count <= 0) {

        /* shutdown any bulk writes that might be

        going on */

        usb_unlink_urb (skel->write_urb);

        skel->open_count = 0;

        }

        /* decrement our usage count for the module */

        MOD_DEC_USE_COUNT;

        USB設(shè)備可以在任何時間點從系統(tǒng)中取走,即使程序目前正在訪問它。USB驅(qū)動程序必須要能夠很好地處理解決此問題,它需要能夠切斷任何當(dāng)前的讀寫,同時通知用戶空間程序:USB設(shè)備已經(jīng)被取走。

       2.設(shè)計實例

        下面通過介紹鍵盤飛梭驅(qū)動程序的實例來讓讀者更好的理解USB驅(qū)動程序的工作原理,實現(xiàn)代碼如下:

        /*需要的頭文件*/

        #include <linux/kernel.h>

        #include <linux/slab.h>            

        #include <linux/module.h>

        #include <linux/input.h>

        #include <linux/init.h>

        #include <linux/usb.h>

        #include <linux/kbd_ll.h>

        /* 驅(qū)動程序版本信息*/

        #define DRIVER_VERSION ""

        #define DRIVER_AUTHOR " TGE HOTKEY "

        #define DRIVER_DESC "USB HID Tge hotkey driver"

        #define USB_HOTKEY_VENDOR_ID 0x07e4

        #define USB_HOTKEY_PRODUCT_ID 0x9473

        /*廠商和產(chǎn)品ID信息就是/proc/bus/usb/devices中看到的值,通過cat/proc/bus/usb/devices得到當(dāng)前系統(tǒng)探測到的USB總線上的設(shè)備信息。它包括Vendor、ProdID、Product等*/

        MODULE_AUTHOR( DRIVER_AUTHOR );

        MODULE_DESCRIPTION( DRIVER_DESC );

        /*此結(jié)構(gòu)來自內(nèi)核中drivers/usb/usbkbd.c*/

        struct usb_kbd {

        struct input_dev dev;

        struct usb_device *usbdev;

        unsigned char new[8];

        unsigned char old[8];

        struct urb irq, led;

        struct usb_ctrlrequest dr;

        unsigned char leds, newleds;

        char name[128];

        int open;

        };

        static void usb_kbd_irq(struct urb *urb) /*urb為USB請求塊*/

        {

        struct usb_kbd *kbd = urb->context;

        int *new;

        new = (int *) kbd->new;

        if(kbd->new[0] == (char)0x01)

        {

        if(((kbd->new[1]>>4)&0x0f)!=0x7)

        {

        handle_scancode(0xe0,1);

        handle_scancode(0x4b,1);

        handle_scancode(0xe0,0);

        handle_scancode(0x4b,0);

        }

        else

        { handle_scancode(0xe0,1);

        handle_scancode(0x4d,1);

        handle_scancode(0xe0,0);

        handle_scancode(0x4d,0);

        }

        }

        printk("new=%x %x %x %x %x %x %x %x", kbd->new[0],kbd->new[1],kbd->new[2],kbd->new[3],

        kbd->new[4],kbd->new[5],kbd->new[6],kbd->new[7]);

        }

        static void *usb_kbd_probe(struct usb_device *dev, unsigned int ifnum, const struct usb_device_id *id)

        {

        struct usb_interface *iface;

        struct usb_interface_descriptor *interface;

        struct usb_endpoint_descriptor *endpoint;

        struct usb_kbd *kbd;

        int pipe, maxp;

        iface = &dev->actconfig->interface[ifnum];

        interface = &iface->altsetting[iface->act_altsetting];

        if ((dev->descriptor.idVendor != USB_HOTKEY_VENDOR_ID) || (dev->descriptor.idProduct != USB_HOTKEY_PRODUCT_ID) || (ifnum != 1))

        {

        return NULL;

        }

        if (dev->actconfig->bNumInterfaces != 2)

        {

        return NULL;

        }

        if (interface->bNumEndpoints != 1) return NULL;

        endpoint = interface->endpoint + 0;

        pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);

        maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));

        usb_set_protocol(dev, interface->bInterfaceNumber, 0);

        usb_set_idle(dev, interface->bInterfaceNumber, 0, 0);

        printk(KERN_INFO "GUO: Vid = %.4x, Pid = %.4x, Device = %.2x, ifnum = %.2x, bufCount = %.8x\\n", dev->descriptor.idVendor,dev->descriptor.idProduct,dev->descriptor.bcdDevice, ifnum, maxp);

        if (!(kbd = kmalloc(sizeof(struct usb_kbd), GFP_KERNEL))) return NULL;

        memset(kbd, 0, sizeof(struct usb_kbd));

        kbd->usbdev = dev;

        FILL_INT_URB(&kbd->irq, dev, pipe, kbd->new, maxp > 8 ? 8 : maxp, usb_kbd_irq,kbd, endpoint->bInterval); kbd->irq.dev = kbd->usbdev;

        if (dev->descriptor.iManufacturer) usb_string(dev, dev->descriptor.iManufacturer, kbd->name, 63);

        if (usb_submit_urb(&kbd->irq)) {

        kfree(kbd); return NULL; }

        printk(KERN_INFO "input%d: %s on usb%d:%d.%d\\n", kbd->dev.number, kbd->name, dev->bus->busnum, dev->devnum, ifnum);

        return kbd; }

        static void usb_kbd_disconnect(struct usb_device *dev, void *ptr)

        {

        struct usb_kbd *kbd = ptr;

        usb_unlink_urb(&kbd->irq);

        kfree(kbd);

        }

        static struct usb_device_id usb_kbd_id_table [] = {

        { USB_DEVICE(USB_HOTKEY_VENDOR_ID, USB_HOTKEY_PRODUCT_ID) },

        { } /* Terminating entry */

        };

        MODULE_DEVICE_TABLE (usb, usb_kbd_id_table);

        static struct usb_driver usb_kbd_driver = {

        name: "Hotkey",

        probe: usb_kbd_probe,

        disconnect: usb_kbd_disconnect,

        id_table: usb_kbd_id_table,

        NULL,

        };

        static int __init usb_kbd_init(void)

        {

        usb_register(&usb_kbd_driver);

        info(DRIVER_VERSION ":" DRIVER_DESC);

        return 0;

        }

        static void __exit usb_kbd_exit(void)

        {

        usb_deregister(&usb_kbd_driver);

        }

        module_init(usb_kbd_init);

        module_exit(usb_kbd_exit);

        三、結(jié)語

        USB規(guī)范是一門比較新的技術(shù),接口使用方便,但是驅(qū)動程序的設(shè)計較復(fù)雜。上面介紹了USB設(shè)備驅(qū)動程序的設(shè)計,主要分析了主機端驅(qū)動程序的設(shè)計,并且給出了一個編寫USB驅(qū)動程序的實例。

        參考文獻(xiàn)

        1.劉崢嶸.嵌入式Linux應(yīng)用開發(fā)詳界解.機械工業(yè)出版社,2004

        2.周立功.ARM嵌入式Linux系統(tǒng)構(gòu)件與驅(qū)動開發(fā)范例.北京航天航空大學(xué)出版社,2006

        3.劉淼.嵌入式系統(tǒng)接口設(shè)計與Linux驅(qū)動程序開發(fā).北京航天航空大學(xué)出版社,2006

    posted on 2009-06-11 21:29 MEYE 閱讀(565) 評論(0)  編輯  收藏

    只有注冊用戶登錄后才能發(fā)表評論。


    網(wǎng)站導(dǎo)航:
     
    主站蜘蛛池模板: 成人无遮挡裸免费视频在线观看| 国产精品玖玖美女张开腿让男人桶爽免费看| 91在线亚洲精品专区| 久久精品国产亚洲麻豆| 日韩亚洲变态另类中文| 国产gv天堂亚洲国产gv刚刚碰| 在线观看亚洲成人| 久久亚洲AV永久无码精品| 国产午夜亚洲精品午夜鲁丝片| 国产亚洲AV夜间福利香蕉149| 亚洲日韩v无码中文字幕| 国产成人亚洲精品青草天美| 亚洲va久久久噜噜噜久久 | 久久国产乱子伦精品免费不卡 | 亚洲综合激情五月色一区| 亚洲精品中文字幕无乱码麻豆| 亚洲国产精品免费观看| 亚洲精品乱码久久久久蜜桃| 香蕉视频在线观看免费| 国产高清对白在线观看免费91| a毛片免费全部播放完整成| 午夜免费啪视频在线观看| 毛片免费全部播放无码| 成年免费大片黄在线观看岛国| 午夜寂寞在线一级观看免费| 日本xxwwxxww在线视频免费| 亚洲精品国产自在久久| 亚洲av中文无码乱人伦在线播放| 精品亚洲成a人片在线观看少妇| 33333在线亚洲| 国产亚洲精品欧洲在线观看| 99久久免费国产精品热| 中文字幕免费在线看线人| 免费鲁丝片一级在线观看| 亚洲国产精品激情在线观看| 亚洲国产精华液网站w| 亚洲综合一区二区精品久久| 成人毛片手机版免费看| 免费一级黄色毛片| 国产美女被遭强高潮免费网站| 亚洲AV无码之日韩精品|