vme_user.c revision 584721cab2bdd26f63bfeca60c83f5e6b8eee7d0
1f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch/* 2f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch * VMEbus User access driver 3f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch * 466bd8db52ab48e7189e02d4bf1f23109cc1ede70Martyn Welch * Author: Martyn Welch <martyn.welch@ge.com> 566bd8db52ab48e7189e02d4bf1f23109cc1ede70Martyn Welch * Copyright 2008 GE Intelligent Platforms Embedded Systems, Inc. 6f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch * 7f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch * Based on work by: 8f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch * Tom Armistead and Ajit Prem 9f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch * Copyright 2004 Motorola Inc. 10f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch * 11f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch * 12f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch * This program is free software; you can redistribute it and/or modify it 13f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch * under the terms of the GNU General Public License as published by the 14f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch * Free Software Foundation; either version 2 of the License, or (at your 15f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch * option) any later version. 16f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch */ 17f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 18f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch#include <linux/cdev.h> 19f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch#include <linux/delay.h> 20f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch#include <linux/device.h> 21f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch#include <linux/dma-mapping.h> 22f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch#include <linux/errno.h> 23f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch#include <linux/init.h> 24f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch#include <linux/ioctl.h> 25f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch#include <linux/kernel.h> 26f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch#include <linux/mm.h> 27f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch#include <linux/module.h> 28f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch#include <linux/pagemap.h> 29f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch#include <linux/pci.h> 30f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch#include <linux/semaphore.h> 315a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h> 32f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch#include <linux/spinlock.h> 33f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch#include <linux/syscalls.h> 348e2394a981b9258db47f8e223a550d46c6d40cc8Arnd Bergmann#include <linux/mutex.h> 35f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch#include <linux/types.h> 36f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 3745f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos#include <linux/io.h> 3845f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos#include <linux/uaccess.h> 39f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 40f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch#include "../vme.h" 41f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch#include "vme_user.h" 42f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 438e2394a981b9258db47f8e223a550d46c6d40cc8Arnd Bergmannstatic DEFINE_MUTEX(vme_user_mutex); 44584721cab2bdd26f63bfeca60c83f5e6b8eee7d0Vincent Bossierstatic const char driver_name[] = "vme_user"; 45238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch 46238add523bf9c89db1a191599fff2770af55e0fdMartyn Welchstatic int bus[USER_BUS_MAX]; 47c949231838006d7de4ad38be38d9e112826862daEmilio G. Cotastatic unsigned int bus_num; 48238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch 49f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch/* Currently Documentation/devices.txt defines the following for VME: 50f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch * 51f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch * 221 char VME bus 5245f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos * 0 = /dev/bus/vme/m0 First master image 5345f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos * 1 = /dev/bus/vme/m1 Second master image 5445f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos * 2 = /dev/bus/vme/m2 Third master image 5545f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos * 3 = /dev/bus/vme/m3 Fourth master image 5645f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos * 4 = /dev/bus/vme/s0 First slave image 5745f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos * 5 = /dev/bus/vme/s1 Second slave image 5845f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos * 6 = /dev/bus/vme/s2 Third slave image 5945f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos * 7 = /dev/bus/vme/s3 Fourth slave image 6045f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos * 8 = /dev/bus/vme/ctl Control 61f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch * 6245f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos * It is expected that all VME bus drivers will use the 6345f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos * same interface. For interface documentation see 6445f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos * http://www.vmelinux.org/. 65f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch * 66f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch * However the VME driver at http://www.vmelinux.org/ is rather old and doesn't 67f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch * even support the tsi148 chipset (which has 8 master and 8 slave windows). 68f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch * We'll run with this or now as far as possible, however it probably makes 69f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch * sense to get rid of the old mappings and just do everything dynamically. 70f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch * 71f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch * So for now, we'll restrict the driver to providing 4 masters and 4 slaves as 72f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch * defined above and try to support at least some of the interface from 73f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch * http://www.vmelinux.org/ as an alternative drive can be written providing a 74f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch * saner interface later. 75238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch * 76238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch * The vmelinux.org driver never supported slave images, the devices reserved 77238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch * for slaves were repurposed to support all 8 master images on the UniverseII! 78238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch * We shall support 4 masters and 4 slaves with this driver. 79f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch */ 80f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch#define VME_MAJOR 221 /* VME Major Device Number */ 81f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch#define VME_DEVS 9 /* Number of dev entries */ 82f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 83f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch#define MASTER_MINOR 0 84f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch#define MASTER_MAX 3 85f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch#define SLAVE_MINOR 4 86f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch#define SLAVE_MAX 7 87f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch#define CONTROL_MINOR 8 88f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 89f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch#define PCI_BUF_SIZE 0x20000 /* Size of one slave image buffer */ 90f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 91f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch/* 92f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch * Structure to handle image related parameters. 93f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch */ 94584721cab2bdd26f63bfeca60c83f5e6b8eee7d0Vincent Bossierstruct image_desc { 950a81a0f768e5bb0c32db6e44440c5b7c1b4658e7Emilio G. Cota void *kern_buf; /* Buffer address in kernel space */ 96f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch dma_addr_t pci_buf; /* Buffer address in PCI address space */ 97f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch unsigned long long size_buf; /* Buffer size */ 98f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch struct semaphore sem; /* Semaphore for locking image */ 99f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch struct device *device; /* Sysfs device */ 100f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch struct vme_resource *resource; /* VME resource */ 101f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch int users; /* Number of current users */ 102584721cab2bdd26f63bfeca60c83f5e6b8eee7d0Vincent Bossier}; 103584721cab2bdd26f63bfeca60c83f5e6b8eee7d0Vincent Bossierstatic struct image_desc image[VME_DEVS]; 104f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 105584721cab2bdd26f63bfeca60c83f5e6b8eee7d0Vincent Bossierstruct driver_stats { 106f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch unsigned long reads; 107f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch unsigned long writes; 108f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch unsigned long ioctls; 109f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch unsigned long irqs; 110f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch unsigned long berrs; 111f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch unsigned long dmaErrors; 112f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch unsigned long timeouts; 113f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch unsigned long external; 114584721cab2bdd26f63bfeca60c83f5e6b8eee7d0Vincent Bossier}; 115584721cab2bdd26f63bfeca60c83f5e6b8eee7d0Vincent Bossierstatic struct driver_stats statistics; 116f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 117b9cc293486e27f4d29a9813da6ebcb8574071dfaEmilio G. Cotastatic struct cdev *vme_user_cdev; /* Character device */ 118b9cc293486e27f4d29a9813da6ebcb8574071dfaEmilio G. Cotastatic struct class *vme_user_sysfs_class; /* Sysfs class */ 119b9cc293486e27f4d29a9813da6ebcb8574071dfaEmilio G. Cotastatic struct device *vme_user_bridge; /* Pointer to bridge device */ 120f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 121f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 122f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welchstatic const int type[VME_DEVS] = { MASTER_MINOR, MASTER_MINOR, 123f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch MASTER_MINOR, MASTER_MINOR, 124f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch SLAVE_MINOR, SLAVE_MINOR, 125f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch SLAVE_MINOR, SLAVE_MINOR, 126f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch CONTROL_MINOR 127f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch }; 128f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 129f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 130f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welchstatic int vme_user_open(struct inode *, struct file *); 131f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welchstatic int vme_user_release(struct inode *, struct file *); 1321a85f2073d99080ea70962f767edca479c768b6eEmilio G. Cotastatic ssize_t vme_user_read(struct file *, char __user *, size_t, loff_t *); 1331a85f2073d99080ea70962f767edca479c768b6eEmilio G. Cotastatic ssize_t vme_user_write(struct file *, const char __user *, size_t, 1341a85f2073d99080ea70962f767edca479c768b6eEmilio G. Cota loff_t *); 135f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welchstatic loff_t vme_user_llseek(struct file *, loff_t, int); 136b1f2ac07636aadee5cb077fc7e830908b00fcaaeArnd Bergmannstatic long vme_user_unlocked_ioctl(struct file *, unsigned int, unsigned long); 137f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 1384740a0846069f6d4cbba9e328a9d92e6dd76110dEmilio G. Cotastatic int __devinit vme_user_probe(struct device *, int, int); 1394740a0846069f6d4cbba9e328a9d92e6dd76110dEmilio G. Cotastatic int __devexit vme_user_remove(struct device *, int, int); 140f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 141584721cab2bdd26f63bfeca60c83f5e6b8eee7d0Vincent Bossierstatic const struct file_operations vme_user_fops = { 14245f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos .open = vme_user_open, 14345f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos .release = vme_user_release, 14445f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos .read = vme_user_read, 14545f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos .write = vme_user_write, 14645f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos .llseek = vme_user_llseek, 14745f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos .unlocked_ioctl = vme_user_unlocked_ioctl, 148f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch}; 149f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 150f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 151f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch/* 152f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch * Reset all the statistic counters 153f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch */ 154f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welchstatic void reset_counters(void) 155f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch{ 15645f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos statistics.reads = 0; 15745f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos statistics.writes = 0; 15845f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos statistics.ioctls = 0; 15945f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos statistics.irqs = 0; 16045f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos statistics.berrs = 0; 16145f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos statistics.dmaErrors = 0; 16245f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos statistics.timeouts = 0; 163f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch} 164f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 165f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welchstatic int vme_user_open(struct inode *inode, struct file *file) 166f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch{ 167f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch int err; 168f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch unsigned int minor = MINOR(inode->i_rdev); 169f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 170f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch down(&image[minor].sem); 171f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch /* Only allow device to be opened if a resource is allocated */ 172f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch if (image[minor].resource == NULL) { 173f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch printk(KERN_ERR "No resources allocated for device\n"); 174f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch err = -EINVAL; 175f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch goto err_res; 176f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch } 177f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 178f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch /* Increment user count */ 179f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch image[minor].users++; 180f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 181f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch up(&image[minor].sem); 182f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 183f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch return 0; 184f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 185f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welcherr_res: 186f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch up(&image[minor].sem); 187f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 188f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch return err; 189f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch} 190f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 191f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welchstatic int vme_user_release(struct inode *inode, struct file *file) 192f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch{ 193f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch unsigned int minor = MINOR(inode->i_rdev); 194f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 195f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch down(&image[minor].sem); 196f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 197f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch /* Decrement user count */ 198f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch image[minor].users--; 199f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 200f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch up(&image[minor].sem); 201f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 202f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch return 0; 203f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch} 204f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 205f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch/* 206f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch * We are going ot alloc a page during init per window for small transfers. 207f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch * Small transfers will go VME -> buffer -> user space. Larger (more than a 208f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch * page) transfers will lock the user space buffer into memory and then 209f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch * transfer the data directly into the user space buffers. 210f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch */ 211f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welchstatic ssize_t resource_to_user(int minor, char __user *buf, size_t count, 212f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch loff_t *ppos) 213f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch{ 214f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch ssize_t retval; 215f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch ssize_t copied = 0; 216f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 217f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch if (count <= image[minor].size_buf) { 218f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch /* We copy to kernel buffer */ 219f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch copied = vme_master_read(image[minor].resource, 220f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch image[minor].kern_buf, count, *ppos); 22145f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos if (copied < 0) 222f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch return (int)copied; 223f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 224f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch retval = __copy_to_user(buf, image[minor].kern_buf, 225f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch (unsigned long)copied); 226f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch if (retval != 0) { 227f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch copied = (copied - retval); 22845f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos printk(KERN_INFO "User copy failed\n"); 229f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch return -EINVAL; 230f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch } 231f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 232f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch } else { 233f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch /* XXX Need to write this */ 23445f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos printk(KERN_INFO "Currently don't support large transfers\n"); 235f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch /* Map in pages from userspace */ 236f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 237f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch /* Call vme_master_read to do the transfer */ 238f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch return -EINVAL; 239f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch } 240f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 241f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch return copied; 242f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch} 243f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 244f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch/* 245f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch * We are going ot alloc a page during init per window for small transfers. 246f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch * Small transfers will go user space -> buffer -> VME. Larger (more than a 247f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch * page) transfers will lock the user space buffer into memory and then 248f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch * transfer the data directly from the user space buffers out to VME. 249f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch */ 2501a85f2073d99080ea70962f767edca479c768b6eEmilio G. Cotastatic ssize_t resource_from_user(unsigned int minor, const char __user *buf, 251f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch size_t count, loff_t *ppos) 252f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch{ 253f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch ssize_t retval; 254f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch ssize_t copied = 0; 255f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 256f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch if (count <= image[minor].size_buf) { 257f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch retval = __copy_from_user(image[minor].kern_buf, buf, 258f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch (unsigned long)count); 259f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch if (retval != 0) 260f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch copied = (copied - retval); 261f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch else 262f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch copied = count; 263f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 264f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch copied = vme_master_write(image[minor].resource, 265f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch image[minor].kern_buf, copied, *ppos); 266f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch } else { 267f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch /* XXX Need to write this */ 26845f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos printk(KERN_INFO "Currently don't support large transfers\n"); 269f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch /* Map in pages from userspace */ 270f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 271f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch /* Call vme_master_write to do the transfer */ 272f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch return -EINVAL; 273f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch } 274f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 275f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch return copied; 276f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch} 277f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 278f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welchstatic ssize_t buffer_to_user(unsigned int minor, char __user *buf, 279f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch size_t count, loff_t *ppos) 280f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch{ 2810a81a0f768e5bb0c32db6e44440c5b7c1b4658e7Emilio G. Cota void *image_ptr; 282f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch ssize_t retval; 283f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 284f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch image_ptr = image[minor].kern_buf + *ppos; 285f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 286f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch retval = __copy_to_user(buf, image_ptr, (unsigned long)count); 287f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch if (retval != 0) { 288f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch retval = (count - retval); 289f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch printk(KERN_WARNING "Partial copy to userspace\n"); 290f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch } else 291f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch retval = count; 292f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 293f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch /* Return number of bytes successfully read */ 294f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch return retval; 295f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch} 296f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 2971a85f2073d99080ea70962f767edca479c768b6eEmilio G. Cotastatic ssize_t buffer_from_user(unsigned int minor, const char __user *buf, 298f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch size_t count, loff_t *ppos) 299f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch{ 3000a81a0f768e5bb0c32db6e44440c5b7c1b4658e7Emilio G. Cota void *image_ptr; 301f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch size_t retval; 302f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 303f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch image_ptr = image[minor].kern_buf + *ppos; 304f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 305f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch retval = __copy_from_user(image_ptr, buf, (unsigned long)count); 306f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch if (retval != 0) { 307f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch retval = (count - retval); 308f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch printk(KERN_WARNING "Partial copy to userspace\n"); 309f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch } else 310f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch retval = count; 311f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 312f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch /* Return number of bytes successfully read */ 313f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch return retval; 314f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch} 315f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 3161a85f2073d99080ea70962f767edca479c768b6eEmilio G. Cotastatic ssize_t vme_user_read(struct file *file, char __user *buf, size_t count, 31745f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos loff_t *ppos) 318f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch{ 319f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev); 320f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch ssize_t retval; 321f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch size_t image_size; 322f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch size_t okcount; 323f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 324f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch down(&image[minor].sem); 325f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 326f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch /* XXX Do we *really* want this helper - we can use vme_*_get ? */ 327f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch image_size = vme_get_size(image[minor].resource); 328f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 329f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch /* Ensure we are starting at a valid location */ 330f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch if ((*ppos < 0) || (*ppos > (image_size - 1))) { 331f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch up(&image[minor].sem); 332f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch return 0; 333f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch } 334f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 335f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch /* Ensure not reading past end of the image */ 336f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch if (*ppos + count > image_size) 337f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch okcount = image_size - *ppos; 338f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch else 339f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch okcount = count; 340f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 34145f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos switch (type[minor]) { 342f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch case MASTER_MINOR: 343f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch retval = resource_to_user(minor, buf, okcount, ppos); 344f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch break; 345f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch case SLAVE_MINOR: 346f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch retval = buffer_to_user(minor, buf, okcount, ppos); 347f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch break; 348f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch default: 349f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch retval = -EINVAL; 350f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch } 351f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 352f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch up(&image[minor].sem); 353f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 354f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch if (retval > 0) 355f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch *ppos += retval; 356f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 357f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch return retval; 358f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch} 359f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 3601a85f2073d99080ea70962f767edca479c768b6eEmilio G. Cotastatic ssize_t vme_user_write(struct file *file, const char __user *buf, 3611a85f2073d99080ea70962f767edca479c768b6eEmilio G. Cota size_t count, loff_t *ppos) 362f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch{ 363f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev); 364f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch ssize_t retval; 365f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch size_t image_size; 366f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch size_t okcount; 367f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 368f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch down(&image[minor].sem); 369f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 370f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch image_size = vme_get_size(image[minor].resource); 371f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 372f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch /* Ensure we are starting at a valid location */ 373f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch if ((*ppos < 0) || (*ppos > (image_size - 1))) { 374f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch up(&image[minor].sem); 375f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch return 0; 376f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch } 377f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 378f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch /* Ensure not reading past end of the image */ 379f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch if (*ppos + count > image_size) 380f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch okcount = image_size - *ppos; 381f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch else 382f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch okcount = count; 383f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 38445f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos switch (type[minor]) { 385f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch case MASTER_MINOR: 386f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch retval = resource_from_user(minor, buf, okcount, ppos); 387f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch break; 388f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch case SLAVE_MINOR: 389f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch retval = buffer_from_user(minor, buf, okcount, ppos); 390f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch break; 391f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch default: 392f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch retval = -EINVAL; 393f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch } 394f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 395f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch up(&image[minor].sem); 396f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 397f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch if (retval > 0) 398f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch *ppos += retval; 399f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 400f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch return retval; 401f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch} 402f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 403f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welchstatic loff_t vme_user_llseek(struct file *file, loff_t off, int whence) 404f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch{ 405877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov loff_t absolute = -1; 406877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev); 407877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov size_t image_size; 408877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov 409877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov down(&image[minor].sem); 410877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov image_size = vme_get_size(image[minor].resource); 411877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov 412877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov switch (whence) { 413877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov case SEEK_SET: 414877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov absolute = off; 415877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov break; 416877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov case SEEK_CUR: 417877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov absolute = file->f_pos + off; 418877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov break; 419877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov case SEEK_END: 420877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov absolute = image_size + off; 421877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov break; 422877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov default: 423877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov up(&image[minor].sem); 424877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov return -EINVAL; 425877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov break; 426877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov } 427877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov 428877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov if ((absolute < 0) || (absolute >= image_size)) { 429877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov up(&image[minor].sem); 430877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov return -EINVAL; 431877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov } 432877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov 433877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov file->f_pos = absolute; 434877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov 435877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov up(&image[minor].sem); 436877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov 437877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov return absolute; 438f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch} 439f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 440238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch/* 441238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch * The ioctls provided by the old VME access method (the one at vmelinux.org) 442238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch * are most certainly wrong as the effectively push the registers layout 443238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch * through to user space. Given that the VME core can handle multiple bridges, 444238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch * with different register layouts this is most certainly not the way to go. 445238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch * 446238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch * We aren't using the structures defined in the Motorola driver either - these 447238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch * are also quite low level, however we should use the definitions that have 448238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch * already been defined. 449238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch */ 450f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welchstatic int vme_user_ioctl(struct inode *inode, struct file *file, 451f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch unsigned int cmd, unsigned long arg) 452f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch{ 453238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch struct vme_master master; 454238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch struct vme_slave slave; 455238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch unsigned long copied; 456f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch unsigned int minor = MINOR(inode->i_rdev); 457238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch int retval; 458238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch dma_addr_t pci_addr; 4591a85f2073d99080ea70962f767edca479c768b6eEmilio G. Cota void __user *argp = (void __user *)arg; 460f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 461f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch statistics.ioctls++; 462238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch 463f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch switch (type[minor]) { 464f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch case CONTROL_MINOR: 465f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch break; 466f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch case MASTER_MINOR: 467f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch switch (cmd) { 468238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch case VME_GET_MASTER: 469238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch memset(&master, 0, sizeof(struct vme_master)); 470238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch 471238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch /* XXX We do not want to push aspace, cycle and width 472238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch * to userspace as they are 473238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch */ 474238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch retval = vme_master_get(image[minor].resource, 475886953e9b70bcb6913716b49bdf21b69450a7cd6Emilio G. Cota &master.enable, &master.vme_addr, 476886953e9b70bcb6913716b49bdf21b69450a7cd6Emilio G. Cota &master.size, &master.aspace, 477886953e9b70bcb6913716b49bdf21b69450a7cd6Emilio G. Cota &master.cycle, &master.dwidth); 478238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch 4791a85f2073d99080ea70962f767edca479c768b6eEmilio G. Cota copied = copy_to_user(argp, &master, 480238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch sizeof(struct vme_master)); 481238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch if (copied != 0) { 482238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch printk(KERN_WARNING "Partial copy to " 483238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch "userspace\n"); 484238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch return -EFAULT; 485238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch } 486f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 487238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch return retval; 488238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch break; 489238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch 490238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch case VME_SET_MASTER: 491238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch 4921a85f2073d99080ea70962f767edca479c768b6eEmilio G. Cota copied = copy_from_user(&master, argp, sizeof(master)); 493238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch if (copied != 0) { 494f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch printk(KERN_WARNING "Partial copy from " 495f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch "userspace\n"); 496f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch return -EFAULT; 497f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch } 498f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 499238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch /* XXX We do not want to push aspace, cycle and width 500238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch * to userspace as they are 501238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch */ 502238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch return vme_master_set(image[minor].resource, 503238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch master.enable, master.vme_addr, master.size, 504238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch master.aspace, master.cycle, master.dwidth); 505f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 506f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch break; 507238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch } 508238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch break; 509238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch case SLAVE_MINOR: 510238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch switch (cmd) { 511f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch case VME_GET_SLAVE: 512238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch memset(&slave, 0, sizeof(struct vme_slave)); 513238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch 514238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch /* XXX We do not want to push aspace, cycle and width 515238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch * to userspace as they are 516238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch */ 517238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch retval = vme_slave_get(image[minor].resource, 518886953e9b70bcb6913716b49bdf21b69450a7cd6Emilio G. Cota &slave.enable, &slave.vme_addr, 519886953e9b70bcb6913716b49bdf21b69450a7cd6Emilio G. Cota &slave.size, &pci_addr, &slave.aspace, 520886953e9b70bcb6913716b49bdf21b69450a7cd6Emilio G. Cota &slave.cycle); 521238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch 5221a85f2073d99080ea70962f767edca479c768b6eEmilio G. Cota copied = copy_to_user(argp, &slave, 523238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch sizeof(struct vme_slave)); 524238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch if (copied != 0) { 525238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch printk(KERN_WARNING "Partial copy to " 526238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch "userspace\n"); 527238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch return -EFAULT; 528238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch } 529238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch 530238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch return retval; 531238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch break; 532f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 533238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch case VME_SET_SLAVE: 534f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 5351a85f2073d99080ea70962f767edca479c768b6eEmilio G. Cota copied = copy_from_user(&slave, argp, sizeof(slave)); 536238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch if (copied != 0) { 537238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch printk(KERN_WARNING "Partial copy from " 538f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch "userspace\n"); 539f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch return -EFAULT; 540f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch } 541f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 542238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch /* XXX We do not want to push aspace, cycle and width 543238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch * to userspace as they are 544238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch */ 545238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch return vme_slave_set(image[minor].resource, 546238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch slave.enable, slave.vme_addr, slave.size, 547238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch image[minor].pci_buf, slave.aspace, 548238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch slave.cycle); 549238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch 550f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch break; 551f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch } 552f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch break; 553f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch } 554f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 555f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch return -EINVAL; 556f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch} 557f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 558b1f2ac07636aadee5cb077fc7e830908b00fcaaeArnd Bergmannstatic long 559b1f2ac07636aadee5cb077fc7e830908b00fcaaeArnd Bergmannvme_user_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 560b1f2ac07636aadee5cb077fc7e830908b00fcaaeArnd Bergmann{ 561b1f2ac07636aadee5cb077fc7e830908b00fcaaeArnd Bergmann int ret; 562b1f2ac07636aadee5cb077fc7e830908b00fcaaeArnd Bergmann 5638e2394a981b9258db47f8e223a550d46c6d40cc8Arnd Bergmann mutex_lock(&vme_user_mutex); 564b1f2ac07636aadee5cb077fc7e830908b00fcaaeArnd Bergmann ret = vme_user_ioctl(file->f_path.dentry->d_inode, file, cmd, arg); 5658e2394a981b9258db47f8e223a550d46c6d40cc8Arnd Bergmann mutex_unlock(&vme_user_mutex); 566b1f2ac07636aadee5cb077fc7e830908b00fcaaeArnd Bergmann 567b1f2ac07636aadee5cb077fc7e830908b00fcaaeArnd Bergmann return ret; 568b1f2ac07636aadee5cb077fc7e830908b00fcaaeArnd Bergmann} 569b1f2ac07636aadee5cb077fc7e830908b00fcaaeArnd Bergmann 570f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 571f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch/* 572f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch * Unallocate a previously allocated buffer 573f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch */ 57445f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomosstatic void buf_unalloc(int num) 575f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch{ 576f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch if (image[num].kern_buf) { 577f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch#ifdef VME_DEBUG 578f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch printk(KERN_DEBUG "UniverseII:Releasing buffer at %p\n", 579f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch image[num].pci_buf); 580f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch#endif 581f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 582f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch vme_free_consistent(image[num].resource, image[num].size_buf, 583f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch image[num].kern_buf, image[num].pci_buf); 584f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 585f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch image[num].kern_buf = NULL; 586f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch image[num].pci_buf = 0; 587f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch image[num].size_buf = 0; 588f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 589f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch#ifdef VME_DEBUG 590f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch } else { 591f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch printk(KERN_DEBUG "UniverseII: Buffer not allocated\n"); 592f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch#endif 593f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch } 594f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch} 595f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 596f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welchstatic struct vme_driver vme_user_driver = { 59745f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos .name = driver_name, 59845f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos .probe = vme_user_probe, 5994740a0846069f6d4cbba9e328a9d92e6dd76110dEmilio G. Cota .remove = __devexit_p(vme_user_remove), 600f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch}; 601f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 602f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 603238add523bf9c89db1a191599fff2770af55e0fdMartyn Welchstatic int __init vme_user_init(void) 604f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch{ 605238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch int retval = 0; 606238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch int i; 607238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch struct vme_device_id *ids; 608238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch 609f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch printk(KERN_INFO "VME User Space Access Driver\n"); 610238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch 611238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch if (bus_num == 0) { 612238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch printk(KERN_ERR "%s: No cards, skipping registration\n", 613238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch driver_name); 61455db50205adaf14db1bda07d0931b647b794de2fEmilio G. Cota retval = -ENODEV; 615238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch goto err_nocard; 616238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch } 617238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch 618238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch /* Let's start by supporting one bus, we can support more than one 619238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch * in future revisions if that ever becomes necessary. 620238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch */ 621238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch if (bus_num > USER_BUS_MAX) { 62251616e21066d040988a33effa82d4ef37fd60959Martyn Welch printk(KERN_ERR "%s: Driver only able to handle %d buses\n", 62351616e21066d040988a33effa82d4ef37fd60959Martyn Welch driver_name, USER_BUS_MAX); 624238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch bus_num = USER_BUS_MAX; 625238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch } 626238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch 627238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch 628238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch /* Dynamically create the bind table based on module parameters */ 629238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch ids = kmalloc(sizeof(struct vme_device_id) * (bus_num + 1), GFP_KERNEL); 630238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch if (ids == NULL) { 631238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch printk(KERN_ERR "%s: Unable to allocate ID table\n", 632238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch driver_name); 63355db50205adaf14db1bda07d0931b647b794de2fEmilio G. Cota retval = -ENOMEM; 634238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch goto err_id; 635238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch } 636238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch 637238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch memset(ids, 0, (sizeof(struct vme_device_id) * (bus_num + 1))); 638238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch 639238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch for (i = 0; i < bus_num; i++) { 640238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch ids[i].bus = bus[i]; 641238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch /* 642238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch * We register the driver against the slot occupied by *this* 643238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch * card, since it's really a low level way of controlling 644238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch * the VME bridge 645238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch */ 646238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch ids[i].slot = VME_SLOT_CURRENT; 647238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch } 648238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch 649238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch vme_user_driver.bind_table = ids; 650238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch 651f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch retval = vme_register_driver(&vme_user_driver); 652238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch if (retval != 0) 653238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch goto err_reg; 654238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch 655238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch return retval; 656238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch 657238add523bf9c89db1a191599fff2770af55e0fdMartyn Welcherr_reg: 658238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch kfree(ids); 659238add523bf9c89db1a191599fff2770af55e0fdMartyn Welcherr_id: 660238add523bf9c89db1a191599fff2770af55e0fdMartyn Welcherr_nocard: 661f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch return retval; 662f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch} 663f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 664f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch/* 665238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch * In this simple access driver, the old behaviour is being preserved as much 666238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch * as practical. We will therefore reserve the buffers and request the images 667238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch * here so that we don't have to do it later. 668f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch */ 6694740a0846069f6d4cbba9e328a9d92e6dd76110dEmilio G. Cotastatic int __devinit vme_user_probe(struct device *dev, int cur_bus, 6704740a0846069f6d4cbba9e328a9d92e6dd76110dEmilio G. Cota int cur_slot) 671f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch{ 672f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch int i, err; 673beb9ccc635433065a099b75dc8b22caf0844014aMartyn Welch char name[12]; 674f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 675238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch /* Save pointer to the bridge device */ 676238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch if (vme_user_bridge != NULL) { 677238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch printk(KERN_ERR "%s: Driver can only be loaded for 1 device\n", 678238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch driver_name); 679238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch err = -EINVAL; 680238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch goto err_dev; 681238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch } 682f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch vme_user_bridge = dev; 683f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 684f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch /* Initialise descriptors */ 685f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch for (i = 0; i < VME_DEVS; i++) { 686f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch image[i].kern_buf = NULL; 687f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch image[i].pci_buf = 0; 688886953e9b70bcb6913716b49bdf21b69450a7cd6Emilio G. Cota sema_init(&image[i].sem, 1); 689f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch image[i].device = NULL; 690f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch image[i].resource = NULL; 691f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch image[i].users = 0; 692f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch } 693f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 694f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch /* Initialise statistics counters */ 695f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch reset_counters(); 696f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 697f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch /* Assign major and minor numbers for the driver */ 698f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch err = register_chrdev_region(MKDEV(VME_MAJOR, 0), VME_DEVS, 699f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch driver_name); 700f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch if (err) { 701f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch printk(KERN_WARNING "%s: Error getting Major Number %d for " 702f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch "driver.\n", driver_name, VME_MAJOR); 703f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch goto err_region; 704f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch } 705f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 706f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch /* Register the driver as a char device */ 707f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch vme_user_cdev = cdev_alloc(); 708f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch vme_user_cdev->ops = &vme_user_fops; 709f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch vme_user_cdev->owner = THIS_MODULE; 710f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch err = cdev_add(vme_user_cdev, MKDEV(VME_MAJOR, 0), VME_DEVS); 711f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch if (err) { 712f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch printk(KERN_WARNING "%s: cdev_all failed\n", driver_name); 713f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch goto err_char; 714f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch } 715f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 716f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch /* Request slave resources and allocate buffers (128kB wide) */ 717f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch for (i = SLAVE_MINOR; i < (SLAVE_MAX + 1); i++) { 718f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch /* XXX Need to properly request attributes */ 7195188d74cc4597a63a0907b8996ca0a2d36f1b970Arthur Benilov /* For ca91cx42 bridge there are only two slave windows 7205188d74cc4597a63a0907b8996ca0a2d36f1b970Arthur Benilov * supporting A16 addressing, so we request A24 supported 7215188d74cc4597a63a0907b8996ca0a2d36f1b970Arthur Benilov * by all windows. 7225188d74cc4597a63a0907b8996ca0a2d36f1b970Arthur Benilov */ 723f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch image[i].resource = vme_slave_request(vme_user_bridge, 7245188d74cc4597a63a0907b8996ca0a2d36f1b970Arthur Benilov VME_A24, VME_SCT); 725f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch if (image[i].resource == NULL) { 726f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch printk(KERN_WARNING "Unable to allocate slave " 727f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch "resource\n"); 728238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch goto err_slave; 729f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch } 730f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch image[i].size_buf = PCI_BUF_SIZE; 731f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch image[i].kern_buf = vme_alloc_consistent(image[i].resource, 732886953e9b70bcb6913716b49bdf21b69450a7cd6Emilio G. Cota image[i].size_buf, &image[i].pci_buf); 733f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch if (image[i].kern_buf == NULL) { 734f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch printk(KERN_WARNING "Unable to allocate memory for " 735f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch "buffer\n"); 736f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch image[i].pci_buf = 0; 737f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch vme_slave_free(image[i].resource); 738f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch err = -ENOMEM; 739238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch goto err_slave; 740f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch } 741f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch } 742f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 743f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch /* 744f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch * Request master resources allocate page sized buffers for small 745f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch * reads and writes 746f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch */ 747f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch for (i = MASTER_MINOR; i < (MASTER_MAX + 1); i++) { 748f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch /* XXX Need to properly request attributes */ 749f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch image[i].resource = vme_master_request(vme_user_bridge, 750f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch VME_A32, VME_SCT, VME_D32); 751f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch if (image[i].resource == NULL) { 752f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch printk(KERN_WARNING "Unable to allocate master " 753f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch "resource\n"); 754238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch goto err_master; 755f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch } 75633e920d9ebaddbc9cf51cf6e1de7baa8d7b8d6ddArthur Benilov image[i].size_buf = PCI_BUF_SIZE; 75733e920d9ebaddbc9cf51cf6e1de7baa8d7b8d6ddArthur Benilov image[i].kern_buf = kmalloc(image[i].size_buf, GFP_KERNEL); 75833e920d9ebaddbc9cf51cf6e1de7baa8d7b8d6ddArthur Benilov if (image[i].kern_buf == NULL) { 75933e920d9ebaddbc9cf51cf6e1de7baa8d7b8d6ddArthur Benilov printk(KERN_WARNING "Unable to allocate memory for " 76033e920d9ebaddbc9cf51cf6e1de7baa8d7b8d6ddArthur Benilov "master window buffers\n"); 76133e920d9ebaddbc9cf51cf6e1de7baa8d7b8d6ddArthur Benilov err = -ENOMEM; 76233e920d9ebaddbc9cf51cf6e1de7baa8d7b8d6ddArthur Benilov goto err_master_buf; 76333e920d9ebaddbc9cf51cf6e1de7baa8d7b8d6ddArthur Benilov } 764f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch } 765f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 766f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch /* Create sysfs entries - on udev systems this creates the dev files */ 767f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch vme_user_sysfs_class = class_create(THIS_MODULE, driver_name); 768f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch if (IS_ERR(vme_user_sysfs_class)) { 769f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch printk(KERN_ERR "Error creating vme_user class.\n"); 770f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch err = PTR_ERR(vme_user_sysfs_class); 771f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch goto err_class; 772f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch } 773f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 774f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch /* Add sysfs Entries */ 77545f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos for (i = 0; i < VME_DEVS; i++) { 776584721cab2bdd26f63bfeca60c83f5e6b8eee7d0Vincent Bossier int num; 777f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch switch (type[i]) { 778f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch case MASTER_MINOR: 77945f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos sprintf(name, "bus/vme/m%%d"); 780f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch break; 781f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch case CONTROL_MINOR: 78245f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos sprintf(name, "bus/vme/ctl"); 783f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch break; 784f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch case SLAVE_MINOR: 78545f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos sprintf(name, "bus/vme/s%%d"); 786f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch break; 787f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch default: 788f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch err = -EINVAL; 789f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch goto err_sysfs; 790f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch break; 791f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch } 792f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 793584721cab2bdd26f63bfeca60c83f5e6b8eee7d0Vincent Bossier num = (type[i] == SLAVE_MINOR) ? i - (MASTER_MAX + 1) : i; 794584721cab2bdd26f63bfeca60c83f5e6b8eee7d0Vincent Bossier image[i].device = device_create(vme_user_sysfs_class, NULL, 795584721cab2bdd26f63bfeca60c83f5e6b8eee7d0Vincent Bossier MKDEV(VME_MAJOR, i), NULL, name, num); 796f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch if (IS_ERR(image[i].device)) { 79745f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos printk(KERN_INFO "%s: Error creating sysfs device\n", 798f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch driver_name); 799f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch err = PTR_ERR(image[i].device); 800f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch goto err_sysfs; 801f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch } 802f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch } 803f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 804f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch return 0; 805f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 806f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch /* Ensure counter set correcty to destroy all sysfs devices */ 807f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch i = VME_DEVS; 808f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welcherr_sysfs: 80945f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos while (i > 0) { 810f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch i--; 811f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch device_destroy(vme_user_sysfs_class, MKDEV(VME_MAJOR, i)); 812f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch } 813f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch class_destroy(vme_user_sysfs_class); 814f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 815238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch /* Ensure counter set correcty to unalloc all master windows */ 816238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch i = MASTER_MAX + 1; 81733e920d9ebaddbc9cf51cf6e1de7baa8d7b8d6ddArthur Beniloverr_master_buf: 81833e920d9ebaddbc9cf51cf6e1de7baa8d7b8d6ddArthur Benilov for (i = MASTER_MINOR; i < (MASTER_MAX + 1); i++) 81933e920d9ebaddbc9cf51cf6e1de7baa8d7b8d6ddArthur Benilov kfree(image[i].kern_buf); 820238add523bf9c89db1a191599fff2770af55e0fdMartyn Welcherr_master: 821238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch while (i > MASTER_MINOR) { 822238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch i--; 823238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch vme_master_free(image[i].resource); 824238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch } 825238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch 826238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch /* 827238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch * Ensure counter set correcty to unalloc all slave windows and buffers 828238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch */ 829f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch i = SLAVE_MAX + 1; 830238add523bf9c89db1a191599fff2770af55e0fdMartyn Welcherr_slave: 831238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch while (i > SLAVE_MINOR) { 832f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch i--; 833f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch buf_unalloc(i); 8341daa38d379932bde0d2036c2e10ced3e8842b74fEmilio G. Cota vme_slave_free(image[i].resource); 835f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch } 836f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welcherr_class: 837f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch cdev_del(vme_user_cdev); 838f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welcherr_char: 839f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch unregister_chrdev_region(MKDEV(VME_MAJOR, 0), VME_DEVS); 840f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welcherr_region: 841238add523bf9c89db1a191599fff2770af55e0fdMartyn Welcherr_dev: 842f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch return err; 843f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch} 844f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 8454740a0846069f6d4cbba9e328a9d92e6dd76110dEmilio G. Cotastatic int __devexit vme_user_remove(struct device *dev, int cur_bus, 8464740a0846069f6d4cbba9e328a9d92e6dd76110dEmilio G. Cota int cur_slot) 847f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch{ 848f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch int i; 849f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 850f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch /* Remove sysfs Entries */ 85145f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos for (i = 0; i < VME_DEVS; i++) 852f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch device_destroy(vme_user_sysfs_class, MKDEV(VME_MAJOR, i)); 853f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch class_destroy(vme_user_sysfs_class); 854f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 855b62c99b17c2c513eaf6b77a76907a13a1beb86d3Emilio G. Cota for (i = MASTER_MINOR; i < (MASTER_MAX + 1); i++) { 85633e920d9ebaddbc9cf51cf6e1de7baa8d7b8d6ddArthur Benilov kfree(image[i].kern_buf); 857b62c99b17c2c513eaf6b77a76907a13a1beb86d3Emilio G. Cota vme_master_free(image[i].resource); 858b62c99b17c2c513eaf6b77a76907a13a1beb86d3Emilio G. Cota } 85933e920d9ebaddbc9cf51cf6e1de7baa8d7b8d6ddArthur Benilov 860f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch for (i = SLAVE_MINOR; i < (SLAVE_MAX + 1); i++) { 861238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch vme_slave_set(image[i].resource, 0, 0, 0, 0, VME_A32, 0); 862f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch buf_unalloc(i); 8631daa38d379932bde0d2036c2e10ced3e8842b74fEmilio G. Cota vme_slave_free(image[i].resource); 864f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch } 865f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 866f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch /* Unregister device driver */ 867f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch cdev_del(vme_user_cdev); 868f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 869f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch /* Unregiser the major and minor device numbers */ 870f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch unregister_chrdev_region(MKDEV(VME_MAJOR, 0), VME_DEVS); 871238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch 872238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch return 0; 873f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch} 874f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 875238add523bf9c89db1a191599fff2770af55e0fdMartyn Welchstatic void __exit vme_user_exit(void) 876238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch{ 877238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch vme_unregister_driver(&vme_user_driver); 878238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch 879238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch kfree(vme_user_driver.bind_table); 880238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch} 881238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch 882238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch 883238add523bf9c89db1a191599fff2770af55e0fdMartyn WelchMODULE_PARM_DESC(bus, "Enumeration of VMEbus to which the driver is connected"); 884238add523bf9c89db1a191599fff2770af55e0fdMartyn Welchmodule_param_array(bus, int, &bus_num, 0); 885238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch 886f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn WelchMODULE_DESCRIPTION("VME User Space Access Driver"); 88766bd8db52ab48e7189e02d4bf1f23109cc1ede70Martyn WelchMODULE_AUTHOR("Martyn Welch <martyn.welch@ge.com"); 888f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn WelchMODULE_LICENSE("GPL"); 889f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 890238add523bf9c89db1a191599fff2770af55e0fdMartyn Welchmodule_init(vme_user_init); 891238add523bf9c89db1a191599fff2770af55e0fdMartyn Welchmodule_exit(vme_user_exit); 892