Under the Pine
/* AUTHOR: Pinus
* Creat on : 2018-11-4
* KERNEL : linux-4.4.145
*/
概述
現象:把USB裝置接到PC
1. 右下角彈出"發現android phone"
2. 跳出一個對話方塊,提示你安裝驅動程式
問1. 既然還沒有"驅動程式",為何能知道是"android phone"
答1. windows裡已經有了USB的匯流排驅動程式,接入USB裝置後,是"匯流排驅動程式"知道你是"android phone",提示你安裝的是"裝置驅動程式" USB匯流排驅動程式負責:識別USB裝置, 給USB裝置找到對應的驅動程式
問2. USB裝置種類非常多,為什麼一接入電腦,就能識別出來?
答2. PC和USB裝置都得遵守一些規範。
比如:USB裝置接入電腦後,PC機會發出"你是什麼"?
USB裝置就必須回答"我是xxx", 並且回答的語言必須是中文
USB匯流排驅動程式會發出某些命令想獲取裝置資訊(描述符),
USB裝置必須返回"裝置描述符"給PC
問3. PC機上接有非常多的USB裝置,怎麼分辨它們? USB介面只有4條線: 5V,GND,D-,D+
答3. 每一個USB裝置接入PC時,USB匯流排驅動程式都會給它分配一個編號
接在USB總線上的每一個USB裝置都有自己的編號(地址)
PC機想訪問某個USB裝置時,發出的命令都含有對應的編號(地址)
問4. USB裝置剛接入PC時,還沒有編號;那麼PC怎麼把"分配的編號"告訴它?
答4. 新接入的USB裝置的預設編號是0,在未分配新編號前,PC使用0編號和它通訊。
問5. 為什麼一接入USB裝置,PC機就能發現它?
答5. PC的USB口內部,D-和D+接有15K的下拉電阻,未接USB裝置時為低電平
USB裝置的USB口內部,D-或D+接有1.5K的上拉電阻;它一接入PC,就會把PC USB口的D-或D+拉高,從硬體的角度通知PC有新裝置接入
其他大致概念:
1. USB是主從結構的
所有的USB傳輸,都是從USB主機這方發起;USB裝置沒有"主動"通知USB主機的能力。
例子:USB滑鼠滑動一下立刻產生資料,但是它沒有能力通知PC機來讀資料,只能被動地等得PC機來讀。
2. USB的傳輸型別:
a. 控制傳輸:可靠,時間有保證,比如:USB裝置的識別過程
b. 批量傳輸: 可靠, 時間沒有保證, 比如:U盤
c. 中斷傳輸:可靠,實時,比如:USB滑鼠
d. 實時傳輸:不可靠,實時,比如:USB攝像頭
3. USB傳輸的物件:端點(endpoint)
我們說"讀U盤"、"寫U盤",可以細化為:把資料寫到U盤的端點1,從U盤的端點2裡讀出資料
除了端點0外,每一個端點只支援一個方向的資料傳輸
端點0用於控制傳輸,既能輸出也能輸入
4. 每一個端點都有傳輸型別,傳輸方向
5. 術語裡、程式裡說的輸入(IN)、輸出(OUT) "都是" 基於USB主機的立場說的。
比如滑鼠的資料是從滑鼠傳到PC機, 對應的端點稱為"輸入端點"
6. USB匯流排驅動程式的作用
a. 識別USB裝置
b. 查詢並安裝對應的裝置驅動程式
c. 提供USB讀寫函式
USB知識
一、USB 描述符:(存在於USB 的E2PROM裡面)
USB裝置描述符的資訊可以在include\linux\usb\ch9.h看到
通過命令lsusb 列出系統中所有的USB裝置:
通過命令lsusb -v 列出系統中所有的USB裝置的各個描述符資訊:
裝置描述符:
struct usb_device_descriptor {
__u8 bLength; //長度
__u8 bDescriptorType; //描述符型別
__le16 bcdUSB;
__u8 bDeviceClass; //裝置型別
__u8 bDeviceSubClass; //裝置子型別
__u8 bDeviceProtocol; //協議
__u8 bMaxPacketSize0; //最大傳輸大小
__le16 idVendor; //廠商 ID
__le16 idProduct; //裝置 ID
__le16 bcdDevice;
__u8 iManufacturer;
__u8 iProduct;
__u8 iSerialNumber; //序列號
__u8 bNumConfigurations; //包含的配置數目(每個USB裝置會對應多個配置)
} __attribute__ ((packed));
配置描述符:
struct usb_config_descriptor { //USB 配置描述符
__u8 bLength;
__u8 bDescriptorType;
__le16 wTotalLength; //總長度
__u8 bNumInterfaces; //介面數目(每個介面代表一種功能)
__u8 bConfigurationValue;
__u8 iConfiguration;
__u8 bmAttributes;
__u8 bMaxPower;
} __attribute__ ((packed));
介面描述符:
struct usb_interface_descriptor {
__u8 bLength;
__u8 bDescriptorType;
__u8 bInterfaceNumber;
__u8 bAlternateSetting;
__u8 bNumEndpoints;
__u8 bInterfaceClass;
__u8 bInterfaceSubClass;
__u8 bInterfaceProtocol;
__u8 iInterface;
} __attribute__ ((packed));
端點描述符:
struct usb_endpoint_descriptor { //USB 端點描述符(每個USB裝置最多有16個端點)
__u8 bLength; //描述符的位元組長度
__u8 bDescriptorType; //描述符型別,對於端點就是USB_DT_ENDPOINT
__u8 bEndpointAddress; //bit0~3表示端點地址,bit8 表示方向,輸入還是輸出
__u8 bmAttributes; //屬性(bit0、bit1構成傳輸型別,00--控制,01--等時,10--批量,11--中斷)
__le16 wMaxPacketSize; //端點一次可以處理的最大位元組數
__u8 bInterval; //希望主機輪詢自己的時間間隔
/* NOTE: these two are _only_ in audio endpoints. */
/* use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof. */
__u8 bRefresh;
__u8 bSynchAddress;
} __attribute__ ((packed));
二、USB的傳輸方式:(不同的裝置對於傳輸的資料各有各的要求)
1、 控制傳輸---獲取/配置裝置
2、 中斷傳輸---例如USB滑鼠、USB鍵盤(這裡說的中斷和硬體上下文的中斷不一樣,它不是裝置主動傳送一箇中斷請求,而是主控制器在保證不大於某個時間間隔interval內安排的一次資料傳輸)
3、 批量傳輸---用於大容量資料傳輸,沒有固定的傳輸速率,例如usb印表機、掃描器、U盤等,對應的端點就叫批量端點
4、 等時傳輸---可以傳輸大批量資料,但是對資料是否到達沒有保證,對實時性要求很高, 例如音訊、視訊等裝置(USB攝像頭、USB話筒),對應的端點就叫等時端點
三、URB(usb request block),USB請求塊
urb 是usb資料傳輸機制使用的核心資料結構,urb供usb協議棧使用;
struct urb {
/* private: usb core and host controller only fields in the urb */
struct kref kref; /* reference count of the URB */
void *hcpriv; /* private data for host controller */
atomic_t use_count; /* concurrent submissions counter */
atomic_t reject; /* submissions will fail */
int unlinked; /* unlink error code */
/* public: documented fields in the urb that can be used by drivers */
struct list_head urb_list; /* list head for use by the urb's
* current owner */
struct list_head anchor_list; /* the URB may be anchored */
struct usb_anchor *anchor;
struct usb_device *dev; /* (in) pointer to associated device */
struct usb_host_endpoint *ep; /* (internal) pointer to endpoint */
unsigned int pipe; /* (in) pipe information */
unsigned int stream_id; /* (in) stream ID */
int status; /* (return) non-ISO status */
unsigned int transfer_flags; /* (in) URB_SHORT_NOT_OK | ...*/
void *transfer_buffer; /* (in) associated data buffer */
dma_addr_t transfer_dma; /* (in) dma addr for transfer_buffer */
struct scatterlist *sg; /* (in) scatter gather buffer list */
int num_mapped_sgs; /* (internal) mapped sg entries */
int num_sgs; /* (in) number of entries in the sg list */
u32 transfer_buffer_length; /* (in) data buffer length */
u32 actual_length; /* (return) actual transfer length */
unsigned char *setup_packet; /* (in) setup packet (control only) */
dma_addr_t setup_dma; /* (in) dma addr for setup_packet */
int start_frame; /* (modify) start frame (ISO) */
int number_of_packets; /* (in) number of ISO packets */
int interval; /* (modify) transfer interval
* (INT/ISO) */
int error_count; /* (return) number of ISO errors */
void *context; /* (in) context for completion */
usb_complete_t complete; /* (in) completion routine */
struct usb_iso_packet_descriptor iso_frame_desc[0];
/* (in) ISO ONLY */
};
urb的使用方法:
1、 分配urb
struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags); //\drivers\usb\core\urb.c
2、 初始化urb
void usb_fill_[control | int | bulk]_urb{ } ///對應控制傳輸、中斷傳輸、批量傳輸
3、 提交urb(提交給主控制器,由主控制器傳送給USB裝置)
(1) 非同步提交urb,提交完成後執行通過usb_fill_[control | int | bulk]_urb 傳入的回撥函式
int usb_submit_urb(struct urb *urb, gfp_t mem_flags); //\drivers\usb\core\urb.c
(2) 同步提交urb
int usb_[control | interrupt | bulk]_msg () //\drivers\usb\core\Message.c
四、usb驅動資料結構 usb_device
struct usb_device {
int devnum;
char devpath[16];
u32 route;
enum usb_device_state state;
enum usb_device_speed speed;
...
};
五、 管道
每個端點通過管道和usb主控制器連線,管道包括以下幾個部分:
(1) 端點地址
(2) 資料傳輸方向(in 或 out)
(3) 資料傳輸模式
usb_[rcv| snd| ctrl| int| bulk| isoc ]pipe
根據端點地址、傳輸方式和傳輸方向建立不同的pipe:
#define usb_sndctrlpipe(dev, endpoint) \
((PIPE_CONTROL << 30) | __create_pipe(dev, endpoint))
#define usb_rcvctrlpipe(dev, endpoint) \
((PIPE_CONTROL << 30) | __create_pipe(dev, endpoint) | USB_DIR_IN)
#define usb_sndisocpipe(dev, endpoint) \
((PIPE_ISOCHRONOUS << 30) | __create_pipe(dev, endpoint))
#define usb_rcvisocpipe(dev, endpoint) \
((PIPE_ISOCHRONOUS << 30) | __create_pipe(dev, endpoint) | USB_DIR_IN)
#define usb_sndbulkpipe(dev, endpoint) \
((PIPE_BULK << 30) | __create_pipe(dev, endpoint))
#define usb_rcvbulkpipe(dev, endpoint) \
((PIPE_BULK << 30) | __create_pipe(dev, endpoint) | USB_DIR_IN)
#define usb_sndintpipe(dev, endpoint) \
((PIPE_INTERRUPT << 30) | __create_pipe(dev, endpoint))
#define usb_rcvintpipe(dev, endpoint) \
((PIPE_INTERRUPT << 30) | __create_pipe(dev, endpoint) | USB_DIR_IN