Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /*
- * Course :CSE-326 System Programming Lab
- *
- * Assignment No :03
- * Assignment Name :Kernel Module Programming by creating a simple character device driver
- * Module name :sakin_dev.ko
- * Devices :sakin-0, sakin-1
- * Plateform :kernel 2.6.38.6 or later
- * Compiling the module: make (By running Makefile)
- * Create the necessary devices and insert the module by executing: ./sakin_load
- * To remove the module and to delete the devices execute: ./sakin_unload
- *
- * Author :Sayef Azad Sakin
- * Roll :1563
- * Language :c
- */
- #ifndef __KERNEL__
- # define __KERNEL__
- #endif
- #ifndef MODULE
- # define MODULE
- #endif
- #include <linux/module.h>
- #include <linux/moduleparam.h>
- #include <linux/init.h>
- #include <linux/slab.h> /* kmalloc() */
- #include <linux/kernel.h> /* printk(), min() */
- #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> /* O_ACCMODE */
- #include <linux/wait.h>
- #include <linux/cdev.h>
- #include <asm/uaccess.h> /* copy_*_user */
- #include <linux/semaphore.h>
- #include <asm/system.h> /* cli(), *_flags */
- #include <linux/sched.h>
- #define DEVICE_NAME "sakin_dev"
- #define MAJOR_NUMBER 249
- #define SAKIN_NR_DEVS 2
- #define MIN_MINOR_NUMBER 0
- #define MAX_MINOR_NUMBER 1
- #define DEVICE_COUNT 2
- #define BUFFER_SIZE 1024
- #define PDEBUG(fmt, args...) printk( KERN_DEBUG "sakin: " fmt, ## args)
- MODULE_AUTHOR("Sayef Azad Sakin");
- MODULE_LICENSE("GPL");
- MODULE_DESCRIPTION("A simple character device driver.");
- MODULE_SUPPORTED_DEVICE(DEVICE_NAME);
- char *buffer0, *end0, *buffer1, *end1; /* begin of buf, end of buf */
- char *rp0, *wp0, *rp1, *wp1; /* where to read, where to write */
- struct sakin_dev {
- wait_queue_head_t inq, outq; /* read and write queues */
- int buffersize; /* used in pointer arithmetic */
- int nreaders, nwriters; /* number of openings for r/w */
- struct semaphore sem; /* mutual exclusion semaphore */
- struct cdev cdev; /* Char device structure */
- };
- dev_t sakin_devno;
- static struct sakin_dev *sakin_devices;
- int sakin_init_module( void ) ;
- void sakin_cleanup_module( void );
- static int sakin_open( struct inode*, struct file* );
- static int sakin_release( struct inode*, struct file* );
- static ssize_t sakin_read( struct file*, char*, size_t, loff_t* );
- static int sakin_getwritespace(struct sakin_dev *dev, struct file *filp, const int c_minor);
- static int spacefree(struct sakin_dev *dev, const int c_minor);
- static ssize_t sakin_write( struct file*, const char*, size_t, loff_t* );
- //int sakin_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);
- /* Called when a process tries to open the device file */
- static int sakin_open( struct inode *inode, struct file *filp ) {
- struct sakin_dev *dev;
- dev = container_of(inode->i_cdev, struct sakin_dev, cdev);
- filp->private_data = dev;
- if (down_interruptible(&dev->sem))
- return -ERESTARTSYS;
- if (!buffer0) {
- // allocate the buffer
- buffer0 = kmalloc(BUFFER_SIZE, GFP_KERNEL);
- if (!buffer0) {
- up(&dev->sem);
- return -ENOMEM;
- }
- dev->buffersize = BUFFER_SIZE;
- end0 = buffer0 + dev->buffersize;
- rp0 = wp0 = buffer0; // rd and wr from the beginning
- }
- if (!buffer1) {
- // allocate the buffer
- buffer1 = kmalloc(BUFFER_SIZE, GFP_KERNEL);
- if (!buffer1) {
- up(&dev->sem);
- return -ENOMEM;
- }
- dev->buffersize = BUFFER_SIZE;
- end1 = buffer1 + dev->buffersize;
- rp1 = wp1 = buffer1; // rd and wr from the beginning
- }
- dev->buffersize = BUFFER_SIZE;
- /* use f_mode,not f_flags: it's cleaner (fs/open.c tells why) */
- if (filp->f_mode & FMODE_READ)
- dev->nreaders++;
- if (filp->f_mode & FMODE_WRITE){
- if(dev->nwriters){
- up(&dev->sem);
- return -EACCES;
- }
- dev->nwriters++;
- }
- up(&dev->sem);
- return nonseekable_open(inode, filp);
- }
- /* Called when a process closes the device file. */
- static int sakin_release( struct inode *inode, struct file *filp ) {
- struct sakin_dev *dev = filp->private_data;
- down(&dev->sem);
- if (filp->f_mode & FMODE_READ)
- dev->nreaders--;
- if (filp->f_mode & FMODE_WRITE)
- dev->nwriters--;
- up(&dev->sem);
- return 0;
- }
- /* Called when a process, which already opened the dev file, attempts to read from it. */
- static ssize_t sakin_read( struct file *filp, char *buf, size_t count, loff_t *f_pos ) {
- struct sakin_dev *dev = filp->private_data;
- int c_minor = iminor(filp->f_dentry->d_inode);
- if(c_minor==1){
- if (down_interruptible(&dev->sem))
- return -ERESTARTSYS;
- while (rp1 == wp1) { /* nothing to read */
- up(&dev->sem); /* release the lock */
- if (filp->f_flags & O_NONBLOCK)
- return -EAGAIN;
- PDEBUG("\"%s\" reading: going to sleep %d in inq1\n", current->comm, c_minor);
- PDEBUG("read p %p write p %p && read p %p write p %p in reading", rp0, wp0, rp1, wp1);
- if (wait_event_interruptible(dev->inq, (rp1 != wp1)))
- return -ERESTARTSYS; /* signal: tell the fs layer to handle it */
- /* otherwise loop, but first reacquire the lock */
- PDEBUG("Wake up from inq1 having %d\n",c_minor);
- if (down_interruptible(&dev->sem))
- return -ERESTARTSYS;
- }
- /* ok, data is there, return something */
- if (wp1 > rp1)
- count = min(count, (size_t)(wp1 - rp1));
- else /* the write pointer has wrapped, return data up to dev->end */
- count = min(count, (size_t)(end1 - rp1));
- if (copy_to_user(buf, rp1, count)) {
- up (&dev->sem);
- return -EFAULT;
- }
- rp1 += count;
- if (rp1 == end1)
- rp1 = buffer1; /* wrapped */
- up (&dev->sem);
- /* finally, awake any writers and return */
- wake_up_interruptible(&sakin_devices[0].outq);
- PDEBUG("\"%s\" did read %li bytes %d\n",current->comm, (long)count, c_minor);
- }
- else if(c_minor==0){
- if (down_interruptible(&dev->sem))
- return -ERESTARTSYS;
- while (rp0 == wp0) { /* nothing to read */
- up(&dev->sem); /* release the lock */
- if (filp->f_flags & O_NONBLOCK)
- return -EAGAIN;
- PDEBUG("\"%s\" reading: going to sleep %d in inq0\n", current->comm, c_minor);
- PDEBUG("read p %p write p %p && read p %p write p %p in reading", rp0, wp0, rp1, wp1);
- if (wait_event_interruptible(dev->inq, (rp0 != wp0)))
- return -ERESTARTSYS; /* signal: tell the fs layer to handle it */
- /* otherwise loop, but first reacquire the lock */
- PDEBUG("Wake up from inq0 having %d\n",c_minor);
- if (down_interruptible(&dev->sem))
- return -ERESTARTSYS;
- }
- /* ok, data is there, return something */
- if (wp0 > rp0)
- count = min(count, (size_t)(wp0 - rp0));
- else /* the write pointer has wrapped, return data up to dev->end */
- count = min(count, (size_t)(end0 - rp0));
- if (copy_to_user(buf, rp0, count)) {
- up (&dev->sem);
- return -EFAULT;
- }
- rp0 += count;
- if (rp0 == end0)
- rp0 = buffer0; /* wrapped */
- up (&dev->sem);
- /* finally, awake any writers and return */
- wake_up_interruptible(&sakin_devices[1].outq);
- PDEBUG("\"%s\" did read %li bytes %d\n",current->comm, (long)count, c_minor);
- }
- return count;
- }
- /* Wait for space for writing; caller must hold device semaphore. On
- * error the semaphore will be released before returning. */
- static int sakin_getwritespace(struct sakin_dev *dev, struct file *filp, const int c_minor)
- {
- while (spacefree(dev, c_minor) == 0) { /* full */
- DEFINE_WAIT(wait);
- up(&dev->sem);
- if (filp->f_flags & O_NONBLOCK)
- return -EAGAIN;
- PDEBUG("\"%s\" writing: going to sleep\n",current->comm);
- prepare_to_wait(&dev->outq, &wait, TASK_INTERRUPTIBLE);
- if (spacefree(dev, c_minor) == 0)
- schedule();
- finish_wait(&dev->outq, &wait);
- if (signal_pending(current))
- return -ERESTARTSYS; /* signal: tell the fs layer to handle it */
- if (down_interruptible(&dev->sem))
- return -ERESTARTSYS;
- }
- return 0;
- }
- /* How much space is free? */
- static int spacefree(struct sakin_dev *dev, const int c_minor)
- {
- if(c_minor==0){
- if (rp1 == wp1)
- return dev->buffersize - 1;
- return ((rp1 + dev->buffersize - wp1) % dev->buffersize) - 1;
- }
- if (rp0 == wp0)
- return dev->buffersize - 1;
- return ((rp0 + dev->buffersize - wp0) % dev->buffersize) - 1;
- }
- static ssize_t sakin_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos){
- struct sakin_dev *dev = filp->private_data;
- int c_minor = iminor(filp->f_dentry->d_inode);
- int result;
- if (down_interruptible(&dev->sem))
- return -ERESTARTSYS;
- /* Make sure there's space to write */
- result = sakin_getwritespace(dev, filp, c_minor);
- if (result)
- return result; /* sakin_getwritespace called up(&dev->sem) */
- if(c_minor==0){
- /* ok, space is there, accept something */
- PDEBUG("Write is called in %d\n",c_minor);
- PDEBUG("read p %p write p %p && read p %p write p %p\n", rp0,wp0, rp1, wp1);
- count = min(count, (size_t)spacefree(dev, c_minor));
- if (wp1 >= rp1)
- count = min(count, (size_t)(end1 - wp1)); /* to end-of-buf */
- else /* the write pointer has wrapped, fill up to rp-1 */
- count = min(count, (size_t)(rp1 - wp1 - 1));
- PDEBUG("Going to accept %li bytes to %p from %p\n", (long)count, wp1, buf);
- if (copy_from_user(wp1, buf, count)) {
- up (&dev->sem);
- return -EFAULT;
- }
- wp1 += count;
- if (wp1 == end1)
- wp1 = buffer1; /* wrapped */
- up(&dev->sem);
- /* finally, awake any reader */
- PDEBUG("read p %p write p %p while writing", rp1, wp1);
- PDEBUG("Wake up from inq1 has %d\n",c_minor);
- wake_up_interruptible(&sakin_devices[1].inq); /* blocked in read() and select() */
- /* and signal asynchronous readers, explained late in chapter 5 */
- /*if (dev->async_queue)
- kill_fasync(&dev->async_queue, SIGIO, POLL_IN);*/
- PDEBUG("\"%s\" did write %li bytes %d\n",current->comm, (long)count, c_minor);
- }
- else if(c_minor==1){
- /* ok, space is there, accept something */
- PDEBUG("Write is called in %d\n",c_minor);
- count = min(count, (size_t)spacefree(dev, c_minor));
- if (wp0 >= rp0)
- count = min(count, (size_t)(end0 - wp0)); /* to end-of-buf */
- else /* the write pointer has wrapped, fill up to rp-1 */
- count = min(count, (size_t)(rp0 - wp0 - 1));
- PDEBUG("Going to accept %li bytes to %p from %p\n", (long)count, wp0, buf);
- if (copy_from_user(wp0, buf, count)) {
- up (&dev->sem);
- return -EFAULT;
- }
- wp0 += count;
- if (wp0 == end0)
- wp0 = buffer0; /* wrapped */
- up(&dev->sem);
- /* finally, awake any reader */
- PDEBUG("Wake up from inq0 has %d\n",c_minor);
- PDEBUG("read p %p write p %p while writing", rp0, wp0);
- wake_up_interruptible(&sakin_devices[0].inq); /* blocked in read() and select() */
- /* and signal asynchronous readers, explained late in chapter 5 */
- /*if (dev->async_queue)
- kill_fasync(&dev->async_queue, SIGIO, POLL_IN);*/
- PDEBUG("\"%s\" did write %li bytes %d\n",current->comm, (long)count, c_minor);
- }
- return count;
- }
- static struct file_operations sakin_fops = {
- .owner = THIS_MODULE,
- .read = sakin_read,
- .write = sakin_write,
- .open = sakin_open,
- .release = sakin_release,
- //.ioctl = sakin_ioctl
- };
- static void sakin_setup_cdev(struct sakin_dev *dev, int index){
- int err, devno = sakin_devno + index;
- cdev_init(&dev->cdev, &sakin_fops);
- dev->cdev.owner = THIS_MODULE;
- dev->cdev.ops = &sakin_fops;
- err = cdev_add (&dev->cdev, devno, 1);
- if (err)
- printk(KERN_NOTICE "Error %d adding sakin_dev %d", err, index);
- }
- int sakin_init_module( void ) {/* called when module is loaded */
- int result, i;
- dev_t dev = 0;
- dev = MKDEV(MAJOR_NUMBER, MIN_MINOR_NUMBER);
- result = register_chrdev_region(dev, SAKIN_NR_DEVS, DEVICE_NAME);
- printk(KERN_INFO "sakin: Hello, I am here in the kernel (Module loaded)\n");
- if (result < 0) {
- printk(KERN_NOTICE "Unable to get sakin_dev region, error %d\n", result);
- return 0;
- }
- sakin_devno = dev;
- sakin_devices = kmalloc(SAKIN_NR_DEVS * sizeof(struct sakin_dev), GFP_KERNEL);
- if (sakin_devices == NULL) {
- unregister_chrdev_region(dev, SAKIN_NR_DEVS);
- return 0;
- }
- memset(sakin_devices, 0, SAKIN_NR_DEVS * sizeof(struct sakin_dev));
- for (i = 0; i < SAKIN_NR_DEVS; i++) {
- init_waitqueue_head(&(sakin_devices[i].inq));
- init_waitqueue_head(&(sakin_devices[i].outq));
- //init_MUTEX(&sakin_devices[i].sem);
- //init_MUTEX(&sakin_devices[i].sem);
- sema_init(&sakin_devices[i].sem,1); //for kernel 2.6.38.6
- sakin_setup_cdev(sakin_devices + i, i);
- }
- return 0;
- }
- void sakin_cleanup_module( void ) {/* Called when module is unloaded */
- int i;
- if (!sakin_devices)
- return; /* nothing else to release */
- for (i = 0; i < SAKIN_NR_DEVS; i++) {
- cdev_del(&sakin_devices[i].cdev);
- }
- kfree(buffer0);
- kfree(buffer1);
- kfree(sakin_devices);
- unregister_chrdev_region(sakin_devno, SAKIN_NR_DEVS);
- sakin_devices = NULL; /* pedantic */
- printk(KERN_INFO "sakin: Goodbye (Module unloaded).\n");
- }
- module_init( sakin_init_module );
- module_exit( sakin_cleanup_module );
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement