/*
 * fanout.c:  A one-to-many multiplexer
 * GPL : original by Bob Smith 
 * changes, added more locking: Edwin van den Oetelaar (www.oetelaar.com)
 * $Id$
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/poll.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <asm/uaccess.h>
#include <linux/devfs_fs_kernel.h>

/* Limits and other defines */
/* The # fanout devices.  Max minor # is one less than this */
#define NUM_FO_DEVS (2)
#define DEVNAME "fanout"
#define DEBUGLEVEL (2)
/* Data structure definitions */
/* This data structure describes one fanout device.  There
 * is one of these for each instance (minor #) of fanout */
struct fo {
    int minor;			/* minor number of this fanout instance */
    char *buf;			/* points to circular buffer, first char */
    int indx;			/* where to put next char received */
    loff_t count;		/* number chars received */
    wait_queue_head_t inq;	/* readers wait on this queue */
    struct semaphore sem;	/* lock to keep buf/indx sane */
};

/*  Function prototypes.  */
int fanout_init_module(void);
void fanout_exit_module(void);
static int fanout_open(struct inode *, struct file *);
static int fanout_release(struct inode *, struct file *);
static ssize_t fanout_read(struct file *, char *, size_t, loff_t *);
static ssize_t fanout_write(struct file *, const char *, size_t, loff_t *);
static unsigned int fanout_poll(struct file *, poll_table *);

/* Global variables */
static int buffersize = 0x4000;	/* Size of the circular buffer 0x4000 16K */
static unsigned char numberofdevices = NUM_FO_DEVS;	/* number of fanout devices, dynamic */
static int fo_major = 0;	/* major device number */
/* Debuglvl controls whether a printk is executed
 * 0 = no printk at all
 * 1 = printk on error only
 * 2 = printk on errors and on init/remove
 * 3 = debug prink to trace calls into fanout
 * 4 = debug trace inside of fanout calls 
 */
static unsigned char debuglevel = DEBUGLEVEL;	/* printk verbosity */

struct cdev fo_cdev;		/* a char device global just 1 */
dev_t fo_devicenumber;		/* first device number */

module_param(buffersize, int, S_IRUSR);
module_param(debuglevel, byte, S_IRUSR);
module_param(numberofdevices, byte, S_IRUSR);

static struct fo *fo_devices;	/* point to devices (minors) */

/* mapping the callbacks into this driver */

static struct file_operations fanout_fops = {
    .owner = THIS_MODULE,
    .read = fanout_read,
    .open = fanout_open,
    .write = fanout_write,
    .poll = fanout_poll,
    .release = fanout_release
};

/* Module description and macros */

MODULE_DESCRIPTION
    ("A device to replicate input (writer) on all outputs (readers), readers block, writer never block");
MODULE_AUTHOR("Bob Smith");
MODULE_LICENSE("GPL");
MODULE_PARM_DESC(buffersize, "Size of each buffer. default=16384 (16K) ");
MODULE_PARM_DESC(debuglevel, "Debug level. Higher=verbose. default=2");
MODULE_PARM_DESC(numberofdevices,
		 "Create this many minor devices. default=2");


int fanout_init_module(void)
{				/* called when driver is loaded */
    int i, err;
    fo_devices = kmalloc(numberofdevices * sizeof(struct fo), GFP_KERNEL);
    if (fo_devices == NULL) {
	/* no memory available */
	if (debuglevel >= 1) {
	    printk(KERN_ALERT "fanout: init fails. no memory\n");
	}
	return 0;
    }
/* clean this memory */
    memset(fo_devices, 0, numberofdevices * sizeof(struct fo));
/* init devices in this block */
    for (i = 0; i < numberofdevices; i++) {	/* for every minor device */
	fo_devices[i].minor = i;	/* set number */
	fo_devices[i].buf = (char *) 0;	/* init buf */
	fo_devices[i].indx = 0;	/* init index */
	fo_devices[i].count = 0;	/* init count */
	init_waitqueue_head(&fo_devices[i].inq);	/* init wait-queue for readers */
	init_MUTEX(&fo_devices[i].sem);	/* init sema */
    }

    err = alloc_chrdev_region(&fo_devicenumber, 0, numberofdevices, DEVNAME);	/* alloc number of char devs in kernel */
    if (err < 0) {
	if (debuglevel >= 1) {
	    printk(KERN_ALERT "fanout: init fails. err=%d\n", err);
	}
	return err;
    }
    fo_major = MAJOR(fo_devicenumber);	/* save assign major */
    cdev_init(&fo_cdev, &fanout_fops);	/* init dev structures */
    kobject_set_name(&(fo_cdev.kobj), "fanout%d", fo_devicenumber);

    err = cdev_add(&fo_cdev, fo_devicenumber, numberofdevices);	/* add to kernel */
    if (err < 0) {
	if (debuglevel >= 1) {
	    printk(KERN_ALERT "fanout: init fails. err=%d\n", err);
	}
	return err;
    }

    if (debuglevel >= 2) {
	printk(KERN_INFO
	       "fanout: Installed [%d] minor devices on major number [%d]\n",
	       numberofdevices, fo_major);
    }
    return 0;			/* success */
}

void fanout_exit_module(void)
{				/* cleanup is called when unloading driver */
    int i;
    if (!fo_devices) {
	return;
	/*nothing else to release */
    }
    for (i = 0; i < numberofdevices; i++) {	/* for every minor */
	if (fo_devices[i].buf) {
	    kfree(fo_devices[i].buf);	/* free alloced memory */
	}
    }

    cdev_del(&fo_cdev);		/* delete major device */
    kfree(fo_devices);		/* free */
    fo_devices = NULL;		/* reset pointer */
    unregister_chrdev_region(fo_devicenumber, numberofdevices);

    if (debuglevel >= 2) {
	printk(KERN_INFO "fanout: Uninstalled\n");
    }
}

static int fanout_open(struct inode *inode, struct file *filp)
{				/* called when device is opened */
    int mnr = iminor(inode);
    struct fo *dev = &fo_devices[mnr];
    if (debuglevel >= 3) {
	printk(KERN_DEBUG "fanout open. Minor#=%d\n", mnr);
    }

    if (down_interruptible(&dev->sem))	/* get sema to prevent races on open */
	return -ERESTARTSYS;

    if (!dev->buf) {
	/* alloc the buffer, shared by all readers */
	dev->buf = kmalloc(buffersize, GFP_KERNEL);
	if (debuglevel >= 3) {
	    printk(KERN_DEBUG "fanout: Got memory for minor=%d\n", mnr);
	}
	if (!dev->buf) {
	    if (debuglevel >= 1) {
		printk(KERN_ALERT "fanout: No memory dev=%d\n", mnr);
	    }
	    up(&dev->sem);	/* unlock sema */
	    return -ENOMEM;
	}
    }

    /* store which fanout device in the file's private data */
    filp->private_data = (void *) dev;

    /* define the file to be immediately caught up with the fanout dev */
    filp->f_pos = dev->count;
    up(&dev->sem);		/* unlock sema we are done */
    return nonseekable_open(inode, filp);	/* success */
}

static int fanout_release(struct inode *inode, struct file *filp)
{				/* called when nobody has device open anymore */
    if (debuglevel >= 3) {
	printk(KERN_DEBUG "fanout close. Minor#=%d\n",
	       ((struct fo *) filp->private_data)->minor);
    }

    return 0;			/* success */
}

static ssize_t fanout_read(struct file *filp, char __user * buff,
			   size_t count, loff_t * offset)
{
    /* called when a process calls read on the device (when open) */
    int ret;
    size_t xfer;		/* num bytes read from fanout buf */
    int cpcnt, cpstrt;		/* cp count and start location */

    struct fo *dev = (struct fo *) filp->private_data;

    if (down_interruptible(&dev->sem)) {	/* lock semaphore */
	return -ERESTARTSYS;
    }

    if (debuglevel >= 3) {
	printk(KERN_DEBUG "fanout: read %d char from dev%d, off=%lld\n",
	       count, dev->minor, *offset);
    }

    /* Verify that data requested is in the buffer or is next byte */
    xfer = dev->count - *offset;	/* amount is total count minus requested pointer */
    if ((xfer > buffersize) || (xfer < 0)) {
	printk(KERN_DEBUG "not good:fanout, xfer=%d buffersize=%d", xfer,
	       buffersize);
	up(&dev->sem);		/* unlock sema */
	return -EPIPE;		/* buffer overrun */
    }

    /* Wait here until new data is available */

    while (*offset == dev->count) {
	up(&dev->sem);		/* unlock sema */
	/* wait on event queue, predicate is .. */
	if (wait_event_interruptible(dev->inq, (*offset != dev->count))) {
	    return -ERESTARTSYS;
	}
	if (down_interruptible(&dev->sem))	/* lock */
	    return -ERESTARTSYS;
    }

    /* Copy the new data out to the user */
    xfer = dev->count - *offset;	/* amount of data available to copy */
    /* check buffer wrapping here todo */

    xfer = min(count, xfer);
    /* xfer is less then available when so requested */
    ret = xfer;			/* we will handle these bytes */
    while (xfer) {
	cpstrt = dev->indx - xfer;	/* cpstrt is writeptr minux requested block */
	if (cpstrt < 0) {	/* not enough data before writeptr to satisfy request */
	    cpcnt = -cpstrt;
	    cpstrt += buffersize;
	} else {
	    cpcnt = xfer;
	}

	if (copy_to_user(buff, dev->buf + cpstrt, cpcnt)) {
	    up(&dev->sem);
	    return -EFAULT;
	}

	buff += cpcnt;
	xfer -= cpcnt;
    };

    /* We may have slept during the above copy_to_user() and more
     * data may have been added while we slept.  Do a sanity check
     * to make sure the buffer did not wrap while we slept. */
    if (dev->count - *offset > buffersize) {
	up(&dev->sem);		/* unlock sema */
	return -EPIPE;
    }

    *offset += ret;
    // wake up the writers, not in this driver
    up(&dev->sem);		/* unlock sema */
    return ret;
}

static ssize_t fanout_write(struct file *filp, const char __user * buff,
			    size_t count, loff_t * off)
{
    /* called when process calls write on device */

    struct fo *dev = filp->private_data;

    int ret, xfer;		/* num bytes to read from user */
    int cpcnt;			/* num bytes in a copy */

    if (down_interruptible(&dev->sem)) {	/* lock semaphore */
	return -ERESTARTSYS;
    }

    if (debuglevel >= 3) {
	printk(KERN_DEBUG "fanout: write %d char to dev%d, off=%d\n",
	       count, dev->minor, (int) *off);
    }

    /* Copy at most one-quarter of the circular buffer size.  This
     * gives readers more of a chance to wake up and get some data 
     * In other words feed the reader little chuncks of data, they will
     * call again if they still want more */

    ret = xfer = min((int) count, buffersize / 4);
    /* we loop over the amount, because the buffer is not a single block but wraps arround */
    while (xfer) {
	cpcnt = buffersize - dev->indx;
	cpcnt = min(cpcnt, xfer);

	if (debuglevel >= 3) {
	    printk(KERN_DEBUG "fanout: write copy from user(%p,%p,%d)\n",
		   dev->buf + dev->indx, buff, cpcnt);
	}

	if (copy_from_user(dev->buf + dev->indx, buff, cpcnt)) {
	    up(&dev->sem);	/* unlock semaphore */
	    return -EFAULT;
	}
	*off += cpcnt;		/* not sure, was in the LDD 3rd edition */
	dev->indx += cpcnt;
	dev->indx = (dev->indx == buffersize) ? 0 : dev->indx;	/* wrap buffer ptr */
	xfer -= cpcnt;		/* we did cpcnt bytes, update counters and pointer */
	buff += cpcnt;
    }

    dev->count += ret;		/* update file size */
    up(&dev->sem);		/* unlock semaphore */
    /* This is what the readers have been waiting for */
    wake_up_interruptible(&dev->inq);
    return ret;
}

static unsigned int fanout_poll(struct file *filp, poll_table * ppt)
{
    /* used by poll and select calls from process */
    /* The circular buffer is always available for writing */
    int ready_mask = POLLOUT | POLLWRNORM;

    struct fo *dev = filp->private_data;
    poll_wait(filp, &dev->inq, ppt);

    if (filp->f_pos != dev->count) {
	ready_mask = (POLLIN | POLLRDNORM);
    }

    if (debuglevel >= 3) {
	printk(KERN_DEBUG "fanout: poll returns 0x%x\n", ready_mask);
    }
    return ready_mask;
}

module_init(fanout_init_module);
module_exit(fanout_exit_module);
