linux驱动 ---- USB驱动框架


前言

本文基于S3C2440开发板。

一、usb驱动框架分析

在这里插入图片描述

在这里插入图片描述

USB 总线驱动程序,在接入 USB 设备时,会帮我们构造一个新的 usb_dev 注册到“usb_bus_type”里去。这部分是内核做好的。我们要做的是,构造一个 usb_driver
构体,注册到“usb_bus_type”中去。在“usb_driver”结构体中有“id_table”表示他能支持哪些设备,当 USB 设备能匹配 id_table 中某一个设备时,就会调用“usb_driver”结构体中的“.probe”(自已确定在 probe 中做的事情)等函数,如当 拔掉USB 设备时,就会调用其中的“.disconnect”函数。 “usb_bus_type”USB 总线驱动设备模型只是提供了这一种框架而已。在".probe"函数里面,注册“字符设备”也好,注册一个“input_dev”结构体也好,再或注册一个块设备也好。再或只是加了句打印也好,都可以由自已确定。

二、源码实例分析(鼠标)


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

#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 *uk_dev;
static char *usb_buf;   //如果有数据产生,usb驱动总线会把数据放到这个字符指针里面,
static dma_addr_t usb_buf_phys;  //物理地址
static int len;
static struct urb *uk_urb;   //usb的三要素使用包结构体

/*  下面这个结构体主要是用来与usb总线驱动中的device进行比较,相同就调用.probe*/
static struct usb_device_id usbmouse_as_key_id_table [] = {
	{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
		USB_INTERFACE_PROTOCOL_MOUSE) },
	//{USB_DEVICE(0x1234,0x5678)},
	{ }	/* Terminating entry */
};       

static void usbmouse_as_key_irq(struct urb *urb)  //当usb设备有数据产生时,就会调用这个中断,在这里判断是什么事件并进行事件的上报
{
	static unsigned char pre_val;
#if 0	
	int i;
	static int cnt = 0;
	printk("data cnt %d: ", ++cnt);
	for (i = 0; i < len; i++)
	{
		printk("%02x ", usb_buf[i]);
	}
	printk("\n");
#endif
	/* USB鼠标数据含义
	 * data[0]: bit0-左键, 1-按下, 0-松开
	 *          bit1-右键, 1-按下, 0-松开
	 *          bit2-中键, 1-按下, 0-松开 
	 *
     */
	if ((pre_val & (1<<0)) != (usb_buf[1] & (1<<0)))
	{
		/* 左键发生了变化 */
		input_event(uk_dev, EV_KEY, KEY_L, (usb_buf[1] & (1<<0)) ? 1 : 0);
		input_sync(uk_dev);
	}

	if ((pre_val & (1<<1)) != (usb_buf[1] & (1<<1)))
	{
		/* 右键发生了变化 */
		input_event(uk_dev, EV_KEY, KEY_S, (usb_buf[1] & (1<<1)) ? 1 : 0);
		input_sync(uk_dev);
	}

	if ((pre_val & (1<<2)) != (usb_buf[1] & (1<<2)))
	{
		/* 中键发生了变化 */
		input_event(uk_dev, EV_KEY, KEY_ENTER, (usb_buf[1] & (1<<2)) ? 1 : 0);
		input_sync(uk_dev);
	}
	
	pre_val = usb_buf[1];

	/* 重新提交urb */
	usb_submit_urb(uk_urb, GFP_KERNEL);
}

static int usbmouse_as_key_probe(struct usb_interface *intf, const struct usb_device_id *id)  //如果device和driver匹配成功就调用这个函数
{
	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;

	/* a. 分配一个input_dev */
	uk_dev = input_allocate_device();
	
	/* b. 设置 */
	/* b.1 能产生哪类事件 */
	set_bit(EV_KEY, uk_dev->evbit);
	set_bit(EV_REP, uk_dev->evbit);
	
	/* b.2 能产生哪些事件 */
	set_bit(KEY_L, uk_dev->keybit);
	set_bit(KEY_S, uk_dev->keybit);
	set_bit(KEY_ENTER, uk_dev->keybit);
	
	/* c. 注册 */
	input_register_device(uk_dev);
	
	/* d. 硬件相关操作 */
	/* 数据传输3要素: 源,目的,长度 */
	/* 源: USB设备的某个端点 */
	pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);

	/* 长度: */
	len = endpoint->wMaxPacketSize;

	/* 目的: */
	usb_buf = usb_buffer_alloc(dev, len, GFP_ATOMIC, &usb_buf_phys);

	/* 使用"3要素" */
	/* 分配usb request block */
	uk_urb = usb_alloc_urb(0, GFP_KERNEL);
	/* 使用"3要素设置urb" */
	usb_fill_int_urb(uk_urb, dev, pipe, usb_buf, len, usbmouse_as_key_irq, NULL, endpoint->bInterval);
	uk_urb->transfer_dma = usb_buf_phys;
	uk_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;

	/* 使用URB */
	usb_submit_urb(uk_urb, GFP_KERNEL);
	
	return 0;
}

static void usbmouse_as_key_disconnect(struct usb_interface *intf) //拔出就调用这个函数
{
	struct usb_device *dev = interface_to_usbdev(intf);

	//printk("disconnect usbmouse!\n");
	usb_kill_urb(uk_urb);
	usb_free_urb(uk_urb);

	usb_buffer_free(dev, len, usb_buf, usb_buf_phys);
	input_unregister_device(uk_dev);
	input_free_device(uk_dev);
}

/* 1. 分配/设置usb_driver */
static struct usb_driver usbmouse_as_key_driver = {
	.name		= "usbmouse_as_key_",
	.probe		= usbmouse_as_key_probe,
	.disconnect	= usbmouse_as_key_disconnect,
	.id_table	= usbmouse_as_key_id_table,
};


static int usbmouse_as_key_init(void)
{
	/* 2. 注册 */
	usb_register(&usbmouse_as_key_driver);
	return 0;
}

static void usbmouse_as_key_exit(void)
{
	usb_deregister(&usbmouse_as_key_driver);	
}

module_init(usbmouse_as_key_init);
module_exit(usbmouse_as_key_exit);

MODULE_LICENSE("GPL");

根据“usb_bus_type”总线驱动设备驱动模型,里面有个“.match”函数,左边是由“USB 总线驱动程序”帮我们来发现这个新“USB 设备”,会注册"usb_new_device()“一个"USB 设备”,并且从右边“driver”链表里找了一个个 USB 驱动程序和左边注册进来的“USB 设备”比较.所谓的比较,是“usb_bus_type”中的“.match”函数,把左边“usb_interface”结构中的“接口”与右边“usb_driver”结构中的“id_table”比较。若能吻合,则调用“usb_driver”中的“.probe”函数。“usb_bus_type”提供了这套机制。 在“.probe”函数中,可以只是打印,也可以注册字符设备,或注册“input_dev”结构。完全由自已确定。以前的驱动程序,数据是从“中断”(按键中断,ADC 中断)里面读寄存器得到。现在“USB 设备驱动程序”中的数据从“USB 总线”来,是 USB 总线驱动程序提供的函数(读写等)发起 USB 传输,从 USB 传输里得到那些数据。(数据传输三要素:源,目的,长度。再构造一个“usb_urb = usb_alloc_urb(0,GFP_KERNEL)”后,接接着把“源,目的,长度”填充到“usb_urb”中,使用就是提交"usb_urb",提交 usb_sunmit_urb 函数是 USB 总线驱动程序提供的)。当“USB 主机控制器”接收到数据后,“usb_as_key_irq”函数(complete_fn 完成函数)被调用。在这个函数里根据"USB 设备"数据的含义去上报(input_event())。

三、实验结果

在这里插入图片描述

  • 按键按下松开会产生4组数据,分别是按下的数据和同步数据,松开和同步数据。
   / * 左键发生了变化 */
		input_event(uk_dev, EV_KEY, KEY_L, (usb_buf[1] & (1<<0)) ? 1 : 0);
		input_sync(uk_dev);    

0000000 0073 0000 ba91 000a 0001 0026 0001 0000 鼠标左键按下
0000010 0073 0000 ba9c 000a 0000 0000 0000 0000 上报同步数据
0000020 0073 0000 cdc9 000c 0001 0026 0000 0000 鼠标左键松开
0000030 0073 0000 cdd2 000c 0000 0000 0000 0000 上报同步数据

0000000 //序号
0073 0000 //时间
ba91 000a //时间
0001 //按键类型
0026 //按下的值为0x26,代表l
0001 0000 //1 代表按下,0 代表松开

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值