#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/dma-mapping.h>
#include <linux/pci.h>
#include <linux/pnp.h>
#include <linux/platform_device.h>
#include <linux/sysctl.h>
#include <linux/io.h>
#include <linux/uaccess.h>

#include <asm/dma.h>

#include <linux/parport.h>
#include <linux/parport_pc.h>

//#include "type.h"
#define TRUE    1
#define FALSE   0

#include "parport_rdc_A9610.h"

// DrvDebug
//
#define DBGPRINTF
#ifdef DBGPRINTF
    #define DrvDbgPrint(DebugPrintLevel, format, arg...) printk(KERN_INFO format, ## arg)
    #define DrvDbgPrintIf(bShow, format, arg...) if (bShow) { printk(KERN_INFO format, ## arg); }
#else
    #define DrvDbgPrint(DebugPrintLevel, format, arg...)
#endif

#ifdef DEBUG
#define DPRINTK  printk
#else
#define DPRINTK(stuff...)
#endif

// config parport module
//
#define CONFIG_PARPORT_PC_FIFO
#define CONFIG_PARPORT_1284

/* ECR modes */
#define ECR_SPP 00
#define ECR_PS2 01
#define ECR_PPF 02
#define ECR_ECP 03
#define ECR_EPP 04
#define ECR_VND 05
#define ECR_TST 06
#define ECR_CNF 07
#define ECR_MODE_MASK 0xe0
#define ECR_WRITE(p,v) frob_econtrol((p),0xff,(v))

#if defined(CONFIG_PARPORT_PC_SUPERIO) || (defined(CONFIG_PARPORT_1284) && defined(CONFIG_PARPORT_PC_FIFO))
static int verbose_probing;
#endif

static int user_specified;
//static int pci_registered_parport;
//static int pnp_registered_parport;

/* frob_control, but for ECR */
static void frob_econtrol (struct parport *pb, unsigned char m,
               unsigned char v)
{
    unsigned char ectr = 0;

    if (m != 0xff)
        ectr = inb (ECONTROL (pb));

    DPRINTK (KERN_DEBUG "frob_econtrol(%02x,%02x): %02x -> %02x\n",
        m, v, ectr, (ectr & ~m) ^ v);

    outb ((ectr & ~m) ^ v, ECONTROL (pb));
}

static inline void frob_set_mode(struct parport *p, int mode)
{
    frob_econtrol (p, ECR_MODE_MASK, mode << 5);
}

#ifdef CONFIG_PARPORT_PC_FIFO
/* Safely change the mode bits in the ECR 
   Returns:
        0    : Success
       -EBUSY: Could not drain FIFO in some finite amount of time,
           mode not changed!
 */
static int change_mode(struct parport *p, int m)
{
    const struct parport_pc_private *priv = p->physport->private_data;
    unsigned char oecr;
    int mode;

    DPRINTK(KERN_INFO "parport change_mode ECP-ISA to mode 0x%02x\n",m);

    if (!priv->ecr) {
        printk (KERN_DEBUG "change_mode: but there's no ECR!\n");
        return 0;
    }

    /* Bits <7:5> contain the mode. */
    oecr = inb (ECONTROL (p));
    mode = (oecr >> 5) & 0x7;
	if (mode == m)
		return 0;

    if (mode >= 2 && !(priv->ctr & 0x20)) {
        /* This mode resets the FIFO, so we may
         * have to wait for it to drain first. */
        unsigned long expire = jiffies + p->physport->cad->timeout;
        int counter;
        switch (mode) {
        case ECR_PPF: /* Parallel Port FIFO mode */
        case ECR_ECP: /* ECP Parallel Port mode */
            /* Busy wait for 200us */
            for (counter = 0; counter < 40; counter++) {
                if (inb (ECONTROL (p)) & 0x01)
                    break;
				if (signal_pending(current))
					break;
                udelay (5);
            }

            /* Poll slowly. */
            while (!(inb (ECONTROL (p)) & 0x01)) {
                if (time_after_eq (jiffies, expire))
                    /* The FIFO is stuck. */
                    return -EBUSY;
				schedule_timeout_interruptible(
							msecs_to_jiffies(10));
                if (signal_pending (current))
                    break;
            }
        }
    }

    if (mode >= 2 && m >= 2) {
        /* We have to go through mode 001 */
        oecr &= ~(7 << 5);
        oecr |= ECR_PS2 << 5;
        ECR_WRITE (p, oecr);
    }

    /* Set the mode. */
    oecr &= ~(7 << 5);
    oecr |= m << 5;
    ECR_WRITE (p, oecr);
    return 0;
}
#endif /* FIFO support */

/*
 * Clear TIMEOUT BIT in EPP MODE
 *
 * This is also used in SPP detection.
 */
static int clear_epp_timeout(struct parport *pb)
{
    unsigned char r;

    if (!(parport_pc_read_status(pb) & 0x01))
        return 1;

    /* To clear timeout some chips require double read */
    parport_pc_read_status(pb);
    r = parport_pc_read_status(pb);
    outb (r | 0x01, STATUS (pb)); /* Some reset by writing 1 */
    outb (r & 0xfe, STATUS (pb)); /* Others by writing 0 */
    r = parport_pc_read_status(pb);

    return !(r & 0x01);
}

/*
 * Access functions.
 *
 * Most of these aren't static because they may be used by the
 * parport_xxx_yyy macros.  extern __inline__ versions of several
 * of these are in parport_pc.h.
 */

static void parport_pc_init_state(struct pardevice *dev,
						struct parport_state *s)
{
    s->u.pc.ctr = 0xc;
    if (dev->irq_func &&
        dev->port->irq != PARPORT_IRQ_NONE)
        /* Set ackIntEn */
        s->u.pc.ctr |= 0x10;

    s->u.pc.ecr = 0x34; /* NetMos chip can cause problems 0x24;
                 * D.Gruszka VScom */
}

static void parport_pc_save_state(struct parport *p, struct parport_state *s)
{
    const struct parport_pc_private *priv = p->physport->private_data;
    s->u.pc.ctr = priv->ctr;
    if (priv->ecr)
        s->u.pc.ecr = inb (ECONTROL (p));
}

static void parport_pc_restore_state(struct parport *p,
						struct parport_state *s)
{
    struct parport_pc_private *priv = p->physport->private_data;
    register unsigned char c = s->u.pc.ctr & priv->ctr_writable;
    outb (c, CONTROL (p));
    priv->ctr = c;
    if (priv->ecr)
        ECR_WRITE (p, s->u.pc.ecr);
}

#ifdef CONFIG_PARPORT_1284
static size_t parport_pc_epp_read_data (struct parport *port, void *buf,
                    size_t length, int flags)
{
    size_t got = 0;

    if (flags & PARPORT_W91284PIC) {
        unsigned char status;
        size_t left = length;

        /* use knowledge about data lines..:
         *  nFault is 0 if there is at least 1 byte in the Warp's FIFO
         *  pError is 1 if there are 16 bytes in the Warp's FIFO
         */
        status = inb (STATUS (port));

		while (!(status & 0x08) && got < length) {
			if (left >= 16 && (status & 0x20) && !(status & 0x08)) {
				/* can grab 16 bytes from warp fifo */
				if (!((long)buf & 0x03))
					insl(EPPDATA(port), buf, 4);
				else
					insb(EPPDATA(port), buf, 16);
                buf += 16;
                got += 16;
                left -= 16;
            } else {
                /* grab single byte from the warp fifo */
                *((char *)buf) = inb (EPPDATA (port));
                buf++;
                got++;
                left--;
            }
            status = inb (STATUS (port));
			if (status & 0x01) {
				/* EPP timeout should never occur... */
				printk(KERN_DEBUG
"%s: EPP timeout occurred while talking to w91284pic (should not have done)\n", port->name);
                clear_epp_timeout (port);
            }
        }
        return got;
    }
    if ((flags & PARPORT_EPP_FAST) && (length > 1)) {
		if (!(((long)buf | length) & 0x03))
			insl(EPPDATA(port), buf, (length >> 2));
		else
			insb(EPPDATA(port), buf, length);
        if (inb (STATUS (port)) & 0x01) {
            clear_epp_timeout (port);
            return -EIO;
        }
        return length;
    }
    for (; got < length; got++) {
        *((char*)buf) = inb (EPPDATA(port));
        buf++;
        if (inb (STATUS (port)) & 0x01) {
            /* EPP timeout */
            clear_epp_timeout (port);
            break;
        }
    }

    return got;
}

static size_t parport_pc_epp_write_data (struct parport *port, const void *buf,
                     size_t length, int flags)
{
    size_t written = 0;

    if ((flags & PARPORT_EPP_FAST) && (length > 1)) {
		if (!(((long)buf | length) & 0x03))
			outsl(EPPDATA(port), buf, (length >> 2));
		else
			outsb(EPPDATA(port), buf, length);
        if (inb (STATUS (port)) & 0x01) {
            clear_epp_timeout (port);
            return -EIO;
        }
        return length;
    }
    for (; written < length; written++) {
        outb (*((char*)buf), EPPDATA(port));
        buf++;
        if (inb (STATUS(port)) & 0x01) {
            clear_epp_timeout (port);
            break;
        }
    }

    return written;
}

static size_t parport_pc_epp_read_addr (struct parport *port, void *buf,
                    size_t length, int flags)
{
    size_t got = 0;

    if ((flags & PARPORT_EPP_FAST) && (length > 1)) {
        insb (EPPADDR (port), buf, length);
        if (inb (STATUS (port)) & 0x01) {
            clear_epp_timeout (port);
            return -EIO;
        }
        return length;
    }
    for (; got < length; got++) {
        *((char*)buf) = inb (EPPADDR (port));
        buf++;
        if (inb (STATUS (port)) & 0x01) {
            clear_epp_timeout (port);
            break;
        }
    }

    return got;
}

static size_t parport_pc_epp_write_addr (struct parport *port,
                     const void *buf, size_t length,
                     int flags)
{
    size_t written = 0;

    if ((flags & PARPORT_EPP_FAST) && (length > 1)) {
        outsb (EPPADDR (port), buf, length);
        if (inb (STATUS (port)) & 0x01) {
            clear_epp_timeout (port);
            return -EIO;
        }
        return length;
    }
    for (; written < length; written++) {
        outb (*((char*)buf), EPPADDR (port));
        buf++;
        if (inb (STATUS (port)) & 0x01) {
            clear_epp_timeout (port);
            break;
        }
    }

    return written;
}

static size_t parport_pc_ecpepp_read_data (struct parport *port, void *buf,
                       size_t length, int flags)
{
    size_t got;

    frob_set_mode (port, ECR_EPP);
    parport_pc_data_reverse (port);
    parport_pc_write_control (port, 0x4);
    got = parport_pc_epp_read_data (port, buf, length, flags);
    frob_set_mode (port, ECR_PS2);

    return got;
}

static size_t parport_pc_ecpepp_write_data (struct parport *port,
                        const void *buf, size_t length,
                        int flags)
{
    size_t written;

    frob_set_mode (port, ECR_EPP);
    parport_pc_write_control (port, 0x4);
    parport_pc_data_forward (port);
    written = parport_pc_epp_write_data (port, buf, length, flags);
    frob_set_mode (port, ECR_PS2);

    return written;
}

static size_t parport_pc_ecpepp_read_addr (struct parport *port, void *buf,
                       size_t length, int flags)
{
    size_t got;

    frob_set_mode (port, ECR_EPP);
    parport_pc_data_reverse (port);
    parport_pc_write_control (port, 0x4);
    got = parport_pc_epp_read_addr (port, buf, length, flags);
    frob_set_mode (port, ECR_PS2);

    return got;
}

static size_t parport_pc_ecpepp_write_addr (struct parport *port,
                        const void *buf, size_t length,
                        int flags)
{
    size_t written;

    frob_set_mode (port, ECR_EPP);
    parport_pc_write_control (port, 0x4);
    parport_pc_data_forward (port);
    written = parport_pc_epp_write_addr (port, buf, length, flags);
    frob_set_mode (port, ECR_PS2);

    return written;
}
#endif /* IEEE 1284 support */

#ifdef CONFIG_PARPORT_PC_FIFO
static size_t parport_pc_fifo_write_block_pio (struct parport *port,
                           const void *buf, size_t length)
{
    int ret = 0;
    const unsigned char *bufp = buf;
    size_t left = length;
    unsigned long expire = jiffies + port->physport->cad->timeout;
    const int fifo = FIFO (port);
    int poll_for = 8; /* 80 usecs */
    const struct parport_pc_private *priv = port->physport->private_data;
    const int fifo_depth = priv->fifo_depth;

    port = port->physport;

    /* We don't want to be interrupted every character. */
    parport_pc_disable_irq (port);
    /* set nErrIntrEn and serviceIntr */
    frob_econtrol (port, (1<<4) | (1<<2), (1<<4) | (1<<2));

    /* Forward mode. */
    parport_pc_data_forward (port); /* Must be in PS2 mode */

    while (left) {
        unsigned char byte;
        unsigned char ecrval = inb (ECONTROL (port));
        int i = 0;

        if (need_resched() && time_before (jiffies, expire))
            /* Can't yield the port. */
            schedule ();

        /* Anyone else waiting for the port? */
        if (port->waithead) {
            printk (KERN_DEBUG "Somebody wants the port\n");
            break;
        }

        if (ecrval & 0x02) {
            /* FIFO is full. Wait for interrupt. */

            /* Clear serviceIntr */
            ECR_WRITE (port, ecrval & ~(1<<2));
        false_alarm:
            ret = parport_wait_event (port, HZ);
			if (ret < 0)
				break;
            ret = 0;
            if (!time_before (jiffies, expire)) {
                /* Timed out. */
                printk (KERN_DEBUG "FIFO write timed out\n");
                break;
            }
            ecrval = inb (ECONTROL (port));
            if (!(ecrval & (1<<2))) {
                if (need_resched() &&
                    time_before (jiffies, expire))
                    schedule ();

                goto false_alarm;
            }

            continue;
        }

        /* Can't fail now. */
        expire = jiffies + port->cad->timeout;

    poll:
        if (signal_pending (current))
            break;

        if (ecrval & 0x01) {
            /* FIFO is empty. Blast it full. */
            const int n = left < fifo_depth ? left : fifo_depth;
            outsb (fifo, bufp, n);
            bufp += n;
            left -= n;

            /* Adjust the poll time. */
			if (i < (poll_for - 2))
				poll_for--;
            continue;
        } else if (i++ < poll_for) {
            udelay (10);
            ecrval = inb (ECONTROL (port));
            goto poll;
        }

        /* Half-full (call me an optimist) */
        byte = *bufp++;
        outb (byte, fifo);
        left--;
        }
	dump_parport_state("leave fifo_write_block_pio", port);
    return length - left;
}

#ifdef HAS_DMA
static size_t parport_pc_fifo_write_block_dma (struct parport *port,
                           const void *buf, size_t length)
{
    int ret = 0;
    unsigned long dmaflag;
    size_t left = length;
    const struct parport_pc_private *priv = port->physport->private_data;
	struct device *dev = port->physport->dev;
    dma_addr_t dma_addr, dma_handle;
    size_t maxlen = 0x10000; /* max 64k per DMA transfer */
    unsigned long start = (unsigned long) buf;
    unsigned long end = (unsigned long) buf + length - 1;

dump_parport_state ("enter fifo_write_block_dma", port);
    if (end < MAX_DMA_ADDRESS) {
        /* If it would cross a 64k boundary, cap it at the end. */
        if ((start ^ end) & ~0xffffUL)
            maxlen = 0x10000 - (start & 0xffff);

		dma_addr = dma_handle = dma_map_single(dev, (void *)buf, length,
						       DMA_TO_DEVICE);
        } else {
		/* above 16 MB we use a bounce buffer as ISA-DMA
		   is not possible */
        maxlen   = PAGE_SIZE;          /* sizeof(priv->dma_buf) */
        dma_addr = priv->dma_handle;
        dma_handle = 0;
    }

    port = port->physport;

    /* We don't want to be interrupted every character. */
    parport_pc_disable_irq (port);
    /* set nErrIntrEn and serviceIntr */
    frob_econtrol (port, (1<<4) | (1<<2), (1<<4) | (1<<2));

    /* Forward mode. */
    parport_pc_data_forward (port); /* Must be in PS2 mode */

    while (left) {
        unsigned long expire = jiffies + port->physport->cad->timeout;

        size_t count = left;

        if (count > maxlen)
            count = maxlen;

        if (!dma_handle)   /* bounce buffer ! */
            memcpy(priv->dma_buf, buf, count);

        dmaflag = claim_dma_lock();
        disable_dma(port->dma);
        clear_dma_ff(port->dma);
        set_dma_mode(port->dma, DMA_MODE_WRITE);
        set_dma_addr(port->dma, dma_addr);
        set_dma_count(port->dma, count);

        /* Set DMA mode */
        frob_econtrol (port, 1<<3, 1<<3);

        /* Clear serviceIntr */
        frob_econtrol (port, 1<<2, 0);

        enable_dma(port->dma);
        release_dma_lock(dmaflag);

        /* assume DMA will be successful */
        left -= count;
        buf  += count;
		if (dma_handle)
			dma_addr += count;

        /* Wait for interrupt. */
    false_alarm:
        ret = parport_wait_event (port, HZ);
		if (ret < 0)
			break;
        ret = 0;
        if (!time_before (jiffies, expire)) {
            /* Timed out. */
            printk (KERN_DEBUG "DMA write timed out\n");
            break;
        }
        /* Is serviceIntr set? */
        if (!(inb (ECONTROL (port)) & (1<<2))) {
            cond_resched();

            goto false_alarm;
        }

        dmaflag = claim_dma_lock();
        disable_dma(port->dma);
        clear_dma_ff(port->dma);
        count = get_dma_residue(port->dma);
        release_dma_lock(dmaflag);

        cond_resched(); /* Can't yield the port. */

        /* Anyone else waiting for the port? */
        if (port->waithead) {
            printk (KERN_DEBUG "Somebody wants the port\n");
            break;
        }

        /* update for possible DMA residue ! */
        buf  -= count;
        left += count;
		if (dma_handle)
			dma_addr -= count;
    }

    /* Maybe got here through break, so adjust for DMA residue! */
    dmaflag = claim_dma_lock();
    disable_dma(port->dma);
    clear_dma_ff(port->dma);
    left += get_dma_residue(port->dma);
    release_dma_lock(dmaflag);

    /* Turn off DMA mode */
    frob_econtrol (port, 1<<3, 0);
    
	if (dma_handle)
		dma_unmap_single(dev, dma_handle, length, DMA_TO_DEVICE);

dump_parport_state ("leave fifo_write_block_dma", port);
    return length - left;
}
#endif

static inline size_t parport_pc_fifo_write_block(struct parport *port,
                           const void *buf, size_t length)
{
#ifdef HAS_DMA
    if (port->dma != PARPORT_DMA_NONE)
        return parport_pc_fifo_write_block_dma (port, buf, length);
#endif
    return parport_pc_fifo_write_block_pio (port, buf, length);
}

/* Parallel Port FIFO mode (ECP chipsets) */
static size_t parport_pc_compat_write_block_pio (struct parport *port,
                         const void *buf, size_t length,
                         int flags)
{
    size_t written;
    int r;
    unsigned long expire;
    const struct parport_pc_private *priv = port->physport->private_data;

    /* Special case: a timeout of zero means we cannot call schedule().
     * Also if O_NONBLOCK is set then use the default implementation. */
    if (port->physport->cad->timeout <= PARPORT_INACTIVITY_O_NONBLOCK)
        return parport_ieee1284_write_compat (port, buf,
                              length, flags);

    /* Set up parallel port FIFO mode.*/
    parport_pc_data_forward (port); /* Must be in PS2 mode */
    parport_pc_frob_control (port, PARPORT_CONTROL_STROBE, 0);
    r = change_mode (port, ECR_PPF); /* Parallel port FIFO */
	if (r)
		printk(KERN_DEBUG "%s: Warning change_mode ECR_PPF failed\n",
								port->name);

    port->physport->ieee1284.phase = IEEE1284_PH_FWD_DATA;

    /* Write the data to the FIFO. */
    written = parport_pc_fifo_write_block(port, buf, length);

    /* Finish up. */
    /* For some hardware we don't want to touch the mode until
     * the FIFO is empty, so allow 4 seconds for each position
     * in the fifo.
     */
        expire = jiffies + (priv->fifo_depth * HZ * 4);
    do {
        /* Wait for the FIFO to empty */
        r = change_mode (port, ECR_PS2);
		if (r != -EBUSY)
			break;
    } while (time_before (jiffies, expire));
    if (r == -EBUSY) {

        printk (KERN_DEBUG "%s: FIFO is stuck\n", port->name);

        /* Prevent further data transfer. */
        frob_set_mode (port, ECR_TST);

        /* Adjust for the contents of the FIFO. */
        for (written -= priv->fifo_depth; ; written++) {
            if (inb (ECONTROL (port)) & 0x2) {
                /* Full up. */
                break;
            }
            outb (0, FIFO (port));
        }

        /* Reset the FIFO and return to PS2 mode. */
        frob_set_mode (port, ECR_PS2);
    }

    r = parport_wait_peripheral (port,
                     PARPORT_STATUS_BUSY,
                     PARPORT_STATUS_BUSY);
    if (r)
        printk (KERN_DEBUG
            "%s: BUSY timeout (%d) in compat_write_block_pio\n", 
            port->name, r);

    port->physport->ieee1284.phase = IEEE1284_PH_FWD_IDLE;

    return written;
}

/* ECP */
#ifdef CONFIG_PARPORT_1284
static size_t parport_pc_ecp_write_block_pio (struct parport *port,
                          const void *buf, size_t length,
                          int flags)
{
    size_t written;
    int r;
    unsigned long expire;
    const struct parport_pc_private *priv = port->physport->private_data;

    /* Special case: a timeout of zero means we cannot call schedule().
     * Also if O_NONBLOCK is set then use the default implementation. */
    if (port->physport->cad->timeout <= PARPORT_INACTIVITY_O_NONBLOCK)
        return parport_ieee1284_ecp_write_data (port, buf,
                            length, flags);

    /* Switch to forward mode if necessary. */
    if (port->physport->ieee1284.phase != IEEE1284_PH_FWD_IDLE) {
        /* Event 47: Set nInit high. */
        parport_frob_control (port,
                      PARPORT_CONTROL_INIT
                      | PARPORT_CONTROL_AUTOFD,
                      PARPORT_CONTROL_INIT
                      | PARPORT_CONTROL_AUTOFD);

        /* Event 49: PError goes high. */
        r = parport_wait_peripheral (port,
                         PARPORT_STATUS_PAPEROUT,
                         PARPORT_STATUS_PAPEROUT);
        if (r) {
            printk (KERN_DEBUG "%s: PError timeout (%d) "
                "in ecp_write_block_pio\n", port->name, r);
        }
    }

    /* Set up ECP parallel port mode.*/
    parport_pc_data_forward (port); /* Must be in PS2 mode */
    parport_pc_frob_control (port,
                 PARPORT_CONTROL_STROBE |
                 PARPORT_CONTROL_AUTOFD,
                 0);
    r = change_mode (port, ECR_ECP); /* ECP FIFO */
	if (r)
		printk(KERN_DEBUG "%s: Warning change_mode ECR_ECP failed\n",
								port->name);
    port->physport->ieee1284.phase = IEEE1284_PH_FWD_DATA;

    /* Write the data to the FIFO. */
    written = parport_pc_fifo_write_block(port, buf, length);

    /* Finish up. */
    /* For some hardware we don't want to touch the mode until
     * the FIFO is empty, so allow 4 seconds for each position
     * in the fifo.
     */
    expire = jiffies + (priv->fifo_depth * (HZ * 4));
    do {
        /* Wait for the FIFO to empty */
        r = change_mode (port, ECR_PS2);
		if (r != -EBUSY)
			break;
    } while (time_before (jiffies, expire));
    if (r == -EBUSY) {

        printk (KERN_DEBUG "%s: FIFO is stuck\n", port->name);

        /* Prevent further data transfer. */
        frob_set_mode (port, ECR_TST);

        /* Adjust for the contents of the FIFO. */
        for (written -= priv->fifo_depth; ; written++) {
            if (inb (ECONTROL (port)) & 0x2) {
                /* Full up. */
                break;
            }
            outb (0, FIFO (port));
        }

        /* Reset the FIFO and return to PS2 mode. */
        frob_set_mode (port, ECR_PS2);

        /* Host transfer recovery. */
        parport_pc_data_reverse (port); /* Must be in PS2 mode */
        udelay (5);
        parport_frob_control (port, PARPORT_CONTROL_INIT, 0);
        r = parport_wait_peripheral (port, PARPORT_STATUS_PAPEROUT, 0);
        if (r)
            printk (KERN_DEBUG "%s: PE,1 timeout (%d) "
                "in ecp_write_block_pio\n", port->name, r);

        parport_frob_control (port,
                      PARPORT_CONTROL_INIT,
                      PARPORT_CONTROL_INIT);
        r = parport_wait_peripheral (port,
                         PARPORT_STATUS_PAPEROUT,
                         PARPORT_STATUS_PAPEROUT);
                if (r)
                        printk (KERN_DEBUG "%s: PE,2 timeout (%d) "
                "in ecp_write_block_pio\n", port->name, r);
    }

    r = parport_wait_peripheral (port,
                     PARPORT_STATUS_BUSY, 
                     PARPORT_STATUS_BUSY);
    if(r)
        printk (KERN_DEBUG
            "%s: BUSY timeout (%d) in ecp_write_block_pio\n",
            port->name, r);

    port->physport->ieee1284.phase = IEEE1284_PH_FWD_IDLE;

    return written;
}
#endif /* IEEE 1284 support */
#endif /* Allowed to use FIFO/DMA */


/*
 *    ******************************************
 *    INITIALISATION AND MODULE STUFF BELOW HERE
 *    ******************************************
 */

/* GCC is not inlining extern inline function later overwriten to non-inline,
   so we use outlined_ variants here.  */
static const struct parport_operations parport_pc_ops = {
    .write_data    = parport_pc_write_data,
    .read_data    = parport_pc_read_data,

    .write_control    = parport_pc_write_control,
    .read_control    = parport_pc_read_control,
    .frob_control    = parport_pc_frob_control,

    .read_status    = parport_pc_read_status,

    .enable_irq    = parport_pc_enable_irq,
    .disable_irq    = parport_pc_disable_irq,

    .data_forward    = parport_pc_data_forward,
    .data_reverse    = parport_pc_data_reverse,

    .init_state    = parport_pc_init_state,
    .save_state    = parport_pc_save_state,
    .restore_state    = parport_pc_restore_state,

    .epp_write_data    = parport_ieee1284_epp_write_data,
    .epp_read_data    = parport_ieee1284_epp_read_data,
    .epp_write_addr    = parport_ieee1284_epp_write_addr,
    .epp_read_addr    = parport_ieee1284_epp_read_addr,

    .ecp_write_data    = parport_ieee1284_ecp_write_data,
    .ecp_read_data    = parport_ieee1284_ecp_read_data,
    .ecp_write_addr    = parport_ieee1284_ecp_write_addr,

    .compat_write_data    = parport_ieee1284_write_compat,
    .nibble_read_data    = parport_ieee1284_read_nibble,
    .byte_read_data        = parport_ieee1284_read_byte,

    .owner        = THIS_MODULE,
};


/* --- Mode detection ------------------------------------- */

/*
 * Checks for port existence, all ports support SPP MODE
 * Returns: 
 *         0           :  No parallel port at this address
 *  PARPORT_MODE_PCSPP :  SPP port detected 
 *                        (if the user specified an ioport himself,
 *                         this shall always be the case!)
 *
 */
static int parport_SPP_supported(struct parport *pb)
{
    unsigned char r, w;

    /*
     * first clear an eventually pending EPP timeout 
     * I (sailer@ife.ee.ethz.ch) have an SMSC chipset
     * that does not even respond to SPP cycles if an EPP
     * timeout is pending
     */
    clear_epp_timeout(pb);

    /* Do a simple read-write test to make sure the port exists. */
    w = 0xc;
    outb (w, CONTROL (pb));

    /* Is there a control register that we can read from?  Some
     * ports don't allow reads, so read_control just returns a
     * software copy. Some ports _do_ allow reads, so bypass the
     * software copy here.  In addition, some bits aren't
     * writable. */
    r = inb (CONTROL (pb));
    if ((r & 0xf) == w) {
        w = 0xe;
        outb (w, CONTROL (pb));
        r = inb (CONTROL (pb));
        outb (0xc, CONTROL (pb));
        if ((r & 0xf) == w)
            return PARPORT_MODE_PCSPP;
    }

    if (user_specified)
        /* That didn't work, but the user thinks there's a
         * port here. */
        printk (KERN_INFO "parport 0x%lx (WARNING): CTR: "
            "wrote 0x%02x, read 0x%02x\n", pb->base, w, r);

    /* Try the data register.  The data lines aren't tri-stated at
     * this stage, so we expect back what we wrote. */
    w = 0xaa;
    parport_pc_write_data (pb, w);
    r = parport_pc_read_data (pb);
    if (r == w) {
        w = 0x55;
        parport_pc_write_data (pb, w);
        r = parport_pc_read_data (pb);
        if (r == w)
            return PARPORT_MODE_PCSPP;
    }

    if (user_specified) {
        /* Didn't work, but the user is convinced this is the
         * place. */
        printk (KERN_INFO "parport 0x%lx (WARNING): DATA: "
            "wrote 0x%02x, read 0x%02x\n", pb->base, w, r);
        printk (KERN_INFO "parport 0x%lx: You gave this address, "
            "but there is probably no parallel port there!\n",
            pb->base);
    }

    /* It's possible that we can't read the control register or
     * the data register.  In that case just believe the user. */
    if (user_specified)
        return PARPORT_MODE_PCSPP;

    return 0;
}

/* Check for ECR
 *
 * Old style XT ports alias io ports every 0x400, hence accessing ECR
 * on these cards actually accesses the CTR.
 *
 * Modern cards don't do this but reading from ECR will return 0xff
 * regardless of what is written here if the card does NOT support
 * ECP.
 *
 * We first check to see if ECR is the same as CTR.  If not, the low
 * two bits of ECR aren't writable, so we check by writing ECR and
 * reading it back to see if it's what we expect.
 */
static int parport_ECR_present(struct parport *pb)
{
    struct parport_pc_private *priv = pb->private_data;
    unsigned char r = 0xc;

    outb (r, CONTROL (pb));
    if ((inb (ECONTROL (pb)) & 0x3) == (r & 0x3)) {
        outb (r ^ 0x2, CONTROL (pb)); /* Toggle bit 1 */

        r = inb (CONTROL (pb));
        if ((inb (ECONTROL (pb)) & 0x2) == (r & 0x2))
            goto no_reg; /* Sure that no ECR register exists */
    }
    
    if ((inb (ECONTROL (pb)) & 0x3 ) != 0x1)
        goto no_reg;

    ECR_WRITE (pb, 0x34);
    if (inb (ECONTROL (pb)) != 0x35)
        goto no_reg;

    priv->ecr = 1;
    outb (0xc, CONTROL (pb));
    
    /* Go to mode 000 */
    frob_set_mode (pb, ECR_SPP);

    return 1;

 no_reg:
    outb (0xc, CONTROL (pb));
    return 0; 
}

#ifdef CONFIG_PARPORT_1284
/* Detect PS/2 support.
 *
 * Bit 5 (0x20) sets the PS/2 data direction; setting this high
 * allows us to read data from the data lines.  In theory we would get back
 * 0xff but any peripheral attached to the port may drag some or all of the
 * lines down to zero.  So if we get back anything that isn't the contents
 * of the data register we deem PS/2 support to be present. 
 *
 * Some SPP ports have "half PS/2" ability - you can't turn off the line
 * drivers, but an external peripheral with sufficiently beefy drivers of
 * its own can overpower them and assert its own levels onto the bus, from
 * where they can then be read back as normal.  Ports with this property
 * and the right type of device attached are likely to fail the SPP test,
 * (as they will appear to have stuck bits) and so the fact that they might
 * be misdetected here is rather academic. 
 */

static int parport_PS2_supported(struct parport *pb)
{
    int ok = 0;
  
    clear_epp_timeout(pb);

    /* try to tri-state the buffer */
    parport_pc_data_reverse (pb);
    
    parport_pc_write_data(pb, 0x55);
	if (parport_pc_read_data(pb) != 0x55)
		ok++;

    parport_pc_write_data(pb, 0xaa);
	if (parport_pc_read_data(pb) != 0xaa)
		ok++;

    /* cancel input mode */
    parport_pc_data_forward (pb);

    if (ok) {
        pb->modes |= PARPORT_MODE_TRISTATE;
    } else {
        struct parport_pc_private *priv = pb->private_data;
        priv->ctr_writable &= ~0x20;
    }

    return ok;
}

#ifdef CONFIG_PARPORT_PC_FIFO
static int parport_ECP_supported(struct parport *pb)
{
    int i;
    int config, configb;
    int pword;
    struct parport_pc_private *priv = pb->private_data;
    /* Translate ECP intrLine to ISA irq value */    
    static const int intrline[]= { 0, 7, 9, 10, 11, 14, 15, 5 }; 

    /* If there is no ECR, we have no hope of supporting ECP. */
    if (!priv->ecr)
        return 0;

    /* Find out FIFO depth */
    ECR_WRITE (pb, ECR_SPP << 5); /* Reset FIFO */
    ECR_WRITE (pb, ECR_TST << 5); /* TEST FIFO */
    for (i=0; i < 1024 && !(inb (ECONTROL (pb)) & 0x02); i++)
        outb (0xaa, FIFO (pb));

    /*
     * Using LGS chipset it uses ECR register, but
     * it doesn't support ECP or FIFO MODE
     */
    if (i == 1024) {
        ECR_WRITE (pb, ECR_SPP << 5);
        return 0;
    }

    priv->fifo_depth = i;
    if (verbose_probing)
        printk (KERN_DEBUG "0x%lx: FIFO is %d bytes\n", pb->base, i);

    /* Find out writeIntrThreshold */
    frob_econtrol (pb, 1<<2, 1<<2);
    frob_econtrol (pb, 1<<2, 0);
    for (i = 1; i <= priv->fifo_depth; i++) {
        inb (FIFO (pb));
        udelay (50);
        if (inb (ECONTROL (pb)) & (1<<2))
            break;
    }

    if (i <= priv->fifo_depth) {
        if (verbose_probing)
            printk (KERN_DEBUG "0x%lx: writeIntrThreshold is %d\n",
                pb->base, i);
    } else
        /* Number of bytes we know we can write if we get an
                   interrupt. */
        i = 0;

    priv->writeIntrThreshold = i;

    /* Find out readIntrThreshold */
    frob_set_mode (pb, ECR_PS2); /* Reset FIFO and enable PS2 */
    parport_pc_data_reverse (pb); /* Must be in PS2 mode */
    frob_set_mode (pb, ECR_TST); /* Test FIFO */
    frob_econtrol (pb, 1<<2, 1<<2);
    frob_econtrol (pb, 1<<2, 0);
    for (i = 1; i <= priv->fifo_depth; i++) {
        outb (0xaa, FIFO (pb));
        if (inb (ECONTROL (pb)) & (1<<2))
            break;
    }

    if (i <= priv->fifo_depth) {
        if (verbose_probing)
            printk (KERN_INFO "0x%lx: readIntrThreshold is %d\n",
                pb->base, i);
    } else
        /* Number of bytes we can read if we get an interrupt. */
        i = 0;

    priv->readIntrThreshold = i;

    ECR_WRITE (pb, ECR_SPP << 5); /* Reset FIFO */
    ECR_WRITE (pb, 0xf4); /* Configuration mode */
    config = inb (CONFIGA (pb));
    pword = (config >> 4) & 0x7;
    switch (pword) {
    case 0:
        pword = 2;
        printk (KERN_WARNING "0x%lx: Unsupported pword size!\n",
            pb->base);
        break;
    case 2:
        pword = 4;
        printk (KERN_WARNING "0x%lx: Unsupported pword size!\n",
            pb->base);
        break;
    default:
        printk (KERN_WARNING "0x%lx: Unknown implementation ID\n",
            pb->base);
        /* Assume 1 */
    case 1:
        pword = 1;
    }
    priv->pword = pword;

    if (verbose_probing) {
		printk(KERN_DEBUG "0x%lx: PWord is %d bits\n",
			pb->base, 8 * pword);
        
        printk (KERN_DEBUG "0x%lx: Interrupts are ISA-%s\n", pb->base,
            config & 0x80 ? "Level" : "Pulses");

        configb = inb (CONFIGB (pb));
        printk (KERN_DEBUG "0x%lx: ECP port cfgA=0x%02x cfgB=0x%02x\n",
            pb->base, config, configb);
        printk (KERN_DEBUG "0x%lx: ECP settings irq=", pb->base);
        if ((configb >>3) & 0x07)
            printk("%d",intrline[(configb >>3) & 0x07]);
        else
            printk("<none or set by other means>");
        printk (" dma=");
        if( (configb & 0x03 ) == 0x00)
            printk("<none or set by other means>\n");
        else
            printk("%d\n",configb & 0x07);
    }

    /* Go back to mode 000 */
    frob_set_mode (pb, ECR_SPP);

    return 1;
}
#endif

static int parport_ECPPS2_supported(struct parport *pb)
{
    const struct parport_pc_private *priv = pb->private_data;
    int result;
    unsigned char oecr;

    if (!priv->ecr)
        return 0;

    oecr = inb (ECONTROL (pb));
    ECR_WRITE (pb, ECR_PS2 << 5);
    result = parport_PS2_supported(pb);
    ECR_WRITE (pb, oecr);
    return result;
}

/* EPP mode detection  */

static int parport_EPP_supported(struct parport *pb)
{
    const struct parport_pc_private *priv = pb->private_data;

    /*
     * Theory:
     *    Bit 0 of STR is the EPP timeout bit, this bit is 0
     *    when EPP is possible and is set high when an EPP timeout
     *    occurs (EPP uses the HALT line to stop the CPU while it does
     *    the byte transfer, an EPP timeout occurs if the attached
     *    device fails to respond after 10 micro seconds).
     *
     *    This bit is cleared by either reading it (National Semi)
     *    or writing a 1 to the bit (SMC, UMC, WinBond), others ???
     *    This bit is always high in non EPP modes.
     */

    /* If EPP timeout bit clear then EPP available */
	if (!clear_epp_timeout(pb))
		return 0;  /* No way to clear timeout */

    /* Check for Intel bug. */
    if (priv->ecr) {
        unsigned char i;
        for (i = 0x00; i < 0x80; i += 0x20) {
            ECR_WRITE (pb, i);
            if (clear_epp_timeout (pb)) {
                /* Phony EPP in ECP. */
                return 0;
            }
        }
    }

    pb->modes |= PARPORT_MODE_EPP;

    /* Set up access functions to use EPP hardware. */
    pb->ops->epp_read_data = parport_pc_epp_read_data;
    pb->ops->epp_write_data = parport_pc_epp_write_data;
    pb->ops->epp_read_addr = parport_pc_epp_read_addr;
    pb->ops->epp_write_addr = parport_pc_epp_write_addr;

    return 1;
}

static int parport_ECPEPP_supported(struct parport *pb)
{
    struct parport_pc_private *priv = pb->private_data;
    int result;
    unsigned char oecr;

	if (!priv->ecr)
		return 0;

    oecr = inb (ECONTROL (pb));
    /* Search for SMC style EPP+ECP mode */
    ECR_WRITE (pb, 0x80);
    outb (0x04, CONTROL (pb));
    result = parport_EPP_supported(pb);
    
    inb (ECONTROL (pb)); // fix hardware bug

    ECR_WRITE (pb, oecr);

    if (result) {
        /* Set up access functions to use ECP+EPP hardware. */
        pb->ops->epp_read_data = parport_pc_ecpepp_read_data;
        pb->ops->epp_write_data = parport_pc_ecpepp_write_data;
        pb->ops->epp_read_addr = parport_pc_ecpepp_read_addr;
        pb->ops->epp_write_addr = parport_pc_ecpepp_write_addr;
    }

    return result;
}

#else /* No IEEE 1284 support */

/* Don't bother probing for modes we know we won't use. */
static int __devinit parport_PS2_supported(struct parport *pb) { return 0; }
#ifdef CONFIG_PARPORT_PC_FIFO
static int parport_ECP_supported(struct parport *pb)
{
	return 0;
}
#endif
static int __devinit parport_EPP_supported(struct parport *pb)
{
	return 0;
}

static int __devinit parport_ECPEPP_supported(struct parport *pb)
{
	return 0;
}

static int __devinit parport_ECPPS2_supported(struct parport *pb)
{
	return 0;
}

#endif /* No IEEE 1284 support */

/* --- Initialisation code -------------------------------- */

static LIST_HEAD(ports_list);
static DEFINE_SPINLOCK(ports_lock);

struct parport *my_parport_pc_probe_port (unsigned long int base,
                       unsigned long int base_hi,
                       int irq, int dma,
                       struct device *dev)
{
    struct parport_pc_private *priv;
    struct parport_operations *ops;
    struct parport *p;
    int probedirq = PARPORT_IRQ_NONE;
    struct resource *base_res;
    struct resource    *ECR_res = NULL;
    struct resource    *EPP_res = NULL;
	struct platform_device *pdev = NULL;

	if (!dev) {
		/* We need a physical device to attach to, but none was
		 * provided. Create our own. */
		pdev = platform_device_register_simple("parport_pc",
						       base, NULL, 0);
		if (IS_ERR(pdev))
			return NULL;
		dev = &pdev->dev;

		dev->coherent_dma_mask = DMA_BIT_MASK(24);
		dev->dma_mask = &dev->coherent_dma_mask;
	}

    ops = kmalloc(sizeof (struct parport_operations), GFP_KERNEL);
    if (!ops)
        goto out1;

    priv = kmalloc (sizeof (struct parport_pc_private), GFP_KERNEL);
    if (!priv)
        goto out2;

    /* a misnomer, actually - it's allocate and reserve parport number */
    p = parport_register_port(base, irq, dma, ops);
    if (!p)
        goto out3;

    base_res = request_region(base, 3, p->name);
    if (!base_res)
        goto out4;

    memcpy(ops, &parport_pc_ops, sizeof (struct parport_operations));
    priv->ctr = 0xc;
    priv->ctr_writable = ~0x10;
    priv->ecr = 0;
    priv->fifo_depth = 0;
    priv->dma_buf = NULL;
    priv->dma_handle = 0;
    INIT_LIST_HEAD(&priv->list);
    priv->port = p;

	p->dev = dev;
    p->base_hi = base_hi;
    p->modes = PARPORT_MODE_PCSPP | PARPORT_MODE_SAFEININT;
    p->private_data = priv;

    if (base_hi) {
        ECR_res = request_region(base_hi, 3, p->name);
        if (ECR_res)
            parport_ECR_present(p);
    }

    if (base != 0x3bc) {
        EPP_res = request_region(base+0x3, 5, p->name);
        if (EPP_res)
            if (!parport_EPP_supported(p))
                parport_ECPEPP_supported(p);
    }
    if (!parport_SPP_supported (p))
        /* No port. */
        goto out5;
    if (priv->ecr)
        parport_ECPPS2_supported(p);
    else
        parport_PS2_supported(p);

    p->size = (p->modes & PARPORT_MODE_EPP)?8:3;

    printk(KERN_INFO "%s: PC-style at 0x%lx", p->name, p->base);
    if (p->base_hi && priv->ecr)
		printk(KERN_CONT " (0x%lx)", p->base_hi);
    if (p->irq == PARPORT_IRQ_AUTO) {
        p->irq = PARPORT_IRQ_NONE;
        //parport_irq_probe(p);
    } else if (p->irq == PARPORT_IRQ_PROBEONLY) {
        p->irq = PARPORT_IRQ_NONE;
        //parport_irq_probe(p);
        probedirq = p->irq;
        p->irq = PARPORT_IRQ_NONE;
    }
    if (p->irq != PARPORT_IRQ_NONE) {
		printk(KERN_CONT ", irq %d", p->irq);
        priv->ctr_writable |= 0x10;

        if (p->dma == PARPORT_DMA_AUTO) {
            p->dma = PARPORT_DMA_NONE;
            //parport_dma_probe(p);
        }
    }
    if (p->dma == PARPORT_DMA_AUTO) /* To use DMA, giving the irq
                                           is mandatory (see above) */
        p->dma = PARPORT_DMA_NONE;

#ifdef CONFIG_PARPORT_PC_FIFO
    if (parport_ECP_supported(p) &&
        p->dma != PARPORT_DMA_NOFIFO &&
        priv->fifo_depth > 0 && p->irq != PARPORT_IRQ_NONE) {
        p->modes |= PARPORT_MODE_ECP | PARPORT_MODE_COMPAT;
        p->ops->compat_write_data = parport_pc_compat_write_block_pio;
#ifdef CONFIG_PARPORT_1284
        p->ops->ecp_write_data = parport_pc_ecp_write_block_pio;
        /* currently broken, but working on it.. (FB) */
        /* p->ops->ecp_read_data = parport_pc_ecp_read_block_pio; */
#endif /* IEEE 1284 support */
        if (p->dma != PARPORT_DMA_NONE) {
			printk(KERN_CONT ", dma %d", p->dma);
            p->modes |= PARPORT_MODE_DMA;
		} else
			printk(KERN_CONT ", using FIFO");
	} else
        /* We can't use the DMA channel after all. */
        p->dma = PARPORT_DMA_NONE;
#endif /* Allowed to use FIFO/DMA */

	printk(KERN_CONT " [");

#define printmode(x) \
	{\
		if (p->modes & PARPORT_MODE_##x) {\
			printk(KERN_CONT "%s%s", f ? "," : "", #x);\
			f++;\
		} \
	}

    {
        int f = 0;
        printmode(PCSPP);
        printmode(TRISTATE);
        printmode(COMPAT)
        printmode(EPP);
        printmode(ECP);
        printmode(DMA);
    }
#undef printmode
#ifndef CONFIG_PARPORT_1284
	printk(KERN_CONT "(,...)");
#endif /* CONFIG_PARPORT_1284 */
	printk(KERN_CONT "]\n");
    if (probedirq != PARPORT_IRQ_NONE) 
        printk(KERN_INFO "%s: irq %d detected\n", p->name, probedirq);

    /* If No ECP release the ports grabbed above. */
    if (ECR_res && (p->modes & PARPORT_MODE_ECP) == 0) {
        release_region(base_hi, 3);
        ECR_res = NULL;
    }
    /* Likewise for EEP ports */
    if (EPP_res && (p->modes & PARPORT_MODE_EPP) == 0) {
        release_region(base+3, 5);
        EPP_res = NULL;
    }
    if (p->irq != PARPORT_IRQ_NONE) {
		if (request_irq(p->irq, parport_irq_handler,
                 0, p->name, p)) {
            printk (KERN_WARNING "%s: irq %d in use, "
                "resorting to polled operation\n",
                p->name, p->irq);
            p->irq = PARPORT_IRQ_NONE;
            p->dma = PARPORT_DMA_NONE;
        }

#ifdef CONFIG_PARPORT_PC_FIFO
#ifdef HAS_DMA
        if (p->dma != PARPORT_DMA_NONE) {
            if (request_dma (p->dma, p->name)) {
                printk (KERN_WARNING "%s: dma %d in use, "
                    "resorting to PIO operation\n",
                    p->name, p->dma);
                p->dma = PARPORT_DMA_NONE;
            } else {
                priv->dma_buf =
				  dma_alloc_coherent(dev,
						       PAGE_SIZE,
						       &priv->dma_handle,
						       GFP_KERNEL);
                if (! priv->dma_buf) {
                    printk (KERN_WARNING "%s: "
                        "cannot get buffer for DMA, "
                        "resorting to PIO operation\n",
                        p->name);
                    free_dma(p->dma);
                    p->dma = PARPORT_DMA_NONE;
                }
            }
        }
#endif
#endif
    }

    /* Done probing.  Now put the port into a sensible start-up state. */
    if (priv->ecr)
        /*
         * Put the ECP detected port in PS2 mode.
         * Do this also for ports that have ECR but don't do ECP.
         */
        ECR_WRITE (p, 0x34);

    parport_pc_write_data(p, 0);
    parport_pc_data_forward (p);

    /* Now that we've told the sharing engine about the port, and
       found out its characteristics, let the high-level drivers
       know about it. */
    spin_lock(&ports_lock);
    list_add(&priv->list, &ports_list);
    spin_unlock(&ports_lock);
    parport_announce_port (p);

    return p;

out5:
    if (ECR_res)
        release_region(base_hi, 3);
    if (EPP_res)
        release_region(base+0x3, 5);
    release_region(base, 3);
out4:
    parport_put_port(p);
out3:
    kfree (priv);
out2:
    kfree (ops);
out1:
	if (pdev)
		platform_device_unregister(pdev);
    return NULL;
}

//EXPORT_SYMBOL (my_parport_pc_probe_port);

void my_parport_pc_unregister_port (struct parport *p)
{
    struct parport_pc_private *priv = p->private_data;
    struct parport_operations *ops = p->ops;

    parport_remove_port(p);
    spin_lock(&ports_lock);
    list_del_init(&priv->list);
    spin_unlock(&ports_lock);
#if defined(CONFIG_PARPORT_PC_FIFO) && defined(HAS_DMA)
    if (p->dma != PARPORT_DMA_NONE)
        free_dma(p->dma);
#endif
    if (p->irq != PARPORT_IRQ_NONE)
        free_irq(p->irq, p);
    release_region(p->base, 3);
    if (p->size > 3)
        release_region(p->base + 3, p->size - 3);
    if (p->modes & PARPORT_MODE_ECP)
        release_region(p->base_hi, 3);
#if defined(CONFIG_PARPORT_PC_FIFO) && defined(HAS_DMA)
    if (priv->dma_buf)
		dma_free_coherent(p->physport->dev, PAGE_SIZE,
				    priv->dma_buf,
				    priv->dma_handle);
#endif
    kfree (p->private_data);
    parport_put_port(p);
    kfree (ops); /* hope no-one cached it */
}

//EXPORT_SYMBOL (parport_pc_unregister_port);
