原文链接:http://blog.chinaunix.net/uid-20543183-id-1930815.html
前言
serio总线同之前分析的platform总线一样,也是一种虚拟总线。它是Serial I/O的缩写,表示串行的输入输出设备,很多输入输出设备都是以此为基础的。同前面几篇笔记一样,下面的代码分析是基于linux kernel 2.6.25版本。
serio总线的初始化
serio总线的初始化是在linux2.6.25/drivers/input/serio/serio.c
中完成的。代码如下:
在这里,创建了对应bus_type
为serio_bus
的总线,还创建了一个名为“kseriod
”的内核线程。我们暂且不管它是做什么的,等以后的代码涉及到再来进行分析。
serio设备注册
serio设备对应的数据结构为struct serio
, 对应的注册接口为:serio_register_port()
。代码如下:
它是__serio_register_port()
的一个封装函数。代码如下:
它先初始化一个serio设备,在serio_init_port()
中,它指定了设备的总线类型为serio_bus
。之后,调用serio_queue_event()
。看这个函数的名称好像是产生了什么事件,来看一下它的代码:
这个函数比较简单,就是根据参数信息生成了一个struct serio_event
结构,再将此结构链接至serio_event_list
末尾。
接着就要来寻找serio_event_list
这个链表的处理。这个就是serio_thread
的工作了,还记得初始化的时候所创建的线程么,不错,就是它。Kernel可能认为对serio的操作比较频繁,所以对一些操作事件化,将它以多线程处理。serio_thread()
代码如下:
这个函数的核心处理是serio_handle_event()
, 代码如下:
这个函数的流程大致是这样的:
调用serio_get_event()
从链表中取出struct serio_event
元素,然后对这个元素的事件类型做不同的处理,处理完了之后,调用serio_remove_duplicate_events()
在链表中删除相同请求的event
.
对应之前的serio_register_port()
函数,它产生的事件类型是SERIO_REGISTER_PORT
。也就是说,对于注册serio设备来说,流程会转入serio_add_port()
。
代码如下:
我们终于看到serio device注册的庐山真面目了,它会调用设备的start()
函数,然后调用device_add()
将设备注册到总线上。
同platform总线一样,这里的serio device注册的时候也会产生一个hotplug事件,对应就会调用总线的uenvent
函数,serio_bus
的event
接口为:
可见, 会在hotplug的环境变量中添加几项值。记得我们之前分析设备驱动模型的时候,在总线下面的设备都有一个属性文件,这个文件的内容就是对应bus
和kset
所添加的环境变量。在/sys
文件系统中就可以看到这此环境变量。做个测试:
serio driver的注册
对应serio driver注册的接口serio_register_driver()
。代码如下:
它是__serio_register_driver()
的封装函数,代码如下:
上面这段代码比较简单,在注册驱动的时候,将驱动的总线指定为serio_bus
,然后调用driver_register()
将驱动注册到总线。如果drv->manual_bind
不为1
,还会产生一个SERIO_ATTACH_DRIVER
事件。drv->manual_bind
成员的含义应该是要手动进行驱动与设备的绑定。
在注册驱动的时候,会产生一次驱动与设备的匹配过程,这过程会调用bus->match --> bus->probe
,看下serio总线是怎么样处理的。serio_bus.match
的函数如下:
如果驱动或者设备指定了手动绑定,那么这次绑定是不成功的,否则调用serio_match_port()
进行更细致的判断。代码如下:
由此看出,只有serio device信息与serio driver的id_table
中的信息匹配的时候,才会将设备和驱动绑定起来。
serio_bus.probe
的接口函数如下示:
即会调用设备驱动的connect()
函数。
在注册serio driver的时候,还会产生SERIO_ATTACH_DRIVER
事件,这个事件的处理是在serio_handle_event()
中完成的。相应的处理接口为:
可以看出,这里也是执行一次设备与驱动的匹配过程。
serio 的中断处理函数分析
serio_interrupt()
在serio bus构造的驱动也是一个常用的接口,这个接口用来处理serio 设备的中断。我们来看下它的代码:
首先,先判断当前设备是否已经关联到了驱动程序。如果已经被关联了,那么调用驱动的中断处理函数。如果没有,就会转入serio_rescan()
中执行。该函数代码如下:
由此可见,它是执行了一个SERIO_RESCAN_PORT
动作。同样,这个动作是在serio_handle_event()
中处理的。相应的处理接口为:
首先调用serio_disconnect_port()
解除serio设备与驱动绑定,然后调用serio_find_driver()
重新执行一次设备与驱动的匹配过程。serio_find_driver()
代码如下:
如果有合适的驱动,就会将之与设备关联起来。
小结
来小结一下,在serio总线中,注册一个serio设备时,除了将其注册到所属的serio_bus
上,还会调用设备的start()
函数。在中断处理的时候,如果serio设备已经关联到驱动,则调用驱动的interrupt
函数。如果没有关联,则重新匹配驱动并将其注册到总线。
总的说来,serio 总线的代码很简单。到这里,我们已经分析过platform, serio两种虚拟总线,应该结合这两个虚拟总线的例子好好体会一下结构封装的技巧。