2 * Copyright (C) 2012 CERN (www.cern.ch)
3 * Author: Alessandro Rubini <rubini@gnudd.com>
5 * Released according to the GNU GPL, version 2 or any later version.
7 * This work is part of the White Rabbit project, a research effort led
8 * by CERN, the European Institute for Nuclear Research.
10 #include <linux/module.h>
11 #include <linux/init.h>
12 #include <linux/list.h>
13 #include <linux/slab.h>
15 #include <linux/miscdevice.h>
16 #include <linux/spinlock.h>
17 #include <linux/fmc.h>
18 #include <linux/uaccess.h>
20 static LIST_HEAD(fc_devices
);
21 static DEFINE_SPINLOCK(fc_lock
);
24 struct list_head list
;
25 struct fmc_device
*fmc
;
26 struct miscdevice misc
;
29 /* at open time, we must identify our device */
30 static int fc_open(struct inode
*ino
, struct file
*f
)
32 struct fmc_device
*fmc
;
33 struct fc_instance
*fc
;
34 int minor
= iminor(ino
);
36 list_for_each_entry(fc
, &fc_devices
, list
)
37 if (fc
->misc
.minor
== minor
)
39 if (fc
->misc
.minor
!= minor
)
42 if (try_module_get(fmc
->owner
) == 0)
45 f
->private_data
= fmc
;
49 static int fc_release(struct inode
*ino
, struct file
*f
)
51 struct fmc_device
*fmc
= f
->private_data
;
52 module_put(fmc
->owner
);
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
,
60 struct fmc_device
*fmc
= f
->private_data
;
64 if (count
< sizeof(val
))
69 if (addr
> fmc
->memlen
)
70 return -ESPIPE
; /* Illegal seek */
71 val
= fmc_readl(fmc
, addr
);
72 if (copy_to_user(buf
, &val
, count
))
78 static ssize_t
fc_write(struct file
*f
, const char __user
*buf
, size_t count
,
81 struct fmc_device
*fmc
= f
->private_data
;
85 if (count
< sizeof(val
))
90 if (addr
> fmc
->memlen
)
91 return -ESPIPE
; /* Illegal seek */
92 if (copy_from_user(&val
, buf
, count
))
94 fmc_writel(fmc
, val
, addr
);
99 static const struct file_operations fc_fops
= {
100 .owner
= THIS_MODULE
,
102 .release
= fc_release
,
103 .llseek
= generic_file_llseek
,
110 static int fc_probe(struct fmc_device
*fmc
);
111 static int fc_remove(struct fmc_device
*fmc
);
113 static struct fmc_driver fc_drv
= {
114 .version
= FMC_VERSION
,
115 .driver
.name
= KBUILD_MODNAME
,
118 /* no table: we want to match everything */
121 /* We accept the generic busid parameter */
122 FMC_PARAM_BUSID(fc_drv
);
124 /* probe and remove must allocate and release a misc device */
125 static int fc_probe(struct fmc_device
*fmc
)
130 struct fc_instance
*fc
;
132 if (fmc
->op
->validate
)
133 index
= fmc
->op
->validate(fmc
, &fc_drv
);
135 return -EINVAL
; /* not our device: invalid */
137 /* Create a char device: we want to create it anew */
138 fc
= kzalloc(sizeof(*fc
), GFP_KERNEL
);
142 fc
->misc
.minor
= MISC_DYNAMIC_MINOR
;
143 fc
->misc
.fops
= &fc_fops
;
144 fc
->misc
.name
= kstrdup(dev_name(&fmc
->dev
), GFP_KERNEL
);
146 ret
= misc_register(&fc
->misc
);
150 list_add(&fc
->list
, &fc_devices
);
151 spin_unlock(&fc_lock
);
152 dev_info(&fc
->fmc
->dev
, "Created misc device \"%s\"\n",
157 kfree(fc
->misc
.name
);
162 static int fc_remove(struct fmc_device
*fmc
)
164 struct fc_instance
*fc
;
166 list_for_each_entry(fc
, &fc_devices
, list
)
169 if (fc
->fmc
!= fmc
) {
170 dev_err(&fmc
->dev
, "remove called but not found\n");
176 spin_unlock(&fc_lock
);
177 misc_deregister(&fc
->misc
);
178 kfree(fc
->misc
.name
);
185 static int fc_init(void)
189 ret
= fmc_driver_register(&fc_drv
);
193 static void fc_exit(void)
195 fmc_driver_unregister(&fc_drv
);
198 module_init(fc_init
);
199 module_exit(fc_exit
);
201 MODULE_LICENSE("GPL");