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