USB滑鼠裝置驅動程式簡單實現(一)
阿新 • • 發佈:2019-02-02
一、Linux下的USB驅動程式
分離和分層是Linux下驅動程式開發採用的最基本的形式,USB驅動開發在主機端主要涉及兩個部分:主機控制器驅動和裝置驅動。
主機控制器驅動主要是和具體的Soc相關的,它來識別USB裝置,安裝對應的裝置驅動程式,提供對USB裝置的讀寫函式。
裝置驅動主要是根據具體的USB裝置對USB主機驅動提供的讀寫函式獲得的資料進行處理,實現這種USB裝置特有的功能。具體的層次結構如下所示:
基本的開發環境:
作業系統:Ubuntu12.04
核心:Linux-3.0.86
GUI :Qtopia2.2.0
交叉編譯工具gcc版本 : 4.5.1
二、USB滑鼠裝置驅動的實現
這一部分主要完成的功能是:實現一個簡單的USB滑鼠的裝置驅動程式,讀取滑鼠進行操作所產生的原始的資料。
1、準備工作
預設的在Linux核心當中已經配置上了USB滑鼠相關的驅動,所以為了自己編寫的驅動程式能載入進核心和使用,先要去掉核心上的USB滑鼠驅動。進入核心目錄,執行make menuconfig,執行如下配置:
-> Device Drivers
-> HID Devices
[ ]USB Human Interface Device (full HID) support
2、裝置驅動編寫
a、分配、設定、註冊一個usb_driver的結構體變數,為了方便定義瞭如下一個結構體:
定義一個usb_driver的結構體,並設定它:/* 定義一個描述USB的結構體 */ struct yl_usbmouse { dma_addr_t usb_buf_phys; /* 描述分配的緩衝區的實體地址 */ char *usb_buf_virt; /* 描述分配的緩衝區的虛擬地址 */ int usb_buf_len; /* 用來描述緩衝區的大小 */ struct urb *urb; /* 描述usb的請求塊 */ };
/* 定義一個usb_driver的結構體變數 */
static struct usb_driver yl_usb_mouse_driver = {
.name = "yl_usbmouse",
.probe = yl_usb_mouse_probe,
.disconnect = yl_usb_mouse_disconnect,
.id_table = yl_usb_mouse_id_table,
};
在入口函式中註冊這個結構體:
b、分配、設定、初始化、提交一個urb,urb是用來傳遞USB主機控制器驅動的資料。當插入的裝置和這個usb_driver匹配時,它的probe函式將會呼叫,我們在probe函式當中實現對urb的一些列操作./* 模組的入口函式 */ static int __init yl_usb_mouse_init(void) { /* 註冊一個usb_driver的結構體變數 */ usb_register(&yl_usb_mouse_driver); return 0; }
分配一個urb:
/* 1、分配一個usb請求塊 */
g_yl_usbmouse.urb = usb_alloc_urb(0, GFP_KERNEL);
設定、初始化這個urb:
/* 獲取USB裝置的某個端點 */
pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
/* 獲取傳輸的資料的長度 */
g_yl_usbmouse.usb_buf_len = endpoint->wMaxPacketSize;
/* 分配一塊緩衝區用來存放usb滑鼠的資料 */
g_yl_usbmouse.usb_buf_virt = usb_alloc_coherent(dev, g_yl_usbmouse.usb_buf_len, GFP_ATOMIC, &g_yl_usbmouse.usb_buf_phys);
/* 2、初始化這個usb請求塊 */
usb_fill_int_urb(g_yl_usbmouse.urb, dev, pipe, g_yl_usbmouse.usb_buf_virt, g_yl_usbmouse.usb_buf_len, yl_usbmouse_irq, NULL, endpoint->bInterval);
g_yl_usbmouse.urb->transfer_dma = g_yl_usbmouse.usb_buf_phys;
g_yl_usbmouse.urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
提交這個urb:
/* 3、提交這個usb請求塊 */
usb_submit_urb(g_yl_usbmouse.urb, GFP_KERNEL);
c、usb滑鼠獲取資料的中斷處理函式,這個函式是在urb初始化的時候傳遞進來的回撥函式,滑鼠發生動作時便會觸及這個函式的呼叫,把滑鼠的資料傳遞進來。它的具體的實現如下所示:
/* usb滑鼠的中斷處理函式 */
static void yl_usbmouse_irq(struct urb *urb)
{
int i = 0;
/* 依次把資料打印出來 */
printk("yl_usbmouse data : ");
for(i = 0; i < g_yl_usbmouse.usb_buf_len; i++)
{
printk("0x%x ", g_yl_usbmouse.usb_buf_virt[i]);
}
printk("\n");
/* 再次提交urb */
usb_submit_urb(g_yl_usbmouse.urb, GFP_ATOMIC);
}
這個函式實現的功能很簡單:就是把滑鼠產生的原始資料列印到終端上即可。
3、編譯並安裝驅動,在開發板上接入滑鼠實驗結果如下:
從上面可以看出,滑鼠每操作一次會一次性產生7個位元組的資料。可以看出這些資料本身只是一些普通的資料,沒有任何意義,那怎麼讓滑鼠產生的這些資料發揮作用呢,就需要將usb滑鼠和輸入子系統結合使用,才能真正發揮USB滑鼠的作用。具體實現見下文。
附錄:本文實現的完整的例程原始碼如下所示:
#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>
/* 定義一個描述USB的結構體 */
struct yl_usbmouse {
dma_addr_t usb_buf_phys; /* 描述分配的緩衝區的實體地址 */
char *usb_buf_virt; /* 描述分配的緩衝區的虛擬地址 */
int usb_buf_len; /* 用來描述緩衝區的大小 */
struct urb *urb; /* 描述usb的請求塊 */
};
/* 定義一個描述USB結構體的變數 */
static struct yl_usbmouse g_yl_usbmouse;
/* usb滑鼠的中斷處理函式 */
static void yl_usbmouse_irq(struct urb *urb)
{
int i = 0;
/* 依次把資料打印出來 */
printk("yl_usbmouse data : ");
for(i = 0; i < g_yl_usbmouse.usb_buf_len; i++)
{
printk("0x%x ", g_yl_usbmouse.usb_buf_virt[i]);
}
printk("\n");
/* 再次提交urb */
usb_submit_urb(g_yl_usbmouse.urb, GFP_ATOMIC);
}
/* 匹配裝置成功時呼叫的探測函式 */
static int yl_usb_mouse_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
struct usb_device *dev = interface_to_usbdev(intf);
struct usb_host_interface *interface;
struct usb_endpoint_descriptor *endpoint;
int pipe;
/* 獲取介面和端點資訊 */
interface = intf->cur_altsetting;
endpoint = &interface->endpoint[0].desc;
/* 獲取USB裝置的某個端點 */
pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
/* 獲取傳輸的資料的長度 */
g_yl_usbmouse.usb_buf_len = endpoint->wMaxPacketSize;
/* 分配一塊緩衝區用來存放usb滑鼠的資料 */
g_yl_usbmouse.usb_buf_virt = usb_alloc_coherent(dev, g_yl_usbmouse.usb_buf_len, GFP_ATOMIC, &g_yl_usbmouse.usb_buf_phys);
/* 1、分配一個usb請求塊 */
g_yl_usbmouse.urb = usb_alloc_urb(0, GFP_KERNEL);
/* 2、初始化這個usb請求塊 */
usb_fill_int_urb(g_yl_usbmouse.urb, dev, pipe, g_yl_usbmouse.usb_buf_virt, g_yl_usbmouse.usb_buf_len, yl_usbmouse_irq, NULL, endpoint->bInterval);
g_yl_usbmouse.urb->transfer_dma = g_yl_usbmouse.usb_buf_phys;
g_yl_usbmouse.urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
/* 3、提交這個usb請求塊 */
usb_submit_urb(g_yl_usbmouse.urb, GFP_KERNEL);
return 0;
}
/* usb裝置拔除時呼叫的函式 */
static void yl_usb_mouse_disconnect(struct usb_interface *intf)
{
struct usb_device *dev = interface_to_usbdev(intf);
usb_kill_urb(g_yl_usbmouse.urb);
usb_free_urb(g_yl_usbmouse.urb);
usb_free_coherent(dev, g_yl_usbmouse.usb_buf_len, g_yl_usbmouse.usb_buf_virt, g_yl_usbmouse.usb_buf_phys);
}
/* 定義一個id_table的陣列,當usb裝置插入時進行比較和判斷 */
static struct usb_device_id yl_usb_mouse_id_table [] = {
{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
USB_INTERFACE_PROTOCOL_MOUSE) },
{ } /* Terminating entry */
};
/* 定義一個usb_driver的結構體變數 */
static struct usb_driver yl_usb_mouse_driver = {
.name = "yl_usbmouse",
.probe = yl_usb_mouse_probe,
.disconnect = yl_usb_mouse_disconnect,
.id_table = yl_usb_mouse_id_table,
};
/* 模組的入口函式 */
static int __init yl_usb_mouse_init(void)
{
/* 註冊一個usb_driver的結構體變數 */
usb_register(&yl_usb_mouse_driver);
return 0;
}
/* 模組的出口函式 */
static void __exit yl_usb_mouse_exit(void)
{
/* 登出一個usb_driver的結構體變數 */
usb_deregister(&yl_usb_mouse_driver);
}
module_init(yl_usb_mouse_init);
module_exit(yl_usb_mouse_exit);
MODULE_LICENSE("GPL");