From: sergej.bauer@gmail.com
To: linux-kernel@vger.kernel.org
Cc: arnd@arndb.de, gregkh@linuxfoundation.org
Subject: [PATCH 1/1] Add mkopci driver
Date: Fri, 20 Mar 2015 15:10:26 +0300 [thread overview]
Message-ID: <201503201510.26639.sergej.bauer@gmail.com> (raw)
mkopci (MB11.xx) device (RC Module project) provides data transference through a serial bus bar according to MIL-STD-1553.
the driver used for operating devices, reads PCI configuration space and pass interrupts to user-space applications.
Please consider adding this patch to the linux-next queue.
Signed-off-by: Sergej Bauer <sergej.bauer@gmail.com>
---
Documentation/misc-devices/mkopci.txt | 51 ++
drivers/misc/Kconfig | 9 +
drivers/misc/Makefile | 1 +
drivers/misc/mkopci.c | 1272 +++++++++++++++++++++++++++++++++
include/misc/mkopci.h | 81 +++
5 files changed, 1414 insertions(+)
create mode 100644 Documentation/misc-devices/mkopci.txt
create mode 100644 drivers/misc/mkopci.c
create mode 100644 include/misc/mkopci.h
diff --git a/Documentation/misc-devices/mkopci.txt b/Documentation/misc-devices/mkopci.txt
new file mode 100644
index 0000000..0fcec6d
--- /dev/null
+++ b/Documentation/misc-devices/mkopci.txt
@@ -0,0 +1,51 @@
+ PCI-based MKO bus driver.
+
+
+For dealing with driver without using of root's account it will be helpfull
+to add group `mkopci' with appropriate users and put file, say 60-mkopci.rules to
+/etc/udev/rules.d in your system.
+--- cut 60-mkopci.rules ---
+# MKO devices
+KERNEL=="mkopci*", SUBSYSTEM=="mkopci", ACTION=="add", DRIVERS=="?*", ATTRS{idVendor}=="0x6403"
+GROUP="mkopci"
+---
+
+
+Kernel module parameters
+
+Kernel module can take parameters 'v' and 'omited'
+- 'v' is 0 to 3, and affects the amount of information
+output to the system log.
+0 - (default) comletely silent
+1 - prints only detected BARs, reports loading / unloading
+2 - + prints IRQ and memory mapping events
+3 - + prints ioctl events
+
+- 'plx9050bug_quirk' controls workaround controller PLX9050. Can
+the following values:
+0 - workaround is disabled (default)
+1 - workaround is enabled
+2 - forced bug
+
+- 'omited' tells the driver which device should not be initialized
+at load time.
+The value of this parameter to the kernel module 2.4 is a physical address
+device on the bus, such as "0x10800" without the quotes.
+In kernel versions 2.6+ can exclude multiple devices, transferring them
+values separated by commas, but not more than 4.
+
+The parameter values can be specified as follows:
+$ insmod/modprobe mkopci.[ko/o] parameter1_name=value [parameter2_name=value]
+
+Record the physical address of the device in the file /proc/mkopci/core
+also controls "visibility" for user programs. Missed return device
+You can command 'echo ADDR > /proc/mkopci/core'. ADDR can be either a simple
+number of device, but always in hexadecimal, or (for Linux-2.6+)
+the number of devices in a standard format Linux kind NM:XY.z.
+
+
+PROC filesystem
+
+/proc/mkopci/devices - read only text device table
+/proc/mkopci/core - device table for user space applications
+
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 006242c..7db247e 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -124,6 +124,15 @@ config PHANTOM
If you choose to build module, its name will be phantom. If unsure,
say N here.
+config MKOPCI
+ tristate "Module PCI bus driver"
+ depends on PCI && PROC_FS
+ help
+ Say Y here if you want to build a driver for Module(RC) MKOPCI devices.
+
+ If you choose to build module, its name will be mkopci. If unsure,
+ say N here.
+
config INTEL_MID_PTI
tristate "Parallel Trace Interface for MIPI P1149.7 cJTAG standard"
depends on PCI && TTY && (X86_INTEL_MID || COMPILE_TEST)
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 7d5c4cd..afb92b4 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -56,3 +56,4 @@ obj-$(CONFIG_GENWQE) += genwqe/
obj-$(CONFIG_ECHO) += echo/
obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o
obj-$(CONFIG_CXL_BASE) += cxl/
+obj-$(CONFIG_MKOPCI) += mkopci.o
diff --git a/drivers/misc/mkopci.c b/drivers/misc/mkopci.c
new file mode 100644
index 0000000..d223478
--- /dev/null
+++ b/drivers/misc/mkopci.c
@@ -0,0 +1,1272 @@
+/*
+ * MKOPCI driver
+ *
+ * Copyright (C) 2007-2015 Sergej Bauer <sergej.bauer@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, as published by
+ * the Free Software Foundation, version 2.
+*/
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/pci.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/interrupt.h>
+#include <linux/cdev.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+
+#include "misc/mkopci.h"
+
+#define MKO_VENDOR 0x6403
+#define MKO_DEVICE1 0x0430
+#define MKO_DEVICE2 0x0431
+#define MKO_DEVICE3 0x0434
+
+#define PLX9050BUG_BAR0 0x1
+#define PLX9050BUG_BAR1 0x2
+#define PLX9050BUG_INJECT 0x4
+
+#if !defined(__user)
+#define __user
+#endif
+
+static struct pci_device_id ids[] = {
+ {MKO_VENDOR, MKO_DEVICE1, PCI_ANY_ID, PCI_ANY_ID,},
+ {MKO_VENDOR, MKO_DEVICE2, PCI_ANY_ID, PCI_ANY_ID,},
+ {MKO_VENDOR, MKO_DEVICE3, PCI_ANY_ID, PCI_ANY_ID,},
+ {0, 0,}
+};
+
+MODULE_DEVICE_TABLE(pci, ids);
+
+#ifdef __LP64__
+#define PFMT "llx"
+#else
+#define PFMT "x"
+#endif
+
+unsigned char drv_version = 0x11;
+#define mko_pci_addr(bus, device, func, regoffs) (\
+ ((bus & 0xFF) << 16) | ((device & 0x1F) << 11) | \
+ ((func & 0x7) << 8) | (regoffs & 0xFC))
+
+static struct kmem_cache *mkopci_device_cache;
+static dev_t devp;
+static struct class *mkopci_class;
+static struct rw_semaphore devices_sem;
+static LIST_HEAD(devices);
+static atomic_t devices_nr = ATOMIC_INIT(0);
+
+/*** module parameters ***/
+static int plx9050bug_quirk = 1;
+/* verbosity level */
+static int v;
+module_param(plx9050bug_quirk, int, S_IRUGO);
+module_param(v, int, S_IRUGO);
+MODULE_PARM_DESC(plx9050bug_quirk, "PLX9050 bug quirk");
+MODULE_PARM_DESC(v, "verbosity level");
+
+/* only MAX_DEVICES_NR devices at once can be omited */
+static int omited[MAX_DEVICES_NR];
+static int omited_nr;
+module_param_array(omited, int, &omited_nr, S_IRUGO);
+MODULE_PARM_DESC(omited, "device(s) to omit");
+#ifndef VM_RESERVED
+#define VM_RESERVED (VM_DONTEXPAND | VM_DONTDUMP)
+#endif
+#define __unused __attribute__ ((unused))
+
+/***************************** Interrupt handling *****************************/
+static int mkopci_wait_irq(struct mkopci_device *dev)
+{
+ volatile unsigned long *LPCI_4C =
+ (unsigned long *)(dev->core.lin_base[0] + 0x4C);
+
+ *LPCI_4C |= 0x49;
+
+ if (wait_event_interruptible(dev->wq, *LPCI_4C & 0x24))
+ return -ERESTARTSYS;
+ if (v > 1)
+ pr_info("mkopci%d: irq received\n", dev->core.n_dev);
+ *LPCI_4C &= ~0x40;
+
+ return 0;
+}
+
+static irqreturn_t mkopci_int_handler(int __unused irq, void *dev)
+{
+ struct mkopci_device *mko_dev = (struct mkopci_device *)dev;
+ volatile unsigned long *LPCI_4C =
+ (unsigned long *)((mko_dev->core.lin_base[0] + 0x4C));
+
+ if (*LPCI_4C & 0x24) {
+ *LPCI_4C &= ~0x40;
+ wake_up_interruptible(&mko_dev->wq);
+ } else
+ return IRQ_NONE;
+
+ return IRQ_HANDLED;
+}
+
+static int mkopci_request_irq(struct mkopci_device *dev)
+{
+ int ret = 0;
+
+ if (dev->core.irq_requested) {
+ pr_err("mkopci%d: irq already requested\n", dev->core.n_dev);
+ return -EBUSY;
+ }
+ ret = request_irq(dev->core.irq, &mkopci_int_handler, IRQF_SHARED,
+ dev->core.name, dev);
+ if (ret) {
+ pr_err("mkopci%d: failed to request irq %d\n", dev->core.n_dev,
+ dev->core.irq);
+ return ret;
+ }
+
+ dev->core.irq_requested++;
+
+ if (strcmp(current->comm, "TestSetISR") && v > 1) {
+ pr_info("mkopci%d: irq %d requested (%s)\n", dev->core.n_dev,
+ dev->core.irq, current->comm);
+ }
+
+ return ret;
+}
+
+static void mkopci_free_irq(struct mkopci_device *dev)
+{
+ if (dev->core.irq_requested) {
+ synchronize_irq(dev->core.irq);
+ free_irq(dev->core.irq, dev);
+ dev->core.irq_requested--;
+
+ if (strcmp(current->comm, "TestSetISR") && v > 1) {
+ pr_info("mkopci%d: irq %d released\n", dev->core.n_dev,
+ dev->core.irq);
+ }
+ }
+}
+/************************* End of Interrupt handling **************************/
+
+/**************************** File operations *********************************/
+static int mkopci_open(struct inode *inode, struct file *filp)
+{
+ int ret = 0;
+ struct mkopci_device *dev =
+ container_of(inode->i_cdev, struct mkopci_device, cdev);
+
+ if (!try_module_get(THIS_MODULE))
+ return -EINVAL;
+
+ if (dev == NULL) {
+ module_put(THIS_MODULE);
+ return -ENODEV;
+ }
+
+ down_write(&dev->rwsem);
+ if (dev->omited) {
+ ret = -ENODEV;
+ goto err;
+ }
+ if (!(filp->f_flags & O_NONBLOCK)) {
+ if (dev->core.process) {
+ ret = -EBUSY;
+ goto err;
+ }
+ dev->backdoor = 0;
+ dev->core.process = current->pid;
+ } else {
+ dev->backdoor = current->pid;
+ dev->core.process = 0;
+ }
+
+ filp->private_data = dev;
+
+ if (strcmp(current->comm, "TstOpenClose") && v > 1)
+ pr_info("mkopci%d: device opened in %s mode (%s)\n",
+ dev->core.n_dev, dev->backdoor ? "backdoor" : "regular",
+ current->comm);
+ goto out;
+
+err:
+ module_put(THIS_MODULE);
+out:
+ up_write(&dev->rwsem);
+
+ return ret;
+}
+
+static int mkopci_release(struct inode __unused *inode, struct file *filp)
+{
+ struct mkopci_device *dev;
+
+ if (filp->private_data == NULL)
+ return 0;
+
+ dev = (struct mkopci_device *)filp->private_data;
+ down_write(&dev->rwsem);
+ dev->core.process = 0;
+ dev->backdoor = 0;
+ dev->core.c_bar = -1;
+ filp->private_data = NULL;
+ mkopci_free_irq(dev);
+
+ if (strcmp(current->comm, "TstOpenClose") && v > 1)
+ pr_info("mkopci%d: device released\n", dev->core.n_dev);
+ up_write(&dev->rwsem);
+ module_put(THIS_MODULE);
+
+ return 0;
+}
+
+static long mkopci_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ int ret = 0, n = 0;
+ struct mkopci_device *dev =
+ (struct mkopci_device *)filp->private_data, *d;
+
+ if (_IOC_TYPE(cmd) != MKO_IOC_MAGIC) {
+ pr_err("mkopci%d: _IOC_TYPE(cmd) != MKO_IOC_MAGIC\n",
+ dev->core.n_dev);
+ return -ENOTTY;
+ }
+
+ if (_IOC_NR(cmd) > MKO_IOC_MAXNR) {
+ pr_err("mkopci%d: _IOC_NR(cmd) > MKO_IOC_MAXNR\n",
+ dev->core.n_dev);
+ return -ENOTTY;
+ }
+
+ if (_IOC_DIR(cmd) & _IOC_READ) {
+ ret =
+ !access_ok(VERIFY_WRITE, (void __user *)arg,
+ _IOC_SIZE(cmd));
+ } else if (_IOC_DIR(cmd) & _IOC_WRITE) {
+ ret =
+ !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd));
+ }
+
+ if (ret) {
+ pr_err("mkopci%d: mkopci_ioctl: access_ok failed\n",
+ dev->core.n_dev);
+ return -EIO;
+ }
+
+ switch (cmd) {
+ case MKOPCI_IOCTL_CWPID:
+ if (v > 2)
+ pr_info("mkopci%d: MKOPCI_IOCTL_CWPID ioctl\n",
+ dev->core.n_dev);
+ down_read(&dev->rwsem);
+ ret = dev->core.process;
+ up_read(&dev->rwsem);
+ break;
+ case MKOPCI_IOCTL_GET_VERSION:
+ if (v > 2)
+ pr_info("mkopci%d: MKOPCI_IOCTL_GET_VERSION ioctl\n",
+ dev->core.n_dev);
+ ret = put_user(drv_version, (int __user *)arg);
+ break;
+ case MKOPCI_IOCTL_GET_BOARDS_COUNT:
+ if (v > 2)
+ pr_info
+ ("mkopci%d: MKOPCI_IOCTL_GET_BOARDS_COUNT ioctl\n",
+ dev->core.n_dev);
+ down_read(&dev->rwsem);
+ ret =
+ put_user((unsigned short)atomic_read(&devices_nr),
+ (int __user *)arg);
+ up_read(&dev->rwsem);
+ break;
+ case MKOPCI_IOCTL_GET_DEVICE_TABLE:
+ if (v > 2)
+ pr_info
+ ("mkopci%d: MKOPCI_IOCTL_GET_DEVICE_TABLE ioctl\n",
+ dev->core.n_dev);
+ down_read(&devices_sem);
+ if (put_user
+ ((unsigned short)atomic_read(&devices_nr),
+ (int __user *)arg)) {
+ up_read(&devices_sem);
+ ret = -ERESTARTSYS;
+ break;
+ }
+ list_for_each_entry(d, &devices, list) {
+ down_read(&d->rwsem);
+ if (d->omited) {
+ up_read(&d->rwsem);
+ continue;
+ }
+ if (copy_to_user
+ ((void __user *)arg + sizeof(unsigned short) +
+ n * sizeof(struct mkopci_core), &d->core,
+ sizeof(struct mkopci_core))) {
+ up_read(&d->rwsem);
+ ret = -ERESTARTSYS;
+ break;
+ }
+ up_read(&d->rwsem);
+ n++;
+ }
+ up_read(&devices_sem);
+ break;
+ case MKOPCI_IOCTL_ATTACH_IRQ:
+ if (v > 2)
+ pr_info("mkopci%d: MKOPCI_IOCTL_ATTACH_IRQ ioctl\n",
+ dev->core.n_dev);
+ down_write(&dev->rwsem);
+ ret = mkopci_request_irq(dev);
+ up_write(&dev->rwsem);
+ break;
+ case MKOPCI_IOCTL_DETACH_IRQ:
+ if (v > 2)
+ pr_info("mkopci%d: MKOPCI_IOCTL_DETACH_IRQ ioctl\n",
+ dev->core.n_dev);
+ down_write(&dev->rwsem);
+ mkopci_free_irq(dev);
+ up_write(&dev->rwsem);
+ break;
+ case MKOPCI_IOCTL_WAIT_IRQ:
+ if (v > 2)
+ pr_info("mkopci%d: MKOPCI_IOCTL_WAIT_IRQ ioctl\n",
+ dev->core.n_dev);
+ ret = mkopci_wait_irq(dev);
+ break;
+ case MKOPCI_IOCTL_REQUEST_BAR:
+ if (v > 2)
+ pr_info("mkopci%d: MKOPCI_IOCTL_REQUEST_BAR ioctl\n",
+ dev->core.n_dev);
+ down_write(&dev->rwsem);
+ ret = get_user(dev->core.c_bar, (int __user *)arg);
+ up_write(&dev->rwsem);
+ break;
+ default:
+ pr_err("mkopci%d: invalid ioctl %d\n", dev->core.n_dev,
+ _IOC_NR(cmd));
+ ret = -EINVAL;
+ break;
+ };
+
+ return ret;
+}
+
+#ifndef pgprot_noncached
+static inline pgprot_t pgprot_noncached(pgprot_t _prot)
+{
+ unsigned long prot = pgprot_val(_prot);
+
+ if (boot_cpu_data.x86 > 3)
+ prot |= _PAGE_PCD | _PAGE_PWT;
+
+ return __pgprot(prot);
+}
+#endif
+
+static int mkopci_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ struct mkopci_device *dev = filp->private_data;
+ unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+
+ if (dev->core.c_bar == -1) {
+ pr_err("mkopci%d: invalid c_bar number\n", dev->core.n_dev);
+ return -EINVAL;
+ }
+
+ if (vma->vm_pgoff != 0) {
+ pr_err("mkopci%d: vma->vm_pgoff != 0, aborting mapping\n",
+ dev->core.n_dev);
+ return -EINVAL;
+ }
+
+ if (PAGE_ALIGN(dev->core.mem_size[dev->core.c_bar]) !=
+ PAGE_ALIGN(vma->vm_end - vma->vm_start)) {
+ pr_err("mkopci%d: PAGE_ALIGN error\n", dev->core.n_dev);
+ return -EINVAL;
+ }
+
+ if (offset >= __pa(high_memory) || (filp->f_flags & O_SYNC))
+ vma->vm_flags |= VM_IO;
+ vma->vm_flags |= VM_RESERVED | VM_SHARED;
+
+ if (v > 1 && strcmp("TstOpenClose", current->comm)) {
+ pr_info
+ ("mkopci%d: .mmap BAR%d: [0x%lx - 0x%lx], vma [0x%lx - 0x%lx]\n",
+ dev->core.n_dev, dev->core.c_bar,
+ dev->core.mem_base[dev->core.c_bar],
+ dev->core.mem_base[dev->core.c_bar] +
+ dev->core.mem_size[dev->core.c_bar], vma->vm_start,
+ vma->vm_end);
+ }
+
+ if (remap_pfn_range
+ (vma, vma->vm_start,
+ virt_to_phys(bus_to_virt(dev->core.mem_base[dev->core.c_bar])) >>
+ PAGE_SHIFT, dev->core.mem_size[dev->core.c_bar],
+ pgprot_noncached(vma->vm_page_prot))) {
+ pr_err("mkopci%d: memory remapping failed\n", dev->core.n_dev);
+ return -EAGAIN;
+ }
+
+ return 0;
+}
+
+static const struct file_operations mkopci_fops = {
+ .owner = THIS_MODULE,
+ .open = mkopci_open,
+ .release = mkopci_release,
+ .unlocked_ioctl = mkopci_ioctl,
+ .mmap = mkopci_mmap,
+};
+/************************ End of File operations ******************************/
+
+/*************************** pci_driver functions *****************************/
+static int mkopci_probe_cdevhelper(struct pci_dev *dev)
+{
+ struct mkopci_device *device =
+ (struct mkopci_device *)pci_get_drvdata(dev);
+ struct device *dev_;
+ int err = 0;
+
+ cdev_init(&device->cdev, &mkopci_fops);
+ device->cdev.owner = THIS_MODULE;
+
+ err = cdev_add(&device->cdev, MKDEV(MAJOR(devp),
+ MINOR(devp) + device->core.n_dev), 1);
+ if (err) {
+ pr_err("mkopci%d: error while cdev_add\n", device->core.n_dev);
+ return err;
+ }
+
+ dev_ =
+ device_create(mkopci_class, NULL,
+ MKDEV(MAJOR(devp), MINOR(devp) + device->core.n_dev),
+ NULL, device->core.name);
+ if (IS_ERR(dev_)) {
+ pr_err("mkopci%d: Unable to create device\n",
+ device->core.n_dev);
+ err = PTR_ERR(dev_);
+ cdev_del(&device->cdev);
+ }
+
+ return err;
+}
+
+static int mkopci_plx9050workaround(struct pci_dev *dev, int plx9050bug)
+{
+ struct mkopci_device *device;
+ int err = 0;
+ phys_addr_t plxphys = ~(phys_addr_t) 0;
+ resource_size_t len;
+ struct resource *res;
+
+ device = (struct mkopci_device *)pci_get_drvdata(dev);
+ if (!device) {
+ err = -ENODEV;
+ goto out;
+ }
+
+ if (plx9050bug & PLX9050BUG_INJECT || plx9050bug & PLX9050BUG_BAR0) {
+ plxphys =
+ (pci_resource_start(dev, 0) & ~0xff) +
+ ((plx9050bug & PLX9050BUG_INJECT) ? 0x80 : 0x0);
+ if (plx9050bug & PLX9050BUG_INJECT) {
+ pr_info
+ ("mkopci%d: [PLX9050 bug injection] mem start = 0x%"
+ PFMT "\n", device->core.n_dev, plxphys);
+ dev->resource[0].start |= 0x80;
+ dev->resource[0].end =
+ dev->resource[0].start + 0x80 - 1;
+ err = pci_request_region(dev, 0, device->core.name);
+ if (err) {
+ pr_err("failed to request region 0");
+ goto out;
+ }
+ } else {
+ pr_info
+ ("mkopci%d: [PLX9050 bug workaround] mem start = 0x%"
+ PFMT "\n", device->core.n_dev, plxphys);
+ res = &dev->resource[0];
+ res->start &= ~0xff;
+ res->end = res->start + 0x80 - 1;
+ err = pci_request_region(dev, 0, device->core.name);
+ if (err) {
+ err =
+ allocate_resource(dev->resource[0].parent,
+ res, 0x80,
+ res->parent->start,
+ res->parent->end - 0x80,
+ 0x100, NULL, NULL);
+ if (err)
+ err =
+ allocate_resource(&iomem_resource,
+ res, 0x80,
+ iomem_resource.
+ start,
+ iomem_resource.
+ end - 0x80, 0x100,
+ NULL, NULL);
+ if (err) {
+ pr_err("failed to allocate region");
+ goto out;
+ }
+ err =
+ pci_request_region(dev, 0,
+ device->core.name);
+ if (err) {
+ pr_err("failed to allocate region");
+ goto out;
+ }
+ }
+ }
+ len = pci_resource_len(dev, 0);
+ pci_write_config_dword(dev, PCI_BASE_ADDRESS_0,
+ dev->resource[0].start);
+
+ device->core.mem_base[0] = dev->resource[0].start;
+ device->core.mem_size[0] = len;
+ pr_info("mkopci%d: device->core.mem_size[0] = %d\n",
+ device->core.n_dev, device->core.mem_size[0]);
+ }
+
+ if (plx9050bug & PLX9050BUG_INJECT)
+ goto out;
+
+ if (plx9050bug & PLX9050BUG_BAR1) {
+ plxphys = pci_resource_start(dev, 1);
+ len = pci_resource_len(dev, 1);
+ plxphys = plxphys & (PAGE_MASK | 0xf00);
+ pci_write_config_dword(dev, PCI_BASE_ADDRESS_1, plxphys);
+ dev->resource[1].start = plxphys;
+ dev->resource[1].end = dev->resource[1].start + len - 1;
+
+ if (pci_request_region(dev, 1, device->core.name)) {
+ err = -EFAULT;
+ if (plx9050bug & PLX9050BUG_BAR0) {
+ iounmap((void *)device->core.lin_base[0]);
+ device->core.lin_base[0] = 0;
+ pci_release_region(dev, 0);
+ }
+ goto out;
+ }
+
+ if (v > 0)
+ pr_info
+ ("mkopci%d: BAR%d = 0x%lx I/O region [PLX9050 bug workaround]\n",
+ device->core.n_dev, 1,
+ (unsigned long)pci_resource_start(dev, 1));
+ }
+out:
+ return err;
+}
+
+static int mkopci_probe_helper(struct pci_dev *dev)
+{
+ int reg = 0, n = 0, err = 0, plx9050bug = 0;
+ struct mkopci_device *device;
+
+ device = (struct mkopci_device *)pci_get_drvdata(dev);
+ if (!device)
+ return -ENODEV;
+
+ if (dev->device == MKO_DEVICE1 || plx9050bug_quirk == 2 ||
+ plx9050bug_quirk == 1) {
+ if (plx9050bug_quirk == 2) {
+ err = mkopci_plx9050workaround(dev,
+ plx9050bug |
+ PLX9050BUG_INJECT);
+ if (err)
+ return err;
+ }
+
+ if (pci_resource_start(dev, 0) & 0x80)
+ plx9050bug = PLX9050BUG_BAR0;
+ if (pci_resource_start(dev, 1) & 0x80)
+ plx9050bug |= PLX9050BUG_BAR1;
+
+ if (plx9050bug && plx9050bug_quirk == 1) {
+ err = mkopci_plx9050workaround(dev, plx9050bug);
+ if (err)
+ return err;
+ }
+ }
+
+ for (; reg < MAX_MEM_WIN + 1; reg++) {
+ if (pci_resource_flags(dev, reg) & IORESOURCE_MEM) {
+ device->core.mem_base[n] = pci_resource_start(dev, reg);
+ if (!device->core.mem_base[n]) {
+ pr_err("mkopci%d: Unable to get BAR%d\n",
+ device->core.n_dev, n);
+ err = -EFAULT;
+ goto fail;
+ }
+
+ device->core.mem_size[n] = pci_resource_len(dev, reg);
+ if (!device->core.mem_size[n]) {
+ pr_err
+ ("mkopci%d: Unable to get size of BAR%d\n",
+ device->core.n_dev, n);
+ err = -EFAULT;
+ goto fail;
+ }
+
+ if ((reg != 0) || !(plx9050bug & PLX9050BUG_BAR0)) {
+ err = pci_request_region(dev, reg, device->core.name);
+ if (err) {
+ pr_err
+ ("mkopci%d: couldn't request region 0x%x with size 0x%x\n",
+ device->core.n_dev,
+ (unsigned int)device->core.mem_base[n],
+ (unsigned int)device->core.mem_size[n]);
+ iounmap((void *)device->core.lin_base[n]);
+ goto fail;
+ }
+ }
+
+ device->core.lin_base[n] =
+ (unsigned long)ioremap_nocache(device->core.
+ mem_base[n],
+ device->core.
+ mem_size[n]);
+ if (!device->core.lin_base[n]) {
+ pr_err
+ ("mkopci%d: Unable to remap BAR%d[0x%lx:0x%lx]\n",
+ device->core.n_dev, n,
+ device->core.mem_base[n],
+ device->core.mem_base[n] +
+ device->core.mem_size[n] - 1);
+ err = -EFAULT;
+ goto fail;
+ }
+ if (v > 0)
+ pr_info
+ ("mkopci%d: BAR%d = 0x%08lx, linear = 0x%08lx, len = 0x%x\n",
+ device->core.n_dev, reg,
+ device->core.mem_base[n],
+ device->core.lin_base[n],
+ device->core.mem_size[n]);
+ n++;
+ } else {
+ if (v > 0)
+ pr_info("mkopci%d: BAR%d = 0x%lx I/O region\n",
+ device->core.n_dev, reg,
+ (unsigned long)pci_resource_start(dev,
+ reg));
+ err = pci_request_region(dev, reg, device->core.name);
+ if (err) {
+ pr_err("mkopci%d: couldn't request region %d\n",
+ device->core.n_dev, reg);
+ goto fail;
+ }
+ }
+ }
+ device->core.mem_windows_nr = n;
+
+ device->core.ltype =
+ (*((unsigned short *)(device->core.lin_base[4]) + 3)) & 0xFF;
+ device->core.irq = dev->irq;
+ if (v > 0)
+ pr_info("mkopci%d: irq = %d\n", device->core.n_dev,
+ device->core.irq);
+ device->core.irq_requested = 0;
+ device->core.c_bar = -1;
+
+ err = mkopci_probe_cdevhelper(dev);
+ if (err) {
+ plx9050bug = 0;
+ goto fail2;
+ }
+ atomic_inc(&devices_nr);
+ goto out;
+
+fail:
+ reg--;
+ n--;
+fail2:
+ for (; reg >= 0; reg--) {
+ if (plx9050bug && reg < 2)
+ break;
+ if (pci_resource_flags(dev, reg) & IORESOURCE_MEM) {
+ iounmap((void *)device->core.lin_base[n]);
+ device->core.lin_base[n--] = 0;
+ }
+ pci_release_region(dev, reg);
+ }
+
+out:
+ return err;
+}
+
+static int mkopci_probe(struct pci_dev *dev, const struct pci_device_id __unused *id)
+{
+ int n_prdev = 0, err = 0;
+ int n;
+ struct mkopci_device *device, *mdev = NULL;
+ struct list_head *it;
+
+ if (atomic_read(&devices_nr) == (1 << 8 * sizeof(unsigned char)) - 1)
+ return -EBUSY;
+
+ err = pci_enable_device(dev);
+ if (err) {
+ pr_err("mkopci: error = %d while enabling PCI device\n", err);
+ return err;
+ }
+
+ device = kmem_cache_zalloc(mkopci_device_cache, GFP_KERNEL);
+ if (!device) {
+ pr_err("mkopci: Unable to allocate memory\n");
+ pci_disable_device(dev);
+ return -ENOMEM;
+ }
+
+ down_write(&devices_sem);
+ if (!list_empty(&devices)) {
+ list_for_each(it, &devices) {
+ mdev = list_entry(it, struct mkopci_device, list);
+ if (n_prdev < mdev->core.n_dev) {
+ if (n_prdev)
+ device->core.n_dev = n_prdev - 1;
+ else
+ device->core.n_dev = n_prdev;
+ list_add(&device->list, mdev->list.prev);
+ break;
+ }
+ n_prdev++;
+ }
+ if (n_prdev >= mdev->core.n_dev) {
+ device->core.n_dev = atomic_read(&devices_nr);
+ list_add_tail(&device->list, &devices);
+ }
+ } else
+ list_add_tail(&device->list, &devices);
+ up_write(&devices_sem);
+
+ pci_set_drvdata(dev, device);
+ device->pci_dev = dev;
+ for (n = 0; n < omited_nr; n++) {
+ if (omited[n] ==
+ mko_pci_addr(dev->bus->number, PCI_SLOT(dev->devfn),
+ PCI_FUNC(dev->devfn), 0)) {
+ pci_disable_device(dev);
+ device->omited = 1;
+ }
+ }
+
+ device->core.vendor_id = dev->vendor;
+ device->core.device_id = dev->device;
+ device->core.subs_vendor_id = dev->subsystem_vendor;
+ device->core.subs_id = dev->subsystem_device;
+ device->core.instance =
+ mko_pci_addr(dev->bus->number, PCI_SLOT(dev->devfn),
+ PCI_FUNC(dev->devfn), 0);
+ sprintf(device->core.name, "mkopci%d", device->core.n_dev);
+
+ init_waitqueue_head(&device->wq);
+ init_rwsem(&device->rwsem);
+
+ down_write(&device->rwsem);
+ if (device->omited)
+ goto out;
+
+ err = mkopci_probe_helper(dev);
+ if (err) {
+ pci_disable_device(dev);
+ list_del(&device->list);
+ kmem_cache_free(mkopci_device_cache, device);
+ }
+
+out:
+ up_write(&device->rwsem);
+ return err;
+}
+
+static void mkopci_remove_helper(struct pci_dev *dev)
+{
+ int reg, n = 0;
+ unsigned char rom_base_reg = dev->rom_base_reg;
+ struct mkopci_device *device =
+ (struct mkopci_device *)pci_get_drvdata(dev);
+
+ rom_base_reg = dev->rom_base_reg / 8;
+ for (reg = 0; reg < DEVICE_COUNT_RESOURCE; reg++) {
+ if (reg == rom_base_reg)
+ continue;
+ if (pci_resource_flags(dev, reg) & IORESOURCE_MEM) {
+ if (v > 1)
+ pr_info("mkopci%d: unmapping BAR%d (0x%lx)\n",
+ device->core.n_dev, reg,
+ device->core.lin_base[n]);
+ iounmap((void *)device->core.lin_base[n]);
+ device->core.lin_base[n++] = 0;
+ }
+
+ pci_release_region(dev, reg);
+ }
+
+ mkopci_free_irq(device);
+ pci_disable_device(dev);
+ device_destroy(mkopci_class,
+ MKDEV(MAJOR(devp), MINOR(devp) + device->core.n_dev));
+ cdev_del(&device->cdev);
+ atomic_dec(&devices_nr);
+}
+
+static void mkopci_remove(struct pci_dev *dev)
+{
+ struct mkopci_device *device =
+ (struct mkopci_device *)pci_get_drvdata(dev);
+
+ down_write(&device->rwsem);
+ if (!device->omited)
+ mkopci_remove_helper(dev);
+ list_del(&device->list);
+ up_write(&device->rwsem);
+ kmem_cache_free(mkopci_device_cache, device);
+ if (v > 0)
+ pr_info("mkopci%d: removed\n", device->core.n_dev);
+}
+
+#ifdef CONFIG_PM
+static int mkopci_suspend(struct device __unused *device)
+{
+ return -ENOSYS;
+}
+
+static int mkopci_resume(struct device __unused *device)
+{
+ return 0;
+}
+#else
+#define mkopci_suspend NULL
+#define mkopci_resume NULL
+#endif
+
+/*********************** End of pci_driver functions **************************/
+
+/*************************** proc entries functions ***************************/
+static struct proc_dir_entry *mkopci_proc_file, *mkopci_proc_dir,
+ *mkopci_proc_core;
+
+static int mkopci_proc_show(struct seq_file *m, void __unused *vp)
+{
+ struct mkopci_device *mko_dev;
+
+ down_read(&devices_sem);
+ if (!atomic_read(&devices_nr))
+ seq_printf(m, "no devices detected\n");
+ else
+ seq_printf(m, "%d device(s) detected\n",
+ atomic_read(&devices_nr));
+
+ seq_printf(m, "plx9050bug_quirk = %d\n", plx9050bug_quirk);
+ seq_printf(m, "verbosity level = %d\n", v);
+
+ list_for_each_entry(mko_dev, &devices, list) {
+ down_read(&mko_dev->rwsem);
+ seq_printf(m,
+ "\ndevice\t\t\t/dev/%s\n" "vendor_id =\t\t0x%04x\n"
+ "device_id =\t\t0x%04x\n"
+ "subs_vendor_id =\t0x%04x\n" "subs_id =\t\t0x%04x\n"
+ "ltype =\t\t\t%d\n" "instance =\t\t%02x:%02x.%x"
+ " (0x%x)\n" "process =\t\t%d\n"
+ "mem_base[0] =\t\t0x%08lx (linear = 0x%lx, size 0x%x)\n"
+ "mem_base[1] =\t\t0x%08lx (linear = 0x%lx, size 0x%x)\n"
+ "mem_base[2] =\t\t0x%08lx (linear = 0x%lx, size 0x%x)\n"
+ "mem_base[3] =\t\t0x%08lx (linear = 0x%lx, size 0x%x)\n"
+ "mem_base[4] =\t\t0x%08lx (linear = 0x%lx, size 0x%x)\n"
+ "irq_n =\t\t\t%d\n" "irq_requested =\t\t%d\n",
+ mko_dev->omited ? "(OMITED)" : mko_dev->core.name,
+ mko_dev->core.vendor_id, mko_dev->core.device_id,
+ mko_dev->core.subs_vendor_id, mko_dev->core.subs_id,
+ mko_dev->core.ltype, mko_dev->core.instance >> 16,
+ mko_dev->core.instance >> 11 & 0x001f,
+ (mko_dev->core.instance >> 8) & 0x7,
+ mko_dev->core.instance, mko_dev->core.process,
+ mko_dev->core.mem_base[0], mko_dev->core.lin_base[0],
+ mko_dev->core.mem_size[0], mko_dev->core.mem_base[1],
+ mko_dev->core.lin_base[1], mko_dev->core.mem_size[1],
+ mko_dev->core.mem_base[2], mko_dev->core.lin_base[2],
+ mko_dev->core.mem_size[2], mko_dev->core.mem_base[3],
+ mko_dev->core.lin_base[3], mko_dev->core.mem_size[3],
+ mko_dev->core.mem_base[4], mko_dev->core.lin_base[4],
+ mko_dev->core.mem_size[4], mko_dev->core.irq,
+ mko_dev->core.irq_requested);
+ up_read(&mko_dev->rwsem);
+ }
+ up_read(&devices_sem);
+
+ return 0;
+}
+
+static int proc_text_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, mkopci_proc_show, inode->i_cdev);
+}
+
+static const struct file_operations mkopci_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = proc_text_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static ssize_t mkopci_proc_core_read(struct file __unused *file, char __user *buffer,
+ size_t count, loff_t *offset)
+{
+ const int MAX_CORE_LEN =
+ MAX_DEVICES_NR * sizeof(struct mkopci_core) +
+ sizeof(unsigned short);
+ int n, len = 0, ret = 0;
+ struct mkopci_device *d;
+
+ down_read(&devices_sem);
+ n = atomic_read(&devices_nr) * sizeof(struct mkopci_core) +
+ sizeof(unsigned short);
+ if (*offset >= n)
+ goto out;
+
+ if (*offset == 0) {
+ if (put_user((unsigned long)atomic_read(&devices_nr), buffer)) {
+ ret = -ERESTARTSYS;
+ goto out;
+ }
+ len = sizeof(unsigned long);
+ }
+
+ list_for_each_entry(d, &devices, list) {
+ down_read(&d->rwsem);
+ if (d->omited) {
+ up_read(&d->rwsem);
+ continue;
+ }
+ if (copy_to_user
+ (buffer + len, &d->core, sizeof(struct mkopci_core))) {
+ up_read(&d->rwsem);
+ ret = -ERESTARTSYS;
+ goto out;
+ }
+ up_read(&d->rwsem);
+ len += sizeof(struct mkopci_core);
+ }
+
+ if (count > len)
+ if (len < MAX_CORE_LEN) {
+ if (clear_user(buffer + len + 1, MAX_CORE_LEN - len)) {
+ ret = -ERESTARTSYS;
+ goto out;
+ }
+ }
+
+ *offset += len;
+ ret = len;
+
+out:
+ up_read(&devices_sem);
+ return ret;
+}
+
+static int hex2int(const char s[])
+{
+ static const char hexalpha[] = "aAbBcCdDeEfF";
+ unsigned int ret = 0;
+ int i = 0, k;
+ int err = 0;
+ int hexint = 0;
+
+ if (s[i] == '0') {
+ i++;
+ if (s[i] == 'x' || s[i] == 'X')
+ i++;
+ }
+
+ while (!err && s[i] != '\0') {
+ ret = ret << 4;
+ if (s[i] >= '0' && s[i] <= '9')
+ ret = ret + (s[i] - '0');
+ else {
+ for (k = 0; hexint == 0 && hexalpha[k] != '\0'; k++) {
+ if (hexalpha[k] == s[i])
+ hexint = 10 + k / 2;
+ }
+ if (hexint == 0) {
+ err = -EINVAL;
+ break;
+ }
+ ret = ret + hexint;
+ hexint = 0;
+ }
+ i++;
+ }
+
+ if (err)
+ ret = err;
+
+ return ret;
+}
+
+static ssize_t mkopci_proc_core_write(struct file __unused *file,
+ const char __user *buffer, size_t count,
+ loff_t __unused *offset)
+{
+ char buf[8];
+ int ret = 0, instance = 0, nodev = 1;
+ struct mkopci_device *d;
+ struct pci_dev *dev;
+
+ if (copy_from_user(buf, buffer, min(sizeof(buf), count))) {
+ ret = -EFAULT;
+ goto out;
+ }
+
+ if (count > sizeof(buf)) {
+ ret = -EINVAL;
+ goto out;
+ } else
+ buf[count - 1] = 0;
+
+ if (!strncmp(buf, "0x", 2) || !strncmp(buf, "0X", 2))
+ instance = hex2int(buf);
+ else if ((strlen(buf) == 7) && (buf[2] == ':') && (buf[5] == '.')) {
+ int a, b, c;
+
+ buf[2] = 0;
+ buf[5] = 0;
+ a = hex2int(buf);
+ b = hex2int(buf + 3);
+ c = hex2int(buf + 6);
+ buf[2] = ':';
+ buf[5] = '.';
+
+ instance = mko_pci_addr(a, b, c, 0);
+ } else
+ instance = -EINVAL;
+
+ if (instance == -EINVAL) {
+ pr_err("mkopci: inappropriate PCI address '%s'\n", buf);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ down_write(&devices_sem);
+ list_for_each_entry(d, &devices, list) {
+ down_write(&d->rwsem);
+ if (d->core.instance != instance) {
+ up_write(&d->rwsem);
+ continue;
+ }
+ nodev = 0;
+ if (d->core.process || d->backdoor) {
+ if (v > 0) {
+ if (d->core.process)
+ pr_info
+ ("mkopci%d: device is handled by process [%d]\n",
+ d->core.n_dev, d->core.process);
+ else
+ pr_info
+ ("mkopci%d: device is handled by process [%d] in backdoor mode\n",
+ d->core.n_dev, d->backdoor);
+ }
+ ret = -EBUSY;
+ goto out_up_sems;
+ }
+ dev = d->pci_dev;
+ if (d->omited) {
+ ret = pci_enable_device(dev);
+ if (ret) {
+ pr_err
+ ("mkopci: error = %d while enabling PCI device\n",
+ ret);
+ goto out_up_sems;
+ }
+
+ ret = mkopci_probe_helper(dev);
+ if (ret) {
+ pr_err
+ ("mkopci: error = %d in mkopci_probe_helper\n",
+ ret);
+ goto out_ph_fail;
+ }
+ d->omited = 0;
+ } else {
+ mkopci_remove_helper(dev);
+ d->omited = 1;
+ }
+ up_write(&d->rwsem);
+ break;
+ }
+ up_write(&devices_sem);
+
+ if (nodev) {
+ pr_info("mkopci: no device with address %s was found\n", buf);
+ ret = -ENODEV;
+ goto out;
+ }
+ ret = min(sizeof(buf), count);
+ goto out;
+
+out_ph_fail:
+ pci_disable_device(dev);
+out_up_sems:
+ up_write(&d->rwsem);
+ up_write(&devices_sem);
+out:
+ return ret;
+}
+
+static const struct file_operations mko_core_proc_fops = {
+ .owner = THIS_MODULE,
+ .read = mkopci_proc_core_read,
+ .write = mkopci_proc_core_write,
+};
+
+static int mkopci_create_proc_entry(void)
+{
+ mkopci_proc_dir = proc_mkdir("mkopci", NULL);
+ if (mkopci_proc_dir == NULL) {
+ remove_proc_entry("mkopci", NULL);
+ pr_err("mkopci: could not initialize /proc/mkopci/\n");
+ return -ENOMEM;
+ }
+
+ mkopci_proc_file =
+ proc_create("devices", S_IFREG | S_IRUGO, mkopci_proc_dir,
+ &mkopci_proc_fops);
+ if (mkopci_proc_file == NULL) {
+ remove_proc_entry("mkopci", NULL);
+ pr_err("mkopci: could not initialize /proc/mkopci/devices\n");
+ return -ENOMEM;
+ }
+ mkopci_proc_core =
+ proc_create("core", S_IFREG | S_IRUGO | S_IWUSR | S_IWGRP,
+ mkopci_proc_dir, &mko_core_proc_fops);
+ if (mkopci_proc_core == NULL) {
+ remove_proc_entry("devices", mkopci_proc_dir);
+ remove_proc_entry("mkopci", NULL);
+ pr_err("mkopci: could not initialize /proc/mkopci/core\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+/************************* End of proc entries functions **********************/
+
+static SIMPLE_DEV_PM_OPS(mkopci_pm_ops, mkopci_suspend, mkopci_resume);
+
+static struct pci_driver mkopci_driver = {
+ .name = "mkopci",
+ .id_table = ids,
+ .probe = mkopci_probe,
+ .remove = mkopci_remove,
+ .driver.pm = &mkopci_pm_ops,
+};
+
+static int __init mkopci_init(void)
+{
+ int ret = 0;
+ struct mkopci_device *d;
+
+ if (v < 0 || v > 3 || omited_nr > MAX_DEVICES_NR) {
+ pr_err("mkopci: inappropriate parameters\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ mkopci_device_cache =
+ kmem_cache_create("mkopci_dev_cache", sizeof(struct mkopci_device),
+ 0, 0, 0);
+
+ if (!mkopci_device_cache) {
+ pr_err("mkopci: Unable to allocate memory\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+ init_rwsem(&devices_sem);
+
+ mkopci_class = class_create(THIS_MODULE, "mkopci");
+ if ((IS_ERR(mkopci_class))) {
+ pr_err("mkopci: error creating class\n");
+ ret = PTR_ERR(mkopci_class);
+ goto destroy_cache;
+ }
+
+ ret = alloc_chrdev_region(&devp, 0, MAX_DEVICES_NR, "mkopci");
+ if (ret) {
+ pr_err("mkopci: failed to allocate chrdev region\n");
+ goto destoy_class;
+ }
+
+ if (v > 0)
+ pr_info("mkopci: major number = %d\n", MAJOR(devp));
+
+ ret = pci_register_driver(&mkopci_driver);
+ if (ret < 0) {
+ pr_err("mkopci: error (%d) while pci_register_driver\n", ret);
+ goto free_chr_reg;
+ }
+
+ ret = mkopci_create_proc_entry();
+ if (ret) {
+ pr_err("mkopci: error creating proc entries\n");
+ goto unreg_drv;
+ }
+ pr_info("mkopci: driver loaded\n");
+ goto out;
+
+unreg_drv:
+ pci_unregister_driver(&mkopci_driver);
+free_chr_reg:
+ unregister_chrdev_region(devp, MAX_DEVICES_NR);
+destoy_class:
+ class_destroy(mkopci_class);
+ while (!list_empty(&devices)) {
+ d = list_entry(devices.next, struct mkopci_device, list);
+ list_del(devices.next);
+ kmem_cache_free(mkopci_device_cache, d);
+ }
+destroy_cache:
+ kmem_cache_destroy(mkopci_device_cache);
+
+out:
+ return ret;
+}
+
+static void __exit mkopci_exit(void)
+{
+ struct mkopci_device *d;
+
+ remove_proc_entry("core", mkopci_proc_dir);
+ remove_proc_entry("devices", mkopci_proc_dir);
+ remove_proc_entry("mkopci", NULL);
+
+ pci_unregister_driver(&mkopci_driver);
+ unregister_chrdev_region(devp, MAX_DEVICES_NR);
+ class_destroy(mkopci_class);
+ while (!list_empty(&devices)) {
+ d = list_entry(devices.next, struct mkopci_device, list);
+ list_del(devices.next);
+ kmem_cache_free(mkopci_device_cache, d);
+ }
+ kmem_cache_destroy(mkopci_device_cache);
+
+ pr_info("mkopci: driver unloaded\n");
+}
+
+MODULE_DESCRIPTION("MKO PCI card driver");
+MODULE_AUTHOR("Sergej Bauer");
+MODULE_LICENSE("GPL");
+
+module_init(mkopci_init);
+module_exit(mkopci_exit);
+
diff --git a/include/misc/mkopci.h b/include/misc/mkopci.h
new file mode 100644
index 0000000..3fa3a4e
--- /dev/null
+++ b/include/misc/mkopci.h
@@ -0,0 +1,81 @@
+#ifndef MKOPCI_H
+#define MKOPCI_H
+
+#define MKO_IOC_MAGIC 'X'
+
+#define MAX_MEM_WIN 5
+#define MAX_DEVICES_NR 16
+
+#if !defined(__KERNEL__)
+#define MaxDeviceCount MAX_DEVICES_NR
+typedef struct {
+ unsigned short VendorId;
+ unsigned short DeviceId;
+ unsigned short SubsystemVendorId;
+ unsigned short SubsystemId;
+ unsigned int InstanceId;
+ unsigned int ProcessId;
+ unsigned int LType;
+ unsigned short NumMemWindows;
+ unsigned long MemBase[MAX_MEM_WIN];
+ unsigned long LinBase[MAX_MEM_WIN];
+ unsigned int MemSize[MAX_MEM_WIN];
+ unsigned short IRQ;
+ int handle; // device file descriptor
+ char name[sizeof("mkopci00") + 1];
+ unsigned char irq_requested;
+ int c_bar;
+ unsigned char n_dev;
+} mkopcilnx_device_info_t;
+
+typedef struct {
+ unsigned short DeviceCount;
+ mkopcilnx_device_info_t DeviceInfo[MaxDeviceCount];
+} mkopcilnx_device_table_t;
+#else // defined(__KERNEL__)
+struct mkopci_core {
+ unsigned short vendor_id;
+ unsigned short device_id;
+ unsigned short subs_vendor_id;
+ unsigned short subs_id;
+ unsigned int instance;
+ unsigned int process;
+ unsigned int ltype;
+ unsigned short mem_windows_nr;
+ unsigned long mem_base[MAX_MEM_WIN];
+ unsigned long lin_base[MAX_MEM_WIN];
+ unsigned int mem_size[MAX_MEM_WIN];
+ unsigned short irq;
+ int handle;
+ char name[sizeof("mkopci00") + 1];
+ unsigned char irq_requested;
+ int c_bar;
+ unsigned char n_dev;
+};
+
+// kernel's structure of the driver
+struct mkopci_device {
+ struct mkopci_core core;
+ struct rw_semaphore rwsem;
+ wait_queue_head_t wq;
+ struct cdev cdev;
+ unsigned char omited;
+ unsigned int backdoor;
+ struct pci_dev *pci_dev;
+ struct list_head list;
+};
+#endif // !defined(_KERNEL__)
+
+#define MKOPCI_IOCTL_GET_VERSION _IOR(MKO_IOC_MAGIC, 1, int)
+#define MKOPCI_IOCTL_GET_BOARDS_COUNT _IOR(MKO_IOC_MAGIC, 2, int)
+#define MKOPCI_IOCTL_GET_DEVICE_TABLE _IOR(MKO_IOC_MAGIC, 3, struct mkopci_core)
+#define MKOPCI_IOCTL_ATTACH_IRQ _IO(MKO_IOC_MAGIC, 4)
+#define MKOPCI_IOCTL_WAIT_IRQ _IO(MKO_IOC_MAGIC, 5)
+#define MKOPCI_IOCTL_DETACH_IRQ _IO(MKO_IOC_MAGIC, 6)
+#define MKOPCI_IOCTL_REQUEST_BAR _IOW(MKO_IOC_MAGIC, 7, int)
+#define MKOPCI_IOCTL_CWPID _IO(MKO_IOC_MAGIC, 8)
+
+#define MKO_IOC_MAXNR 8
+
+#endif
+
---
next reply other threads:[~2015-03-20 12:10 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-03-20 12:10 sergej.bauer [this message]
2015-03-20 12:43 ` [PATCH 1/1] Add mkopci driver Richard Weinberger
2015-03-20 12:44 ` Richard Weinberger
2015-03-21 12:11 ` Paul Bolle
2015-03-21 14:41 ` Sergej Bauer
2015-03-21 15:41 ` Arnd Bergmann
2015-03-21 17:24 ` Sergej Bauer
2015-03-21 17:32 ` Richard Weinberger
2015-03-21 18:29 ` Sergej Bauer
2015-03-26 22:47 ` Greg KH
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=201503201510.26639.sergej.bauer@gmail.com \
--to=sergej.bauer@gmail.com \
--cc=arnd@arndb.de \
--cc=gregkh@linuxfoundation.org \
--cc=linux-kernel@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.