int __init EmbedSky_ts_init(void)
{
return driver_register(&EmbedSky_ts_driver);
}
static struct device_driver EmbedSky_ts_driver = {
.name = "EmbedSky-ts",
.bus = &platform_bus_type,
.probe = EmbedSky_ts_probe,
.remove = EmbedSky_ts_remove,
};
我们来主要分析一下EmbedSky_ts_probe,它是最为贴近我们INPUT子系统的。我们先来分析与INPUT子系统无关的部分,与具体硬件相关的部分。
struct s3c2410_ts_mach_info *info;
info = ( struct s3c2410_ts_mach_info *)dev->platform_data;
注:
s3c2410_ts_mach_info这个结构需要我们去填充,里面存放的是触摸屏需要的一些配置参数。这个数据结构的位置在/arch/arm/mach-s3c2410/dev.c。我们来看看他的具体内容吧。
static struct s3c2410_ts_mach_info sbc2410_ts_platdata = {
.delay = 10000,
.presc = 49,
.oversampling_shift = 2,
};
struct platform_device s3c_device_ts = {
.name = "s3c2410-ts",
.id = -1,
.dev = {
.platform_data = &sbc2410_ts_platdata,
}
};
EXPORT_SYMBOL(s3c_device_ts);
接下来我们继续回到EmbedSky_ts_probe中去。
adc_clock = clk_get(NULL, "adc");
if (!adc_clock) {
printk(KERN_ERR "failed to get adc clock source/n");
return -ENOENT;
}
clk_enable(adc_clock);
这是对系统时钟的获得。
base_addr=ioremap(S3C2410_PA_ADC,0x20);
if (base_addr == NULL) {
printk(KERN_ERR "Failed to remap register block/n");
return -ENOMEM;
}
映射触摸屏的控制寄存器。
EmbedSky_ts_connect();
配置相应的GPIO的功能。
if ((info->delay&0xffff) > 0)
writel(info->delay & 0xffff, base_addr+S3C2410_ADCDLY);
writel(WAIT4INT(0), base_addr+S3C2410_ADCTSC);
预分频的功能配置。
ADCDLY寄存器的用法:在正常转换模式,独立X/Y位置转换模式和自动X/Y位置转换模式下,X/Y位置转换延迟值。
当在等待中断模式中有触笔按下时,这个寄存器在间歇的几个毫秒时间内,为自动X/Y位置转换产生中断信号(INT_TC)好处在于在等待中断的时候还可以进行AD转换。
以下是关于INPUT子系统的分析:
memset(&ts, 0, sizeof(struct EmbedSky_ts));//全局变量static struct EmbedSky_ts ts;
ts.dev = input_allocate_device();
input设备注册分析:
struct input_dev *input_allocate_device(void)
{
struct input_dev *dev;
dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL);//向内核申请存放input_dev结构体的内存空间
if (dev) {
dev->dev.type = &input_dev_type;
dev->dev.class = &input_class;
device_initialize(&dev->dev);//对于设备的初始化现在本人还没有水平分析
mutex_init(&dev->mutex);
spin_lock_init(&dev->event_lock);
INIT_LIST_HEAD(&dev->h_list);//初始化队列,这个很重要
INIT_LIST_HEAD(&dev->node);
__module_get(THIS_MODULE);
}
return dev;
}
总的来说,就是向内核申请一个input_dev结构体,然后初始化。
ts.dev->evbit[0] = BIT(EV_SYN) | BIT(EV_KEY) | BIT(EV_ABS);
对设备所支持的事件进行置位,它支持同步(EN_SYN)、按键(EN_KEY)、绝对坐标(EV_ABS)事件。
ts.dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT(BTN_TOUCH);
对于触摸屏来说就只有一个按键,则只需说明支持哪个键值即可。
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);
以上三行是对ads事件的参数进行参数设置。功能和上述键值是类似的设置。
static inline void input_set_abs_params(struct input_dev *dev, int axis, int min, int max, int fuzz, int flat)
{
dev->absmin[axis] = min;//以上三行都对最大值和最小值进行设置。ABS_X,ABS_Y设置最大值为0x3ff,最小值为0。ABS_PRESSURE设置为最大值1,最小值为0。
dev->absmax[axis] = max;
dev->absfuzz[axis] = fuzz;
dev->absflat[axis] = flat;
dev->absbit[BIT_WORD(axis)] |= BIT_MASK(axis);//说明支持的三个事件ABS_X,ABS_Y,ABS_PRESSURE。
}
sprintf(ts.phys, "ts0");
ts.dev->private = &ts;
ts.dev->name = EmbedSky_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 = S3C2410TSVERSION;
ts.shift = info->oversampling_shift;
/*
这个比较重要,配置输入数据的缓存区大小,
在这里oversampling_shift设为2,也就是缓存区的大小为4(1<<2)
*/
if (request_irq(IRQ_ADC, stylus_action, SA_SAMPLE_RANDOM, "s3c2410_action", ts.dev))
{
printk(KERN_ERR "EmbedSky_ts.c: Could not allocate ts IRQ_ADC !/n");
iounmap(base_addr);
return -EIO;
}
if (request_irq(IRQ_TC, stylus_updown, SA_SAMPLE_RANDOM,
"s3c2410_action", ts.dev)) {
printk(KERN_ERR "EmbedSky_ts.c: Could not allocate ts IRQ_TC !/n");
iounmap(base_addr);
return -EIO;
}
都是申请中断号,具体的在前面那篇已经分析过了。大家可以回去看看。
input_register_device(ts.dev);
这个很重要,我们来分析其中的代码,代码如下:
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;
__set_bit(EV_SYN, dev->evbit);//所有设备都支持这样的事件。
/*
* 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;
}//.如果dev->rep[REP_DELAY]和dev->rep[REP_PERIOD]没有设值,则将其赋默认值。这主要是处理重复按键的.
if (!dev->getkeycode)
我们来看下匹配的函数调用:
static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
const struct input_device_id *id;
int error;
if (handler->blacklist && input_match_device(handler->blacklist, dev))
return -ENODEV;
id = input_match_device(handler->id_table, dev);//如果两次input_match_device都成功了,就执行下面重要的connect
if (!id)
return -ENODEV;
error = handler->connect(handler, dev, id);
if (error && error != -ENODEV)
printk(KERN_ERR
"input: 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(const struct input_device_id *id,
struct input_dev *dev)
{
int i;
for (; id->flags || id->driver_info; id++) {
if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)
if (id->bustype != dev->id.bustype)//可以看出dev的id成员的赋值都是在先前的EmbedSky_ts_probe函数里面完成的。
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);
return id;
}
return NULL;
}
现在问题出现了,哪里定义了const struct input_device_id *id这个变量,找到它才可以知道它的flag是怎么设置的。在先前的input_register_device函数中,大家还记不记得list_for_each_entry(handler, &input_handler_list, node)这条语句,可以说在input文件夹下的apm-power.c、evbug.c、evdev.c、ff-core.c、ff-memless.c......和文件tsdev.c,这些文件都是对应不同input_dev结构体的逻辑层,换句话说,在这些文件都会申请和初始化input_handler结构体。对应的就会有input_handler_list这样的队列生成。我们来看下以下代码(以tsdev.c为例)
static int __init tsdev_init(void)
{
return input_register_handler(&tsdev_handler);//这个很重要,进过这里处理后就会有一个input_handler结构体产生了。
}
我们来仔细看看它的代码吧:
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;
}//handler->minor表示对应input设备节点的次设备号,把handler的次设备号左移5位,可以看出一个handler可以支持32个设备,这个是个理论值,在下面你会看到特殊情tsdev_handler只是支持16个设备。
list_add_tail(&handler->node, &input_handler_list);//然后将handler挂到input_handler_list中
list_for_each_entry(dev, &input_dev_list, node)
input_attach_handler(dev, handler);//然后将其与挂在input_dev_list中的input device匹配
input_wakeup_procfs_readers();
out:
mutex_unlock(&input_mutex);
return retval;
}
由上可以看出,这里把tsdev_handler挂在input_handler_list队列中,以便后面的匹配(如下会有详解的)。
这样与我们本文讨论相关的文件就是tsdev.c,我们进去看看可不可以找到我们先前需要的id。
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,
};
注意到这里的minor成员,TSDEV_MINOR_BASE=128,可以知道tsdev_handler代表的设备范围是从(13,128)到(13,128+16)这里为什么是16,看到下面你就知道了。
可以看到最后一项就是我们需要的,我们再往里看看:
static const struct input_device_id tsdev_ids[] = {
{
.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT | INPUT_DEVICE_ID_MATCH_RELBIT,
.evbit = { BIT(EV_KEY) | BIT(EV_REL) },
.keybit = { [BTN_LEFT/32] = BIT(BTN_LEFT) },
.relbit = { BIT(REL_X) | BIT(REL_Y) },
}, /* A mouse like device, at least one button, two relative axes */
{
.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT | INPUT_DEVICE_ID_MATCH_ABSBIT,
.evbit = { BIT(EV_KEY) | BIT(EV_ABS) },
.keybit = { [BTN_TOUCH/32] = BIT(BTN_TOUCH) },
.absbit = { BIT(ABS_X) | BIT(ABS_Y) },
}, /* A tablet like device, at least touch detection, two absolute axes */
{
.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_ABSBIT,
.evbit = { BIT(EV_ABS) },
.absbit = { BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE) },
}, /* A tablet like device with several gradations of pressure */
{} /* Terminating entry */
};
这么一大串其实就是一个数组,可以看到我们先前要找的flag了吧。现在我们回去先前的代码input_match_device中去。
由上的flag可以看出,匹配直接运行到MATCH_BIT的宏里面了。可以看出,只有当iput device和input handler的id成员在evbit, keybit,… swbit项相同才会匹配成功。如果半途匹配不成功就直接 if ((id->bit[i] & dev->bit[i]) != id->bit[i])break;只要不是完全相同的话,就会执行if (i != BITS_TO_LONGS(max)) continue;这样就不进行下面的对比,直接是id++(也就是说进行数组的下一个单元的对比)。
const struct input_device_id *id)
{
struct tsdev *tsdev;
int delta;
int minor;
int error;
if (!tsdev_table[minor])
break;
printk(KERN_ERR "tsdev: no more free tsdev devices/n");
return -ENFILE;
}
return -ENOMEM;
spin_lock_init(&tsdev->client_lock);
mutex_init(&tsdev->mutex);
init_waitqueue_head(&tsdev->wait);
tsdev->exist = 1;
tsdev->minor = minor;
tsdev->handle.name = tsdev->name;
tsdev->handle.handler = handler;//使handler封装到handle结构体当中
tsdev->handle.private = tsdev;
if (delta == 0)
delta = 1;
tsdev->cal.xscale = (xres << 8) / delta;
tsdev->cal.xtrans = - ((dev->absmin [ABS_X] * tsdev->cal.xscale) >> 8);
if (delta == 0)
delta = 1;
tsdev->cal.yscale = (yres << 8) / delta;
tsdev->cal.ytrans = - ((dev->absmin [ABS_Y] * tsdev->cal.yscale) >> 8);
tsdev->dev.devt = MKDEV(INPUT_MAJOR, TSDEV_MINOR_BASE + minor);//INPUT_MAJOR是主设备号,TSDEV_MINOR_BASE+minor是次设备号
tsdev->dev.class = &input_class;
tsdev->dev.parent = &dev->dev;
tsdev->dev.release = tsdev_free;
device_initialize(&tsdev->dev);
{
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;
list_add_tail_rcu(&handle->d_node, &dev->h_list);//将handle挂到所对应input device的h_list链表上
mutex_unlock(&dev->mutex);
synchronize_rcu();
* 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(&handle->h_node, &handler->h_list);//还将handle挂到对应的handler的hlist链表上
handler->start(handle);
}
if (error)
goto err_free_tsdev;
{
tsdev_table[tsdev->minor] = tsdev;
return 0;
}
if (error)
goto err_unregister_handle;
if (error)
goto err_cleanup_tsdev;
tsdev_cleanup(tsdev);
err_unregister_handle:
input_unregister_handle(&tsdev->handle);
err_free_tsdev:
put_device(&tsdev->dev);
return error;
}