最近要写一段button driver,照着内核的button.c抄了一遍,发现不能工作(这几乎是一定的)。不得已查了一下acpid的源码,把input device driver
与UI中的acpid deamon交互方式搞的一知半解了。但我又不想花时间在这上面,由于对driver不太熟(其实我对所有模块都不熟),
比较危险的事情就是深度优先学习一个知识,这样学习容易迷失方向。
其实我们大可以把不了解的模块先看作一个黑盒,了解他是怎么用的,
大体流程怎样就可以,不伤大雅,后期有需要再dig into the code。
为了不浪费看官的时间,以下内容可以不用再看了,因为重点都在上面,由于懒惰本人写的语焉不详,仅供个人参考。
1. x86下,有人按了power button键,这个时候很可能是一个fix event中断,或者是一个sci中断,
总之在我的机器上,这个中断最后是由Embedded Controller
驱动的Query method回调来处理,这个method会通过ACPI的notify机制
(你别管这个是什么机制了,就理解成调用button driver的notify回调就好了),
往button driver传0xc6号软件event,表示发生了power button的press事件,
嗯, power button的notify回调里,会根据这个编号做两件事,这两件事要达到的目的,
就是给用户态程序(主要就是UI守护进程,也就是我们的例子acpid),发送power button
按键这个消息,然后守护进程再根据这个消息,做预定义的一些操作;
2.目前已知的比较常用的内核通知用户的方式有哪些? netlink是一种比较经典的对不对?
另外,作为power button,本身也是一个input device,所以input device交互机制
也用上了。为什么button driver要用两种方式来传递呢? 因为有些发行版的守护进程,
只用netlink来接受内核消息,另一些发行版,则跟踪input device的消息,所以
为了安全起见,两种方式都通知。
3.先看input device的通知,其实就一句话:
input_report_key(input, keycode, 0);
input_sync(input);
什么,你说是两句话?不,你一定是眼花了,这两句其实可以看作一句谢谢。
中间的传递就不说了,总之,最后的效果时,用户态程序可以通过read(/dev/input/eventx,buf),
来读取buf->event.KEY和buf->event.CODE以及buf->event.VALUE,
这三个值分别是EV_KEY,KEY_POWER,以及1, 这表示,
一个类型为EV_KEY的按键设备被按下了(value=1),这个设备的名字叫KEY_POWER。
3.1 你一定有疑问了,这个eventx的x怎么得到?(神马?你没有疑问?看文章要随时质疑才能进步),
这是通过:
cat /proc/bus/input/devices的H: Handlers=sysrq kdb event12
得到,也就是12
3.2 用户态进程(acpid)得到这个消息后,会执行相应的操作(后面等netlink说完一起总结)
4. 再来看netlink往用户态传递消息的代码:
acpi_bus_generate_netlink_event(
device->pnp.device_class,
dev_name(&device->dev),
event, ++button->pushed);
也就是通过套接字往用户态传递一个数据结构(device_class, bus_id, type, value)
5 最后来看 作为守护进程的acpid, 会怎么处理这些消息:
首先,acpid的工作原理是,检查/etc/acpi/events目录下的rule文件,
找到匹配的事件后,就执行该事件对应的action(我靠,我怎么老是中英文夹杂)
比如这里有一个文件叫powerbtn:
event=button[ /]power
action=/etc/acpi/powerbtn.sh
意思就是说,凡是正则匹配button[ /]power的消息,都给我去执行/etc/acpi/powerbtn.sh,
后者里面很可能就是一句poweroff。 button[ /]power的意思是,要么匹配“button power ”,
要么匹配“button/power”字符串。之前我们在步骤3说到,acpid会收到一个结构
(EV_KEY,KEY_POWER,1)于是acpid就根据这个结构体返回一个字符串叫
"button/power PBTN 0000080 000000",然后用这个字符串去正则查找是否有
button[ /]power,于是就找到了,从而fork , execv(etc/acpi/powerbtn.sh);
对于netlink也是一样的原理,acpid收到(device_class, bus_id, type, value)后,
会把这个结构体做成一个字符串叫"button/power button 0x80 1",也用button[ /]power去正则匹配,
也找到了,从而执行fork , execv(etc/acpi/powerbtn.sh),我们可以看到,button driver如果
要传递netlink消息给用户态,必须给dev_class(dev)设置成跟用户态约定好的字符,
比如这里的"button/power",这样用户态才能匹配上。
好,我说完了。能看到这里的读者都是好同志。