鼠标驱动框架:模拟键盘按键

复制代码
/* 参考: drivers\hid\usbhid\usbmouse.c */
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/usb.h>
#include <linux/input.h>
#include <linux/hid.h>

struct usb_mouse_as_key_desc {
    struct usb_device *dev;
    struct usb_interface *intf;
    const struct usb_device_id *id;
    int pipe, maxp;
    int bInterval;
    void *data_buffer;
    dma_addr_t data_dma;
    struct urb *urb;
};

/* 1. 构造usb_driver 
 * 1.1 id_table : 能支持哪些设备
 * 1.2 probe : 记录某些信息, 分配/设置/注册input_dev, 也许也可以在probe里面做"分配/填充/提交URB"
 */

static struct usb_device_id usb_mouse_as_key_id_table [] = {
	{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
		USB_INTERFACE_PROTOCOL_MOUSE), .driver_info = (kernel_ulong_t)"it is a mouse", },/*.driver_info = (kernel_ulong_t)"it is a mouse" 可以增加一些probe需要的驱动信息,probe里面会自动得到这些信息*/
	{ }	/* Terminating entry */
};


static void usb_mouse_as_key_irq(struct urb *urb)
{
	struct input_dev *dev = urb->context;
    struct usb_mouse_as_key_desc *desc = input_get_drvdata(dev);
	signed char *data = desc->data_buffer;
	int status;

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

	switch (urb->status) {
	case 0:			/* success */
		break;
	case -ECONNRESET:	/* unlink */
	case -ENOENT:
	case -ESHUTDOWN:
		return;
	/* -EPIPE:  should clear the halt */
	default:		/* error */
		goto resubmit;  /*如果错误,重新提交*/
	}

//    printk("data[0] = 0x%x\n", data[1]);
	input_report_key(dev, KEY_L, data[1] & 0x01);   /*查看鼠标数据格式可知data[1]代表 按键状态   见 07_使用libusb读取鼠标数据.md*/
	input_report_key(dev, KEY_S, data[1] & 0x02);
	input_report_key(dev, KEY_ENTER, data[1] & 0x04);

	input_sync(dev);
resubmit:
	status = usb_submit_urb (urb, GFP_ATOMIC);
    //printk("%s %s %d, status = %d\n", __FILE__, __FUNCTION__, __LINE__, status);
}


static int usb_mouse_as_key_open(struct input_dev *dev)
{
    struct urb *urb;
    struct usb_mouse_as_key_desc *desc = input_get_drvdata(dev); /*获取我们之前存入的驱动信息*/
    int err;
    urb= desc->urb;
    printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);

	
    err = usb_submit_urb (urb, GFP_KERNEL);
    
    printk("%s %s %d, err = %d\n", __FILE__, __FUNCTION__, __LINE__, err);
    
    return err;
}

static void usb_mouse_as_key_close(struct input_dev *dev)
{
    struct usb_mouse_as_key_desc *desc = input_get_drvdata(dev);    /*获取我们之前存入的驱动信息*/
    
    /* 取消/释放 URB */
    usb_kill_urb(desc->urb);

    usb_free_urb(desc->urb);
}


static int usb_mouse_as_key_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
    struct usb_device *dev = interface_to_usbdev(intf);
    struct input_dev *input_dev;
    struct usb_mouse_as_key_desc *desc;
    int error;
	struct usb_host_interface *interface;
	struct usb_endpoint_descriptor *endpoint;
	int pipe, maxp;

    printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
    /* 1. 记录设备信息:  intf */
	
	/*判断是不是真正的鼠标*/
	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;

	pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
	maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));  /*管道一次性能够传输的数据*/

    desc = kmalloc(sizeof(struct usb_mouse_as_key_desc), GFP_KERNEL);
    desc->dev  = dev;
    desc->intf = intf;
    desc->id   = id;
    desc->pipe = pipe;
    desc->maxp = maxp;
    desc->bInterval = endpoint->bInterval;
    desc->data_buffer = usb_alloc_coherent(dev, maxp, GFP_ATOMIC, &desc->data_dma); /*分配dma*/
    //printk("%s %s %d, desc->data_buffer = 0x%x\n", __FILE__, __FUNCTION__, __LINE__, desc->data_buffer);

    /* 2. 分配/设置/注册input_dev 
     * 2.1 能产生哪类事件
     * 2.2 能产生这类事件里哪些些事件: L/S/ENTER       ,可见   input   框架
     * 2.3 设置函数, 比如open
     * 2.4 在open函数里: 分配/填充/提交 URB
     * 2.5 URB的回调函数: 解析数据, 上报input_event
     */

    input_dev = devm_input_allocate_device(&intf->dev);  /*里面自带free   ,一般带前缀的allocate,里面都自带free*/
    
    input_set_drvdata(input_dev, desc);   /*存入一些我们想要的驱动描述信息*/

	/* set 1: which type event ? */	
	__set_bit(EV_KEY, input_dev->evbit);

	/* set 2: which event ? */	
	__set_bit(KEY_L, input_dev->keybit);
	__set_bit(KEY_S, input_dev->keybit);
	__set_bit(KEY_ENTER, input_dev->keybit);

    /* set 3: open */
    input_dev->open  = usb_mouse_as_key_open;
    input_dev->close = usb_mouse_as_key_close;

	    /* 分配/填充 URB */
    desc->urb = usb_alloc_urb(0, GFP_KERNEL);

    usb_fill_int_urb(desc->urb,
				     desc->dev,
				     desc->pipe,
				     desc->data_buffer,
				     (desc->maxp > 8 ? 8 : desc->maxp),
				     usb_mouse_as_key_irq,
				     dev,           /*context参数会传入回调函数*/
				     desc->bInterval);
    /**如果URB使用DMA Buffer,需要如下的设置**/
	desc->urb->transfer_dma = desc->data_dma;     
	desc->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;

	
	error = input_register_device(input_dev);

    usb_set_intfdata(intf, input_dev);
    
    return 0;
}

static void usb_mouse_as_key_disconnect(struct usb_interface *intf)
{
	struct input_dev *input_dev = usb_get_intfdata (intf);
    struct usb_mouse_as_key_desc *desc = input_get_drvdata(input_dev);

    usb_free_coherent(desc->dev, desc->maxp, desc->data_buffer, desc->data_dma);
    kfree(desc);
        
    input_unregister_device(input_dev);
    usb_set_intfdata(intf, NULL);
}


static struct usb_driver usb_mouse_as_key_driver = {
	.name		= "usbmouse_as_key",
	.probe		= usb_mouse_as_key_probe,
	.disconnect	= usb_mouse_as_key_disconnect,
	.id_table	= usb_mouse_as_key_id_table,
};

/* 入口函数  / 出口函数 */
// module_usb_driver(usb_mouse_as_key_driver);可以使用这个宏一键完成入口函数  / 出口函数 等同于196:208行代码
static int __init usb_mouse_as_key__init(void)
{
    printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
	return usb_register(&usb_mouse_as_key_driver);
}

static void __exit usb_mouse_as_key__init_exit(void)
{ 
	usb_deregister(&usb_mouse_as_key_driver);
}

module_init(usb_mouse_as_key__init);
module_exit(usb_mouse_as_key__init_exit);


/* 2. 注册usb_driver */

MODULE_LICENSE("GPL");

装载该驱动时,首先要卸载原本内核自带的驱动程序

把USB鼠标查到开发板上

先看看原来有哪些设备节点

ls /dev/input/event*

安装驱动程序

insmod usbmouse_as_key.ko

再看看新得到了哪个设备节点

ls /dev/input/event*

执行命令, 假设event4是新节点

hexdump /dev/input/event4

点击鼠标按键即可观察输出信息

第2种测试方法: 执行以下命令,按鼠标左键、右键,再按中键就有输出"ls"

cat /dev/tty0

第3种测试方法: 执行以下命令(注意"<"号前后没有空格),就可以使用鼠标按键在控制台输入字符

exec 0</dev/tty0

打印内核input信息:

复制代码
hexdump /dev/input/event*  /*具体对应 event几 要自己判断*/
相关推荐
郝亚军30 分钟前
如何在Ubuntu和win10/11之间通过samba访问对方的文件
linux·服务器·ubuntu
爱编码的小八嘎1 小时前
C语言对话-21.模板特化,缺省参数和其他一些有趣的事情
c语言
曦云沐1 小时前
【避坑指南】Ubuntu更新报错“Repository is not signed”的快速修复
linux·ubuntu·docker
带土12 小时前
10. .out文件
linux
STCNXPARM2 小时前
Linux camera之V4L2子系统详解
android·linux·camera·v4l2架构
yueyuexiaokeai12 小时前
linux kernel常用函数整理
linux·c语言
想放学的刺客3 小时前
单片机嵌入式试题(第29期)嵌入式系统的电源完整性设计与去耦电容选型。抗干扰设计与EMC合规性
c语言·stm32·嵌入式硬件·物联网·51单片机
郝亚军4 小时前
ubuntu-18.04.6-desktop-amd64安装步骤
linux·运维·ubuntu
Konwledging4 小时前
kernel-devel_kernel-headers_libmodules
linux
Web极客码4 小时前
CentOS 7.x如何快速升级到CentOS 7.9
linux·运维·centos