Linux输入子系统分析(二)

来源:互联网 发布:nginx http1.0 配置 编辑:程序博客网 时间:2024/05/16 19:17

                                              基于Linux输入子系统的触摸屏输入设备驱动分析

1、下面的代码只是关于输入子系统的一部分,据此分析其原理

#include <linux/input.h>

/* For ts->dev.id.version */
#define S3C_TSVERSION 0x0101 //触摸屏版本号

/* Touchscreen default configuration */触摸屏默认的初始化值
struct s3c_ts_mach_info s3c_ts_default_cfg __initdata = {
                .delay = 10000,
                .presc = 49,
                .oversampling_shift = 2,
.resol_bit = 10
};

/*
 * Definitions & global arrays.
 */
static char *s3c_ts_name = "S3C TouchScreen";
static void __iomem *ts_base;
static struct resource *ts_mem;
static struct resource *ts_irq;
static struct clk *ts_clock;
static struct s3c_ts_info *ts;

static int downflag=0;

static void touch_timer_fire(unsigned long data)
{
unsigned long data0;
unsigned long data1;
int updown;

data0 = readl(ts_base+S3C_ADCDAT0);
data1 = readl(ts_base+S3C_ADCDAT1);

updown = (!(data0 & S3C_ADCDAT0_UPDOWN)) && (!(data1 & S3C_ADCDAT1_UPDOWN));

if (0) {
unsigned tmp;
tmp = readl(S3C64XX_GPACON);
printk("GPACON: 0x%X\n", tmp);
}

if (updown) {
                     if (ts->count) {

                              if(downflag==0)
                             {
                                  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);
                             }
                            else
                              {
    downflag=0;
                              }
                              }

                            ts->xp = 0;
                           ts->yp = 0;
                           ts->count = 0;
                          writel(S3C_ADCTSC_PULL_UP_DISABLE | AUTOPST, ts_base+S3C_ADCTSC);
                          writel(readl(ts_base+S3C_ADCCON) | S3C_ADCCON_ENABLE_START, ts_base+S3C_ADCCON);
                       }
            else {

                   ts->xp = 0;
                   ts->yp = 0;
                   ts->count = 0;
                   input_report_key(ts->dev, BTN_TOUCH, 0);
                   input_report_abs(ts->dev, ABS_PRESSURE, 0);
                   input_sync(ts->dev);

                   writel(WAIT4INT(0), ts_base+S3C_ADCTSC);
             }
}

static struct timer_list touch_timer =
TIMER_INITIALIZER(touch_timer_fire, 0, 0);

static irqreturn_t stylus_updown(int irqno, void *param)
{
               ...................
}

static irqreturn_t stylus_action(int irqno, void *param)
{
                       ....................
}
/*
 * The functions for inserting/removing us as a module.
 */

/*
* The functions for inserting/removing us as a module.
*/
static int __init s3c_ts_probe(struct platform_device *pdev)
{
struct resource *res;
struct device *dev;
struct input_dev *input_dev;声明为输入子系统设备
struct s3c_ts_mach_info * s3c_ts_cfg;
int ret, size;

dev = &pdev->dev;

res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
dev_err(dev,"no memory resource specified\n");
return -ENOENT;
}

size = (res->end - res->start) + 1;
ts_mem = request_mem_region(res->start, size, pdev->name);
if (ts_mem == NULL) {
dev_err(dev, "failed to get memory region\n");
ret = -ENOENT;
goto err_req;
}

ts_base = ioremap(res->start, size);
if (ts_base == NULL) {
dev_err(dev, "failed to ioremap() region\n");
ret = -EINVAL;
goto err_map;
}

ts_clock = clk_get(&pdev->dev, "adc");
if (IS_ERR(ts_clock)) {
dev_err(dev, "failed to find watchdog clock source\n");
ret = PTR_ERR(ts_clock);
goto err_clk;
}

clk_enable(ts_clock);

s3c_ts_cfg = s3c_ts_get_platdata(&pdev->dev);

if ((s3c_ts_cfg->presc&0xff) > 0)
writel(S3C_ADCCON_PRSCEN | S3C_ADCCON_PRSCVL(s3c_ts_cfg->presc&0xFF),\
ts_base+S3C_ADCCON);
else
writel(0, ts_base+S3C_ADCCON);


/* Initialise registers */
if ((s3c_ts_cfg->delay&0xffff) > 0)
writel(s3c_ts_cfg->delay & 0xffff, ts_base+S3C_ADCDLY);

if (s3c_ts_cfg->resol_bit==12) {
switch(s3c_ts_cfg->s3c_adc_con) {
case ADC_TYPE_2:
writel(readl(ts_base+S3C_ADCCON)|S3C_ADCCON_RESSEL_12BIT, ts_base+S3C_ADCCON);
break;

case ADC_TYPE_1:
writel(readl(ts_base+S3C_ADCCON)|S3C_ADCCON_RESSEL_12BIT_1, ts_base+S3C_ADCCON);
break;

default:
dev_err(dev, "Touchscreen over this type of AP isn't supported !\n");
break;
}
}

writel(WAIT4INT(0), ts_base+S3C_ADCTSC);

ts = kzalloc(sizeof(struct s3c_ts_info), GFP_KERNEL);
//从这里开始设置input device
input_dev = input_allocate_device();分配一个输入设备结构

ts->dev = input_dev;

ts->dev->evbit[0] = ts->dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
ts->dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);

if (s3c_ts_cfg->resol_bit==12) {
input_set_abs_params(ts->dev, ABS_X, 0, 0xFFF, 0, 0);
input_set_abs_params(ts->dev, ABS_Y, 0, 0xFFF, 0, 0);
}
else {
input_set_abs_params(ts->dev, ABS_X, 0, 0x3FF, 0, 0);
input_set_abs_params(ts->dev, ABS_Y, 0, 0x3FF, 0, 0);
}

input_set_abs_params(ts->dev, ABS_PRESSURE, 0, 1, 0, 0);

sprintf(ts->phys, "input(ts)");
//填充input device
ts->dev->name = s3c_ts_name;
ts->dev->phys = ts->phys;
ts->dev->id.bustype = BUS_RS232;
ts->dev->id.vendor = 0xDEAD;
ts->dev->id.product = 0xBEEF;
ts->dev->id.version = S3C_TSVERSION;

ts->shift = s3c_ts_cfg->oversampling_shift;
ts->resol_bit = s3c_ts_cfg->resol_bit;
ts->s3c_adc_con = s3c_ts_cfg->s3c_adc_con;

/* For IRQ_PENDUP */
ts_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);

ret = request_irq(ts_irq->start, stylus_updown, IRQF_SAMPLE_RANDOM, "s3c_updown", ts);

/* For IRQ_ADC */
ts_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 1);

ret = request_irq(ts_irq->start, stylus_action, IRQF_SAMPLE_RANDOM, "s3c_action", ts);

printk(KERN_INFO "%s got loaded successfully : %d bits\n", s3c_ts_name, s3c_ts_cfg->resol_bit);

/* All went ok, so register to the input system */
ret = input_register_device(ts->dev);


}


static struct platform_driver s3c_ts_driver = {
       .probe          = s3c_ts_probe,
       .remove         = s3c_ts_remove,
       .suspend        = s3c_ts_suspend,
       .resume         = s3c_ts_resume,
       .driver = {
.owner = THIS_MODULE,
.name = "s3c-ts",
},
};

static int __init s3c_ts_init(void)
{
            printk(banner);
            return platform_driver_register(&s3c_ts_driver);
}

static void __exit s3c_ts_exit(void)
{
            platform_driver_unregister(&s3c_ts_driver);
}
module_init(s3c_ts_init);
module_exit(s3c_ts_exit);

2、input_dev注册

     由上代码可看到在s3c_ts_probe函数的完成input_dev结构的初始化后,最后调用input_register_device()往输入子系统注册设备。进入inpu_register_device看其源码实现如下:

/**
 * input_register_device - register device with input core
 * @dev: device to be registered
 *
 * This function registers device with input core. The device must be
 * allocated with input_allocate_device() and all it's capabilities
 * set up before registering.
 * If function fails the device must be freed with input_free_device().
 * Once device has been successfully registered it can be unregistered
 * with input_unregister_device(); input_free_device() should not be
 * called in this case.
 */
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_new)
dev->getkeycode_new = input_default_getkeycode;

if (!dev->setkeycode && !dev->setkeycode_new)
dev->setkeycode_new = 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);
pr_info("%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);
       //将本设备和已经存在的handler进行比较,与id相匹配的handler建立连接。需要说明的是设备可能跟多个handler连接,这样此设备产生的事件会分发给所有连接的handler
list_for_each_entry(handler, &input_handler_list, node)

           input_attach_handler(dev, handler);

input_wakeup_procfs_readers();

mutex_unlock(&input_mutex);

return 0;
}

下面继续分析input_attach_handler这个函数

static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
const struct input_device_id *id;
int error;
        // handler->id_table存储handler支持的设备id。如果能够找到匹配的id,则建立devhandler之间的连接
id = input_match_device(handler, dev);
if (!id)
return -ENODEV;
       //建立devhandler之间的连接
error = handler->connect(handler, dev, id);
if (error && error != -ENODEV)
pr_err("failed to attach handler %s to device %s, error: %d\n",
      handler->name, kobject_name(&dev->dev.kobj), error);

return error;
}

下面看看input_match_device的实现

static const struct input_device_id *input_match_device(struct input_handler *handler,
struct input_dev *dev)
{
const struct input_device_id *id;
int i;
              /*根据id->flag检查id是否匹配。id->flag记录需要匹配哪些域*/
for (id = handler->id_table; id->flags || id->driver_info; id++) {

if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)
if (id->bustype != dev->id.bustype)
continue;
if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)
if (id->vendor != dev->id.vendor)
continue;
if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)
if (id->product != dev->id.product)
continue;
if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)
if (id->version != dev->id.version)
continue;
                           检查支持的事件种类是否一致
MATCH_BIT(evbit,  EV_MAX); 
MATCH_BIT(keybit, KEY_MAX);
MATCH_BIT(relbit, REL_MAX);
MATCH_BIT(absbit, ABS_MAX);
MATCH_BIT(mscbit, MSC_MAX);
MATCH_BIT(ledbit, LED_MAX);
MATCH_BIT(sndbit, SND_MAX);
MATCH_BIT(ffbit,  FF_MAX);
MATCH_BIT(swbit,  SW_MAX);


if (!handler->match || handler->match(handler, dev))
return id;
}
return NULL;
}


#define MATCH_BIT(bit, max) \
for (i = 0; i < BITS_TO_LONGS(max); i++) \
if ((id->bit[i] & dev->bit[i]) != id->bit[i]) \  
break; \
if (i != BITS_TO_LONGS(max)) \
continue;

        其中最重要的一句是“if ((id->bit[i] &dev->bit[i]) != id->bit[i])”,这句话意味着id支持的事件种类是dev支持的事件的子集就算匹配了。如果某个handlerid除了id->driver_info之外的域都为0,那么此handler可以和任意dev匹配。实际上<内核>/driver/input/evdev.c中就是这么初始化id的。

        总结一下input_dev注册的过程:一个input_dev注册的过程主要是在将自己加入input_dev_list,然后在input_handler_list中找到id和事件种类相匹配的handler并与之建立连接的过程。


input_dev产生的事件会分发给所有建立连接的handler。下面继续分析事件的传递。


原创粉丝点击