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

闲聊linux中的input设备(10) 原来你也在这里

2012-10-21 21:49 375 查看
废话不多讲,开门见山,我们继续上节那个没有完的故事,我们收集到的那些值到底会传到哪里去呢?深入input_pass_event(dev, type, code, value)内部,看她花落谁家?

static void input_pass_event(struct input_dev *dev,

unsigned int type, unsigned int code, int value)

{

1 struct input_handle *handle;



2 rcu_read_lock();



3 handle = rcu_dereference(dev->grab);

4 if (handle)

5 handle->handler->event(handle, type, code, value);

6 else

7 list_for_each_entry_rcu(handle, &dev->h_list, d_node)

8 if (handle->open)

9 handle->handler->event(handle,

10 type, code, value);

11 rcu_read_unlock();

}

1行,定义一个input_handle结构体变量,不会这么快就忘了input_handle这位兄弟吧?

2、11两行是一种 RCU形式的锁机制。它允许多个执行单元对它同时对它进行读,但是多个执行单元对它进行写操作时,并不会让你随随便便就碰她,它会要求你先复制一个副本,要改要删随你便,反正你改的不是她本身。当所有的写单元都一一改完后,再统一写回,是不是比一般的自旋锁高级多了。不过不要轻易用它,系统开销较大。

3-5行,看看dev->grab有没有设置,回顾前面dev的初始化。显然没动它。所以这几行代码就这样废了。

7-10行,遍历dev->h_list链表上的handle,看看有没有handle->open不为0的,这里在我们在用户空间通过open函数打开某个evdev的时候,相应的handle->open会++,这里显然我们要打开evdev设备来对input设备进行操作,才会发生前面那些那儿,要不然我说了这么多不是等于一个0.?

第9行代码,我们通过这个handle户口信息表找到孩子的爸爸然后调用孩子爸爸里面的函数evdev_event。

我没有食言!记性好的哥们还记不记得,我们在第8节中了解evdev_handler的时候,有一个函数没讲,正是此函数。我们再来看一下evdev_handler叔的全貌:

static struct input_handler evdev_handler = {

.event = evdev_event,

.connect = evdev_connect,

.disconnect = evdev_disconnect,

.fops = &evdev_fops,

.minor = EVDEV_MINOR_BASE,

.name = "evdev",

.id_table = evdev_ids,

};



现在是时候来分析这个evdev_event,的时候了,为了成功泡上他的美丽女儿,我们还是有必要来攻下这最后一座城池:

static void evdev_event(struct input_handle *handle,

unsigned int type, unsigned int code, int value)

{

1 struct evdev *evdev = handle->private;

2 struct evdev_client *client;

3 struct input_event event;

4 struct timespec ts;



5 ktime_get_ts(&ts);

6 event.time.tv_sec = ts.tv_sec;

7 event.time.tv_usec = ts.tv_nsec / NSEC_PER_USEC;

8 event.type = type;

9 event.code = code;

10 event.value = value;



11 rcu_read_lock();



12 client = rcu_dereference(evdev->grab);

13 if (client)

14 evdev_pass_event(client, &event);

15 else

16 list_for_each_entry_rcu(client, &evdev->client_list, node)

17 evdev_pass_event(client, &event);



18 rcu_read_unlock();



19 wake_up_interruptible(&evdev->wait);

}

1行,大家还记不记得,那个时候,evdev handler兄和我们的akm
input设备美眉喜得贵女后,带着孩子去政府部门注册户口信息时,就是把他们的女儿交给了handle->private。这里把她再提取出来,赋给我们的struct
evdev *evdev,没有疑问吧!

2行,又来一个新的数据结构struct evdev_client *client;,先看看她的内部结构:



struct evdev_client {

struct input_event buffer[EVDEV_BUFFER_SIZE];

int head;

int tail;

spinlock_t buffer_lock; /* protects access to buffer, head and tail */

struct fasync_struct *fasync;

struct evdev *evdev;

struct list_head node;

struct wake_lock wake_lock;

};

眼神犀利的哥们马上会看到很关键的一行代码:struct evdev *evdev;没有错,她内嵌了一个evdev结构,怎么理解呢?这就好比我们把mtk、高通、或者Marvel厂商那儿提供给我们的整个手机设计方案搬过来,改改代码,添加点应用程序,加上一个漂亮的外壳,再来一个时尚的品牌商标,一款时尚的手机就出炉了,还卖的很好哦。对,这里的struct
evdev_client就是我们那个evdev的pvt版本,她是和我们的evdev紧紧地绑定在一起的,在哪儿绑定的呢?作为一个负责任的男人,我得告诉你,还是发生在应用层调用open时发生的。关于这个open整个过程我们下一节会详细讲解。不过她确实多了几个很重要的东东:struct
input_event buffer[EVDEV_BUFFER_SIZE];定义一个内核区buffer。它用来干嘛?input_event结构是什么样?请看接下来的第三行代码。

3行,世界上谁跑的最快?“当然是input_event了”凤姐很得意的回答道。凤姐智商果然超群。说input_event,input_event到。我们来看看这个家伙长什么样:

struct input_event {

struct timeval time;

__u16 type;

__u16 code;

__s32 value;

};

她的作用很明显,就是用来存储我们要传递的那些值type、code、value。不信,不信请往下看。

4-7行,跟时间片有关的一些东东,和我们的这个akm 设备无关。不过handler兄老婆多,总有一个用得上的。

8-10行,把通过参数传进来的ype、code、value放到我们的event中。是不是验证了前面的说法。

11、18两行,又是加锁操作。前面有聊过,飘过。

12-14行,看看这个client有没有被强制绑定,这里我们没绑定,故略过。

16-17行,遍历client链表,找到与我们对应的那个client,然后把刚刚那个event放到里面去。放到哪里面去呢?放到struct
input_event buffer[EVDEV_BUFFER_SIZE]里面去,注意最多可放EVDEV_BUFFER_SIZE个event。

进入evdev_pass_event(client, &event);跟踪:



static void evdev_pass_event(struct evdev_client *client,

struct input_event *event)

{

/*

* Interrupts are disabled, just acquire the lock

*/

spin_lock(&client->buffer_lock);

wake_lock_timeout(&client->wake_lock, 5 * HZ);

client->buffer[client->head++] = *event;

client->head &= EVDEV_BUFFER_SIZE - 1;

spin_unlock(&client->buffer_lock);



kill_fasync(&client->fasync, SIGIO, POLL_IN);

}

等下我们的用户程序就可以通过一定的方式(read()系统调用)来从这个buffer中拷贝数据了。这样一次从最下层驱动通过input子系统传到内核区buffer,然后再到用户空间的数据传输就这样完成了。

好了,貌似故事就这样结束了,数据终于传完了。整个handler叔全弄明白了,既然这样,那还等什么,赶紧操着家伙evdev_fops,去把玩他的美丽女儿吧。不过兄弟先别急,再去之前,有两个小小的注意事项需要交代一下,以防不测:

1, 上面的evdev_event()函数的最后一行代码wake_up_interruptible(&evdev->wait);是用来唤醒一个等待队列,他的另一半是wait_event_interruptible(&evdev->wait),她一般位于另外一个地方。比如:

int fun1()

{

……

……

wake_up_interruptible(&evdev->wait);





}

int fun2()

{

……

……

wait_event_interruptible(&evdev->wait);

……

……

}

函数fun2执行到wait_event_interruptible(&evdev->wait)这一句时,她就不走了,干嘛?睡个觉先,等待她的另一半的到来,然后把她唤醒。这就需要我们的fun1函数执行到wake_up_interruptible(&evdev->wait);明白了吧!有哥们要发话了,这个机制有什么作用啊?

好吧,举个例子:两个函数对一buffer进行操作,一个读她,一个写她。要读的那个哥们是不是要先等那个要写的哥们把数据写到里面去了再读呢?否则,你去读空气啊?

好了,这里在我们这个evdev_event()函数来这么一个wake_up_interruptible(&evdev->wait);干嘛,表示你的数据已经好了,那位想读的哥们赶紧过来读吧,再不读就被别人读走了哦!



2我们通过input_report_abs(data->input_dev, ABS_RZ, rbuf[2]);最开始传输数据的,可是rbuf[2])哪来的呢?具体过程是这样的:akm8973传感器是通过I2C总线挂到系统中的,它不断地从外界收集数据(比如各坐标信息)然后把他们送到自己的寄存器中,然后我们就通过I2C总线从它的寄存器中获取数据,最终放到我们上面的rbuf[2]中。接下来就发生了上面的那一切,致使这些数据最终被传到我们的用户空间被我的应用程序处理和显示,哟!从手机的GPS导航可以发现,兄弟你现在位于广东省东莞市长安镇乌沙区大润发旁不远处的一个神秘地方,哟!显示了,是一个叫海豚湾的地方!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: