上一篇博文只是usb總線驅動程序的框架,下面來真正寫一個usb驅動程序。
USB鼠標驅動,鼠標輸入HID類型,其數據傳輸採用中斷URB,鼠標端點類型爲IN
目的:usb鼠標按鍵的驅動代碼編寫:
框架:
- 分配一個input_dev結構體
- 設置
- 註冊
- 硬件相關的操作
思路:
1、分配/設置usb_driver結構體
static struct usb_driver usb_mouse_driver = {
.name = "usbmouse",
.probe = usb_mouse_probe,
.disconnect = usb_mouse_disconnect,
.id_table = usb_mouse_id_table,
};
a.這個結構體中有兩個函數:1、dev和drv匹配之後會調用probe函數,2、usb鼠標拔出後會調用disconnect函數3、id_table 支持項
支持項中有三個匹配項:接口類,接口子類,接口協議
usb鼠標使用的支持項爲:
static struct usb_device_id usb_mouse_id_table [] = {
{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
USB_INTERFACE_PROTOCOL_MOUSE) },
{ } /* Terminating entry */
};
b.大部分工作都在probe函數中完成,先寫出usb_mouse_probe函數原型,逐漸完善它,我們truct usb_interface *intf, const struct usb_device_id *id)
static int usb_mouse_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
return 0;
}
以上就是基本的框架,只要usb鼠標插上之後,就會調用 probe函數---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
下面完成具體的操作:
c、還是用輸入子系統的套路:
- 分配
- 設置
- 註冊
- 硬件的相關操作
c.1分配
static struct input_dev *um_dev;
um_dev = input_allocate_device();
這樣就分配了一個input_dev結構體。
c.2設置
需要設置兩方面的內容:1能產生哪些類事件,2能產生這個這些類裏面的具體哪個事件。
/*b.1能產生哪類事件*/
set_bit(EV_KEY,um_dev->evbit);
set_bit(EV_REP,um_dev->evbit);
/*b.2能產生這類裏面的哪些事件*/
set_bit(KEY_L,um_dev->keybit);
set_bit(KEY_S,um_dev->keybit);
set_bit(KEY_ENTER,um_dev->keybit);
EV_KEY:按鍵事件。EV_REP:重複事件。
c.3註冊
只需要註冊 input_dev 結構體:
input_register_device(um_dev);
c.4硬件的相關操作
這個部分主要是設置數據的傳輸。數據傳輸圍繞三個要素:源、目的、長度
源:
pipe = usb_rcvintpipe(dev,endpoint->bEndpointAddress);
根據中斷端點獲取源。
長度:
length = endpoint->wMaxPacketSize;
目的:
usb_buf = usb_buffer_alloc(dev,length,GFP_ATOMIC,&usb_buf_phys);
usb數據會存儲在 usb_buf中。
d。使用這三個要素,完成最終的驅動程序-----》使用urb
urb處理流程:
- USB設備驅動程序創建並初始化一個訪問特定USB設備特定端點的urb,並提交給USB core
- USB core提交該urb到到USB主控制器驅動程序。
- USB主控制器驅動程序根據該urb描述的信息,來訪問usb設備
- 當設備訪問結束後,USB的主控制器驅動程序通知USB設備驅動程序。
um_urb = usb_alloc_urb(0,GFP_KERNEL);
/*使用三要素設置 urb*/
usb_fill_int_urb(um_urb, dev, pipe,usb_buf,length,
usb_mouse_irq, NULL, endpoint->bInterval);
um_urb->transfer_dma = usb_buf_phys;
um_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
/*使用URB( 提交 urb)*/
usb_submit_urb(um_urb,GFP_KERNEL);
說明1:usb_alloc_urb(int iso_packets, gfp_t mem_flags);
iso_packets :是這個urb應當包含的等時數據包數目,若爲0:表示不創建等時數據包。
mem_flags :是分配內存的標誌。
說明2:
對於中斷urb,使用usb_fill_int_urb來初始化urb。函數定義如下:
static inline void usb_fill_int_urb(struct urb *urb,
struct usb_device *dev, //指向這個urb要被髮送的usb設備
unsigned int pipe, //源
void *transfer_buffer, //目的
int buffer_length, //長度
usb_complete_t complete_fn, //這個urb完成時,被調用的完成處理函數
void *context, //完成處理函數的“上下文”
int interval) //urb應當被調用的間隔
那麼在完成處理函數中就可以完成我們想做的東西:
例如,打印 usb鼠標傳回的數據
static void usb_mouse_irq(struct urb *urb)
{
static unsigned char pre_val;
int i;
static int cnt =0;
printk("data cnt %d ",++cnt);
for(i=0;i<length;i++)
{
printk("%02x ",usb_buf[i]);
}
printk("\n");
}
這樣,一個完整的驅動程序基本就寫好了。完整的驅動代碼,我會在後面使用csdncode 地址的形式給出
測試:
1、make menuconfig 去掉原來的usb鼠標驅動
-> Device Drivers
-> HID Devices (HID_SUPPORT [=y])
2、make
3、使用新內核
4、加載驅動
5、可以使用 hexdump /dev/event1 查看 插入USB鼠標新生成的event
6、或者使用 cat /dev/tty1