vme_user.c revision 8e2394a981b9258db47f8e223a550d46c6d40cc8
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); 44238add523bf9c89db1a191599fff2770af55e0fdMartyn Welchstatic char driver_name[] = "vme_user"; 45238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch 46238add523bf9c89db1a191599fff2770af55e0fdMartyn Welchstatic int bus[USER_BUS_MAX]; 47238add523bf9c89db1a191599fff2770af55e0fdMartyn Welchstatic 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 */ 94f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welchtypedef struct { 95f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch void __iomem *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 */ 102f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch} image_desc_t; 103f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welchstatic image_desc_t image[VME_DEVS]; 104f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 105f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welchtypedef struct { 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; 114f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch} driver_stats_t; 115f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welchstatic driver_stats_t statistics; 116f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 117f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welchstruct cdev *vme_user_cdev; /* Character device */ 118f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welchstruct class *vme_user_sysfs_class; /* Sysfs class */ 119f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welchstruct device *vme_user_bridge; /* Pointer to the 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 *); 132f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welchstatic ssize_t vme_user_read(struct file *, char *, size_t, loff_t *); 133f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welchstatic ssize_t vme_user_write(struct file *, const char *, size_t, loff_t *); 134f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welchstatic loff_t vme_user_llseek(struct file *, loff_t, int); 135b1f2ac07636aadee5cb077fc7e830908b00fcaaeArnd Bergmannstatic long vme_user_unlocked_ioctl(struct file *, unsigned int, unsigned long); 136f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 137238add523bf9c89db1a191599fff2770af55e0fdMartyn Welchstatic int __init vme_user_probe(struct device *, int, int); 138238add523bf9c89db1a191599fff2770af55e0fdMartyn Welchstatic int __exit vme_user_remove(struct device *, int, int); 139f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 140f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welchstatic struct file_operations vme_user_fops = { 14145f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos .open = vme_user_open, 14245f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos .release = vme_user_release, 14345f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos .read = vme_user_read, 14445f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos .write = vme_user_write, 14545f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos .llseek = vme_user_llseek, 14645f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos .unlocked_ioctl = vme_user_unlocked_ioctl, 147f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch}; 148f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 149f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 150f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch/* 151f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch * Reset all the statistic counters 152f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch */ 153f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welchstatic void reset_counters(void) 154f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch{ 15545f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos statistics.reads = 0; 15645f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos statistics.writes = 0; 15745f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos statistics.ioctls = 0; 15845f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos statistics.irqs = 0; 15945f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos statistics.berrs = 0; 16045f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos statistics.dmaErrors = 0; 16145f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos statistics.timeouts = 0; 162f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch} 163f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 164f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welchstatic int vme_user_open(struct inode *inode, struct file *file) 165f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch{ 166f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch int err; 167f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch unsigned int minor = MINOR(inode->i_rdev); 168f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 169f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch down(&image[minor].sem); 170f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch /* Only allow device to be opened if a resource is allocated */ 171f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch if (image[minor].resource == NULL) { 172f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch printk(KERN_ERR "No resources allocated for device\n"); 173f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch err = -EINVAL; 174f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch goto err_res; 175f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch } 176f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 177f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch /* Increment user count */ 178f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch image[minor].users++; 179f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 180f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch up(&image[minor].sem); 181f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 182f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch return 0; 183f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 184f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welcherr_res: 185f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch up(&image[minor].sem); 186f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 187f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch return err; 188f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch} 189f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 190f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welchstatic int vme_user_release(struct inode *inode, struct file *file) 191f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch{ 192f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch unsigned int minor = MINOR(inode->i_rdev); 193f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 194f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch down(&image[minor].sem); 195f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 196f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch /* Decrement user count */ 197f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch image[minor].users--; 198f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 199f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch up(&image[minor].sem); 200f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 201f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch return 0; 202f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch} 203f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 204f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch/* 205f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch * We are going ot alloc a page during init per window for small transfers. 206f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch * Small transfers will go VME -> buffer -> user space. Larger (more than a 207f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch * page) transfers will lock the user space buffer into memory and then 208f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch * transfer the data directly into the user space buffers. 209f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch */ 210f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welchstatic ssize_t resource_to_user(int minor, char __user *buf, size_t count, 211f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch loff_t *ppos) 212f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch{ 213f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch ssize_t retval; 214f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch ssize_t copied = 0; 215f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 216f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch if (count <= image[minor].size_buf) { 217f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch /* We copy to kernel buffer */ 218f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch copied = vme_master_read(image[minor].resource, 219f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch image[minor].kern_buf, count, *ppos); 22045f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos if (copied < 0) 221f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch return (int)copied; 222f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 223f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch retval = __copy_to_user(buf, image[minor].kern_buf, 224f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch (unsigned long)copied); 225f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch if (retval != 0) { 226f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch copied = (copied - retval); 22745f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos printk(KERN_INFO "User copy failed\n"); 228f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch return -EINVAL; 229f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch } 230f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 231f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch } else { 232f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch /* XXX Need to write this */ 23345f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos printk(KERN_INFO "Currently don't support large transfers\n"); 234f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch /* Map in pages from userspace */ 235f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 236f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch /* Call vme_master_read to do the transfer */ 237f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch return -EINVAL; 238f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch } 239f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 240f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch return copied; 241f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch} 242f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 243f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch/* 244f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch * We are going ot alloc a page during init per window for small transfers. 245f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch * Small transfers will go user space -> buffer -> VME. Larger (more than a 246f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch * page) transfers will lock the user space buffer into memory and then 247f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch * transfer the data directly from the user space buffers out to VME. 248f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch */ 249f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welchstatic ssize_t resource_from_user(unsigned int minor, const char *buf, 250f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch size_t count, loff_t *ppos) 251f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch{ 252f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch ssize_t retval; 253f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch ssize_t copied = 0; 254f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 255f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch if (count <= image[minor].size_buf) { 256f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch retval = __copy_from_user(image[minor].kern_buf, buf, 257f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch (unsigned long)count); 258f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch if (retval != 0) 259f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch copied = (copied - retval); 260f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch else 261f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch copied = count; 262f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 263f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch copied = vme_master_write(image[minor].resource, 264f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch image[minor].kern_buf, copied, *ppos); 265f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch } else { 266f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch /* XXX Need to write this */ 26745f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos printk(KERN_INFO "Currently don't support large transfers\n"); 268f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch /* Map in pages from userspace */ 269f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 270f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch /* Call vme_master_write to do the transfer */ 271f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch return -EINVAL; 272f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch } 273f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 274f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch return copied; 275f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch} 276f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 277f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welchstatic ssize_t buffer_to_user(unsigned int minor, char __user *buf, 278f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch size_t count, loff_t *ppos) 279f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch{ 280f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch void __iomem *image_ptr; 281f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch ssize_t retval; 282f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 283f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch image_ptr = image[minor].kern_buf + *ppos; 284f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 285f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch retval = __copy_to_user(buf, image_ptr, (unsigned long)count); 286f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch if (retval != 0) { 287f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch retval = (count - retval); 288f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch printk(KERN_WARNING "Partial copy to userspace\n"); 289f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch } else 290f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch retval = count; 291f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 292f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch /* Return number of bytes successfully read */ 293f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch return retval; 294f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch} 295f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 296f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welchstatic ssize_t buffer_from_user(unsigned int minor, const char *buf, 297f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch size_t count, loff_t *ppos) 298f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch{ 299f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch void __iomem *image_ptr; 300f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch size_t retval; 301f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 302f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch image_ptr = image[minor].kern_buf + *ppos; 303f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 304f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch retval = __copy_from_user(image_ptr, buf, (unsigned long)count); 305f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch if (retval != 0) { 306f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch retval = (count - retval); 307f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch printk(KERN_WARNING "Partial copy to userspace\n"); 308f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch } else 309f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch retval = count; 310f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 311f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch /* Return number of bytes successfully read */ 312f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch return retval; 313f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch} 314f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 315f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welchstatic ssize_t vme_user_read(struct file *file, char *buf, size_t count, 31645f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos loff_t *ppos) 317f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch{ 318f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev); 319f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch ssize_t retval; 320f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch size_t image_size; 321f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch size_t okcount; 322f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 323f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch down(&image[minor].sem); 324f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 325f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch /* XXX Do we *really* want this helper - we can use vme_*_get ? */ 326f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch image_size = vme_get_size(image[minor].resource); 327f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 328f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch /* Ensure we are starting at a valid location */ 329f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch if ((*ppos < 0) || (*ppos > (image_size - 1))) { 330f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch up(&image[minor].sem); 331f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch return 0; 332f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch } 333f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 334f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch /* Ensure not reading past end of the image */ 335f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch if (*ppos + count > image_size) 336f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch okcount = image_size - *ppos; 337f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch else 338f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch okcount = count; 339f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 34045f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos switch (type[minor]) { 341f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch case MASTER_MINOR: 342f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch retval = resource_to_user(minor, buf, okcount, ppos); 343f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch break; 344f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch case SLAVE_MINOR: 345f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch retval = buffer_to_user(minor, buf, okcount, ppos); 346f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch break; 347f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch default: 348f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch retval = -EINVAL; 349f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch } 350f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 351f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch up(&image[minor].sem); 352f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 353f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch if (retval > 0) 354f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch *ppos += retval; 355f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 356f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch return retval; 357f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch} 358f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 359f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welchstatic ssize_t vme_user_write(struct file *file, const char *buf, size_t count, 360f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch loff_t *ppos) 361f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch{ 362f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev); 363f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch ssize_t retval; 364f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch size_t image_size; 365f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch size_t okcount; 366f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 367f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch down(&image[minor].sem); 368f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 369f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch image_size = vme_get_size(image[minor].resource); 370f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 371f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch /* Ensure we are starting at a valid location */ 372f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch if ((*ppos < 0) || (*ppos > (image_size - 1))) { 373f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch up(&image[minor].sem); 374f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch return 0; 375f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch } 376f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 377f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch /* Ensure not reading past end of the image */ 378f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch if (*ppos + count > image_size) 379f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch okcount = image_size - *ppos; 380f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch else 381f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch okcount = count; 382f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 38345f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos switch (type[minor]) { 384f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch case MASTER_MINOR: 385f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch retval = resource_from_user(minor, buf, okcount, ppos); 386f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch break; 387f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch case SLAVE_MINOR: 388f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch retval = buffer_from_user(minor, buf, okcount, ppos); 389f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch break; 390f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch default: 391f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch retval = -EINVAL; 392f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch } 393f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 394f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch up(&image[minor].sem); 395f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 396f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch if (retval > 0) 397f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch *ppos += retval; 398f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 399f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch return retval; 400f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch} 401f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 402f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welchstatic loff_t vme_user_llseek(struct file *file, loff_t off, int whence) 403f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch{ 404877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov loff_t absolute = -1; 405877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev); 406877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov size_t image_size; 407877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov 408877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov down(&image[minor].sem); 409877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov image_size = vme_get_size(image[minor].resource); 410877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov 411877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov switch (whence) { 412877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov case SEEK_SET: 413877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov absolute = off; 414877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov break; 415877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov case SEEK_CUR: 416877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov absolute = file->f_pos + off; 417877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov break; 418877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov case SEEK_END: 419877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov absolute = image_size + off; 420877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov break; 421877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov default: 422877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov up(&image[minor].sem); 423877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov return -EINVAL; 424877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov break; 425877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov } 426877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov 427877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov if ((absolute < 0) || (absolute >= image_size)) { 428877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov up(&image[minor].sem); 429877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov return -EINVAL; 430877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov } 431877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov 432877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov file->f_pos = absolute; 433877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov 434877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov up(&image[minor].sem); 435877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov 436877de4b4866f1cc0a25a4d67d3927304556f5d1fArthur Benilov return absolute; 437f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch} 438f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 439238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch/* 440238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch * The ioctls provided by the old VME access method (the one at vmelinux.org) 441238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch * are most certainly wrong as the effectively push the registers layout 442238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch * through to user space. Given that the VME core can handle multiple bridges, 443238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch * with different register layouts this is most certainly not the way to go. 444238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch * 445238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch * We aren't using the structures defined in the Motorola driver either - these 446238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch * are also quite low level, however we should use the definitions that have 447238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch * already been defined. 448238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch */ 449f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welchstatic int vme_user_ioctl(struct inode *inode, struct file *file, 450f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch unsigned int cmd, unsigned long arg) 451f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch{ 452238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch struct vme_master master; 453238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch struct vme_slave slave; 454238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch unsigned long copied; 455f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch unsigned int minor = MINOR(inode->i_rdev); 456238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch int retval; 457238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch dma_addr_t pci_addr; 458f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 459f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch statistics.ioctls++; 460238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch 461f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch switch (type[minor]) { 462f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch case CONTROL_MINOR: 463f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch break; 464f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch case MASTER_MINOR: 465f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch switch (cmd) { 466238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch case VME_GET_MASTER: 467238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch memset(&master, 0, sizeof(struct vme_master)); 468238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch 469238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch /* XXX We do not want to push aspace, cycle and width 470238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch * to userspace as they are 471238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch */ 472238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch retval = vme_master_get(image[minor].resource, 473238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch &(master.enable), &(master.vme_addr), 474238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch &(master.size), &(master.aspace), 475238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch &(master.cycle), &(master.dwidth)); 476238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch 477238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch copied = copy_to_user((char *)arg, &master, 478238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch sizeof(struct vme_master)); 479238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch if (copied != 0) { 480238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch printk(KERN_WARNING "Partial copy to " 481238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch "userspace\n"); 482238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch return -EFAULT; 483238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch } 484f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 485238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch return retval; 486238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch break; 487238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch 488238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch case VME_SET_MASTER: 489238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch 490238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch copied = copy_from_user(&master, (char *)arg, 491238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch sizeof(master)); 492238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch if (copied != 0) { 493f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch printk(KERN_WARNING "Partial copy from " 494f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch "userspace\n"); 495f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch return -EFAULT; 496f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch } 497f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 498238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch /* XXX We do not want to push aspace, cycle and width 499238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch * to userspace as they are 500238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch */ 501238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch return vme_master_set(image[minor].resource, 502238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch master.enable, master.vme_addr, master.size, 503238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch master.aspace, master.cycle, master.dwidth); 504f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 505f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch break; 506238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch } 507238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch break; 508238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch case SLAVE_MINOR: 509238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch switch (cmd) { 510f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch case VME_GET_SLAVE: 511238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch memset(&slave, 0, sizeof(struct vme_slave)); 512238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch 513238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch /* XXX We do not want to push aspace, cycle and width 514238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch * to userspace as they are 515238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch */ 516238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch retval = vme_slave_get(image[minor].resource, 517238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch &(slave.enable), &(slave.vme_addr), 518238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch &(slave.size), &pci_addr, &(slave.aspace), 519238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch &(slave.cycle)); 520238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch 521238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch copied = copy_to_user((char *)arg, &slave, 522238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch sizeof(struct vme_slave)); 523238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch if (copied != 0) { 524238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch printk(KERN_WARNING "Partial copy to " 525238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch "userspace\n"); 526238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch return -EFAULT; 527238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch } 528238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch 529238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch return retval; 530238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch break; 531f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 532238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch case VME_SET_SLAVE: 533f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 534238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch copied = copy_from_user(&slave, (char *)arg, 535f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 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, 599238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch .remove = 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); 614238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch goto err_nocard; 615238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch } 616238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch 617238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch /* Let's start by supporting one bus, we can support more than one 618238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch * in future revisions if that ever becomes necessary. 619238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch */ 620238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch if (bus_num > USER_BUS_MAX) { 62151616e21066d040988a33effa82d4ef37fd60959Martyn Welch printk(KERN_ERR "%s: Driver only able to handle %d buses\n", 62251616e21066d040988a33effa82d4ef37fd60959Martyn Welch driver_name, USER_BUS_MAX); 623238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch bus_num = USER_BUS_MAX; 624238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch } 625238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch 626238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch 627238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch /* Dynamically create the bind table based on module parameters */ 628238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch ids = kmalloc(sizeof(struct vme_device_id) * (bus_num + 1), GFP_KERNEL); 629238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch if (ids == NULL) { 630238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch printk(KERN_ERR "%s: Unable to allocate ID table\n", 631238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch driver_name); 632238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch goto err_id; 633238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch } 634238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch 635238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch memset(ids, 0, (sizeof(struct vme_device_id) * (bus_num + 1))); 636238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch 637238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch for (i = 0; i < bus_num; i++) { 638238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch ids[i].bus = bus[i]; 639238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch /* 640238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch * We register the driver against the slot occupied by *this* 641238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch * card, since it's really a low level way of controlling 642238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch * the VME bridge 643238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch */ 644238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch ids[i].slot = VME_SLOT_CURRENT; 645238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch } 646238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch 647238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch vme_user_driver.bind_table = ids; 648238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch 649f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch retval = vme_register_driver(&vme_user_driver); 650238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch if (retval != 0) 651238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch goto err_reg; 652238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch 653238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch return retval; 654238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch 655238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch vme_unregister_driver(&vme_user_driver); 656238add523bf9c89db1a191599fff2770af55e0fdMartyn Welcherr_reg: 657238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch kfree(ids); 658238add523bf9c89db1a191599fff2770af55e0fdMartyn Welcherr_id: 659238add523bf9c89db1a191599fff2770af55e0fdMartyn Welcherr_nocard: 660f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch return retval; 661f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch} 662f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 663f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch/* 664238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch * In this simple access driver, the old behaviour is being preserved as much 665238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch * as practical. We will therefore reserve the buffers and request the images 666238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch * here so that we don't have to do it later. 667f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch */ 668238add523bf9c89db1a191599fff2770af55e0fdMartyn Welchstatic int __init vme_user_probe(struct device *dev, int cur_bus, int cur_slot) 669f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch{ 670f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch int i, err; 671beb9ccc635433065a099b75dc8b22caf0844014aMartyn Welch char name[12]; 672f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 673238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch /* Save pointer to the bridge device */ 674238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch if (vme_user_bridge != NULL) { 675238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch printk(KERN_ERR "%s: Driver can only be loaded for 1 device\n", 676238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch driver_name); 677238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch err = -EINVAL; 678238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch goto err_dev; 679238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch } 680f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch vme_user_bridge = dev; 681f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 682f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch /* Initialise descriptors */ 683f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch for (i = 0; i < VME_DEVS; i++) { 684f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch image[i].kern_buf = NULL; 685f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch image[i].pci_buf = 0; 686f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch init_MUTEX(&(image[i].sem)); 687f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch image[i].device = NULL; 688f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch image[i].resource = NULL; 689f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch image[i].users = 0; 690f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch } 691f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 692f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch /* Initialise statistics counters */ 693f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch reset_counters(); 694f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 695f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch /* Assign major and minor numbers for the driver */ 696f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch err = register_chrdev_region(MKDEV(VME_MAJOR, 0), VME_DEVS, 697f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch driver_name); 698f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch if (err) { 699f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch printk(KERN_WARNING "%s: Error getting Major Number %d for " 700f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch "driver.\n", driver_name, VME_MAJOR); 701f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch goto err_region; 702f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch } 703f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 704f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch /* Register the driver as a char device */ 705f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch vme_user_cdev = cdev_alloc(); 706f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch vme_user_cdev->ops = &vme_user_fops; 707f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch vme_user_cdev->owner = THIS_MODULE; 708f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch err = cdev_add(vme_user_cdev, MKDEV(VME_MAJOR, 0), VME_DEVS); 709f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch if (err) { 710f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch printk(KERN_WARNING "%s: cdev_all failed\n", driver_name); 711f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch goto err_char; 712f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch } 713f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 714f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch /* Request slave resources and allocate buffers (128kB wide) */ 715f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch for (i = SLAVE_MINOR; i < (SLAVE_MAX + 1); i++) { 716f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch /* XXX Need to properly request attributes */ 7175188d74cc4597a63a0907b8996ca0a2d36f1b970Arthur Benilov /* For ca91cx42 bridge there are only two slave windows 7185188d74cc4597a63a0907b8996ca0a2d36f1b970Arthur Benilov * supporting A16 addressing, so we request A24 supported 7195188d74cc4597a63a0907b8996ca0a2d36f1b970Arthur Benilov * by all windows. 7205188d74cc4597a63a0907b8996ca0a2d36f1b970Arthur Benilov */ 721f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch image[i].resource = vme_slave_request(vme_user_bridge, 7225188d74cc4597a63a0907b8996ca0a2d36f1b970Arthur Benilov VME_A24, VME_SCT); 723f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch if (image[i].resource == NULL) { 724f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch printk(KERN_WARNING "Unable to allocate slave " 725f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch "resource\n"); 726238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch goto err_slave; 727f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch } 728f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch image[i].size_buf = PCI_BUF_SIZE; 729f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch image[i].kern_buf = vme_alloc_consistent(image[i].resource, 730f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch image[i].size_buf, &(image[i].pci_buf)); 731f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch if (image[i].kern_buf == NULL) { 732f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch printk(KERN_WARNING "Unable to allocate memory for " 733f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch "buffer\n"); 734f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch image[i].pci_buf = 0; 735f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch vme_slave_free(image[i].resource); 736f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch err = -ENOMEM; 737238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch goto err_slave; 738f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch } 739f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch } 740f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 741f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch /* 742f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch * Request master resources allocate page sized buffers for small 743f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch * reads and writes 744f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch */ 745f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch for (i = MASTER_MINOR; i < (MASTER_MAX + 1); i++) { 746f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch /* XXX Need to properly request attributes */ 747f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch image[i].resource = vme_master_request(vme_user_bridge, 748f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch VME_A32, VME_SCT, VME_D32); 749f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch if (image[i].resource == NULL) { 750f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch printk(KERN_WARNING "Unable to allocate master " 751f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch "resource\n"); 752238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch goto err_master; 753f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch } 75433e920d9ebaddbc9cf51cf6e1de7baa8d7b8d6ddArthur Benilov image[i].size_buf = PCI_BUF_SIZE; 75533e920d9ebaddbc9cf51cf6e1de7baa8d7b8d6ddArthur Benilov image[i].kern_buf = kmalloc(image[i].size_buf, GFP_KERNEL); 75633e920d9ebaddbc9cf51cf6e1de7baa8d7b8d6ddArthur Benilov if (image[i].kern_buf == NULL) { 75733e920d9ebaddbc9cf51cf6e1de7baa8d7b8d6ddArthur Benilov printk(KERN_WARNING "Unable to allocate memory for " 75833e920d9ebaddbc9cf51cf6e1de7baa8d7b8d6ddArthur Benilov "master window buffers\n"); 75933e920d9ebaddbc9cf51cf6e1de7baa8d7b8d6ddArthur Benilov err = -ENOMEM; 76033e920d9ebaddbc9cf51cf6e1de7baa8d7b8d6ddArthur Benilov goto err_master_buf; 76133e920d9ebaddbc9cf51cf6e1de7baa8d7b8d6ddArthur Benilov } 762f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch } 763f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 764f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch /* Create sysfs entries - on udev systems this creates the dev files */ 765f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch vme_user_sysfs_class = class_create(THIS_MODULE, driver_name); 766f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch if (IS_ERR(vme_user_sysfs_class)) { 767f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch printk(KERN_ERR "Error creating vme_user class.\n"); 768f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch err = PTR_ERR(vme_user_sysfs_class); 769f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch goto err_class; 770f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch } 771f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 772f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch /* Add sysfs Entries */ 77345f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos for (i = 0; i < VME_DEVS; i++) { 774f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch switch (type[i]) { 775f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch case MASTER_MINOR: 77645f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos sprintf(name, "bus/vme/m%%d"); 777f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch break; 778f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch case CONTROL_MINOR: 77945f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos sprintf(name, "bus/vme/ctl"); 780f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch break; 781f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch case SLAVE_MINOR: 78245f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos sprintf(name, "bus/vme/s%%d"); 783f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch break; 784f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch default: 785f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch err = -EINVAL; 786f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch goto err_sysfs; 787f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch break; 788f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch } 789f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 790f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch image[i].device = 791f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch device_create(vme_user_sysfs_class, NULL, 792f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch MKDEV(VME_MAJOR, i), NULL, name, 79345f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos (type[i] == SLAVE_MINOR) ? i - (MASTER_MAX + 1) : i); 794f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch if (IS_ERR(image[i].device)) { 79545f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos printk(KERN_INFO "%s: Error creating sysfs device\n", 796f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch driver_name); 797f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch err = PTR_ERR(image[i].device); 798f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch goto err_sysfs; 799f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch } 800f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch } 801f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 802f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch return 0; 803f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 804f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch /* Ensure counter set correcty to destroy all sysfs devices */ 805f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch i = VME_DEVS; 806f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welcherr_sysfs: 80745f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos while (i > 0) { 808f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch i--; 809f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch device_destroy(vme_user_sysfs_class, MKDEV(VME_MAJOR, i)); 810f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch } 811f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch class_destroy(vme_user_sysfs_class); 812f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 813238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch /* Ensure counter set correcty to unalloc all master windows */ 814238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch i = MASTER_MAX + 1; 81533e920d9ebaddbc9cf51cf6e1de7baa8d7b8d6ddArthur Beniloverr_master_buf: 81633e920d9ebaddbc9cf51cf6e1de7baa8d7b8d6ddArthur Benilov for (i = MASTER_MINOR; i < (MASTER_MAX + 1); i++) 81733e920d9ebaddbc9cf51cf6e1de7baa8d7b8d6ddArthur Benilov kfree(image[i].kern_buf); 818238add523bf9c89db1a191599fff2770af55e0fdMartyn Welcherr_master: 819238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch while (i > MASTER_MINOR) { 820238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch i--; 821238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch vme_master_free(image[i].resource); 822238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch } 823238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch 824238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch /* 825238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch * Ensure counter set correcty to unalloc all slave windows and buffers 826238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch */ 827f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch i = SLAVE_MAX + 1; 828238add523bf9c89db1a191599fff2770af55e0fdMartyn Welcherr_slave: 829238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch while (i > SLAVE_MINOR) { 830f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch i--; 831f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch vme_slave_free(image[i].resource); 832f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch buf_unalloc(i); 833f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch } 834f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welcherr_class: 835f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch cdev_del(vme_user_cdev); 836f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welcherr_char: 837f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch unregister_chrdev_region(MKDEV(VME_MAJOR, 0), VME_DEVS); 838f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welcherr_region: 839238add523bf9c89db1a191599fff2770af55e0fdMartyn Welcherr_dev: 840f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch return err; 841f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch} 842f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 843238add523bf9c89db1a191599fff2770af55e0fdMartyn Welchstatic int __exit vme_user_remove(struct device *dev, int cur_bus, int cur_slot) 844f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch{ 845f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch int i; 846f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 847f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch /* Remove sysfs Entries */ 84845f9f018967c3fc112a03a99a8fdfad3621407a7Nanakos Chrysostomos for (i = 0; i < VME_DEVS; i++) 849f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch device_destroy(vme_user_sysfs_class, MKDEV(VME_MAJOR, i)); 850f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch class_destroy(vme_user_sysfs_class); 851f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 85233e920d9ebaddbc9cf51cf6e1de7baa8d7b8d6ddArthur Benilov for (i = MASTER_MINOR; i < (MASTER_MAX + 1); i++) 85333e920d9ebaddbc9cf51cf6e1de7baa8d7b8d6ddArthur Benilov kfree(image[i].kern_buf); 85433e920d9ebaddbc9cf51cf6e1de7baa8d7b8d6ddArthur Benilov 855f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch for (i = SLAVE_MINOR; i < (SLAVE_MAX + 1); i++) { 856238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch vme_slave_set(image[i].resource, 0, 0, 0, 0, VME_A32, 0); 857238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch vme_slave_free(image[i].resource); 858f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch buf_unalloc(i); 859f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch } 860f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 861f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch /* Unregister device driver */ 862f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch cdev_del(vme_user_cdev); 863f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 864f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch /* Unregiser the major and minor device numbers */ 865f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch unregister_chrdev_region(MKDEV(VME_MAJOR, 0), VME_DEVS); 866238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch 867238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch return 0; 868f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch} 869f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 870238add523bf9c89db1a191599fff2770af55e0fdMartyn Welchstatic void __exit vme_user_exit(void) 871238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch{ 872238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch vme_unregister_driver(&vme_user_driver); 873238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch 874238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch kfree(vme_user_driver.bind_table); 875238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch} 876238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch 877238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch 878238add523bf9c89db1a191599fff2770af55e0fdMartyn WelchMODULE_PARM_DESC(bus, "Enumeration of VMEbus to which the driver is connected"); 879238add523bf9c89db1a191599fff2770af55e0fdMartyn Welchmodule_param_array(bus, int, &bus_num, 0); 880238add523bf9c89db1a191599fff2770af55e0fdMartyn Welch 881f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn WelchMODULE_DESCRIPTION("VME User Space Access Driver"); 88266bd8db52ab48e7189e02d4bf1f23109cc1ede70Martyn WelchMODULE_AUTHOR("Martyn Welch <martyn.welch@ge.com"); 883f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn WelchMODULE_LICENSE("GPL"); 884f00a86d98a1ec3e99d352cda926fab767ba43b1fMartyn Welch 885238add523bf9c89db1a191599fff2770af55e0fdMartyn Welchmodule_init(vme_user_init); 886238add523bf9c89db1a191599fff2770af55e0fdMartyn Welchmodule_exit(vme_user_exit); 887