采用平台设备驱动机制来写,s5pc110自带按键控制器,且按键控制器内部带有硬件滤波电路.支持多个按键同时按下.
一:按键控制器原理分析
参考s5pc110开发手册KEYPAD INTERFACE一章的keypad scanning procedure小节
初始化状态下列线全为低电平,行线全为高电平,当按键按下,对应的行线和列线短接,行线变为低电平,读取行线寄存器便可确定是哪一行产生了中断,再循环的将列线输出低电平并去读取刚检测到行线,若行线为低电平,说明
对应的列线参生了中断,行列便可以确定是哪个按键按下.(为单个按键的扫描过程,多个在这不做讨论)
二:驱动分析
设备在哪里?--->平台文件:arch/arm/plat-s5p/devs.c
按键相关代码如下:
-
/* Keypad interface */
-
static struct resource s3c_keypad_resource[] = {
-
[0] = {
-
.start = S3C_PA_KEYPAD,//0xE1600000
-
.end = S3C_PA_KEYPAD + S3C_SZ_KEYPAD - 1,//#define S5PV2XX_SZ_KEYPAD SZ_4K(#define SZ_4K 0x00001000)
-
.flags = IORESOURCE_MEM,//I/O内存资源
-
},
-
[1] = {
-
.start = IRQ_KEYPAD,//121
-
.end = IRQ_KEYPAD,
-
.flags = IORESOURCE_IRQ,//中断资源
-
}
-
};
-
-
struct platform_device s3c_device_keypad = {
-
.name = "s3c-keypad",
-
.id = -1,
-
.num_resources = ARRAY_SIZE(s3c_keypad_resource),
-
.resource = s3c_keypad_resource,
-
};
资源在哪注册?--->板级文件arch/arm/mach-s5pv210/mach-smdkc110.c
-
static void __init smdkc110_machine_init(void)
-
{
-
arm_pm_restart = smdkc110_pm_restart;
-
-
s3c_usb_set_serial();
-
platform_add_devices(smdkc110_devices, ARRAY_SIZE(smdkc110_devices));//此处将所有的设备注册到内核中
-
....
-
}
smdkc110_machine_init()函数中platform_add_devices()函数的smdkc110_devices参数便是平台设备结构体的一个数组---->arch/arm/mach-s5pv210/mach-smdkc110.c
-
static struct platform_device *smdkc110_devices[] __initdata = {
-
....
-
&s3c_device_keypad,
-
......
-
};
platform_add_devices函数的实现--->driver/input/keyboard/s3c-keypad.c
-
int platform_add_devices(struct platform_device **devs, int num)
-
{
-
int i, ret = 0;
-
-
for (i = 0; i < num; i++) {
-
ret = platform_device_register(devs[i]);//注册平台设备
-
if (ret) {
-
while (--i >= 0)
-
platform_device_unregister(devs[i]);
-
break;
-
}
-
}
-
-
return ret;
-
}
按键驱动程序文件--->drivers/input/keyboard/s3c-keypad.c
驱动的注册:
-
static int __init s3c_keypad_init(void)
-
{
-
int ret;
-
-
ret = platform_driver_register(&s3c_keypad_driver);//驱动的注册
-
-
if (!ret)
-
pr_info("S3C Keypad Driver\n");
-
-
return ret;
-
}
将s3c_keypad_driver注册到驱动链表,然后按照驱动的名字去匹配设备,匹配成功调用驱动结构体中的probe函数
s3c_keypad_driver结构体:---->driver/input/keyboard/s3c-keypad.c
-
static struct platform_driver s3c_keypad_driver = {
-
.probe = s3c_keypad_probe,
-
.remove = s3c_keypad_remove,
-
.suspend = s3c_keypad_suspend,//挂起(节能相关)
-
.resume = s3c_keypad_resume,//唤醒(节能相关)
-
.driver = {
-
.owner = THIS_MODULE,
-
.name = "s3c-keypad",
-
},
-
};
驱动的注册和匹配过程这里不作分析,自己可以去跟源代码.下面来分析probe函数--->driver/input/keyboard/s3c-keypad.c
-
static int __init s3c_keypad_probe(struct platform_device *pdev)
-
{
-
struct resource *res, *keypad_mem, *keypad_irq;//资源结构体
-
struct input_dev *input_dev;//输入设备结构体
-
struct s3c_keypad *s3c_keypad;//按键结构体
-
int ret, size;
-
int key, code;
-
-
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);//获取平台的I/O内存资源(按键控制器的寄存器的地址)
-
if (res == NULL) {
-
dev_err(&pdev->dev, "no memory resource specified\n");
-
return -ENOENT;
-
}
-
-
size = (res->end - res->start) + 1;//计算寄存器资源的大小,从0开始计数所以需要加1
-
-
keypad_mem = request_mem_region(res->start, size, pdev->name);//申请I/O资源,避免冲突
-
if (keypad_mem == NULL) {
-
dev_err(&pdev->dev, "failed to get memory region\n");
-
ret = -ENOENT;
-
goto err_req;
-
}
-
-
key_base = ioremap(res->start, size);//映射I/O资源
-
if (key_base == NULL) {
-
pr_err("Failed to remap register block\n");
-
ret = -ENOMEM;
-
goto err_map;
-
}
-
-
keypad_clock = clk_get(&pdev->dev, "keypad");//获取时钟,硬件消抖需要时钟
-
if (IS_ERR(keypad_clock)) {
-
dev_err(&pdev->dev, "failed to find keypad clock source\n");
-
ret = PTR_ERR(keypad_clock);
-
goto err_clk;
-
}
-
-
clk_enable(keypad_clock);//使能时钟
-
-
s3c_keypad = kzalloc(sizeof(struct s3c_keypad), GFP_KERNEL);//分配按键结构体
-
input_dev = input_allocate_device();//分配输入设备结构体(输入子系统)
-
-
if (!s3c_keypad || !input_dev) {
-
ret = -ENOMEM;
-
goto err_alloc;
-
}
-
-
platform_set_drvdata(pdev, s3c_keypad);//填充平台数据
-
s3c_keypad->dev = input_dev;//填充按键结构体的dev成员
-
-
writel(KEYIFCON_INIT, key_base+S3C_KEYIFCON);//按键初始化
-
writel(KEYIFFC_DIV, key_base+S3C_KEYIFFC);//硬件消抖时钟配置
-
-
/* Set GPIO Port for keypad mode and pull-up disable*/
-
-
#if defined(CONFIG_KEYPAD_S3C_MSM)//该宏没有定义
-
s3c_setup_keypad_cfg_gpio();
-
#else
-
s3c_setup_keypad_cfg_gpio(KEYPAD_ROWS, KEYPAD_COLUMNS);//gpio管脚设置
-
#endif
-
writel(KEYIFCOL_CLEAR, key_base+S3C_KEYIFCOL);//清除列线
-
-
/* create and register the input driver */
-
set_bit(EV_KEY, input_dev->evbit);//设置按键事件
-
s3c_keypad->nr_rows = KEYPAD_ROWS;//按键矩阵的行数
-
s3c_keypad->no_cols = KEYPAD_COLUMNS;//按键矩阵的列数
-
s3c_keypad->total_keys = MAX_KEYPAD_NR;//按键数
-
-
for (key = 0; key < s3c_keypad->total_keys; key++) {
-
code = s3c_keypad->keycodes[key] = keypad_keycode[key];//按键结构体的按键编码数组的赋值
-
if (code <= 0)
-
continue;
-
set_bit(code & KEY_MAX, input_dev->keybit);//set_bit和keybit在下文有分析
-
}
-
-
input_dev->name = DEVICE_NAME;
-
input_dev->phys = "s3c-keypad/input0";
-
-
input_dev->id.bustype = BUS_HOST;
-
input_dev->id.vendor = 0x0001;
-
input_dev->id.product = 0x0001;
-
input_dev->id.version = 0x0001;
-
-
input_dev->keycode = keypad_keycode;//以上都市填充input_dev结构体
-
-
/* Scan timer init */
-
init_timer(&keypad_timer);//初始化一个定时器
-
keypad_timer.function = keypad_timer_handler;//定时器函数
-
keypad_timer.data = (unsigned long)s3c_keypad;//定时器私有数据,定时器函数的参数
-
-
/* For IRQ_KEYPAD */
-
keypad_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);//获取中断资源
-
if (keypad_irq == NULL) {
-
dev_err(&pdev->dev, "no irq resource specified\n");
-
ret = -ENOENT;
-
goto err_irq;
-
}
-
ret = request_irq(keypad_irq->start, s3c_keypad_isr, 0,
-
DEVICE_NAME, (void *) pdev);//申请中断
-
if (ret) {
-
pr_err("request_irq failed (IRQ_KEYPAD)\n");
-
ret = -EIO;
-
goto err_irq;
-
}
-
-
ret = input_register_device(input_dev);//注册input设备
-
if (ret) {
-
pr_err("Unable to register s3c-keypad input device!!!\n");
-
goto out;
-
}
-
-
keypad_timer.expires = jiffies + (HZ/10);//定时器定时时间
-
-
if (is_timer_on == false) {//添加定时器
-
add_timer(&keypad_timer);
-
is_timer_on = true;
-
} else {
-
mod_timer(&keypad_timer, keypad_timer.expires);
-
}
-
-
pr_info(DEVICE_NAME " Initialized\n");
-
return 0;
-
/*以下是错误处理*/
-
out:
-
free_irq(keypad_irq->start, input_dev);
-
free_irq(keypad_irq->end, input_dev);
-
-
err_irq:
-
input_free_device(input_dev);
-
kfree(s3c_keypad);
-
-
err_alloc:
-
clk_disable(keypad_clock);
-
clk_put(keypad_clock);
-
-
err_clk:
-
iounmap(key_base);
-
-
err_map:
-
release_resource(keypad_mem);
-
kfree(keypad_mem);
-
-
err_req:
-
return ret;
-
}
中断处理函数分析--->
driver/input/keyboard/s3c-keypad.c
-
static irqreturn_t s3c_keypad_isr(int irq, void *dev_id)
-
{
-
/* disable keypad interrupt and schedule for keypad timer handler */
-
writel(readl(key_base + S3C_KEYIFCON) & ~(INT_F_EN|INT_R_EN),
-
key_base + S3C_KEYIFCON);//禁止按键按下和松开的中断,按下期间不响应其他的按下松开中断
-
-
keypad_timer.expires = jiffies + (HZ/100);//更新定时器的定时时间
-
if (is_timer_on == false) {
-
add_timer(&keypad_timer);
-
is_timer_on = true;
-
} else {
-
mod_timer(&keypad_timer, keypad_timer.expires);//激活定时器,在probe函数的116行将is_timer_on赋值为true
-
}
-
/*Clear the keypad interrupt status*/
-
writel(KEYIFSTSCLR_CLEAR, key_base + S3C_KEYIFSTSCLR);//清按键松开的中断
-
-
return IRQ_HANDLED;
-
}
总结:中断处理函数里主要做的事就是激活定时器,等定时时间到去执行定时函数
定时函数分析:--->drivers/input/keyboard/s3c-keypad.c
-
static void keypad_timer_handler(unsigned long data)
-
{
-
u32 press_mask;
-
u32 release_mask;
-
u32 restart_timer = 0;
-
int i, col;
-
struct s3c_keypad *pdata = (struct s3c_keypad *)data;//取出私有数据,在probe函数的
-
struct input_dev *dev = pdata->dev;
-
-
keypad_scan();//扫描按键,下面分析
-
for (col = 0; col < KEYPAD_COLUMNS; col++) {//列线
-
press_mask = ((keymask[col] ^ prevmask[col]) & keymask[col]);//prevmask[]为按键按下的前一次的状态(代码中假设按下相应的位为1)^操作为不同为1,所以前面的括号的作用是判断是是否有按键按下或松开,前面一个括号的值再与上keymask的值便可以确定是否有按键按下
-
release_mask = ((keymask[col] ^ prevmask[col]) & prevmask[col]);//同理,确认是否有按键松开
-
i = col * KEYPAD_ROWS;//这里是获得按键码在按键码数组中的小标(每列占用KEYPAD_ROWS个按键)
-
-
while (press_mask) {//有按键按下
-
if (press_mask & 1) {//跟i++一起某一列中的哪一行
-
input_report_key(dev, pdata->keycodes[i], 1);
-
pr_debug("key Pressed : key %d map %d\n",
-
i, pdata->keycodes[i]);
-
printk("key pressed :key %d map %d\n",i,pdata->keycodes[i]);
-
}
-
press_mask >>= 1;
-
i++;//行++
-
}
-
-
i = col * KEYPAD_ROWS;
-
-
while (release_mask) {//有按键松开,同理按下的操作
-
if (release_mask & 1) {
-
input_report_key(dev, pdata->keycodes[i], 0);
-
pr_debug("key Released : %d map %d\n",
-
i, pdata->keycodes[i]);
-
printk("key pressed :key %d map %d\n",i,pdata->keycodes[i]);
-
}
-
release_mask >>= 1;
-
i++;
-
}
-
prevmask[col] = keymask[col];//保存上一次的按键状态
-
-
restart_timer |= keymask[col];//keymask不为0说明有按键按下或松开,restart_timer的作用就是标志是否有按键状态变化
-
}
-
-
if (restart_timer) {
-
mod_timer(&keypad_timer, jiffies + HZ/10);//有按键按下或松开,重新设定定时时间并激活定时器,开始下一次计时
-
} else {
-
writel(KEYIFCON_INIT, key_base + S3C_KEYIFCON);//中断和消抖使能
-
is_timer_on = false;//没有按键,关闭定时器
-
}
-
}
keypad_scan()分析
-
static int keypad_scan(void)
-
{
-
u32 col, cval, rval;
-
-
pr_debug("keypad_scan() is called\n");
-
-
pr_debug("row val = %x", readl(key_base + S3C_KEYIFROW));
-
-
for (col = 0; col < KEYPAD_COLUMNS; col++) {//循环的将列线输出低电平(并设置为正常输出状态)
-
/* clear that column number and make that normal output */
-
cval = KEYCOL_DMASK & ~((1 << col) | (1 << (col + 8)));//KEYCOL_DMASK=0xff
-
writel(cval, key_base + S3C_KEYIFCOL);//列线输出低电平
-
-
阅读(1201) | 评论(0) | 转发(0) |