这个驱动使能了几个CPLD控制的按键,F1, F2,F3,F4, HOME, UP, DOWN,LEFT,RIGHT, ESC, ENTER。
1.cpld_kpd_probe()
static int cpld_kpd_probe(struct platform_device *pdev)
{
struct input_dev *cpld_input_dev;
int i, irq;
int retval;
dprintk("%s.\n", __func__);
key_pad_enabled = 0;
cpld_kpd_dev = kmalloc(sizeof(struct cpld_kpd), GFP_KERNEL);
cpldkpd_keycodes = key_mapping;
cpldkpd_keycodes_size = 4*3;
if ((key_mapping == (void *)0)
|| (cpldkpd_keycodes_size == 0)) {
retval = -ENODEV;
goto err1;
}
cpld_input_dev=input_allocate_device();
if(!cpld_kpd_dev || !cpld_input_dev) {
retval = -ENOMEM;
goto err1;
}
platform_set_drvdata(pdev, cpld_kpd_dev);
cpld_kpd_dev->input = cpld_input_dev;
cpld_input_dev->keycode = (void *)cpldkpd_keycodes;
cpld_input_dev->keycodesize = sizeof(cpldkpd_keycodes[0]);
cpld_input_dev->keycodemax = cpldkpd_keycodes_size;
cpld_input_dev->name = "cpldkpd";
cpld_input_dev->id.bustype = BUS_HOST;
retval = input_register_device(cpld_input_dev);
if(retval < 0) {
printk(KERN_ERR
"cpld_input_dev: failed to register input device.\n");
goto err2;
}
__set_bit(EV_KEY, cpld_input_dev->evbit);
for (i = 0; i < cpldkpd_keycodes_size; i++)
__set_bit(cpldkpd_keycodes[i], cpld_input_dev->keybit);
cpld_kpd_dev->poll_timer.expires = jiffies + KScanRate;
cpld_kpd_dev->poll_timer.function = cpld_kpd_handle_timer;
/* Initialize the polling timer */
init_timer(&cpld_kpd_dev->poll_timer);
/* map CPLD memory */
cpld_kpd_dev->base = ioremap(CPLD_BASE_ADDR, 64);
if(!cpld_kpd_dev->base) {
retval = -ENOMEM;
goto err2;
}
irq = setup_gpio_irq();
retval = request_irq(irq, cpld_kpd_interrupt, 0, MOD_NAME, MOD_NAME);
if(retval) {
pr_debug("CPLDKPD: request_irq(%d) error %d\n",
irq, retval);
goto err3;
}
key_pad_enabled = 1;
device_init_wakeup(&pdev->dev, 1);
return 0;
err3:
free_irq(irq, MOD_NAME);
err2:
input_unregister_device(cpld_input_dev);
err1:
input_free_device(cpld_input_dev);
kfree(cpld_kpd_dev);
return retval;
}
这算是个标准的keyboard input设备的probe函数。
cpld_kpd_dev是个全局变量,原型如下:
/* Keypad private data structure */
struct cpld_kpd {
struct input_dev *input;
/* Timer used for keypad polling */
struct timer_list poll_timer;
int irq;
unsigned int n_keys;
/* The base address */
void __iomem *base;
};
struct cpld_kpd *cpld_kpd_dev;
input_dev: 表示是个输入设备。
poll_timer.:因为整了一个定时器,所以有一个timer_list
irq是中断号,
nkeys:是按键的个数
base:是需要ioremap的 映射CPLD的地址,用来读取按键寄存器值的。
然后分配一个input_dev, 接着是初始化和注册输入设备。
然后是注册中断。
初始化定时器:
cpld_kpd_dev->poll_timer.expires = jiffies + KScanRate;
cpld_kpd_dev->poll_timer.function = cpld_kpd_handle_timer;
/* Initialize the polling timer */
init_timer(&cpld_kpd_dev->poll_timer);
remap CPLD内存空间,访问CPLD寄存器需要:
/* map CPLD memory */
cpld_kpd_dev->base = ioremap(CPLD_BASE_ADDR, 64);
if(!cpld_kpd_dev->base) {
retval = -ENOMEM;
goto err2;
}
设置中断触发方式,并注册中断:
irq = setup_gpio_irq();
retval = request_irq(irq, cpld_kpd_interrupt, 0, MOD_NAME, MOD_NAME);
if(retval) {
pr_debug("CPLDKPD: request_irq(%d) error %d\n",
irq, retval);
goto err3;
}
2. 中断服务例程, 发现中断就报告event:
static irqreturn_t cpld_kpd_interrupt(int irq, void *dev_id)
{
dprintk("%s.\n", __func__);
del_timer(&cpld_kpd_dev->poll_timer);
cpld_kpd_handle_timer(0);
add_timer(&cpld_kpd_dev->poll_timer);
return IRQ_RETVAL(1);
}
static void cpld_kpd_handle_timer(unsigned long time)
{
dprintk("%s.\n", __func__);
if (key_pad_enabled == 0) {
dprintk("key pad not enabled\n");
return;
}
if (cpld_kpd_scan_matrix() == 0) {
dprintk("scan matrix = 0\n");
return;
}
}
扫描键盘,看哪个键按下,并发送event,即input_event()函数,报告按下之后要报告一个弹起的事件,因为这里有弹起中断。
static int cpld_kpd_scan_matrix(void)
{
volatile unsigned short reg;
int count = 0;
dprintk("%s.\n", __func__);
reg = __raw_readw(cpld_kpd_dev->base + KEY_STATUS);
dprintk("read cpld reg = 0x%x\n", reg);
if (reg & F1_BITMAP) {
input_event(cpld_kpd_dev->input, EV_KEY, KEY_F1, 1);
count++;
input_event(cpld_kpd_dev->input, EV_KEY, KEY_F1, 0);
}
if(reg & F2_BITMAP) {
input_event(cpld_kpd_dev->input, EV_KEY, KEY_F2, 1);
count++;
input_event(cpld_kpd_dev->input, EV_KEY, KEY_F2, 0);
}
if(reg & F3_BITMAP) {
input_event(cpld_kpd_dev->input, EV_KEY, KEY_F3, 1);
count++;
input_event(cpld_kpd_dev->input, EV_KEY, KEY_F3, 0);
}
if(reg & F4_BITMAP) {
input_event(cpld_kpd_dev->input, EV_KEY, KEY_F4, 1);
count++;
input_event(cpld_kpd_dev->input, EV_KEY, KEY_F4, 0);
}
if(reg & HOME_BITMAP) {
input_event(cpld_kpd_dev->input, EV_KEY, KEY_HOME, 1);
count++;
input_event(cpld_kpd_dev->input, EV_KEY, KEY_HOME, 0);
}
if(reg & UP_BITMAP) {
input_event(cpld_kpd_dev->input, EV_KEY, KEY_UP, 1);
count++;
input_event(cpld_kpd_dev->input, EV_KEY, KEY_UP, 0);
}
if(reg & LEFT_BITMAP) {
input_event(cpld_kpd_dev->input, EV_KEY, KEY_LEFT, 1);
count++;
input_event(cpld_kpd_dev->input, EV_KEY, KEY_LEFT, 0);
}
if(reg & RIGHT_BITMAP) {
input_event(cpld_kpd_dev->input, EV_KEY, KEY_RIGHT, 1);
count++;
input_event(cpld_kpd_dev->input, EV_KEY, KEY_RIGHT, 0);
}
if(reg & DOWN_BITMAP) {
input_event(cpld_kpd_dev->input, EV_KEY, KEY_DOWN, 1);
count++;
input_event(cpld_kpd_dev->input, EV_KEY, KEY_DOWN, 0);
}
if(reg & ENTER_BITMAP) {
input_event(cpld_kpd_dev->input, EV_KEY, KEY_ENTER, 1);
count++;
input_event(cpld_kpd_dev->input, EV_KEY, KEY_ENTER, 0);
}
if(reg & ESC_BITMAP) {
input_event(cpld_kpd_dev->input, EV_KEY, KEY_ESC, 1);
count++;
input_event(cpld_kpd_dev->input, EV_KEY, KEY_ESC, 0);
}
dprintk("count = 0x%x\n", count);
return count;
}
具体的按键有如下这些:
static u16 key_mapping[] = {
KEY_F1, KEY_HOME, KEY_DOWN,
KEY_F2, KEY_UP, KEY_ENTER,
KEY_F3, KEY_LEFT, KEY_ESC,
KEY_F4, KEY_RIGHT, 0
};