linux/drivers/fmc/fmc-chardev.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2012 CERN (www.cern.ch)
   3 * Author: Alessandro Rubini <rubini@gnudd.com>
   4 *
   5 * Released according to the GNU GPL, version 2 or any later version.
   6 *
   7 * This work is part of the White Rabbit project, a research effort led
   8 * by CERN, the European Institute for Nuclear Research.
   9 */
  10#include <linux/module.h>
  11#include <linux/init.h>
  12#include <linux/list.h>
  13#include <linux/slab.h>
  14#include <linux/fs.h>
  15#include <linux/miscdevice.h>
  16#include <linux/spinlock.h>
  17#include <linux/fmc.h>
  18#include <linux/uaccess.h>
  19
  20static LIST_HEAD(fc_devices);
  21static DEFINE_SPINLOCK(fc_lock);
  22
  23struct fc_instance {
  24        struct list_head list;
  25        struct fmc_device *fmc;
  26        struct miscdevice misc;
  27};
  28
  29/* at open time, we must identify our device */
  30static int fc_open(struct inode *ino, struct file *f)
  31{
  32        struct fmc_device *fmc;
  33        struct fc_instance *fc;
  34        int minor = iminor(ino);
  35
  36        list_for_each_entry(fc, &fc_devices, list)
  37                if (fc->misc.minor == minor)
  38                        break;
  39        if (fc->misc.minor != minor)
  40                return -ENODEV;
  41        fmc = fc->fmc;
  42        if (try_module_get(fmc->owner) == 0)
  43                return -ENODEV;
  44
  45        f->private_data = fmc;
  46        return 0;
  47}
  48
  49static int fc_release(struct inode *ino, struct file *f)
  50{
  51        struct fmc_device *fmc = f->private_data;
  52        module_put(fmc->owner);
  53        return 0;
  54}
  55
  56/* read and write are simple after the default llseek has been used */
  57static ssize_t fc_read(struct file *f, char __user *buf, size_t count,
  58                       loff_t *offp)
  59{
  60        struct fmc_device *fmc = f->private_data;
  61        unsigned long addr;
  62        uint32_t val;
  63
  64        if (count < sizeof(val))
  65                return -EINVAL;
  66        count = sizeof(val);
  67
  68        addr = *offp;
  69        if (addr > fmc->memlen)
  70                return -ESPIPE; /* Illegal seek */
  71        val = fmc_readl(fmc, addr);
  72        if (copy_to_user(buf, &val, count))
  73                return -EFAULT;
  74        *offp += count;
  75        return count;
  76}
  77
  78static ssize_t fc_write(struct file *f, const char __user *buf, size_t count,
  79                        loff_t *offp)
  80{
  81        struct fmc_device *fmc = f->private_data;
  82        unsigned long addr;
  83        uint32_t val;
  84
  85        if (count < sizeof(val))
  86                return -EINVAL;
  87        count = sizeof(val);
  88
  89        addr = *offp;
  90        if (addr > fmc->memlen)
  91                return -ESPIPE; /* Illegal seek */
  92        if (copy_from_user(&val, buf, count))
  93                return -EFAULT;
  94        fmc_writel(fmc, val, addr);
  95        *offp += count;
  96        return count;
  97}
  98
  99static const struct file_operations fc_fops = {
 100        .owner = THIS_MODULE,
 101        .open = fc_open,
 102        .release = fc_release,
 103        .llseek = generic_file_llseek,
 104        .read = fc_read,
 105        .write = fc_write,
 106};
 107
 108
 109/* Device part .. */
 110static int fc_probe(struct fmc_device *fmc);
 111static int fc_remove(struct fmc_device *fmc);
 112
 113static struct fmc_driver fc_drv = {
 114        .version = FMC_VERSION,
 115        .driver.name = KBUILD_MODNAME,
 116        .probe = fc_probe,
 117        .remove = fc_remove,
 118        /* no table: we want to match everything */
 119};
 120
 121/* We accept the generic busid parameter */
 122FMC_PARAM_BUSID(fc_drv);
 123
 124/* probe and remove must allocate and release a misc device */
 125static int fc_probe(struct fmc_device *fmc)
 126{
 127        int ret;
 128        int index = 0;
 129
 130        struct fc_instance *fc;
 131
 132        if (fmc->op->validate)
 133                index = fmc->op->validate(fmc, &fc_drv);
 134        if (index < 0)
 135                return -EINVAL; /* not our device: invalid */
 136
 137        /* Create a char device: we want to create it anew */
 138        fc = kzalloc(sizeof(*fc), GFP_KERNEL);
 139        if (!fc)
 140                return -ENOMEM;
 141        fc->fmc = fmc;
 142        fc->misc.minor = MISC_DYNAMIC_MINOR;
 143        fc->misc.fops = &fc_fops;
 144        fc->misc.name = kstrdup(dev_name(&fmc->dev), GFP_KERNEL);
 145
 146        ret = misc_register(&fc->misc);
 147        if (ret < 0)
 148                goto out;
 149        spin_lock(&fc_lock);
 150        list_add(&fc->list, &fc_devices);
 151        spin_unlock(&fc_lock);
 152        dev_info(&fc->fmc->dev, "Created misc device \"%s\"\n",
 153                 fc->misc.name);
 154        return 0;
 155
 156out:
 157        kfree(fc->misc.name);
 158        kfree(fc);
 159        return ret;
 160}
 161
 162static int fc_remove(struct fmc_device *fmc)
 163{
 164        struct fc_instance *fc;
 165
 166        list_for_each_entry(fc, &fc_devices, list)
 167                if (fc->fmc == fmc)
 168                        break;
 169        if (fc->fmc != fmc) {
 170                dev_err(&fmc->dev, "remove called but not found\n");
 171                return -ENODEV;
 172        }
 173
 174        spin_lock(&fc_lock);
 175        list_del(&fc->list);
 176        spin_unlock(&fc_lock);
 177        misc_deregister(&fc->misc);
 178        kfree(fc->misc.name);
 179        kfree(fc);
 180
 181        return 0;
 182}
 183
 184
 185static int fc_init(void)
 186{
 187        int ret;
 188
 189        ret = fmc_driver_register(&fc_drv);
 190        return ret;
 191}
 192
 193static void fc_exit(void)
 194{
 195        fmc_driver_unregister(&fc_drv);
 196}
 197
 198module_init(fc_init);
 199module_exit(fc_exit);
 200
 201MODULE_LICENSE("GPL");
 202