diff options
author | Javier Martinez Canillas <martinez.javier@gmail.com> | 2010-11-27 07:49:17 +0100 |
---|---|---|
committer | Javier Martinez Canillas <martinez.javier@gmail.com> | 2010-11-27 07:49:17 +0100 |
commit | ab121f379a3cff458c90e6f480ba4bb68c8733dd (patch) | |
tree | a9851af109ee83646d108bc247d03b131461b764 /scullc | |
download | ldd3-ab121f379a3cff458c90e6f480ba4bb68c8733dd.tar.gz |
Linux Device Drivers 3 examples
Diffstat (limited to 'scullc')
-rw-r--r-- | scullc/Makefile | 46 | ||||
-rw-r--r-- | scullc/main.c | 600 | ||||
-rw-r--r-- | scullc/mmap.c | 118 | ||||
-rw-r--r-- | scullc/scullc.h | 122 | ||||
-rw-r--r-- | scullc/scullc_load | 30 | ||||
-rw-r--r-- | scullc/scullc_unload | 11 |
6 files changed, 927 insertions, 0 deletions
diff --git a/scullc/Makefile b/scullc/Makefile new file mode 100644 index 0000000..9b90b41 --- /dev/null +++ b/scullc/Makefile @@ -0,0 +1,46 @@ + +# Comment/uncomment the following line to enable/disable debugging +#DEBUG = y + + +ifeq ($(DEBUG),y) + DEBFLAGS = -O -g -DSCULLC_DEBUG # "-O" is needed to expand inlines +else + DEBFLAGS = -O2 +endif + +CFLAGS += $(DEBFLAGS) -I$(LDDINC) + +TARGET = scullc + +ifneq ($(KERNELRELEASE),) + +scullc-objs := main.o + +obj-m := scullc.o + +else + +KERNELDIR ?= /lib/modules/$(shell uname -r)/build +PWD := $(shell pwd) + +modules: + $(MAKE) -C $(KERNELDIR) M=$(PWD) LDDINC=$(PWD) modules + +endif + + +install: + install -d $(INSTALLDIR) + install -c $(TARGET).o $(INSTALLDIR) + +clean: + rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions + + +depend .depend dep: + $(CC) $(CFLAGS) -M *.c > .depend + +ifeq (.depend,$(wildcard .depend)) +include .depend +endif diff --git a/scullc/main.c b/scullc/main.c new file mode 100644 index 0000000..f7a1ca5 --- /dev/null +++ b/scullc/main.c @@ -0,0 +1,600 @@ +/* -*- C -*- + * main.c -- the bare scullc char module + * + * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet + * Copyright (C) 2001 O'Reilly & Associates + * + * The source code in this file can be freely used, adapted, + * and redistributed in source or binary form, so long as an + * acknowledgment appears in derived source files. The citation + * should list that the code comes from the book "Linux Device + * Drivers" by Alessandro Rubini and Jonathan Corbet, published + * by O'Reilly & Associates. No warranty is attached; + * we cannot take responsibility for errors or fitness for use. + * + * $Id: _main.c.in,v 1.21 2004/10/14 20:11:39 corbet Exp $ + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/kernel.h> /* printk() */ +#include <linux/slab.h> /* kmalloc() */ +#include <linux/fs.h> /* everything... */ +#include <linux/errno.h> /* error codes */ +#include <linux/types.h> /* size_t */ +#include <linux/proc_fs.h> +#include <linux/fcntl.h> /* O_ACCMODE */ +#include <linux/aio.h> +#include <asm/uaccess.h> +#include "scullc.h" /* local definitions */ + + +int scullc_major = SCULLC_MAJOR; +int scullc_devs = SCULLC_DEVS; /* number of bare scullc devices */ +int scullc_qset = SCULLC_QSET; +int scullc_quantum = SCULLC_QUANTUM; + +module_param(scullc_major, int, 0); +module_param(scullc_devs, int, 0); +module_param(scullc_qset, int, 0); +module_param(scullc_quantum, int, 0); +MODULE_AUTHOR("Alessandro Rubini"); +MODULE_LICENSE("Dual BSD/GPL"); + +struct scullc_dev *scullc_devices; /* allocated in scullc_init */ + +int scullc_trim(struct scullc_dev *dev); +void scullc_cleanup(void); + +/* declare one cache pointer: use it for all devices */ +kmem_cache_t *scullc_cache; + + + + + +#ifdef SCULLC_USE_PROC /* don't waste space if unused */ +/* + * The proc filesystem: function to read and entry + */ + +void scullc_proc_offset(char *buf, char **start, off_t *offset, int *len) +{ + if (*offset == 0) + return; + if (*offset >= *len) { + /* Not there yet */ + *offset -= *len; + *len = 0; + } else { + /* We're into the interesting stuff now */ + *start = buf + *offset; + *offset = 0; + } +} + +/* FIXME: Do we need this here?? It be ugly */ +int scullc_read_procmem(char *buf, char **start, off_t offset, + int count, int *eof, void *data) +{ + int i, j, quantum, qset, len = 0; + int limit = count - 80; /* Don't print more than this */ + struct scullc_dev *d; + + *start = buf; + for(i = 0; i < scullc_devs; i++) { + d = &scullc_devices[i]; + if (down_interruptible (&d->sem)) + return -ERESTARTSYS; + qset = d->qset; /* retrieve the features of each device */ + quantum=d->quantum; + len += sprintf(buf+len,"\nDevice %i: qset %i, quantum %i, sz %li\n", + i, qset, quantum, (long)(d->size)); + for (; d; d = d->next) { /* scan the list */ + len += sprintf(buf+len," item at %p, qset at %p\n",d,d->data); + scullc_proc_offset (buf, start, &offset, &len); + if (len > limit) + goto out; + if (d->data && !d->next) /* dump only the last item - save space */ + for (j = 0; j < qset; j++) { + if (d->data[j]) + len += sprintf(buf+len," % 4i:%8p\n",j,d->data[j]); + scullc_proc_offset (buf, start, &offset, &len); + if (len > limit) + goto out; + } + } + out: + up (&scullc_devices[i].sem); + if (len > limit) + break; + } + *eof = 1; + return len; +} + +#endif /* SCULLC_USE_PROC */ + +/* + * Open and close + */ + +int scullc_open (struct inode *inode, struct file *filp) +{ + struct scullc_dev *dev; /* device information */ + + /* Find the device */ + dev = container_of(inode->i_cdev, struct scullc_dev, cdev); + + /* now trim to 0 the length of the device if open was write-only */ + if ( (filp->f_flags & O_ACCMODE) == O_WRONLY) { + if (down_interruptible (&dev->sem)) + return -ERESTARTSYS; + scullc_trim(dev); /* ignore errors */ + up (&dev->sem); + } + + /* and use filp->private_data to point to the device data */ + filp->private_data = dev; + + return 0; /* success */ +} + +int scullc_release (struct inode *inode, struct file *filp) +{ + return 0; +} + +/* + * Follow the list + */ +struct scullc_dev *scullc_follow(struct scullc_dev *dev, int n) +{ + while (n--) { + if (!dev->next) { + dev->next = kmalloc(sizeof(struct scullc_dev), GFP_KERNEL); + memset(dev->next, 0, sizeof(struct scullc_dev)); + } + dev = dev->next; + continue; + } + return dev; +} + +/* + * Data management: read and write + */ + +ssize_t scullc_read (struct file *filp, char __user *buf, size_t count, + loff_t *f_pos) +{ + struct scullc_dev *dev = filp->private_data; /* the first listitem */ + struct scullc_dev *dptr; + int quantum = dev->quantum; + int qset = dev->qset; + int itemsize = quantum * qset; /* how many bytes in the listitem */ + int item, s_pos, q_pos, rest; + ssize_t retval = 0; + + if (down_interruptible (&dev->sem)) + return -ERESTARTSYS; + if (*f_pos > dev->size) + goto nothing; + if (*f_pos + count > dev->size) + count = dev->size - *f_pos; + /* find listitem, qset index, and offset in the quantum */ + item = ((long) *f_pos) / itemsize; + rest = ((long) *f_pos) % itemsize; + s_pos = rest / quantum; q_pos = rest % quantum; + + /* follow the list up to the right position (defined elsewhere) */ + dptr = scullc_follow(dev, item); + + if (!dptr->data) + goto nothing; /* don't fill holes */ + if (!dptr->data[s_pos]) + goto nothing; + if (count > quantum - q_pos) + count = quantum - q_pos; /* read only up to the end of this quantum */ + + if (copy_to_user (buf, dptr->data[s_pos]+q_pos, count)) { + retval = -EFAULT; + goto nothing; + } + up (&dev->sem); + + *f_pos += count; + return count; + + nothing: + up (&dev->sem); + return retval; +} + + + +ssize_t scullc_write (struct file *filp, const char __user *buf, size_t count, + loff_t *f_pos) +{ + struct scullc_dev *dev = filp->private_data; + struct scullc_dev *dptr; + int quantum = dev->quantum; + int qset = dev->qset; + int itemsize = quantum * qset; + int item, s_pos, q_pos, rest; + ssize_t retval = -ENOMEM; /* our most likely error */ + + if (down_interruptible (&dev->sem)) + return -ERESTARTSYS; + + /* find listitem, qset index and offset in the quantum */ + item = ((long) *f_pos) / itemsize; + rest = ((long) *f_pos) % itemsize; + s_pos = rest / quantum; q_pos = rest % quantum; + + /* follow the list up to the right position */ + dptr = scullc_follow(dev, item); + if (!dptr->data) { + dptr->data = kmalloc(qset * sizeof(void *), GFP_KERNEL); + if (!dptr->data) + goto nomem; + memset(dptr->data, 0, qset * sizeof(char *)); + } + /* Allocate a quantum using the memory cache */ + if (!dptr->data[s_pos]) { + dptr->data[s_pos] = kmem_cache_alloc(scullc_cache, GFP_KERNEL); + if (!dptr->data[s_pos]) + goto nomem; + memset(dptr->data[s_pos], 0, scullc_quantum); + } + if (count > quantum - q_pos) + count = quantum - q_pos; /* write only up to the end of this quantum */ + if (copy_from_user (dptr->data[s_pos]+q_pos, buf, count)) { + retval = -EFAULT; + goto nomem; + } + *f_pos += count; + + /* update the size */ + if (dev->size < *f_pos) + dev->size = *f_pos; + up (&dev->sem); + return count; + + nomem: + up (&dev->sem); + return retval; +} + +/* + * The ioctl() implementation + */ + +int scullc_ioctl (struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + + int err = 0, ret = 0, tmp; + + /* don't even decode wrong cmds: better returning ENOTTY than EFAULT */ + if (_IOC_TYPE(cmd) != SCULLC_IOC_MAGIC) return -ENOTTY; + if (_IOC_NR(cmd) > SCULLC_IOC_MAXNR) return -ENOTTY; + + /* + * the type is a bitmask, and VERIFY_WRITE catches R/W + * transfers. Note that the type is user-oriented, while + * verify_area is kernel-oriented, so the concept of "read" and + * "write" is reversed + */ + if (_IOC_DIR(cmd) & _IOC_READ) + err = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd)); + else if (_IOC_DIR(cmd) & _IOC_WRITE) + err = !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd)); + if (err) + return -EFAULT; + + switch(cmd) { + + case SCULLC_IOCRESET: + scullc_qset = SCULLC_QSET; + scullc_quantum = SCULLC_QUANTUM; + break; + + case SCULLC_IOCSQUANTUM: /* Set: arg points to the value */ + ret = __get_user(scullc_quantum, (int __user *) arg); + break; + + case SCULLC_IOCTQUANTUM: /* Tell: arg is the value */ + scullc_quantum = arg; + break; + + case SCULLC_IOCGQUANTUM: /* Get: arg is pointer to result */ + ret = __put_user (scullc_quantum, (int __user *) arg); + break; + + case SCULLC_IOCQQUANTUM: /* Query: return it (it's positive) */ + return scullc_quantum; + + case SCULLC_IOCXQUANTUM: /* eXchange: use arg as pointer */ + tmp = scullc_quantum; + ret = __get_user(scullc_quantum, (int __user *) arg); + if (ret == 0) + ret = __put_user(tmp, (int __user *) arg); + break; + + case SCULLC_IOCHQUANTUM: /* sHift: like Tell + Query */ + tmp = scullc_quantum; + scullc_quantum = arg; + return tmp; + + case SCULLC_IOCSQSET: + ret = __get_user(scullc_qset, (int __user *) arg); + break; + + case SCULLC_IOCTQSET: + scullc_qset = arg; + break; + + case SCULLC_IOCGQSET: + ret = __put_user(scullc_qset, (int __user *)arg); + break; + + case SCULLC_IOCQQSET: + return scullc_qset; + + case SCULLC_IOCXQSET: + tmp = scullc_qset; + ret = __get_user(scullc_qset, (int __user *)arg); + if (ret == 0) + ret = __put_user(tmp, (int __user *)arg); + break; + + case SCULLC_IOCHQSET: + tmp = scullc_qset; + scullc_qset = arg; + return tmp; + + default: /* redundant, as cmd was checked against MAXNR */ + return -ENOTTY; + } + + return ret; +} + +/* + * The "extended" operations + */ + +loff_t scullc_llseek (struct file *filp, loff_t off, int whence) +{ + struct scullc_dev *dev = filp->private_data; + long newpos; + + switch(whence) { + case 0: /* SEEK_SET */ + newpos = off; + break; + + case 1: /* SEEK_CUR */ + newpos = filp->f_pos + off; + break; + + case 2: /* SEEK_END */ + newpos = dev->size + off; + break; + + default: /* can't happen */ + return -EINVAL; + } + if (newpos<0) return -EINVAL; + filp->f_pos = newpos; + return newpos; +} + + +/* + * A simple asynchronous I/O implementation. + */ + +struct async_work { + struct kiocb *iocb; + int result; + struct work_struct work; +}; + +/* + * "Complete" an asynchronous operation. + */ +static void scullc_do_deferred_op(void *p) +{ + struct async_work *stuff = (struct async_work *) p; + aio_complete(stuff->iocb, stuff->result, 0); + kfree(stuff); +} + + +static int scullc_defer_op(int write, struct kiocb *iocb, char __user *buf, + size_t count, loff_t pos) +{ + struct async_work *stuff; + int result; + + /* Copy now while we can access the buffer */ + if (write) + result = scullc_write(iocb->ki_filp, buf, count, &pos); + else + result = scullc_read(iocb->ki_filp, buf, count, &pos); + + /* If this is a synchronous IOCB, we return our status now. */ + if (is_sync_kiocb(iocb)) + return result; + + /* Otherwise defer the completion for a few milliseconds. */ + stuff = kmalloc (sizeof (*stuff), GFP_KERNEL); + if (stuff == NULL) + return result; /* No memory, just complete now */ + stuff->iocb = iocb; + stuff->result = result; + INIT_WORK(&stuff->work, scullc_do_deferred_op, stuff); + schedule_delayed_work(&stuff->work, HZ/100); + return -EIOCBQUEUED; +} + + +static ssize_t scullc_aio_read(struct kiocb *iocb, char __user *buf, size_t count, + loff_t pos) +{ + return scullc_defer_op(0, iocb, buf, count, pos); +} + +static ssize_t scullc_aio_write(struct kiocb *iocb, const char __user *buf, + size_t count, loff_t pos) +{ + return scullc_defer_op(1, iocb, (char __user *) buf, count, pos); +} + + + + +/* + * The fops + */ + +struct file_operations scullc_fops = { + .owner = THIS_MODULE, + .llseek = scullc_llseek, + .read = scullc_read, + .write = scullc_write, + .ioctl = scullc_ioctl, + .open = scullc_open, + .release = scullc_release, + .aio_read = scullc_aio_read, + .aio_write = scullc_aio_write, +}; + +int scullc_trim(struct scullc_dev *dev) +{ + struct scullc_dev *next, *dptr; + int qset = dev->qset; /* "dev" is not-null */ + int i; + + if (dev->vmas) /* don't trim: there are active mappings */ + return -EBUSY; + + for (dptr = dev; dptr; dptr = next) { /* all the list items */ + if (dptr->data) { + for (i = 0; i < qset; i++) + if (dptr->data[i]) + kmem_cache_free(scullc_cache, dptr->data[i]); + + kfree(dptr->data); + dptr->data=NULL; + } + next=dptr->next; + if (dptr != dev) kfree(dptr); /* all of them but the first */ + } + dev->size = 0; + dev->qset = scullc_qset; + dev->quantum = scullc_quantum; + dev->next = NULL; + return 0; +} + + +static void scullc_setup_cdev(struct scullc_dev *dev, int index) +{ + int err, devno = MKDEV(scullc_major, index); + + cdev_init(&dev->cdev, &scullc_fops); + dev->cdev.owner = THIS_MODULE; + dev->cdev.ops = &scullc_fops; + err = cdev_add (&dev->cdev, devno, 1); + /* Fail gracefully if need be */ + if (err) + printk(KERN_NOTICE "Error %d adding scull%d", err, index); +} + + + +/* + * Finally, the module stuff + */ + +int scullc_init(void) +{ + int result, i; + dev_t dev = MKDEV(scullc_major, 0); + + /* + * Register your major, and accept a dynamic number. + */ + if (scullc_major) + result = register_chrdev_region(dev, scullc_devs, "scullc"); + else { + result = alloc_chrdev_region(&dev, 0, scullc_devs, "scullc"); + scullc_major = MAJOR(dev); + } + if (result < 0) + return result; + + + /* + * allocate the devices -- we can't have them static, as the number + * can be specified at load time + */ + scullc_devices = kmalloc(scullc_devs*sizeof (struct scullc_dev), GFP_KERNEL); + if (!scullc_devices) { + result = -ENOMEM; + goto fail_malloc; + } + memset(scullc_devices, 0, scullc_devs*sizeof (struct scullc_dev)); + for (i = 0; i < scullc_devs; i++) { + scullc_devices[i].quantum = scullc_quantum; + scullc_devices[i].qset = scullc_qset; + sema_init (&scullc_devices[i].sem, 1); + scullc_setup_cdev(scullc_devices + i, i); + } + + scullc_cache = kmem_cache_create("scullc", scullc_quantum, + 0, SLAB_HWCACHE_ALIGN, NULL, NULL); /* no ctor/dtor */ + if (!scullc_cache) { + scullc_cleanup(); + return -ENOMEM; + } + +#ifdef SCULLC_USE_PROC /* only when available */ + create_proc_read_entry("scullcmem", 0, NULL, scullc_read_procmem, NULL); +#endif + return 0; /* succeed */ + + fail_malloc: + unregister_chrdev_region(dev, scullc_devs); + return result; +} + + + +void scullc_cleanup(void) +{ + int i; + +#ifdef SCULLC_USE_PROC + remove_proc_entry("scullcmem", NULL); +#endif + + for (i = 0; i < scullc_devs; i++) { + cdev_del(&scullc_devices[i].cdev); + scullc_trim(scullc_devices + i); + } + kfree(scullc_devices); + + if (scullc_cache) + kmem_cache_destroy(scullc_cache); + unregister_chrdev_region(MKDEV (scullc_major, 0), scullc_devs); +} + + +module_init(scullc_init); +module_exit(scullc_cleanup); diff --git a/scullc/mmap.c b/scullc/mmap.c new file mode 100644 index 0000000..841f62c --- /dev/null +++ b/scullc/mmap.c @@ -0,0 +1,118 @@ +/* -*- C -*- + * mmap.c -- memory mapping for the scullc char module + * + * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet + * Copyright (C) 2001 O'Reilly & Associates + * + * The source code in this file can be freely used, adapted, + * and redistributed in source or binary form, so long as an + * acknowledgment appears in derived source files. The citation + * should list that the code comes from the book "Linux Device + * Drivers" by Alessandro Rubini and Jonathan Corbet, published + * by O'Reilly & Associates. No warranty is attached; + * we cannot take responsibility for errors or fitness for use. + * + * $Id: _mmap.c.in,v 1.13 2004/10/18 18:07:36 corbet Exp $ + */ + +#include <linux/config.h> +#include <linux/module.h> + +#include <linux/mm.h> /* everything */ +#include <linux/errno.h> /* error codes */ +#include <asm/pgtable.h> + +#include "scullc.h" /* local definitions */ + + +/* + * open and close: just keep track of how many times the device is + * mapped, to avoid releasing it. + */ + +void scullc_vma_open(struct vm_area_struct *vma) +{ + struct scullc_dev *dev = vma->vm_private_data; + + dev->vmas++; +} + +void scullc_vma_close(struct vm_area_struct *vma) +{ + struct scullc_dev *dev = vma->vm_private_data; + + dev->vmas--; +} + +/* + * The nopage method: the core of the file. It retrieves the + * page required from the scullc device and returns it to the + * user. The count for the page must be incremented, because + * it is automatically decremented at page unmap. + * + * For this reason, "order" must be zero. Otherwise, only the first + * page has its count incremented, and the allocating module must + * release it as a whole block. Therefore, it isn't possible to map + * pages from a multipage block: when they are unmapped, their count + * is individually decreased, and would drop to 0. + */ + +struct page *scullc_vma_nopage(struct vm_area_struct *vma, + unsigned long address, int *type) +{ + unsigned long offset; + struct scullc_dev *ptr, *dev = vma->vm_private_data; + struct page *page = NOPAGE_SIGBUS; + void *pageptr = NULL; /* default to "missing" */ + + down(&dev->sem); + offset = (address - vma->vm_start) + (vma->vm_pgoff << PAGE_SHIFT); + if (offset >= dev->size) goto out; /* out of range */ + + /* + * Now retrieve the scullc device from the list,then the page. + * If the device has holes, the process receives a SIGBUS when + * accessing the hole. + */ + offset >>= PAGE_SHIFT; /* offset is a number of pages */ + for (ptr = dev; ptr && offset >= dev->qset;) { + ptr = ptr->next; + offset -= dev->qset; + } + if (ptr && ptr->data) pageptr = ptr->data[offset]; + if (!pageptr) goto out; /* hole or end-of-file */ + + /* got it, now increment the count */ + get_page(page); + if (type) + *type = VM_FAULT_MINOR; + out: + up(&dev->sem); + return page; +} + + + +struct vm_operations_struct scullc_vm_ops = { + .open = scullc_vma_open, + .close = scullc_vma_close, + .nopage = scullc_vma_nopage, +}; + + +int scullc_mmap(struct file *filp, struct vm_area_struct *vma) +{ + struct inode *inode = filp->f_dentry->d_inode; + + /* refuse to map if order is not 0 */ + if (scullc_devices[iminor(inode)].order) + return -ENODEV; + + /* don't do anything here: "nopage" will set up page table entries */ + vma->vm_ops = &scullc_vm_ops; + vma->vm_flags |= VM_RESERVED; + vma->vm_private_data = filp->private_data; + scullc_vma_open(vma); + return 0; +} + diff --git a/scullc/scullc.h b/scullc/scullc.h new file mode 100644 index 0000000..86d5090 --- /dev/null +++ b/scullc/scullc.h @@ -0,0 +1,122 @@ +/* -*- C -*- + * scullc.h -- definitions for the scullc char module + * + * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet + * Copyright (C) 2001 O'Reilly & Associates + * + * The source code in this file can be freely used, adapted, + * and redistributed in source or binary form, so long as an + * acknowledgment appears in derived source files. The citation + * should list that the code comes from the book "Linux Device + * Drivers" by Alessandro Rubini and Jonathan Corbet, published + * by O'Reilly & Associates. No warranty is attached; + * we cannot take responsibility for errors or fitness for use. + */ + +#include <linux/ioctl.h> +#include <linux/cdev.h> + +/* + * Macros to help debugging + */ + +#undef PDEBUG /* undef it, just in case */ +#ifdef SCULLC_DEBUG +# ifdef __KERNEL__ + /* This one if debugging is on, and kernel space */ +# define PDEBUG(fmt, args...) printk( KERN_DEBUG "scullc: " fmt, ## args) +# else + /* This one for user space */ +# define PDEBUG(fmt, args...) fprintf(stderr, fmt, ## args) +# endif +#else +# define PDEBUG(fmt, args...) /* not debugging: nothing */ +#endif + +#undef PDEBUGG +#define PDEBUGG(fmt, args...) /* nothing: it's a placeholder */ + +#define SCULLC_MAJOR 0 /* dynamic major by default */ + +#define SCULLC_DEVS 4 /* scullc0 through scullc3 */ + +/* + * The bare device is a variable-length region of memory. + * Use a linked list of indirect blocks. + * + * "scullc_dev->data" points to an array of pointers, each + * pointer refers to a memory page. + * + * The array (quantum-set) is SCULLC_QSET long. + */ +#define SCULLC_QUANTUM 4000 /* use a quantum size like scull */ +#define SCULLC_QSET 500 + +struct scullc_dev { + void **data; + struct scullc_dev *next; /* next listitem */ + int vmas; /* active mappings */ + int quantum; /* the current allocation size */ + int qset; /* the current array size */ + size_t size; /* 32-bit will suffice */ + struct semaphore sem; /* Mutual exclusion */ + struct cdev cdev; +}; + +extern struct scullc_dev *scullc_devices; + +extern struct file_operations scullc_fops; + +/* + * The different configurable parameters + */ +extern int scullc_major; /* main.c */ +extern int scullc_devs; +extern int scullc_order; +extern int scullc_qset; + +/* + * Prototypes for shared functions + */ +int scullc_trim(struct scullc_dev *dev); +struct scullc_dev *scullc_follow(struct scullc_dev *dev, int n); + + +#ifdef SCULLC_DEBUG +# define SCULLC_USE_PROC +#endif + +/* + * Ioctl definitions + */ + +/* Use 'K' as magic number */ +#define SCULLC_IOC_MAGIC 'K' + +#define SCULLC_IOCRESET _IO(SCULLC_IOC_MAGIC, 0) + +/* + * S means "Set" through a ptr, + * T means "Tell" directly + * G means "Get" (to a pointed var) + * Q means "Query", response is on the return value + * X means "eXchange": G and S atomically + * H means "sHift": T and Q atomically + */ +#define SCULLC_IOCSQUANTUM _IOW(SCULLC_IOC_MAGIC, 1, int) +#define SCULLC_IOCTQUANTUM _IO(SCULLC_IOC_MAGIC, 2) +#define SCULLC_IOCGQUANTUM _IOR(SCULLC_IOC_MAGIC, 3, int) +#define SCULLC_IOCQQUANTUM _IO(SCULLC_IOC_MAGIC, 4) +#define SCULLC_IOCXQUANTUM _IOWR(SCULLC_IOC_MAGIC, 5, int) +#define SCULLC_IOCHQUANTUM _IO(SCULLC_IOC_MAGIC, 6) +#define SCULLC_IOCSQSET _IOW(SCULLC_IOC_MAGIC, 7, int) +#define SCULLC_IOCTQSET _IO(SCULLC_IOC_MAGIC, 8) +#define SCULLC_IOCGQSET _IOR(SCULLC_IOC_MAGIC, 9, int) +#define SCULLC_IOCQQSET _IO(SCULLC_IOC_MAGIC, 10) +#define SCULLC_IOCXQSET _IOWR(SCULLC_IOC_MAGIC,11, int) +#define SCULLC_IOCHQSET _IO(SCULLC_IOC_MAGIC, 12) + +#define SCULLC_IOC_MAXNR 12 + + + diff --git a/scullc/scullc_load b/scullc/scullc_load new file mode 100644 index 0000000..8ca1f69 --- /dev/null +++ b/scullc/scullc_load @@ -0,0 +1,30 @@ +#!/bin/sh +module="scullc" +device="scullc" +mode="664" + +# Group: since distributions do it differently, look for wheel or use staff +if grep '^staff:' /etc/group > /dev/null; then + group="staff" +else + group="wheel" +fi + +# remove stale nodes +rm -f /dev/${device}? + +# invoke insmod with all arguments we got +# and use a pathname, as newer modutils don't look in . by default +/sbin/insmod -f ./$module.ko $* || exit 1 + +major=`cat /proc/devices | awk "\\$2==\"$module\" {print \\$1}"` + +mknod /dev/${device}0 c $major 0 +mknod /dev/${device}1 c $major 1 +mknod /dev/${device}2 c $major 2 +mknod /dev/${device}3 c $major 3 +ln -sf ${device}0 /dev/${device} + +# give appropriate group/permissions +chgrp $group /dev/${device}[0-3] +chmod $mode /dev/${device}[0-3] diff --git a/scullc/scullc_unload b/scullc/scullc_unload new file mode 100644 index 0000000..bb91f71 --- /dev/null +++ b/scullc/scullc_unload @@ -0,0 +1,11 @@ +#!/bin/sh +module="scullc" +device="scullc" + +# invoke rmmod with all arguments we got +/sbin/rmmod $module $* || exit 1 + +# remove nodes +rm -f /dev/${device}[0-3] /dev/${device} + +exit 0 |