路由器上有一个按键(wps),按键属于gpio的应用。(见trunk\apps\wpsPBC文件夹)
在应用层:只关注gpio(按键)的初始化(gpio_init.c),主要分为以下几个步骤:
1. 打开设备文件;
fd = open("/dev/gpio", O_RDONLY);
2. 设置gpio引脚(0)为输入模式
if (ioctl(fd, RALINK_GPIO_SET_DIR_IN, RALINK_GPIO(0)) < 0) // 这里能看到对gpio号0设置输入模式
3. 使能gpio的中断功能;
if (ioctl(fd, RALINK_GPIO_ENABLE_INTP) < 0)
4. 注册进程的进程号到相应的gpio号,用来接收相应引脚发生中断时,接收来自中断处理程序发个应用进程一个信号。
info.pid = getpid();
info.irq = 0;
if (ioctl(fd, RALINK_GPIO_REG_IRQ, &info) < 0)
在ralink_gpio驱动中,维护了一个全局数组,ralink_gpio_reg_info ralink_gpio_info[RALINK_GPIO_NUMBER];,这个数组记录了,相应引脚对应的应用层的进程号。
RALINK_GPIO_REG_IRQ命令就是用来在数组[0]中加入进程号。
在gpio驱动中,先为gpio注册中断,这个中断理解为为所有的gpio引脚的中断。
在ralink_gpio.c中ralink_gpio_init_irq函数,
void __init ralink_gpio_init_irq(void)
{
setup_irq(SURFBOARDINT_GPIO, &ralink_gpio_irqaction); // SURFBOARDINT_GPIO为中断号6,ralink_gpio_irqaction为中断处理结构,struct irqaction。
}
struct irqaction ralink_gpio_irqaction = {
.handler = ralink_gpio_irq_handler, // 这是中断处理函数
.flags = SA_INTERRUPT,
.name = "ralink_gpio",
};
进入ralink_gpio_irq_handler 函数,在这个函数中首先获取是在哪个引脚(gpio号)发生了中断。这里有个关键的寄存器,gpio中断状态寄存器,这个寄存器记录了所有gpio引脚的中断状态,如果有中断发生,则相应的位置1.
ralink_gpio_save_clear_intp();// 这个函数里ralink_gpio_intp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIOINT)); RALINK_REG_PIOINT这个则就是gpio中断状态寄存器// 的地址,这里ralink_gpio_intp 就是获取了这个寄存器的值
for (i = 0; i < 24; i++) { // ralink5350一共有24个gpio引脚
if (! (ralink_gpio_intp & (1 << i))) // 判断从0-23 相应的位是否是为1,为1则相应的引脚发生了中断。
continue;
.......
很多路由器上,按键是复用的(比如长按恢复出厂,短按打开或者关闭无线),按键需要判断长按还是短按,则要判断相应gpio的上升沿和下降沿的时间差;
有一个结构体
struct gpio_time_record {
unsigned long falling;
unsigned long rising;
};
随后的代码则是对这些进行判断(按照需求,去修改代码)。
在判断长按短按后,通过void ralink_gpio_notify_user(int usr) 函数向应用层进程发送一个信号,这个函数很简单,流程一看就明白,参数usr 则是发送的信号值。
引用层的代码会安装相应的信号处理函数,来处理不同的动作。
原文地址:http://blog.csdn.net/onetwothreef/article/details/20034385