1. 程式人生 > >Under the Pine

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