久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

怎樣寫linux下的USB設(shè)備驅(qū)動(dòng)程序

 fhr625 2014-01-23

引言
隨著人們生活水平的提高,,我們用到的USB設(shè)備也越來越多,但是Linux在硬件配置上仍然沒有做到完全即插即用,,對(duì)于Linux怎樣配置和使用他們,,也 越來越成為困擾我們的一大問題,;本文的目地是使大家了解怎樣編制USB設(shè)備驅(qū)動(dòng),為更好地配置和使用USB設(shè)備提供方便,;對(duì)于希望開發(fā)Linux系統(tǒng)下 USB設(shè)備驅(qū)動(dòng)的人員,,也可作為進(jìn)一步學(xué)習(xí)USB驅(qū)動(dòng)的大體架構(gòu)進(jìn)而編寫出特殊USB設(shè)備的驅(qū)動(dòng)程序。


USB基礎(chǔ)知識(shí)
USB是英文Universal Serial Bus的縮寫,,意為通用串行總線,。USB最初是為了替代許多不同的低速總線(包括并行、串行和鍵盤連接)而設(shè)計(jì)的,,它以單一類型的總線連接各種不同的類型 的設(shè)備,。USB的發(fā)展已經(jīng)超越了這些低速的連接方式,它現(xiàn)在可以支持幾乎所有可以連接到PC上的設(shè)備,。最新的USB規(guī)范修訂了理論上高達(dá)480Mbps的 高速連接,。Linux內(nèi)核支持兩種主要類型的USB驅(qū)動(dòng)程序:宿主系統(tǒng)上的驅(qū)動(dòng)程序和設(shè)備上的驅(qū)動(dòng)程序,從宿主的觀點(diǎn)來看(一個(gè)普通的宿主也就是一個(gè)PC 機(jī)),,宿主系統(tǒng)的USB設(shè)備驅(qū)動(dòng)程序控制插入其中的USB設(shè)備,,而USB設(shè)備的驅(qū)動(dòng)程序控制該設(shè)備如何作為一個(gè)USB設(shè)備和主機(jī)通信。本文將詳細(xì)介紹運(yùn)行 于PC機(jī)上的USB系統(tǒng)是如何運(yùn)作的,。并同時(shí)用USB驅(qū)動(dòng)程序的框架程序當(dāng)例子作詳細(xì)的說明,,我們?cè)诖宋闹胁挥懻揢SB器件的驅(qū)動(dòng)程序。

 

USB驅(qū)動(dòng)程序基礎(chǔ)
在動(dòng)手寫USB驅(qū)動(dòng)程序這前,,讓我們先看看寫的USB驅(qū)動(dòng)程序在內(nèi)核中的結(jié)構(gòu),,如下圖:


 
USB驅(qū)動(dòng)程序存在于不同的內(nèi)核子系統(tǒng)和USB硬件控制器之間,USB核心為USB驅(qū)動(dòng)程序提供了一個(gè)用于訪問和控制USB硬件的接口,,而不必考慮系統(tǒng)當(dāng) 前存在的各種不同類型的USB硬件控制器,。USB是一個(gè)非常復(fù)雜的設(shè)備,linux內(nèi)核為我們提供了一個(gè)稱為USB的核心的子系統(tǒng)來處理大部分的復(fù)雜 性,,USB設(shè)備包括配置(configuration),、接口(interface)和端點(diǎn)(endpoint),USB設(shè)備綁定到接口上,,而不是整個(gè) USB設(shè)備,。如下圖所示:


 
USB通信最基本的形式是通過端點(diǎn)(USB端點(diǎn)分中斷、批量,、等時(shí),、控制四種,每種用途不同),,USB端點(diǎn)只能往一個(gè)方向傳送數(shù)據(jù),,從主機(jī)到設(shè)備或者從設(shè) 備到主機(jī),端點(diǎn)可以看作是單向的管道(pipe)。所以我們可以這樣認(rèn)為:設(shè)備通常具有一個(gè)或者更多的配置,,配置經(jīng)常具有一個(gè)或者更多的接口,,接口通常具 有一個(gè)或者更多的設(shè)置,接口沒有或具有一個(gè)以上的端點(diǎn),。驅(qū)動(dòng)程序把驅(qū)動(dòng)程序?qū)ο笞?cè)到USB子系統(tǒng)中,,稍后再使用制造商和設(shè)備標(biāo)識(shí)來判斷是否已經(jīng)安裝了硬 件。USB核心使用一個(gè)列表(是一個(gè)包含制造商ID和設(shè)備號(hào)ID的一個(gè)結(jié)構(gòu)體)來判斷對(duì)于一個(gè)設(shè)備該使用哪一個(gè)驅(qū)動(dòng)程序,,熱插撥腳本使用它來確定當(dāng)一個(gè)特 定的設(shè)備插入到系統(tǒng)時(shí)該自動(dòng)裝載哪一個(gè)驅(qū)動(dòng)程序,。
上面我們簡要說明了驅(qū)動(dòng)程序的基本理論,在寫一個(gè)設(shè)備驅(qū)動(dòng)程序之前,,我們還要了解以下兩個(gè)概念:模塊和設(shè)備文件,。
模塊:是在內(nèi)核空間運(yùn)行的程序,實(shí)際上是一種目標(biāo)對(duì)象文件,,沒有鏈接,,不能獨(dú)立運(yùn)行,但是可以裝載到系統(tǒng)中作為內(nèi)核的一部分運(yùn)行,,從而可以動(dòng)態(tài)擴(kuò)充內(nèi)核的 功能,。模塊最主要的用處就是用來實(shí)現(xiàn)設(shè)備驅(qū)動(dòng)程序。Linux下對(duì)于一個(gè)硬件的驅(qū)動(dòng),,可以有兩種方式:直接加載到內(nèi)核代碼中,,啟動(dòng)內(nèi)核時(shí)就會(huì)驅(qū)動(dòng)此硬件設(shè) 備。另一種就是以模塊方式,,編譯生成一個(gè).ko文件(在2.4以下內(nèi)核中是用.o作模塊文件,,我們以2.6的內(nèi)核為準(zhǔn),以下同),。當(dāng)應(yīng)用程序需要時(shí)再加載 到內(nèi)核空間運(yùn)行,。所以我們所說的一個(gè)硬件的驅(qū)動(dòng)程序,通常指的就是一個(gè)驅(qū)動(dòng)模塊,。
設(shè)備文件:對(duì)于一個(gè)設(shè)備,,它可以在/dev下面存在一個(gè)對(duì)應(yīng)的邏輯設(shè)備節(jié)點(diǎn),這個(gè)節(jié)點(diǎn)以文件的形式存在,,但它不是普通意義上的文件,,它是設(shè)備文件,更確切 的說,,它是設(shè)備節(jié)點(diǎn),。這個(gè)節(jié)點(diǎn)是通過mknod命令建立的,其中指定了主設(shè)備號(hào)和次設(shè)備號(hào),。主設(shè)備號(hào)表明了某一類設(shè)備,,一般對(duì)應(yīng)著確定的驅(qū)動(dòng)程序,;次設(shè)備 號(hào)一般是區(qū)分不同屬性,例如不同的使用方法,,不同的位置,,不同的操作,。這個(gè)設(shè)備號(hào)是從/proc/devices文件中獲得的,,所以一般是先有驅(qū)動(dòng)程序在 內(nèi)核中,才有設(shè)備節(jié)點(diǎn)在目錄中,。這個(gè)設(shè)備號(hào)(特指主設(shè)備號(hào))的主要作用,,就是聲明設(shè)備所使用的驅(qū)動(dòng)程序。驅(qū)動(dòng)程序和設(shè)備號(hào)是一一對(duì)應(yīng)的,,當(dāng)你打開一個(gè)設(shè)備 文件時(shí),,操作系統(tǒng)就已經(jīng)知道這個(gè)設(shè)備所對(duì)應(yīng)的驅(qū)動(dòng)程序。對(duì)于一個(gè)硬件,,Linux是這樣來進(jìn)行驅(qū)動(dòng)的:首先,,我們必須提供一個(gè).ko的驅(qū)動(dòng)模塊文件。我們 要使用這個(gè)驅(qū)動(dòng)程序,,首先要加載它,,我們可以用insmod xxx.ko,這樣驅(qū)動(dòng)就會(huì)根據(jù)自己的類型(字符設(shè)備類型或塊設(shè)備類型,,例如鼠標(biāo)就是字符設(shè)備而硬盤就是塊設(shè)備)向系統(tǒng)注冊(cè),,注冊(cè)成功系統(tǒng)會(huì)反饋一個(gè)主設(shè) 備號(hào),這個(gè)主設(shè)備號(hào)就是系統(tǒng)對(duì)它的唯一標(biāo)識(shí),。驅(qū)動(dòng)就是根據(jù)此主設(shè)備號(hào)來創(chuàng)建一個(gè)一般放置在/dev目錄下的設(shè)備文件,。在我們要訪問此硬件時(shí),就可以對(duì)設(shè)備 文件通過open,、read,、write、close等命令進(jìn)行,。而驅(qū)動(dòng)就會(huì)接收到相應(yīng)的read,、write操作而根據(jù)自己的模塊中的相應(yīng)函數(shù)進(jìn)行操作 了。

 

USB驅(qū)動(dòng)程序?qū)嵺`
了解了上述理論后,,我們就可以動(dòng)手寫驅(qū)動(dòng)程序,,如果你基本功好,而且寫過linux下的硬件驅(qū)動(dòng),,USB的硬件驅(qū)動(dòng)和pci_driver很類似,,那么寫 USB的驅(qū)動(dòng)就比較簡單了,如果你只是大體了解了linux的硬件驅(qū)動(dòng),,那也不要緊,,因?yàn)樵趌inux的內(nèi)核源碼中有一個(gè)框架程序可以拿來借用一下,,這個(gè) 框架程序在/usr/src/~(你的內(nèi)核版本,以下同)/drivers/usb下,,文件名為usb-skeleton.c,。寫一個(gè)USB的驅(qū)動(dòng)程序最 基本的要做四件事:驅(qū)動(dòng)程序要支持的設(shè)備、注冊(cè)USB驅(qū)動(dòng)程序,、探測和斷開,、提交和控制urb(USB請(qǐng)求塊)(當(dāng)然也可以不用urb來傳輸數(shù)據(jù),下文我 們會(huì)說到),。
驅(qū)動(dòng)程序支持的設(shè)備:有一個(gè)結(jié)構(gòu)體struct usb_device_id,,這個(gè)結(jié)構(gòu)體提供了一列不同類型的該驅(qū)動(dòng)程序支持的USB設(shè)備,對(duì)于一個(gè)只控制一個(gè)特定的USB設(shè)備的驅(qū)動(dòng)程序來說,,struct usb_device_id表被定義為:
/* 驅(qū)動(dòng)程序支持的設(shè)備列表 */
static struct usb_device_id skel_table [] = {
    { USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) },
    { }                    /* 終止入口 */
};
MODULE_DEVICE_TABLE (usb, skel_table);
對(duì)于PC驅(qū)動(dòng)程序,,MODULE_DEVICE_TABLE是必需的,而且usb必需為該宏的第一個(gè)值,,而USB_SKEL_VENDOR_ID和 USB_SKEL_PRODUCT_ID就是這個(gè)特殊設(shè)備的制造商和產(chǎn)品的ID了,,我們?cè)诔绦蛑邪讯x的值改為我們這款USB的,如:
/* 定義制造商和產(chǎn)品的ID號(hào) */
#define USB_SKEL_VENDOR_ID    0x1234
#define USB_SKEL_PRODUCT_ID    0x2345
這兩個(gè)值可以通過命令lsusb,,當(dāng)然你得先把USB設(shè)備先插到主機(jī)上了,。或者查看廠商的USB設(shè)備的手冊(cè)也能得到,,在我機(jī)器上運(yùn)行l(wèi)susb是這樣的結(jié)果:
Bus 004 Device 001: ID 0000:0000 
Bus 003 Device 002: ID 1234:2345  Abc  Corp.
Bus 002 Device 001: ID 0000:0000 
Bus 001 Device 001: ID 0000:0000
得到這兩個(gè)值后把它定義到程序里就可以了,。
注冊(cè)USB驅(qū)動(dòng)程序:所有的USB驅(qū)動(dòng)程序都必須創(chuàng)建的結(jié)構(gòu)體是struct usb_driver。這個(gè)結(jié)構(gòu)體必須由USB驅(qū)動(dòng)程序來填寫,,包括許多回調(diào)函數(shù)和變量,,它們向USB核心代碼描述USB驅(qū)動(dòng)程序。創(chuàng)建一個(gè)有效的 struct usb_driver結(jié)構(gòu)體,,只須要初始化五個(gè)字段就可以了,,在框架程序中是這樣的:
static struct usb_driver skel_driver = {
    .owner =    THIS_MODULE,
    .name =        "skeleton",
    .probe =    skel_probe,
    .disconnect =    skel_disconnect,
    .id_table =    skel_table,
};
struct module *owner :指向該驅(qū)動(dòng)程序的模塊所有者的指針。USB核心使用它來正確地對(duì)該USB驅(qū)動(dòng)程序進(jìn)行引用計(jì)數(shù),,使它不會(huì)在不合適的時(shí)刻被卸載掉,,這個(gè)變量應(yīng)該被設(shè)置為THIS_MODULE宏。
const char *name:指向驅(qū)動(dòng)程序名字的指針,,在內(nèi)核的所有USB驅(qū)動(dòng)程序中它必須是唯一的,,通常被設(shè)置為和驅(qū)動(dòng)程序模塊名相同的名字。
int (*probe) (struct usb_interface *intf,const struct usb_device_id *id):這個(gè)是指向USB驅(qū)動(dòng)程序中的探測函數(shù)的指針,。當(dāng)USB核心認(rèn)為它有一個(gè)接口(usb_interface)可以由該驅(qū)動(dòng)程序處理時(shí),,這個(gè)函數(shù) 被調(diào)用。
void (disconnect)(struct usb_interface *intf):指向USB驅(qū)動(dòng)程序中的斷開函數(shù)的指針,,當(dāng)一個(gè)USB接口(usb_interface)被從系統(tǒng)中移除或者驅(qū)動(dòng)程序正在從USB核心中卸載時(shí),,USB核心將調(diào)用這個(gè)函數(shù),。
const struct usb_device_id *id_table:指向ID設(shè)備表的指針,這個(gè)表包含了一列該驅(qū)動(dòng)程序可以支持的USB設(shè)備,,如果沒有設(shè)置這個(gè)變量,,USB驅(qū)動(dòng)程序中的探測回調(diào)函數(shù)就不會(huì)被調(diào)用。
在這個(gè)結(jié)構(gòu)體中還有其它的幾個(gè)回調(diào)函數(shù)不是很常用,,這里就不一一說明了,。以struct usb_driver 指針為參數(shù)的usb_register_driver函數(shù)調(diào)用把struct usb_driver注冊(cè)到USB核心。一般是在USB驅(qū)動(dòng)程序的模塊初始化代碼中完成這個(gè)工作的:
static int __init usb_skel_init(void)
{
    int result;

    /* 驅(qū)動(dòng)程序注冊(cè)到USB子系統(tǒng)中*/
    result = usb_register(&skel_driver);
    if (result)
        err("usb_register failed. Error number %d", result);

    return result;
}
當(dāng)USB驅(qū)動(dòng)程序?qū)⒁恍堕_時(shí),,需要把struct usb_driver從內(nèi)核中注銷,。通過調(diào)用usb_deregister_driver來完成這個(gè)工作,,當(dāng)調(diào)用發(fā)生時(shí),,當(dāng)前綁定到該驅(qū)動(dòng)程序上的任何USB接口都被斷開,斷開函數(shù)將被調(diào)用:
static void __exit usb_skel_exit(void)
{
    /* 從子系統(tǒng)注銷驅(qū)動(dòng)程序 */
    usb_deregister(&skel_driver);
}
探測和斷開:當(dāng)一個(gè)設(shè)備被安裝而USB核心認(rèn)為該驅(qū)動(dòng)程序應(yīng)該處理時(shí),,探測函數(shù)被調(diào)用,,探測函數(shù)檢查傳遞給它的設(shè)備信 息,確定驅(qū)動(dòng)程序是否真的適合該設(shè)備,。當(dāng)驅(qū)動(dòng)程序因?yàn)槟撤N原因不應(yīng)該控制設(shè)備時(shí),,斷開函數(shù)被調(diào)用,它可以做一些清理工作,。探測回調(diào)函數(shù)中,,USB驅(qū)動(dòng)程序 初始化任何可能用于控制USB設(shè)備的局部結(jié)構(gòu)體,它還把所需的任何設(shè)備相關(guān)信息保存到一個(gè)局部結(jié)構(gòu)體中,,下面是探測函數(shù)的部分源碼,,我們加以分析。
    /* 設(shè)置端點(diǎn)信息 */
    /* 只使用第一個(gè)批量IN和批量OUT端點(diǎn) */
    iface_desc = interface->cur_altsetting;
    for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
        endpoint = &iface_desc->endpoint[i].desc;

        if (!dev->bulk_in_endpointAddr &&
            (endpoint->bEndpointAddress & USB_DIR_IN) &&
            ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
                    == USB_ENDPOINT_XFER_BULK)) {
            /* 找到一個(gè)批量IN端點(diǎn) */
            buffer_size = endpoint->wMaxPacketSize;
            dev->bulk_in_size = buffer_size;
            dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;
            dev->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL);
            if (!dev->bulk_in_buffer) {
                err("Could not allocate bulk_in_buffer");
                goto error;
            }
        }

        if (!dev->bulk_out_endpointAddr &&
            !(endpoint->bEndpointAddress & USB_DIR_IN) &&
            ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
                    == USB_ENDPOINT_XFER_BULK)) {
            /* 找到一個(gè)批量OUT端點(diǎn) */
            dev->bulk_out_endpointAddr = endpoint->bEndpointAddress;
        }
    }
    if (!(dev->bulk_in_endpointAddr && dev->bulk_out_endpointAddr)) {
        err("Could not find both bulk-in and bulk-out endpoints");
        goto error;
    }
在探測函數(shù)里,,這個(gè)循環(huán)首先訪問該接口中存在的每一個(gè)端點(diǎn),,給該端點(diǎn)一個(gè)局部指針以便以后訪問:
for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
        endpoint = &iface_desc->endpoint[i].desc;
在一輪探測過后,我們就有了一個(gè)端點(diǎn),,在還沒有發(fā)現(xiàn)批量IN類型的端點(diǎn)時(shí),,探測該端點(diǎn)方向是否為IN,這可以通過檢查USB_DIR_IN是否包含在 bEndpointAddress端點(diǎn)變量有確定,,如果是的話,,我們?cè)谔綔y該端點(diǎn)類型是否為批量,先用 USB_ENDPOINT_XFERTYPE_MASK位掩碼來取bmAttributes變量的值,,然后探測它是否和 USB_ENDPOINT_XFER_BULK值匹配:
        if (!dev->bulk_out_endpointAddr &&
            !(endpoint->bEndpointAddress & USB_DIR_IN) &&
            ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
                    == USB_ENDPOINT_XFER_BULK))
如果所有這些探測都通過了,,驅(qū)動(dòng)程序就知道它已經(jīng)發(fā)現(xiàn)了正確的端點(diǎn)類型,可以把該端點(diǎn)的相關(guān)信息保存到一個(gè)局部結(jié)構(gòu)體中以便稍后用它來和端點(diǎn)進(jìn)行通信:
            /* 找到一個(gè)批量IN類型的端點(diǎn) */
            buffer_size = endpoint->wMaxPacketSize;
            dev->bulk_in_size = buffer_size;
            dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;
            dev->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL);
            if (!dev->bulk_in_buffer) {
                err("Could not allocate bulk_in_buffer");
                goto error;
            }
因?yàn)閁SB驅(qū)動(dòng)程序要在設(shè)備的生命周期的稍后時(shí)間獲取和接口相關(guān)聯(lián)的局部數(shù)據(jù)結(jié)構(gòu)體,,所以調(diào)用了usb_set_intfdata函數(shù),,把它保存到struct usb_interface結(jié)構(gòu)體中以便后面的訪問
    /* 把數(shù)據(jù)指針保存到這個(gè)接口設(shè)備中 */
    usb_set_intfdata(interface, dev);
我們以后調(diào)用usb_set_intfdata函數(shù)來獲取數(shù)據(jù),。當(dāng)這一切都完成后,USB驅(qū)動(dòng)程序必須在探測函數(shù)中調(diào)用usb_register_dev函數(shù)來把該設(shè)備注冊(cè)到USB核心里:
    /* 注冊(cè)設(shè)備到USB核心 */
    retval = usb_register_dev(interface, &skel_class);
    if (retval) {
        /* 有些情況下是不允許注冊(cè)驅(qū)動(dòng)程序的 */
        err("Not able to get a minor for this device.");
        usb_set_intfdata(interface, NULL);
        goto error;
    }
當(dāng)一個(gè)USB設(shè)備被斷開時(shí),,和該設(shè)備相關(guān)聯(lián)的所有資源都應(yīng)該被盡可能的清理掉,,在此時(shí),如果已在在探測函數(shù)中調(diào)用了注冊(cè)函數(shù)來為該USB設(shè)備分配了一個(gè)次 設(shè)備號(hào)話,,必須調(diào)用usb_deregister_dev函數(shù)來把次設(shè)備號(hào)交還給USB核心,。在斷開函數(shù)中,從接口獲取之前調(diào)用 usb_set_intfdata設(shè)置的任何數(shù)據(jù)也是很重要的,。然后設(shè)置struct usb_interface結(jié)構(gòu)體中的數(shù)據(jù)指針為NULL,,以防任何不適當(dāng)?shù)膶?duì)該數(shù)據(jù)的錯(cuò)誤訪問。
在探測函數(shù)中會(huì)對(duì)每一個(gè)接口進(jìn)行一次探測,,所以我們?cè)趯慤SB驅(qū)動(dòng)程序的時(shí)候,,只要做好第一個(gè)端點(diǎn),其它的端點(diǎn)就會(huì)自動(dòng)完成探測,。在探測函數(shù)中我們要注意 的是在內(nèi)核中用結(jié)構(gòu)體struct usb_host_endpoint來描述USB端點(diǎn),,這個(gè)結(jié)構(gòu)體在另一個(gè)名為struct usb_endpoint_descriptor的結(jié)構(gòu)體中包含了真正的端點(diǎn)信息,struct usb_endpoint_descriptor結(jié)構(gòu)體包含了所有的USB特定的數(shù)據(jù),,該結(jié)構(gòu)體中我們要關(guān)心的幾個(gè)字段是:
bEndpointAddress:這個(gè)是特定的USB地址,,可以結(jié)合USB_DIR_IN和USB_DIR_OUT來使用,以確定該端點(diǎn)的數(shù)據(jù)是傳向設(shè)備還是主機(jī),。
bmAttributes:這個(gè)是端點(diǎn)的類型,,這個(gè)值可以結(jié)合位掩碼USB_ENDPOINT_XFERTYPE_MASK來使用,以確定此端點(diǎn)的類型是 USB_ENDPOINT_XFER_ISOC(等時(shí)),、USB_ENDPOINT_XFER_BULK(批量),、 USB_ENDPOINT_XFER_INT的哪一種。
wMaxPacketSize:這個(gè)是端點(diǎn)一次可以處理的最大字節(jié)數(shù),,驅(qū)動(dòng)程序可以發(fā)送數(shù)量大于此值的數(shù)據(jù)到端點(diǎn),,在實(shí)際傳輸中,數(shù)據(jù)量如果大于此值會(huì)被分割,。
bInterval:這個(gè)值只有在端點(diǎn)類型是中斷類型時(shí)才起作用,,它是端點(diǎn)中斷請(qǐng)求的間隔時(shí)間,以毫秒為單位,。
提交和控制urb:當(dāng)驅(qū)動(dòng)程序有數(shù)據(jù)要發(fā)送到USB設(shè)備時(shí)(大多數(shù)情況是在驅(qū)動(dòng)程序的寫函數(shù)中),,要分配一個(gè)urb來把數(shù)據(jù)傳輸給設(shè)備:
    /* 創(chuàng)建一個(gè)urb,并且給它分配一個(gè)緩存*/
    urb = usb_alloc_urb(0, GFP_KERNEL);
    if (!urb) {
        retval = -ENOMEM;
        goto error;
    }
當(dāng)urb被成功分配后,還要?jiǎng)?chuàng)建一個(gè)DMA緩沖區(qū)來以高效的方式發(fā)送數(shù)據(jù)到設(shè)備,,傳遞給驅(qū)動(dòng)程序的數(shù)據(jù)要復(fù)制到這塊緩沖中去:
    buf = usb_buffer_alloc(dev->udev, count, GFP_KERNEL, &urb->transfer_dma);
    if (!buf) {
        retval = -ENOMEM;
        goto error;
    }

    if (copy_from_user(buf, user_buffer, count)) {
        retval = -EFAULT;
        goto error;
    }
當(dāng)數(shù)據(jù)從用戶空間正確復(fù)制到局部緩沖區(qū)后,,urb必須在可以被提交給USB核心之前被正確初始化:
    /* 初始化urb */
    usb_fill_bulk_urb(urb, dev->udev,
              usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr),
              buf, count, skel_write_bulk_callback, dev);
    urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
然后urb就可以被提交給USB核心以傳輸?shù)皆O(shè)備了:
    /* 把數(shù)據(jù)從批量OUT端口發(fā)出 */
    retval = usb_submit_urb(urb, GFP_KERNEL);
    if (retval) {
        err("%s - failed submitting write urb, error %d", __FUNCTION__, retval);
        goto error;
    }
當(dāng)urb被成功傳輸?shù)経SB設(shè)備之后,urb回調(diào)函數(shù)將被USB核心調(diào)用,,在我們的例子中,,我們初始化urb,,使它指向skel_write_bulk_callback函數(shù),以下就是該函數(shù):
static void skel_write_bulk_callback(struct urb *urb, struct pt_regs *regs)
{
    struct usb_skel *dev;

    dev = (struct usb_skel *)urb->context;

    if (urb->status &&
        !(urb->status == -ENOENT ||
          urb->status == -ECONNRESET ||
          urb->status == -ESHUTDOWN)) {
        dbg("%s - nonzero write bulk status received: %d",
            __FUNCTION__, urb->status);
    }

    /* 釋放已分配的緩沖區(qū) */
    usb_buffer_free(urb->dev, urb->transfer_buffer_length,
            urb->transfer_buffer, urb->transfer_dma);
}
有時(shí)候USB驅(qū)動(dòng)程序只是要發(fā)送或者接收一些簡單的數(shù)據(jù),,驅(qū)動(dòng)程序也可以不用urb來進(jìn)行數(shù)據(jù)的傳輸,,這是里涉及到兩個(gè)簡單的接口函數(shù):usb_bulk_msg和usb_control_msg ,在這個(gè)USB框架程序里讀操作就是這樣的一個(gè)應(yīng)用:
/* 進(jìn)行阻塞的批量讀以從設(shè)備獲取數(shù)據(jù) */
    retval = usb_bulk_msg(dev->udev,
                  usb_rcvbulkpipe(dev->udev, dev->bulk_in_endpointAddr),
                  dev->bulk_in_buffer,
                  min(dev->bulk_in_size, count),
                  &count, HZ*10);

    /*如果讀成功,,復(fù)制到用戶空間 */
    if (!retval) {
        if (copy_to_user(buffer, dev->bulk_in_buffer, count))
            retval = -EFAULT;
        else
            retval = count;
    }
usb_bulk_msg接口函數(shù)的定義如下:
int usb_bulk_msg(struct usb_device *usb_dev,unsigned int pipe,
void *data,int len,int *actual_length,int timeout);
其參數(shù)為:
struct usb_device *usb_dev:指向批量消息所發(fā)送的目標(biāo)USB設(shè)備指針,。
unsigned int pipe:批量消息所發(fā)送目標(biāo)USB設(shè)備的特定端點(diǎn),此值是調(diào)用usb_sndbulkpipe或者usb_rcvbulkpipe來創(chuàng)建的,。
void *data:如果是一個(gè)OUT端點(diǎn),,它是指向即將發(fā)送到設(shè)備的數(shù)據(jù)的指針。如果是IN端點(diǎn),,它是指向從設(shè)備讀取的數(shù)據(jù)應(yīng)該存放的位置的指針,。
int len:data參數(shù)所指緩沖區(qū)的大小。
int *actual_length:指向保存實(shí)際傳輸字節(jié)數(shù)的位置的指針,,至于是傳輸?shù)皆O(shè)備還是從設(shè)備接收取決于端點(diǎn)的方向,。
int timeout:以Jiffies為單位的等待的超時(shí)時(shí)間,,如果該值為0,,該函數(shù)一直等待消息的結(jié)束。
如果該接口函數(shù)調(diào)用成功,,返回值為0,,否則返回一個(gè)負(fù)的錯(cuò)誤值。
usb_control_msg接口函數(shù)定義如下:
int usb_control_msg(struct usb_device *dev,unsigned int pipe,__u8    request,__u8requesttype,__u16 value,__u16 index,void *data,__u16 size,int timeout)
除了允許驅(qū)動(dòng)程序發(fā)送和接收USB控制消息之外,,usb_control_msg函數(shù)的運(yùn)作和usb_bulk_msg函數(shù)類似,,其參數(shù)和usb_bulk_msg的參數(shù)有幾個(gè)重要區(qū)別:
struct usb_device *dev:指向控制消息所發(fā)送的目標(biāo)USB設(shè)備的指針。
unsigned int pipe:控制消息所發(fā)送的目標(biāo)USB設(shè)備的特定端點(diǎn),,該值是調(diào)用usb_sndctrlpipe或usb_rcvctrlpipe來創(chuàng)建的,。
__u8 request:控制消息的USB請(qǐng)求值。
__u8 requesttype:控制消息的USB請(qǐng)求類型值,。
__u16 value:控制消息的USB消息值,。
__u16 index:控制消息的USB消息索引值。
void *data:如果是一個(gè)OUT端點(diǎn),,它是指身即將發(fā)送到設(shè)備的數(shù)據(jù)的指針,。如果是一個(gè)IN端點(diǎn),它是指向從設(shè)備讀取的數(shù)據(jù)應(yīng)該存放的位置的指針,。
__u16 size:data參數(shù)所指緩沖區(qū)的大小,。
int timeout:以Jiffies為單位的應(yīng)該等待的超時(shí)時(shí)間,如果為0,,該函數(shù)將一直等待消息結(jié)束,。
如果該接口函數(shù)調(diào)用成功,,返回傳輸?shù)皆O(shè)備或者從設(shè)備讀取的字節(jié)數(shù);如果不成功它返回一個(gè)負(fù)的錯(cuò)誤值,。
這兩個(gè)接口函數(shù)都不能在一個(gè)中斷上下文中或者持有自旋鎖的情況下調(diào)用,,同樣,該函數(shù)也不能被任何其它函數(shù)取消,,使用時(shí)要謹(jǐn)慎,。
我們要給未知的USB設(shè)備寫驅(qū)動(dòng)程序,只需要把這個(gè)框架程序稍做修改就可以用了,,前面我們已經(jīng)說過要修改制造商和產(chǎn)品的ID號(hào),,把0xfff0這兩個(gè)值改為未知USB的ID號(hào)。
 #define USB_SKEL_VENDOR_ID      0xfff0
     #define USB_SKEL_PRODUCT_ID     0xfff0
還有就是在探測函數(shù)中把需要探測的接口端點(diǎn)類型寫好,,在這個(gè)框架程序中只探測了批量(USB_ENDPOINT_XFER_BULK)IN和OUT端點(diǎn),, 可以在此處使用掩碼(USB_ENDPOINT_XFERTYPE_MASK)讓其探測其它的端點(diǎn)類型,驅(qū)動(dòng)程序會(huì)對(duì)USB設(shè)備的每一個(gè)接口進(jìn)行一次探 測,,當(dāng)探測成功后,,驅(qū)動(dòng)程序就被綁定到這個(gè)接口上。再有就是urb的初始化問題,,如果你只寫簡單的USB驅(qū)動(dòng),,這塊不用多加考慮,框架程序里的東西已經(jīng)夠 用了,,這里我們簡單介紹三個(gè)初始化urb的輔助函數(shù):
usb_fill_int_urb :它的函數(shù)原型是這樣的:
void usb_fill_int_urb(struct urb *urb,struct usb_device *dev,
unsigned int pipe,void *transfer_buff,
int buffer_length,usb_complete_t complete,
void *context,int interval);
這個(gè)函數(shù)用來正確的初始化即將被發(fā)送到USB設(shè)備的中斷端點(diǎn)的urb,。
usb_fill_bulk_urb :它的函數(shù)原型是這樣的:
void usb_fill_bulk_urb(struct urb *urb,struct usb_device *dev,
unsigned int pipe,void *transfer_buffer,
int buffer_length,usb_complete_t complete)
這個(gè)函數(shù)是用來正確的初始化批量urb端點(diǎn)的。
usb_fill_control_urb :它的函數(shù)原型是這樣的:
void usb_fill_control_urb(struct urb *urb,struct usb_device *dev,unsigned int pipe,unsigned char *setup_packet,void *transfer_buffer,int buffer_length,usb_complete_t complete,void *context);
這個(gè)函數(shù)是用來正確初始化控制urb端點(diǎn)的,。
還有一個(gè)初始化等時(shí)urb的,,它現(xiàn)在還沒有初始化函數(shù),所以它們?cè)诒惶峤坏経SB核心前,,必須在驅(qū)動(dòng)程序中手工地進(jìn)行初始化,,可以參考內(nèi)核源代碼樹下的/usr/src/~/drivers/usb/media下的konicawc.c文件。

 

驅(qū)動(dòng)模塊的編譯,、配置和使用
現(xiàn)在我們的驅(qū)動(dòng)程序已經(jīng)大體寫好了,,然后在linux下把它編譯成模塊就可以把驅(qū)動(dòng)模塊插入到內(nèi)核中運(yùn)行了,編譯的Makefile文件可以這樣來寫:
ifneq ($(KERNELRELEASE),)
    obj-m := xxx.o
else
    KERNELDIR ?= /lib/modules/$(shell uname -r)/build
    PWD := $(shell pwd)
default:
    $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
endif
clean:
    rm -rf *.mod.* *.o *.ko .*.ko.* .tmp* .*.mod.o.* .*.o.*
其中xxx是源文件的文件名,,在linux下直接執(zhí)行make就可以生成驅(qū)動(dòng)模塊(xxx.ko)了,。生成驅(qū)動(dòng)模塊后使用insmod xxx.ko就可以插入到內(nèi)核中運(yùn)行了,用lsmod可以看到你插入到內(nèi)核中的模塊,,也可以從系統(tǒng)中用命令rmmod xxx把模塊卸載掉,;如果把編譯出來的驅(qū)動(dòng)模塊拷貝到/lib/modules/~/kernel/drivers/usb/下,然后depmod一下, 那么你在插入U(xiǎn)SB設(shè)備的時(shí)候,,系統(tǒng)就會(huì)自動(dòng)為你加載驅(qū)動(dòng)模塊的,;當(dāng)然這個(gè)得有hotplug的支持;加載驅(qū)動(dòng)模塊成功后就會(huì)在/dev/下生成設(shè)備文件 了,,如果用命令cat /proc/bus/usb/devices,,我們可以看到驅(qū)動(dòng)程序已經(jīng)綁定到接口上了:
T:  Bus=03 Lev=01 Prnt=01 Port=01 Cnt=01 Dev#=  2 Spd=12  MxCh= 0
D:  Ver= 1.10 Cls=02(comm.) Sub=00 Prot=00 MxPS= 8 #Cfgs=  1
P:  Vendor=1234 ProdID=2345 Rev= 1.10
C:* #Ifs= 1 Cfg#= 1 Atr=c0 MxPwr=  0mA
I:  If#= 1 Alt= 0 #EPs= 2 Cls=0a(data ) Sub=00 Prot=00 Driver=test_usb_driver /*我們的驅(qū)動(dòng)*/
E:  Ad=01(O) Atr=02(Bulk) MxPS=  64 Ivl=0ms
E:  Ad=82(I) Atr=02(Bulk) MxPS=  64 Ivl=0ms
此框架程序生成的是skel0(可以自由修改)的設(shè)備文件,現(xiàn)在就可以對(duì)這個(gè)設(shè)備文件進(jìn)行打開,、讀寫,、關(guān)閉等的操作了。

 

結(jié)束語
面對(duì)層出不窮的新的USB設(shè)備,,必須有人不斷編寫新的驅(qū)動(dòng)程序以便讓這些設(shè)備能夠在linux下正常的工作,,從這個(gè)意義上講,驅(qū)動(dòng)程序的編寫本身就是一件 非常有意義的工作,,本文只是起到一個(gè)拋磚引玉的作用,,幫助那些有志于寫驅(qū)動(dòng)程序的開發(fā)人員進(jìn)一步了解USB驅(qū)動(dòng)程序的設(shè)計(jì)思路,從而吸引更多的人加入到這 個(gè)隊(duì)伍中來,。linux不僅為我們提供了一個(gè)頂級(jí)質(zhì)量的操作系統(tǒng),,而且也為我們提供了參與到其未來開發(fā)過程的機(jī)會(huì),我們完全可以從中得到無盡的快樂,!

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

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多