您的位置:首页 > 运维架构 > Linux

S3C44B0X键盘的uClinux驱动

2009-08-24 23:45 471 查看
最近几天在研究S3C44B0X键盘驱动,在网上搜了遍,只找到一篇(是千篇一律,不是只有一个搜索结果),都是讲把pc_keyb.c改造成合适的驱动,我业尝试过这个方法,开始是一头雾水(毕竟自己还是很菜),pc_keyb.c里面那么多函数,对linux上的中断驱动没有任何经验,最终放弃了这条路,我们正着走,光明一些。看了一些关于linux驱动的资料,再结合ARM手册上的说明,终于从正向实现了键盘的驱动,然后我再回过去看pc_keyb.c,下意识地就把它改好了,这个事实告诉我们要踏踏实实地,一步一个脚印向前走,要深厚内功。

下面是我自己写的驱动代码:

#include <linux/init.h>
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/sched.h>
#include <linux/types.h>
#include <linux/interrupt.h>

#include <asm/uaccess.h>
#include <asm/arch/s3c44b0x.h>

#define IO_IRQ_NUM	21
#define KBD_MAJOR	232
#define KBD_NAME	"keyboard"

//////////		global vars		////////////
//static struct tasklet_struct keytask;
long jiffies_IRQ_21;

//////////////////		announce of key functions	/////////////////////
static int kbd_open(struct inode *inode, struct file *filp);
static int kbd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long param);
static int kbd_release(struct inode *inode, struct file *filp);
void kbd_init(void);
int kbd_cleanup(void);

/////////////////			interrupt handler		/////////////////
static irqreturn_t interrupt_handler_4567(int irq, void *dev_id, struct pt_regs *regs);

struct file_operations fd_ops = {
owner:		THIS_MODULE,
open:		kbd_open,
release:		kbd_release,
ioctl:		kbd_ioctl,
};

static int kbd_open(struct inode *inode, struct file *filp)
{
int ret;
printk("Open kbd driver!/n");

//assign IRQ 21
ret = request_irq(IO_IRQ_NUM, interrupt_handler_4567, 0, "ih_4567", NULL);
if(ret != 0)
{
printk("Can not assign IRQ!/n");
return -EAGAIN;
}
jiffies_IRQ_21 = jiffies;
(*(volatile unsigned *)S3C44B0X_EXTINT) &= 0xFFFF;
(*(volatile unsigned *)S3C44B0X_PCONG) |= 0xFF00;
(*(volatile unsigned *)S3C44B0X_PUPG) |= 0x00;
(*(volatile unsigned *)S3C44B0X_INTMOD) &= ~(1<<21);	//irq mode
(*(volatile unsigned *)S3C44B0X_INTCON) &= ~(2);		//enable FIQ interrupt
(*(volatile unsigned *)S3C44B0X_EXTINPND) |= 0xF;
(*(volatile unsigned *)S3C44B0X_INTPND) &= ~(1<<21);	//clear 21 interrupt request
//config led registers
(*(volatile unsigned *)S3C44B0X_PUPC) = 0x00;
(*(volatile unsigned *)S3C44B0X_PCONC) &= 0xFFFFFF00;
(*(volatile unsigned *)S3C44B0X_PCONC) |= 0x00000055;
(*(volatile unsigned *)S3C44B0X_PDATC) &= 0xFFFFFF00;
printk("EXTINTPND = %02X/n", (*(volatile unsigned *)S3C44B0X_EXTINPND));

MOD_INC_USE_COUNT;
return 0;
}

static int kbd_release(struct inode *inode, struct file *filp)
{
printk("Release kbd driver!/n");

free_irq(IO_IRQ_NUM, NULL);

MOD_DEC_USE_COUNT;
return 0;
}

static int kbd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long param)
{
printk("ioctl of kbd driver!/n");
}

static irqreturn_t interrupt_handler_4567(int irq, void *dev_id, struct pt_regs *regs)
{
int wval, ibits;
(*(volatile unsigned *)S3C44B0X_EXTINPND) |= 0xF;
(*(volatile unsigned *)S3C44B0X_INTPND) &= ~(1<<21);	//clear interrupt request
(*(volatile unsigned *)S3C44B0X_I_ISPC) &= ~(1<<21);
if((jiffies - jiffies_IRQ_21) < 100)
return IRQ_HANDLED;
wval = (*(volatile unsigned *)S3C44B0X_PDATC);
ibits = ((*(volatile unsigned *)S3C44B0X_EXTINPND) << 1);
ibits &= 0x0000000E;
wval ^= ibits;
(*(volatile unsigned *)S3C44B0X_PDATC) = wval;
return IRQ_HANDLED;
}

///////////////////////		module initialization		/////////////////////////////
void __init kbd_init(void)
{
int ret;
ret = register_chrdev(KBD_MAJOR, KBD_NAME, &fd_ops);
if(ret < 0)
printk("Unable to use major: %d/n", KBD_MAJOR);
else
printk("Init keyboard driver OK!/n");
}

int kbd_cleanup(void)
{
unregister_chrdev(KBD_MAJOR, KBD_NAME);
return 0;
}

MODULE_AUTHOR("Cricket");
MODULE_LICENSE("GPL");


由于只有3个LED,所以第四个键没用到,不过原理是一样的

下面是我修改pc_keyb.c得到的键盘驱动,我把它重命名为44b0kbd.c

#include <linux/config.h>
#include <linux/spinlock.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/tty.h>
#include <linux/mm.h>
#include <linux/signal.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/random.h>
#include <linux/poll.h>
#include <linux/miscdevice.h>
#include <linux/slab.h>
#include <linux/smp_lock.h>
#include <linux/pm.h>

#include <asm/arch/s3c44b0x.h>
#include <asm/bitops.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/system.h>

#include <asm/io.h>

unsigned long prevjiffies;

static unsigned char handle_kbd_event(void);

static unsigned char handle_kbd_event(void)
{
(*(volatile unsigned *)S3C44B0X_EXTINPND) |= 0xF;
(*(volatile unsigned *)S3C44B0X_INTPND) &= ~(1<<21);  //先清除中断位
(*(volatile unsigned *)S3C44B0X_I_ISPC) &= ~(1<<21);  //同上
if((jiffies - prejiffies) < 100)  //100个周期内只响应一次按键
return 1;
prevjiffies = jiffies;
return 0;
}

static void keyboard_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
printk("Cricket's keyboard, invoked ketboard_interrupt()/n");
handle_kbd_event();
}

/********************
***  module init  ***
********************/
void __init pckbd_init_hw(void)
{
int ret;
ret = request_irq(21, keyboard_interrupt, 0, "keyboard", NULL);
if(ret != 0)
{
printk("Can not assign IRQ 21!/n");
return;
}
(*(volatile unsigned *)S3C44B0X_INTPND) &= ~(1<<21);
(*(volatile unsigned *)S3C44B0X_INTMOD) &= ~(1<<21); //IRQ模式,不能设为FIQ模式
(*(volatile unsigned *)S3C44B0X_INTCON) &= ~(2);    //IRQ模式
(*(volatile unsigned *)S3C44B0X_EXTINT) |= 0xFFFF0000;  //一个上升沿加一个下降沿出发
printk("Assign IRQ 21 successfully!/n");
}


这里需要注意一个问题:

(*(volatile unsigned *)S3C44B0X_EXTINPND) |= 0xF;
(*(volatile unsigned *)S3C44B0X_INTPND) &= ~(1<<21); //clear interrupt request
(*(volatile unsigned *)S3C44B0X_I_ISPC) &= ~(1<<21);

---------------------------------------------
if((jiffies - jiffies_IRQ_21) < 100)
return IRQ_HANDLED;

上面用分隔线分开的两部分不能颠倒,当时我颠倒了,按了一次之后老是出错,系统输出:

IRQ LOCK: IRQ21 is locking the system, disabled

这个问题折腾了我好久,可能是我当时犯傻了,这样的逻辑都整理不好……

接着再把初始化函数写入driver/char文件夹下的mem.c里面,再做一些改动(前面我写的那篇《uClinux+S3C44B0X驱动编写总结》有详细的步骤),kbd驱动要使用用户程序来打开才能使用,44b0kbd就不用了(系统初始化时已经设置好中断),因此在make menuconfig的时候两个驱动不能同时选中,这样给调试带来了一些麻烦。

编译后放到板子上,两个驱动都正常工作。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: