kpp.c --------------------------------------------- #include <linux/kernel.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/slab.h> #include <linux/init.h> #include <linux/list.h> #include <linux/ioport.h> #include <linux/io.h> #include <linux/interrupt.h> #include <linux/input.h> #include <linux/fs.h> #include <linux/cdev.h> #include <linux/poll.h> #include <linux/irq.h> #include <linux/delay.h> #include <linux/workqueue.h> #include <linux/jiffies.h> #include <asm/uaccess.h> #include <asm/irq.h> #include <asm/arch-magus/hardware.h> #include <asm/arch-magus/irqs.h> #include <asm/gpio.h> #include <asm/param.h> #define KPPMAJOR 240 #define MODULE_NAME "kpp" #define HOLD_IRQ MAGUS_GPIO_HOLD #define KPP_GET _IOR('f', 2, int) //<<<<<Global Variable struct kpp_dev { struct cdev cdev; struct fasync_struct *async_queue; }; struct kpp_dev *kpp_devp; //work queue struct struct work_struct hold_wq; void hold_do_work(void); static int gvalue; static struct class *hold_class; static int number; void hold_do_work(void) { int state; struct kpp_dev *dev = kpp_devp; state = gpio_get_value(HOLD_IRQ); if (state == 1) { gvalue = 10; number++; if (number == 1) { if (dev->async_queue) kill_fasync(&dev->async_queue, SIGIO, POLL_IN); } schedule_work(&hold_wq); } else { gvalue = -10; number = 0; enable_irq(gpio_to_irq(HOLD_IRQ)); if (dev->async_queue) kill_fasync(&dev->async_queue, SIGIO, POLL_IN); } msleep(100); } static irqreturn_t hold_isr(int irq, void *dev_id) { int state; mdelay(5); state = gpio_get_value(HOLD_IRQ); if (state == 1) { disable_irq(irq); schedule_work(&hold_wq); } return IRQ_HANDLED; } static int kpp_fasync(int fd, struct file *filp, int mode) { struct kpp_dev *dev = filp->private_data; return fasync_helper(fd, filp, mode, &dev->async_queue); } static int kpp_release(struct inode *inode, struct file *filp) { kpp_fasync(-1, filp, 0); return 0; } static int kpp_open(struct inode *inode, struct file *filp) { filp->private_data = kpp_devp; return 0; } static int kpp_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { int value = gvalue; switch (cmd) { case KPP_GET: put_user(value, (int __user *) arg); break; default: return -ENOTTY; } return 0; } static struct file_operations g_kpp_fops = { .owner = THIS_MODULE, .ioctl = kpp_ioctl, .fasync = kpp_fasync, .open = kpp_open, .release = kpp_release, }; static void kpp_setup_cdev(struct kpp_dev *dev, int index) { int devno = MKDEV(KPPMAJOR, index); cdev_init(&dev->cdev, &g_kpp_fops); dev->cdev.owner = THIS_MODULE; dev->cdev.ops = &g_kpp_fops; cdev_add(&dev->cdev, devno, 1); } static dev_t devno; static void register_kpp(void) { devno = MKDEV(KPPMAJOR, 0); register_chrdev_region(devno, 1, MODULE_NAME); kpp_devp = kmalloc(sizeof (struct kpp_dev), GFP_KERNEL); memset(kpp_devp, 0, sizeof (struct kpp_dev)); kpp_setup_cdev(kpp_devp, 0); hold_class = class_create(THIS_MODULE, "hold_class"); if (IS_ERR(hold_class)) { printk("<1> create class error/n"); return -1; } class_device_create(hold_class, NULL, devno, NULL, "hold"); } static int __init kpp_init(void) { int hold_irq; register_kpp(); /*register hold-key irq interrupt */ hold_irq = gpio_to_irq(HOLD_IRQ); set_irq_type(hold_irq, IRQ_TYPE_EDGE_RISING); if (request_irq(hold_irq, hold_isr, IRQF_SAMPLE_RANDOM, "hold-key", NULL)) { printk(KERN_ERR "Request HOLD IRQ error !/n"); return -EINVAL; } /*init work queue for hold irq */ INIT_WORK(&hold_wq, (void (*)(void *)) hold_do_work); printk("Solomon Systech Limited/n"); return 0; } static void __exit kpp_cleanup(void) { int hold_irq = gpio_to_irq(HOLD_IRQ); free_irq(hold_irq, NULL); cdev_del(&kpp_devp->cdev); unregister_chrdev_region(devno, 1); class_device_destroy(hold_class, devno); class_destroy(hold_class); } module_init(kpp_init); module_exit(kpp_cleanup); MODULE_DESCRIPTION("Magus Keypad Driver"); MODULE_LICENSE("GPL"); kpptest.c --------------------------------------------- #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <errno.h> #include <stdlib.h> #include <linux/ioctl.h> #include <stdio.h> #include <signal.h> #include <unistd.h> #define KPP_GET _IOR('f', 2, int) static int fd; void gpiovol_handler(int sigum) { int value; ioctl(fd, KPP_GET, &value); printf("the value = %d./n", value); } int main() { int oflags; fd = open("/dev/hold", O_RDWR); if (fd < 0) { return -1; } // 当按下hold键后,hold driver将向该进程发送SIGIO信号 signal(SIGIO, gpiovol_handler); // F_SETOWN设置接收SIGIO和SIGURG信号的进程ID或进程组ID.正的 // arg指定一个进程ID,负的arg表示等于arg绝对值的一个进程组ID fcntl(fd, F_SETOWN, getpid()); oflags = fcntl(fd, F_GETFL); fcntl(fd, F_SETFL, oflags | FASYNC); // (1) while (1) ; return 0; } (1) 当一个文件的FASYNC标志变化时(调用fcntl()函数,设置FASYNC文件标志时),该文件所对应的设备驱动的fasync()接口将被调用。 驱动程序向用户程序发信号 --------------------------------------------- 当设备有IO事件发生,就有机制保证向应用进程发送信号,显然设备驱动程序扮演重要角色,实际终端tty、网络socket等的标准实现已经包括了实时信号驱动的支持,所以,在Linux中它们可以如上直接使用。但有些设备的驱动程序还并没有支持,所以需要定制设备驱动程序。以下两个API应该是可以屏蔽所有相关琐碎操作(类似send_sig())的标准接口: int fasync_helper(int fd, struct file *filp, int mode, struct fasync_struct **fa); void kill_fasync(struct fasync_struct **fa, int sig, int band); 如果需要支持异步通知机制,如下设备结构中需要有异步事件通知队列(它应该与睡眠队列类似),并且增加fasync()接口的实现(该函数将本进程登记到async_queue上去)。 当一个打开的文件FASYNC标志变化时(调用fcntl()函数,设置FASYNC文件标志时),fasync()接口将被调用。 struct kpp_dev { struct cdev cdev; struct fasync_struct *async_queue; }; static int kpp_fasync(int fd, struct file *filp, int mode) { struct kpp_dev *dev = filp->private_data; return fasync_helper(fd, filp, mode, &dev->async_queue); } 事件发生的时机,就是中断服务程序或相应的软中断中调用kill_fasync(): if (dev->async_queue) kill_fasync(&dev->async_queue, SIGIO, POLL_IN); 如果是写操作,就是POLL_OUT。注意,无论用户进程设定了什么期望的信号,在这个环节,发送的一般就是SIGIO。注意在设备文件关闭(release方法)时,注意执行fasync(),使得本文件的操作从上述的设备异步事件等待链表中剥离。 static int kpp_release(struct inode *inode, struct file *filp) { kpp_fasync(-1, filp, 0); return 0; } |