.6内核输入子系统分析-续
2008年09月23日 星期二 上午 11:43
上文介绍了input_dev
、input_handle、input_handler三者是如何联系起来了,现在继续介绍如何通过它们来传递信息。
在开始之前还是先引用一位大侠的帖子:
引: 现在看用户获取触摸屏输入的一个流程(以tsdev为例/drivers/input/tsdev.c): static struct file_operations tsdev_fops = { .owner = THIS_MODULE, .open = tsdev_open, .release = tsdev_release, .read = tsdev_read, .poll = tsdev_poll, .fasync = tsdev_fasync, .ioctl = tsdev_ioctl, }; 假设所有初始化早已完成,用户open该设备后,使用read系统调用进入内核,系统 转移控制到tsdev_read,使用wait_event_interruptible等待事件。 此时驱动层得到用户输入,于是调用input_report_abs,input_report_abs只是 input_event的简单包装: static inline void input_report_abs(struct input_dev *dev, unsigned int code, int value) { input_event(dev, EV_ABS, code, value); } void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) { ... switch (type) { ... case EV_ABS: ... break; ... } ... if (dev->grab) dev->grab->handler->event(dev->grab, type, code, value); else list_for_each_entry(handle, &dev->h_list, d_node) if (handle->open) handle->handler->event(handle, type, code, value); } 前面的处理关系具体设备,见最后对handler函数的调用,就是从input_dev的h_list链 上的input_handle获得每一个相关input_handler,并调用其中的event函数,对tsdev 来说: static struct input_handler tsdev_handler = { .event = tsdev_event, .connect = tsdev_connect, .disconnect = tsdev_disconnect, .fops = &tsdev_fops, .minor = TSDEV_MINOR_BASE, .name = "tsdev", .id_table = tsdev_ids, }; 即调用tsdev_event函数,接着看: static void tsdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value) { ... switch (type) { case EV_ABS: break; ... list_for_each_entry(list, &tsdev->list, node) { int x, y, tmp; do_gettimeofday(&time); list->event[list->head].millisecs = time.tv_usec / 100; list->event[list->head].pressure = tsdev->pressure; x = tsdev->x; y = tsdev->y; /* Calibration */ if (!list->raw) { x = ((x * tsdev->cal.xscale) >> 8) + tsdev->cal.xtrans; y = ((y * tsdev->cal.yscale) >> 8) + tsdev->cal.ytrans; if (tsdev->cal.xyswap) { tmp = x; x = y; y = tmp; } } list->event[list->head].x = x; list->event[list->head].y = y; list->head = (list->head + 1) & (TSDEV_BUFFER_SIZE - 1); kill_fasync(&list->fasync, SIGIO, POLL_IN); } wake_up_interruptible(&tsdev->wait); } 它填充数据,并唤醒等待着的请求。于是前面等待着的read请求就可继续了, 回到tsdev_read中,copy_to_user拷贝数据,最后返回用户层。 一个简单流程就结束了。注: 原文请看以下网址: http://bbs.ustc.edu.cn/cgi/bbstcon?board=Kernel&file=M.1179398612.A 看了以上的内容,相信你对2.6内核的输入子系统的消息传递过程应该有个大概的了解了,现在我就如何通过input_dev 、input_handle、input_handler这三者传递信息进行详细的分析: ################ 从<tsdev.c>开始 ################ static ssize_t tsdev_read(struct file *file, char __user *buffer, size_t count, loff_t * ppos) { struct tsdev_list *list = file->private_data; int retval = 0; /* 设备存在(exist=1:在tsdev_connect函数里设置),但缓冲中无数据,而又配置为非阻塞读取方式,直接返回 */ if (list->head == list->tail && list->tsdev->exist && (file->f_flags & O_NONBLOCK)) return -EAGAIN; /* 否则睡眠等待数据都来临 */ retval = wait_event_interruptible(list->tsdev->wait, list->head != list->tail || !list->tsdev->exist); /* 被信号中断唤醒,直接返回 */ if (retval) return retval; /* 检查设备是否还存在,不存在(exist=0:在tsdev_disconnect函数中设置)的话直接返回 */ if (!list->tsdev->exist) return -ENODEV; /* 有数据,循环读取用户所需要都数据 */ while (list->head != list->tail && retval + sizeof (struct ts_event) <= count) { if (copy_to_user (buffer + retval, list->event + list->tail, sizeof (struct ts_event))) return -EFAULT; list->tail = (list->tail + 1) & (TSDEV_BUFFER_SIZE - 1);//更新读指针 retval += sizeof (struct ts_event);//更新读字节数 } return retval; } ################# <s3c2410-ts.c> ################# input_report_abs(&ts.dev, ABS_X, ts.xp); input_report_abs(&ts.dev, ABS_Y, ts.yp); input_report_key(&ts.dev, BTN_TOUCH, 1); input_report_abs(&ts.dev, ABS_PRESSURE, 1); input_sync(&ts.dev); ########################### <input.h> ########################### static inline void input_report_abs(struct input_dev *dev, unsigned int code, int value) { input_event(dev, EV_ABS, code, value); } ########################## <input.c> ########################## void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) { struct input_handle *handle; ........................................................................................................ switch (type) { case EV_ABS: if (code > ABS_MAX || !test_bit(code, dev->absbit))//一些条件测试 return; if (dev->absfuzz[code]) { if ((value > dev->abs[code] - (dev->absfuzz[code] >> 1)) && (value < dev->abs[code] + (dev->absfuzz[code] >> 1))) return; if ((value > dev->abs[code] - dev->absfuzz[code]) && (value < dev->abs[code] + dev->absfuzz[code])) value = (dev->abs[code] * 3 + value) >> 2; if ((value > dev->abs[code] - (dev->absfuzz[code] << 1)) && (value < dev->abs[code] + (dev->absfuzz[code] << 1))) value = (dev->abs[code] + value) >> 1; } if (dev->abs[code] == value)//比较当前值与上一次都值是否相同,相同则不作处理 return; dev->abs[code] = value;//备份当前值,以便下一次作比较 break; ............................................................................................................... if (type != EV_SYN) dev->sync = 0; if (dev->grab) dev->grab->handler->event(dev->grab, type, code, value); else list_for_each_entry(handle, &dev->h_list, d_node)//通过input_dev找出与其联系的input_handle if (handle->open)//相应的接口设备(比如tsdev)被打开(通过调用tsdev.c的tsdev_open函数进而调用input_open_device 函数增加handle->open的计数值) handle->handler->event(handle, type, code, value);//调用该接口设备的event函数对数据进行处理 } ################ 在<tsdev.c>结束 ################ static void tsdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value) { struct tsdev *tsdev = handle->private; struct tsdev_list *list; struct timeval time; switch (type) { case EV_ABS: switch (code) { case ABS_X: tsdev->x = value;//记录x坐标值 break; case ABS_Y: tsdev->y = value;//记录y坐标值 break; case ABS_PRESSURE: if (value > handle->dev->absmax[ABS_PRESSURE]) value = handle->dev->absmax[ABS_PRESSURE]; value -= handle->dev->absmin[ABS_PRESSURE]; if (value < 0) value = 0; tsdev->pressure = value;//记录触摸屏的按压状态 break; } break; ................................................................................................... if (type != EV_SYN || code != SYN_REPORT)//键值的传递以EV_SYN为结束标志(通过input_sync函数),等到数据都填充好tsdev结构后再统一发送出去,否则直接返回,继续填充另一个数据 return; list_for_each_entry(list, &tsdev->list, node) { //通过tsdev获取struct tsdev_list结构(在tsdev_open函数中定义): //list_add_tail(&list->node, &tsdev_table[i]->list); int x, y, tmp; do_gettimeofday(&time); //填充事件的时间 list->event[list->head].millisecs = time.tv_usec / 100; list->event[list->head].pressure = tsdev->pressure;//填充触摸屏的状态 x = tsdev->x; y = tsdev->y; /* Calibration */ if (!list->raw) { x = ((x * tsdev->cal.xscale) >> 8) + tsdev->cal.xtrans; y = ((y * tsdev->cal.yscale) >> 8) + tsdev->cal.ytrans; if (tsdev->cal.xyswap) { tmp = x; x = y; y = tmp; } } list->event[list->head].x = x; //填充x坐标值 list->event[list->head].y = y; //填充y坐标值 list->head = (list->head + 1) & (TSDEV_BUFFER_SIZE - 1);//更新写指针 kill_fasync(&list->fasync, SIGIO, POLL_IN); } wake_up_interruptible(&tsdev->wait);//唤醒睡眠在tsdev->wait下等待数据都进程读取数据。至此,数据传递过程结束,开始新一轮的数据传递。 |