public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/1] Add mkopci driver
@ 2015-03-20 12:10 sergej.bauer
  2015-03-20 12:43 ` Richard Weinberger
  2015-03-26 22:47 ` Greg KH
  0 siblings, 2 replies; 10+ messages in thread
From: sergej.bauer @ 2015-03-20 12:10 UTC (permalink / raw)
  To: linux-kernel; +Cc: arnd, gregkh

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
+
---

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH 1/1] Add mkopci driver
  2015-03-20 12:10 [PATCH 1/1] Add mkopci driver sergej.bauer
@ 2015-03-20 12:43 ` Richard Weinberger
  2015-03-20 12:44   ` Richard Weinberger
                     ` (2 more replies)
  2015-03-26 22:47 ` Greg KH
  1 sibling, 3 replies; 10+ messages in thread
From: Richard Weinberger @ 2015-03-20 12:43 UTC (permalink / raw)
  To: sergej.bauer; +Cc: LKML, Arnd Bergmann, Greg KH

Sergej,

On Fri, Mar 20, 2015 at 1:10 PM,  <sergej.bauer@gmail.com> wrote:
> 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.

>From a quick look I'd suggest to get the driver first in shape.

- You add new proc files, which is not really welcomed. Please consider sysfs.
- In the debug prints you compare against current->comm, also not nice.
- You use printk() in the interrupt handler.
- Why is this a misc driver at all? Does it really not fit into any
other device class?
- I'm sure running checkpatch.pl against the patch would also not hurt. :)

-- 
Thanks,
//richard

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 1/1] Add mkopci driver
  2015-03-20 12:43 ` Richard Weinberger
@ 2015-03-20 12:44   ` Richard Weinberger
  2015-03-21 12:11   ` Paul Bolle
  2015-03-21 14:41   ` Sergej Bauer
  2 siblings, 0 replies; 10+ messages in thread
From: Richard Weinberger @ 2015-03-20 12:44 UTC (permalink / raw)
  To: sergej.bauer; +Cc: LKML, Arnd Bergmann, Greg KH

On Fri, Mar 20, 2015 at 1:43 PM, Richard Weinberger
<richard.weinberger@gmail.com> wrote:
> Sergej,
>
> On Fri, Mar 20, 2015 at 1:10 PM,  <sergej.bauer@gmail.com> wrote:
>> 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.

BTW: Forgot to mention that this sounds like a job for UIO or VFIO.
Did you consider it?

-- 
Thanks,
//richard

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 1/1] Add mkopci driver
  2015-03-20 12:43 ` Richard Weinberger
  2015-03-20 12:44   ` Richard Weinberger
@ 2015-03-21 12:11   ` Paul Bolle
  2015-03-21 14:41   ` Sergej Bauer
  2 siblings, 0 replies; 10+ messages in thread
From: Paul Bolle @ 2015-03-21 12:11 UTC (permalink / raw)
  To: Richard Weinberger; +Cc: sergej.bauer, LKML, Arnd Bergmann, Greg KH

On Fri, 2015-03-20 at 13:43 +0100, Richard Weinberger wrote:
> Sergej,
> 
> On Fri, Mar 20, 2015 at 1:10 PM,  <sergej.bauer@gmail.com> wrote:
> > 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.
> 
> From a quick look I'd suggest to get the driver first in shape.
> 
> - You add new proc files, which is not really welcomed. Please consider sysfs.
> - In the debug prints you compare against current->comm, also not nice.
> - You use printk() in the interrupt handler.
> - Why is this a misc driver at all? Does it really not fit into any
> other device class?
> - I'm sure running checkpatch.pl against the patch would also not hurt. :)
 
And an additional, trivial, change you might also consider is using
    MODULE_LICENSE("GPL v2");

as that would match the license stated in the comment at the top of
drivers/misc/mkopci.c.


Paul Bolle


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 1/1] Add mkopci driver
  2015-03-20 12:43 ` 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
  2 siblings, 1 reply; 10+ messages in thread
From: Sergej Bauer @ 2015-03-21 14:41 UTC (permalink / raw)
  To: Richard Weinberger, Paul Bolle; +Cc: linux-kernel, arnd, gregkh

[-- Attachment #1: Type: Text/Plain, Size: 41058 bytes --]

Richard, thanks for your review.

But I still have several notes about driver:

> - You add new proc files, which is not really welcomed. Please consider sysfs.
That will break a bunch of userspace applications, which use proc-files for several years (as long as from 2006
year)

> - In the debug prints you compare against current->comm, also not nice.
> - You use printk() in the interrupt handler.
That printks was only for driver tests, and does not need anymore, I think.
And you're absolutely right :)

> - Why is this a misc driver at all? Does it really not fit into any
> other device class?
I've don't think I can find an appropriate class. On the one hand, this is industrial bus but it has nothing in common with drivers/iio, or with drives/bus

> - I'm sure running checkpatch.pl against the patch would also not hurt. :)
Yes, but it only hurts with "line over 80 characters" warnings and hurts much :)
And a couple of such warnings still remain...

> BTW: Forgot to mention that this sounds like a job for UIO or VFIO.
And again, you are right. But, again, there a number of applications wich use /proc/mkopci/core

But, of course, there may be decided that the kernel main line - this is not the place for such a driver. :)
If the driver is suitable anyway, patch is at the end of this message

And thanks to Paul Bolle for noticing about license mismatch.

On Friday 20 March 2015 15:43:50 you wrote:
> Sergej,
> 
> On Fri, Mar 20, 2015 at 1:10 PM,  <sergej.bauer@gmail.com> wrote:
> > 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.
> 
> From a quick look I'd suggest to get the driver first in shape.
> 
> - You add new proc files, which is not really welcomed. Please consider sysfs.
> - In the debug prints you compare against current->comm, also not nice.
> - You use printk() in the interrupt handler.
> - Why is this a misc driver at all? Does it really not fit into any
> other device class?
> - I'm sure running checkpatch.pl against the patch would also not hurt. :)
> 
> 

diff --git a/Documentation/misc-devices/mkopci.txt b/Documentation/misc-devices/mkopci.txt
new file mode 100644
index 0000000..499bc29
--- /dev/null
+++ b/Documentation/misc-devices/mkopci.txt
@@ -0,0 +1,44 @@
+    PCI-based MKO bus driver.
+
+
+For dealing with driver without using of root's account it will be helpful
+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.
+
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 006242c..c571451 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -124,6 +124,18 @@ 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
+	  MKOPCI (MB11.xx) device (by RC Module project) provides data transference
+	  through a serial bus bar according to MIL-STD-1553.
+
+	  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..1e2b77a
--- /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++;
+
+	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--;
+
+		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;
+
+	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);
+
+	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;
+
+	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_puts(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 v2");
+
+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..fdc5270
--- /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;
+	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
+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
+
+#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
+
Signed-off-by: Sergej Bauer <sergej.bauer@gmail.com>


[-- Attachment #2: mkopci.patch --]
[-- Type: text/x-patch, Size: 38794 bytes --]

diff --git a/Documentation/misc-devices/mkopci.txt b/Documentation/misc-devices/mkopci.txt
new file mode 100644
index 0000000..499bc29
--- /dev/null
+++ b/Documentation/misc-devices/mkopci.txt
@@ -0,0 +1,44 @@
+    PCI-based MKO bus driver.
+
+
+For dealing with driver without using of root's account it will be helpful
+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.
+
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 006242c..c571451 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -124,6 +124,18 @@ 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
+	  MKOPCI (MB11.xx) device (by RC Module project) provides data transference
+	  through a serial bus bar according to MIL-STD-1553.
+
+	  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..1e2b77a
--- /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++;
+
+	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--;
+
+		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;
+
+	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);
+
+	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;
+
+	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_puts(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 v2");
+
+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..fdc5270
--- /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;
+	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
+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
+
+#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
+
Signed-off-by: Sergej Bauer <sergej.bauer@gmail.com>

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH 1/1] Add mkopci driver
  2015-03-21 14:41   ` Sergej Bauer
@ 2015-03-21 15:41     ` Arnd Bergmann
  2015-03-21 17:24       ` Sergej Bauer
  0 siblings, 1 reply; 10+ messages in thread
From: Arnd Bergmann @ 2015-03-21 15:41 UTC (permalink / raw)
  To: Sergej Bauer; +Cc: Richard Weinberger, Paul Bolle, linux-kernel, gregkh

On Saturday 21 March 2015, Sergej Bauer wrote:
> Richard, thanks for your review.
> 
> But I still have several notes about driver:
> 
> > - You add new proc files, which is not really welcomed. Please consider sysfs.
> That will break a bunch of userspace applications, which use proc-files for several years (as long as from 2006
> year)
> 

> > BTW: Forgot to mention that this sounds like a job for UIO or VFIO.
> And again, you are right. But, again, there a number of applications wich use /proc/mkopci/core
> 
> But, of course, there may be decided that the kernel main line - this is not the place for such a driver. :)
> If the driver is suitable anyway, patch is at the end of this message

I don't think we should merge the driver with the proposed user interface. You can either
create a high-level abstraction for MIL-STD-1553, or use UIO or VFIO to provide a trivial
passthrough. In either case, both the ioctl interface and the procfs interface have no
future, and existing user space programs need to adapt.

There is nothing wrong with adding a driver for this hardware, but I'd rather see it done
properly than having an ad-hoc user space interface that was never reviewed publically
before it got used by applications.

	Arnd

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 1/1] Add mkopci driver
  2015-03-21 15:41     ` Arnd Bergmann
@ 2015-03-21 17:24       ` Sergej Bauer
  2015-03-21 17:32         ` Richard Weinberger
  0 siblings, 1 reply; 10+ messages in thread
From: Sergej Bauer @ 2015-03-21 17:24 UTC (permalink / raw)
  To: Arnd Bergmann; +Cc: Richard Weinberger, Paul Bolle, linux-kernel, gregkh

Ok, I realized uselessness of merging this driver...

And you brought me to a standstill:
> passthrough. In either case, both the ioctl interface and the procfs interface have no future
But what will be after ioctl?


On Saturday 21 March 2015 18:41:42 Arnd Bergmann wrote:
> On Saturday 21 March 2015, Sergej Bauer wrote:
> > Richard, thanks for your review.
> > 
> > But I still have several notes about driver:
> > 
> > > - You add new proc files, which is not really welcomed. Please consider sysfs.
> > That will break a bunch of userspace applications, which use proc-files for several years (as long as from 2006
> > year)
> > 
> 
> > > BTW: Forgot to mention that this sounds like a job for UIO or VFIO.
> > And again, you are right. But, again, there a number of applications wich use /proc/mkopci/core
> > 
> > But, of course, there may be decided that the kernel main line - this is not the place for such a driver. :)
> > If the driver is suitable anyway, patch is at the end of this message
> 
> I don't think we should merge the driver with the proposed user interface. You can either
> create a high-level abstraction for MIL-STD-1553, or use UIO or VFIO to provide a trivial
> passthrough. In either case, both the ioctl interface and the procfs interface have no
> future, and existing user space programs need to adapt.
> 
> There is nothing wrong with adding a driver for this hardware, but I'd rather see it done
> properly than having an ad-hoc user space interface that was never reviewed publically
> before it got used by applications.
> 
> 	Arnd
> 

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 1/1] Add mkopci driver
  2015-03-21 17:24       ` Sergej Bauer
@ 2015-03-21 17:32         ` Richard Weinberger
  2015-03-21 18:29           ` Sergej Bauer
  0 siblings, 1 reply; 10+ messages in thread
From: Richard Weinberger @ 2015-03-21 17:32 UTC (permalink / raw)
  To: Sergej Bauer, Arnd Bergmann; +Cc: Paul Bolle, linux-kernel, gregkh

Am 21.03.2015 um 18:24 schrieb Sergej Bauer:
> Ok, I realized uselessness of merging this driver...

Please don't give up that fast!
See: http://airlied.livejournal.com/#item80112

> And you brought me to a standstill:
>> passthrough. In either case, both the ioctl interface and the procfs interface have no future
> But what will be after ioctl?

We will not drop the ioctl() system call. But having a character device with random
ioctl()s is not really a good driver design.
Today we have much more elegant ways to interact between kernel and userspace than ioctl().
For example sysfs or netlink.

Thanks,
//richard

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 1/1] Add mkopci driver
  2015-03-21 17:32         ` Richard Weinberger
@ 2015-03-21 18:29           ` Sergej Bauer
  0 siblings, 0 replies; 10+ messages in thread
From: Sergej Bauer @ 2015-03-21 18:29 UTC (permalink / raw)
  To: Richard Weinberger; +Cc: Arnd Bergmann, Paul Bolle, linux-kernel, gregkh

Thanks a lot :)

But the driver is really very insignificant.
I was wondering about sysfs before, but consumers will not be very happy to get another revision of userspace tools/library.
So this is their own problems :)

Thanks again and sorry for this uselessness dialogue :)

On Saturday 21 March 2015 20:32:16 Richard Weinberger wrote:
> Am 21.03.2015 um 18:24 schrieb Sergej Bauer:
> > Ok, I realized uselessness of merging this driver...
> 
> Please don't give up that fast!
> See: http://airlied.livejournal.com/#item80112
> 
> > And you brought me to a standstill:
> >> passthrough. In either case, both the ioctl interface and the procfs interface have no future
> > But what will be after ioctl?
> 
> We will not drop the ioctl() system call. But having a character device with random
> ioctl()s is not really a good driver design.
> Today we have much more elegant ways to interact between kernel and userspace than ioctl().
> For example sysfs or netlink.
> 
> Thanks,
> //richard
> 

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 1/1] Add mkopci driver
  2015-03-20 12:10 [PATCH 1/1] Add mkopci driver sergej.bauer
  2015-03-20 12:43 ` Richard Weinberger
@ 2015-03-26 22:47 ` Greg KH
  1 sibling, 0 replies; 10+ messages in thread
From: Greg KH @ 2015-03-26 22:47 UTC (permalink / raw)
  To: sergej.bauer; +Cc: linux-kernel, arnd

On Fri, Mar 20, 2015 at 03:10:26PM +0300, sergej.bauer@gmail.com wrote:
> 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.

Why can't this be a UIO driver?  Isn't that what the UIO interface
already does?

> 
> 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

Please use the built-in dynamic debugging levels instead of custom ones
like this.

> +
> +- '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

As others have said, proc is not for devices, we can't take new drivers
that use this, sorry.

> +
> 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 

Trailing whitespace :(

> +	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

Why is this needed?

> +
> +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

Are you sure you need this?  We have data types to handle this
already...

> +
> +unsigned char drv_version = 0x11;

Global variables?

> +#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

Why is this needed?

> +#define __unused __attribute__ ((unused))

We already have this.

> +
> +/***************************** 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;

You can't directly access memory like this safely, please use the proper
io memory functions instead, otherwise bad things are guaranteed to
happen.

Hint, volatile doesn't mean what you think it does here...

> +
> +	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));

Same here, don't use this.

> +
> +	if (*LPCI_4C & 0x24) {
> +		*LPCI_4C &= ~0x40;

That breaks on lots of architectures :(

> +		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;

That's racy, and not needed, and should never be in any kernel code
ever.  Please remove.

> +
> +	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;

This is just funny :)

It also doesn't handle pid namespaces at all :(

> +		dev->core.process = 0;
> +	}
> +
> +	filp->private_data = dev;
> +
> +	if (strcmp(current->comm, "TstOpenClose") && v > 1)

This also is pretty funny, please never do this.

> +		pr_info("mkopci%d: device opened in %s mode (%s)\n",
> +			dev->core.n_dev, dev->backdoor ? "backdoor" : "regular",
> +			current->comm);

drivers should always use dev_info() and friends, not pr_info().

> +	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)

Also funny :)

> +		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));
> +	}

We never need to do this type of checking anymore, it's useless now.
This must have been written originally for a very old kernel version.

> +
> +	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;

Again, broken with pid namespaces :(

> +		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);

version can go lots of other places, it doesn't have to be an ioctl.

> +		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);

You can read this today from sysfs, no need for an ioctl.

> +		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;

Don't use put_user() if at all possible.  Use copy_to_user() please.

> +		}
> +		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;

Wrong error value.

> +				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);

Not a good idea.  Look at how UIO does this instead.

> +		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;
> +}

Again, UIO does this already for you, please move to that framework
instead.  I can't take a duplicate one into the kernel tree.

Especially one with these fun security holes :)

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2015-03-26 22:47 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-03-20 12:10 [PATCH 1/1] Add mkopci driver sergej.bauer
2015-03-20 12:43 ` 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

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox