Linux驱动开发(十四)---USB驱动开发学习(键盘+鼠标)

这篇具有很好参考价值的文章主要介绍了Linux驱动开发(十四)---USB驱动开发学习(键盘+鼠标)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前文回顾

《Linux驱动开发(一)—环境搭建与hello world》
《Linux驱动开发(二)—驱动与设备的分离设计》
《Linux驱动开发(三)—设备树》
《Linux驱动开发(四)—树莓派内核编译》
《Linux驱动开发(五)—树莓派设备树配合驱动开发》
《Linux驱动开发(六)—树莓派配合硬件进行字符驱动开发》
《Linux驱动开发(七)—树莓派按键驱动开发》
《Linux驱动开发(八)—树莓派SR04驱动开发》
《Linux驱动开发(九)—树莓派I2C设备驱动开发(BME280)》
《Linux驱动开发(十)—树莓派输入子系统学习(红外接收)》
《Linux驱动开发(十一)—树莓派SPI驱动学习(OLED)》
《Linux驱动开发(十二)—树莓派framebuffer学习(改造OLED)》
《Linux驱动开发(十三)—USB驱动HID开发学习(鼠标)》
继续宣传一下韦老师的视频

Linux驱动开发(十四)---USB驱动开发学习(键盘+鼠标)

70天30节Linux驱动开发快速入门系列课程【实战教学、技术讨论、直播答疑】

Linux驱动开发(十四)---USB驱动开发学习(键盘+鼠标)
昨天学习了一下usb鼠标的简单识别,今天来完整的写一套键盘和鼠标的驱动,起码能够支持树莓派使用的。
Linux驱动开发(十四)---USB驱动开发学习(键盘+鼠标)
先来写一下键盘的驱动。

键盘驱动框架

框架部分很简单,和昨天的鼠标基本一样,但是今天这里的table_id,要用一个通用定义,保证识别出所有的usb键盘。

#include <linux/init.h>
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/usb/input.h>
#include <linux/hid.h>

//通用ID
static const struct usb_device_id usb_kbd_id_table[] = {
	{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,USB_INTERFACE_PROTOCOL_KEYBOARD) },
	{ }	/* Terminating entry */
};

/*
MODULE_DEVICE_TABLE 有两个功能。
一是:将设备加入到外设队列中,
二是告诉程序阅读者该设备是热插拔设备或是说该设备支持热插拔功能。
该宏定义在<linux/module.h>下
这个宏有两个参数,第一个参数设备名,第二个参数该设备加入到模块中时对应产生的设备搜索符号,这个宏生成了一个名为__mod_pci_device_table
局部变量,这个变量指向第二个参数
*/
MODULE_DEVICE_TABLE (usb,usb_kbd_id_table);
 
//USB设备信息与驱动端匹配成功的时候调用。
static int myusbkbd_probe(struct usb_interface *intf,const struct usb_device_id *id)  //资源探索函数
{
	printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
	printk("USB驱动匹配成功! ID: 0x%X,0x%X\n",id->idVendor,id->idProduct);
	return 0;
}
 
//USB断开的时候调用
static void myusbkbd_disconnect(struct usb_interface *intf)
{
	printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
	printk("USB 设备释放成功!\n"); 
}
 
//定义USB驱动结构体 
static struct usb_driver myusbkbd_driver = 
{
    .name = "myusbkbd_drv",
    .probe = myusbkbd_probe,
    .disconnect = myusbkbd_disconnect,
    .id_table = usb_kbd_id_table,
};
 
static int __init myusbkbd_init(void)
{
    //注册USB设备驱动
	printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
    usb_register(&myusbkbd_driver);
    return 0;
}
 
static void __exit myusbkbd_exit(void)
{
     //注销USB设备驱动
	 printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
     usb_deregister(&myusbkbd_driver);
}
 
module_init(myusbkbd_init);
module_exit(myusbkbd_exit);
MODULE_AUTHOR("PGG");
MODULE_LICENSE("GPL");

还是来测试一下效果,能否顺利加载

[  194.723576] drivers/char/myusbkbd.c myusbkbd_init 54
[  194.723771] usbcore: registered new interface driver myusbkbd_drv
[  200.588535] usb 1-1.3: new low-speed USB device number 6 using dwc_otg
[  200.733864] usb 1-1.3: New USB device found, idVendor=0c45, idProduct=760b, bcdDevice= 1.05
[  200.733901] usb 1-1.3: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[  200.733921] usb 1-1.3: Product: USB Keyboard
[  200.733937] usb 1-1.3: Manufacturer: SONiX
[  200.735207] drivers/char/myusbkbd.c myusbkbd_probe 29
[  200.735228] USB驱动匹配成功! ID: 0x0,0x0

加载在成功!!不过不清楚为啥ID没有读出来。
Linux驱动开发(十四)---USB驱动开发学习(键盘+鼠标)

尝试一下,修改为按照ID匹配

static const struct usb_device_id my_usbkbd_id_table[] = 
{
    {USB_DEVICE(0x0c45,0x760b)},//键盘
    {}
};

结果又执行了两次probe函数

[  124.011262] drivers/char/myusbkbd.c myusbkbd_init 58
[  124.011457] usbcore: registered new interface driver myusbkbd_drv
[  130.851294] usb 1-1.3: new low-speed USB device number 5 using dwc_otg
[  130.996576] usb 1-1.3: New USB device found, idVendor=0c45, idProduct=760b, bcdDevice= 1.05
[  130.996613] usb 1-1.3: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[  130.996633] usb 1-1.3: Product: USB Keyboard
[  130.996650] usb 1-1.3: Manufacturer: SONiX
[  130.997809] drivers/char/myusbkbd.c myusbkbd_probe 33
[  130.997829] USB驱动匹配成功! ID: 0xC45,0x760B
[  130.998194] drivers/char/myusbkbd.c myusbkbd_probe 33
[  130.998211] USB驱动匹配成功! ID: 0xC45,0x760B

着实有点费解。不去想了。
Linux驱动开发(十四)---USB驱动开发学习(键盘+鼠标)

丰富probe函数

切换回通用识别的方式,先修改probe函数。这里主要增加两部分内容:

  • 消息获取
  • 输入子系统的调用。

首先,我们先增加获取一些用得到的数据的内容

struct usb_device *dev = NULL;
struct usb_host_interface *interface;
struct usb_endpoint_descriptor *endpoint;

struct input_dev *input_dev;
int i, pipe, maxp, maxps;

printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
printk("USB驱动匹配成功! ID: 0x%X,0x%X\n",id->idVendor,id->idProduct);

//获取设备信息,主机接口信息,终端描述符
dev = interface_to_usbdev(intf);
interface = intf->cur_altsetting;
endpoint = &interface->endpoint[0].desc;

//建立终端传输管道
pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);

maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
/*从端点描述符中获取传输的数据大小 */
maxps = endpoint->wMaxPacketSize;

printk("maxp[%d] maxps[%d]\n",maxp,maxps);

获取传输数据最大包数,发现了两种写法,标准驱动采用的上面的写法。其实都可用。

maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
maxps = endpoint->wMaxPacketSize;

然后就开始结合输入子系统,创建input_dev,注册各个按键。

//创建输入子系统
input_dev = input_allocate_device();

input_dev->name = "myusb_kbd";
input_dev->phys = "myusb_kbd";

input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
for (i = 0; i < 255; i++)
{
	set_bit(usb_kbd_keycode[i], input_dev->keybit);
}

到这里的话,输入设备会出现,但是如何与usb终端进行数据交互,这里就用到了urb这个重要操作了

USB请求块(USB request block,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内核。

那么简单来说,这个urb就是负责穿梭于驱动和设备之间,将用户数据带回来的单元。
Linux驱动开发(十四)---USB驱动开发学习(键盘+鼠标)

用法如下,注意:这里有个缓存大小的问题

USB支持4种基本的数据传输模式:控制传输、同步传输、中断传输、批量传输。控制传输方式支持双向传输,用来处理主端口到USB从端口的数据传输,包括设备控制指令、设备状态查询及确认命令。对于高速设备,允许数据包最大容量为8,16,32或64字节,对于低速设备只有8字节一种选择。

//urb申请使用
my_kbd_data = usb_alloc_coherent(dev, 8, GFP_ATOMIC, &my_kbd_data_dma);
my_kbd_urb = usb_alloc_urb(0, GFP_KERNEL);

usb_fill_int_urb(my_kbd_urb, dev, pipe, my_kbd_data,
		 		(maxp > 8 ? 8 : maxp),
				usb_mouse_irq, NULL, endpoint->bInterval);
my_kbd_urb->transfer_dma = my_kbd_data_dma;
my_kbd_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;

最后,注册输入设备,并且开始传输urb

input_register_device(input_dev);
usb_submit_urb(my_kbd_urb, GFP_KERNEL);

Linux驱动开发(十四)---USB驱动开发学习(键盘+鼠标)

中断函数先添加打印,看看前面的分析对不对。

static void my_usb_kbd_irq(struct urb *urb)
{
	printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
	usb_submit_urb(my_kbd_urb, GFP_ATOMIC);
}

上机调试

root@raspberrypi:/home/pgg/work/driver# insmod myusbkbd.ko 
root@raspberrypi:/home/pgg/work/driver# dmesg 
[ 5296.645084] drivers/char/myusbkbd.c myusbkbd_init 144
[ 5296.647678] usbcore: registered new interface driver myusbkbd_drv
root@raspberrypi:/home/pgg/work/driver# dmesg 
[ 5296.645084] drivers/char/myusbkbd.c myusbkbd_init 144
[ 5296.647678] usbcore: registered new interface driver myusbkbd_drv
[ 5309.228696] usb 1-1.3: new low-speed USB device number 7 using dwc_otg
[ 5309.375217] usb 1-1.3: New USB device found, idVendor=0c45, idProduct=760b, bcdDevice= 1.05
[ 5309.375255] usb 1-1.3: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[ 5309.375275] usb 1-1.3: Product: USB Keyboard
[ 5309.375292] usb 1-1.3: Manufacturer: SONiX
[ 5309.386724] drivers/char/myusbkbd.c myusbkbd_probe 73
[ 5309.386755] USB驱动匹配成功! ID: 0x0,0x0
[ 5309.386769] maxp[8] maxps[8]
[ 5309.387055] input: myusb_kbd as /devices/virtual/input/input4
[ 5309.891936] drivers/char/myusbkbd.c my_usb_kbd_irq 59
[ 5310.388930] drivers/char/myusbkbd.c my_usb_kbd_irq 59
[ 5310.893929] drivers/char/myusbkbd.c my_usb_kbd_irq 59
[ 5311.398930] drivers/char/myusbkbd.c my_usb_kbd_irq 59

看来流程是对的,不过这个中断,在没有按键的时候,还是会一直触发。我们来调试一下是不是有数据。
Linux驱动开发(十四)---USB驱动开发学习(键盘+鼠标)

调试中断信息

通过打印一下status,来看一下有没有数据

	printk("%s %s %d urb->status[%d]\n", __FILE__, __FUNCTION__, __LINE__,urb->status);

倒是一直有这个打印,然后status一直为0,说明一直有中断上来
间隔大约0.5秒。那这里暂时不分析为什么会一直上报。

解析按键并上报

增加一下按键分析,参考usbkbd.c的代码

static void my_usb_kbd_irq(struct urb *urb)
{
	int i=0;
	
	//printk("%s %s %d urb->status[%d]\n", __FILE__, __FUNCTION__, __LINE__,urb->status);

	for (i = 0; i < 8; i++)
	{
		input_report_key(my_kbd_input_dev, usb_kbd_keycode[i + 224], (my_kbd_data[0] >> i) & 1);
	}
	for (i = 2; i < 8; i++) 
	{
	
		if (my_kbd_data_old[i] > 3 && memscan(my_kbd_data + 2, my_kbd_data_old[i], 6) == my_kbd_data + 8) 
		{
			if (usb_kbd_keycode[my_kbd_data_old[i]])
			{
				input_report_key(my_kbd_input_dev, usb_kbd_keycode[my_kbd_data_old[i]], 0);
			}
			else
			{
				hid_info(urb->dev, "Unknown key (scancode %#x) released.\n", my_kbd_data_old[i]);
			}
		}

		if (my_kbd_data[i] > 3 && memscan(my_kbd_data_old + 2, my_kbd_data[i], 6) == my_kbd_data_old + 8) 
		{
			if (usb_kbd_keycode[my_kbd_data[i]])
			{
				input_report_key(my_kbd_input_dev, usb_kbd_keycode[my_kbd_data[i]], 1);
			}
			else
			{
				hid_info(urb->dev, "Unknown key (scancode %#x) pressed.\n", my_kbd_data[i]);
			}
		}
	}

	input_sync(my_kbd_input_dev);

	memcpy(my_kbd_data_old, my_kbd_data, 8);
	usb_submit_urb(urb, GFP_KERNEL);
}

更新驱动,然后监听一下生成的event1,按下按键A
Linux驱动开发(十四)---USB驱动开发学习(键盘+鼠标)
只有按下的时候有数据,说明还是对的。
Linux驱动开发(十四)---USB驱动开发学习(键盘+鼠标)

用户侧获取按键

那么怎么知道是A呢。那么我们用之前遥控器的用户侧程序,来解析一下事件
分别按下A和B
Linux驱动开发(十四)---USB驱动开发学习(键盘+鼠标)
看来event中定义的数值是吻合的
Linux驱动开发(十四)---USB驱动开发学习(键盘+鼠标)
那么我们就创建一个数组,在用户侧解析一下具体是什么按键。
Linux驱动开发(十四)---USB驱动开发学习(键盘+鼠标)
成功获取按键的值。
Linux驱动开发(十四)---USB驱动开发学习(键盘+鼠标)

完成鼠标驱动

在昨天的基础上,给鼠标也增加了输入系统,然后设备装载一下我自己写的两个驱动,看看能不能让鼠标动起来,键盘敲起来。
结果遇到一个有意思的问题。就是鼠标只能向右下方向移动,最终只能落到右下角。
原因就是键盘发送相对坐标的时候,应该是有符号的
Linux驱动开发(十四)---USB驱动开发学习(键盘+鼠标)
所以数组定义的时候,应该也是有符号的。
最终成功让我的树莓派,用上了自己的驱动。
Linux驱动开发(十四)---USB驱动开发学习(键盘+鼠标)

代码下载

《下载地址》
其实建议自己根绝usbkbd和usbmouse的源码自己理解和重写一遍,印象就会更加深刻。也更能理解usb对于hid设备的驱动流程。
Linux驱动开发(十四)---USB驱动开发学习(键盘+鼠标)

结束语

今天的大事就是一个年近八旬的老太太,让中国的热血青年们夜不能寐。不过还是那首歌中唱的:
朋友来了,有好酒;若是那豺狼来了,迎接他的有,biubiubiu。

马上就七夕了,别忘了过节啊
Linux驱动开发(十四)---USB驱动开发学习(键盘+鼠标)文章来源地址https://www.toymoban.com/news/detail-482471.html

到了这里,关于Linux驱动开发(十四)---USB驱动开发学习(键盘+鼠标)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【usb】linux内核USB键盘驱动解析--普通键值上报及转化

    建议阅读前置文章【usb】linux内核USB键盘驱动解析–特殊键值上报及转化 以Linux5.10内核中USB键盘驱动为例进行解析:https://mirrors.edge.kernel.org/pub/linux/kernel/v5.x/linux-5.10.tar.gz 文件路径:linux-5.10/drivers/hid/usbhid/usbkbd.c 本次我们主要分析第120~139行的这个for循环。 for循环变量i范围是

    2023年04月08日
    浏览(22)
  • Rockchip linux USB 驱动开发

    Linux USB 协议栈是一个分层的架构,如下图 5-1 所示,左边是 USB Device 驱动,右边是 USB Host 驱动,最底层是 Rockchip 系列芯片不同 USB 控制器和 PHY 的驱动。                                               Linux USB 驱动架构 Rockchip 系列芯片,主要使用两种 USB 2.0 PHY IP:Innosi

    2024年01月25日
    浏览(28)
  • 嵌入式Linux开发-USB驱动

    哥们马上就要被裁了,总得整理一下技术方面的积累,准备开始下一轮的面试和找工作之旅了。。。。 通用串行总线(USB)是主机和外围设备之间的一种连接。 从拓扑上来看,是一颗由几个点对点的连接构建而成的树。这些连接是连接设备和集线器(hub)的四线电缆(底线、电源线

    2024年02月20日
    浏览(52)
  • 【正点原子STM32连载】 第六十章 USB鼠标键盘(Host)实验 摘自【正点原子】MiniPro STM32H750 开发指南_V1.1

    1)实验平台:正点原子MiniPro H750开发板 2)平台购买地址:https://detail.tmall.com/item.htm?id=677017430560 3)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-336836-1-1.html 4)对正点原子STM32感兴趣的同学可以加群讨论:879133275 本章我们介绍如何使用STM32H750的USB HOST来驱动USB鼠

    2024年02月09日
    浏览(35)
  • Android 9.0 禁止usb键盘和usb鼠标挂载

    在9.0的系统产品开发中,对于系统中usb鼠标和usb键盘的等外设输入设备挂载处理,系统是在inputflinger模块中处理的,在产品的需求中对于外设输入设备的usb鼠标和usb键盘的挂载是禁用的,所以需要从挂载入手,禁止挂载usb鼠标和usb键盘 在android系统中是由各个子系统分工协作

    2024年02月09日
    浏览(31)
  • 教你STM32做USB鼠标、键盘

    使用CubeMX软件傻瓜式的配置,一键生成USB的HID驱动。 ①、选择相对应的芯片  ②、配置时钟和Debug和debug      ③、配置USB    ④、生成代码          最好把这个也勾上,勾上以后每个外设配置不再都给你塞到main.c里,而是建一个.c.h,这样感觉舒服多了         USB协议

    2024年01月23日
    浏览(27)
  • 【流量分析】USB键盘与鼠标流量分析

    USB流量指的是USB设备接口的流量,攻击者能够通过监听usb接口流量获取键盘敲击键、鼠标移动与点击、存储设备的铭文传输通信、USB无线网卡网络传输内容等等。 在CTF中,USB流量分析主要以键盘和鼠标流量为主。 下面通过简单的讲解与例题的展示,分析键盘流量与鼠标流量

    2024年02月08日
    浏览(29)
  • 项目:USB键盘和鼠标的复合设备

            我们的复合设备使用一个物理设备就可以完成多个功能。       使用复合设备同时完成USB键盘和鼠标功能,它的主要实现方式有两种, 第一个就是我们将多个设备描述符合并成一个,这个相对比较简单,我们只要根据相应的报告描述符处理数据就可以。 第二个就是

    2024年04月11日
    浏览(34)
  • linux嵌入式开发-Zynq开发板配置usb_gadget模拟HID鼠标

    在PC上使用VMWare,在ubuntu下创建petalinux工程,编译内核,vmware、vivdado、petalinux的安装详见alinx官方教程course4-linux实验中的步骤 创建petalinux工程,在工程目录下打开终端,输入命令准备编译内核 然后编译内核: 进入Devicedrivers - USB Support,勾选USB Gadget Support(按Y) 进入USB Gadge

    2024年02月04日
    浏览(43)
  • USB键盘鼠标描述符及数据格式分析

            1:键盘的hid描述符如下,数据的输入断点为中断方式,当有键盘敲击事件时,会上报长度为8字节的数据描述符,描述符共有8字节的输入报告和1字节的输出报告。         0x05,0x01,// Global Generic Desktop         0x09,0x06,// Local KeyBoard         0xA1,0x01,// Main app

    2024年04月22日
    浏览(27)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包