1. 程式人生 > >USB滑鼠裝置驅動程式簡單實現(一)

USB滑鼠裝置驅動程式簡單實現(一)

一、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的結構體 */
struct yl_usbmouse {
	dma_addr_t usb_buf_phys;	/* 描述分配的緩衝區的實體地址 */
	char *usb_buf_virt;		/* 描述分配的緩衝區的虛擬地址 */
	int usb_buf_len;		/* 用來描述緩衝區的大小 */
	struct urb *urb;		/* 描述usb的請求塊 */
};
定義一個usb_driver的結構體,並設定它:
/* 定義一個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;
}
b、分配、設定、初始化、提交一個urb,urb是用來傳遞USB主機控制器驅動的資料。當插入的裝置和這個usb_driver匹配時,它的probe函式將會呼叫,我們在probe函式當中實現對urb的一些列操作.

分配一個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");