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 repeatstatic 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队列中, 这时队列不再为空, 解阻塞并将事件上报到应用层, 需要用另一篇做介绍.
阅读全文
0 0
- Bluetooth Remote Controller Linux Kernel Key Report Flow
- Network Data Flow through the Linux Kernel
- Bluetooth init flow
- Bluetooth init flow
- qualcomm bluetooth enable flow
- Linux kernal map & Network data flow through kernel
- linux kernel packet receive flow(内核封包处理流程)
- 10 Highlights of Jon Corbet's Linux Kernel Report
- Bluetooth Link Controller Operation
- start kernel flow
- Linux Kernel Controller Area Network Protocol Local Privilege Escalation Vulnerability
- uboot and kernel boot flow
- kernel中bluetooth的初始化
- kernel中bluetooth的初始化
- Linux Bluetooth
- linux bluetooth
- linux-kernel编译出现can't read private key
- android camera flow and V4L2 in linux kernel--------android 调用流程
- 安装Qt及相关问题解决
- 快速解决Android中的SELinux权限问题
- mysql语句执行超时设置
- 微信刷卡支付子商户(服务商)
- 第三周项目3
- Bluetooth Remote Controller Linux Kernel Key Report Flow
- $('div').click()事件不能用
- MySQL Update inner join数据库去重,以及根据一张表的值更新另一张表
- DB2 Load命令与DB2_LOAD_COPY_NO_OVERRIDE 注册表变量
- 《Modern Python Cookbook》(Python编程范例)笔记1.2 命名
- 传输层-4、TCP协议
- 计算今年和去年的时间,一个月的都是,用来计算同比
- 2.22 网络基础
- Linux下编译安装Nginx