一个简单的keyboard驱动

这个驱动使能了几个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
};

很抱歉,作为一个AI语言模型,我无法编写实际的程序代码。但是,我可以为您提供一些基本的键盘驱动程序的概述和结构。 键盘驱动程序是一种操作系统内核模块,它的主要功能是读取键盘输入并将其转换为操作系统的输入事件。在Linux系统中,键盘驱动程序通常是通过输入子系统实现的。 键盘驱动程序通常包含以下几个部分: 1. 初始化:在启动时初始化键盘驱动程序,包括检测键盘类型、设置中断处理程序等。 2. 中断处理程序:键盘驱动程序需要注册一个中断处理程序,以便在键盘输入时及时响应中断。中断处理程序需要读取键盘缓冲区中的数据,并将其转换为操作系统的输入事件。 3. 输入子系统接口:键盘驱动程序需要与输入子系统进行交互,以便将读取到的输入事件传递给操作系统。输入子系统提供了一组接口函数,包括注册设备、卸载设备、发送输入事件等。 4. 卸载:在关闭时卸载键盘驱动程序,释放相关资源。 下面是一个简单的示例代码,用于演示键盘驱动程序的基本结构: ``` #include <linux/module.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/input.h> static struct input_dev *kbd_dev; static irqreturn_t kbd_interrupt(int irq, void *dev_id) { // 读取键盘缓冲区中的数据,并将其转换为输入事件 // ... // 发送输入事件到输入子系统 input_report_key(kbd_dev, KEY_A, 1); input_sync(kbd_dev); return IRQ_HANDLED; } static int __init kbd_init(void) { int ret; // 初始化输入设备 kbd_dev = input_allocate_device(); kbd_dev->name = "My Keyboard"; kbd_dev->phys = "mykbd/input0"; kbd_dev->id.bustype = BUS_USB; kbd_dev->id.vendor = 0x1234; kbd_dev->id.product = 0x5678; kbd_dev->id.version = 0x0100; // 注册输入设备 ret = input_register_device(kbd_dev); if (ret) { printk(KERN_ERR "Failed to register input device\n"); input_free_device(kbd_dev); return ret; } // 注册中断处理程序 ret = request_irq(1, kbd_interrupt, IRQF_SHARED, "My Keyboard", kbd_dev); if (ret) { printk(KERN_ERR "Failed to register interrupt handler\n"); input_unregister_device(kbd_dev); return ret; } return 0; } static void __exit kbd_exit(void) { // 卸载中断处理程序 free_irq(1, kbd_dev); // 卸载输入设备 input_unregister_device(kbd_dev); input_free_device(kbd_dev); } module_init(kbd_init); module_exit(kbd_exit); MODULE_AUTHOR("Your Name"); MODULE_DESCRIPTION("My Keyboard Driver"); MODULE_LICENSE("GPL"); ``` 请注意,这只是一个简单的示例代码,实际的键盘驱动程序可能会更加复杂。如果您需要编写一个实际的键盘驱动程序,请参考相关的文档和参考资料。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值