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

/* 参考: 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几 要自己判断*/
相关推荐
码农研究僧1 分钟前
详细分析ip addr show 查看网络配置的命令
linux·tcp/ip·ip addr show
hutaotaotao2 分钟前
c语言用户不同命令调用不同函数实现
c语言·开发语言
码狂☆3 分钟前
ubuntu连接orangepi-zero-2w桌面的几种方法
linux·ubuntu
lb36363636365 分钟前
介绍一下位操作符(c基础)
c语言·知识点
学习向前冲21 分钟前
安装一键式重置密码插件(Linux)-CloudResetPwdAgent
linux·运维·服务器
石兴稳1 小时前
Ceph client 写入osd 数据的两种方式librbd 和kernel rbd
linux·ceph
大G哥2 小时前
python 数据类型----可变数据类型
linux·服务器·开发语言·前端·python
vortex52 小时前
解决 VSCode 中 C/C++ 编码乱码问题的两种方法
c语言·c++·vscode
BillKu2 小时前
Linux设置Nginx开机启动
linux·运维·nginx
Xiao Fei Xiangζั͡ޓއއ3 小时前
一觉睡醒,全世界计算机水平下降100倍,而我却精通C语言——scanf函数
c语言·开发语言·笔记·程序人生·面试·蓝桥杯·学习方法