1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (C) 2012 CERN (www.cern.ch)
4 * Author: Alessandro Rubini <rubini@gnudd.com>
6 * This work is part of the White Rabbit project, a research effort led
7 * by CERN, the European Institute for Nuclear Research.
9 #include <linux/module.h>
10 #include <linux/init.h>
11 #include <linux/list.h>
12 #include <linux/slab.h>
14 #include <linux/miscdevice.h>
15 #include <linux/spinlock.h>
16 #include <linux/fmc.h>
17 #include <linux/uaccess.h>
19 static LIST_HEAD(fc_devices);
20 static DEFINE_SPINLOCK(fc_lock);
23 struct list_head list;
24 struct fmc_device *fmc;
25 struct miscdevice misc;
28 /* at open time, we must identify our device */
29 static int fc_open(struct inode *ino, struct file *f)
31 struct fmc_device *fmc;
32 struct fc_instance *fc;
33 int minor = iminor(ino);
35 list_for_each_entry(fc, &fc_devices, list)
36 if (fc->misc.minor == minor)
38 if (fc->misc.minor != minor)
41 if (try_module_get(fmc->owner) == 0)
44 f->private_data = fmc;
48 static int fc_release(struct inode *ino, struct file *f)
50 struct fmc_device *fmc = f->private_data;
51 module_put(fmc->owner);
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,
59 struct fmc_device *fmc = f->private_data;
63 if (count < sizeof(val))
68 if (addr > fmc->memlen)
69 return -ESPIPE; /* Illegal seek */
70 val = fmc_readl(fmc, addr);
71 if (copy_to_user(buf, &val, count))
77 static ssize_t fc_write(struct file *f, const char __user *buf, size_t count,
80 struct fmc_device *fmc = f->private_data;
84 if (count < sizeof(val))
89 if (addr > fmc->memlen)
90 return -ESPIPE; /* Illegal seek */
91 if (copy_from_user(&val, buf, count))
93 fmc_writel(fmc, val, addr);
98 static const struct file_operations fc_fops = {
101 .release = fc_release,
102 .llseek = generic_file_llseek,
109 static int fc_probe(struct fmc_device *fmc);
110 static int fc_remove(struct fmc_device *fmc);
112 static struct fmc_driver fc_drv = {
113 .version = FMC_VERSION,
114 .driver.name = KBUILD_MODNAME,
117 /* no table: we want to match everything */
120 /* We accept the generic busid parameter */
121 FMC_PARAM_BUSID(fc_drv);
123 /* probe and remove must allocate and release a misc device */
124 static int fc_probe(struct fmc_device *fmc)
129 struct fc_instance *fc;
131 index = fmc_validate(fmc, &fc_drv);
133 return -EINVAL; /* not our device: invalid */
135 /* Create a char device: we want to create it anew */
136 fc = kzalloc(sizeof(*fc), GFP_KERNEL);
140 fc->misc.minor = MISC_DYNAMIC_MINOR;
141 fc->misc.fops = &fc_fops;
142 fc->misc.name = kstrdup(dev_name(&fmc->dev), GFP_KERNEL);
144 ret = misc_register(&fc->misc);
148 list_add(&fc->list, &fc_devices);
149 spin_unlock(&fc_lock);
150 dev_info(&fc->fmc->dev, "Created misc device \"%s\"\n",
155 kfree(fc->misc.name);
160 static int fc_remove(struct fmc_device *fmc)
162 struct fc_instance *fc;
164 list_for_each_entry(fc, &fc_devices, list)
167 if (fc->fmc != fmc) {
168 dev_err(&fmc->dev, "remove called but not found\n");
174 spin_unlock(&fc_lock);
175 misc_deregister(&fc->misc);
176 kfree(fc->misc.name);
183 static int fc_init(void)
187 ret = fmc_driver_register(&fc_drv);
191 static void fc_exit(void)
193 fmc_driver_unregister(&fc_drv);
196 module_init(fc_init);
197 module_exit(fc_exit);
199 MODULE_LICENSE("GPL");