[cpp] view plain copy
- 版本信息:
- Linux:3.10
- Android: 4.4
一、唤醒源
设备休眠后,通过触发唤醒源使设备恢复正常工作模式。设备唤醒源有多种,对于Android设备常见的就有PowerKey、来电唤醒、Alarm唤醒等。
唤醒源的实现处于内核空间,本文重点讨论下PowerKey作为唤醒源的具体实现。
二、PowerKey唤醒源
PowerKey唤醒设备的原理,本质其实就是中断。

PowerKey连接到CPU的一个输入(Input)引脚(Pin)上,该Pin运行在中断模式上。一旦 PowerKey按下,引发Pin中断;而该中断具有唤醒CPU的功能,于是设备得以唤醒。
三、PowerKey对应的Pin Configuration
和PowerKey相连的Pin的具体配置位于板级dts文件中,比如 如下
配置:
[cpp] view plain copy
1. arch/arm/boot/dts/xxxxx<span style="font-family:Consolas;">.</span>dts
2. power-key {
3. /** 是CPU的哪个Pin */
4. gpios = <&gpio0 GPIO_A5 GPIO_ACTIVE_LOW>;
5. /** Key code */
6. linux,code = <116>;
7. /** 起个名字 */
8. "power";
9. /** 该Pin具有wakeup的功能 */
10. gpio-key,wakeup;
11. };
着重说下 linux,code = <116>,116怎么来的?
对于键盘,每一个按键都有唯一的编码,在Linux中,编码值位于:
[cpp] view plain copy
1. input.h (kernel\include\uapi\linux)
2. /*
3. * Keys and buttons
4. */
5. #define KEY_RESERVED 0
6. #define KEY_ESC 1
7. #define KEY_BACKSPACE 14
8. #define KEY_TAB 15
9. #define KEY_POWER 116 /* SC System Power Down */
可知,PowerKey的编码也在该文件中,且编码值为116;一旦按下
PowerKey,该值作为键值传到
input_event结构体的code成员变量中:
[cpp] view plain copy
1. input.h (kernel\include\uapi\linux)
2. /*
3. * The event structure itself
4. */
5.
6. struct input_event {
7. struct timeval time;
8. __u16 type;
9. __u16 code;
10. __s32 value;
11. }; 之后我们会写个Linux应用程序读取code值。
四、PowerKey驱动
1、PowerKey驱动注册
在我的板上, PowerKey驱动是按照
platform_device注册的,对象:
[cpp] view plain copy
1. static struct platform_driver keys_device_driver = {
2. .probe = keys_probe,
3. .remove = keys_remove,
4. .driver = {
5. "xxx-keypad",
6. .owner = THIS_MODULE,
7. .of_match_table = xxx_key_match,
8. #ifdef CONFIG_PM
9. .pm = &keys_pm_ops,
10. #endif
11. }
12. };
对象注册:
[cpp] view plain copy
- module_platform_driver(keys_device_driver);
这里遇到了“新伙伴”:之前驱动注册时调用的是“module_init/module_exit”宏,PowerKey驱动注册用“ module_platform_driver”,什么鬼?看下宏注释:
[cpp] view plain copy
1. /* module_platform_driver() - Helper macro for drivers that don't do
2. * anything special in module init/exit. This eliminates(清除/淘汰) a lot of
3. * boilerplate(样板文件). Each module may only use this macro once, and
4. * calling it replaces module_init() and module_exit()
5. */
6. #define module_platform_driver(__platform_driver) \
7. module_driver(__platform_driver, platform_driver_register, \
8. platform_driver_unregister) 我们并不需要在
“module_init/module_exit”宏规定的函数中做什么工作,使用这种方式(注册驱动的模版)注册驱动的话就得准备xxx_init/xxx_exit函数,而采用 “module_platform_driver”注册就免去了这些无用功。
2、PowerKey驱动实现
贯穿始终的连个结构体:
[cpp] view plain copy
1. /**
2. * 描述Key具有的属性
3. */
4. struct xxx_keys_button {
5. // key code
6. const char *desc;//key label
7. //key up & down state
8. int gpio;
9. int active_low;
10. int wakeup;
11. struct timer_list timer;
12. };
13.
14. /**
15. * 驱动属性封装
16. */
17. struct xxx_keys_drvdata {
18. int nbuttons;
19. bool in_suspend; /* Flag to indicate if we're suspending/resuming */
20. int result;
21. struct input_dev *input;
22. struct xxx_keys_button button[0];
23. };
(1)驱动从xxx_probe()函数起始,注意代码的注释:
[cpp] view plain copy
1. // 省略异常处理代码
2. static int keys_probe(struct platform_device *pdev)
3. {
4. struct device *dev = &pdev->dev;
5. struct device_node *np = pdev->dev.of_node;
6. struct xxx_keys_drvdata *ddata = NULL;
7. struct input_dev *input = NULL;
8. int i, error = 0;
9. int wakeup, key_num = 0;
10.
11. // 1、of_get_child_count: 获取pin configuration的数目
12. key_num = of_get_child_count(np);
13.
14. // 2、为xxx_keys_drvdata 分配空间
15. sizeof(struct xxx_keys_drvdata) +
16. sizeof(struct xxx_keys_button), GFP_KERNEL);
17.
18. // 3、PowerKey是作为Input设备进行注册的,这里为PowerKey分配Input设备空间
19. input = devm_input_allocate_device(dev);
20.
21. platform_set_drvdata(pdev, ddata);
22.
23. // input->name:设备名字,可以通过cat /sys/class/input/eventX/device/name查看
24. "xxx-keypad";
25. input->dev.parent = dev;
26.
27. // 总线类型
28. input->id.vendor = 0x0001;
29. input->id.product = 0x0001;
30. input->id.version = 0x0100;
31. ddata->input = input;
32.
33. ddata->nbuttons = key_num;
34. // 4、解析之前的dts文件
35. error = xxx_keys_parse_dt(ddata, pdev);
36.
37. struct xxx_keys_button *button = &ddata->button[i];
38. // 6、code = 116
39. if (button->code){
40. setup_timer(&button->timer,
41. long)button);}
42.
43. // 7、解析dts文件的时候赋值,此处非0
44. if (button->wakeup)
45. wakeup = 1;
46.
47. // 8、__set_bit(code, input->keybit); input->keybit: 存放PowerKey键值
48. input_set_capability(input, EV_KEY, button->code);
49.
50. struct xxx_keys_button *button = &ddata->button[i];
51. int irq;
52. // 9、->desc:解析dts文件的时候赋值,devm_gpio_request()申请GPIO
53. "keys");
54. // 10、PowerKey相连的Pin为输入模式
55. error = gpio_direction_input(button->gpio);
56. // 11、设置为中断Pin并获取中断号irq
57. irq = gpio_to_irq(button->gpio);
58.
59. /**keys_isr:中断Handler
60. * 中断触发方式:IRQF_TRIGGER_FALLING下降沿、IRQF_TRIGGER_RISING上升沿
61. */
62. error = devm_request_irq(dev, irq, keys_isr,
63. (button->active_low)?IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING,
64. "keys", button);
65. }
66.
67. // 存放KEY_WAKEUP键值
68. input_set_capability(input, EV_KEY, KEY_WAKEUP);
69. // 12、wakeup非0则启用唤醒CPU功能
70. device_init_wakeup(dev, wakeup);
71.
72. // 注册Input驱动
73. error = input_register_device(input);
74.
75. return error;
76.
77. fail2:
78. device_init_wakeup(dev, 0);
79. fail1:
80. while (--i >= 0) {
81. del_timer_sync(&ddata->button[i].timer);
82. }
83. fail0:
84. platform_set_drvdata(pdev, NULL);
85.
86. return error;
87. }这里完成:
- 数据成员空间分配
- 数据成员初始化
- dts文件中PowerKey配置解析
- Input设备驱动注册
- 启用唤醒功能
- 作为唤醒源的中断ISR注册
(2)解析dts文件中PowerKey配置
[cpp] view plain copy
1. // 解析dts文件中PowerKey配置
2. static int xxx_keys_parse_dt(struct xxx_keys_drvdata *pdata, struct platform_device *pdev)
3. {
4. struct device_node *node = pdev->dev.of_node;
5. struct device_node *child_node;
6. int ret, gpio, i =0;
7. u32 code, flags;;
8.
9. if(of_property_read_u32(child_node, "linux,code", &code)) {
10. "Missing linux,code property in the DT.\n");
11. ret = -EINVAL;
12. goto error_ret;
13. }
14. // 116
15. "label", NULL); // "power"
16.
17. gpio = of_get_gpio_flags(child_node, 0, &flags);
18. pdata->button[i].gpio = gpio;
19. pdata->button[i].active_low = flags & OF_GPIO_ACTIVE_LOW;
20. "gpio-key,wakeup", NULL);
21.
22. return 0;
23. error_ret:
24. return ret;
25. }
(3) 唤醒源注册
[cpp] view plain copy
1. wakeup.c (kernel\drivers\base\power)
2. /**@dev: Device to handle.
3. * @enable: Whether or not to enable @dev as a wakeup device.
4. */
5. int device_init_wakeup(struct device *dev, bool enable)
6. {
7. int ret = 0;
8. if (enable) {
9. // 1、dev->power.can_wakeup = true
10. true);
11. // 2、Enable given device to be a wakeup source.
12. ret = device_wakeup_enable(dev);
13. else {
14. false);
15. }
16.
17. return ret;
18. }
(4)唤醒动作
还记得之前注册的中断处理函数keys_isr?
[cpp] view plain copy
1. devm_request_irq(struct device *dev, unsigned int irq, irq_handler_t handler,
2. long irqflags, const char *devname, void *dev_id)
3. devm_request_irq(dev, irq, keys_isr,(button->active_low)?IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING,
4. "keys", button);
5.
6. static irqreturn_t keys_isr(int irq, void *dev_id)
7. {
8. // 1、获取在keys_probe()建立的xxx_keys_drvdata对象数据
9. struct xxx_keys_drvdata *pdata = xxx_key_get_drvdata();
10. // 2、dev_id即evm_request_irq()的最后一个参数,这里就是我们的PowerKey
11. struct xxx_keys_button *button = (struct xxx_keys_button *)dev_id;
12. struct input_dev *input = pdata->input;
13.
14. // 3、具有休眠唤醒功能且处于休眠模式,
15. if(button->wakeup == 1 && pdata->in_suspend == true){
16. button->state = 1;
17. input_event(input, EV_KEY, button->code, button->state);
18. input_sync(input);
19. }
20. // Timer去抖动
21. mod_timer(&button->timer,
22. jiffies + msecs_to_jiffies(DEFAULT_DEBOUNCE_INTERVAL));
23. return IRQ_HANDLED;
24. }
25.
26. setup_timer(&button->timer, keys_timer, (unsigned long)button)
27. static void keys_timer(unsigned long _data)
28. {
29. struct xxx_keys_drvdata *pdata = xxx_key_get_drvdata();
30. struct xxx_keys_button *button = (struct xxx_keys_button *)_data;
31. struct input_dev *input = pdata->input;
32. int state;
33.
34. state = !!((gpio_get_value(button->gpio) ? 1 : 0) ^ button->active_low);
35.
36. if(button->state != state) {
37. button->state = state;
38. input_event(input, EV_KEY, button->code, button->state);
39. input_event(input, EV_KEY, button->code, button->state);
40. input_sync(input);
41. }
42.
43. if(state)
44. mod_timer(&button->timer,
45. jiffies + msecs_to_jiffies(DEFAULT_DEBOUNCE_INTERVAL));
46. }
如果处于休眠态,直接上报唤醒事件(button->state = 1);否则就需要判断按键状态(keys_timer)。
至此,PowerKey作为唤醒源的实现就完成了。
五、PowerKey 事件读取
[cpp] view plain copy
1. #include <stdio.h>
2. #include <linux/input.h>
3. #include <stdlib.h>
4. #include <sys/types.h>
5. #include <sys/stat.h>
6. #include <fcntl.h>
7.
8. #define DEV_PATH "/dev/input/event2" // PowerKey report event node
9.
10. int main(int argc, char **argv)
11. {
12. int event_fd = -1;
13. struct input_event event = {0};
14. const size_t read_size = sizeof(struct input_event);
15.
16. event_fd = open(DEV_PATH, O_RDONLY);
17. if (event_fd <= 0) {
18. "%s open failed: %s\n", DEV_PATH, strerror(errno));
19. return -1;
20. }
21.
22. while (1) {
23. if (read(event_fd, &event, read_size) == read_size) {
24. if (event.type == EV_KEY) {
25. "event code: %d\n", event.code);
26. "event value: %d\n", event.value);
27. else {
28. "type != EV_KEY, type: %d\n", event.type);
29. }
30. }
31.
32. usleep(10*1000);
33. }
34.
35. close(event_fd);
36. return 0;
37. }
编译、adb push到Android设备中,运行后操作PowerKey,可见Log:

















