驱动程序之_1_字符设备_13_USB设备_3_鼠标驱动

2023-05-16

驱动程序之_1_字符设备_13_USB设备_3_鼠标驱动

从上一篇文章知道,当我们接入一个USB设备,USB总线驱动会为我们构建一个device并注册,编写驱动程序时只需要构造driver并注册到总线即可,其中driver中有一个id_table[],用来示意所支持的设备;probe函数用于,完成初始化,(probe函数在总线驱动匹配设备);disconnect函数,切断联系;等等

编写一个简单的USB驱动程序:将鼠标用作键盘,按下左键相当于按下’l‘,按下右键相当于按下’s‘,按下滚轮相当于按下"enter"

以前写键盘类驱动程序(使用input子系统)流程:
一、入口函数
1、分配input_dev
2、设置input_dev
3、注册input_dev
4、硬件初始化(中断等)
二、出口函数,完成与入口函数相反的工作
三、使用input子系统提供的事件函数,完善功能函数

写USB驱动程序的流程:
一、入口函数
1、分配usb_driver
2、设置usb_driver
3、注册usb_driver,(会调用到probe函数,在probe中同样要设置一个input_dev)
4、硬件初始化(中断等)
二、出口函数,完成与入口函数相反的工作
三、使用usb总线驱动提供的读写函数,完善功能函数

分析:
定义、设置usb_driver,在入口函数中注册

static struct usb_driver mouse = {
	.name 		= "mouse",
	.probe 		= mouse_probe,
	.disconnect = mouse_disconnect,
	.id_table   = mouse_id_table,
};
static int mouse_init(void)
{
	int error;
	
	error = usb_register(&mouse);

	return 0;
}

在probe函数中
1、获取描述符
2、分配、设置、注册input_dev
3、获得数据源、数据包大小、分配数据缓存
4、分配、填充urb(usb请求块)
5、提交urb

static int mouse_probe(struct usb_interface *intf, const struct usb_device_id *id)
{	
	endpoint = &interface->endpoint[0].desc;
	/**********************************************/
	input_mouse = input_allocate_device();
	set_bit(EV_KEY,input_mouse->evbit);
	set_bit(EV_REP,input_mouse->evbit);	
	set_bit(KEY_L,input_mouse->keybit);		
	set_bit(KEY_S,input_mouse->keybit);		
	set_bit(KEY_ENTER,input_mouse->keybit);			
	input_register_device(input_mouse);	
	/**********************************************/
	pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);	
	maxp = endpoint->wMaxPacketSize;
	mouse_buf = usb_buffer_alloc(dev, 8, GFP_ATOMIC, &mouse_buf_phys);
	/**********************************************/	
	mouse_urb = usb_alloc_urb(0, GFP_KERNEL);
	usb_fill_int_urb(mouse_urb, dev, pipe, mouse_buf, maxp, mouse_irq, 0, endpoint->bInterval);
	mouse_urb->transfer_dma = mouse_buf_phys;
	mouse_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;	
	/**********************************************/
	usb_submit_urb(mouse_urb, GFP_KERNEL);	
	/**********************************************/
}

在disconnect函数中
1、注销、释放urb
2、释放数据缓存
3、注销释放input_dev

static void mouse_disconnect(struct usb_interface *intf)
{
	/**********************************************/
	usb_kill_urb(mouse_urb);
	usb_free_urb(mouse_urb);
	/**********************************************/
	usb_buffer_free(interface_to_usbdev(intf),maxp, mouse_buf, mouse_buf_phys);	
	/**********************************************/
	input_unregister_device(input_mouse);	
	input_free_device(input_mouse);	
	/**********************************************/
}

在irq中断函数中
1、上报数据
2、提交urb

static void mouse_irq(struct urb *urb)
{
	int error;

#ifdef DEBUG
	int i;

	printk("usb_buf : ");
	for(i = 0;i < maxp;i++)
		printk("%02x ",mouse_buf[i]);
	printk("\r\n");
#else
	input_report_key(input_mouse, KEY_L,  mouse_buf[0] & 0x01);
	input_report_key(input_mouse, KEY_S,  mouse_buf[0] & 0x02);
	input_report_key(input_mouse, KEY_ENTER,  mouse_buf[0] & 0x04);	
	input_sync(input_mouse);
#endif
	error = usb_submit_urb(urb, GFP_ATOMIC);	
}

测试方法:
重新编译内核(反选USB驱动),用新内核启动
一、定义宏DEBUG,加载驱动,插上鼠标,按下按键后串口即有输出
二、注释宏DEBUG,测试方法与input子系统相同

附上完整代码

#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>

//#define DEBUG


static struct input_dev *input_mouse;
static dma_addr_t mouse_buf_phys;
static char *mouse_buf;
static struct urb *mouse_urb;
static int maxp;

static void mouse_irq(struct urb *urb)
{
	int error;

#ifdef DEBUG
	int i;

	printk("usb_buf : ");
	for(i = 0;i < maxp;i++)
		printk("%02x ",mouse_buf[i]);
	printk("\r\n");
#else
	input_report_key(input_mouse, KEY_L,  mouse_buf[0] & 0x01);
	input_report_key(input_mouse, KEY_S,  mouse_buf[0] & 0x02);
	input_report_key(input_mouse, KEY_ENTER,  mouse_buf[0] & 0x04);	
	input_sync(input_mouse);
#endif
	error = usb_submit_urb(urb, GFP_ATOMIC);	
}

static int 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;
	if (interface->desc.bNumEndpoints != 1)
		return -ENODEV;

	endpoint = &interface->endpoint[0].desc;
	if (!usb_endpoint_is_int_in(endpoint))
		return -ENODEV;


	printk("found mouse\r\n");
	printk("bcdUSB %x\r\n",dev->descriptor.bcdUSB);
	printk("Vid    %x\r\n",dev->descriptor.idVendor);	
	printk("Pid    %x\r\n",dev->descriptor.idProduct);	


	input_mouse = input_allocate_device();

	set_bit(EV_KEY,input_mouse->evbit);
	set_bit(EV_REP,input_mouse->evbit);	

	set_bit(KEY_L,input_mouse->keybit);		
	set_bit(KEY_S,input_mouse->keybit);		
	set_bit(KEY_ENTER,input_mouse->keybit);			

	input_register_device(input_mouse);


	pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
	maxp = endpoint->wMaxPacketSize;
	mouse_buf = usb_buffer_alloc(dev, 8, GFP_ATOMIC, &mouse_buf_phys);
	
	mouse_urb = usb_alloc_urb(0, GFP_KERNEL);
	usb_fill_int_urb(mouse_urb, dev, pipe, mouse_buf, maxp, mouse_irq, 0, endpoint->bInterval);
	mouse_urb->transfer_dma = mouse_buf_phys;
	mouse_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;

	usb_submit_urb(mouse_urb, GFP_KERNEL);

	return 0;
}

static void mouse_disconnect(struct usb_interface *intf)
{
	printk("disconnect mouse\r\n");

	usb_kill_urb(mouse_urb);
	usb_free_urb(mouse_urb);
	usb_buffer_free(interface_to_usbdev(intf),maxp, mouse_buf, mouse_buf_phys);
	
	input_unregister_device(input_mouse);	
	input_free_device(input_mouse);
}

static struct usb_device_id mouse_id_table[] = {
	{	
		USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, 
						   USB_INTERFACE_SUBCLASS_BOOT,
						   USB_INTERFACE_PROTOCOL_MOUSE) 	
	}
};
	
static struct usb_driver mouse = {
	.name 		= "mouse",
	.probe 		= mouse_probe,
	.disconnect = mouse_disconnect,
	.id_table   = mouse_id_table,
};

static int mouse_init(void)
{
	int error;
	
	error = usb_register(&mouse);

	return 0;
}

static void mouse_exit(void)
{
	usb_deregister(&mouse);
}

module_init(mouse_init);
module_exit(mouse_exit);
MODULE_LICENSE("GPL");
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

驱动程序之_1_字符设备_13_USB设备_3_鼠标驱动 的相关文章

  • STAR法则的简历应用

    STAR法则 即为Situation Task Action Result的缩写 xff0c 具体含义是 Situation 事情是在什么情况下发生 Task 你是如何明确你的任务的 Action 针对这样的情况分析 xff0c 你采用了什
  • BI商业智能

    关键字 xff1a 商务智能 xff0c 数据仓库 xff0c ETL BI xff08 Business Intelligence即商务智能 xff09 xff0c 百度百科用的解释是 xff0c 它是一套完整的解决方案 xff0c 用来
  • 遗传算法 一个模拟自然进化过程的启发式搜索算法

    关键字 xff1a 遗传算法 遗传算法 xff08 Genetic Algorithm xff09 是一种模拟自然界 自然选择 和 自然遗传 的启发式搜索算法 xff0c 通过模拟自然进化过程搜索最优解的方法 直到1989年 xff0c 实
  • PHP四大基本排序算法

    冒泡排序 思路分析 xff1a 在要排序的一组数中 xff0c 对当前还未排好的序列 xff0c 从前往后对相邻的两个数依次进行比较和调整 xff0c 让较大的数往下沉 xff0c 较小的往上冒 即 xff0c 每当两相邻的数比较后发现它们
  • 设置centos的YUM源为国内阿里云源

    阿里云Linux安装镜像源地址 xff1a http mirrors aliyun com http mirrors aliyun com repo CentOS系统更换软件安装源 第一步 xff1a 备份你的原镜像文件 xff0c 以免出
  • PHP八大设计模式

    PHP命名空间 可以更好地组织代码 xff0c 与Java中的包类似 Test1 php span class php span class hljs preprocessor lt php span span class hljs key
  • GitLab使用手册

    安装Git 安装环境 xff1a windows下载地址 xff1a git官方网站安装包 xff1a 64位安装过程 xff1a 傻瓜式安装至此安装完毕 生成私钥和公钥 ssh span class hljs attribute keyg
  • echarts使用心得

    前言 第一次参加工作 xff0c 公司使用图表很频繁 xff0c 我之前会highcharts xff0c 但是公司基本上都是使用的echarts xff0c 于是自己开始琢磨echarts xff0c 使用起来却颇费了一番工夫 所以就把使
  • JQuery插件之Masked Input

    Masked Input是一个字符输入格式化的jQuery插件 它可让你轻松的实现对各种数据的输入进行格式限制 xff0c 如日期 电话等 Masked Input在IE Firefox Safari和Chrome通过测试 Mask会自动为
  • 关于jQuery的九大使用误区

    jQuery是如此容易使用 xff0c 以至于我们有时候忘记了CSS的存在 我们在使用CSS时 xff0c 几乎不去考虑性能 xff0c 因为它已经是快得不值得再去做什么优化上 的努力 但现实世界中 xff0c JQuery会导致令开发人员
  • echarts主题属性设置

    theme 61 span class hljs comment 全图默认背景 span backgroundColor span class hljs string 39 rgba 0 0 0 0 39 span span class h
  • java keytool证书工具使用小结

    Keytool 是一个Java数据证书的管理工具 Keytool将密钥 xff08 key xff09 和证书 xff08 certificates xff09 存在一个称为keystore的文件中在keystore里 xff0c 包含两种
  • Another app is currently holding the yum lock; waiting for it to exit...

    Another app span class hljs keyword is span currently holding span class hljs keyword the span yum lock waiting span cla
  • Javascript网页打印大全

    目录 普通打印 xff08 整页打 xff09 打印去掉 添加页眉页脚 使用外部控件 方法实现多功能打印 打印背景 普通打印 xff08 整页打 xff09 span class hljs built in window span span
  • centos6.5启动nginx报错

    问题 xff1a nginx emerg socket 80 failed 97 Address family not supported by protocol 解决方案 vim etc nginx conf d default conf
  • echarts和highchart的区别

    echarts 先大体了解一下echarts的历史 xff1a echarts是百度公司前端开发的一个图表库 支持柱状图 饼状图 k线图 map图 热导向图 折线图 主要采用canvas画图 highchart highcharts是国外的
  • vi/vim常用配置和快捷键

    快捷键 命令名功能描述gg将光标移动到文档头部G将光标移动到文档尾部nyy全选文本 xff08 没有全选命令的 xff0c 我的做法就是复制n行 xff0c 只要n大于文本的行数就行 xff09 如何复制文本内容到另一个文件中 使用vim进
  • Cannot resolve module 'child_process'

    ERROR span class hljs keyword in span span class hljs regexp xmlhttprequest span lib XMLHttpRequest js Module not found
  • Cannot resolve module 'fs'

    可能很多人都会遇到这个问题 xff0c 反正我的话已经遇到两次了 xff0c 上一次解决的时候没有记录解决办法 xff0c 这次又遇到了 xff0c 而且国内搜索引擎是搜不到这个问题的解决办法的 xff0c 所以写个博客记录一下吧 xff0
  • 面试题13:机器人的运动范围

    题目描述 地上有一个m行和n列的方格 一个机器人从坐标0 0的格子开始移动 xff0c 每一次只能向左 xff0c 右 xff0c 上 xff0c 下四个方向移动一格 xff0c 但是不能进入行坐标和列坐标的数位之和大于k的格子 例如 xf

随机推荐

  • flex布局知识点Beta

    先看图 xff0c 实现下方列表中的效果 xff0c 名字在左 xff0c 数量在右 xff0c 相对条目居中 html代码 span class hljs tag lt span class hljs title view span sp
  • js如何一次循环删除数组中的多个元素

    思路 xff1a 数组遍历删除一个元素很容易 xff0c 通过splice方法删除对应索引的元素即可 xff0c 但是遍历删除多个元素就复杂了很多 xff0c 首先不能按索引从小到大的顺序删除 xff0c 这样可能会导致索引对应的元素发生变
  • git删除远程分支

    两步删除远程仓库 xff1a git branch r d origin dev 删除远程分支 git push origin dev然后提交到远程 注意 xff1a dev分支前的冒号 不能少
  • nodejs基础篇(一)

    我从2017年开始接触nodejs xff0c 到现在用了一年多了 xff0c 觉得我有必要写点东西出来 xff0c 记录自己的积累的知识体系 xff0c 以便更好的掌握nodejs nodejs专题不是按照严格的学习历程书写 xff0c
  • 滚动到顶部的实现方法

    span class token comment scrollTop animation span span class token keyword export span span class token keyword function
  • 浅谈ES6的Promise对象

    相信凡是写过javascript的童鞋也一定都写过回调方法 xff08 callback xff09 xff0c 简单说回调方法就是将一个方法func2作为参数传入另一个方法func1中 xff0c 当func1执行到某一步或者满足某种条件
  • node版本管理器——nvm

    nvm是管理node版本的一个工具 xff0c 具体介绍不再赘述 xff0c 请到GitHub xff08 https github com creationix nvm xff09 查看 安装 Linux Mac curl o https
  • centos下修改mysql默认端口

    mysql5 6安装 xff1a br wget http repo mysql com mysql community release el7 5 noarch rpm br rpm ivh mysql community release
  • nginx开启gzip压缩

    nginx安装 xff1a yum install y nginx 配置文件默认在 etc nginx nginx conf 打开nginx conf添加 gzip span class hljs function start span c
  • wordpress安装后问题汇总

    问题一 xff1a wordpress写文章界面点击 添加媒体 和 可视化 文本 无反应 解决方法 xff1a 在wp config php中追加 define 34 CONCATENATE SCRIPTS 34 false 问题二 xff
  • ucos ii是怎么实现多任务运行的?很通俗易懂的描述

    问题 xff1a ucos上建立一个任务 xff0c 格式如上图 xff0c 它是一个死循环 xff0c 但如果我建立了五个任务 xff0c 并且五个任务里面没有延时 xff0c 就只是像无操作系统那样写法 xff0c 用死循环让它们一直跑
  • 解决vim中文乱码

    执行 xff1a cd xff5e vim vimrc 将如下文本复制保存退出即可 set fileencodings 61 utf 8 ucs bom gb18030 gbk gb2312 cp936 set termencoding 6
  • nginx开启ssl证书

    修改listen为443 如果你又开启防火墙 xff0c 还需将443端口放开 在server中添加 ssl on ssl session timeout 5m ssl certificate cert 214050429370638 pe
  • 一键安装全局npm模块

    GitHub xff1a https github com flitrue npm install global 简介 当我们使用nvm管理node xff0c 切换node版本时 xff0c 安装在全局的npm包无法共用之前node版本中
  • vscode设置命令行启动

    方法一 xff1a 配置 启动 VS Code打开命令面板 shift 43 ctrl 43 P mac shift 43 cmmand 43 P xff0c 输入 shell command xff0c 找到 Install code c
  • Vue和React的生命周期

    最新的Vue和React生命周期 Vue 生命周期 vue生命周期主要有8个 beforeCreatecreatedbeforeMountmountedbeforeUpdateupdatedbeforeDestroydestroyed Re
  • js实现千位分隔符运算

    方法一 xff1a span class token keyword function span span class token function format span span class token punctuation span
  • 调整浏览器滚动条样式

    我们知道浏览器自带滚动条很丑 xff0c 有时影响整个页面到美观 xff0c 尤其在页面内嵌一个滚动列表 xff0c 显得奇丑无比 xff0c 下面我们根据如下代码调节滚动条样式 span class token punctuation s
  • 视觉SLAM理论入门——(7)视觉里程计之特征点法—对极几何

    提取ORB特征后 xff0c 需要根据点估计相机的运动 根据相机的原理 xff0c 可以分为下面几种情况 xff1a 1 当采用单目相机 xff0c 只知道2D像素坐标 xff0c 需要根据两组2D点估计运动 xff0c 这时用对极几何求解
  • 驱动程序之_1_字符设备_13_USB设备_3_鼠标驱动

    驱动程序之 1 字符设备 13 USB设备 3 鼠标驱动 从上一篇文章知道 xff0c 当我们接入一个USB设备 xff0c USB总线驱动会为我们构建一个device并注册 xff0c 编写驱动程序时只需要构造driver并注册到总线即可