lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <201503211741.11580.sergej.bauer@gmail.com>
Date:	Sat, 21 Mar 2015 17:41:11 +0300
From:	Sergej Bauer <sergej.bauer@...il.com>
To:	Richard Weinberger <richard.weinberger@...il.com>,
	Paul Bolle <pebolle@...cali.nl>
Cc:	linux-kernel@...r.kernel.org, arnd@...db.de,
	gregkh@...uxfoundation.org
Subject: Re: [PATCH 1/1] Add mkopci driver

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@...il.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@...il.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@...il.com>


View attachment "mkopci.patch" of type "text/x-patch" (38795 bytes)

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ