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        index = fmc_validate(fmc, &fc_drv);
 133        if (index < 0)
 134                return -EINVAL; /* not our device: invalid */
 135
 136        /* Create a char device: we want to create it anew */
 137        fc = kzalloc(sizeof(*fc), GFP_KERNEL);
 138        if (!fc)
 139                return -ENOMEM;
 140        fc->fmc = fmc;
 141        fc->misc.minor = MISC_DYNAMIC_MINOR;
 142        fc->misc.fops = &fc_fops;
 143        fc->misc.name = kstrdup(dev_name(&fmc->dev), GFP_KERNEL);
 144
 145        ret = misc_register(&fc->misc);
 146        if (ret < 0)
 147                goto out;
 148        spin_lock(&fc_lock);
 149        list_add(&fc->list, &fc_devices);
 150        spin_unlock(&fc_lock);
 151        dev_info(&fc->fmc->dev, "Created misc device \"%s\"\n",
 152                 fc->misc.name);
 153        return 0;
 154
 155out:
 156        kfree(fc->misc.name);
 157        kfree(fc);
 158        return ret;
 159}
 160
 161static int fc_remove(struct fmc_device *fmc)
 162{
 163        struct fc_instance *fc;
 164
 165        list_for_each_entry(fc, &fc_devices, list)
 166                if (fc->fmc == fmc)
 167                        break;
 168        if (fc->fmc != fmc) {
 169                dev_err(&fmc->dev, "remove called but not found\n");
 170                return -ENODEV;
 171        }
 172
 173        spin_lock(&fc_lock);
 174        list_del(&fc->list);
 175        spin_unlock(&fc_lock);
 176        misc_deregister(&fc->misc);
 177        kfree(fc->misc.name);
 178        kfree(fc);
 179
 180        return 0;
 181}
 182
 183
 184static int fc_init(void)
 185{
 186        int ret;
 187
 188        ret = fmc_driver_register(&fc_drv);
 189        return ret;
 190}
 191
 192static void fc_exit(void)
 193{
 194        fmc_driver_unregister(&fc_drv);
 195}
 196
 197module_init(fc_init);
 198module_exit(fc_exit);
 199
 200MODULE_LICENSE("GPL");
 201