Merge tag 'iio-fixes-for-4.14a' of git://git.kernel.org/pub/scm/linux/kernel/git...
[sfrench/cifs-2.6.git] / drivers / fmc / fmc-chardev.c
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
20 static LIST_HEAD(fc_devices);
21 static DEFINE_SPINLOCK(fc_lock);
22
23 struct 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 */
30 static 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
49 static 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 */
57 static 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
78 static 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
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,
106 };
107
108
109 /* Device part .. */
110 static int fc_probe(struct fmc_device *fmc);
111 static int fc_remove(struct fmc_device *fmc);
112
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 */
119 };
120
121 /* We accept the generic busid parameter */
122 FMC_PARAM_BUSID(fc_drv);
123
124 /* probe and remove must allocate and release a misc device */
125 static 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
155 out:
156         kfree(fc->misc.name);
157         kfree(fc);
158         return ret;
159 }
160
161 static 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
184 static int fc_init(void)
185 {
186         int ret;
187
188         ret = fmc_driver_register(&fc_drv);
189         return ret;
190 }
191
192 static void fc_exit(void)
193 {
194         fmc_driver_unregister(&fc_drv);
195 }
196
197 module_init(fc_init);
198 module_exit(fc_exit);
199
200 MODULE_LICENSE("GPL");