16、USB驅動


一、USB固件和USB傳輸方式

USB固件:

USB固件一般不需要我們編寫,在此不做程序分析。

USB固件中包含USB設備的出廠信息,如廠商ID、產品ID、主版本號和次版本號等。這就是為什么當我們把U盤插入USB口的時候主機可以知道這是一個U盤設備。

 

除包含出廠信息外,固件中還包含處理USB協議和設備讀寫操作的程序,如將數據從設備發送到總線上或從總線中將數據讀取到設備中。驅動程序只是將USB規范定義的請求發送給固件程序,固件程序負責將數據寫入設備中。USB規范定義了USB設備間的通信方式。

 

USB結構:

USB是主從結構的。所有的USB傳輸,都是從USB主機這方發起,USB設備沒有“主動”通知USB主機的能力。

如USB鼠標滑動一下產生了數據,但它沒有能力通知PC機來讀數據,只能被動地等待PC來讀。

 

USB傳輸類型:

1. 控制傳輸:可靠,時間有保證。如:USB設備的識別過程

2. 批量傳輸: 可靠, 時間沒有保證。如:U盤

3. 中斷傳輸:可靠,實時。如:USB鼠標

4. 實時傳輸:不可靠,實時。如:USB攝像頭

 

 

二、Linux USB設備驅動模型

USB設備驅動模型采用總線設備驅動模型,所以它具有三部分:

1. USB Bus

2. USB Device

3. USB Driver

 

usb_bus和usb_bus_type:

每一條USB總線對應一個struct usb_bus。

 1 struct usb_bus {
 2     struct device *controller;    /* host/master side hardware */
 3     int busnum;            /* Bus number (in order of reg) */
 4     const char *bus_name;        /* stable id (PCI slot_name etc) */
 5     u8 uses_dma;            /* Does the host controller use DMA? */
 6     u8 uses_pio_for_control;    /*
 7                      * Does the host controller use PIO
 8                      * for control transfers?
 9                      */
10     u8 otg_port;            /* 0, or number of OTG/HNP port */
11     unsigned is_b_host:1;        /* true during some HNP roleswitches */
12     unsigned b_hnp_enable:1;    /* OTG: did A-Host enable HNP? */
13     unsigned sg_tablesize;        /* 0 or largest number of sg list entries */
14 
15     int devnum_next;        /* Next open device number in
16                      * round-robin allocation */
17 
18     struct usb_devmap devmap;    /* device address allocation map */
19     struct usb_device *root_hub;    /* Root hub */
20     struct usb_bus *hs_companion;    /* Companion EHCI bus, if any */
21     struct list_head bus_list;    /* list of busses */
22 
23     int bandwidth_allocated;    /* on this bus: how much of the time
24                      * reserved for periodic (intr/iso)
25                      * requests is used, on average?
26                      * Units: microseconds/frame.
27                      * Limits: Full/low speed reserve 90%,
28                      * while high speed reserves 80%.
29                      */
30     int bandwidth_int_reqs;        /* number of Interrupt requests */
31     int bandwidth_isoc_reqs;    /* number of Isoc. requests */
32 
33 #ifdef CONFIG_USB_DEVICEFS
34     struct dentry *usbfs_dentry;    /* usbfs dentry entry for the bus */
35 #endif
36 
37 #if defined(CONFIG_USB_MON) || defined(CONFIG_USB_MON_MODULE)
38     struct mon_bus *mon_bus;    /* non-null when associated */
39     int monitored;            /* non-zero when monitored */
40 #endif
41 };
View Code

 

usb_bus_type定義了一種usb總線類型。在軟件層次中,usb_bus屬於usb_bus_type。

usb_bus_type通過bus_register(&usb_bus_type)函數向內核注冊usb總線。

struct bus_type usb_bus_type = {
    .name   = "usb",
    .match  = usb_device_match,
    .uevent = usb_uevent,
    .pm     = &usb_bus_pm_ops,
};

其中,match()函數通過id_table來匹配device和driver。

 1 static int usb_device_match(struct device *dev, struct device_driver *drv)
 2 {
 3     /* devices and interfaces are handled separately */
 4     if (is_usb_device(dev)) {
 5 
 6         /* interface drivers never match devices */
 7         if (!is_usb_device_driver(drv))
 8             return 0;
 9 
10         /* TODO: Add real matching code */
11         return 1;
12 
13     } else if (is_usb_interface(dev)) {
14         struct usb_interface *intf;
15         struct usb_driver *usb_drv;
16         const struct usb_device_id *id;
17 
18         /* device drivers never match interfaces */
19         if (is_usb_device_driver(drv))
20             return 0;
21 
22         intf = to_usb_interface(dev);
23         usb_drv = to_usb_driver(drv);
24         
25         /* 匹配interface和driver->id_table */
26         id = usb_match_id(intf, usb_drv->id_table);
27         if (id)
28             return 1;
29 
30         id = usb_match_dynamic_id(intf, usb_drv);
31         if (id)
32             return 1;
33     }
34 
35     return 0;
36 }
View Code

 

USB總線的任務有:

1. 識別USB設備

  a. 分配地址並將地址告訴USB設備

  b. 發出命令獲取描述符

2. 查找並安裝對應的設備驅動程序

3. 提供USB讀寫接口

 

我們現在來分析總線框架:

usb_init()
  -> bus_register(&usb_bus_type);   /* 注冊usb總線 */
  -> bus_register_notifier(&usb_bus_type, &usb_bus_nb);
  -> usb_major_init()
    -> register_chrdev(180, "usb", &usb_fops);
  -> usb_register(&usbfs_driver);   /* usbfs驅動 */
  -> usb_hub_init();                /* 初始化USB集線器 */
    -> usb_register(&hub_driver);
    -> kthread_run(hub_thread, NULL, "khubd");        /* 執行線程 */
      -> hub_events();
        /* 如果有插拔USB設備操作 */
        -> hub_port_connect_change(hub, i, portstatus, portchange);
          -> usb_alloc_dev(hdev, hdev->bus, port1);   /* 分配usb內存 */
          -> choose_address(udev);                    /* 設置地址 */
          -> hub_port_init(hub, udev, port1, i);      /* 獲取設備描述符 */
    -> usb_deregister(&hub_driver);
  -> usb_register_device_driver(&usb_generic_driver, THIS_MODULE);

 

usb_device:

每一個USB設備對應一個struct usb_device。

 1 struct usb_device {
 2     int        devnum;                    /* 設備號 */
 3     char        devpath[16];        /* 設備路徑 */
 4     u32        route;
 5     enum usb_device_state    state;
 6     enum usb_device_speed    speed;
 7 
 8     struct usb_tt    *tt;
 9     int        ttport;
10 
11     unsigned int toggle[2];
12 
13     struct usb_device *parent;
14     struct usb_bus *bus;            /* 設備所屬總線 */
15     struct usb_host_endpoint ep0;    /* 設備端點 */
16 
17     struct device dev;
18 
19     struct usb_device_descriptor descriptor;
20     struct usb_host_config *config;    /* 設備配置 */
21 
22     struct usb_host_config *actconfig;
23     struct usb_host_endpoint *ep_in[16];
24     struct usb_host_endpoint *ep_out[16];
25 
26     char **rawdescriptors;
27 
28     unsigned short bus_mA;
29     u8 portnum;
30     u8 level;
31 
32     unsigned can_submit:1;
33     unsigned persist_enabled:1;
34     unsigned have_langid:1;
35     unsigned authorized:1;
36     unsigned authenticated:1;
37     unsigned wusb:1;
38     int string_langid;
39 
40     /* static strings from the device */
41     char *product;
42     char *manufacturer;
43     char *serial;
44 
45     struct list_head filelist;
46 ...
47     int maxchild;
48     struct usb_device *children[USB_MAXCHILDREN];
49 
50     u32 quirks;
51     atomic_t urbnum;
52 ...
53     struct wusb_dev *wusb_dev;
54     int slot_id;
55 };
View Code

其注冊函數為int usb_new_device(struct usb_device *udev)。

 

在USB設備的邏輯組織中,包含設備、配置、接口和端點四個層次。在此我使用宋寶華老師書中圖片作為解釋:

 

在Linux內核中,這四個層次通過一組標准的描述符來描述,如下所示。

1. 設備描述符:usb_device用於定義USB設備,usb_device_descriptor用於定義產品ID等。

struct usb_device_descriptor {
    __u8  bLength;            /* 描述符長度 */
    __u8  bDescriptorType;    /* 描述符類型編號 */

    __le16 bcdUSB;            /* USB版本號 */
    __u8  bDeviceClass;       /* USB分配的設備類 */
    __u8  bDeviceSubClass;    /* USB分配的子類 */
    __u8  bDeviceProtocol;    /* USB分配的協議 */
    __u8  bMaxPacketSize0;    /* 端點0的最大包大小 */
    __le16 idVendor;          /* 廠商編號 */
    __le16 idProduct;         /* 產品編號 */
    __le16 bcdDevice;         /* 出廠編號 */
    __u8  iManufacturer;
    __u8  iProduct;
    __u8  iSerialNumber;
    __u8  bNumConfigurations;
} __attribute__ ((packed));

當一個USB設備插入時,默認的設備編號為0,在未分配新的編號前,PC使用編號0與它通信。

由於PC可能擁有多個USB設備,因此在PC需要訪問某個USB設備時,發出的命令含有此USB設備對應的編號地址。

 

2. 配置描述符:usb_host_config用於定義USB配置,usb_config_descriptor用於定義此配置下的接口數,供電模式和功率要求等。

struct usb_config_descriptor {
    __u8  bLength;            /* 描述符長度 */
    __u8  bDescriptorType;    /* 描述符類型編號 */

    __le16 wTotalLength;      /* 配置返回的所有數據大小 */
    __u8  bNumInterfaces;     /* 配置支持的接口數 */
    __u8  bConfigurationValue;
    __u8  iConfiguration;     /* 配置索引值 */
    __u8  bmAttributes;       /* 供電模式 */
    __u8  bMaxPower;          /* 最大電流 */
} __attribute__ ((packed));

 

3. 接口描述符:usb_interface用於定義USB接口,usb_interface_descriptor用於定義此接口下的端點數,協議等。

struct usb_interface_descriptor {
    __u8  bLength;            /* 描述符長度 */
    __u8  bDescriptorType;    /* 描述符類型 */

    __u8  bInterfaceNumber;   /* 接口編號 */
    __u8  bAlternateSetting;
    __u8  bNumEndpoints;      /* 端點數 */
    __u8  bInterfaceClass;    /* 接口類型 */
    __u8  bInterfaceSubClass;
    __u8  bInterfaceProtocol;
    __u8  iInterface;         /* 接口索引值 */
} __attribute__ ((packed));

 

4. 端點描述符:usb_host_endpoint用於定義USB端點,usb_endpoint_descriptor用於定義此端點下的端點地址,方向和類型等。

struct usb_endpoint_descriptor {
    __u8  bLength;            /* 描述符長度 */
    __u8  bDescriptorType;    /* 描述符類型 */

    __u8  bEndpointAddress;   /* 端點地址 */
    __u8  bmAttributes;       /* 端點屬性 */
    __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));

 

我們可以使用“lsusb -v -s 總線號:設備號”查看設備的詳細信息

 

usb_driver:

每一個USB設備驅動對應一個struct usb_driver。

 1 struct usb_driver {
 2     const char *name;
 3 
 4     int (*probe) (struct usb_interface *intf,
 5               const struct usb_device_id *id);
 6 
 7     void (*disconnect) (struct usb_interface *intf);
 8 
 9     int (*unlocked_ioctl) (struct usb_interface *intf, unsigned int code,
10             void *buf);
11 
12     int (*suspend) (struct usb_interface *intf, pm_message_t message);
13     int (*resume) (struct usb_interface *intf);
14     int (*reset_resume)(struct usb_interface *intf);
15 
16     int (*pre_reset)(struct usb_interface *intf);
17     int (*post_reset)(struct usb_interface *intf);
18 
19     const struct usb_device_id *id_table;
20 
21     struct usb_dynids dynids;
22     struct usbdrv_wrap drvwrap;
23     unsigned int no_dynamic_id:1;
24     unsigned int supports_autosuspend:1;
25     unsigned int soft_unbind:1;
26 };
View Code

其注冊函數為int usb_register(struct usb_driver *driver)。

1 #define usb_register(driver) \
2     usb_register_driver(driver, THIS_MODULE, KBUILD_MODNAME)

 

 

三、USB數據傳輸

USB設備驅動和USB設備通信所有的結構體為USB請求塊(USB Request Block,URB)。

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;      /* 用於計數,URB若提交則加1,URB若返回驅動程序減1 */
    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;       /* URB需要發送到的設備 */
    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;        /* 緩沖區,其數據可以從設備發送到主機,也可以從主機發送到設備 */
    dma_addr_t transfer_dma;      /* (in) dma addr for transfer_buffer */
    struct scatterlist *sg;       /* (in) scatter gather buffer list */
    int num_sgs;                  /* (in) number of entries in the sg list */
    u32 transfer_buffer_length;   /* 表示transfer_buffer或transfer_dma的長度 */
    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的處理流程:

在USB設備的邏輯層次中,interface是邏輯上的設備,它使用endpoint傳輸urb。

也就是說,USB設備中每個endpoint都處理一個urb隊列,在隊列清空之前,一個urb的傳輸過程如下:

當要進行數據傳輸時,需要分配、設置和提交一個urb給USB核心。核心對urb進行解析,將控制信息提交給主機控制器,由主機控制器完成數據到設備的傳輸。此時,驅動程序只需等待。當數據回傳到主機控制器后,會轉發給USB核心,喚醒等待的驅動程序。

 

具體函數操作如下圖:

各函數聲明如下:

struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags)

inline void usb_fill_int_urb (struct urb *urb,    /* 要初始化的urb指針 */
                        struct usb_device *dev,    /* 所要訪問的設備 */
                        unsigned int      pipe,    /* 要訪問的端點所對應的管道,使用usb_sndintpipe()或usb_rcvintpipe()創建 */
                        void              *transfer_buffer,    /* 要傳輸的數據緩沖區 */
                        int               buffer_length,    /* 緩沖區長度 */
                        usb_complete_t    complete_fn,        /* 當完成該urb所請求的操作時,要調用的回調函數 */
                        void              *context,            /* complet_fn函數所需的上下文,通常取值為dev */
                        int               interval)            /*urb被調度的時間間隔 */
inline 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_fn, void *context)
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_fn, void *context)

int usb_submit_urb(struct urb *urb, gfp_t mem_flags);

void usb_kill_urb(struct urb *urb);

void usb_free_urb(struct urb *urb);

其中complete_fn()就是傳輸完成函數

 

 

四、USB鼠標設備驅動

在熟悉各個函數后,我們在本節完成USB鼠標設備驅動,步驟如下:

1. 分配設置usb_driver

.id_table

.probe

.disconnect

2. 使用usb_register()注冊usb_driver

3. 在probe()函數中完成輸入子系統和urb設置,最后提交urb

4. 在傳輸完成函數函數中判斷數據,上報數據,重新提交urb

5. 在disconnect()函數中注銷urb和輸入子系統

6. 使用usb_deregister()注銷usb_driver

 

在編寫驅動時,可參考例子drivers/hid/usbhid/usbmouse.c

 

USB鼠標驅動源代碼:

  1 #include <linux/module.h>
  2 #include <linux/fs.h>
  3 #include <linux/init.h>
  4 #include <linux/cdev.h>
  5 #include <linux/slab.h>
  6 #include <linux/device.h>
  7 #include <linux/usb/input.h>
  8 #include <linux/hid.h>
  9 
 10 #include <asm/uaccess.h>
 11 
 12 static struct usb_device_id mouse_table [] = {
 13     { USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
 14         USB_INTERFACE_PROTOCOL_MOUSE) },
 15     { }    /* Terminating entry */
 16 };
 17 
 18 static struct input_dev *usbinput;
 19 static dma_addr_t usb_buf_phys;
 20 static char *buf_mem;
 21 static struct urb *usb_urb;
 22 static int len;
 23 
 24 static unsigned int cnt;
 25 
 26 static void usb_mouse_irq(struct urb *purb)
 27 {
 28 #if 0
 29     /* 用於測試哪個bit是用於表示左、中、右鍵
 30      * 我所使用的鼠標使用bit[0]表示左、中、右鍵
 31      */
 32     int i;
 33     
 34     printk("cnt: %02d, ", cnt++);
 35     for(i = 0; i < len; ++i)
 36     {
 37         printk("%02x ", buf_mem[i]);
 38     }
 39     printk("\n");
 40 #else
 41     static unsigned char preval = 0;    /* 用於存儲上一個數據 */
 42     if((preval & 1) != (buf_mem[0] & 1)) {    /* 鼠標左鍵數據有變化 */
 43         input_report_key(usbinput, KEY_L, (buf_mem[0] & 1) ? 1 : 0);
 44         input_sync(usbinput);
 45     }
 46     if((preval & (1 << 1)) != (buf_mem[0] & (1 << 1))) {
 47         input_report_key(usbinput, KEY_S,   (buf_mem[0] & (1 << 1)) ? 1 : 0);
 48         input_sync(usbinput);
 49     }
 50     if((preval & (1 << 2)) != (buf_mem[0] & (1 << 2))) {
 51         input_report_key(usbinput, KEY_ENTER,   (buf_mem[0] & (1 << 2)) ? 1 : 0);
 52         input_sync(usbinput);
 53     }
 54     preval = buf_mem[0];
 55 #endif
 56 
 57     usb_submit_urb(usb_urb, GFP_KERNEL);
 58 
 59 }
 60 
 61 static int mouse_probe(struct usb_interface *intf, const struct usb_device_id *id)
 62 {
 63     struct usb_device *dev = interface_to_usbdev(intf);
 64     struct usb_host_interface *interface;
 65     struct usb_endpoint_descriptor *endpoint;
 66     unsigned int pipe;
 67     int ret;
 68 
 69     interface = intf->cur_altsetting;
 70     endpoint = &interface->endpoint[0].desc;
 71 
 72     /* 1. 輸入子系統設置 */
 73     usbinput = input_allocate_device();
 74 
 75     set_bit(EV_KEY, usbinput->evbit);
 76     set_bit(EV_REP, usbinput->evbit);
 77 
 78     set_bit(KEY_L, usbinput->keybit);
 79     set_bit(KEY_S, usbinput->keybit);
 80     set_bit(KEY_ENTER, usbinput->keybit);
 81 
 82     ret = input_register_device(usbinput);
 83 
 84     /* 2. urb設置 */
 85     usb_urb = usb_alloc_urb(0, GFP_KERNEL);
 86     pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
 87     
 88     len = endpoint->wMaxPacketSize;
 89     buf_mem = usb_alloc_coherent(dev, len, GFP_ATOMIC, &usb_buf_phys);
 90 
 91     usb_fill_int_urb(usb_urb, dev, pipe, buf_mem, len, 
 92         usb_mouse_irq, NULL, endpoint->bInterval);
 93     usb_urb->transfer_dma = usb_buf_phys;
 94     usb_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
 95 
 96     return usb_submit_urb(usb_urb, GFP_KERNEL);
 97 }
 98 
 99 static void mouse_disconnect(struct usb_interface *intf)
100 {
101     struct usb_device *dev = interface_to_usbdev(intf);
102 
103     usb_kill_urb(usb_urb);
104     usb_free_urb(usb_urb);
105 
106     usb_free_coherent(dev, len, buf_mem, usb_buf_phys);
107     input_unregister_device(usbinput);
108     input_free_device(usbinput);
109 }
110 
111 static struct usb_driver mouse_driver = {
112     .name  = "itop4412_mouse",
113     .probe = mouse_probe,
114     .disconnect = mouse_disconnect,
115     .id_table   = mouse_table,
116 };
117 
118 static int __init usbmouse_init(void)
119 {
120     return usb_register(&mouse_driver);
121 }
122  
123 static void __exit usbmouse_exit(void)
124 {
125     usb_deregister(&mouse_driver);
126 }
127  
128 /* 聲明段屬性 */
129 module_init(usbmouse_init);
130 module_exit(usbmouse_exit);
131 
132 MODULE_LICENSE("GPL");
View Code

Makefile:

 1 KERN_DIR = /work/itop4412/tools/linux-3.5
 2 
 3 all:
 4     make -C $(KERN_DIR) M=`pwd` modules 
 5 
 6 clean:
 7     make -C $(KERN_DIR) M=`pwd` modules clean
 8     rm -rf modules.order
 9 
10 obj-m    += usbmouse.o
View Code

 

測試:

在編譯並在開發板上insmod后,會出現如下信息:

input: Unspecified device as /devices/virtual/input/input4

插入USB鼠標后,會出現如下信息:

sbcore: registered new interface driver itop4412_mouse

 

接下來執行:

# ls /dev/event*

最后一個/dev/event2為USB鼠標對應事件

執行:

#hexdump /dev/event2

接下來按鍵,效果如下圖:

在此我以第一行數據為例解釋一下各個數字的意義:

0000000 0393 0000 55a4 0008   0001    001f    0001 0000

 次數    秒    微秒    按鍵類  哪個按鍵    按下

 

 

下一章  17、塊設備驅動

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM