基于ok6410的韦东山驱动视频简要分析--USB驱动 .

注意:本篇讲的鼠标驱动仅能实现鼠标左右键跟滑轮这三个按键类似button的功能,按下左键则打出"l",右键打出“s”,滑轮打出“enter”。如果要实现正常的鼠标驱动,参考内核的鼠标驱动,修改input的一些参数即可。

 

一、写驱动的步骤(新手稍微看下即可,内容有点搞)

1、复制头文件;

2、写入口函数,出口函数,再加上协议;

3、分配注册usb_driver结构体(拷别人的),

static struct usb_driver usb_mk_driver = {};

在init中注册该结构体:usb_register(&usb_mk_driver);

在exit中注销该结构体:usb_deregister(&usb_mk_driver);

4、写id_table函数,static struct usb_device_id usb_mk_id_table [] = {},只有满足id_table中的各种类别,子类号,协议,才能调用;

5、写probe函数:static int usb_mk_probe(struct usb_interface *intf, const struct usb_device_id *id);

6、写disconnect函数:static void usb_mk_disconnect(struct usb_interface *intf)

7、如果想在开发板插入usb设备的时候打印设备信息(可以不用),可以在probe中加入:

printk("VID = 0x%x, PID = 0x%x\n", dev->descriptor.idVendor, dev->descriptor.idProduct);

printk("USB VERS = 0x%x, PID = 0x%x\n", dev->descriptor.bcdUSB);

8、probe之后要进一步判断你这个是不是鼠标:

1、分配一个input_dev结构体:在头文件下定义;static struct input_dev       *mk_dev;  在probe内:mk_dev = input_allocate_device();

2、设置:

1、能产生哪类事件

set_bit(EV_KEY, mk_dev->evbit);

set_bit(EV_REP, mk_dev->evbit);

2、能产生哪些事件

set_bit(KEY_L, mk_dev->keybit);

set_bit(KEY_S, mk_dev->keybit);

set_bit(KEY_ENTER, mk_dev->keybit);

3、注册:input_register_device(mk_dev);

4、设置:数据传输3要素: 源, 目的, 长度

先从usbmouse.c中拷入几行代码:

struct usb_device *dev = interface_to_usbdev(intf);

struct usb_host_interface *interface;

struct usb_endpoint_descriptor *endpoint;

int pipe;

endpoint = &interface->endpoint[0].desc;

源:usb设备的某个端点

pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);

目的:需要设置缓冲区buffer:

buf = usb_alloc_coherent(dev, len, GFP_ATOMIC, &buf_phys);

在头文件下定义一个缓冲区:

static char *buf; 

static dma_addr_t buf_phys;

长度:static  int len(作为全局变量,等会有用)

len = endpoint->wMaxPacketSize;

5、怎么用那三要素呢?

在probe中;

1、分配一个urb,mk_urb = usb_alloc_urb(0, GFP_KERNEL);

在头文件下定义urb:static struct urb *mk_urb;

2、使用三要素填充urb:

usb_fill_int_urb(mk_urb, dev, pipe, buf,

 len,

 uk_callback, NULL, endpoint->bInterval);

mk_urb->transfer_dma = buf_phys;

mk_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;

 

3、使用URB:usb_submit_urb(mk_urb, GFP_KERNEL);

6、要写一个中断函数,原因是usb控制器收到从usb传来的数据后,需要一个中断来通知cpu有信号啦!static void uk_callback(struct urb *urb)

在中断函数中定义一些唧唧歪歪的东西后看,重新提交urb:

usb_submit_urb(mk_urb, GFP_KERNEL);

7,提交urb后当然还要做杀掉urb,在disconnect中杀掉它!usb_kill_urb(mk_urb);

顺便再disconnect中注销或卸载其他的函数;

8、在中断函数里上报事件。

 

**************************************************************************************************************

 

二、驱动程序

#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>
#include <linux/input.h>

/* 参考drivers\hid\usbhid\usbmouse.c
 */

static struct input_dev *mk_dev;
static int len;
static char *buf;  //定义一个缓冲区
static dma_addr_t buf_phys; //dma_addr实际上是一个物理地址
static struct urb *mk_urb;

static struct usb_device_id usb_mk_id_table [] = {
	{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
		USB_INTERFACE_PROTOCOL_MOUSE) },
/*只要它的类是class_hid,子类是boot,协议是mouse,那么就可以匹配*/
 *{USB_DEVICE(0x46d, 0xc52f)或者直接指定自己的usb设备只支持这种厂家的设备。*/
	{ }	/* Terminating entry */
};

/* 当USB主机控制器获得鼠标数据后,
 * 会调用这个函数
 */
static void uk_callback(struct urb *urb)//这是一个中断函数!
{
	int i;
	static char pre_val;
#if 0	//这一段都用不到了,因为if = 0 ;这一段的作用就是用来看鼠标按下之后都显示什么数值,最后通过这些数值来设置上报事件。
	printk("Get datas:\n");
	for (i = 0; i < len; i++)
	{
		printk("%02x ", buf[i]);
	}
	printk("\n");
#endif
	/* 鼠标数据含义:
	 * buf[0]: bit0-左键, 0-松开, 1-按下
	 *         bit1-右键, 0-松开, 1-按下
	 *         bit2-中键, 0-松开, 1-按下
	 * buf[1],buf[2]构成一个整数, 表示X方向的相对位移
	 *         >0 : 右移
	 *         <0 : 左移
	 * buf[3],buf[4]构成一个整数, 表示Y方向的相对位移
	 *         >0 : 下移
	 *         <0 : 上移
	 * buf[6]: 滚轮
	 */
	 
	/* 确定按键值 */
	/* 上报数据 */
	if ((pre_val & (1<<0)) != (buf[0] & (1<<0))) //如果上次数据的bit0不等于这次数据的bit0,那么就是左键发生变化
	{
		/* 左键按下或松开 */
		input_event(mk_dev, EV_KEY, KEY_L, (buf[0] & (1<<0)) ? 1 : 0);//如果buf0=1的话那就是按下,既是1,否则为0;
		input_sync(mk_dev);
	}

	if ((pre_val & (1<<1)) != (buf[0] & (1<<1)))
	{
		/* 右键按下或松开 */
		input_event(mk_dev, EV_KEY, KEY_S, (buf[0] & (1<<1)) ? 1 : 0);
		input_sync(mk_dev);
	}

	if ((pre_val & (1<<2)) != (buf[0] & (1<<2)))
	{
		/* 中键按下或松开 */
		input_event(mk_dev, EV_KEY, KEY_ENTER, (buf[0] & (1<<2)) ? 1 : 0);
		input_sync(mk_dev);
	}

	pre_val = buf[0];

	/* 重新提交URB */
	usb_submit_urb(mk_urb, GFP_KERNEL);
}

/*interface是指接口,一个usb设备可能有多个逻辑接口,这个逻辑接口就是用下面的usb_interface来表示的*/
static int usb_mk_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;
	static int first = 1;

	if (!first)
		return -EIO;
	first = 0;

	/* 每一个设备都有端点0
	 * interface->endpoint[]数组里放"除了端点0外的其他端点"
	 * interface->endpoint[0]表示"除端点0外的第1个端点"
	 * interface->endpoint[1]表示"除端点0外的第2个端点"
	 */
	interface = intf->cur_altsetting;
	endpoint = &interface->endpoint[0].desc;

	/* 1. 分配inputd_dev */
	mk_dev = input_allocate_device();
	
	/* 2. 设置 */
	/* 2.1 能产生哪类事件 */
	set_bit(EV_KEY, mk_dev->evbit);  //按键类事件
	set_bit(EV_REP, mk_dev->evbit);  //重复类事件,例如一直按着L,则会显示LLLLLLL。。。
	
	/* 2.2 能产生这类事件里的哪些事件 */
	set_bit(KEY_L, mk_dev->keybit);
	set_bit(KEY_S, mk_dev->keybit);
	set_bit(KEY_ENTER, mk_dev->keybit);
	
	/* 3. 注册 */
	input_register_device(mk_dev);

	/* 4. 硬件相关的操作: 
	 *    对于GPIO按键, 是request_irq, 在中断处理函数里上报按键
	 *    对于USB设备, 是使用"USB主机驱动程序提供的函数"发起USB传输获得数据
	 */
	/* 数据传输3要素: 源, 目的, 长度 */
	
	/* A. 源: USB设备的某个端点 */
	/* ((PIPE_INTERRUPT << 30) | (dev->devnum << 8) | (endpoint << 15) | USB_DIR_IN) */
	pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);//pipe是源,源是一个整数,这个整数里有端点的类型,端点的方向
	
	/* C. 长度: 这个端点描述符的wMaxPacketSize */
	len = endpoint->wMaxPacketSize;

	/* B. 目的: 分配buffer */
	buf = usb_alloc_coherent(dev, len, GFP_ATOMIC, &buf_phys);//从usb_buffer_alloc换成usb_alloc_coherent

	/* D. 怎么使用这3要素 ? */
	/* 分配URB: USB Reqeust Block ,usb请求块*/
	mk_urb = usb_alloc_urb(0, GFP_KERNEL);
	/* 用3要素填充URB 
	*实际上usb设备没有中断cpu的能力,但是电脑的usb主机有中断cpu的能力,所以usb主机不断查询,有信号便中断cpu。
	*/
	usb_fill_int_urb(mk_urb, dev, pipe, buf,
			 len,
			 uk_callback, NULL, endpoint->bInterval);//bInterval:查询频率,uk_callback是一个中断函数;
	mk_urb->transfer_dma = buf_phys; //得到数据后要往某个内存里写,它没那么聪明,需要一个物理地址
	mk_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;//还有设置某些标记,这标记什么意思伟哥也不懂
	
	/* 使用URB ,看看别人怎么做*/
	usb_submit_urb(mk_urb, GFP_KERNEL);//提交urb

	return 0;
}

static void usb_mk_disconnect(struct usb_interface *intf)
{
	struct usb_device *dev = interface_to_usbdev(intf);
	
	printk("disconnect usb mouse!!!!!\n");
	usb_kill_urb(mk_urb);
	usb_free_urb(mk_urb);
	usb_free_coherent(dev,len, buf, buf_phys);//应该是usb_free_coherent
	
	input_unregister_device(mk_dev);
	input_free_device(mk_dev);
}

/* 1. 分配usb_driver */
/* 2. 设置 */
static struct usb_driver usb_mk_driver = {
	.name		= "usbmk",
	.probe		= usb_mk_probe,
	.disconnect	= usb_mk_disconnect,
	.id_table	= usb_mk_id_table,
};

static int usb_mk_init(void)
{
	/* 3. 注册 */
	usb_register(&usb_mk_driver);
	return 0;
}

static void usb_mk_exit(void)
{
	usb_deregister(&usb_mk_driver);
}

module_init(usb_mk_init);
module_exit(usb_mk_exit);

MODULE_LICENSE("GPL");


 

posted @ 2012-09-26 09:43  star特530  阅读(560)  评论(0编辑  收藏  举报