13.4. 編寫一個(gè) USB 驅(qū)動(dòng)

2018-02-24 15:50 更新

13.4.?編寫一個(gè) USB 驅(qū)動(dòng)

編寫一個(gè) USB 設(shè)備驅(qū)動(dòng)的方法類似于一個(gè) pci 驅(qū)動(dòng): 驅(qū)動(dòng)注冊(cè)它的驅(qū)動(dòng)對(duì)象到 USB 子系統(tǒng)并且之后使用供應(yīng)商和設(shè)備標(biāo)識(shí)來告知是否它的硬件已經(jīng)安裝.

13.4.1.?驅(qū)動(dòng)支持什么設(shè)備

struct usb_device_id 結(jié)構(gòu)提供了這個(gè)驅(qū)動(dòng)支持的一個(gè)不同類型 USB 設(shè)備的列表. 這個(gè)列表被USB 核心用來決定給設(shè)備哪個(gè)驅(qū)動(dòng), 并且通過熱插拔腳本來決定哪個(gè)驅(qū)動(dòng)自動(dòng)加載, 當(dāng)特定設(shè)備被插入系統(tǒng)時(shí).

struct usb_device_id 結(jié)構(gòu)定義有下面的成員:

__u16 match_flags
決定設(shè)備應(yīng)當(dāng)匹配結(jié)構(gòu)中下列的哪個(gè)成員. 這是一個(gè)位成員, 由在 include/linux/mod_devicetable.h 文件中指定的不同的 USB_DEVICE_IDMATCH* 值所定義. 這個(gè)成員常常從不直接設(shè)置, 但是由 USB_DEVICE 類型宏來初始化.

__u16 idVendor
這個(gè)設(shè)備的 USB 供應(yīng)商 ID. 這個(gè)數(shù)由 USB 論壇分配給它的成員并且不能由任何別的構(gòu)成.

__u16 idProduct
這個(gè)設(shè)備的 USB 產(chǎn)品 ID. 所有的有分配給他們的供應(yīng)商 ID 的供應(yīng)商可以隨意管理它們的產(chǎn)品 ID.

__u16 bcdDevice_lo__u16 bcdDevice_hi
定義供應(yīng)商分配的產(chǎn)品版本號(hào)的高低范圍. bcdDevice_hi 值包括其中; 它的值是最高編號(hào)的設(shè)備號(hào). 這 2 個(gè)值以BCD 方式編碼. 這些變量, 連同 idVendor 和 idProduct, 用來定義一個(gè)特定的設(shè)備版本.

u8 bDeviceClassu8 bDeviceSubClass__u8 bDeviceProtocol
定義類, 子類, 和設(shè)備協(xié)議, 分別地. 這些值被 USB 論壇分配并且定義在 USB 規(guī)范中. 這些值指定這個(gè)設(shè)備的行為, 包括設(shè)備上所有的接口.

u8 bInterfaceClassu8 bInterfaceSubClass__u8 bInterfaceProtocol
非常象上面的設(shè)備特定值, 這些定義了類, 子類, 和單個(gè)接口協(xié)議, 分別地. 這些值由 USB 論壇指定并且定義在 USB 規(guī)范中.

kernel_ulong_t driver_info
這個(gè)值不用來匹配, 但是它持有信息, 驅(qū)動(dòng)可用來在 USB 驅(qū)動(dòng)的探測(cè)回調(diào)函數(shù)區(qū)分不同的設(shè)備.

至于 PCI 設(shè)備, 有幾個(gè)宏可用來初始化這個(gè)結(jié)構(gòu):

USB_DEVICE(vendor, product)
創(chuàng)建一個(gè) struct usb_device_id, 可用來只匹配特定供應(yīng)商和產(chǎn)品 ID 值. 這是非常普遍用的, 對(duì)于需要特定驅(qū)動(dòng)的 USB 設(shè)備.

USB_DEVICE_VER(vendor, product, lo, hi)
創(chuàng)建一個(gè) struct usb_device_id, 用來在一個(gè)版本范圍中只匹配特定供應(yīng)商和產(chǎn)品 ID 值.

USB_DEVICE_INFO(class, subclass, protocol)
創(chuàng)建一個(gè) struct usb_device_id, 可用來只匹配一個(gè)特定類的 USB 設(shè)備.

USB_INTERFACE_INFO(class, subclass, protocol)
創(chuàng)建一個(gè) struct usb_device_id, 可用來只匹配一個(gè)特定類的 USB 接口.

對(duì)于一個(gè)簡(jiǎn)單的 USB 設(shè)備驅(qū)動(dòng), 只控制來自一個(gè)供應(yīng)商的一個(gè)單一 USB 設(shè)備, struct usb_device_id 表可定義如:


/* 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);

至于 PCI 驅(qū)動(dòng), MODULE_DEVICE_TABLE 宏有必要允許用戶空間工具來發(fā)現(xiàn)這個(gè)驅(qū)動(dòng)可控制什么設(shè)備. 但是對(duì)于 USB 驅(qū)動(dòng), 字符串 usb 必須是在這個(gè)宏中的第一個(gè)值.

13.4.2.?注冊(cè)一個(gè) USB 驅(qū)動(dòng)

所有 USB 驅(qū)動(dòng)必須創(chuàng)建的主要結(jié)構(gòu)是 struct usb_driver. 這個(gè)結(jié)構(gòu)必須被 USB 驅(qū)動(dòng)填充并且包含多個(gè)函數(shù)回調(diào)和變量, 來向 USB 核心代碼描述 USB 驅(qū)動(dòng):

struct module *owner
指向這個(gè)驅(qū)動(dòng)的模塊擁有者的指針. USB 核心使用它正確地引用計(jì)數(shù)這個(gè) USB 驅(qū)動(dòng), 以便它不被在不合適的時(shí)刻卸載. 這個(gè)變量應(yīng)當(dāng)設(shè)置到 THIS_MODULE 宏.

const char *name
指向驅(qū)動(dòng)名子的指針. 它必須在內(nèi)核 USB 驅(qū)動(dòng)中是唯一的并且通常被設(shè)置為和驅(qū)動(dòng)的模塊名相同. 它出現(xiàn)在 sysfs 中在 /sys/bus/usb/drivers/ 之下, 當(dāng)驅(qū)動(dòng)在內(nèi)核中時(shí).

const struct usb_device_id *id_table
指向 struct usb_device_id 表的指針, 包含這個(gè)驅(qū)動(dòng)可接受的所有不同類型 USB 設(shè)備的列表. 如果這個(gè)變量沒被設(shè)置, USB 驅(qū)動(dòng)中的探測(cè)回調(diào)函數(shù)不會(huì)被調(diào)用. 如果你需要你的驅(qū)動(dòng)給系統(tǒng)中每個(gè) USB 設(shè)備一直被調(diào)用, 創(chuàng)建一個(gè)只設(shè)置這個(gè) driver_info 成員的入口項(xiàng):


static struct usb_device_id usb_ids[] = {
 {.driver_info = 42},
    {} 
};

int (probe) (struct usb_interface intf, const struct usb_device_id *id)
指向 USB 驅(qū)動(dòng)中探測(cè)函數(shù)的指針. 這個(gè)函數(shù)(在"探測(cè)和去連接的細(xì)節(jié)"一節(jié)中描述)被 USB 核心調(diào)用當(dāng)它認(rèn)為它有一個(gè)這個(gè)驅(qū)動(dòng)可處理的 struct usb_interface. 一個(gè)指向 USB 核心用來做決定的 struct usb_device_id 的指針也被傳遞到這個(gè)函數(shù). 如果這個(gè) USB 驅(qū)動(dòng)主張傳遞給它的 struct usb_interface, 它應(yīng)當(dāng)正確地初始化設(shè)備并且返回 0. 如果驅(qū)動(dòng)不想主張這個(gè)設(shè)備, 或者發(fā)生一個(gè)錯(cuò)誤, 它應(yīng)當(dāng)返回一個(gè)負(fù)錯(cuò)誤值.

void (disconnect) (struct usb_interface intf)
指向 USB 驅(qū)動(dòng)的去連接函數(shù)的指針. 這個(gè)函數(shù)(在"探測(cè)和去連接的細(xì)節(jié)"一節(jié)中描述)被 USB 核心調(diào)用, 當(dāng) struct usb_interface 已被從系統(tǒng)中清除或者當(dāng)驅(qū)動(dòng)被從 USB 核心卸載.

為創(chuàng)建一個(gè)值 struct usb_driver 結(jié)構(gòu), 只有 5 個(gè)成員需要被初始化:


static struct usb_driver skel_driver = {
 .owner = THIS_MODULE,
 .name = "skeleton",
 .id_table = skel_table,
 .probe = skel_probe,
 .disconnect = skel_disconnect, 
}; 

struct usb_driver 確實(shí)包含更多幾個(gè)回調(diào), 它們通常不經(jīng)常用到, 并且不被要求使 USB 驅(qū)動(dòng)正確工作:

int (ioctl) (struct usb_interface intf, unsigned int code, void *buf)
指向 USB 驅(qū)動(dòng)的 ioctl 函數(shù)的指針. 如果它出現(xiàn), 在用戶空間程序?qū)σ粋€(gè)關(guān)聯(lián)到 USB 設(shè)備的 usbfs 文件系統(tǒng)設(shè)備入口, 做一個(gè) ioctl 調(diào)用時(shí)被調(diào)用. 實(shí)際上, 只有 USB 集線器驅(qū)動(dòng)使用這個(gè) ioctl, 因?yàn)闆]有其他的真實(shí)需要對(duì)于任何其他 USB 驅(qū)動(dòng)要使用.

int (suspend) (struct usb_interface intf, u32 state)
指向 USB 驅(qū)動(dòng)中的懸掛函數(shù)的指針. 當(dāng)設(shè)備要被 USB 核心懸掛時(shí)被調(diào)用.

int (resume) (struct usb_interface intf)
指向 USB 驅(qū)動(dòng)中的恢復(fù)函數(shù)的指針. 當(dāng)設(shè)備正被 USB 核心恢復(fù)時(shí)被調(diào)用.

為注冊(cè) struct usb_driver 到 USB 核心, 一個(gè)調(diào)用 usb_register_driver 帶一個(gè)指向 struct usb_driver 的指針. 傳統(tǒng)上在 USB 驅(qū)動(dòng)的模塊初始化代碼做這個(gè):


static int __init usb_skel_init(void)
{
        int result;
        /* register this driver with the USB subsystem */
        result = usb_register(&skel_driver);
        if (result)
                err("usb_register failed. Error number %d", result);
        return result;
}

當(dāng) USB 驅(qū)動(dòng)被卸載, struct usb_driver 需要從內(nèi)核注銷. 使用對(duì) usb_deregister_driver 的調(diào)用做這個(gè). 當(dāng)這個(gè)調(diào)用發(fā)生, 任何當(dāng)前綁定到這個(gè)驅(qū)動(dòng)的 USB 接口被去連接, 并且去連接函數(shù)為它們而被調(diào)用.


static void __exit usb_skel_exit(void)
{
        /* deregister this driver with the USB subsystem */
        usb_deregister(&skel_driver);
}

13.4.2.1.?探測(cè)和去連接的細(xì)節(jié)

在之前章節(jié)描述的 struct usb_driver 結(jié)構(gòu)中, 驅(qū)動(dòng)指定 2 個(gè) USB 核心在合適的時(shí)候調(diào)用的函數(shù). 探測(cè)函數(shù)被調(diào)用, 當(dāng)設(shè)備被安裝時(shí), USB 核心認(rèn)為這個(gè)驅(qū)動(dòng)應(yīng)當(dāng)處理; 探測(cè)函數(shù)應(yīng)當(dāng)進(jìn)行檢查傳遞給它的關(guān)于設(shè)備的信息, 并且決定是否驅(qū)動(dòng)真正合適那個(gè)設(shè)備. 去連接函數(shù)被調(diào)用當(dāng)驅(qū)動(dòng)應(yīng)當(dāng)不再控制設(shè)備, 由于某些理由, 并且可做清理.

探測(cè)和去連接函數(shù)回調(diào)都在 USB 集線器內(nèi)核線程上下文中被調(diào)用, 因此它們中睡眠是合法的. 但是, 建議如果有可能大部分工作應(yīng)當(dāng)在設(shè)備被用戶打開時(shí)完成. 為了保持 USB 探測(cè)時(shí)間為最小. 這是因?yàn)?USB 核心處理 USB 設(shè)備的添加和去除在一個(gè)線程中, 因此任何慢設(shè)備驅(qū)動(dòng)可導(dǎo)致 USB 設(shè)備探測(cè)時(shí)間慢下來并且用戶可注意到.

在探測(cè)函數(shù)回調(diào)中, USB 驅(qū)動(dòng)應(yīng)當(dāng)初始化任何它可能使用來管理 USB 設(shè)備的本地結(jié)構(gòu). 它還應(yīng)當(dāng)保存任何它需要的關(guān)于設(shè)備的信息到本地結(jié)構(gòu), 因?yàn)樵诖藭r(shí)做這些通常更容易. 作為一個(gè)例子, USB 驅(qū)動(dòng)常常想為設(shè)備探測(cè)端點(diǎn)地址和緩沖大小是什么, 因?yàn)楹驮O(shè)備通訊需要它們. 這里是一些例子代碼, 它探測(cè) BULK 類型的 IN 和 OUT 端點(diǎn), 并且保存一些關(guān)于它們的信息在一個(gè)本地設(shè)備結(jié)構(gòu)中:


/* set up the endpoint information */
/* use only the first bulk-in and bulk-out endpoints */
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)) { /* we found a bulk in endpoint */ 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)) { /* we found a bulk out endpoint */ 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;
}

這塊代碼首先循環(huán)在這個(gè)接口中出現(xiàn)的每個(gè)端點(diǎn), 并且分配一個(gè)本地指針到端點(diǎn)結(jié)構(gòu)來使它之后容易存取:


for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
 endpoint = &iface_desc->endpoint[i].desc;

那么, 在我們有了一個(gè)端點(diǎn)后, 我們還沒有發(fā)現(xiàn)一個(gè)塊 IN 類型端點(diǎn), 我們看是否這個(gè)端點(diǎn)的方向是 IN. 那個(gè)可被測(cè)試通過看是否位掩碼 USB_DIR_IN 被包含在 bEndpointAddress 端點(diǎn)變量中. 如果這是真, 我們決定是否端點(diǎn)類型是塊, 通過使用 USB_ENDPOINT_XFERTYPE_MASK 位掩碼首先掩去 bmAttributes 變量, 并且接著檢查是否它匹配值 USB_ENDPOINT_XFER_BULK:


if (!dev->bulk_in_endpointAddr &&
                (endpoint->bEndpointAddress & USB_DIR_IN) &&
                ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)

                 == USB_ENDPOINT_XFER_BULK))
{

如果所有的這些檢查都是真, 驅(qū)動(dòng)知道它發(fā)現(xiàn)了正確的端點(diǎn)類型, 并且可保存關(guān)于端點(diǎn)的信息到本地結(jié)構(gòu)中, 它后來將需要這些信息和它通訊.


/* we found a bulk in endpoint */
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)?USB 驅(qū)動(dòng)需要獲取在設(shè)備的生命周期后期和這個(gè) struct usb_interface 關(guān)聯(lián)的本地?cái)?shù)據(jù)結(jié)構(gòu), 函數(shù) usb_set_intfdata 可被調(diào)用:


/* save our data pointer in this interface device */
usb_set_intfdata(interface, dev);

這個(gè)函數(shù)接受一個(gè)指向任何數(shù)據(jù)類型的指針, 并且保存它到 struct usb_interface 結(jié)構(gòu)為后面的存取. 為獲取這個(gè)數(shù)據(jù), 函數(shù) usb_get_intfdata 應(yīng)當(dāng)被調(diào)用:


struct usb_skel *dev;
struct usb_interface *interface;
int subminor;
int retval = 0;

subminor = iminor(inode);
interface = usb_find_interface(&skel_driver, subminor);
if (!interface)
{
        err ("%s - error, can't find device for minor %d",
             __FUNCTION__, subminor);
        retval = -ENODEV;
        goto exit;
}

dev = usb_get_intfdata(interface);
if (!dev)
{
        retval = -ENODEV;
        goto exit;
}

usb_get_intfdata 常常被調(diào)用, 在 USB 驅(qū)動(dòng)的 open 函數(shù)和在去連接函數(shù). 感謝這 2 個(gè)函數(shù), USB 驅(qū)動(dòng)不需要保持一個(gè)靜態(tài)指針數(shù)組來保存單個(gè)設(shè)備結(jié)構(gòu)為系統(tǒng)中所有當(dāng)前的設(shè)備. 對(duì)設(shè)備信息的非直接引用允許一個(gè)無限數(shù)目的設(shè)備被任何 USB 驅(qū)動(dòng)支持.

如果 USB 驅(qū)動(dòng)沒有和另一種處理用戶和設(shè)備交互的子系統(tǒng)(例如 input, tty, video, 等待)關(guān)聯(lián), 驅(qū)動(dòng)可使用 USB 主編號(hào)為了使用傳統(tǒng)的和用戶空間之間的字符驅(qū)動(dòng)接口. 為此, USB 驅(qū)動(dòng)必須在探測(cè)函數(shù)中調(diào)用 usb_register_dev 函數(shù), 當(dāng)它想注冊(cè)一個(gè)設(shè)備到 USB 核心. 確認(rèn)設(shè)備和驅(qū)動(dòng)處于正確的狀態(tài), 來處理一個(gè)想在調(diào)用這個(gè)函數(shù)時(shí)盡快存取這個(gè)設(shè)備的用戶.


/* we can register the device now, as it is ready */
retval = usb_register_dev(interface, &skel_class);
if (retval)
{
        /* something prevented us from registering this driver */
        err("Not able to get a minor for this device.");
        usb_set_intfdata(interface, NULL);
        goto error;
}

usb_register_dev 函數(shù)要求一個(gè)指向 struct usb_interface 的指針和指向 struct usb_class_driver 的指針. struct -usb_class_driver 用來定義許多不同的參數(shù), 當(dāng)注冊(cè)一個(gè)次編號(hào)USB 驅(qū)動(dòng)要 USB 核心知道這些參數(shù). 這個(gè)結(jié)構(gòu)包括下列變量:.

char *name
sysfs 用來描述設(shè)備的名子. 一個(gè)前導(dǎo)路徑名, 如果存在, 只用在 devfs 并且本書不涉及. 如果設(shè)備號(hào)需要在這個(gè)名子中, 字符 %d 應(yīng)當(dāng)在名子串中. 例如, 位創(chuàng)建 devfs 名子 usb/foo1 和 sysfs 類名 foo1, 名子串應(yīng)當(dāng)設(shè)置為 usb/foo%d.

struct file_operations *fops;
指向 struct file_operations 的結(jié)構(gòu)的指針, 這個(gè)驅(qū)動(dòng)已定義來注冊(cè)為字符設(shè)備. 這個(gè)結(jié)構(gòu)的更多信息見第 3 章.

mode_t mode;
給這個(gè)驅(qū)動(dòng)的要被創(chuàng)建的 devfs 文件的模式; 否則不使用. 這個(gè)變量的典型設(shè)置是值 S_IRUSR 和 值 S_IWUSR 的結(jié)合, 它將只提供這個(gè)設(shè)備文件的擁有者讀和寫存取.

int minor_base;
這是給這個(gè)驅(qū)動(dòng)安排的次編號(hào)的開始. 所有和這個(gè)驅(qū)動(dòng)相關(guān)的設(shè)備被創(chuàng)建為從這個(gè)值開始的唯一的, 遞增的次編號(hào). 只有 16 個(gè)設(shè)備被允許在任何時(shí)刻和這個(gè)驅(qū)動(dòng)關(guān)聯(lián), 除非 CONFIG_USB_DYNAMIC_MINORS 配置選項(xiàng)被打開. 如果這樣, 忽略這個(gè)變量, 并且這個(gè)設(shè)備的所有的次編號(hào)被以先來先服務(wù)的方式分配. 建議打開了這個(gè)選項(xiàng)的系統(tǒng)使用一個(gè)程序例如 udev 來關(guān)聯(lián)系統(tǒng)中的設(shè)備節(jié)點(diǎn), 因?yàn)橐粋€(gè)靜態(tài)的 /dev 樹不會(huì)正確工作.

當(dāng) USB 設(shè)備斷開, 所有的關(guān)聯(lián)到這個(gè)設(shè)備的資源應(yīng)當(dāng)被清除, 如果可能. 在此時(shí), 如果 usb_register_dev 已被在探測(cè)函數(shù)中調(diào)用來分配一個(gè) USB 設(shè)備的次編號(hào), 函數(shù) usb_deregister_dev 必須被調(diào)用來將次編號(hào)給回 USB 核心.

在斷開函數(shù)中, 也重要的是從接口獲取之前調(diào)用 usb_set_intfdata 所設(shè)置的數(shù)據(jù). 接著設(shè)置數(shù)據(jù)指針在 struct us_interface 結(jié)構(gòu)為 NULL 來阻止在不正確存取數(shù)據(jù)中的任何進(jìn)一步的錯(cuò)誤.


static void skel_disconnect(struct usb_interface *interface)
{
        struct usb_skel *dev;
        int minor = interface->minor;
        /* prevent skel_open() from racing skel_disconnect( ) */
        lock_kernel();

        dev = usb_get_intfdata(interface);
        usb_set_intfdata(interface, NULL);
        /* give back our minor */
        usb_deregister_dev(interface, &skel_class);

        unlock_kernel(); /* decrement our usage count */

        kref_put(&dev->kref, skel_delete);
        info("USB Skeleton #%d now disconnected", minor);
}

注意在之前代碼片段中的調(diào)用 lock_kernel. 它獲取了 bigkernel 鎖, 以至于 disconnect 回調(diào)不會(huì)遇到一個(gè)競(jìng)爭(zhēng)情況, 在使用 open 調(diào)用試圖獲取一個(gè)指向正確接口數(shù)據(jù)結(jié)構(gòu)的指針. 因?yàn)?open 在 bigkernel 鎖獲取情況下被調(diào)用, 如果 disconnect 也獲取同一個(gè)鎖, 只有驅(qū)動(dòng)的一部分可存取并且接著設(shè)置接口數(shù)據(jù)指針.

就在 disconnect 函數(shù)為一個(gè) USB 設(shè)備被調(diào)用, 所有的當(dāng)前在被傳送的 urb 可被 USB 核心取消, 因此驅(qū)動(dòng)不必明確為這些 urb 調(diào)用 usb_kill_urb. 如果一個(gè)驅(qū)動(dòng)試圖提交一個(gè) urb 給 USB 設(shè)備, 在調(diào)用 usb_submit_urb 被斷開之后, 這個(gè)任務(wù)會(huì)失敗, 錯(cuò)誤值為-EPIPE.

13.4.3.?提交和控制一個(gè) urb

當(dāng)驅(qū)動(dòng)有數(shù)據(jù)發(fā)送到 USB 設(shè)備(如同在驅(qū)動(dòng)的 write 函數(shù)中發(fā)生的), 一個(gè) urb 必須被分配來傳送數(shù)據(jù)到設(shè)備.


urb = usb_alloc_urb(0, GFP_KERNEL);
if (!urb)
{
        retval = -ENOMEM;
        goto error;
}

在 urb 被成功分配后, 一個(gè) DMA 緩沖也應(yīng)當(dāng)被創(chuàng)建來發(fā)送數(shù)據(jù)到設(shè)備以最有效的方式, 并且被傳遞到驅(qū)動(dòng)的數(shù)據(jù)應(yīng)當(dāng)被拷貝到緩沖:


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;
}

應(yīng)當(dāng)數(shù)據(jù)被正確地從用戶空間拷貝到本地緩沖, urb 在它可被提交給 USB 核心之前必須被正確初始化:


/* initialize the urb properly */
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;

現(xiàn)在 urb 被正確分配, 數(shù)據(jù)被正確拷貝, 并且 urb 被正確初始化, 它可被提交給 USB 核心來傳遞給設(shè)備.


/* send the data out the bulk port */
retval = usb_submit_urb(urb, GFP_KERNEL);
if (retval)
{
        err("%s - failed submitting write urb, error %d", __FUNCTION__, retval);
        goto error;
}

在urb被成功傳遞到 USB 設(shè)備(或者在傳輸中發(fā)生了什么), urb 回調(diào)被 USB 核心調(diào)用. 在我們的例子中, 我們初始化 urb 來指向函數(shù) skel_write_bulk_callback, 并且那就是被調(diào)用的函數(shù):


static void skel_write_bulk_callback(struct urb *urb, struct pt_regs *regs)
{
        /* sync/async unlink faults aren't errors */
        if (urb->status &&

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

        /* free up our allocated buffer */
        usb_buffer_free(urb->dev, urb->transfer_buffer_length,
                        urb->transfer_buffer, urb->transfer_dma);
}

回調(diào)函數(shù)做的第一件事是檢查 urb 的狀態(tài)來決定是否這個(gè) urb 成功完成或沒有. 錯(cuò)誤值, -ENOENT, -ECONNRESET, 和 -ESHUTDOWN 不是真正的傳送錯(cuò)誤, 只是報(bào)告伴隨成功傳送的情況. (見 urb 的可能錯(cuò)誤的列表, 在"結(jié)構(gòu) struct urb"一節(jié)中詳細(xì)列出). 接著這個(gè)回調(diào)釋放安排給這個(gè) urb 傳送的已分配的緩沖.

在 urb 的回調(diào)函數(shù)在運(yùn)行時(shí)另一個(gè) urb 被提交給設(shè)備是普遍的. 當(dāng)流數(shù)據(jù)到設(shè)備時(shí)是有用的. 記住 urb 回調(diào)是在中斷上下文運(yùn)行, 因此它不應(yīng)當(dāng)做任何內(nèi)存分配, 持有任何旗標(biāo), 或者任何可導(dǎo)致進(jìn)程睡眠的事情. 當(dāng)從回調(diào)中提交 urb, 使用 GFP_ATOMIC 標(biāo)志來告知 USB 核心不要睡眠, 如果它需要分配新內(nèi)存塊在提交過程中.

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)