#include <linux/module.h>#include <linux/kernel.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/delay.h> #include <asm/irq.h> #include <linux/interrupt.h> #include <asm/uaccess.h> #include <asm/arch/regs-gpio.h> #include <asm/hardware.h> #include <linux/poll.h>/* 加载模式后,执行”cat /proc/devices”命令看到的设备名称 */ #define DEVICE_NAME "key_poll"/*自动创建设备节点类*/ struct class *key_poll_dev_class = NULL; struct class_device *key_poll_dev_class_dev = NULL;/* 用来指定按键所用的外部中断引脚及中断触发方式, 名字 */ struct button_irq_desc { int irq; unsigned long flags; char *name; }; static struct button_irq_desc button_irqs [] = { {IRQ_EINT19, IRQF_TRIGGER_FALLING, "key_poll1"}, /* K1 */ {IRQ_EINT11, IRQF_TRIGGER_FALLING, "key_poll2"}, /* K2 */ {IRQ_EINT2, IRQF_TRIGGER_FALLING, "key_poll3"}, /* K3 */ {IRQ_EINT0, IRQF_TRIGGER_FALLING, "key_poll4"}, /* K4 */ };/* 按键被按下的次数(准确地说,是发生中断的次数) */ static volatile int press_cnt [] = {0, 0, 0, 0};/* 等待队列: * 当没有按键被按下时,如果有进程调用key_poll_dev_read函数, * 它将休眠 */ static DECLARE_WAIT_QUEUE_HEAD(button_waitq);/* 中断事件标志, 中断服务程序将它置1,key_poll_dev_read将它清0 */ static volatile int ev_press = 0; static irqreturn_t buttons_interrupt(int irq, void *dev_id) { volatile int *press_cnt = (volatile int *)dev_id; *press_cnt = *press_cnt + 1; /* 按键计数加1 */ ev_press = 1; /* 表示中断发生了 */ wake_up_interruptible(&button_waitq); /* 唤醒休眠的进程 */ return IRQ_RETVAL(IRQ_HANDLED); }static int key_poll_dev_open(struct inode *inode, struct file *file) { int i; int err; for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++){ // 注册中断处理函数 err = request_irq(button_irqs[i].irq, buttons_interrupt, button_irqs[i].flags, button_irqs[i].name, (void *)&press_cnt[i]); if (err) break; } if (err){ // 释放已经注册的中断 i--; for (; i >= 0; i--) free_irq(button_irqs[i].irq, (void *)&press_cnt[i]); return -EBUSY; } return 0; }static int key_poll_dev_close(struct inode *inode, struct file *file) { int i; for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) { // 释放已经注册的中断 free_irq(button_irqs[i].irq, (void *)&press_cnt[i]); } return 0; }/* 应用程序对设备文件/dev/buttons执行read(...)时, * 就会调用key_poll_dev_read函数 */ static int key_poll_dev_read(struct file *filp, char __user *buff, size_t count, loff_t *offp) { unsigned long err = 0; /* 如果ev_press等于0,休眠 */ wait_event_interruptible(button_waitq, ev_press); /* 执行到这里时,ev_press等于1,将它清0 */ ev_press = 0; /* 将按键状态复制给用户*/ err = copy_to_user(buff, (const void *)press_cnt, min(sizeof(press_cnt), count)); //memset((void *)press_cnt, 0, sizeof(press_cnt));return err ? -EFAULT : 0; }static int key_poll_dev_poll(struct file *file, poll_table *wait) { unsigned int mask = 0; /* void poll_wait(struct file *filp, wait_queue_head_t *queue, poll_table *wait); typedef struct poll_table_struct { poll_queue_proc qproc; unsigned long key; } poll_table; poll_wait的作用就是把当前进程添加到指定等待列表wait(poll_table)中。 需要注意的是这个函数是不会引起阻塞的,呵呵,谁给它取得个名字带wait的,给咱们添这么多麻烦。 */ poll_wait(file, &button_waitq, wait); if (ev_press) mask |= POLLIN | POLLRDNORM; /* POLLIN 普通或优先级带数据可读 POLLRDNORM 普通数据可读 POLLRDBAND 优先级带数据可读 POLLPRI 高优先级数据可读 POLLOUT 普通数据可写 POLLWRNORM 普通数据可写 POLLWRBAND 优先级带数据可写 POLLERR 发生错误 POLLHUP 发生挂起 POLLNVAL 描述字不是一个打开的文件 */ return mask; } /* 这个结构是字符设备驱动程序的核心 * 当应用程序操作设备文件时所调用的open、read、write等函数, * 最终会调用这个结构中的对应函数 */ static struct file_operations key_poll_dev_fops = { .owner = THIS_MODULE, /* 这是一个宏,指向编译模块时自动创建的__this_module变量 */ .open = key_poll_dev_open, .release = key_poll_dev_close, .read = key_poll_dev_read, .poll = key_poll_dev_poll, }; /* * 执行“insmod key_poll_driver.ko”命令时就会调用这个函数 */ int key_poll_major = 0; int key_poll_minor = 0; static int __init initialization_key_poll_dev(void) { /* 注册字符设备驱动程序 * 参数为主设备号、设备名字、file_operations结构; * 这样,主设备号就和具体的file_operations结构联系起来了, * 操作主设备为key_poll_major的设备文件时,就会调用key_poll_dev_fops中的相关成员函数 * key_poll_MAJOR可以设为0,表示由内核自动分配主设备号 */ printk("Before register Major = %d\n", key_poll_major); if (key_poll_major) register_chrdev(key_poll_major, DEVICE_NAME, &key_poll_dev_fops); else key_poll_major = register_chrdev(0, DEVICE_NAME, &key_poll_dev_fops); printk("After register Major = %d\n", key_poll_major); /* 自动生成设备节点 */ key_poll_dev_class = class_create(THIS_MODULE, "key_poll_drv"); key_poll_dev_class_dev = class_device_create(key_poll_dev_class, NULL, MKDEV(key_poll_major, key_poll_minor), NULL, "key_poll%d", key_poll_minor); printk(DEVICE_NAME " initialized OK.\n"); return 0; } /*执行”rmmod key_poll_dev.ko”命令时就会调用这个函数*/ static void __exit cleanup_key_poll_dev(void) { /* 卸载驱动程序 */ unregister_chrdev(key_poll_major, DEVICE_NAME); class_device_unregister(key_poll_dev_class_dev); class_destroy(key_poll_dev_class); printk(DEVICE_NAME " cleanup OK.\n"); } /* 这两行指定驱动程序的初始化函数和卸载函数 */ module_init(initialization_key_poll_dev); module_exit(cleanup_key_poll_dev); /*insmod传参*/ module_param(key_poll_major, int, S_IRUGO); module_param(key_poll_minor, int, S_IRUGO); /* 描述驱动程序的一些信息,不是必须的 */ MODULE_AUTHOR("lhbo"); // 驱动程序的作者 MODULE_DESCRIPTION("JZ2440 key_poll Driver"); // 一些描述信息 MODULE_LICENSE("GPL"); // 遵循的协议 /*===========================异步通知驱动测试=========================*/ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/ioctl.h> #include <poll.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int main(int argc, char **argv) { int i; int ret; int fd; int press_cnt[4]; struct pollfd fds[1]; fd = open("/dev/key_poll", 0); // 打开设备 if (fd < 0){ printf("Can‘t open /dev/key\n"); return -1; } fds[0].fd = fd; fds[0].events = POLLIN; /* 这是个无限循环,进程可能在read函数中休眠,当有按键被按下时,它才返回 */ while (1){ int ret = poll(fds, 1, 5000); //5000ms 1个文件 /* 执行上面这条语句后,如果在5s内没有按键,则进程在这5s内被阻塞在poll处,5s之后返回0,否则poll立即返回非0值。 */ if (ret == 0){ printf("Time out!\n"); }else{ /* 读出按键被按下的次数*/ret = read(fd, press_cnt, sizeof(press_cnt)); if (ret < 0){ printf("read err!\n"); continue; } /*读取成功打印按键次数*/ for (i = 0; i < 4; i++) printf("K%d has been pressed %d times!\n", i+1, press_cnt[i]); } } close(fd); return 0; }
linux驱动程序中的poll机制编程,布布扣,bubuko.com
时间: 2024-09-29 03:11:38