查询方式的按键驱动程序
按键驱动编写思路
GPIO 按键的原理图一般有如下 2 种:
按键没被按下时,上图中左边的 GPIO 电平为高,右边的 GPIO 电平为低。
按键被按下后,上图中左边的 GPIO 电平为低,右边的 GPIO 电平为高。
编写按键驱动程序最简单的方法如图所示
回顾一下编写驱动的套路:
对于使用查询方式的按键驱动程序,我们只需要实现 button_open、
button_read。
我们的目的写出一个容易扩展到各种芯片、各种板子的按键驱动程序,所以
驱动程序分为上下两层:
button_drv.c 分配/设置/注册 file_operations 结构体
起承上启下的作用,向上提供 button_open,button_read 供 APP 调用。
而这 2 个函数又会调用底层硬件提供的 p_button_opr 中的 init、read 函数操作硬件。
board_xxx.c 分配/设置/注册 button_operations 结构体
这个结构体是我们自己抽象出来的,里面定义单板 xxx 的按键操作函数。
这样的结构易于扩展,对于不同的单板,只需要替换 board_xxx.c 提供自
己的 button_operations 结构体即可。
程序编写
button_test.c
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <stdio.h> #include <string.h> /* * ./button_test /dev/100ask_button0 * */ int main(int argc, char **argv) { int fd; char val; /* 1. 判断参数 */ if (argc != 2) { printf("Usage: %s <dev>\n", argv[0]); return -1; } /* 2. 打开文件 */ fd = open(argv[1], O_RDWR); if (fd == -1) { printf("can not open file %s\n", argv[1]); return -1; } /* 3. 写文件 */ read(fd, &val, 1); printf("get button : %d\n", val); close(fd); return 0; }
主要是对按键设备的打开和关闭
button_drv.h
#ifndef _BUTTON_DRV_H #define _BUTTON_DRV_H struct button_operations { int count; void (*init) (int which); int (*read) (int which); }; void register_button_operations(struct button_operations *opr); void unregister_button_operations(void); #endif
主要是实现了button_operations结构体的声明和register_button_operations函数和unregister_button_operations的声明
button_drv.c
#include <linux/module.h> #include <linux/errno.h> #include <linux/kernel.h> #include <linux/major.h> #include <linux/sched.h> #include <linux/slab.h> #include <linux/fcntl.h> #include <linux/fs.h> #include <linux/signal.h> #include <linux/mutex.h> #include <linux/mm.h> #include <linux/timer.h> #include <linux/wait.h> #include <linux/skbuff.h> #include <linux/proc_fs.h> #include <linux/poll.h> #include <linux/capi.h> #include <linux/kernelcapi.h> #include <linux/init.h> #include <linux/device.h> #include <linux/moduleparam.h> #include "button_drv.h" static int major = 0; static struct button_operations *p_button_opr; static struct class *button_class; static int button_open (struct inode *inode, struct file *file) { int minor = iminor(inode); p_button_opr->init(minor); return 0; } static ssize_t button_read (struct file *file, char __user *buf, size_t size, loff_t *off) { unsigned int minor = iminor(file_inode(file)); char level; int err; level = p_button_opr->read(minor); err = copy_to_user(buf, &level, 1); return 1; } static struct file_operations button_fops = { .open = button_open, .read = button_read, }; void register_button_operations(struct button_operations *opr) { int i; p_button_opr = opr; for (i = 0; i < opr->count; i++) { device_create(button_class, NULL, MKDEV(major, i), NULL, "100ask_button%d", i); } } void unregister_button_operations(void) { int i; for (i = 0; i < p_button_opr->count; i++) { device_destroy(button_class, MKDEV(major, i)); } } EXPORT_SYMBOL(register_button_operations); EXPORT_SYMBOL(unregister_button_operations); int button_init(void) { major = register_chrdev(0, "100ask_button", &button_fops); button_class = class_create(THIS_MODULE, "100ask_button"); if (IS_ERR(button_class)) return -1; return 0; } void button_exit(void) { class_destroy(button_class); unregister_chrdev(major, "100ask_button"); } module_init(button_init); module_exit(button_exit); MODULE_LICENSE("GPL");
在button_drv.c函数中主要是实现了file_operations button_fops结构体。并注册button_fops结构体,注册名为100ask_button,而硬件信息则在register_button_operations函数里面讲opr赋值给p_button_opr结构体,这个函数声明为外部函数。外部函数的意思是其他.c文件公用同一个函数。
board_xxx.c
#include <linux/module.h> #include <linux/errno.h> #include <linux/kernel.h> #include <linux/major.h> #include <linux/sched.h> #include <linux/slab.h> #include <linux/fcntl.h> #include <linux/fs.h> #include <linux/signal.h> #include <linux/mutex.h> #include <linux/mm.h> #include <linux/timer.h> #include <linux/wait.h> #include <linux/skbuff.h> #include <linux/proc_fs.h> #include <linux/poll.h> #include <linux/capi.h> #include <linux/kernelcapi.h> #include <linux/init.h> #include <linux/device.h> #include <linux/moduleparam.h> #include "button_drv.h" static void board_xxx_button_init_gpio (int which) { printk("%s %s %d, init gpio for button %d\n", __FILE__, __FUNCTION__, __LINE__, which); } static int board_xxx_button_read_gpio (int which) { printk("%s %s %d, read gpio for button %d\n", __FILE__, __FUNCTION__, __LINE__, which); return 1; } static struct button_operations my_buttons_ops ={ .count = 2, .init = board_xxx_button_init_gpio, .read = board_xxx_button_read_gpio, }; int board_xxx_button_init(void) { register_button_operations(&my_buttons_ops); return 0; } void board_xxx_button_exit(void) { unregister_button_operations(); } module_init(board_xxx_button_init); module_exit(board_xxx_button_exit); MODULE_LICENSE("GPL");
注册button_operations my_buttons_ops这个结构体,并实现board_xxx_button_init_gpio 和board_xxx_button_read_gpio的功能。