您的位置:首页 > 运维架构 > Linux

s3c2440触摸屏驱动分析(LINUX2.6)(2)

2010-02-02 11:28 405 查看
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)

dev->getkeycode = input_default_getkeycode;

if (!dev->setkeycode)
dev->setkeycode = input_default_setkeycode;//这两个操作函数就可以用来取键的扫描码和设置键的扫描码。

snprintf(dev->dev.bus_id, sizeof(dev->dev.bus_id),
"input%ld", (unsigned long) atomic_inc_return(&input_no) - 1);

error = device_add(&dev->dev);//然后调用device_add()将input_dev中封装的device注册到sysfs
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);//将input device 挂到input_dev_list链表上

list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler);//对每一个挂在input_handler_list的handler调用input_attach_handler().

input_wakeup_procfs_readers();

mutex_unlock(&input_mutex);

return 0;
}

我们来看下匹配的函数调用:

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中去。

MATCH_BIT宏的定义如下:
#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;
由上的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++(也就是说进行数组的下一个单元的对比)。

简而言之,注册input device的过程就是为input device设置默认值,并将其挂以input_dev_list.与挂载在input_handler_list中的handler相匹配。如果匹配成功,就会调用handler的connect函数.
input_match_device这个匹配成功以后就执行下面这条语句:
error = handler->connect(handler, dev, id);
很明显我们又要回到tsdev.c文件里面寻找connect,在上面贴出的tsdev_handler结构体当中,你会发现一个成员:tsdev_connect。我们来看看它的具体代码吧:
static int tsdev_connect(struct input_handler *handler, struct input_dev *dev,
const struct input_device_id *id)
{
struct tsdev *tsdev;
int delta;
int minor;
int error;
for (minor = 0; minor < TSDEV_MINORS / 2; minor++)
if (!tsdev_table[minor])
break;
很明显在寻找tsdev_table数组中空的单元,TSDEV_MINORS 为32,则代表tsdev_table数组支持16个设备,tsdev_table是tsdev类型的数组
if (minor == TSDEV_MINORS) {
printk(KERN_ERR "tsdev: no more free tsdev devices/n");
return -ENFILE;
}
tsdev = kzalloc(sizeof(struct tsdev), GFP_KERNEL);//找到之后就向内核申请一个这样结构体的内存空间。
if (!tsdev)
return -ENOMEM;
INIT_LIST_HEAD(&tsdev->client_list);
spin_lock_init(&tsdev->client_lock);
mutex_init(&tsdev->mutex);
init_waitqueue_head(&tsdev->wait);
snprintf(tsdev->name, sizeof(tsdev->name), "ts%d", minor);
tsdev->exist = 1;
tsdev->minor = minor;
tsdev->handle.dev = dev;//使dev封装到handle结构体当中
tsdev->handle.name = tsdev->name;
tsdev->handle.handler = handler;//使handler封装到handle结构体当中
tsdev->handle.private = tsdev;
由此可以看出handler和dev两者联系到一起了。我们可以把handle看成是handler和input device的信息集合体。.在这个结构里集合了匹配成功的handler和input device。
delta = dev->absmax [ABS_X] - dev->absmin [ABS_X] + 1;
if (delta == 0)
delta = 1;
tsdev->cal.xscale = (xres << 8) / delta;
tsdev->cal.xtrans = - ((dev->absmin [ABS_X] * tsdev->cal.xscale) >> 8);
delta = dev->absmax [ABS_Y] - dev->absmin [ABS_Y] + 1;
if (delta == 0)
delta = 1;
tsdev->cal.yscale = (yres << 8) / delta;
tsdev->cal.ytrans = - ((dev->absmin [ABS_Y] * tsdev->cal.yscale) >> 8);
这几行的功能应该是校准用的。
strlcpy(tsdev->dev.bus_id, tsdev->name, sizeof(tsdev->dev.bus_id));
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);

在这段代码里主要完成tsdev封装的device的初始化.注意在这里,使它所属的类指向input_class.这样在/sysfs中创建的设备目录就会在/sys/class/input/下面显示.
error = input_register_handle(&tsdev->handle);//这个注册很重要,主要是为了完成对handle的封装。
我们来仔细来分析这段代码,代码如下:
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;
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链表上
if (handler->start)
handler->start(handle);
return 0;
}
其实代码很简单,就是为了把handle,handler还有input_dev关联起来。
我们再次回到tsdev_connect当中,代码如下:
if (error)
goto err_free_tsdev;
error = tsdev_install_chrdev(tsdev);
我们来看tsdev_install_chrdev具体的代码,代码如下:
static int tsdev_install_chrdev(struct tsdev *tsdev)
{
tsdev_table[tsdev->minor] = tsdev;
return 0;
}
作用就是把tsdev加入到tsdev_table数组中去。我们再回去原来的代码中去,代码如下:
if (error)
goto err_unregister_handle;
error = device_add(&tsdev->dev);
if (error)
goto err_cleanup_tsdev;
return 0;
err_cleanup_tsdev:
tsdev_cleanup(tsdev);
err_unregister_handle:
input_unregister_handle(&tsdev->handle);
err_free_tsdev:
put_device(&tsdev->dev);
return error;
}
剩下就是一些简单的结尾工作了。好了,现在基本把input_register_device分析了一通了。其实作用很简单,就是为了找到与之匹配的input_handler(本文就是tsdev_handler)然后再使用connect使input_dev和input_handler关联起来,其中handle起到了最大的作用。关于两者是如何匹配的,也是本文关注的地方。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: