input-dev输入子系统

来源:互联网 发布:闲鱼淘宝介入的客服 编辑:程序博客网 时间:2024/05/19 23:59

一.input子系统简介:

        linux系统中,input输入子系统驱动主要可以分为:设备驱动层、input core层和input handler事件处理层。设备驱动层为具体用户设备驱动,输入设备由struct input-dev 结构表示,并由input_register_device和input_unregister_device来注册和卸载;input hander事件处理层主要和用户空间交互,接收用户空间下发的file operation操作命令,生成/dev/input/xx设备节点供用户空间进行file operations操作; input core层负责管理系统中的input dev设备 和input hander事件处理,并起到承上启下作用,负责输入设备和input handler之间信息传输,输入子系统结构方框图如图1所示。


二.重要数据结构及函数接口:

      1.数据结构

   输入设备信息,匹配input hander时主要用下面参数

struct input_id {

__u16 bustype;                   总线类型

__u16 vendor;                       产家编号

__u16 product;                     产品编号

__u16 version;                        版本信息

};

用于表示输入设备数据结构:

struct input_dev{

const char *name;    设备名
const char *phys;      设备在系统中路径
const char *uniq;
struct input_id id;         用于匹配input hander参数

unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)];               

unsigned long evbit[BITS_TO_LONGS(EV_CNT)];                        设备所支持事件类型,主要有EV_SYNC,EV_KEY,EV_REL,EV_ABS等       

unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];                     按键所对应的位图

unsigned long relbit[BITS_TO_LONGS(REL_CNT)];                       相对坐标对应位图

unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];                       绝对坐标对应位图

unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];

unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];

unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];

unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];

unsigned long swbit[BITS_TO_LONGS(SW_CNT)];

unsigned int hint_events_per_packet;

unsigned int keycodemax;

unsigned int keycodesize;

void *keycode;

int (*setkeycode)(struct input_dev *dev,  const struct input_keymap_entry *ke,  unsigned int *old_keycode);

int (*getkeycode)(struct input_dev *dev, struct input_keymap_entry *ke);

struct ff_device *ff;

unsigned int repeat_key;

struct timer_list timer;

int rep[REP_CNT];

struct input_mt_slot *mt;

int mtsize;

int slot;

int trkid;

struct input_absinfo *absinfo;

unsigned long key[BITS_TO_LONGS(KEY_CNT)];                                 按键对应的键值

unsigned long led[BITS_TO_LONGS(LED_CNT)];                                  LED对应的指示灯状态

unsigned long snd[BITS_TO_LONGS(SND_CNT)];

unsigned long sw[BITS_TO_LONGS(SW_CNT)];

int (*open)(struct input_dev *dev);                                         

void (*close)(struct input_dev *dev);

int (*flush)(struct input_dev *dev, struct file *file);

int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);                  事件处理函数,主要是接收用户下发的命令,如点亮led;

struct input_handle __rcu *grab;

spinlock_t event_lock;

struct mutex mutex;

unsigned int users;

bool going_away;

bool sync;

struct device dev;

struct list_headh_list;                    设备所支持的input handle; 

struct list_headnode;

};

      用于 输入设备事件处理 的数据结构:

struct input_handler {

void *private;                            

void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);

bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value);

bool (*match)(struct input_handler *handler, struct input_dev *dev);

int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);   当输入设备和input handler相匹配时调用该函数;

void (*disconnect)(struct input_handle *handle);                

void (*start)(struct input_handle *handle);

const struct file_operations *fops;       所支持的file operation操作;

int minor;

const char *name;

const struct input_device_id *id_table;                 所有能够支持的输入设备;

struct list_headh_list;                                           

struct list_headnode;

};

连接input-dev 和input handler的数据结构:

struct input_handle {

void *private;         

int open;              

const char *name;                

struct input_dev *dev;                             input dev

struct input_handler *handler;               input handler

struct list_headd_node;                        

struct list_headh_node;

};

三. 设备驱动示例usbmouse

 

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 */

};

MODULE_DEVICE_TABLE (usb, usb_mouse_id_table);

通过USB_INTERFACE_INFO定定义一类USB device,usb device 和usb driver通过USB_DEVICE_ID_MATCH_INT_CLASS, USB_DEVICE_ID_MATCH_INT_SUBCLASS三个方面来进行匹配;

static struct usb_driver usb_mouse_driver = {

.name = "usbmouse",

.probe = usb_mouse_probe,

.disconnect=  usb_mouse_disconnect,

.id_table = usb_mouse_id_table,                                                    

};

static int __init usb_mouse_init(void)
{

int retval = usb_register(&usb_mouse_driver);

if (retval == 0)

printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"DRIVER_DESC "\n");

return retval;

}
static void __exit usb_mouse_exit(void)
{

usb_deregister(&usb_mouse_driver);

}

         USB MOUSE通过usb_register和usb_unregister注册驱动到系统,当usb hub检测到USB MOUSE设备时,通过usb_mouse_driver中定义的id_table来进行驱动匹配;

匹配成功后,调用usb_mouse_driver中的usb_mouse_probe;

static 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;

struct usb_mouse *mouse;

struct input_dev *input_dev;

int pipe, maxp;

int error = -ENOMEM;


interface = intf->cur_altsetting;                                                获取当前USB配置;

if (interface->desc.bNumEndpoints != 1)      判断usb mouse endpoint个数,usb mouse 一般只有一个interrupt endpoint ;

return -ENODEV;

endpoint = &interface->endpoint[0].desc;

if (!usb_endpoint_is_int_in(endpoint))                               判断endpoint是否为中断类型;

return -ENODEV;

pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);     创建中断接收类型的pipe,usb  控制器和usb device之间通过pipe来进行传输;

maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));                      获取endpoint 收发数据大小;

mouse = kzalloc(sizeof(struct usb_mouse), GFP_KERNEL);           为usbmouse申请内存空间;

input_dev = input_allocate_device();                                     申请input-dev空间,并进行初始化;

if (!mouse || !input_dev)

goto fail1;

申请用于存放从usb mouse获取得到数据的存储空间,如果USB 主控制器支持DMA方式,则通过变量data_dma来返回用于DMA方式获取数据的内存地址;

mouse->data = usb_alloc_coherent(dev, 8, GFP_ATOMIC, &mouse->data_dma);         

if (!mouse->data)                                                                   

goto fail1;

mouse->irq = usb_alloc_urb(0, GFP_KERNEL);                            申请int urb 空间;

if (!mouse->irq)

goto fail2;

mouse->usbdev = dev;

mouse->dev = input_dev;

       /*设备设备名字及设备路径;*/

if (dev->manufacturer)

strlcpy(mouse->name, dev->manufacturer, sizeof(mouse->name));

if (dev->product) {

if (dev->manufacturer)

strlcat(mouse->name, " ", sizeof(mouse->name));

strlcat(mouse->name, dev->product, sizeof(mouse->name));

}

if (!strlen(mouse->name))

snprintf(mouse->name, sizeof(mouse->name), "USB HIDBP Mouse %04x:%04x", le16_to_cpu(dev->descriptor.idVendor),le16_to_cpu(dev->descriptor.idProduct));

usb_make_path(dev, mouse->phys, sizeof(mouse->phys));

strlcat(mouse->phys, "/input0", sizeof(mouse->phys));

        input_dev->name = mouse->name;

input_dev->phys = mouse->phys;

设置用于匹配input hander的参数信息,这里主要有bustype,vendor,product,version,注意,在usb中数据用大端方式存放,所以要先进行转换;

usb_to_input_id(dev, &input_dev->id);                  

input_dev->dev.parent = &intf->dev;

input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);                设置input-dev所支持事件类型,这里usb mouse支持按键和相对坐标事件;

input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE) ;      设置EV_KEY对应按键位图;

input_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);                         设置EV_REL相应位图;

input_dev->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_SIDE) |  BIT_MASK(BTN_EXTRA);              

input_dev->relbit[0] |= BIT_MASK(REL_WHEEL);

input_set_drvdata(input_dev, mouse);

input_dev->open = usb_mouse_open;

input_dev->close = usb_mouse_close;

         /*填充int urb基本数据;*/

usb_fill_int_urb(mouse->irq, dev, pipe, mouse->data,  (maxp > 8 ? 8 : maxp) ,  usb_mouse_irq, mouse, endpoint->bInterval);

mouse->irq->transfer_dma = mouse->data_dma;           

mouse->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;          设置该标志可以防止主控制器再次申请dma空间;                   

error = input_register_device(mouse->dev);                注册input-dev设备

if (error)

goto fail3;

usb_set_intfdata(intf, mouse);

return 0;

fail3:

usb_free_urb(mouse->irq);

fail2:

usb_free_coherent(dev, 8, mouse->data, mouse->data_dma);

fail1:

input_free_device(input_dev);

kfree(mouse);

return error;

}

input-dev通过input_register_device注册到input core中,并通过input_id来匹配input handler,如果匹配成功,则会调用usb_mouse_open函数;

static int usb_mouse_open(struct input_dev *dev)
{

struct usb_mouse *mouse = input_get_drvdata(dev);


mouse->irq->dev = mouse->usbdev;

if (usb_submit_urb(mouse->irq, GFP_KERNEL))

return -EIO;

return 0;

}

usb_mouse_open函数主要用于提交usb interrupt urb到usb core中;当urb提交完成后不管usb core层有没有发送成功,都会调用回调函数usb_mouse_irq;

驱动中可以通过urb中的status来判断当前urb是否成功处理;

  static void usb_mouse_irq(struct urb *urb)
{

struct usb_mouse *mouse = urb->context;

signed char *data = mouse->data;

struct input_dev *dev = mouse->dev;

int status;


switch (urb->status) {                                                       通过status来判断urb处理是否成功;

case 0: /* success */

break;

case -ECONNRESET:/* unlink */

case -ENOENT:

case -ESHUTDOWN:

return;

/* -EPIPE:  should clear the halt */

default: /* error */

goto resubmit;

}


input_report_key(dev, BTN_LEFT,   data[0] & 0x01);              报告按键值;

input_report_key(dev, BTN_RIGHT,  data[0] & 0x02);

input_report_key(dev, BTN_MIDDLE, data[0] & 0x04);

input_report_key(dev, BTN_SIDE,   data[0] & 0x08);

input_report_key(dev, BTN_EXTRA,  data[0] & 0x10);


input_report_rel(dev, REL_X,     data[1]);                              报告相对坐标信息;

input_report_rel(dev, REL_Y,     data[2]);

input_report_rel(dev, REL_WHEEL, data[3]);


input_sync(dev);                                                 输入信息同步,表示报告事件结束;

resubmit:

status = usb_submit_urb (urb, GFP_ATOMIC);            重新提交urb,这样系统就可以不断获取usb mouse信息;

}

在usb_mouse_irq中,通过input_report_key和input_report_rel来上报对应按键信息和坐标信息;并通过input_sync来结束一次完整事件报告;最后通过usb_submit_urb来重复提交urb,可以使系统不断得到usb mouse信息;

四 总结

       从上面示例中可以得出写一个input-dev驱动主要要做事情;

1. 为input-dev申请空间;

2. 设置input-dev的名字及设备路径;

3.设置input-dev的id_table成员,及dev parent; 

4. 实现input-dev结构中的open,close,event(有些设备不需要)函数接口;

5.设置input-dev支持的事件类型,及各类型事件code对应的位图;

6.通过input_register_device注册input-dev设备;

7. 通过input_report_key,input_report_rel等函数接口上报相应事件;

8.最后通过input_sync来结束一次完整上报事件;



原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 苹果6蓝牙坏了怎么办 蓝牙密钥不正确不匹配怎么办 华为p6开不了机怎么办 华为c199手机不停重启怎么办 华为手机用户数据被锁定怎么办 朵唯手机丢了怎么办 网件r6220穿墙差怎么办 无线网打王者卡怎么办 酷翼x9忘了密码怎么办 楼上的路由器楼下不好使怎么办 电信4g网络不好怎么办 农村只有2g网怎么办 电信卡4g网速慢怎么办 小米手机触屏失灵怎么办 荣耀v10电信网速很慢怎么办 华为路由器掉线了怎么办 三星s6只识别一张卡怎么办 华为手机卡不显示了怎么办 华为账号手机卡丢了怎么办 荣耀8耗电量太快怎么办 vivo卡2不显示怎么办 电信宽带玩王者荣耀卡怎么办 联通4g玩游戏卡怎么办 华为悦盒遥控器丢了怎么办 6s不能用电信卡怎么办 iphone6电信卡无服务怎么办 魅族手机电信卡怎么办 小米手机读不出sim卡怎么办 魅蓝note6耗电快怎么办 oppo手机下载密码忘了怎么办 华为v9玩飞车卡怎么办 苹果7耳机转换器不支持怎么办 华为mate10耳机声音小怎么办 200打一年到期了怎么办 手机欠费变成2g怎么办 手机4g网用不了怎么办 手机玩王者荣耀卡怎么办 华为隐私空间密码忘记了怎么办 华为手机王者太卡怎么办 华为手机太卡怎么办呢 华为手机5x太卡!怎么办