• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 */
fc_open(struct inode * ino,struct file * f)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 
fc_release(struct inode * ino,struct file * f)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 */
fc_read(struct file * f,char __user * buf,size_t count,loff_t * offp)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 
fc_write(struct file * f,const char __user * buf,size_t count,loff_t * offp)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 */
fc_probe(struct fmc_device * fmc)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 	if (fmc->op->validate)
133 		index = fmc->op->validate(fmc, &fc_drv);
134 	if (index < 0)
135 		return -EINVAL; /* not our device: invalid */
136 
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);
145 
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;
155 
156 out:
157 	kfree(fc->misc.name);
158 	kfree(fc);
159 	return ret;
160 }
161 
fc_remove(struct fmc_device * fmc)162 static int fc_remove(struct fmc_device *fmc)
163 {
164 	struct fc_instance *fc;
165 
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;
172 	}
173 
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);
180 
181 	return 0;
182 }
183 
184 
fc_init(void)185 static int fc_init(void)
186 {
187 	int ret;
188 
189 	ret = fmc_driver_register(&fc_drv);
190 	return ret;
191 }
192 
fc_exit(void)193 static void fc_exit(void)
194 {
195 	fmc_driver_unregister(&fc_drv);
196 }
197 
198 module_init(fc_init);
199 module_exit(fc_exit);
200 
201 MODULE_LICENSE("GPL");
202