// parport.c /* Function: It implement parport read and write method. In this demon, interrupt is also respond. -------------------------------------------------------- How to use NOTE: In order to trigger a parport interrupt. First, you can connect parport's PIN10 with PIN9. Second, Write PIN9 with 0 and later write PIN9 with 1. This can trigger a interrupt. # echo "" > /var/log/messages //clear file # insmod parport.ko # cat /var/log/messages you can see the Major assume it's 252 # mknod /dev/myp c 252 0 // # cat /proc/devices ... 252 my_parport ... In a new terminal: # echo -ne "/x00" > /dev/myp # echo -ne "/x80" > /dev/myp # cat /var/log/messages // you can see the notify info. as follows: May 27 04:40:05 joseph kernel: [ 623.834808] my_parport: major is 252 May 27 04:40:05 joseph kernel: [ 623.835002] result is 0 May 27 04:40:05 joseph kernel: [ 623.835094] irq request success May 27 04:40:05 joseph kernel: [ 623.835166] my_p_init_module called success May 27 04:40:53 joseph kernel: [ 672.117023] my_p_open called success May 27 04:40:53 joseph kernel: [ 672.117045] my_p_write is entered May 27 04:40:53 joseph kernel: [ 672.117047] write parport:0 May 27 04:41:09 joseph kernel: [ 688.270048] my_p_open called success May 27 04:41:09 joseph kernel: [ 688.270070] my_p_write is entered May 27 04:41:09 joseph kernel: [ 688.270073] write parport:-1 May 27 04:41:09 joseph kernel: [ 688.270079] my_irq_handler called <---------<<<<<< */ /********************************************************** This is a simple implementation of parport. This module only suport read and write mothed. ********************************************************/ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/kernel.h> /* printk(), min() */ #include <linux/slab.h> /* kmalloc() */ #include <linux/fs.h> /* everything... */ #include <linux/proc_fs.h> #include <linux/errno.h> /* error codes */ #include <linux/types.h> /* size_t */ #include <linux/fcntl.h> #include <linux/poll.h> #include <linux/cdev.h> #include <asm/uaccess.h> #include <linux/wait.h> /* wait_event_interruptible() */ #include <asm/semaphore.h> /* down_interruptible() up() */ //#include <linux/semaphore.h> #include <linux/sched.h> //error: ‘TASK_INTERRUPTIBLE’ undeclared #include <asm/io.h> // inb_p(), outb_p() #include <linux/interrupt.h> #define MYDEBUG 1 //turn on debug info printing switch //#define BUF_SIZE 1024 //pipe's buffer size // print debug infomation #define print_debug_info() / do { / printk (KERN_NOTICE " %s called success /n", __func__);/ }while (0) struct my_parport { // wait_queue_head_t inq, outq; // read and write queue int nreaders, nwriters; // number of openings for r/w struct fasync_struct *async_r_queue; //asynchronous reader queue struct semaphore sem;//mutual exclusion semaphore struct cdev cdev;// Char device structure }; int mydev_major, mydev_minor;//record the device major and minor number struct my_parport * my_p_dev; // pointer to our device //declaration of functions void my_p_clearup_module (void); int my_p_setup_cdev(struct my_parport* my_p_dev, dev_t devno); int my_p_init_module (void); static int my_p_open(struct inode *inode, struct file *filp); static int my_p_release(struct inode *inode, struct file *filp); static ssize_t my_p_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos); static ssize_t my_p_read (struct file *filp, char __user *buf, size_t count, loff_t *f_pos); irqreturn_t my_irq_handler(int irq, void* dev_id); struct file_operations my_p_fops = { .owner = THIS_MODULE, .llseek = no_llseek, .read = my_p_read, .write = my_p_write, .poll = 0, .ioctl = 0, .open = my_p_open, .release = my_p_release, .fasync = 0, }; /* allocate device number. */ int my_p_init_module (void) { int result = 0; u8 data = 0; dev_t dev; result = alloc_chrdev_region(&dev, 0/*minor*/, 1/*dev nums*/,"my_parport"); mydev_major = MAJOR(dev);//store the major and minor number of our device mydev_minor = MINOR(dev); if (result < 0) { printk (KERN_WARNING "my_parport: alloc major error!/n"); return result; } #ifdef MYDEBUG else { printk (KERN_WARNING "my_parport: major is %d/n", mydev_major); } #endif // allocate device structure my_p_dev = kmalloc (sizeof(struct my_parport), GFP_KERNEL); if (!my_p_dev) { printk (KERN_WARNING "my_parport: alloc struct my_pipe error!/n"); goto fail; } //init our device items memset (my_p_dev, 0, sizeof(my_p_dev)); // init_waitqueue_head(&my_p_dev->inq); // init_waitqueue_head(&my_p_dev->outq); init_MUTEX(&my_p_dev->sem); //my_p_dev->buffer = NULL;//This is very important if (my_p_setup_cdev(my_p_dev, dev) < 0) { printk (KERN_NOTICE "my_p_setup_cdev called error/n"); goto fail; } result = request_irq(7, my_irq_handler, IRQF_DISABLED, "joseph", NULL); if (result) { printk(KERN_NOTICE "irq request failed/n"); goto fail; } else { printk(KERN_NOTICE "irq request success/n"); } data = 0x10;//inb_p(0x378 + 2);//read control register outb_p(data /*| 1 << 4*/, 0x378 + 2);//enable parport interrupt // read only// outb_p(0, 0x378 + 1);// status register clear #ifdef MYDEBUG print_debug_info(); // current function success #endif return 0; fail: my_p_clearup_module(); return result; } /* Init cdev structure and add it to kernel. After this call, our cdev is 'active'. */ int my_p_setup_cdev(struct my_parport* my_p_dev, dev_t devno) { int result; cdev_init(&my_p_dev->cdev, &my_p_fops);// init cdev structure my_p_dev->cdev.owner = THIS_MODULE; my_p_dev->cdev.ops = &my_p_fops; result = cdev_add (&my_p_dev->cdev, devno, 1);// Notify the kernel information // about our device printk(KERN_NOTICE "result is %d/n", result); if (result < 0) { printk (KERN_NOTICE "my_p_dev setup error! /n"); return -EFAULT; } #ifdef MYDEGUG print_debug_info();//success #endif return result; } /* free the device struct */ void my_p_clearup_module (void) { dev_t devno = MKDEV(mydev_major, mydev_minor); free_irq(7, NULL); //remove cdev from the system cdev_del (&my_p_dev->cdev); // unregister charactor number region unregister_chrdev_region(devno, 1); //here we should free the device struct kfree (my_p_dev); #ifdef MYDEGUG print_debug_info();//success #endif } /*IRQs: */ irqreturn_t my_irq_handler(int irq, void* dev_id/*, struct pt_regs *regs*/) { printk(KERN_NOTICE "%s called <---------<<<<<</n", __func__); return IRQ_HANDLED; } /* Store device's pointer in filp's private_data element. This can convenient the access of our device pointer. */ static int my_p_open(struct inode *inode, struct file *filp) { struct my_parport *dev; dev = container_of(inode->i_cdev, struct my_parport, cdev);//get and //save device's pointer filp->private_data = dev; //#ifdef MYDEGUG print_debug_info();//success //#endif return nonseekable_open(inode, filp);//don't want the seekable file descriptor } /* UNINSTALL IRQs */ static int my_p_release(struct inode *inode, struct file *filp) { struct my_pipe *dev; dev = filp->private_data; // get device pointer #ifdef MYDEGUG print_debug_info();//success #endif return 0; } static ssize_t my_p_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) { struct my_parport* dev = filp->private_data; int i = 0; dev = dev; printk(KERN_NOTICE "%s is entered/n", __func__); while (i < count) { if (down_interruptible(&(dev->sem))){ return -ERESTARTSYS; } //write the parport printk(KERN_NOTICE "write parport:%d/n", (int)*(buf + i)); outb_p(*(buf + i), 0x378); up(&dev->sem); i++; } #ifdef MYDEGUG print_debug_info();//success #endif return count; } static ssize_t my_p_read (struct file *filp, char __user *buf, size_t count, loff_t *f_pos) { struct my_parport* dev = filp->private_data; int data = 0; // int max_num = 0; dev = dev; printk(KERN_NOTICE "%s is entered/n", __func__); if (down_interruptible(&dev->sem)){ return -ERESTARTSYS; } data = (int)inb_p(0x378 + 1); copy_to_user(buf, (void*)&data, 1); printk(KERN_NOTICE "parport status register:%x/n", data); data =(int) inb_p(0x378 + 2); printk(KERN_NOTICE "parport control register:%x/n", data); up(&dev->sem); print_debug_info();//success //#endif return count; } MODULE_LICENSE("Dual BSD/GPL"); MODULE_AUTHOR("author:joseph@dslab; mail: [email protected]"); module_init(my_p_init_module); module_exit(my_p_clearup_module);