b2科目四模拟试题多少题驾考考爆了怎么补救
b2科目四模拟试题多少题 驾考考爆了怎么补救

USB鼠标驱动程序开发

电脑杂谈  发布时间:2020-08-14 18:20:09  来源:网络整理

ps2鼠标驱动源代码_鼠标ps2接口_雷蛇鼠标驱动未检测到鼠标

声明:本文章是看完韦东山老师的usb鼠标驱动视频所写的关于usb鼠标的驱动,因此如果有相关内容与其他网友相同,敬请原谅。同时我还是想说本文只是总结自己的学习所得,同时也将自己所学到的知识写下来,所以如果这篇文章对你有帮助,那是我的荣幸。

在介绍驱动程序前我想向大家介绍一下usb_bus_type(usb总线驱动类),内核中有不同的总线类型,不同的总线有不同的匹配方式,如我们前面所学的platform_bus_type是使用名字来匹配的,而这里要讲的usb_bus_type的匹配是通过id_table来匹配的,但是各种总线的匹配流程大致还是一样的。因为要想将设备和驱动通过总线连接起来就不可避免的用到了match函数。就像你要相亲你就要将你的要求都写出来,而女方也要将自己对另一半的要求写出来,然后你们双方都把各自的请求交给婚介所,而婚介所所做的事就是将你的要求与每一个女士的请求进行比较,注意这里就用到了比较(match),当他们发现有一个女士满足你的要求,而你也正好满足这个女士的要求时,他就会对你说“给你匹配到了合适的女士”,而同时他也会对那位女士说“给你匹配到了合适的男士”,然后就安排你们相亲了。而我们的总线——设备——驱动模型就类似这个相亲模型。其中你是设备,那些女士是驱动,而婚介所就是总线了。

有了上面的例子,我们结合这个例子分析一下这个匹配流程:

上面这幅图就将,usb_bus_type的框架大致描绘出来了,下面我们详细的说一下。如上图所示,总线模型的最主要部分就是位于上层的总线,总线中有一个match函数,他会将通过usb_new_device向上注册的usb_interface和通过usb_register向上注册的usb_driver中的id_table一一比较(这个过程就类似于你和那些女士分别向婚介所投递个人信息),当发现设备和驱动匹配时,他就会调用的driver中的probe函数(这就相当于当发现你与其中一位女士匹配时就会通知你们相亲)。很多朋友可能会问“两个人相亲他们匹配的可能是性格,三观,收入等等,而设备和驱动他们匹配的是什么那?”,我们说了,不同的总线类型匹配的标准不一样,但总要有个可以匹配的吧。是的,在usb总线类型中,我们匹配的是id_table,可能很会问可以讲的细点吗?就像你说的相亲的时候匹配三观,可三观太大了,可以细分一下吗?,这个是可以的我们打开id_table的代码就会发现有:

 

static struct usb_device_id usb_mouse_id_table [] = {
	{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
		USB_INTERFACE_PROTOCOL_MOUSE) },
	{ }	/* Terminating entry */
};

#define USB_INTERFACE_INFO(cl,sc,pr) \
	.match_flags = USB_DEVICE_ID_MATCH_INT_INFO, .bInterfaceClass = (cl), \
	.bInterfaceSubClass = (sc), .bInterfaceProtocol = (pr)

以上是详细的比较类型. 它们由三部分组成: 接口类,接口子类和接口协议. 但是您还可以添加其他条件,例如:

{USB_DEVICE(设备ID,产品ID)},

通过这种方式,可以在原始基础上缩小范围.

具体代码描述为:

在hub.c的hub_port_connect_change函数中,有udev = usb_alloc_dev(hdev,hdev-> bus,port1);而usb_alloc_dev将分配设备结构,该设备分配了什么?我们可以看一看,我们可以在usb_alloc_dev函数中看到以下代码:

dev->dev.bus = &usb_bus_type;

这行代码定义了usb_bus_type,所以让我们来看一下该总线中定义的内容:

struct bus_type usb_bus_type = {
	.name =		"usb",
	.match =	usb_device_match,     //非常重要的match函数
	.uevent =	usb_uevent,
	.suspend =	usb_suspend,
	.resume =	usb_resume,
};

鼠标ps2接口_ps2鼠标驱动源代码_雷蛇鼠标驱动未检测到鼠标

我们可以从中看到一个匹配功能. 如前所述,尽管不同的总线可能具有不同的匹配数据,但是它们的一般过程是相同的. 接下来,我们输入usb_device_match函数:

static int usb_device_match(struct device *dev, struct device_driver *drv)
		intf = to_usb_interface(dev);
		usb_drv = to_usb_driver(drv);
		id = usb_match_id(intf, usb_drv->id_table);

通过上面的代码,我们可以知道它们与dev信息和id_table信息匹配.

基于以上理解,我们现在将编写一个简单的USB鼠标驱动程序. 我们的目标是在按下左键时报告按钮L,在按下右键时报告S,并在中间键ENTRR.

从以上目标中,我们可以看到我们需要使用输入子系统来报告击键. 从usb_bus_type中,我们知道usb的总线和设备部分已被写入,我们所能做的就是编写驱动程序. 接下来,我们开始编写驱动程序. 与其他驱动程序一样,我们仍然首先构建该驱动程序的框架,然后填写我们要执行的代码,而USB驱动的框架为:

1. 分配/设置usb_driver结构

2. 在入口函数中注册结构,然后在出口函数中取消结构

详细的代码是:

static struct usb_driver usb_mouse_drv = {        /* 分配设置usb_driver结构体 */
	.name		= "usbmouse",
	.probe		= usb_mouse_probe,
	.disconnect	= usb_mouse_disconnect,
	.id_table	= usb_mouse_id_table,       
};
int usb_drv_init(void)                            
{
	usb_register(&usb_mouse_drv);             /* 注册usb_driver结构体 */
	return 0;
}
void usb_drv_exit(void)                           
{
	usb_deregister(&usb_mouse_drv);/* 注销usb_driver结构体 */
}

从上面的代码中,我们可以看到在usb_driver结构中有id_table(用于匹配设备),探测功能(匹配成功时调用)和断开连接功能(匹配的设备离开时调用). 注意: 这三个是必不可少的. 其他选项是可选的. 我已经在上面提到了id_table. 因此,在这里我不再赘述,只介绍他的内容,代码如下:

ps2鼠标驱动源代码_雷蛇鼠标驱动未检测到鼠标_鼠标ps2接口

static struct usb_device_id usb_mouse_id_table [] = {
  { USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
	  USB_INTERFACE_PROTOCOL_MOUSE) },
  { } /* Terminating entry */
};

USB_INTERFACE_INFO是一个宏:

/**
 * USB_INTERFACE_INFO - macro used to describe a class of usb interfaces 
 * @cl: bInterfaceClass value
 * @sc: bInterfaceSubClass value
 * @pr: bInterfaceProtocol value
 *
 * This macro is used to create a struct usb_device_id that matches a
 * specific class of interfaces.
 */
#define USB_INTERFACE_INFO(cl,sc,pr) \
	.match_flags = USB_DEVICE_ID_MATCH_INT_INFO, .bInterfaceClass = (cl), \
	.bInterfaceSubClass = (sc), .bInterfaceProtocol = (pr)

通过引入以上注释,可以看出他提供了接口类,接口子类和接口协议. 因此,您可以提供相应的三个项目进行匹配. 同时,如果您对制造商ID和设备ID有其他要求,也可以通过id_table中的USB_DEVICE选项进行设置.

在写入id_table之后,可以将设备与驱动程序进行匹配,然后我们应该编写probe函数以在匹配成功后进入. 那么进入探针后我们该怎么办?我们必须考虑我们的目的是通过按下鼠标来实现按钮功能. 由于它是按钮功能,因此使用输入子系统,那么输入子系统的框架是什么?

1. 分配input_dev结构

2. 设置input_dev结构

3. 注册input_dev结构

4. 硬件相关操作

此处与硬件相关的操作与过去有所不同. 先前的按钮和触摸屏读取寄存器或ADC值,而当前与硬件相关的操作是usb驱动程序框架中的操作,因此应由usb总线驱动程序提供usb读写功能以进行数据传输.

ps2鼠标驱动源代码_鼠标ps2接口_雷蛇鼠标驱动未检测到鼠标

以下是探测功能的代码:

int 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;
	
	interface = intf->cur_altsetting;
	endpoint = &interface->endpoint[0].desc;
	
	/*1   分配一个input_dev结构体 */
	uk_dev = input_allocate_device();
	
	/*2   设置 */
	/*2.1 产生哪类事件 */
	set_bit(EV_KEY,uk_dev->evbit);    //产生按键类事件
	set_bit(EV_REP,uk_dev->evbit);    //产生重复类事件
	
	/*2.2 产生这类事件中的那个 */
	set_bit(KEY_L,uk_dev->keybit);   //按键类中的按键L
	set_bit(KEY_S,uk_dev->keybit);   //按键类中的按键S
	set_bit(KEY_ENTER,uk_dev->keybit); //按键类中的按键ENTER
	
	/*3  注册 */
	input_register_device(uk_dev);
	/*4 硬件相关设置:通过使用USB设备总线获取读写函数 */
	/* 数据传输三要素:源,目的,长度 */
	/* 源:USB设备的某个端点 */
	pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
	/* 长度 */
	len = endpoint->wMaxPacketSize;
	/* 目的:  */
	usb_buf = usb_buffer_alloc(dev,len,GFP_KERNEL,&usb_buf_phys);
	/* 使用三要素 */
	/* 分配一个urb(USB request block) */
	uk_urb = usb_alloc_urb(0,GFP_KERNEL);
	/* 设置使用urb */
	usb_fill_int_urb(uk_urb, dev, pipe, usb_buf,
			 len,
			 usb_mouse_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;
}

以上程序已经解释了input_dev的框架,但是有些学生可能会问代码第四部分发生了什么?

这是介绍另一个非常有用的结构USB请求块(USB request block,URB),URB用于USB设备驱动程序中,用于描述用于与USB设备和网络设备进行通信的基本载体和核心数据结构. 驱动程序中的sk_buff结构类似,是USB主机与设备之间数据传输的封装.

urb包含执行USB传输所需的所有信息. 在执行数据传输时,需要分配,初始化urb结构,然后将其提交给usb核心. USB内核解析urb并将控制信息提交给主机控制器,该主机控制器负责将数据传输到设备. 此时,驾驶员只需要等待即可. 当数据传输回主机控制器时,它将被转发到USB内核以唤醒等待的驱动程序,驱动程序将完成剩余的工作.

更具体地说,Linux中的设备驱动程序仅需要为每个请求准备一个urb结构,然后填写它,然后可以调用函数usb_submit_urb()并将其提交给USB内核. 然后,USB内核将urb传递给USB主机控制器,最后传递给USB设备. USB设备获得urb结构后,它将解析该结构并将数据以相反的方式返回给Linux内核.

注意: 以上描述摘自百度百科. 我认为它相对简单,因此我复制并与您分享.

在编写完这些之后,我们应该在usb_fill_int_urb函数中完成usb_mouse_irq函数. 也许有些朋友会问这是否是一个中断功能?我的回答是这是一个中断功能,但这是由主机控制器而不是从设备生成的中断:

雷蛇鼠标驱动未检测到鼠标_ps2鼠标驱动源代码_鼠标ps2接口

与上图类似,主机控制器不断查询usb设备以获取数据并将数据放入缓冲区,然后主机控制器生成中断,以调用上述usb_mouse_irq函数.

在usb_mouse_irq函数中要做的是报告获得的键值:

static void usb_mouse_irq(struct urb *urb)
{
	static unsigned char pre_val;
	if((pre_val & (1<<0)) != (usb_buf[0] & (1<<0))){
		
		/* 左键发生了变化 */
		input_event(uk_dev,EV_KEY,KEY_L,usb_buf[0] & (1<<0) ? 1 : 0);
		input_sync(uk_dev);
	}
	if((pre_val & (1<<1)) != (usb_buf[0] & (1<<1))){
		/* 右键发生了变化 */
		input_event(uk_dev,EV_KEY,KEY_S,usb_buf[0] & (1<<1) ? 1 : 0);
		input_sync(uk_dev);
	}
	if((pre_val & (1<<2)) != (usb_buf[0] & (1<<2))){
		/* 中键发生了变化 */
		input_event(uk_dev,EV_KEY,KEY_ENTER,usb_buf[0] & (1<<2) ? 1 : 0);
		input_sync(uk_dev);
	}
	pre_val = usb_buf[0];
	/* 从新提交urb */
	usb_submit_urb(uk_urb,GFP_KERNEL);
}

上面是驱动程序的主要部分,但是当设备离开时,将调用相应的断开连接功能. 让我们在下面编写disconcert函数:

void usb_mouse_disconnect(struct usb_interface *intf)
{
	struct usb_device *dev = interface_to_usbdev(intf);
	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);
	
}

完成断开功能,驱动程序完成.

还有几篇文章供我参考:

ARM-Linux开发的USB驱动程序鼠标控制: 本文也是由魏东山老师的方法驱动的

嵌入式Linux USB驱动程序开发教您逐步编写USB驱动程序

阅读完嵌入式Linux少走弯路后的USB驱动程序开发方法: 尽管本文内容不多,但对学习USB驱动程序非常有帮助

嵌入式Linux驱动程序学习之路(20)USB设备驱动程序: 本文是对老师课堂内容的总结,并且有完整的代码

Linux USB驱动程序开发(5)-USB驱动程序开发过程的简要概述: 尽管本文不是关于usb鼠标的,但它介绍了usb驱动程序的许多原理

如何在嵌入式Linux下使用USB键盘和鼠标

: 本文是有关USB鼠标的非常好的文章.


本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/bofangqi/article-298871-1.html

    相关阅读
      发表评论  请自觉遵守互联网相关的政策法规,严禁发布、暴力、反动的言论

      • 张小改
        张小改

        请问

      • 戴煜之
        戴煜之

        所以麻麻一把年纪还纯真的喜欢你@TFBOYS-易烊千玺呀

      热点图片
      拼命载入中...