http://blog.ednchina.com/thinkker/133244/message.aspx
arm开发板买了大半年了一直都没怎么用,趁着这些天回家不能上网,每天研究一点点,也算小有所得,便就记录下来,以备后忘.(不能上网虽然有很多不便,但也确实更能合理分配业余时间).
S3C2410_gpio_button.c
模块初始化module_init(init_gpio_button)
init_gpio_button
循环处理gpio_buttons数组,
这个数组是在gpio_button_smdk2410_meri.c或者gpio_button_smdk2410_aiji.c中定义的,meri与
aiji是两个不同的开发板开发公司,所以他们的按键定义会有所不同,需要根据自己所采用的板子来配置(在内核编译里配置),
我们这里分析meri公司的.
如果当前按键有对应的irq,
设置此按键对应的irq的触发方式(set_external_irq). 包括上升沿/下降沿触发, 是否允许内部上拉电阻. 这两个参数是在gpio_buttons数组里已经作为常量定义好了的,这里只要读出来就行了
申请中断(request_irq). 注册中断处理函数gpio_button_interrupt,
设置中断属性为FIQ(SA_INTERRUPT), 注册按键名,
设置gpio_buttons数组的地址作为dev_id(其实是gpio_button_interrupt的第二个参数)
如果当前按键没有对应的irq
如果当前按键有对应的pm_callback(就是按键对应的电源管理的回调函数), 就初始化请求(PM_MZ_INIT)作为参数调用这个函数
打印一些信息
重复循环过程
如果定义了了CONFIG_PM, 即内核有电源管理模块
初始化一个定时器双向链表的结构体,这里结构提里主要是设置了回调函数pwButton_timer_callback
向电源管理模块注册按键的回调函数(pm_register). 注册的是设备类型(PM_USER_DEV,
在这个enum的定义后面有这么一句话: when wakeup, user must be handle this),
设备ID(PM_USER_INPUT), 以及回调函数(gpio_button_pm_callback)
gpio_button_pm_callback
如果当前新状态是休眠
如果此按键有对应的pm_callback, 则以睡眠状态为参数运行此函数
若此按键无对应的pm_callback
若此按键使用irq, 则禁止之
如果当前新状态是唤醒
如果此按键有对应的pm_callback, 则以唤醒状态做参数运行此函数
若此按键无对应的pm_callback
若此按键使用irq, 则开放之
如果定义了CONFIG_S3C2410_SMDK, 也就是基于SMDK2410构架的实现
初始化2410按键(s3c2410tk_button_init). 这里的按键指非特殊按键(不是Ctrl, Alt, shift等)
s3c2410tk_button_init
设置GPIO的B5为输入模式并禁止上拉(set_gpio_ctrl)
设置GPIO的B6为输入模式并禁止上拉(set_gpio_ctrl)
设置GPIO的E11为输入模式并禁止上拉(set_gpio_ctrl)
设置GPIO的E12为输入模式并禁止上拉(set_gpio_ctrl)
初始化一个定时器(init_timer(&button_check_timer))并设置回调函数为tk_button_check_timer_handler, 超时时间为BUTTON_DELAY
将定时器加入定时器链表(即现在开始这个定时器开始工作, 并在BUTTON_DELAY毫秒后第一次调用函数tk_button_check_timer_handler)
tk_button_check_timer_handler:这个函数每隔BUTTON_DELAY毫秒运行一次,并扫描gpio_buttons_tk数组中的按键
禁止中断(cli). 为了防止在扫描期间有按键中断进来,因为handle_scancode不是可重入函数
开始循环检测gpio_buttons_tk数组中每个按键是否有改变
暂存按键的原状态(按下还是未按)
读取按键的现状态(read_gpio_bit), 也就是读此按键对应的IO口电平
若现状态与原状态同,则表示此按键未改变,继续下一按键的检测
若状态有改变,则将按键的扫描值与按键状态发送给系统处理(handle_scancode).
在handle_scancode内主要是做长按的autorepeat处理, 将scancode转换成keycode, 唤醒等待进程,
将键加入队列等. 在这里有个函数比较有意思:
handle_scancode->add_keyboard_randomness->add_timer_randomness->int_ln_12bits.
这个函数分析看附录.
重复循环
循环结束
开放中断
修改定时器的下次触发时间
模块初始化结束
我们再来看按键的中断响应部分gpio_button_interrupt
循环寻找当前irq对应的按键结构
读取此按键状态(按下还是松开)
判断此按键是否有对应的按键处理函数, 如果有,则以按键状态和一个私有成员(BUTTON_TYPE::priv,
其实就是当前按键在gpio_buttons数组中的索引)作为参数调用处理函数.
我们查看gpio_buttons数组中的各个按键对应的处理函数可以发现,除了电源键外,其他所有按键对应的处理函数都是keypad_handler
keypad_handler:(普通功能键的处理函数)
根据传入的priv(实际上是索引值)取得按键信息结构
根据传入的按键状态, 将从按键信息结构体中取得的按键值和按键状态给系统处理(handle_scancode).这里与上面定时扫描部分相同
(这里有点不大明白的是,为什么不直接传按键值或按键信息结构指针而是要传索引号呢?)
pwButtonHandler:(电源键的处理函数)
如果电源键之前未被按下
打印一条信息
任务调度power_button_task::power_button_task_handler, 这里函数将调用pm_user_suspend使系统进入休眠状态
如果电源建之前已经被按下
设置电源键新状态为未按
如果没有处理函数,则将按键的扫描值与按键状态发送给系统处理(handle_scancode).
模块卸载exit_gpio_button就是将一些申请的资源释放掉
总结: S3C2410的按键驱动模块分两部分:功能键(包括电源键)和普通键
功能键全部用irq来实现,这样就可以实现同时按多个功能键以及功能键与普通键的组合. 其主数据结构数组为gpio_buttons
普通键用定时扫描的方法取得按键值.其初始化函数为s3c2410tk_button_init, 扫描算法函数为tk_button_check_timer_handler, 其数据结构数组为gpio_buttons_tk
阅读(1941) | 评论(0) | 转发(0) |