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