输入子系统之触摸屏分析

来源:互联网 发布:字体管家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

1 0
原创粉丝点击