输入子系统之触摸屏分析
来源:互联网 发布:字体管家mac版下载 编辑:程序博客网 时间:2024/05/22 17:26
记录1:
字符设备设置名字在函数中
device_create(tty_class, device, dev, NULL, name);
dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs);
retval = kobject_set_name_vargs(&dev->kobj, fmt, args);
retval = device_register(dev);
与用户空间交互的结构体中设置的kobject名字就是用户需要使用的设备文件
记录2:
1. Input子系统由驱动层、输入子系统核心层和事件处理层三部分组成。驱动程序员需要开发的程序集中在驱动层,核心层和事件处理层在各平台可以通用。在驱动层主要涉及到的结构体就是input_dev结构体。input_dev是物理输入设备的基本数据结构,包括设备相关的一些信息。
struct input_dev { const char *name; const char *phys; const char *uniq; struct input_id id; unsigned long evbit[BITS_TO_LONGS(EV_CNT)]; //支持事件的类别 unsigned long keybit[BITS_TO_LONGS(KEY_CNT)]; //具体支持的事件属性如X坐标,Y坐标事件,Z坐标事件 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 keycodemax; unsigned int keycodesize; void *keycode; int (*setkeycode)(struct input_dev *dev, unsigned int scancode, unsigned int keycode); int (*getkeycode)(struct input_dev *dev, unsigned int scancode, unsigned int *keycode); struct ff_device *ff; unsigned int repeat_key; struct timer_list timer; int sync; int abs[ABS_CNT]; int rep[REP_MAX + 1]; unsigned long key[BITS_TO_LONGS(KEY_CNT)]; unsigned long led[BITS_TO_LONGS(LED_CNT)]; unsigned long snd[BITS_TO_LONGS(SND_CNT)]; unsigned long sw[BITS_TO_LONGS(SW_CNT)]; int absmax[ABS_CNT]; int absmin[ABS_CNT]; int absfuzz[ABS_CNT]; int absflat[ABS_CNT]; int absres[ABS_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); struct input_handle *grab; //grab是强制为input device的handler spinlock_t event_lock; struct mutex mutex; unsigned int users; bool going_away; struct device dev; struct list_head h_list; //handle链表 struct list_head node; //input_dev链表 };
具体案例分析相关三个文件input.c evdev.c w55fa93_ts.c,这三个文件分别位于核心层,事件层,驱动层。
与用户空间进行交互的file_operations位于事件层。
与底层硬件进行交互的函数接口位于驱动层。
作为子系统比较通用的两个文件是input.c evdev.c,当进行子系统设备注册后进行关联。即input_register_device函数。
驱动层
触摸屏涉及到的中断有两种:触碰中断和AD转换完成中断
首先作为一个平台设备,也需要做平台驱动注册。当匹配成功则调用drv->probe函数。
1. 当insmod驱动程序后会执行w55fa93ts_init函数。这个函数所做的工作就是平台驱动进行注册,与平台设备进行匹配,如果成功则执行平台驱动的probe函数。
static int __init w55fa93ts_init(void){ DBG_PRINTF2("ADC: %s\n", __FUNCTION__);#ifdef CONFIG_ADC_LIGHT_BATTERY sys_adc = platform_device_register_simple("w55fa93-adc", -1, NULL, 0); if (sys_adc == NULL) printk("register adc detection module failed\n"); sysfs_create_group(&sys_adc->dev.kobj, &adc_attr_group);#endif return platform_driver_probe(&w55fa93ts_driver, &w55fa93ts_probe);}
platform_driver_probe–>platform_driver_register–>driver_register–>bus_add_driver–>driver_attach(drv)–>return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach)–>__driver_attach–>driver_match_device–>driver_probe_device(drv, dev)–>really_probe(dev, drv)–>drv->probe(dev)
最终到了probe函数。
w55fa93ts_probe函数工作很简单:1.取出资源如中断号 2.获取adc时钟并使能 3.分配一个input_dev结构体并填充里面成员,主要填充的对象有名字、事件、事件属性、open、close等。 4.注册中断,注册子系统
static int __devinit w55fa93ts_probe(struct platform_device *pdev){ int irq, result, err; struct clk *clk; DBG_PRINTF("ADC: %s\n", __FUNCTION__); //outl(inl(REG_APBCLK) | ADC_CKE, REG_APBCLK); clk = clk_get(NULL, "ADC"); clk_enable(clk); irq = platform_get_irq(pdev, 0); if (irq < 0) { dev_err(&pdev->dev, "no irq for device\n"); return -ENOENT; } else { irqnum = irq; } if (!request_mem_region((unsigned long)W55FA93_VA_ADC, SZ_4K, "w55fa93-ts")) { return -EBUSY; } if (!(w55fa93_dev = input_allocate_device())) { printk(KERN_ERR "w55fa93_dev: not enough memory\n"); err = -ENOMEM; goto fail; } INIT_DELAYED_WORK(&ts_work, w55fa93ts_work); w55fa93_dev->name = "W55FA93 TouchScreen"; w55fa93_dev->phys = "w55fa93/event0"; w55fa93_dev->id.bustype = BUS_HOST; w55fa93_dev->id.vendor = 0x0005; w55fa93_dev->id.product = 0x0001; w55fa93_dev->id.version = 0x0100; w55fa93_dev->open = w55fa93ts_open; w55fa93_dev->close = w55fa93ts_close; w55fa93_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_SYN); // 支持按键类事件,绝对坐标事件和所有事件 w55fa93_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH); input_set_abs_params(w55fa93_dev, ABS_X, 0, 0x400, 0, 0); //对于X轴范围是0-ox400,数据误差是0,中心平滑位置是0,AD转换出的数据最大为10位,所以不会超过0x3ff。 input_set_abs_params(w55fa93_dev, ABS_Y, 0, 0x400, 0, 0); input_set_abs_params(w55fa93_dev, ABS_PRESSURE, 0, 1000, 0, 0); //这个是设置触摸屏是否按下的标志 result = request_irq(irq, adc_isr, IRQF_DISABLED | IRQF_SHARED, "ADC", w55fa93_dev); //outl((1 << irqnum ), REG_AIC_MDCR); disable_irq(irqnum); if (result != 0) printk("register ADC ISR failed!\n"); input_register_device(w55fa93_dev); DBG_PRINTF("Register touch screen success\n"); return 0;fail: input_free_device(w55fa93_dev); return err;}
接下来w55fa93ts_open函数
static int w55fa93ts_open(struct input_dev *dev){ //int result; //int irq; DBG_PRINTF2("ADC: %s\n", __FUNCTION__); spin_lock(&spin_ts_opc); i32TsOpenCount++; if (i32TsOpenCount != 1) { // Ts has open, spin_unlock(&spin_ts_opc); return 0; } else spin_unlock(&spin_ts_opc); init_timer(&ts_timer);#ifdef CONFIG_ADC_LIGHT_BATTERY init_timer(&ain_timer);#endif w55fa93_ts_pressing = 0; ts_timer.function = timer_check_touch; /* timer handler */#ifdef CONFIG_ADC_LIGHT_BATTERY ain_timer.function = timer_check_ain; mod_timer(&ain_timer, jiffies + AIN_FREQ);#endif#if 0 /* reset */ outl(inl(REG_APBIPRST) | ADCRST, REG_APBIPRST); udelay(10); outl(inl(REG_APBIPRST) & ~ADCRST, REG_APBIPRST); udelay(10);#endif outl(inl(REG_AUDIO_CON) | AUDIO_RESET, REG_AUDIO_CON); outl(inl(REG_AUDIO_CON) & ~AUDIO_RESET, REG_AUDIO_CON); outl(0x500000, REG_AUDIO_CON); outl(inl(REG_ADC_CON) | ADC_RST, REG_ADC_CON); outl(inl(REG_ADC_CON) & ~ADC_RST, REG_ADC_CON); outl(inl(REG_AGCP1) & ~0x80000000, REG_AGCP1); // Disable EDMA for ADC //Need to modify this value if need outl(0x1800, REG_ADC_DLY); //过多长时间后进行ad转换#ifdef CONFIG_ADC_LIGHT_BATTERY /* For report 1th battery */ SET_NORMAL_AIN2_WI; SET_NORMAL_AIN2_WI_WR; w55fa93_vol = inl(REG_ADC_XDATA); outl( inl(REG_ADC_CON) | WT_INT | LVD_INT | ADC_INT, REG_ADC_CON); SET_NORMAL_AIN3_WI; SET_NORMAL_AIN3_WI_WR; w55fa93_lux = inl(REG_ADC_XDATA); outl( inl(REG_ADC_CON) | WT_INT | LVD_INT | ADC_INT, REG_ADC_CON);#endif /* waiting for trigger mode */ outl( (inl(REG_ADC_CON) | (WT_INT_EN | ADC_TSC_MODE | WT_INT | ADC_INT | ADC_CON_ADC_EN)) & ~ADC_DIV, REG_ADC_CON); state = W55FA93_ADC_STATE_WT; DBG_PRINTF("Open: W55FA93_ADC_STATE_WT"); outl((inl(REG_ADC_TSC) | ADC_PU_EN), REG_ADC_TSC); __raw_writel(__raw_readl(REG_ADC_CON) & ~WT_INT, REG_ADC_CON); __raw_writel(__raw_readl(REG_ADC_CON) | WT_INT, REG_ADC_CON); outl((inl(REG_ADC_TSC) & ~ADC_PU_EN), REG_ADC_TSC); //adc_enable_irq(); enable_irq(irqnum); return 0;}
w55fa93ts_open函数完成的主要工作是定时器的初始化和硬件初始化工作。并将状态设置为等待触摸中断模式。
w55fa93ts_close 函数完成的工作就是失能中断寄存器和删除定时器。
static void w55fa93ts_close(struct input_dev *dev){ DBG_PRINTF2("ADC: %s\n", __FUNCTION__); spin_lock(&spin_ts_opc); i32TsOpenCount--; if (i32TsOpenCount < 0) i32TsOpenCount = 0; if (i32TsOpenCount != 0) { // Ts has open, spin_unlock(&spin_ts_opc); return; } else spin_unlock(&spin_ts_opc); spin_adc_int = SPIN_LOCK_UNLOCKED; //outl((1 << irqnum ), REG_AIC_MDCR); disable_irq(irqnum); del_timer(&ts_timer);#ifdef CONFIG_ADC_LIGHT_BATTERY del_timer(&ain_timer);#endif //return 0;}
接下来看看当触碰到触摸屏时产生中断的过程。本实验使用了工作队列,中断的处理放置在低半部的工作队列中。
static int w55fa93ts_work(){ unsigned long reg; unsigned char temp=0; u32 volatile xPos, yPos,xdata[2],ydata[2]; //unsigned long flags; DBG_PRINTF2("ADC: %s\n", __FUNCTION__); reg = inl(REG_ADC_CON); if ( (reg & WT_INT) && (state == W55FA93_ADC_STATE_WT) ) { //wait for trigger DBG_PRINTF("wait for trigger\n"); outl((inl(REG_ADC_TSC) | ADC_PU_EN), REG_ADC_TSC); __raw_writel(__raw_readl(REG_ADC_CON) & ~WT_INT, REG_ADC_CON); __raw_writel(__raw_readl(REG_ADC_CON) | WT_INT, REG_ADC_CON); outl((inl(REG_ADC_TSC) & ~ADC_PU_EN), REG_ADC_TSC); outl( inl(REG_ADC_CON) | WT_INT | LVD_INT | ADC_INT, REG_ADC_CON); DBG_PRINTF("WT INT = 0x%x\n", inl(REG_ADC_CON)); w55fa93_ts_pressing = 1; state = W55FA93_ADC_STATE_AUTO; SET_AUTO_MODE; /* It will disable WT_INT_EN. Otherwise WT_INT always interrupt system */ } else if ((reg & ADC_INT) && (state == W55FA93_ADC_STATE_AUTO)) { // auto conv end DBG_PRINTF("auto conv end\n"); outl( inl(REG_ADC_CON) | WT_INT | LVD_INT | ADC_INT, REG_ADC_CON); if ((inl(REG_ADC_TSC) & ADC_TSC_MAV_EN) ) { xPos = inl(REG_TSC_MAV_X); yPos = inl(REG_TSC_MAV_Y); } else { for (temp = 0; temp < 2; temp++) { DrvADC_Conversion(); DrvADC_PollingADC(); ydata[temp] = inl(REG_ADC_YDATA) & 0xFFFF; xdata[temp] = inl(REG_ADC_XDATA) & 0xFFFF; } } //Add 2012-04-24 state = W55FA93_ADC_STATE_WT; /* Why add the statement will cause crash ???*/ SET_WT_MODE;// udelay(2); //Wait for WT state stable (not stable) udelay(100); //Wait for WT state stable if ((inl(REG_ADC_TSC)&ADC_UD) == ADC_UD) { //Pen still in down state au16XPos[Idx] = xdata[0]; au16YPos[Idx] = ydata[0]; Idx++; au16XPos[Idx] = xdata[1]; au16YPos[Idx] = ydata[1]; Idx++; }else{ Idx=0; } if(Idx>3 &&(abs(au16XPos[0]-au16XPos[3])>30 ||abs(au16YPos[0]-au16YPos[3])>30 ||abs(au16XPos[1]-au16XPos[2])>30 ||abs(au16YPos[1]-au16YPos[2])>30)) { Idx=0;// printk("error position @@@\r\n"); } if ((inl(REG_ADC_TSC)&ADC_UD) == ADC_UD&&Idx>3) { //Pen still in down state DBG_PRINTF("Pen still in down state\n"); xPos = (au16XPos[SORT_FIFO - 4] + au16XPos[SORT_FIFO - 3] + au16XPos[SORT_FIFO - 2]+au16XPos[SORT_FIFO - 1]) >>2; //averaging yPos = (au16YPos[SORT_FIFO - 4] + au16YPos[SORT_FIFO - 3] + au16YPos[SORT_FIFO - 2]+au16YPos[SORT_FIFO - 1]) >>2; report_touch(xPos, yPos); count = 0; Idx=0; DBG_PRINTF("X0:%d Y0:%d\r\n",au16XPos[0],au16YPos[0]); DBG_PRINTF("X1:%d Y1:%d\r\n",au16XPos[1],au16YPos[1]); DBG_PRINTF("X2:%d Y2:%d\r\n",au16XPos[2],au16YPos[2]); DBG_PRINTF("X3:%d Y3:%d\r\n",au16XPos[3],au16YPos[3]); DBG_PRINTF("X Y = %d, %d\r\n", xPos, yPos); } else count = count + 1; mod_timer(&ts_timer, jiffies + INTERVAL_TIME); } else { // darn, unknown status. It should be wait for trigger interrupt. Change state to AUTO mode. DBG_PRINTF("darn, unknown status\n"); outl((inl(REG_ADC_TSC) | ADC_PU_EN), REG_ADC_TSC); __raw_writel(__raw_readl(REG_ADC_CON) & ~WT_INT, REG_ADC_CON); __raw_writel(__raw_readl(REG_ADC_CON) | WT_INT, REG_ADC_CON); outl((inl(REG_ADC_TSC) & ~ADC_PU_EN), REG_ADC_TSC); outl( inl(REG_ADC_CON) | WT_INT | LVD_INT | ADC_INT, REG_ADC_CON); DBG_PRINTF("WT INT = 0x%x\n", inl(REG_ADC_CON)); w55fa93_ts_pressing = 1; state = W55FA93_ADC_STATE_AUTO; SET_AUTO_MODE; /* It will disable WT_INT_EN. Otherwise WT_INT always interrupt system */ } enable_irq(irqnum);}
工作队列中首先判断产生中断的原因是触摸中断或者是adc转换完成产生的中断。
第一次是触摸笔按下产生中断了,设置按下标记,并启动ad转换
第二次是ad转换完成了,将ad转换的值读出来,并将状态设置为等待触摸中断状态。通过判断标记位查看按键是否还处于按下状态,表明并非抖动引起的按键操作。上报时间并启动定时器来检验长按。
static void timer_check_touch(unsigned long dummy){ //DBG_PRINTF("ADC: %s\n",__FUNCTION__); unsigned long flags; //outl((1 << irqnum ), REG_AIC_MDCR); adc_disable_irq(); LOCK(flags); /*2012-04-24*/ if (state != W55FA93_ADC_STATE_WT) { mod_timer(&ts_timer, jiffies + INTERVAL_TIME); UNLOCK(flags); //outl((1 << irqnum ), REG_AIC_MECR); adc_enable_irq(); return; } if ( ((inl(REG_ADC_TSC)&ADC_UD) == ADC_UD) ) { //report down/up state in WT mode. The bit is only workable in wake for trigger mode. SET_AUTO_MODE; state = W55FA93_ADC_STATE_AUTO; w55fa93_ts_pressing = 1; DBG_PRINTF("%s, ts is pressing, start this timer\n", __func__); mod_timer(&ts_timer, jiffies + INTERVAL_TIME); } else { if ( (u16LastX != 0) && (u16LastY != 0) ) { input_report_abs(w55fa93_dev, ABS_X, u16LastX); input_report_abs(w55fa93_dev, ABS_Y, u16LastY); input_report_key(w55fa93_dev, BTN_TOUCH, 0); input_report_abs(w55fa93_dev, ABS_PRESSURE, 0); input_report_abs(w55fa93_dev, ABS_PRESSURE, 0); input_sync(w55fa93_dev); } Idx = 0; count = 0;#else DBG_PRINTF("%s, SET_WT_MODE_I\n", __func__); SET_WT_MODE_I; state = W55FA93_ADC_STATE_WT; w55fa93_ts_pressing = 0;#endif } //mod_timer(&ts_timer, jiffies + INTERVAL_TIME); UNLOCK(flags); //outl((1 << irqnum ), REG_AIC_MECR); adc_enable_irq(); return;}
第三步在定时器中启动ad转换,若转换完成又回到中断函数中,并进行上报工作。
第四步若已经抬起触摸,则也启动定时器,并在定时器中上报抬起按键事件。
至此 驱动层差不多已经分析完了。
“drivers/input/input.c”核心层
int input_register_device(struct input_dev *dev){ static atomic_t input_no = ATOMIC_INIT(0); struct input_handler *handler; const char *path; int error; /* Every input device generates EV_SYN/SYN_REPORT events. */ __set_bit(EV_SYN, dev->evbit); /* KEY_RESERVED is not supposed to be transmitted to userspace. */ __clear_bit(KEY_RESERVED, dev->keybit); /* Make sure that bitmasks not mentioned in dev->evbit are clean. */ input_cleanse_bitmasks(dev); /* * If delay and period are pre-set by the driver, then autorepeating * is handled by the driver itself and we don't do it in input.c. */ init_timer(&dev->timer); if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) { dev->timer.data = (long) dev; dev->timer.function = input_repeat_key; 这个定时器上层会自动启动。 dev->rep[REP_DELAY] = 250; 按下多久开始算重复按键 dev->rep[REP_PERIOD] = 33; 之后每隔多久检查重复按下 } if (!dev->getkeycode) dev->getkeycode = input_default_getkeycode; if (!dev->setkeycode) dev->setkeycode = input_default_setkeycode; dev_set_name(&dev->dev, "input%ld", (unsigned long) atomic_inc_return(&input_no) - 1); error = device_add(&dev->dev); //作为一个输入设备,也需要将其进行注册,并加入到设备链表 if (error) return error; path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL); printk(KERN_INFO "input: %s as %s\n", dev->name ? dev->name : "Unspecified device", path ? path : "N/A"); kfree(path); error = mutex_lock_interruptible(&input_mutex); if (error) { device_del(&dev->dev); return error; } list_add_tail(&dev->node, &input_dev_list); list_for_each_entry(handler, &input_handler_list, node) //在handler链表上取出handler input_attach_handler(dev, handler); //进行比对,如果比对成功,则调用handler->connect函数。这个函数在事件层定义。 mutex_unlock(&input_mutex); return 0;}EXPORT_SYMBOL(input_register_device);
接下来看看事件层的东西,事件层有与用户空间进行交互的接口。
static const struct file_operations evdev_fops = { .owner = THIS_MODULE, .read = evdev_read, .write = evdev_write, .poll = evdev_poll, .open = evdev_open, .release = evdev_release, .unlocked_ioctl = evdev_ioctl,#ifdef CONFIG_COMPAT .compat_ioctl = evdev_ioctl_compat,#endif .fasync = evdev_fasync, .flush = evdev_flush};
在对事件层进行加载之后,会注册input_register_handler(&evdev_handler);这个函数的作用是将handler放置在链表上面。然后与输入设备进行比对。此函数在”drivers/input/input.c”核心层
int input_register_handler(struct input_handler *handler){ struct input_dev *dev; int retval; retval = mutex_lock_interruptible(&input_mutex); if (retval) return retval; INIT_LIST_HEAD(&handler->h_list); if (handler->fops != NULL) { if (input_table[handler->minor >> 5]) { retval = -EBUSY; goto out; } input_table[handler->minor >> 5] = handler; } list_add_tail(&handler->node, &input_handler_list); list_for_each_entry(dev, &input_dev_list, node) input_attach_handler(dev, handler); input_wakeup_procfs_readers(); out: mutex_unlock(&input_mutex); return retval;}EXPORT_SYMBOL(input_register_handler);
当匹配成功后,调用evdev_connect函数。
static int evdev_connect(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id){ struct evdev *evdev; int minor; int error; for (minor = 0; minor < EVDEV_MINORS; minor++) if (!evdev_table[minor]) break; if (minor == EVDEV_MINORS) { printk(KERN_ERR "evdev: no more free evdev devices\n"); return -ENFILE; } evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL); if (!evdev) return -ENOMEM; INIT_LIST_HEAD(&evdev->client_list); spin_lock_init(&evdev->client_lock); mutex_init(&evdev->mutex); init_waitqueue_head(&evdev->wait); dev_set_name(&evdev->dev, "event%d", minor); evdev->exist = 1; evdev->minor = minor; evdev->handle.dev = input_get_device(dev); evdev->handle.name = dev_name(&evdev->dev); evdev->handle.handler = handler; evdev->handle.private = evdev; evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor); evdev->dev.class = &input_class; evdev->dev.parent = &dev->dev; evdev->dev.release = evdev_free; device_initialize(&evdev->dev); error = input_register_handle(&evdev->handle); if (error) goto err_free_evdev; error = evdev_install_chrdev(evdev); if (error) goto err_unregister_handle; error = device_add(&evdev->dev); if (error) goto err_cleanup_evdev; return 0; err_cleanup_evdev: evdev_cleanup(evdev); err_unregister_handle: input_unregister_handle(&evdev->handle); err_free_evdev: put_device(&evdev->dev); return error;}
这个函数主要是对evdev设备的设备成员和handle成员进行填充。
并注册evdev设备,名字可以在/dev下看到event%d这个设备文件。
而后调用error = input_register_handle(&evdev->handle);函数进行handle的注册。
handle这个结构体是连接handler和input_dev的桥梁。
int input_register_handle(struct input_handle *handle){ struct input_handler *handler = handle->handler; struct input_dev *dev = handle->dev; int error; /* * We take dev->mutex here to prevent race with * input_release_device(). */ error = mutex_lock_interruptible(&dev->mutex); if (error) return error; /* * Filters go to the head of the list, normal handlers * to the tail. */ if (handler->filter) list_add_rcu(&handle->d_node, &dev->h_list); else list_add_tail_rcu(&handle->d_node, &dev->h_list); mutex_unlock(&dev->mutex); /* * Since we are supposed to be called from ->connect() * which is mutually exclusive with ->disconnect() * we can't be racing with input_unregister_handle() * and so separate lock is not needed here. */ list_add_tail_rcu(&handle->h_node, &handler->h_list); if (handler->start) handler->start(handle); return 0;}EXPORT_SYMBOL(input_register_handle);
在这个函数中主要是将其放置在各自的链表中,以便查找。
当用户打开这个设备后
static int evdev_open(struct inode *inode, struct file *file){ struct evdev *evdev; struct evdev_client *client; int i = iminor(inode) - EVDEV_MINOR_BASE; int error; if (i >= EVDEV_MINORS) return -ENODEV; error = mutex_lock_interruptible(&evdev_table_mutex); if (error) return error; evdev = evdev_table[i]; if (evdev) get_device(&evdev->dev); mutex_unlock(&evdev_table_mutex); if (!evdev) return -ENODEV; client = kzalloc(sizeof(struct evdev_client), GFP_KERNEL); if (!client) { error = -ENOMEM; goto err_put_evdev; } spin_lock_init(&client->buffer_lock); client->evdev = evdev; evdev_attach_client(evdev, client); error = evdev_open_device(evdev); if (error) goto err_free_client; file->private_data = client; nonseekable_open(inode, file); return 0; err_free_client: evdev_detach_client(evdev, client); kfree(client); err_put_evdev: put_device(&evdev->dev); return error;}
通过次设备号找到evdev结构体
static ssize_t evdev_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos){ struct evdev_client *client = file->private_data; struct evdev *evdev = client->evdev; struct input_event event; int retval; if (count < input_event_size()) return -EINVAL; if (client->head == client->tail && evdev->exist && (file->f_flags & O_NONBLOCK)) return -EAGAIN; retval = wait_event_interruptible(evdev->wait, client->head != client->tail || !evdev->exist); if (retval) return retval; if (!evdev->exist) return -ENODEV; while (retval + input_event_size() <= count && evdev_fetch_next_event(client, &event)) { if (input_event_to_user(buffer + retval, &event)) return -EFAULT; retval += input_event_size(); } return retval;}
如果如果没读到内容并设置了非阻塞O_NONBLOCK,则直接返回,否则等待有内容填充进来将其唤醒,而后拷贝至用户空间。
何时填充这个结构体呢?
input_event(dev, EV_KEY, code, !!value);这个函数是用来填充evdev结构体的
input_event–>input_handle_event(dev, type, code, value)–>input_pass_event(dev, type, code, value)–>evdev_event–>evdev_pass_event
–>wake_up_interruptible
- 输入子系统之触摸屏分析
- 触摸屏驱动程序设计 之 输入子系统原理分析
- 触摸屏驱动三部曲之输入子系统
- Linux输入子系统之触摸屏驱动2
- linux 输入子系统之电阻式触摸屏驱动
- linux 输入子系统之电阻式触摸屏驱动
- linux 输入子系统之电阻式触摸屏驱动
- 输入子系统之触摸屏驱动1(4th)
- linux 输入子系统之电阻式触摸屏驱动
- 触摸屏驱动程序设计之 输入子系统模型解析
- 输入子系统之典型源码分析
- Android输入子系统之启动过程分析
- linux input输入子系统分析《三》:S3C2440的触摸屏驱动实例
- linux input输入子系统分析《三》:S3C2440的触摸屏驱动实例
- linux input输入子系统分析《三》:S3C2440的触摸屏驱动实例
- linux input输入子系统分析《三》:S3C2440的触摸屏驱动实例
- linux input输入子系统分析《三》:S3C2440的触摸屏驱动实例
- linux input输入子系统分析《三》:S3C2440的触摸屏驱动实例
- 服务端连接日志(AsyncMessenger)
- struts2:处理结果类型
- python学习-07-浅谈对象与类(笔记)
- 表达式求值
- 第五章 5-5 GDI映射模式
- 输入子系统之触摸屏分析
- Msm8937+android7.1.1编译环境搭建及编译
- Hbase的安装
- Java中的BIO、NIO、AIO(NIO2)
- org.apache.jasper.JasperException: The Struts dispatcher cannot
- 在Ubuntu上部署Gearman
- 部署Maven项目到tomcat报错:java.lang.ClassNotFoundException: org.springframework.web.context.ContextLoaderLi
- 通过git托管代码到github
- input输入框模糊提示功能