Bluetooth Remote Controller Linux Kernel Key Report Flow

来源:互联网 发布:铁路运输数据 编辑:程序博客网 时间:2024/05/27 06:54

蓝牙遥控器与linux主机是通过HOGP profile进行连接通信,连接后会通过udev 创建hid device及input device,所以遥控器按键在linux内核传递的过程是:BLE-->HID-->input

本文基于4.9.26 内核

首先BLE 传过来 的按键时间通过 /dev/uhid 写入到内核

1. drivers/hid/uhid.c

static const struct file_operations uhid_fops = {.owner= THIS_MODULE,.open= uhid_char_open,.release= uhid_char_release,.read= uhid_char_read,.write= uhid_char_write,.poll= uhid_char_poll,.llseek= no_llseek,};static struct miscdevice uhid_misc = {.fops= &uhid_fops,.minor= UHID_MINOR,.name= UHID_NAME,};module_misc_device(uhid_misc);

static ssize_t uhid_char_write(struct file *file, const char __user *buffer,size_t count, loff_t *ppos){struct uhid_device *uhid = file->private_data;int ret;size_t len;printk("zewen---> [FUNC]%s [LINE]:%d\n", __FUNCTION__, __LINE__);/* we need at least the "type" member of uhid_event */if (count < sizeof(__u32))return -EINVAL;
2.
static int uhid_dev_input(struct uhid_device *uhid, struct uhid_event *ev){if (!uhid->running)return -EINVAL;printk("zewen---> [FUNC]%s [LINE]:%d\n", __FUNCTION__, __LINE__);hid_input_report(uhid->hid, HID_INPUT_REPORT, ev->u.input.data, min_t(size_t, ev->u.input.size, UHID_DATA_MAX), 0);return 0;}

3. drivers/hid/hid-core.c

int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int interrupt){struct hid_report_enum *report_enum;struct hid_driver *hdrv;struct hid_report *report;int ret = 0;printk("zewen---> [FUNC]%s [LINE]:%d\n", __FUNCTION__, __LINE__);if (!hid)return -ENODEV;if (down_trylock(&hid->driver_input_lock))return -EBUSY;if (!hid->driver) {ret = -ENODEV;goto unlock;}report_enum = hid->report_enum + type;hdrv = hid->driver;if (!size) {dbg_hid("empty report\n");ret = -1;goto unlock;}/* Avoid unnecessary overhead if debugfs is disabled */if (!list_empty(&hid->debug_list))hid_dump_report(hid, type, data, size);report = hid_get_report(report_enum, data);if (!report) {ret = -1;goto unlock;}if (hdrv && hdrv->raw_event && hid_match_report(hid, report)) {ret = hdrv->raw_event(hid, report, data, size);if (ret < 0)goto unlock;}ret = hid_report_raw_event(hid, type, data, size, interrupt);unlock:up(&hid->driver_input_lock);return ret;}EXPORT_SYMBOL_GPL(hid_input_report);
其中的语音会走下面这里:

if (hdrv && hdrv->raw_event && hid_match_report(hid, report)) {ret = hdrv->raw_event(hid, report, data, size);if (ret < 0)goto unlock;}
4.

int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size,int interrupt)
5.
static void hid_input_field(struct hid_device *hid, struct hid_field *field,    __u8 *data, int interrupt)
6.
static void hid_process_event(struct hid_device *hid, struct hid_field *field,struct hid_usage *usage, __s32 value, int interrupt)
7.drivers/hid/hid-input.c
void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value)

8.drivers/input/input.c

普通的按键key, 是从这个地方开始的(input_report_key 之后到这里)

void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value){unsigned long flags;if (is_event_supported(type, dev->evbit, EV_MAX)) {spin_lock_irqsave(&dev->event_lock, flags);input_handle_event(dev, type, code, value);spin_unlock_irqrestore(&dev->event_lock, flags);}}EXPORT_SYMBOL(input_event);
9.

static void input_handle_event(struct input_dev *dev,       unsigned int type, unsigned int code, int value)
10.这里如果判断是EV_KEY 事件, 且这个device支持EV_REP, 则就会启动autorepeat 功能(使用内核定时器实现)在kernel 实现多次重复上报key down 事件. 但是貌似上层的应用很少采用这种方式来key repeat功能, 大多是通过只要kernel 上报key down 事件后在一定时间内没有上报key up 事件, 上层应用会认为是key repeat, 并做相应的动作.

/* * Pass values first through all filters and then, if event has not been * filtered out, through all open handles. This function is called with * dev->event_lock held and interrupts disabled. */static void input_pass_values(struct input_dev *dev,      struct input_value *vals, unsigned int count){struct input_handle *handle;struct input_value *v;if (!count)return;rcu_read_lock();handle = rcu_dereference(dev->grab);if (handle) {count = input_to_handler(handle, vals, count);} else {list_for_each_entry_rcu(handle, &dev->h_list, d_node)if (handle->open) {count = input_to_handler(handle, vals, count);if (!count)break;}}rcu_read_unlock();/* trigger auto repeat for key events */if (test_bit(EV_REP, dev->evbit) && test_bit(EV_KEY, dev->evbit)) {for (v = vals; v != vals + count; v++) {if (v->type == EV_KEY && v->value != 2) {if (v->value){if(!strcmp("MI RC", dev->name) && v->code != 63 && v->code != 0){printk("zewen---> [FUNC]%s [LINE]:%d code:%d dev->name:%s start auto oneshot key\n", __FUNCTION__, __LINE__, v->code, dev->name);input_start_autooneshot(dev, v->code);}elseinput_start_autorepeat(dev, v->code);}else{if(!strcmp("MI RC", dev->name) && v->code != 63 && v->code != 0)printk("zewen---> [FUNC]%s [LINE]:%d MI RC do nothing\n", __FUNCTION__, __LINE__);elseinput_stop_autorepeat(dev);}}}}}
其中的实现autorepeat 的函数input_start_autorepeat, 就是启动之前设置好的定时器来实现key repeat

static void input_start_autorepeat(struct input_dev *dev, int code){if (test_bit(EV_REP, dev->evbit) &&    dev->rep[REP_PERIOD] && dev->rep[REP_DELAY] &&    dev->timer.data) {dev->repeat_key = code;mod_timer(&dev->timer,  jiffies + msecs_to_jiffies(dev->rep[REP_DELAY]));}}
11.这里的handler->events及handler->event 函数其实 就是input_dev对应input_handler 的evdev_events 及evdev_event 函数

static unsigned int input_to_handler(struct input_handle *handle,struct input_value *vals, unsigned int count){struct input_handler *handler = handle->handler;struct input_value *end = vals;struct input_value *v;if (handler->filter) {for (v = vals; v != vals + count; v++) {if (handler->filter(handle, v->type, v->code, v->value))continue;if (end != v)*end = *v;end++;}count = end - vals;}if (!count)return 0;if (handler->events)handler->events(handle, vals, count);else if (handler->event)for (v = vals; v != vals + count; v++)handler->event(handle, v->type, v->code, v->value);return count;}
12.drivers/input/evdev.c

static struct input_handler evdev_handler = {.event= evdev_event,.events= evdev_events,.connect= evdev_connect,.disconnect= evdev_disconnect,.legacy_minors= true,.minor= EVDEV_MINOR_BASE,.name= "evdev",.id_table= evdev_ids,};
/* * Pass incoming events to all connected clients. */static void evdev_events(struct input_handle *handle, const struct input_value *vals, unsigned int count){struct evdev *evdev = handle->private;struct evdev_client *client;ktime_t ev_time[EV_CLK_MAX];printk("zewen---> [FUNC]%s [LINE]:%d type:%d code:%d value:%d\n", __FUNCTION__, __LINE__, vals->type, vals->code, vals->value);ev_time[EV_CLK_MONO] = ktime_get();ev_time[EV_CLK_REAL] = ktime_mono_to_real(ev_time[EV_CLK_MONO]);ev_time[EV_CLK_BOOT] = ktime_mono_to_any(ev_time[EV_CLK_MONO], TK_OFFS_BOOT);rcu_read_lock();client = rcu_dereference(evdev->grab);if (client)evdev_pass_values(client, vals, count, ev_time);elselist_for_each_entry_rcu(client, &evdev->client_list, node)evdev_pass_values(client, vals, count, ev_time);rcu_read_unlock();}

之后input_event就会通过input_handler 由 /dev/input/eventX 上报到应用层, 这个过程也比较复杂,大约的过程是长层有一个service 会open /dev/input/eventX, 再执行read操作, 这是一个block操作,在内核中这个操作会去读一个input_event队列, 如果这个队列是空(队列头等于尾)的话就会阻塞. 而input_event 一直到刚才这里的evdev_events, 当然后面还有一些流程, 最终会将input事件封装为input_event, 并放入input_event队列中, 这时队列不再为空, 解阻塞并将事件上报到应用层, 需要用另一篇做介绍.


原创粉丝点击