1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * VMEbus User access driver
4 *
5 * Author: Martyn Welch <martyn.welch@ge.com>
6 * Copyright 2008 GE Intelligent Platforms Embedded Systems, Inc.
7 *
8 * Based on work by:
9 * Tom Armistead and Ajit Prem
10 * Copyright 2004 Motorola Inc.
11 */
12
13#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
14
15#include <linux/refcount.h>
16#include <linux/cdev.h>
17#include <linux/delay.h>
18#include <linux/device.h>
19#include <linux/dma-mapping.h>
20#include <linux/errno.h>
21#include <linux/init.h>
22#include <linux/ioctl.h>
23#include <linux/kernel.h>
24#include <linux/mm.h>
25#include <linux/module.h>
26#include <linux/pagemap.h>
27#include <linux/pci.h>
28#include <linux/mutex.h>
29#include <linux/slab.h>
30#include <linux/spinlock.h>
31#include <linux/syscalls.h>
32#include <linux/types.h>
33
34#include <linux/io.h>
35#include <linux/uaccess.h>
36
37#include "vme.h"
38#include "vme_user.h"
39
40#define DRIVER_NAME "vme_user"
41
42static int bus[VME_USER_BUS_MAX];
43static unsigned int bus_num;
44
45/* Currently Documentation/admin-guide/devices.rst defines the
46 * following for VME:
47 *
48 * 221 char VME bus
49 * 0 = /dev/bus/vme/m0 First master image
50 * 1 = /dev/bus/vme/m1 Second master image
51 * 2 = /dev/bus/vme/m2 Third master image
52 * 3 = /dev/bus/vme/m3 Fourth master image
53 * 4 = /dev/bus/vme/s0 First slave image
54 * 5 = /dev/bus/vme/s1 Second slave image
55 * 6 = /dev/bus/vme/s2 Third slave image
56 * 7 = /dev/bus/vme/s3 Fourth slave image
57 * 8 = /dev/bus/vme/ctl Control
58 *
59 * It is expected that all VME bus drivers will use the
60 * same interface. For interface documentation see
61 * http://www.vmelinux.org/.
62 *
63 * However the VME driver at http://www.vmelinux.org/ is rather old and doesn't
64 * even support the tsi148 chipset (which has 8 master and 8 slave windows).
65 * We'll run with this for now as far as possible, however it probably makes
66 * sense to get rid of the old mappings and just do everything dynamically.
67 *
68 * So for now, we'll restrict the driver to providing 4 masters and 4 slaves as
69 * defined above and try to support at least some of the interface from
70 * http://www.vmelinux.org/ as an alternative the driver can be written
71 * providing a saner interface later.
72 *
73 * The vmelinux.org driver never supported slave images, the devices reserved
74 * for slaves were repurposed to support all 8 master images on the UniverseII!
75 * We shall support 4 masters and 4 slaves with this driver.
76 */
77#define VME_MAJOR 221 /* VME Major Device Number */
78#define VME_DEVS 9 /* Number of dev entries */
79
80#define MASTER_MINOR 0
81#define MASTER_MAX 3
82#define SLAVE_MINOR 4
83#define SLAVE_MAX 7
84#define CONTROL_MINOR 8
85
86#define PCI_BUF_SIZE 0x20000 /* Size of one slave image buffer */
87
88/*
89 * Structure to handle image related parameters.
90 */
91struct image_desc {
92 void *kern_buf; /* Buffer address in kernel space */
93 dma_addr_t pci_buf; /* Buffer address in PCI address space */
94 unsigned long long size_buf; /* Buffer size */
95 struct mutex mutex; /* Mutex for locking image */
96 struct device *device; /* Sysfs device */
97 struct vme_resource *resource; /* VME resource */
98 int mmap_count; /* Number of current mmap's */
99};
100
101static struct image_desc image[VME_DEVS];
102
103static struct cdev *vme_user_cdev; /* Character device */
104static struct vme_dev *vme_user_bridge; /* Pointer to user device */
105
106static const struct class vme_user_sysfs_class = {
107 .name = DRIVER_NAME,
108};
109static const int type[VME_DEVS] = { MASTER_MINOR, MASTER_MINOR,
110 MASTER_MINOR, MASTER_MINOR,
111 SLAVE_MINOR, SLAVE_MINOR,
112 SLAVE_MINOR, SLAVE_MINOR,
113 CONTROL_MINOR
114 };
115
116struct vme_user_vma_priv {
117 unsigned int minor;
118 refcount_t refcnt;
119};
120
121static ssize_t resource_to_user(int minor, char __user *buf, size_t count,
122 loff_t *ppos)
123{
124 ssize_t copied = 0;
125
126 if (count > image[minor].size_buf)
127 count = image[minor].size_buf;
128
129 copied = vme_master_read(image[minor].resource, image[minor].kern_buf,
130 count, *ppos);
131 if (copied < 0)
132 return (int)copied;
133
134 if (copy_to_user(to: buf, from: image[minor].kern_buf, n: (unsigned long)copied))
135 return -EFAULT;
136
137 return copied;
138}
139
140static ssize_t resource_from_user(unsigned int minor, const char __user *buf,
141 size_t count, loff_t *ppos)
142{
143 if (count > image[minor].size_buf)
144 count = image[minor].size_buf;
145
146 if (copy_from_user(to: image[minor].kern_buf, from: buf, n: (unsigned long)count))
147 return -EFAULT;
148
149 return vme_master_write(image[minor].resource, image[minor].kern_buf,
150 count, *ppos);
151}
152
153static ssize_t buffer_to_user(unsigned int minor, char __user *buf,
154 size_t count, loff_t *ppos)
155{
156 void *image_ptr;
157
158 image_ptr = image[minor].kern_buf + *ppos;
159 if (copy_to_user(to: buf, from: image_ptr, n: (unsigned long)count))
160 return -EFAULT;
161
162 return count;
163}
164
165static ssize_t buffer_from_user(unsigned int minor, const char __user *buf,
166 size_t count, loff_t *ppos)
167{
168 void *image_ptr;
169
170 image_ptr = image[minor].kern_buf + *ppos;
171 if (copy_from_user(to: image_ptr, from: buf, n: (unsigned long)count))
172 return -EFAULT;
173
174 return count;
175}
176
177static ssize_t vme_user_read(struct file *file, char __user *buf, size_t count,
178 loff_t *ppos)
179{
180 unsigned int minor = iminor(inode: file_inode(f: file));
181 ssize_t retval;
182 size_t image_size;
183
184 if (minor == CONTROL_MINOR)
185 return 0;
186
187 mutex_lock(&image[minor].mutex);
188
189 /* XXX Do we *really* want this helper - we can use vme_*_get ? */
190 image_size = vme_get_size(image[minor].resource);
191
192 /* Ensure we are starting at a valid location */
193 if ((*ppos < 0) || (*ppos > (image_size - 1))) {
194 mutex_unlock(lock: &image[minor].mutex);
195 return 0;
196 }
197
198 /* Ensure not reading past end of the image */
199 if (*ppos + count > image_size)
200 count = image_size - *ppos;
201
202 switch (type[minor]) {
203 case MASTER_MINOR:
204 retval = resource_to_user(minor, buf, count, ppos);
205 break;
206 case SLAVE_MINOR:
207 retval = buffer_to_user(minor, buf, count, ppos);
208 break;
209 default:
210 retval = -EINVAL;
211 }
212
213 mutex_unlock(lock: &image[minor].mutex);
214 if (retval > 0)
215 *ppos += retval;
216
217 return retval;
218}
219
220static ssize_t vme_user_write(struct file *file, const char __user *buf,
221 size_t count, loff_t *ppos)
222{
223 unsigned int minor = iminor(inode: file_inode(f: file));
224 ssize_t retval;
225 size_t image_size;
226
227 if (minor == CONTROL_MINOR)
228 return 0;
229
230 mutex_lock(&image[minor].mutex);
231
232 image_size = vme_get_size(image[minor].resource);
233
234 /* Ensure we are starting at a valid location */
235 if ((*ppos < 0) || (*ppos > (image_size - 1))) {
236 mutex_unlock(lock: &image[minor].mutex);
237 return 0;
238 }
239
240 /* Ensure not reading past end of the image */
241 if (*ppos + count > image_size)
242 count = image_size - *ppos;
243
244 switch (type[minor]) {
245 case MASTER_MINOR:
246 retval = resource_from_user(minor, buf, count, ppos);
247 break;
248 case SLAVE_MINOR:
249 retval = buffer_from_user(minor, buf, count, ppos);
250 break;
251 default:
252 retval = -EINVAL;
253 }
254
255 mutex_unlock(lock: &image[minor].mutex);
256
257 if (retval > 0)
258 *ppos += retval;
259
260 return retval;
261}
262
263static loff_t vme_user_llseek(struct file *file, loff_t off, int whence)
264{
265 unsigned int minor = iminor(inode: file_inode(f: file));
266 size_t image_size;
267 loff_t res;
268
269 switch (type[minor]) {
270 case MASTER_MINOR:
271 case SLAVE_MINOR:
272 mutex_lock(&image[minor].mutex);
273 image_size = vme_get_size(image[minor].resource);
274 res = fixed_size_llseek(file, offset: off, whence, size: image_size);
275 mutex_unlock(lock: &image[minor].mutex);
276 return res;
277 }
278
279 return -EINVAL;
280}
281
282/*
283 * The ioctls provided by the old VME access method (the one at vmelinux.org)
284 * are most certainly wrong as the effectively push the registers layout
285 * through to user space. Given that the VME core can handle multiple bridges,
286 * with different register layouts this is most certainly not the way to go.
287 *
288 * We aren't using the structures defined in the Motorola driver either - these
289 * are also quite low level, however we should use the definitions that have
290 * already been defined.
291 */
292static int vme_user_ioctl(struct inode *inode, struct file *file,
293 unsigned int cmd, unsigned long arg)
294{
295 struct vme_master master;
296 struct vme_slave slave;
297 struct vme_irq_id irq_req;
298 unsigned long copied;
299 unsigned int minor = iminor(inode);
300 int retval;
301 dma_addr_t pci_addr;
302 void __user *argp = (void __user *)arg;
303
304 switch (type[minor]) {
305 case CONTROL_MINOR:
306 switch (cmd) {
307 case VME_IRQ_GEN:
308 copied = copy_from_user(to: &irq_req, from: argp,
309 n: sizeof(irq_req));
310 if (copied) {
311 pr_warn("Partial copy from userspace\n");
312 return -EFAULT;
313 }
314
315 return vme_irq_generate(vme_user_bridge,
316 irq_req.level,
317 irq_req.statid);
318 }
319 break;
320 case MASTER_MINOR:
321 switch (cmd) {
322 case VME_GET_MASTER:
323 memset(&master, 0, sizeof(master));
324
325 /* XXX We do not want to push aspace, cycle and width
326 * to userspace as they are
327 */
328 retval = vme_master_get(image[minor].resource,
329 &master.enable,
330 &master.vme_addr,
331 &master.size, &master.aspace,
332 &master.cycle, &master.dwidth);
333
334 copied = copy_to_user(to: argp, from: &master,
335 n: sizeof(master));
336 if (copied) {
337 pr_warn("Partial copy to userspace\n");
338 return -EFAULT;
339 }
340
341 return retval;
342
343 case VME_SET_MASTER:
344
345 if (image[minor].mmap_count != 0) {
346 pr_warn("Can't adjust mapped window\n");
347 return -EPERM;
348 }
349
350 copied = copy_from_user(to: &master, from: argp, n: sizeof(master));
351 if (copied) {
352 pr_warn("Partial copy from userspace\n");
353 return -EFAULT;
354 }
355
356 /* XXX We do not want to push aspace, cycle and width
357 * to userspace as they are
358 */
359 return vme_master_set(image[minor].resource,
360 master.enable, master.vme_addr, master.size,
361 master.aspace, master.cycle, master.dwidth);
362
363 break;
364 }
365 break;
366 case SLAVE_MINOR:
367 switch (cmd) {
368 case VME_GET_SLAVE:
369 memset(&slave, 0, sizeof(slave));
370
371 /* XXX We do not want to push aspace, cycle and width
372 * to userspace as they are
373 */
374 retval = vme_slave_get(image[minor].resource,
375 &slave.enable, &slave.vme_addr,
376 &slave.size, &pci_addr,
377 &slave.aspace, &slave.cycle);
378
379 copied = copy_to_user(to: argp, from: &slave,
380 n: sizeof(slave));
381 if (copied) {
382 pr_warn("Partial copy to userspace\n");
383 return -EFAULT;
384 }
385
386 return retval;
387
388 case VME_SET_SLAVE:
389
390 copied = copy_from_user(to: &slave, from: argp, n: sizeof(slave));
391 if (copied) {
392 pr_warn("Partial copy from userspace\n");
393 return -EFAULT;
394 }
395
396 /* XXX We do not want to push aspace, cycle and width
397 * to userspace as they are
398 */
399 return vme_slave_set(image[minor].resource,
400 slave.enable, slave.vme_addr, slave.size,
401 image[minor].pci_buf, slave.aspace,
402 slave.cycle);
403
404 break;
405 }
406 break;
407 }
408
409 return -EINVAL;
410}
411
412static long
413vme_user_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
414{
415 int ret;
416 struct inode *inode = file_inode(f: file);
417 unsigned int minor = iminor(inode);
418
419 mutex_lock(&image[minor].mutex);
420 ret = vme_user_ioctl(inode, file, cmd, arg);
421 mutex_unlock(lock: &image[minor].mutex);
422
423 return ret;
424}
425
426static void vme_user_vm_open(struct vm_area_struct *vma)
427{
428 struct vme_user_vma_priv *vma_priv = vma->vm_private_data;
429
430 refcount_inc(r: &vma_priv->refcnt);
431}
432
433static void vme_user_vm_close(struct vm_area_struct *vma)
434{
435 struct vme_user_vma_priv *vma_priv = vma->vm_private_data;
436 unsigned int minor = vma_priv->minor;
437
438 if (!refcount_dec_and_test(r: &vma_priv->refcnt))
439 return;
440
441 mutex_lock(&image[minor].mutex);
442 image[minor].mmap_count--;
443 mutex_unlock(lock: &image[minor].mutex);
444
445 kfree(objp: vma_priv);
446}
447
448static const struct vm_operations_struct vme_user_vm_ops = {
449 .open = vme_user_vm_open,
450 .close = vme_user_vm_close,
451};
452
453static int vme_user_master_mmap(unsigned int minor, struct vm_area_struct *vma)
454{
455 int err;
456 struct vme_user_vma_priv *vma_priv;
457
458 mutex_lock(&image[minor].mutex);
459
460 err = vme_master_mmap(resource: image[minor].resource, vma);
461 if (err) {
462 mutex_unlock(lock: &image[minor].mutex);
463 return err;
464 }
465
466 vma_priv = kmalloc(size: sizeof(*vma_priv), GFP_KERNEL);
467 if (!vma_priv) {
468 mutex_unlock(lock: &image[minor].mutex);
469 return -ENOMEM;
470 }
471
472 vma_priv->minor = minor;
473 refcount_set(r: &vma_priv->refcnt, n: 1);
474 vma->vm_ops = &vme_user_vm_ops;
475 vma->vm_private_data = vma_priv;
476
477 image[minor].mmap_count++;
478
479 mutex_unlock(lock: &image[minor].mutex);
480
481 return 0;
482}
483
484static int vme_user_mmap(struct file *file, struct vm_area_struct *vma)
485{
486 unsigned int minor = iminor(inode: file_inode(f: file));
487
488 if (type[minor] == MASTER_MINOR)
489 return vme_user_master_mmap(minor, vma);
490
491 return -ENODEV;
492}
493
494static const struct file_operations vme_user_fops = {
495 .read = vme_user_read,
496 .write = vme_user_write,
497 .llseek = vme_user_llseek,
498 .unlocked_ioctl = vme_user_unlocked_ioctl,
499 .compat_ioctl = compat_ptr_ioctl,
500 .mmap = vme_user_mmap,
501};
502
503static int vme_user_match(struct vme_dev *vdev)
504{
505 int i;
506
507 int cur_bus = vme_bus_num(vdev);
508 int cur_slot = vme_slot_num(vdev);
509
510 for (i = 0; i < bus_num; i++)
511 if ((cur_bus == bus[i]) && (cur_slot == vdev->num))
512 return 1;
513
514 return 0;
515}
516
517/*
518 * In this simple access driver, the old behaviour is being preserved as much
519 * as practical. We will therefore reserve the buffers and request the images
520 * here so that we don't have to do it later.
521 */
522static int vme_user_probe(struct vme_dev *vdev)
523{
524 int i, err;
525 char *name;
526
527 /* Save pointer to the bridge device */
528 if (vme_user_bridge) {
529 dev_err(&vdev->dev, "Driver can only be loaded for 1 device\n");
530 err = -EINVAL;
531 goto err_dev;
532 }
533 vme_user_bridge = vdev;
534
535 /* Initialise descriptors */
536 for (i = 0; i < VME_DEVS; i++) {
537 image[i].kern_buf = NULL;
538 image[i].pci_buf = 0;
539 mutex_init(&image[i].mutex);
540 image[i].device = NULL;
541 image[i].resource = NULL;
542 }
543
544 /* Assign major and minor numbers for the driver */
545 err = register_chrdev_region(MKDEV(VME_MAJOR, 0), VME_DEVS, DRIVER_NAME);
546 if (err) {
547 dev_warn(&vdev->dev, "Error getting Major Number %d for driver.\n",
548 VME_MAJOR);
549 goto err_region;
550 }
551
552 /* Register the driver as a char device */
553 vme_user_cdev = cdev_alloc();
554 if (!vme_user_cdev) {
555 err = -ENOMEM;
556 goto err_char;
557 }
558 vme_user_cdev->ops = &vme_user_fops;
559 vme_user_cdev->owner = THIS_MODULE;
560 err = cdev_add(vme_user_cdev, MKDEV(VME_MAJOR, 0), VME_DEVS);
561 if (err)
562 goto err_class;
563
564 /* Request slave resources and allocate buffers (128kB wide) */
565 for (i = SLAVE_MINOR; i < (SLAVE_MAX + 1); i++) {
566 /* XXX Need to properly request attributes */
567 /* For ca91cx42 bridge there are only two slave windows
568 * supporting A16 addressing, so we request A24 supported
569 * by all windows.
570 */
571 image[i].resource = vme_slave_request(vme_user_bridge,
572 VME_A24, VME_SCT);
573 if (!image[i].resource) {
574 dev_warn(&vdev->dev,
575 "Unable to allocate slave resource\n");
576 err = -ENOMEM;
577 goto err_slave;
578 }
579 image[i].size_buf = PCI_BUF_SIZE;
580 image[i].kern_buf = vme_alloc_consistent(image[i].resource,
581 image[i].size_buf,
582 &image[i].pci_buf);
583 if (!image[i].kern_buf) {
584 dev_warn(&vdev->dev,
585 "Unable to allocate memory for buffer\n");
586 image[i].pci_buf = 0;
587 vme_slave_free(image[i].resource);
588 err = -ENOMEM;
589 goto err_slave;
590 }
591 }
592
593 /*
594 * Request master resources allocate page sized buffers for small
595 * reads and writes
596 */
597 for (i = MASTER_MINOR; i < (MASTER_MAX + 1); i++) {
598 /* XXX Need to properly request attributes */
599 image[i].resource = vme_master_request(vme_user_bridge,
600 VME_A32, VME_SCT,
601 VME_D32);
602 if (!image[i].resource) {
603 dev_warn(&vdev->dev,
604 "Unable to allocate master resource\n");
605 err = -ENOMEM;
606 goto err_master;
607 }
608 image[i].size_buf = PCI_BUF_SIZE;
609 image[i].kern_buf = kmalloc(size: image[i].size_buf, GFP_KERNEL);
610 if (!image[i].kern_buf) {
611 err = -ENOMEM;
612 vme_master_free(image[i].resource);
613 goto err_master;
614 }
615 }
616
617 /* Create sysfs entries - on udev systems this creates the dev files */
618 err = class_register(class: &vme_user_sysfs_class);
619 if (err) {
620 dev_err(&vdev->dev, "Error creating vme_user class.\n");
621 goto err_master;
622 }
623
624 /* Add sysfs Entries */
625 for (i = 0; i < VME_DEVS; i++) {
626 int num;
627
628 switch (type[i]) {
629 case MASTER_MINOR:
630 name = "bus/vme/m%d";
631 break;
632 case CONTROL_MINOR:
633 name = "bus/vme/ctl";
634 break;
635 case SLAVE_MINOR:
636 name = "bus/vme/s%d";
637 break;
638 default:
639 err = -EINVAL;
640 goto err_sysfs;
641 }
642
643 num = (type[i] == SLAVE_MINOR) ? i - (MASTER_MAX + 1) : i;
644 image[i].device = device_create(cls: &vme_user_sysfs_class, NULL,
645 MKDEV(VME_MAJOR, i), NULL,
646 fmt: name, num);
647 if (IS_ERR(ptr: image[i].device)) {
648 dev_info(&vdev->dev, "Error creating sysfs device\n");
649 err = PTR_ERR(ptr: image[i].device);
650 goto err_sysfs;
651 }
652 }
653
654 return 0;
655
656err_sysfs:
657 while (i > 0) {
658 i--;
659 device_destroy(cls: &vme_user_sysfs_class, MKDEV(VME_MAJOR, i));
660 }
661 class_unregister(class: &vme_user_sysfs_class);
662
663 /* Ensure counter set correctly to unalloc all master windows */
664 i = MASTER_MAX + 1;
665err_master:
666 while (i > MASTER_MINOR) {
667 i--;
668 kfree(objp: image[i].kern_buf);
669 vme_master_free(image[i].resource);
670 }
671
672 /*
673 * Ensure counter set correctly to unalloc all slave windows and buffers
674 */
675 i = SLAVE_MAX + 1;
676err_slave:
677 while (i > SLAVE_MINOR) {
678 i--;
679 vme_free_consistent(image[i].resource, image[i].size_buf,
680 image[i].kern_buf, image[i].pci_buf);
681 vme_slave_free(image[i].resource);
682 }
683err_class:
684 cdev_del(vme_user_cdev);
685err_char:
686 unregister_chrdev_region(MKDEV(VME_MAJOR, 0), VME_DEVS);
687err_region:
688err_dev:
689 return err;
690}
691
692static void vme_user_remove(struct vme_dev *dev)
693{
694 int i;
695
696 /* Remove sysfs Entries */
697 for (i = 0; i < VME_DEVS; i++) {
698 mutex_destroy(lock: &image[i].mutex);
699 device_destroy(cls: &vme_user_sysfs_class, MKDEV(VME_MAJOR, i));
700 }
701 class_unregister(class: &vme_user_sysfs_class);
702
703 for (i = MASTER_MINOR; i < (MASTER_MAX + 1); i++) {
704 kfree(objp: image[i].kern_buf);
705 vme_master_free(image[i].resource);
706 }
707
708 for (i = SLAVE_MINOR; i < (SLAVE_MAX + 1); i++) {
709 vme_slave_set(image[i].resource, 0, 0, 0, 0, VME_A32, 0);
710 vme_free_consistent(image[i].resource, image[i].size_buf,
711 image[i].kern_buf, image[i].pci_buf);
712 vme_slave_free(image[i].resource);
713 }
714
715 /* Unregister device driver */
716 cdev_del(vme_user_cdev);
717
718 /* Unregister the major and minor device numbers */
719 unregister_chrdev_region(MKDEV(VME_MAJOR, 0), VME_DEVS);
720}
721
722static struct vme_driver vme_user_driver = {
723 .name = DRIVER_NAME,
724 .match = vme_user_match,
725 .probe = vme_user_probe,
726 .remove = vme_user_remove,
727};
728
729static int __init vme_user_init(void)
730{
731 int retval = 0;
732
733 pr_info("VME User Space Access Driver\n");
734
735 if (bus_num == 0) {
736 pr_err("No cards, skipping registration\n");
737 retval = -ENODEV;
738 goto err_nocard;
739 }
740
741 /* Let's start by supporting one bus, we can support more than one
742 * in future revisions if that ever becomes necessary.
743 */
744 if (bus_num > VME_USER_BUS_MAX) {
745 pr_err("Driver only able to handle %d buses\n",
746 VME_USER_BUS_MAX);
747 bus_num = VME_USER_BUS_MAX;
748 }
749
750 /*
751 * Here we just register the maximum number of devices we can and
752 * leave vme_user_match() to allow only 1 to go through to probe().
753 * This way, if we later want to allow multiple user access devices,
754 * we just change the code in vme_user_match().
755 */
756 retval = vme_register_driver(&vme_user_driver, VME_MAX_SLOTS);
757 if (retval)
758 goto err_reg;
759
760 return retval;
761
762err_reg:
763err_nocard:
764 return retval;
765}
766
767static void __exit vme_user_exit(void)
768{
769 vme_unregister_driver(&vme_user_driver);
770}
771
772MODULE_PARM_DESC(bus, "Enumeration of VMEbus to which the driver is connected");
773module_param_array(bus, int, &bus_num, 0000);
774
775MODULE_DESCRIPTION("VME User Space Access Driver");
776MODULE_AUTHOR("Martyn Welch <martyn.welch@ge.com>");
777MODULE_LICENSE("GPL");
778
779module_init(vme_user_init);
780module_exit(vme_user_exit);
781

source code of linux/drivers/staging/vme_user/vme_user.c