Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris...
[linux/fpc-iii.git] / drivers / fmc / fmc-chardev.c
blobace6ef24d15e66eddb01d82ba6079a4796a19ec8
1 /*
2 * Copyright (C) 2012 CERN (www.cern.ch)
3 * Author: Alessandro Rubini <rubini@gnudd.com>
5 * Released according to the GNU GPL, version 2 or any later version.
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>
20 static LIST_HEAD(fc_devices);
21 static DEFINE_SPINLOCK(fc_lock);
23 struct fc_instance {
24 struct list_head list;
25 struct fmc_device *fmc;
26 struct miscdevice misc;
29 /* at open time, we must identify our device */
30 static int fc_open(struct inode *ino, struct file *f)
32 struct fmc_device *fmc;
33 struct fc_instance *fc;
34 int minor = iminor(ino);
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;
45 f->private_data = fmc;
46 return 0;
49 static int fc_release(struct inode *ino, struct file *f)
51 struct fmc_device *fmc = f->private_data;
52 module_put(fmc->owner);
53 return 0;
56 /* read and write are simple after the default llseek has been used */
57 static ssize_t fc_read(struct file *f, char __user *buf, size_t count,
58 loff_t *offp)
60 struct fmc_device *fmc = f->private_data;
61 unsigned long addr;
62 uint32_t val;
64 if (count < sizeof(val))
65 return -EINVAL;
66 count = sizeof(val);
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;
78 static ssize_t fc_write(struct file *f, const char __user *buf, size_t count,
79 loff_t *offp)
81 struct fmc_device *fmc = f->private_data;
82 unsigned long addr;
83 uint32_t val;
85 if (count < sizeof(val))
86 return -EINVAL;
87 count = sizeof(val);
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;
99 static 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,
109 /* Device part .. */
110 static int fc_probe(struct fmc_device *fmc);
111 static int fc_remove(struct fmc_device *fmc);
113 static 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 */
121 /* We accept the generic busid parameter */
122 FMC_PARAM_BUSID(fc_drv);
124 /* probe and remove must allocate and release a misc device */
125 static int fc_probe(struct fmc_device *fmc)
127 int ret;
128 int index = 0;
130 struct fc_instance *fc;
132 if (fmc->op->validate)
133 index = fmc->op->validate(fmc, &fc_drv);
134 if (index < 0)
135 return -EINVAL; /* not our device: invalid */
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);
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;
156 out:
157 kfree(fc->misc.name);
158 kfree(fc);
159 return ret;
162 static int fc_remove(struct fmc_device *fmc)
164 struct fc_instance *fc;
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;
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);
181 return 0;
185 static int fc_init(void)
187 int ret;
189 ret = fmc_driver_register(&fc_drv);
190 return ret;
193 static void fc_exit(void)
195 fmc_driver_unregister(&fc_drv);
198 module_init(fc_init);
199 module_exit(fc_exit);
201 MODULE_LICENSE("GPL");