如果不能陪你到最后- 是否后悔當初我們牽手
- 如果當初沒能遇見你
- 現在的我
- 在哪里逗留
- 所有的愛都是冒險
- 那就心甘情願
- 等待我們一生中 所有懸念
- 我一往情深的戀人
- 她是我的愛人
- 她給我的愛就像是
- 帶着露水的清晨
- 我多想給她我的真
- 我心疼的愛人
- 我願為她守候寂寞
- 就像這夜晚 深沉
這一章從主機側角度看到的USB 主機控制器驅動和設備驅動從主機側的角度而言,需要編寫的USB 驅動程序包括主機控制器驅動和設備驅動兩類,USB 主機控制器驅動程序控制插入其中的USB 設備,而USB 設備驅動程序控制該設備如何作為從設備與主機通信。
1. Linux USB驅動層次
1.1 主機側與設備側USB 驅動
USB 采用樹形拓撲結構,每條總線上只有一個主機控制器,負責協調主機和設備間的通信,而設備不能主動向主機發送任何消息。
1.2 設備、配置、接口、端點
在USB 設備的邏輯組織中,包含設備、配置、接口和端點4 個層次,每個USB 設備都提供了不同級別的配置信息,可以包含一個或多個配置,不同的配置使設備表現出不同的功能組合,配置由多個接口組成,接口由多個端點組成,代表一個基本的功能,是USB 設備驅動程序控制的對象,如下圖是USB 設備、配置、接口和端點之間的關系。

設備描述符:關於設備的通用信息,如供應商ID 、產品ID 和修訂ID,支持的設備類、子類和適用的協議以及默認端點的最大包大小等。在Linux 內核中,USB 設備用 usb_device 結構體來描述,USB 設備描述符定義為usb_device_descriptor 結構體,其代碼如下:
struct usb device descriptor
{
_ _u8 bLength; // 述符長度
_ _u8 bDescriptorType; // 述符類型編號
_ _le16 bcdUSB; //USB版本號
_ _u8 bDeviceClass; //USB分配的設備類code
_ _u8 bDeviceSubClass;// USB 分配的子類code
_ _u8 bDeviceProtocol; //USB 分配的協議code
_ _u8 bMaxPacketSize0; //endpoint0 最大包大小
_ _le16 idVendor; //廠商編號
_ _le16 idProduct; //產品編號
_ _le16 bcdDevice; //設備出廠編號
_ _u8 iManufacturer; // 述廠商字符串的索引
_ _u8 iProduct; // 述產品字符串的索引
_ _u8 iSerialNumber; // 述設備序列號字符串的索引
_ _u8 bNumConfigurations; //可能的配置數量
} _ _attribute_ _ ((packed));
配置描述符:此配置中的接口數、支持的掛起和恢復能力以及功率要求。USB配置在內核中使用usb_host_config 結構體描述,USB 配置描述符定義為結構體usb_config_descriptor,其代碼如下:
struct usb config descriptor
{
_ _u8 bLength; // 述符長度
_ _u8 bDescriptorType; // 述符類型編號
_ _le16 wTotalLength; //配置所返回的所有數據的大小
_ _u8 bNumInterfaces; // 配置所支持的接口數
_ _u8 bConfigurationValue; //Set Configuration命令需要的參數值
_ _u8 iConfiguration; // 述該配置的字符串的索引值
_ _ u8 bmAttributes; //供電模式的選擇
_ _u8 bMaxPower; //設備從總線提取的最大電流
} _ _attribute_ _ ((packed));
接口描述符:接口類、子類和適用的協議,接口備用配置的數目和端點數目。USB接口在內核中使用 usb_interface 結構體描述,USB 接口描述符定義為結構體 usb_interface_descriptor,其代碼如下:
struct usb interface descriptor
{
_ _u8 bLength; // 述符長度
_ _u8 bDescriptorType; // 述符類型
_ _ u8 bInterfaceNumber; // 接口的編號
_ _u8 bAlternateSetting; //備用的接口 述符編號
_ _u8 bNumEndpoints; //該接口使用的端點數,不包括端點0
_ _ u8 bInterfaceClass; //接口類型
_ _ u8 bInterfaceSubClass; //接口子類型
_ _ u8 bInterfaceProtocol; //接口所遵循的協議
_ _ u8 iInterface; // 述該接口的字符串索引值
} _ _attribute_ _ ((packed));
端點描述符:端點地址、方向和類型,支持的最大包大小,如果是中斷類型的端點則還包括輪詢頻率。在 Linux 內核中,U SB 端點使用 usb_host_endpoint 結構體來描述,USB端點描述符定義為 usb_ endpoint_ descriptor 結構體,其代碼如下:
struct usb endpoint descriptor
{
_ _u8 bLength; // 述符長度
_ _u8 bDescriptorType; // 述符類型
_ _u8 bEndpointAddress; //端點地址:0 ~3 位是端點號,第 7 位是方向 (0-OUT,1-IN)
_ _ u8 bmAttributes; //端點屬性:bit[0:1] 的值為00表示控制,為01表示同步,為02表示批量,為03表示中
_ _le16 wMaxPacketSize; //// 本端點接收或發送的最大信息包的大小
_ _u8 bInterval; //輪詢數據傳送端點的時間間隔
//對於批量傳送的端點以及控制傳送的端點,此域忽略
//對於同步傳送的端點,此域必須為1
//對於中 傳送的端點,此域值的范圍為1~255
_ _u8 bRefresh;
_ _u8 bSynchAddress;
} _ _attribute_ _ ((packed));
字符串描述符:在其他描述符中會為某些字段提供字符串索引,它們被用來檢索描述性字符串,可以以多種語言形式提供。字符串描述符是可選的,有的設備有,有的設備沒有 ,字符 串描述符對應於usb_string_ descriptor 結構體,其代碼如下:
struct usb string descriptor
{
_ _u8 bLength; // 述符長度
_ _u8 bDescriptorType; // 述符類型
_ _le16 wData [1]; /* 以UTF-16LE編碼 */
} _ _attribute_ _ ((packed));
2 USB主機驅動
USB 主機控制器有 3 種規格:OHCI (Open Host Controller Interface) 、UHCI (Universal Host Controller Interface) 和EHCI (Enhanced Host Controller Interface) 。
2.1 主機控制器驅動
在Linux 內核中,用usb_hcd 結構體描述USB 主機控制器驅動,它包含USB 主機控制器的 “家務”信息、硬件資源、狀態描述和用於操作主機控制器的 hc_driver等,其代碼如下:
struct usb hcd
{
/* 管理 “家務” */
struct usb bus self;
const char *product desc; /* 產品/廠商字符串 */
char irq descr [24]; /* 驅動 + 總線 # */
struct timer list rh timer; /* 根Hub 輪詢 */
struct urb *status urb; /* 目前的狀態urb */
/* 硬件信息/狀態 */
const struct hc driver *driver; /* 硬件特定的鈎子函數 */
/* 需要維護的標志 */
unsigned long flags;
#define HCD FLAG HW ACCESSIBLE 0x00000001
#define HCD FLAG SAW IRQ 0x00000002
unsigned rh_registered: 1; /* 根Hub 注冊? */
/* 下一個標志的采用只是 “權益之計”,當所有HCDs 支持新的根Hub 輪詢機制后將移除 */
unsigned uses new polling: 1;
unsigned poll rh: 1; /* 輪詢根Hub 狀態? */
unsigned poll pending: 1; /* 狀態已經改變? */
int irq; /* 被分配的irq */
void _ _iomem *regs; /* 設備內存和I/O */
u64 rsrc start; /* 內存和I/O資源開始位置 */
u64 rsrc len; /* 內存和I/O資源長度 */
unsigned power budget; /* mA, 0 = 無限制 */
#define HCD BUFFER POOLS 4
struct dma pool *pool[HCD BUFFER POOLS];
int state;
#define ACTIVE 0x01
#define SUSPEND 0x04
#define TRANSIENT 0x80
#define HC STATE HALT 0
#define HC STATE RUNNING ( ACTIVE)
#define HC STATE QUIESCING ( SUSPEND| TRANSIENT| ACTIVE)
#define HC STATE RESUMING ( SUSPEND| TRANSIENT)
#define HC STATE SUSPENDED ( SUSPEND)
#define HC IS RUNNING (state) ((state) & ACTIVE)
#define HC IS SUSPENDED(state) ((state) & SUSPEND)
/* 主機控制器驅動的私有數據 */
unsigned long hcd priv [0] attribute ((aligned (sizeof(unsigned long))));
};
Linux中采用以下函數創建HCD:
struct usb hcd *usb create hcd (const struct hc driver *driver, struct device *dev, char *bus name);
以下函數用來增加和移除:
int usb add hcd (struct usb hcd *hcd, unsigned int irqnum, unsigned long irqflags); void usb remove hcd (struct usb hcd *
2.2 OHCI 主機控制器驅動
OHCI HCD 驅動屬於HCD 驅動的實例,它定義了一個ohci_hcd 結構體,使用如下內聯函數可實現usb_hcd 和ohci_hcd 的相互轉換:
struct ohci hcd *hcd to ohci (struct usb hcd *hcd); struct usb hcd *ohci to hcd (const struct ohci hcd *ohci);
從usb_hcd 得到ohci_hcd 只是取得“私有”數據,而從ohci_hcd 得到usb_hcd 則是通過container_of()從結構體成員獲得結構體指,使用如下函數可初始化OHCI 主機控制器:
int ohci init (struct ohci hcd *ohci);
如下函數分別用於開啟、停止及復位OHCI 控制器:
int ohci run (struct ohci hcd *ohci); void ohci stop (struct usb hcd *hcd); void ohci usb reset (struct ohci hcd *ohci);
3 USB設備驅動
3.1 USB設備驅動整體結構
有以下設備類
- 音頻設備類。
- 通信設備類。
- HID (人機接口)設備類。
- 顯示設備類。
- 海量存儲設備類。
- 電源設備類。
- 打印設備類。
- 集線器設備類。
Linux 內核為各類USB 設備分配了相應的設備號,內核中提供了USB 設備文件系統 (usbdevfs,Linux 2.6 改為usbfs,即USB 文件系統),它和/proc 類似,都是動態產生的。通過在/etc/fstab 文件中添加如下一行:
none /proc/bus/usb usbfs defaults
或者輸入命令:
mount -t usbfs none /proc/bus/usb
可以實現USB 設備文件系統的掛載。
此外,在sysfs 文件系統中,同樣包含了USB 相關信息的描述,但只限於接口級別。USB 設備和USB 接口在sysfs 中均表示為單獨的USB 設備,其目錄命名規則如下:
根集線器-集線器端口號 (-集線器端口號-...):配置.接口。
3.2 USB請求塊(URB)
USB 請求塊 (USB request block,urb )是USB 設備驅動中用來描述與USB 設備通信所用的基本載體和核心數據結構,非常類似於網絡設備驅動中的sk_buff 結構體,是USB 主機與設備通信的 “電波”,urb 結構體,代碼如下:
struct urb
{
/* 私有的:只能由USB核心和主機控制器訪問的字段 */
struct kref kref; /*urb 引用計數 */
spinlock t lock; /* urb鎖 */
void *hcpriv; /* 主機控制器私有數據 */
int bandwidth; /* INT/ISO請求的帶寬 */
atomic t use count; /* 並發傳輸計數 */
u8 reject; /* 傳輸將失敗*/
/* 公共的: 可以被驅動使用的字段 */
struct list head urb list; /* 鏈表頭*/
struct usb device *dev; /* 關聯的USB 設備 */
unsigned int pipe; /* 管道信息 */
int status; /* URB 的當前狀態 */ unsigned int transfer flags; /* URB SHORT NOT OK | ...*/
void *transfer buffer; /* 發送數據到設備或從設備接收數據的緩沖區 */
dma addr t transfer dma; /*用來以DMA 方式向設備傳輸數據的緩沖區 */
int transfer buffer length;/*transfer buffer 或 transfer dma 指向緩沖區的大小 */
int actual length; /* URB 結束后,發送或接收數據的實際長度 */
unsigned char *setup packet; /* 指向控制URB 的設置數據包的指針*/
dma addr t setup dma; /*控制URB 的設置數據包的DMA 緩沖區*/
int start frame; /*等時傳輸中用於設置或返回初始幀*/
int number of packets; /*等時傳輸中等時緩沖區數據 */
int interval; /* URB被輪詢到的時間間隔 (對中 和等時urb 有效) */
int error count; /* 等時傳輸錯誤數量 */
void *context; /* completion 函數上下文 */
usb complete t complete; /* 當URB 被完全傳輸或發生錯誤時,被調用 */
struct usb iso packet descriptor iso frame desc[0];
/*單個URB 一次可定義多個等時傳輸時,描述各個等時傳輸 */
};
USB 設備中的每個端點都處理一個urb 隊列,在隊列被清空之前,一個urb 的典型生命周期有以下幾個過程:
- 被一個 USB設備驅動創建
- 初始化,被安排給一個特定USB 設備的特定端點
- 被USB 設備驅動提交給USB
- 提交由USB 核心指定的USB 主機控制器驅動。
- 被USB 主機控制器處理,進行一次到USB 設備的傳送。
- 當urb 完成,USB 主機控制器驅動通知USB 設備驅動
3.3 簡單的批量與控制URB
1)usb_bulk_msg()
usb_bulk_msg()函數創建一個USB 批量urb 並將它發送到特定設備,這個函數是同步的,它一直等待urb 完成后才返回。usb_bulk_msg()函數的原型為:
int usb bulk msg (struct usb device *usb dev, unsigned int pipe, void *data, int len, int *actual length, int timeout);
//usb_dev 參數為批量消息要發送的USB 設備的指 ,pipe 為批量消息要發送到的 USB 設備的端點,data 參數為指向要發送或接收的數據緩沖區的指 ,len 參數為data 參數//所指向的緩沖區的長度,actual_length 用於返回實際發送或接收的字節數,timeout 是發送超時,以ji ffies 為單位,0 意味着永遠等待。
// 如果函數調用成功,返回0 ;否則,返回1 個負的錯誤值。
2 )usb_control_msg()函數
usb_control_msg() 函數與 usb_bulk_msg() 函數類似,不過它提供驅動發送和結束USB 控制信息而非批量信息的能力,該函數的原型為:
int usb control msg (struct usb device *dev, unsigned int pipe, u8 request, _ _u8 requesttype, _ _u16 value, _ _u16 index, void *data, _ _u16 size, int timeout); //dev 指向控制消息發往的USB 設備,pipe 是控制消息要發往的USB 設備的端點, request 是這個控制消息的USB 請求值,requesttype 是這個控制消息的USB 請求類型, //value 是這個控制消息的USB 消息值,index 是這個控制消息的USB 消息索引值,data 指向要發送或接收的數據緩沖區,size
3) 探測和斷開函數
在USB 設備驅動usb_driver 結構體的探測函數中,應該完成如下工作:
- 探測設備的端點地址、緩沖區大小,初始化任何可能用於控制 USB 設備的數據結構。
- 把已初始化數據結構的指 保存到接口設備中
- 注冊USB 設備
對探測函數的調用發生在USB 設備被安裝且USB 核心認為該驅動程序與安裝的USB 設備對應時 (usb_driver 的id_table 成員在此時發揮作用),而對斷開函數的調用則發生在驅動因為種種原因不再控制該設備的時候。對這兩個函數的調用都是在內核線程中進行的.
4) USB 骨架程序
Linux 內核源代碼中的 driver/usb/usb-skeleton.c 文件為我們提供了一個最基礎的USB 驅動程序,即USB 骨架程序,可被看做一個最簡單的USB 設備驅動實例。盡管USB驅動驅動程序千差萬別,但是骨架程序萬變不離其宗。這里我也不多介紹啦~
版權所有,轉載請注明轉載地址:http://www.cnblogs.com/lihuidashen/p/4521915.html
