Linux Audio

Check our new training course

Loading...
Note: File does not exist in v5.9.
  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	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
155out:
156	kfree(fc->misc.name);
157	kfree(fc);
158	return ret;
159}
160
161static 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
184static int fc_init(void)
185{
186	int ret;
187
188	ret = fmc_driver_register(&fc_drv);
189	return ret;
190}
191
192static void fc_exit(void)
193{
194	fmc_driver_unregister(&fc_drv);
195}
196
197module_init(fc_init);
198module_exit(fc_exit);
199
200MODULE_LICENSE("GPL");