Usb的設備是非常複雜的,它由許多不同的邏輯單元組成,這些邏輯單元之間的關係可以簡單地描述如下:
l 設備(usb_device)通常具有一個或者更多的配置(usb_host_config)
l 配置通常具有一個或者更多的接口(usb_interface)
l 接口通常具有一個或者更多的設置
l 設置沒有或者具有一個以上的端點(usb_host_endpoint)
各種機構體詳細描述見include/linux/usb.H
我們編寫設備驅動程序的最實質的一步就是模塊的初始化,
Usb驅動模塊初始化:
module_init (usb_***_init);
module_exit (usb_***_exit);
usb_***_init註冊usb驅動程序
usb_register(&usbdriver)
usb_***_exit卸載驅動程序。
usb_deregister(&usbdriver)
其中的usb_driver機構體包含了如下主要內容:
static struct usb_driver skel_driver = {
.owner = THIS_MODULE,
.name = "skeleton", //驅動程序的名字
.id_table = skel_table, //驅動程序所支持的設備列表
.probe = skel_probe, //探測函數
.disconnect = skel_disconnect, //斷開函數
};
詳細的結構:
struct usb_driver {
struct module *owner;
const char *name;
int (*probe) (struct usb_interface *intf,
const struct usb_device_id *id);
void (*disconnect)(struct usb_interface *intf);
int (*ioctl) (struct usb_interface *intf, unsigned int code, void *buf);
int (*suspend) (struct usb_interface *intf, pm_message_t message);
int (*resume) (struct usb_interface *intf);
const struct usb_device_id *id_table;
struct device_driver driver;
};
驅動所支持的設備列表可以通過多種方法定義,LDD(3rd)上的實例使用的是這個宏來獲得usb_device_id結構體:
USB_DEVICE(vendor,product)
它是根據廠家id,和設備id來匹配驅動程序。
探測函數probe:
當一個設備被安裝而usb核心認爲該驅動程序應該處理時,探測函數被調用,探測函數首先檢查傳遞給它的設備信息,確定驅動程序是否真的適合該設備。
當驅動程序因爲某種原因不應控制設備時,端口函數被調用,它可以做一些清理的工作。
驅動程序通常要探測設備的端點地址,類型和緩衝區大小。
在探測函數中一般都會調用usb_register_dev(interface,&usb_class)函數來把設備註冊到usb核心。只要該函數被調用,就要確保設備和驅動程序都處於可以處理用戶訪問設備的要求的恰當狀態。
其中的兩個參數結構體如下:
usb_interface結構體描述USB的接口,其中的重要字段有:
struct usb_host_interface *altsetting: 一個接口結構體數組,包含了所有可 能用於該接口的可選設置。
unsigned num_altsetting; altsetting指針所指的可選設置的數量。
unsigned usb_host_interface *cur_altsetting:指向altsetting數組內部的指針, 表示該接口的當前活動設置。
int minor: 包含USB核心分配給該接口的次設備號。
usb_interface結構體中的其它字段在usb驅動程序中不需要考慮。
usb_class_driver結構體包含如下內容:
char *name; sysfs用來描述設備的名字。
struct file_operations *fops;
mode_t mode; 爲該驅動程序創建的devfs文件的訪問模式。
int minor_base; 這是爲該驅動程序指派的次設備號範圍的開始值。
usb_class_driver實例:
static struct usb_class_driver skel_class = {
.name = "usb/skel%d",
.fops = &skel_fops,
.mode = S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH,
.minor_base = USB_SKEL_MINOR_BASE,
};
fops結構體實例:
static struct file_operations skel_fops = {
.owner = THIS_MODULE,
.read = skel_read,
.write = skel_write,
.open = skel_open,
.release = skel_release,
};
在porbe函數中註冊的是設備,而在驅動程序模塊初始化中註冊的是驅動程序,這一點要分清楚
:register_usb_dev(usb_interface,&usb_class_driver);把usb設備註冊到usb核心
:usb_register(&usb_driver); 將驅動程序註冊到usb子系統中
回過頭來我們就要寫file_operations中的函數了,簡單的read函數實例如下:
static ssize_t skel_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
{
struct usb_skel *dev;
int retval = 0;
dev = (struct usb_skel *)file->private_data;
/* do a blocking bulk read to get data from the device */
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 the read was successful, copy the data to userspace */
if (!retval) {
if (copy_to_user(buffer, dev->bulk_in_buffer, count))
retval = -EFAULT;
else
retval = count;
}
return retval;
}
Linux 內核中的usb代碼通過一個稱爲urb(usb請求塊)的東西和所有的usb設備通信。Urb被用來以異步的方式往或從特定的USB端點發送或接收數據。Usb驅動程序可能會爲單個端點分配許多urb,也可能對許多不同的端點重用單個urb。一個urb典型的生命週期如下:
1,由USB設備驅動程序創建
2,分配給一個特定usb設備的特定端點
3,由USB設備驅動程序遞交到usb核心
4,由usb核心遞交到特定設備的特定USB主控制器驅動程序
5,由usb主控制器驅動程序處理,它從設備進行usb傳送
6,當urb結束後,usb主控制器驅動程序通知usb設備驅動程序。
struct urb中usb設備驅動程序關心的字段有:
struct usb_device *dev;//urb所發送的目標struct usb_device指針。
unsigned int pipe ; //urb所要發送的特定目標struct usb_device的端點信息。
unsigned int usb_sndctrlpipe(struct usb_device *dev,unsigned int endpoint);//把指定的usb設備的指定端點設置爲一個控制OUT端點。
unsigned int usb_rcvctrlpipe(struct usb_device *dev,unsigned int endpoint);//把指定的usb設備的指定端點設置爲一個控制IN端點。
unsigned int usb_sndbulkpipe(struct usb_device *dev,unsigned int endpoint);//把指定的usb設備的指定端點設置爲一個批量OUT端點。
unsigned int usb_rcvbulkpipe(struct usb_device *dev,unsigned int endpoint);//把指定的usb設備的指定端點設置爲一個批量IN端點。
還有中斷int,等時isoc的out in 端點類似設置。
unsigned int transfer_flags;//URB_SHORT_NOT_OK;URB_ISO_ASAP;....
void *transfer_buffer;//指向用於發送數據到設備(OUT urb)或者從設備接收數據(IN urb)的緩衝區的指針。
dma_addr_t transfer_dma;//用於以DMA方式傳輸數據到usb設備的緩衝區。
int transfer_buffer_length;//緩衝區大小
Unsigned char *setup_packet;//指向控制urb的設置數據包的指針,它在傳輸緩衝區的數據之前被傳送。該變量只對控制urb有效。
Dma_addr_t setup_dma;//控制urb擁有設置數據包的DMA緩衝區。
Usb_complete_t complete;//指向一個結束處理例程的指針。
usb_complete_t 的類型定義爲:
Typedef void (*usb_complete_t)(struct urb *, struct pt_regs *);
Void *contex ;//指向一個可以被usb驅動程序設置的數據塊。
Int actuall_length;//當urb結束後,該變量被設置成發送或接收的數據的實際長度。
Int status;//urb 的當前狀態
其它略。。
創建和銷燬URB:必須使用動態創建usb_alloc_urb。
Struct urb *usb_alloc_urb ( int iso_packet, int mem_falgs);
Iso_packets,是該urb應該包含的等時數據包的數量,如果不打算創建等時數據包,這個設置爲0,
Mem_flags,(同kmalloc 的flags)
銷燬urb:
void usb_free_urb(struct urb* urb);
各urb的初始化函數:
中斷urb:
void usb_fill_int_urb(struct urb * urb, struct usb_device *dev,
unsigned int pipe, void *transfer_buffer,
int buffer_length, usb_complete_t complete,
void *context, int interval);
參數:
Struct urb *urb :需要初始化的urb指針。
Struct usb_device *dev:該urb所發送的目標usb設備。
Pipe:該urb所發送的目標的特定端點。
Transfer_buffer:由kmalloc創建的數據緩衝區指針。
Complete:urb結束之後調用的結束處理例程的指針。
Context:指向一個數據塊,該塊被添加到urb結構中,以便進行結束例程後面的查詢。
Interval:urb應該被調度的間隔。
批量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,
void *context);
控制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:
一旦urb被USB驅動程序正確的創建和初始化後,就可以提交到usb核心以發送到usb設備,這是通過usb_submit_urb函數來實現的。
int usb_submit_urb(struct urb *urb , int mem_flags);
urb參數指向即將被髮送到設備的urb的指針。
mem_flags:只有三個有效的值可以使用:GFP_ATOMIC, GFP_NOIO, GFP_KERNEL.
主要數據結構:
Struct usb_driver: 描述USB驅動程序的結構體
Struct usb_device_id: 描述該驅動程序支持的usb設備類型的結構體
Struct usb_device: 控制整個usb設備的結構體
Struct usb_interface: 所有的usb驅動程序都用它來和USB核心通信
Struct usb_class_driver: 描述了想要使用usb主設備號和用戶空間程序進行通信 的USB驅動程序的機構體。
Struct urb: 描述一個usb數據傳輸的結構體
/*
* struct usb_device - kernel's representation of a USB device
*
* FIXME: Write the kerneldoc!
*
* Usbcore drivers should not set usbdev->state directly. Instead use
* usb_set_device_state().
*/
struct usb_device {
int devnum; /* Address on USB bus */
char devpath [16]; /* Use in messages: /port/port/... */
enum usb_device_state state; /* configured, not attached, etc */
enum usb_device_speed speed; /* high/full/low (or error) */
struct usb_tt *tt; /* low/full speed dev, highspeed hub */
int ttport; /* device port on that tt hub */
struct semaphore serialize;
unsigned int toggle[2]; /* one bit for each endpoint ([0] = IN, [1] = OUT) */
struct usb_device *parent; /* our hub, unless we're the root */
struct usb_bus *bus; /* Bus we're part of */
struct usb_host_endpoint ep0;
struct device dev; /* Generic device interface */
struct usb_device_descriptor descriptor;/* Descriptor */
struct usb_host_config *config; /* All of the configs */
struct usb_host_config *actconfig;/* the active configuration */
struct usb_host_endpoint *ep_in[16];
struct usb_host_endpoint *ep_out[16];
char **rawdescriptors; /* Raw descriptors for each config */
int have_langid; /* whether string_langid is valid yet */
int string_langid; /* language ID for strings */
char *product;
char *manufacturer;
char *serial; /* static strings from the device */
struct list_head filelist;
struct class_device *class_dev;
struct dentry *usbfs_dentry; /* usbfs dentry entry for the device */
/*
* Child devices - these can be either new devices
* (if this is a hub device), or different instances
* of this same device.
*
* Each instance needs its own set of data structures.
*/
int maxchild; /* Number of ports if hub */
struct usb_device *children[USB_MAXCHILDREN];
};