123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202 |
- /*
- * Copyright (C) 2012 CERN (www.cern.ch)
- * Author: Alessandro Rubini <rubini@gnudd.com>
- *
- * Released according to the GNU GPL, version 2 or any later version.
- *
- * This work is part of the White Rabbit project, a research effort led
- * by CERN, the European Institute for Nuclear Research.
- */
- #include <linux/module.h>
- #include <linux/init.h>
- #include <linux/list.h>
- #include <linux/slab.h>
- #include <linux/fs.h>
- #include <linux/miscdevice.h>
- #include <linux/spinlock.h>
- #include <linux/fmc.h>
- #include <linux/uaccess.h>
- static LIST_HEAD(fc_devices);
- static DEFINE_SPINLOCK(fc_lock);
- struct fc_instance {
- struct list_head list;
- struct fmc_device *fmc;
- struct miscdevice misc;
- };
- /* at open time, we must identify our device */
- static int fc_open(struct inode *ino, struct file *f)
- {
- struct fmc_device *fmc;
- struct fc_instance *fc;
- int minor = iminor(ino);
- list_for_each_entry(fc, &fc_devices, list)
- if (fc->misc.minor == minor)
- break;
- if (fc->misc.minor != minor)
- return -ENODEV;
- fmc = fc->fmc;
- if (try_module_get(fmc->owner) == 0)
- return -ENODEV;
- f->private_data = fmc;
- return 0;
- }
- static int fc_release(struct inode *ino, struct file *f)
- {
- struct fmc_device *fmc = f->private_data;
- module_put(fmc->owner);
- return 0;
- }
- /* read and write are simple after the default llseek has been used */
- static ssize_t fc_read(struct file *f, char __user *buf, size_t count,
- loff_t *offp)
- {
- struct fmc_device *fmc = f->private_data;
- unsigned long addr;
- uint32_t val;
- if (count < sizeof(val))
- return -EINVAL;
- count = sizeof(val);
- addr = *offp;
- if (addr > fmc->memlen)
- return -ESPIPE; /* Illegal seek */
- val = fmc_readl(fmc, addr);
- if (copy_to_user(buf, &val, count))
- return -EFAULT;
- *offp += count;
- return count;
- }
- static ssize_t fc_write(struct file *f, const char __user *buf, size_t count,
- loff_t *offp)
- {
- struct fmc_device *fmc = f->private_data;
- unsigned long addr;
- uint32_t val;
- if (count < sizeof(val))
- return -EINVAL;
- count = sizeof(val);
- addr = *offp;
- if (addr > fmc->memlen)
- return -ESPIPE; /* Illegal seek */
- if (copy_from_user(&val, buf, count))
- return -EFAULT;
- fmc_writel(fmc, val, addr);
- *offp += count;
- return count;
- }
- static const struct file_operations fc_fops = {
- .owner = THIS_MODULE,
- .open = fc_open,
- .release = fc_release,
- .llseek = generic_file_llseek,
- .read = fc_read,
- .write = fc_write,
- };
- /* Device part .. */
- static int fc_probe(struct fmc_device *fmc);
- static int fc_remove(struct fmc_device *fmc);
- static struct fmc_driver fc_drv = {
- .version = FMC_VERSION,
- .driver.name = KBUILD_MODNAME,
- .probe = fc_probe,
- .remove = fc_remove,
- /* no table: we want to match everything */
- };
- /* We accept the generic busid parameter */
- FMC_PARAM_BUSID(fc_drv);
- /* probe and remove must allocate and release a misc device */
- static int fc_probe(struct fmc_device *fmc)
- {
- int ret;
- int index = 0;
- struct fc_instance *fc;
- if (fmc->op->validate)
- index = fmc->op->validate(fmc, &fc_drv);
- if (index < 0)
- return -EINVAL; /* not our device: invalid */
- /* Create a char device: we want to create it anew */
- fc = kzalloc(sizeof(*fc), GFP_KERNEL);
- if (!fc)
- return -ENOMEM;
- fc->fmc = fmc;
- fc->misc.minor = MISC_DYNAMIC_MINOR;
- fc->misc.fops = &fc_fops;
- fc->misc.name = kstrdup(dev_name(&fmc->dev), GFP_KERNEL);
- ret = misc_register(&fc->misc);
- if (ret < 0)
- goto out;
- spin_lock(&fc_lock);
- list_add(&fc->list, &fc_devices);
- spin_unlock(&fc_lock);
- dev_info(&fc->fmc->dev, "Created misc device \"%s\"\n",
- fc->misc.name);
- return 0;
- out:
- kfree(fc->misc.name);
- kfree(fc);
- return ret;
- }
- static int fc_remove(struct fmc_device *fmc)
- {
- struct fc_instance *fc;
- list_for_each_entry(fc, &fc_devices, list)
- if (fc->fmc == fmc)
- break;
- if (fc->fmc != fmc) {
- dev_err(&fmc->dev, "remove called but not found\n");
- return -ENODEV;
- }
- spin_lock(&fc_lock);
- list_del(&fc->list);
- spin_unlock(&fc_lock);
- misc_deregister(&fc->misc);
- kfree(fc->misc.name);
- kfree(fc);
- return 0;
- }
- static int fc_init(void)
- {
- int ret;
- ret = fmc_driver_register(&fc_drv);
- return ret;
- }
- static void fc_exit(void)
- {
- fmc_driver_unregister(&fc_drv);
- }
- module_init(fc_init);
- module_exit(fc_exit);
- MODULE_LICENSE("GPL");
|