#include #include #include #include #include #include #include #include #include #include #include
MODULE_AUTHOR('XIAOSHOU'); MODULE_LICENSE('GPL'); MODULE_DESCRIPTION('xiaoshou_IRQ driver test!');
//#define DEG //调试
#ifdef DEG #define DEBUG(msg,arg...) printk('kernel_debug message:#>: 'msg,##arg) #else #define DEBUG(msg,arg...) #endif
#define IO_DEL 0 #define CMD1 1 #define CMD2 2 #define DEV_COUNT 1 #define DEV_NAME 'IRQ_XSH'
#define MAX_BUF_LEN 16 #define SIZE MAX_BUF_LEN
#define INCBUF(x) (++(x)%(MAX_BUF_LEN)) #define DECBUF(x) (--(x)%(MAX_BUF_LEN)) struct device_xsh { unsigned int flag; unsigned char buf[MAX_BUF_LEN]; unsigned int head,tail; wait_queue_head_t wq; struct cdev irq_cdev; };
static int minor_num = 0; static int major_num = 0;
static struct device_xsh irq_device;
static struct class *irq_class;
ssize_t irq_read(struct file *filp,char __user *buf,size_t count,loff_t *f_ops); ssize_t irq_write(struct file *filp,const char __user *buf,size_t count,loff_t *f_ops); int irq_open(struct inode *inode,struct file *filp); int irq_release(struct inode *inode,struct file *filp); int irq_ioctl(struct inode *inode,struct file *filp,unsigned int cmd,unsigned long arg);
static irqreturn_t buttons_irq_isr(int irq,void *dev_id,struct pt_regs *req); //中断服务程序 static void do_task_irq(void); //任务队列的执行函数; DECLARE_TASKLET(task_irq, do_task_irq, 0); //中断服务下半部分的任务队列;
struct file_operations irq_fops = { .owner = THIS_MODULE, .read=irq_read, .write=irq_write, .open=irq_open, .release=irq_release, .ioctl=irq_ioctl, };
static int __init irq_init(void) { dev_t dev_num=0; int res; int err; unsigned long bitval ; //###########分配设备号-start#################### if(major_num) //手动分配设备号 { dev_num=MKDEV(major_num,minor_num); res=register_chrdev_region(dev_num,DEV_COUNT,DEV_NAME); } else //动态分配设备号 { res=alloc_chrdev_region(&dev_num,minor_num,DEV_COUNT,DEV_NAME); major_num=MAJOR(dev_num); }
if(res<0) { DEBUG( 'IRQ alloc_chrdev_region error!! '); return -1; } //#########//申请中断//######################## set_irq_type(IRQ_EINT3,IRQT_FALLING); //下降沿触发; res =request_irq(IRQ_EINT3,&buttons_irq_isr,SA_INTERRUPT,DEV_NAME,NULL); if(res<0) { DEBUG( 'request_irq error_num= %d!! ',res); return -1; } //###########分配设备号-end#################### //############设备初始化####################### memset(&irq_device,0,sizeof(struct device)); memset(irq_device.buf,0,sizeof(unsigned char)*SIZE); irq_device.flag=0; init_waitqueue_head(&(irq_device.wq)); //初始化队列; //############注册设备-start####################### cdev_init(&(irq_device.irq_cdev),&irq_fops);//cdev 结构嵌入一个自己的结构体需初始化 irq_device.irq_cdev.owner=THIS_MODULE; irq_device.irq_cdev.ops=&irq_fops; err=cdev_add(&(irq_device.irq_cdev),dev_num,1); if(err!=0) { DEBUG('IRQ cdev_add error!! '); } irq_class=class_create(THIS_MODULE, 'irq_class'); //udev自动创建/dev/设备文件 class_device_create(irq_class, NULL, dev_num, NULL, DEV_NAME); DEBUG('insmod char_irq success! '); return 0; } static void __exit irq_exit(void) { dev_t dev_num; dev_num=MKDEV(major_num,minor_num); cdev_del(&(irq_device.irq_cdev)); class_device_destroy(irq_class, dev_num); class_destroy(irq_class); free_irq(IRQ_EINT3,NULL); unregister_chrdev_region(dev_num,1); DEBUG('rmmod char_irq success! '); return ; }
int irq_open(struct inode *inode,struct file *filp) { struct device *my_device; try_module_get(THIS_MODULE); //表示此驱动被使用,记数器增加 my_device=container_of(inode->i_cdev,struct device_xsh,irq_cdev); filp->private_data=my_device; DEBUG('IRQ_char open success!! '); return 0; } int irq_release(struct inode *inode,struct file *filp) { DEBUG('IRQ_char release success!! '); module_put(THIS_MODULE); //表示此驱动使用完毕,记数器减一 //free_irq(IRQ_EINT3,NULL); return 0; }
ssize_t irq_read(struct file *filp,char __user *buf,size_t count,loff_t *f_ops) { int err; char tmp; retry: if(irq_device.head!=irq_device.tail) { tmp=irq_device.buf[irq_device.tail]; err=copy_to_user(buf,&tmp,1); DEBUG('irq_device.buf=0x%x ',irq_device.buf[irq_device.tail]); if(err!=0) { DEBUG('irq_char copy_to_user error!! '); return 0; } irq_device.tail=INCBUF(irq_device.tail); return 1; }else { if(filp->f_flags & O_NONBLOCK) { return -EAGAIN; } wait_event_interruptible(irq_device.wq,irq_device.flag); irq_device.flag=0; if(signal_pending(current)) { printk('rturn -ERESTARTSYS '); return -ERESTARTSYS; } goto retry; }
return 1; }
ssize_t irq_write(struct file *filp,const char __user *buf,size_t count,loff_t *f_ops) { int err; memset(irq_device.buf,0,sizeof(char)*SIZE); err=copy_from_user(irq_device.buf,buf,count); if (err!=0) { DEBUG('irq_char copy_from_user error!! '); return 0; } return count; }
int irq_ioctl(struct inode *inode,struct file *filp,unsigned int cmd,unsigned long arg) { switch(cmd) { case IO_DEL: DEBUG( 'ioctl IO_DEL success!'); break; case CMD1: DEBUG( 'kernel_debug : IO CMD1=%d ',CMD1); break; case CMD2: DEBUG( 'kernel_debug : IO CMD2=%d ',CMD2); break; default: return;
} } static irqreturn_t buttons_irq_isr(int irq,void *dev_id,struct pt_regs *req) { DEBUG( 'IRQ_ISR '); tasklet_schedule(&task_irq); //任务调度中断下半部分服务程序; return IRQ_HANDLED; //返回中断已经获得; } static void do_task_irq(void) { irq_device.buf[irq_device.head]='K'; DEBUG('irq_device.head=%d ',irq_device.head); DEBUG('irq_device.buf=0x%x ',irq_device.buf[irq_device.head]); irq_device.head=INCBUF(irq_device.head); irq_device.flag=1; wake_up_interruptible(&(irq_device.wq)); //DEBUG( 'do_task_irq '); } module_init(irq_init); module_exit(irq_exit);
|