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

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起到了最大的作用。关于两者是如何匹配的,也是本文关注的地方。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值