編寫一個(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)安裝.
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è)值.
所有 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);
}
在之前章節(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.
當(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)存塊在提交過程中.
更多建議: