20.Linux-USB鼠標驅動


在上一章分析完USB總線驅動程序后, 接下來開始寫一個USB驅動:

本節目的: 將USB鼠標的左鍵當作L按鍵,將USB鼠標的右鍵當作S按鍵,中鍵當作回車按鍵


 

參考/drivers/hid/usbhid/usbmouse.c(內核自帶的USB鼠標驅動)

1.本節需要用到的宏如下:

struct usb_device_id usbmouse_id_table []=USB_INTERFACE_INFO(cl,sc,pr);          

USB_INTERFACE_INFO()設置usb_driver驅動的id_table成員

cl:接口類,我們USB鼠標為HID類,所以填入0X03,也就是USB_INTERFACE_CLASS_HID

sc:接口子類為啟動設備,填入USB_INTERFACE_SUBCLASS_BOOT

pr:接口協議為鼠標協議,填入USB_INTERFACE_PROTOCOL_MOUSE 

 

        

struct usb_device *dev=interface_to_usbdev(intf);  

 通過usb_ interface接口獲取usb_device設備,為后面設置USB數據傳輸用

 

 

pipe=usb_rcvintpipe(dev,endpoint);

創建一個接收(rcv)中斷(int)類型的端點管道(pipe),用來端點和數據緩沖區之間的連接,鼠標為接收中斷型

dev: usb_device設備結構體

endpoint:為端點描述符的成員endpoint->bEndpointAddress   //端點地址

  • 對於控制類型的端點管道使用: usb_sndctrlpipe()/usb_rcvctrlpipe()
  • 對於實時類型的端點管道使用: usb_sndisocpipe()/usb_sndisocpipe()
  • 對於批量類型的端點管道使用: usb_sndbulkpipe()/usb_rcvbulkpipe()

 

2.本節需要用到的函數如下:

usb_deregister(struct usb_driver *driver);

注冊一個usb_driver驅動,然后內核會通過usb_driver的成員.id_table函數匹配一次USB設備,匹配成功就會調用usb_driver的成員.probe函數

 

 

usb_deregister(struct usb_driver *driver);

注銷一個usb_driver驅動,在出口函數中寫

 

 

*usb_buffer_alloc(struct usb_device *dev,size_t size,gfp_t mem_flags,dma_addr_t *dma);

分配一個usb緩沖區,該緩存區的物理地址會與虛擬地址的數據一致,分配成功返回一個char型緩沖區虛擬地址

*dev: usb_device設備結構體

size:分配的緩沖區大小,這里填端點描述符的成員endpoint->wMaxPacketSize          //端點最大包長

mem_flags:分配內存的參數,這里填GFP_ATOMIC,表示從不睡眠

dma:分配成功則會返回一個DMA緩沖區物理地址

void usb_buffer_free(struct usb_device *dev,size_t size,void *addr,dma_addr_t dma);

注銷分配的usb緩沖區,在usb_driver的disconnect成員函數中使用

addr:要注銷的緩沖區虛擬地址

dma: 要注銷的DMA緩沖區虛擬地址

 

 

struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags);

分配一個urb數據結構體, 分配成功返回一個urb結構體

urb全稱為usb request block,USB傳輸數據時,就是打包成urb結構體來傳輸

iso_packets:表示iso類型的包個數,這里我們不是iso類型包,直接填0

mem_flags:分配內存的參數,這里填入GFP_KERNEL,正常分配

其中urb結構體如下所示:

struct urb
{
 ... ...
 struct usb_device *dev;             //指向usb設備
 struct usb_host_endpoint *ep;    //指向端點的數據結構 
 unsigned int pipe;                  //指向端點管道(pipe), 本節的pipe通過usb_rcvintpipe()宏獲取

 int status;                                 //狀態,當status==0,表示數據被成功地收到/發送
 
 unsigned int transfer_flags;     //傳輸狀態
 ... ...
/*以下兩個緩沖區通過usb_buffer_alloc ()函數獲取 */
//urb結構體默認的transfer_flags是URB_NO_SETUP_DMA_MAP ,也就是說沒有提供DMA的緩沖區
//就會使用transfer_buffer虛擬地址緩沖區來當緩沖區
//當支持DMA緩沖區時,就需要手動設置transfer_flags =URB_NO_TRANSFER_DMA_MAP,並手動設置transfer_dma等於獲取到的DMA物理地址

 void *transfer_buffer;                //虛擬緩沖區
 dma_addr_t transfer_dma;          //DMA物理緩沖區 
... ...
};

 

 

void usb_free_urb(struct urb *urb);

釋放申請的urb,在usb_driver的disconnect成員函數中使用

 

 

static inline void usb_fill_int_urb(struct urb *urb,struct usb_device *dev,unsigned int pipe,

                      void *transfer_buffer,int buffer_length,
                      usb_complete_t complete_fn,void *context,int interval);

初始化中斷型端點的urb數據結構體

針對批量型端點的urb使用usb_fill_bulk_urb()
針對控制型端點的urb使用usb_fill_control_urb()
針對等時型端點的urb  需要手動初始化。

urb:指向要初始化的urb

dev:指向要傳輸的usb設備

pipe:要傳輸的端點管道, 本節的pipe通過usb_rcvintpipe()宏獲取

transfer_buffer:指向要傳輸數據的虛擬地址緩沖區

buffer_length:數據大小, 這里填端點描述符的成員endpoint->wMaxPacketS //端點最大包長

complete_fn:數據傳輸完成后產生的中斷函數

context:會放在urb->context結構成員中,用來給中斷函數用,本節不需要,填NULL即可

interval:間隔時間,表示間隔多少時間讀一次數據,填入endpoint-> bInterval即可

 

 

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

提交urb到內核,初始化urb和中斷函數退出時,都要重新提交一次,告訴內核初始化內存緩存等

 

 

void usb_kill_urb(struct urb *urb);

殺掉urb,在usb_driver的disconnect成員函數中使用

 

3.步驟如下:

首先先定義全局變量:usb_driver結構體,input_dev指針結構體 ,虛擬地址緩存區,DMA地址緩存區

3.1在入口函數中

1)通過usb_register()函數注冊usb_driver結構體

3.2在usb_driver的probe函數中

1)分配一個input_dev結構體

2)設置input_dev支持L、S、回車、3個按鍵事件

3)注冊input_dev結構體

4)設置USB數據傳輸:

 ->4.1)通過usb_rcvintpipe()創建一個接收中斷類型的端點管道,用來端點和數據緩沖區之間的連接

 ->4.2)通過usb_buffer_alloc()申請USB緩沖區

 ->4.3)申請並初始化urb結構體,urb:用來傳輸數據

 ->4.4) 因為我們2440支持DMA,所以要告訴urb結構體,使用DMA緩沖區地址

 ->4.5)使用usb_submit_urb()提交urb

3.3在鼠標中斷函數中

1)判斷緩存區數據是否改變,若改變則上傳鼠標事件

2)使用usb_submit_urb()提交urb

3.4.在usb_driver的disconnect函數中

1)通過usb_kill_urb()殺掉提交到內核中的urb

2)釋放urb

3)釋放USB緩存區

4)注銷input_device,釋放input_device

3.5在出口函數中

1)通過usb_deregister ()函數注銷usb_driver結構體

 

4.代碼如下:

#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/usb/input.h>
#include <linux/hid.h>

static struct input_dev *myusb_mouse_dev;              //input_dev 
static char *myusb_mouse_buf; //虛擬地址緩存區 static dma_addr_t myusb_mouse_phyc; //DMA緩存區; static __le16 myusb_mouse_size; //數據包長度 static struct urb *myusb_mouse_urb; //urb static void myusb_mouse_irq(struct urb *urb) //鼠標中斷函數 { static char buf1=0; //for(i=0;i<myusb_mouse_size;i++) // printk("%02x ",myusb_mouse_buf[i]); // printk("\n"); /*bit 1-左右中鍵 0X01:左鍵 0X02:右鍵 0x04:中鍵 */ if((buf1&(0X01)) != (myusb_mouse_buf[1]&(0X01))) { input_report_key(myusb_mouse_dev, KEY_L, buf1&(0X01)? 1:0); input_sync(myusb_mouse_dev); } if((buf1&(0X02)) != (myusb_mouse_buf[1]&(0X02))) { input_report_key(myusb_mouse_dev, KEY_S, buf1&(0X02)? 1:0); input_sync(myusb_mouse_dev); } if((buf1&(0X04)) != (myusb_mouse_buf[1]&(0X04)) ) { input_report_key(myusb_mouse_dev, KEY_ENTER, buf1&(0X04)? 1:0); input_sync(myusb_mouse_dev); } buf1=myusb_mouse_buf[1]; //更新數據 usb_submit_urb(myusb_mouse_urb, GFP_KERNEL); } static int myusb_mouse_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct usb_device *dev = interface_to_usbdev(intf); //設備 struct usb_endpoint_descriptor *endpoint; struct usb_host_interface *interface; //當前接口 int pipe; //端點管道 interface=intf->cur_altsetting; endpoint = &interface->endpoint[0].desc; //當前接口下的端點描述符 printk("VID=%x,PID=%x\n",dev->descriptor.idVendor,dev->descriptor.idProduct); //打印VID,PID /* 1)分配一個input_dev結構體 */ myusb_mouse_dev=input_allocate_device(); /* 2)設置input_dev支持L、S,回車、3個按鍵事件*/ set_bit(EV_KEY, myusb_mouse_dev->evbit); set_bit(EV_REP, myusb_mouse_dev->evbit); //支持重復按功能
set_bit(KEY_L, myusb_mouse_dev->keybit); set_bit(KEY_S, myusb_mouse_dev->keybit); set_bit(KEY_ENTER, myusb_mouse_dev->keybit); /* 3)注冊input_dev結構體*/ input_register_device(myusb_mouse_dev); /* 4)設置USB數據傳輸 */ /*->4.1)通過usb_rcvintpipe()創建一個端點管道*/ pipe=usb_rcvintpipe(dev,endpoint->bEndpointAddress); /*->4.2)通過usb_buffer_alloc()申請USB緩沖區*/ myusb_mouse_size=endpoint->wMaxPacketSize; myusb_mouse_buf=usb_buffer_alloc(dev,myusb_mouse_size,GFP_ATOMIC,&myusb_mouse_phyc); /*->4.3)通過usb_alloc_urb()和usb_fill_int_urb()申請並初始化urb結構體 */ myusb_mouse_urb=usb_alloc_urb(0,GFP_KERNEL);
usb_fill_int_urb (myusb_mouse_urb,
//urb結構體 dev, //usb設備 pipe, //端點管道 myusb_mouse_buf, //緩存區地址 myusb_mouse_size, //數據長度 myusb_mouse_irq, //中斷函數 0, endpoint->bInterval); //中斷間隔時間 /*->4.4) 因為我們2440支持DMA,所以要告訴urb結構體,使用DMA緩沖區地址*/ myusb_mouse_urb->transfer_dma =myusb_mouse_phyc; //設置DMA地址 myusb_mouse_urb->transfer_flags =URB_NO_TRANSFER_DMA_MAP; //設置使用DMA地址 /*->4.5)使用usb_submit_urb()提交urb*/ usb_submit_urb(myusb_mouse_urb, GFP_KERNEL); return 0; } static void myusb_mouse_disconnect(struct usb_interface *intf) { struct usb_device *dev = interface_to_usbdev(intf); //設備 usb_kill_urb(myusb_mouse_urb); usb_free_urb(myusb_mouse_urb); usb_buffer_free(dev, myusb_mouse_size, myusb_mouse_buf,myusb_mouse_phyc); input_unregister_device(myusb_mouse_dev); //注銷內核中的input_dev input_free_device(myusb_mouse_dev); //釋放input_dev } static struct usb_device_id myusb_mouse_id_table [] = { { USB_INTERFACE_INFO( USB_INTERFACE_CLASS_HID, //接口類:hid類 USB_INTERFACE_SUBCLASS_BOOT, //子類:啟動設備類 USB_INTERFACE_PROTOCOL_MOUSE) }, //USB協議:鼠標協議 }; static struct usb_driver myusb_mouse_drv = { .name = "myusb_mouse", .probe = myusb_mouse_probe, .disconnect = myusb_mouse_disconnect, .id_table = myusb_mouse_id_table, }; /*入口函數*/ static int myusb_mouse_init(void) { usb_register(&myusb_mouse_drv); return 0; } /*出口函數*/ static void myusb_mouse_exit(void) { usb_deregister(&myusb_mouse_drv); } module_init(myusb_mouse_init); module_exit(myusb_mouse_exit); MODULE_LICENSE("GPL");

5.測試運行

5.1 重新設置編譯內核(去掉默認的hid_USB驅動)

make menuconfig ,進入menu菜單重新設置內核參數:

進入-> Device Drivers -> HID Devices 

<> USB Human Interface Device (full HID) support //hid:人機交互的USB驅動,比如鼠標,鍵盤等

然后make uImage 編譯內核

將新的觸摸屏驅動模塊放入nfs文件系統目錄中

5.2然后燒寫內核,裝載觸摸屏驅動模塊

如下圖,當我們插上USB鼠標時,可以看到該VID和PID,和電腦上的鼠標的參數一樣

 

5.3使用hexdump命令來調試

(hexdump命令調試代碼詳解地址:http://www.cnblogs.com/lifexy/p/7553550.html)

 

5.4 使用tty1進程測試

 

 

未完待續~~~~~~~~~~  下節 依葫蘆畫瓢 來寫出 USB鍵盤驅動

 


 


免責聲明!

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



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