怎樣寫linux下的USB裝置驅動程式

來源:互聯網
上載者:User

轉自:http://hyt19860117.love.blog.163.com/blog/static/127773729201042435553852/

隨著人們生活水平的提高,我們用到的USB裝置也越來越多,但是Linux在硬體設定上仍然沒有做到完全隨插即用,對於Linux怎樣配置和使用他們,也
越來越成為困擾我們的一大問題;本文的目地是使大家瞭解怎樣編製USB裝置驅動,為更好地配置和使用USB裝置提供方便;對於希望開發Linux系統下
USB裝置驅動的人員,也可作為進一步學習USB驅動的大體架構進而編寫出特殊USB裝置的驅動程式。

USB基礎知識
USB
是英文Universal Serial
Bus的縮寫,意為通用序列匯流排。USB最初是為了替代許多不同的低速匯流排(包括並行、串列和鍵盤串連)而設計的,它以單一類型的匯流排串連各種不同的類型
的裝置。USB的發展已經超越了這些低速的串連方式,它現在可以支援幾乎所有可以串連到PC上的裝置。最新的USB規範修訂了理論上高達480Mbps的
高速串連。Linux核心支援兩種主要類型的USB驅動程式:宿主系統上的驅動程式和裝置上的驅動程式,從宿主的觀點來看(一個普通的宿主也就是一個PC
機),宿主系統的USB裝置驅動程式控制插入其中的USB裝置,而USB裝置的驅動程式控制該裝置如何作為一個USB裝置和主機通訊。本文將詳細介紹運行
於PC機上的USB系統是如何運作的。並同時用USB驅動程式的架構程式當例子作詳細的說明,我們在此文中不討論USB器件的驅動程式。

USB驅動程式基礎
在動手寫USB驅動程式這前,讓我們先看看寫的USB驅動程式在核心中的結構,如:

 
USB驅動程式存在於不同的核心子系統和USB硬體控制器之間,USB核
心為USB驅動程式提供了一個用於訪問和控制USB硬體的介面,而不必考慮系統當前存在的各種不同類型的USB硬體控制器。USB是一個非常複雜的設
備,linux核心為我們提供了一個稱為USB的核心的子系統來處理大部分的複雜性,USB裝置包括配置(configuration)、介面
(interface)和端點(endpoint),USB裝置綁定到介面上,而不是整個USB裝置。如所示:

 
USB通訊最基本的形式是通過端點(USB端點分中斷、批量、等時、控制
四種,每種用途不同),USB端點只能往一個方向傳送資料,從主機到裝置或者從裝置到主機,端點可以看作是單向的管道(pipe)。所以我們可以這樣認
為:裝置通常具有一個或者更多的配置,配置經常具有一個或者更多的介面,介面通常具有一個或者更多的設定,介面沒有或具有一個以上的端點。驅動程式把驅動
程式對象註冊到USB子系統中,稍後再使用製造商和裝置標識來判斷是否已經安裝了硬體。USB核心使用一個列表(是一個包含製造商ID和裝置號ID的一個
結構體)來判斷對於一個裝置該使用哪一個驅動程式,熱插撥指令碼使用它來確定當一個特定的裝置插入到系統時該自動裝載哪一個驅動程式。
上面我們簡要說明了驅動程式的基本理論,在寫一個裝置驅動程式之前,我們還要瞭解以下兩個概念:模組和裝置檔案。

塊:是在核心空間啟動並執行程式,實際上是一種目標對象檔案,沒有連結,不能獨立運行,但是可以裝載到系統中作為核心的一部分運行,從而可以動態擴充核心的功
能。模組最主要的用處就是用來實現裝置驅動程式。Linux下對於一個硬體的驅動,可以有兩種方式:直接載入到核心代碼中,啟動核心時就會驅動此硬體設
備。另一種就是以模組方式,編譯產生一個.ko檔案(在2.4以下核心中是用.o作模組檔案,我們以2.6的核心為準,以下同)。當應用程式需要時再載入
到核心空間運行。所以我們所說的一個硬體的驅動程式,通常指的就是一個驅動模組。
裝置檔案:對於一個裝置,它可以在/dev下面存在一個對應的邏
輯裝置節點,這個節點以檔案的形式存在,但它不是普通意義上的檔案,它是裝置檔案,更確切的說,它是裝置節點。這個節點是通過mknod命令建立的,其中
指定了主裝置號和次裝置號。主裝置號表明了某一類裝置,一般對應著確定的驅動程式;次裝置號一般是區分不同屬性,例如不同的使用方法,不同的位置,不同的
操作。這個裝置號是從/proc/devices檔案中獲得的,所以一般是先有驅動程式在核心中,才有裝置節點在目錄中。這個裝置號(特指主裝置號)的主
要作用,就是聲明裝置所使用的驅動程式。驅動程式和裝置號是一一對應的,當你開啟一個裝置檔案時,作業系統就已經知道這個裝置所對應的驅動程式。對於一個
硬體,Linux是這樣來進行驅動的:首先,我們必須提供一個.ko的驅動模組檔案。我們要使用這個驅動程式,首先要載入它,我們可以用insmod
xxx.ko,這樣驅動就會根據自己的類型(字元裝置類型或塊裝置類型,例如滑鼠就是字元裝置而硬碟就是塊裝置)向系統註冊,註冊成功系統會反饋一個主設
備號,這個主裝置號就是系統對它的唯一標識。驅動就是根據此主裝置號來建立一個一般放置在/dev目錄下的裝置檔案。在我們要訪問此硬體時,就可以對裝置
檔案通過open、read、write、close等命令進行。而驅動就會接收到相應的read、write操作而根據自己的模組中的相應函數進行操作
了。

USB驅動程式實踐

解了上述理論後,我們就可以動手寫驅動程式,如果你基本功好,而且寫過linux下的硬體驅動,USB的硬體驅動和pci_driver很類似,那麼寫
USB的驅動就比較簡單了,如果你只是大體瞭解了linux的硬體驅動,那也不要緊,因為在linux的核心源碼中有一個架構程式可以拿來借用一下,這個
架構程式在/usr/src/~(你的核心版本,以下同)/drivers/usb下,檔案名稱為usb-skeleton.c。寫一個USB的驅動程式最
基本的要做四件事:驅動程式要支援的裝置、註冊USB驅動程式、探測和斷開、提交和控制urb(USB請求塊)(當然也可以不用urb來傳輸資料,下文我
們會說到)。
驅動程式支援的裝置:有一個結構體struct usb_device_id,這個結構體提供了一列不同類型的該驅動程式支援的USB裝置,對於一個只控制一個特定的USB裝置的驅動程式來說,struct usb_device_id表被定義為:
/* 驅動程式支援的裝置列表 */
static struct usb_device_id skel_table [] = {
    { USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) },
    { }                    /* 終止入口 */
};
MODULE_DEVICE_TABLE (usb, skel_table);

於PC驅動程式,MODULE_DEVICE_TABLE是必需的,而且usb必需為該宏的第一個值,而USB_SKEL_VENDOR_ID和
USB_SKEL_PRODUCT_ID就是這個特殊裝置的製造商和產品的ID了,我們在程式中把定義的值改為我們這款USB的,如:
/* 定義製造商和產品的ID號 */
#define USB_SKEL_VENDOR_ID    0x1234
#define USB_SKEL_PRODUCT_ID    0x2345
這兩個值可以通過命令lsusb,當然你得先把USB裝置先插到主機上了。或者查看廠商的USB裝置的手冊也能得到,在我機器上運行lsusb是這樣的結果:
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
得到這兩個值後把它定義到程式裡就可以了。
註冊USB驅動程式:
有的USB驅動程式都必須建立的結構體是struct
usb_driver。這個結構體必須由USB驅動程式來填寫,包括許多回呼函數和變數,它們向USB核心代碼描述USB驅動程式。建立一個有效
struct usb_driver結構體,只須要初始化五個欄位就可以了,在架構程式中是這樣的:
static struct usb_driver skel_driver = {
    .owner =    THIS_MODULE,
    .name =        "skeleton",
    .probe =    skel_probe,
    .disconnect =    skel_disconnect,
    .id_table =    skel_table,
};
struct module *owner :指向該驅動程式的模組所有者的指標。USB核心使用它來正確地對該USB驅動程式進行引用計數,使它不會在不合適的時刻被卸載掉,這個變數應該被設定為THIS_MODULE宏。
const char *name:指向驅動程式名字的指標,在核心的所有USB驅動程式中它必須是唯一的,通常被設定為和驅動程式模組名相同的名字。
int
(*probe) (struct usb_interface *intf,const struct usb_device_id
*id):這個是指向USB驅動程式中的探測函數的指標。當USB核心認為它有一個介面(usb_interface)可以由該驅動程式處理時,這個函數
被調用。
void (disconnect)(struct usb_interface *intf):指向USB驅動程式中的斷開函數的指標,當一個USB介面(usb_interface)被從系統中移除或者驅動程式正在從USB核心中卸載時,USB核心將調用這個函數。
const struct usb_device_id *id_table:指向ID裝置表的指標,這個表包含了一列該驅動程式可以支援的USB裝置,如果沒有設定這個變數,USB驅動程式中的探測回呼函數就不會被調用。

這個結構體中還有其它的幾個回呼函數不是很常用,這裡就不一一說明了。以struct usb_driver
指標為參數的usb_register_driver函數調用把struct
usb_driver註冊到USB核心。一般是在USB驅動程式的模組初始化代碼中完成這個工作的:
static int __init usb_skel_init(void)
{
    int result;

    /* 驅動程式註冊到USB子系統中*/
    result = usb_register(&skel_driver);
    if (result)
        err("usb_register failed. Error number %d", result);

    return result;
}
當USB驅動程式將要被卸開時,需要把struct usb_driver從核心中登出。通過調用usb_deregister_driver來完成這個工作,當調用發生時,當前綁定到該驅動程式上的任何USB介面都被斷開,斷開函數將被調用:
static void __exit usb_skel_exit(void)
{
    /* 從子系統登出驅動程式 */
    usb_deregister(&skel_driver);
}
探測和斷開:
一個裝置被安裝而USB核心認為該驅動程式應該處理時,探測函數被調用,探測函數檢查傳遞給它的裝置資訊,確定驅動程式是否真的適合該裝置。當驅動程式因
為某種原因不應該控制裝置時,斷開函數被調用,它可以做一些清理工作。探測回呼函數中,USB驅動程式初始化任何可能用於控制USB裝置的局部結構體,它
還把所需的任何裝置相關資訊儲存到一個局部結構體中,下面是探測函數的部分源碼,我們加以分析。
    /* 設定端點資訊 */
    /* 只使用第一個批量IN和批量OUT端點 */
    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)) {
            /* 找到一個批量IN端點 */
            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)) {
            /* 找到一個批量OUT端點 */
            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;
    }
在探測函數裡,這個迴圈首先訪問該介面中存在的每一個端點,給該端點一個局部指標以便以後訪問:
for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
        endpoint = &iface_desc->endpoint[i].desc;

一輪探測過後,我們就有了一個端點,在還沒有發現批量IN類型的端點時,探測該端點方向是否為IN,這可以通過檢查USB_DIR_IN是否包含在
bEndpointAddress端點變數有確定,如果是的話,我們在探測該端點類型是否為批量,先用
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))
如果所有這些探測都通過了,驅動程式就知道它已經發現了正確的端點類型,可以把該端點的相關資訊儲存到一個局部結構體中以便稍後用它來和端點進行通訊:
            /* 找到一個批量IN類型的端點 */
            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;
            }
因為USB驅動程式要在裝置的生命週期的稍後時間擷取和介面相關聯的局部資料結構體,所以調用了usb_set_intfdata函數,把它儲存到struct usb_interface結構體中以便後面的訪問
    /* 把資料指標儲存到這個介面裝置中 */
    usb_set_intfdata(interface, dev);
我們以後調用usb_set_intfdata函數來擷取資料。當這一切都完成後,USB驅動程式必須在探測函數中調用usb_register_dev函數來把該裝置註冊到USB核心裡:
    /* 註冊裝置到USB核心 */
    retval = usb_register_dev(interface, &skel_class);
    if (retval) {
        /* 有些情況下是不允許註冊驅動程式的 */
        err("Not able to get a minor for this device.");
        usb_set_intfdata(interface, NULL);
        goto error;
    }

一個USB裝置被斷開時,和該裝置相關聯的所有資源都應該被儘可能的清理掉,在此時,如果已在在探測函數中調用了註冊函數來為該USB裝置分配了一個次設
備號話,必須調用usb_deregister_dev函數來把次裝置號交還給USB核心。在斷開函數中,從介面擷取之前調用
usb_set_intfdata設定的任何資料也是很重要的。然後設定struct
usb_interface結構體中的資料指標為NULL,以防任何不適當的對該資料的錯誤訪問。
在探測函數中會對每一個介面進行一次探測,所以
我們在寫USB驅動程式的時候,只要做好第一個端點,其它的端點就會自動完成探測。在探測函數中我們要注意的是在核心中用結構體struct
usb_host_endpoint來描述USB端點,這個結構體在另一個名為struct
usb_endpoint_descriptor的結構體中包含了真正的端點資訊,struct
usb_endpoint_descriptor結構體包含了所有的USB特定的資料,該結構體中我們要關心的幾個欄位是:
bEndpointAddress:這個是特定的USB地址,可以結合USB_DIR_IN和USB_DIR_OUT來使用,以確定該端點的資料是傳向裝置還是主機。
bmAttributes:
這個是端點的類型,這個值可以結合位元遮罩USB_ENDPOINT_XFERTYPE_MASK來使用,以確定此端點的類型是
USB_ENDPOINT_XFER_ISOC(等時)、USB_ENDPOINT_XFER_BULK(批量)、
USB_ENDPOINT_XFER_INT的哪一種。
wMaxPacketSize:這個是端點一次可以處理的最大位元組數,驅動程式可以發送數量大於此值的資料到端點,在實際傳輸中,資料量如果大於此值會被分割。
bInterval:這個值只有在端點類型是中斷類型時才起作用,它是端點插斷要求的間隔時間,以毫秒為單位。
提交和控制urb:當驅動程式有資料要發送到USB裝置時(大多數情況是在驅動程式的寫函數中),要分配一個urb來把資料轉送給裝置:
    /* 建立一個urb,並且給它分配一個緩衝*/
    urb = usb_alloc_urb(0, GFP_KERNEL);
    if (!urb) {
        retval = -ENOMEM;
        goto error;
    }
當urb被成功分配後,還要建立一個DMA緩衝區來以高效的方式發送資料到裝置,傳遞給驅動程式的資料要複製到這塊緩衝中去:
    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;
    }
當資料從使用者空間正確複製到局部緩衝區後,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核心以傳輸到裝置了:
    /* 把資料從批量OUT連接埠發出 */
    retval = usb_submit_urb(urb, GFP_KERNEL);
    if (retval) {
        err("%s - failed submitting write urb, error %d", __FUNCTION__, retval);
        goto error;
    }
當urb被成功傳輸到USB裝置之後,urb回呼函數將被USB核心調用,在我們的例子中,我們初始化urb,使它指向skel_write_bulk_callback函數,以下就是該函數:
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);
    }

    /* 釋放已指派的緩衝區 */
    usb_buffer_free(urb->dev, urb->transfer_buffer_length,
            urb->transfer_buffer, urb->transfer_dma);
}
有時候USB驅動程式只是要發送或者接收一些簡單的資料,驅動程式也可以不用urb來進行資料的傳輸,這是裡涉及到兩個簡單的介面函數:usb_bulk_msg和usb_control_msg ,在這個USB架構程式裡讀操作就是這樣的一個應用:
/* 進行阻塞的批量讀以從裝置擷取資料 */
    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);

    /*如果讀成功,複製到使用者空間 */
    if (!retval) {
        if (copy_to_user(buffer, dev->bulk_in_buffer, count))
            retval = -EFAULT;
        else
            retval = count;
    }
usb_bulk_msg介面函數的定義如下:
int usb_bulk_msg(struct usb_device *usb_dev,unsigned int pipe,
void *data,int len,int *actual_length,int timeout);
其參數為:
struct usb_device *usb_dev:指向批量訊息所發送的目標USB裝置指標。
unsigned int pipe:批量訊息所發送目標USB裝置的特定端點,此值是調用usb_sndbulkpipe或者usb_rcvbulkpipe來建立的。
void *data:如果是一個OUT端點,它是指向即將發送到裝置的資料的指標。如果是IN端點,它是指向從裝置讀取的資料應該存放的位置的指標。
int len:data參數所指緩衝區的大小。
int *actual_length:指向儲存實際傳輸位元組數的位置的指標,至於是傳輸到裝置還是從裝置接收取決於端點的方向。
int timeout:以Jiffies為單位的等待的逾時時間,如果該值為0,該函數一直等待訊息的結束。
如果該介面函數調用成功,傳回值為0,否則返回一個負的錯誤值。
usb_control_msg介面函數定義如下:
int
usb_control_msg(struct usb_device *dev,unsigned int pipe,__u8   
request,__u8requesttype,__u16 value,__u16 index,void *data,__u16
size,int timeout)
除了允許驅動程式發送和接收USB控制訊息之外,usb_control_msg函數的運作和usb_bulk_msg函數類似,其參數和usb_bulk_msg的參數有幾個重要區別:
struct usb_device *dev:指向控制訊息所發送的目標USB裝置的指標。
unsigned int pipe:控制訊息所發送的目標USB裝置的特定端點,該值是調用usb_sndctrlpipe或usb_rcvctrlpipe來建立的。
__u8 request:控制訊息的USB請求值。
__u8 requesttype:控制訊息的USB請求類型值。
__u16 value:控制訊息的USB訊息值。
__u16 index:控制訊息的USB訊息索引值。
void *data:如果是一個OUT端點,它是指身即將發送到裝置的資料的指標。如果是一個IN端點,它是指向從裝置讀取的資料應該存放的位置的指標。
__u16 size:data參數所指緩衝區的大小。
int timeout:以Jiffies為單位的應該等待的逾時時間,如果為0,該函數將一直等待訊息結束。
如果該介面函數調用成功,返回傳輸到裝置或者從裝置讀取的位元組數;如果不成功它返回一個負的錯誤值。
這兩個介面函數都不能在一個中斷上下文中或者持有自旋鎖的情況下調用,同樣,該函數也不能被任何其它函數取消,使用時要謹慎。
我們要給未知的USB裝置寫驅動程式,只需要把這個架構程式稍做修改就可以用了,前面我們已經說過要修改製造商和產品的ID號,把0xfff0這兩個值改為未知USB的ID號。
 #define USB_SKEL_VENDOR_ID      0xfff0
     #define USB_SKEL_PRODUCT_ID     0xfff0

有就是在探測函數中把需要探測的介面端點類型寫好,在這個架構程式中只探測了批量(USB_ENDPOINT_XFER_BULK)IN和OUT端點,可
以在此處使用掩碼(USB_ENDPOINT_XFERTYPE_MASK)讓其探測其它的端點類型,驅動程式會對USB裝置的每一個介面進行一次探測,
當探測成功後,驅動程式就被綁定到這個介面上。再有就是urb的初始化問題,如果你唯寫簡單的USB驅動,這塊不用多加考慮,架構程式裡的東西已經夠用
了,這裡我們簡單介紹三個初始化urb的輔助函數:
usb_fill_int_urb :它的函數原型是這樣的:
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);
這個函數用來正確的初始化即將被發送到USB裝置的中斷端點的urb。
usb_fill_bulk_urb :它的函數原型是這樣的:
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)
這個函數是用來正確的初始化批量urb端點的。
usb_fill_control_urb :它的函數原型是這樣的:
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);
這個函數是用來正確初始化控制urb端點的。
還有一個初始化等時urb的,它現在還沒有初始化函數,所以它們在被提交到USB核心前,必須在驅動程式中手工地進行初始化,可以參考核心原始碼樹下的/usr/src/~/drivers/usb/media下的konicawc.c檔案。

驅動模組的編譯、配置和使用
現在我們的驅動程式已經大體寫好了,然後在linux下把它編譯成模組就可以把驅動模組插入到核心中運行了,編譯的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下直接執行make就可以產生驅動模組(xxx.ko)了。產生驅動模組後使用insmod
xxx.ko就可以插入到核心中運行了,用lsmod可以看到你插入到核心中的模組,也可以從系統中用命令rmmod
xxx把模組卸載掉;如果把編譯出來的驅動模組拷貝到/lib/modules/~/kernel/drivers/usb/下,然後depmod一下,
那麼你在插入USB裝置的時候,系統就會自動為你載入驅動模組的;當然這個得有hotplug的支援;載入驅動模組成功後就會在/dev/下產生裝置檔案
了,如果用命令cat /proc/bus/usb/devices,我們可以看到驅動程式已經綁定到介面上了:
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 /*我們的驅動*/
E:  Ad=01(O) Atr=02(Bulk) MxPS=  64 Ivl=0ms
E:  Ad=82(I) Atr=02(Bulk) MxPS=  64 Ivl=0ms
此架構程式產生的是skel0(可以自由修改)的裝置檔案,現在就可以對這個裝置檔案進行開啟、讀寫、關閉等的操作了。

結束語

對層出不窮的新的USB裝置,必須有人不斷編寫新的驅動程式以便讓這些裝置能夠在linux下正常的工作,從這個意義上講,驅動程式的編寫本身就是一件非
常有意義的工作,本文只是起到一個拋磚引玉的作用,協助那些有志於寫驅動程式的開發人員進一步瞭解USB驅動程式的設計思路,從而吸引更多的人加入到這個
隊伍中來。linux不僅為我們提供了一個頂級品質的作業系統,而且也為我們提供了參與到其未來開發過程的機會,我們完全可以從中得到無盡的快樂!

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.