Linux输入子系统分析(二)

                                              基于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。下面继续分析事件的传递。


  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
adc_keys_probe函数是Linux内核中与ADC按键相关的设备树探测函数。在设备树中,如果有ADC按键的相关信息(如所使用的ADC控制器、引脚等),Linux内核会自动调用该函数进行探测。 其主要功能包括: 1. 读取设备树中ADC按键节点的相关信息,如所用ADC控制器和引脚号; 2. 根据以上信息初始化ADC控制器,并将其与对应的GPIO引脚进行绑定; 3. 注册Linux输入子系统的按键输入设备,并将其与初始化好的ADC控制器进行关联。 下面是该函数的代码实现: static int adc_keys_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct adc_keys_platform_data *pdata = dev_get_platdata(dev); const char *adc_name = pdata ? pdata->adc_name : NULL; struct input_dev *input_dev; struct adc_keys *keys; int ret, i; if (!adc_name) { dev_err(dev, "no ADC controller specified in platform data\n"); return -EINVAL; } input_dev = devm_input_allocate_device(dev); if (!input_dev) return -ENOMEM; keys = devm_kzalloc(dev, sizeof(*keys), GFP_KERNEL); if (!keys) return -ENOMEM; platform_set_drvdata(pdev, keys); keys->input = input_dev; keys->adc = devm_iio_channel_get(&pdev->dev, "iio"); if (IS_ERR(keys->adc)) { dev_err(dev, "failed to get ADC channel\n"); ret = PTR_ERR(keys->adc); goto err_free_mem; } input_dev->name = pdev->name; input_dev->phys = "keys/input0"; input_dev->id.bustype = BUS_HOST; input_dev->id.vendor = 0x0001; input_dev->id.product = 0x0001; input_dev->id.version = 0x0100; input_set_capability(input_dev, EV_KEY, KEY_POWER); input_set_capability(input_dev, EV_KEY, KEY_VOLUMEUP); input_set_capability(input_dev, EV_KEY, KEY_VOLUMEDOWN); keys->min_val = pdata ? pdata->min_val : ADC_KEYS_DEFAULT_MAX; keys->max_val = pdata ? pdata->max_val : ADC_KEYS_DEFAULT_MIN; ret = input_register_device(input_dev); if (ret) { dev_err(dev, "failed to register input device\n"); goto err_free_mem; } ret = adc_keys_init_dev(keys, adc_name); if (ret) { dev_err(dev, "failed to init ADC controller\n"); goto err_free_dev; } for (i = 0; i < ARRAY_SIZE(keys->keymap); i++) { ret = input_register_keycode(input_dev, keys->keymap[i].type, keys->keymap[i].code, NULL); if (ret) { dev_err(dev, "failed to register input keycode\n"); goto err_free_dev; } } ret = adc_keys_set_timer_interval(keys); if (ret) dev_warn(dev, "Failed to initialize the polling timer\n"); dev_info(dev, "registered ADC keys input device\n"); return 0; err_free_dev: input_unregister_device(input_dev); err_free_mem: return ret; } 该函数通过dev_get_platdata函数读取设备树节点的平台数据信息,获取相关参数。接下来,分别进行输入子系统的相关初始化、iio_channel获取、ADC控制器的初始化、按键注册及关联操作,最终成功时输出相关信息并返回0,失败则进行相应的错误处理操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值