root/drivers/parport/ieee1284_ops.c
// SPDX-License-Identifier: GPL-2.0
/* IEEE-1284 operations for parport.
 *
 * This file is for generic IEEE 1284 operations.  The idea is that
 * they are used by the low-level drivers.  If they have a special way
 * of doing something, they can provide their own routines (and put
 * the function pointers in port->ops); if not, they can just use these
 * as a fallback.
 *
 * Note: Make no assumptions about hardware or architecture in this file!
 *
 * Author: Tim Waugh <tim@cyberelk.demon.co.uk>
 * Fixed AUTOFD polarity in ecp_forward_to_reverse().  Fred Barnes, 1999
 * Software emulated EPP fixes, Fred Barnes, 04/2001.
 */


#include <linux/module.h>
#include <linux/parport.h>
#include <linux/delay.h>
#include <linux/sched/signal.h>
#include <linux/uaccess.h>

#undef DEBUG /* undef me for production */

#ifdef CONFIG_LP_CONSOLE
#undef DEBUG /* Don't want a garbled console */
#endif

/***                                *
 * One-way data transfer functions. *
 *                                ***/

/* Compatibility mode. */
size_t parport_ieee1284_write_compat (struct parport *port,
                                      const void *buffer, size_t len,
                                      int flags)
{
        int no_irq = 1;
        ssize_t count = 0;
        const unsigned char *addr = buffer;
        unsigned char byte;
        struct pardevice *dev = port->physport->cad;
        unsigned char ctl = (PARPORT_CONTROL_SELECT
                             | PARPORT_CONTROL_INIT);

        if (port->irq != PARPORT_IRQ_NONE) {
                parport_enable_irq (port);
                no_irq = 0;
        }

        port->physport->ieee1284.phase = IEEE1284_PH_FWD_DATA;
        parport_write_control (port, ctl);
        parport_data_forward (port);
        while (count < len) {
                unsigned long expire = jiffies + dev->timeout;
                long wait = msecs_to_jiffies(10);
                unsigned char mask = (PARPORT_STATUS_ERROR
                                      | PARPORT_STATUS_BUSY);
                unsigned char val = (PARPORT_STATUS_ERROR
                                     | PARPORT_STATUS_BUSY);

                /* Wait until the peripheral's ready */
                do {
                        /* Is the peripheral ready yet? */
                        if (!parport_wait_peripheral (port, mask, val))
                                /* Skip the loop */
                                goto ready;

                        /* Is the peripheral upset? */
                        if ((parport_read_status (port) &
                             (PARPORT_STATUS_PAPEROUT |
                              PARPORT_STATUS_SELECT |
                              PARPORT_STATUS_ERROR))
                            != (PARPORT_STATUS_SELECT |
                                PARPORT_STATUS_ERROR))
                                /* If nFault is asserted (i.e. no
                                 * error) and PAPEROUT and SELECT are
                                 * just red herrings, give the driver
                                 * a chance to check it's happy with
                                 * that before continuing. */
                                goto stop;

                        /* Have we run out of time? */
                        if (!time_before (jiffies, expire))
                                break;

                        /* Yield the port for a while.  If this is the
                           first time around the loop, don't let go of
                           the port.  This way, we find out if we have
                           our interrupt handler called. */
                        if (count && no_irq) {
                                parport_release (dev);
                                schedule_timeout_interruptible(wait);
                                parport_claim_or_block (dev);
                        }
                        else
                                /* We must have the device claimed here */
                                parport_wait_event (port, wait);

                        /* Is there a signal pending? */
                        if (signal_pending (current))
                                break;

                        /* Wait longer next time. */
                        wait *= 2;
                } while (time_before (jiffies, expire));

                if (signal_pending (current))
                        break;

                pr_debug("%s: Timed out\n", port->name);
                break;

        ready:
                /* Write the character to the data lines. */
                byte = *addr++;
                parport_write_data (port, byte);
                udelay (1);

                /* Pulse strobe. */
                parport_write_control (port, ctl | PARPORT_CONTROL_STROBE);
                udelay (1); /* strobe */

                parport_write_control (port, ctl);
                udelay (1); /* hold */

                /* Assume the peripheral received it. */
                count++;

                /* Let another process run if it needs to. */
                if (time_before (jiffies, expire))
                        if (!parport_yield_blocking (dev)
                            && need_resched())
                                schedule ();
        }
 stop:
        port->physport->ieee1284.phase = IEEE1284_PH_FWD_IDLE;

        return count;
}

/* Nibble mode. */
size_t parport_ieee1284_read_nibble (struct parport *port, 
                                     void *buffer, size_t len,
                                     int flags)
{
#ifndef CONFIG_PARPORT_1284
        return 0;
#else
        unsigned char *buf = buffer;
        int i;
        unsigned char byte = 0;

        len *= 2; /* in nibbles */
        for (i=0; i < len; i++) {
                unsigned char nibble;

                /* Does the error line indicate end of data? */
                if (((i & 1) == 0) &&
                    (parport_read_status(port) & PARPORT_STATUS_ERROR)) {
                        goto end_of_data;
                }

                /* Event 7: Set nAutoFd low. */
                parport_frob_control (port,
                                      PARPORT_CONTROL_AUTOFD,
                                      PARPORT_CONTROL_AUTOFD);

                /* Event 9: nAck goes low. */
                port->ieee1284.phase = IEEE1284_PH_REV_DATA;
                if (parport_wait_peripheral (port,
                                             PARPORT_STATUS_ACK, 0)) {
                        /* Timeout -- no more data? */
                        pr_debug("%s: Nibble timeout at event 9 (%d bytes)\n",
                                 port->name, i / 2);
                        parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0);
                        break;
                }


                /* Read a nibble. */
                nibble = parport_read_status (port) >> 3;
                nibble &= ~8;
                if ((nibble & 0x10) == 0)
                        nibble |= 8;
                nibble &= 0xf;

                /* Event 10: Set nAutoFd high. */
                parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0);

                /* Event 11: nAck goes high. */
                if (parport_wait_peripheral (port,
                                             PARPORT_STATUS_ACK,
                                             PARPORT_STATUS_ACK)) {
                        /* Timeout -- no more data? */
                        pr_debug("%s: Nibble timeout at event 11\n",
                                 port->name);
                        break;
                }

                if (i & 1) {
                        /* Second nibble */
                        byte |= nibble << 4;
                        *buf++ = byte;
                } else 
                        byte = nibble;
        }

        if (i == len) {
                /* Read the last nibble without checking data avail. */
                if (parport_read_status (port) & PARPORT_STATUS_ERROR) {
                end_of_data:
                        pr_debug("%s: No more nibble data (%d bytes)\n",
                                 port->name, i / 2);

                        /* Go to reverse idle phase. */
                        parport_frob_control (port,
                                              PARPORT_CONTROL_AUTOFD,
                                              PARPORT_CONTROL_AUTOFD);
                        port->physport->ieee1284.phase = IEEE1284_PH_REV_IDLE;
                }
                else
                        port->physport->ieee1284.phase = IEEE1284_PH_HBUSY_DAVAIL;
        }

        return i/2;
#endif /* IEEE1284 support */
}

/* Byte mode. */
size_t parport_ieee1284_read_byte (struct parport *port,
                                   void *buffer, size_t len,
                                   int flags)
{
#ifndef CONFIG_PARPORT_1284
        return 0;
#else
        unsigned char *buf = buffer;
        ssize_t count = 0;

        for (count = 0; count < len; count++) {
                unsigned char byte;

                /* Data available? */
                if (parport_read_status (port) & PARPORT_STATUS_ERROR) {
                        goto end_of_data;
                }

                /* Event 14: Place data bus in high impedance state. */
                parport_data_reverse (port);

                /* Event 7: Set nAutoFd low. */
                parport_frob_control (port,
                                      PARPORT_CONTROL_AUTOFD,
                                      PARPORT_CONTROL_AUTOFD);

                /* Event 9: nAck goes low. */
                port->physport->ieee1284.phase = IEEE1284_PH_REV_DATA;
                if (parport_wait_peripheral (port,
                                             PARPORT_STATUS_ACK,
                                             0)) {
                        /* Timeout -- no more data? */
                        parport_frob_control (port, PARPORT_CONTROL_AUTOFD,
                                                 0);
                        pr_debug("%s: Byte timeout at event 9\n", port->name);
                        break;
                }

                byte = parport_read_data (port);
                *buf++ = byte;

                /* Event 10: Set nAutoFd high */
                parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0);

                /* Event 11: nAck goes high. */
                if (parport_wait_peripheral (port,
                                             PARPORT_STATUS_ACK,
                                             PARPORT_STATUS_ACK)) {
                        /* Timeout -- no more data? */
                        pr_debug("%s: Byte timeout at event 11\n", port->name);
                        break;
                }

                /* Event 16: Set nStrobe low. */
                parport_frob_control (port,
                                      PARPORT_CONTROL_STROBE,
                                      PARPORT_CONTROL_STROBE);
                udelay (5);

                /* Event 17: Set nStrobe high. */
                parport_frob_control (port, PARPORT_CONTROL_STROBE, 0);
        }

        if (count == len) {
                /* Read the last byte without checking data avail. */
                if (parport_read_status (port) & PARPORT_STATUS_ERROR) {
                end_of_data:
                        pr_debug("%s: No more byte data (%zd bytes)\n",
                                 port->name, count);

                        /* Go to reverse idle phase. */
                        parport_frob_control (port,
                                              PARPORT_CONTROL_AUTOFD,
                                              PARPORT_CONTROL_AUTOFD);
                        port->physport->ieee1284.phase = IEEE1284_PH_REV_IDLE;
                }
                else
                        port->physport->ieee1284.phase = IEEE1284_PH_HBUSY_DAVAIL;
        }

        return count;
#endif /* IEEE1284 support */
}

/***              *
 * ECP Functions. *
 *              ***/

#ifdef CONFIG_PARPORT_1284

static inline
int ecp_forward_to_reverse (struct parport *port)
{
        int retval;

        /* Event 38: Set nAutoFd low */
        parport_frob_control (port,
                              PARPORT_CONTROL_AUTOFD,
                              PARPORT_CONTROL_AUTOFD);
        parport_data_reverse (port);
        udelay (5);

        /* Event 39: Set nInit low to initiate bus reversal */
        parport_frob_control (port,
                              PARPORT_CONTROL_INIT,
                              0);

        /* Event 40: PError goes low */
        retval = parport_wait_peripheral (port,
                                          PARPORT_STATUS_PAPEROUT, 0);

        if (!retval) {
                pr_debug("%s: ECP direction: reverse\n", port->name);
                port->ieee1284.phase = IEEE1284_PH_REV_IDLE;
        } else {
                pr_debug("%s: ECP direction: failed to reverse\n", port->name);
                port->ieee1284.phase = IEEE1284_PH_ECP_DIR_UNKNOWN;
        }

        return retval;
}

static inline
int ecp_reverse_to_forward (struct parport *port)
{
        int retval;

        /* 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 */
        retval = parport_wait_peripheral (port,
                                          PARPORT_STATUS_PAPEROUT,
                                          PARPORT_STATUS_PAPEROUT);

        if (!retval) {
                parport_data_forward (port);
                pr_debug("%s: ECP direction: forward\n", port->name);
                port->ieee1284.phase = IEEE1284_PH_FWD_IDLE;
        } else {
                pr_debug("%s: ECP direction: failed to switch forward\n",
                         port->name);
                port->ieee1284.phase = IEEE1284_PH_ECP_DIR_UNKNOWN;
        }


        return retval;
}

#endif /* IEEE1284 support */

/* ECP mode, forward channel, data. */
size_t parport_ieee1284_ecp_write_data (struct parport *port,
                                        const void *buffer, size_t len,
                                        int flags)
{
#ifndef CONFIG_PARPORT_1284
        return 0;
#else
        const unsigned char *buf = buffer;
        size_t written;
        int retry;

        port = port->physport;

        if (port->ieee1284.phase != IEEE1284_PH_FWD_IDLE)
                if (ecp_reverse_to_forward (port))
                        return 0;

        port->ieee1284.phase = IEEE1284_PH_FWD_DATA;

        /* HostAck high (data, not command) */
        parport_frob_control (port,
                              PARPORT_CONTROL_AUTOFD
                              | PARPORT_CONTROL_STROBE
                              | PARPORT_CONTROL_INIT,
                              PARPORT_CONTROL_INIT);
        for (written = 0; written < len; written++, buf++) {
                unsigned long expire = jiffies + port->cad->timeout;
                unsigned char byte;

                byte = *buf;
        try_again:
                parport_write_data (port, byte);
                parport_frob_control (port, PARPORT_CONTROL_STROBE,
                                      PARPORT_CONTROL_STROBE);
                udelay (5);
                for (retry = 0; retry < 100; retry++) {
                        if (!parport_wait_peripheral (port,
                                                      PARPORT_STATUS_BUSY, 0))
                                goto success;

                        if (signal_pending (current)) {
                                parport_frob_control (port,
                                                      PARPORT_CONTROL_STROBE,
                                                      0);
                                break;
                        }
                }

                /* Time for Host Transfer Recovery (page 41 of IEEE1284) */
                pr_debug("%s: ECP transfer stalled!\n", port->name);

                parport_frob_control (port, PARPORT_CONTROL_INIT,
                                      PARPORT_CONTROL_INIT);
                udelay (50);
                if (parport_read_status (port) & PARPORT_STATUS_PAPEROUT) {
                        /* It's buggered. */
                        parport_frob_control (port, PARPORT_CONTROL_INIT, 0);
                        break;
                }

                parport_frob_control (port, PARPORT_CONTROL_INIT, 0);
                udelay (50);
                if (!(parport_read_status (port) & PARPORT_STATUS_PAPEROUT))
                        break;

                pr_debug("%s: Host transfer recovered\n", port->name);

                if (time_after_eq (jiffies, expire)) break;
                goto try_again;
        success:
                parport_frob_control (port, PARPORT_CONTROL_STROBE, 0);
                udelay (5);
                if (parport_wait_peripheral (port,
                                             PARPORT_STATUS_BUSY,
                                             PARPORT_STATUS_BUSY))
                        /* Peripheral hasn't accepted the data. */
                        break;
        }

        port->ieee1284.phase = IEEE1284_PH_FWD_IDLE;

        return written;
#endif /* IEEE1284 support */
}

/* ECP mode, reverse channel, data. */
size_t parport_ieee1284_ecp_read_data (struct parport *port,
                                       void *buffer, size_t len, int flags)
{
#ifndef CONFIG_PARPORT_1284
        return 0;
#else
        struct pardevice *dev = port->cad;
        unsigned char *buf = buffer;
        int rle_count = 0; /* shut gcc up */
        unsigned char ctl;
        int rle = 0;
        ssize_t count = 0;

        port = port->physport;

        if (port->ieee1284.phase != IEEE1284_PH_REV_IDLE)
                if (ecp_forward_to_reverse (port))
                        return 0;

        port->ieee1284.phase = IEEE1284_PH_REV_DATA;

        /* Set HostAck low to start accepting data. */
        ctl = parport_read_control (port);
        ctl &= ~(PARPORT_CONTROL_STROBE | PARPORT_CONTROL_INIT |
                 PARPORT_CONTROL_AUTOFD);
        parport_write_control (port,
                               ctl | PARPORT_CONTROL_AUTOFD);
        while (count < len) {
                unsigned long expire = jiffies + dev->timeout;
                unsigned char byte;
                int command;

                /* Event 43: Peripheral sets nAck low. It can take as
                   long as it wants. */
                while (parport_wait_peripheral (port, PARPORT_STATUS_ACK, 0)) {
                        /* The peripheral hasn't given us data in
                           35ms.  If we have data to give back to the
                           caller, do it now. */
                        if (count)
                                goto out;

                        /* If we've used up all the time we were allowed,
                           give up altogether. */
                        if (!time_before (jiffies, expire))
                                goto out;

                        /* Yield the port for a while. */
                        if (dev->port->irq != PARPORT_IRQ_NONE) {
                                parport_release (dev);
                                schedule_timeout_interruptible(msecs_to_jiffies(40));
                                parport_claim_or_block (dev);
                        }
                        else
                                /* We must have the device claimed here. */
                                parport_wait_event (port, msecs_to_jiffies(40));

                        /* Is there a signal pending? */
                        if (signal_pending (current))
                                goto out;
                }

                /* Is this a command? */
                if (rle)
                        /* The last byte was a run-length count, so
                           this can't be as well. */
                        command = 0;
                else
                        command = (parport_read_status (port) &
                                   PARPORT_STATUS_BUSY) ? 1 : 0;

                /* Read the data. */
                byte = parport_read_data (port);

                /* If this is a channel command, rather than an RLE
                   command or a normal data byte, don't accept it. */
                if (command) {
                        if (byte & 0x80) {
                                pr_debug("%s: stopping short at channel command (%02x)\n",
                                         port->name, byte);
                                goto out;
                        }
                        else if (port->ieee1284.mode != IEEE1284_MODE_ECPRLE)
                                pr_debug("%s: device illegally using RLE; accepting anyway\n",
                                         port->name);

                        rle_count = byte + 1;

                        /* Are we allowed to read that many bytes? */
                        if (rle_count > (len - count)) {
                                pr_debug("%s: leaving %d RLE bytes for next time\n",
                                         port->name, rle_count);
                                break;
                        }

                        rle = 1;
                }

                /* Event 44: Set HostAck high, acknowledging handshake. */
                parport_write_control (port, ctl);

                /* Event 45: The peripheral has 35ms to set nAck high. */
                if (parport_wait_peripheral (port, PARPORT_STATUS_ACK,
                                             PARPORT_STATUS_ACK)) {
                        /* It's gone wrong.  Return what data we have
                           to the caller. */
                        pr_debug("ECP read timed out at 45\n");

                        if (command)
                                pr_warn("%s: command ignored (%02x)\n",
                                        port->name, byte);

                        break;
                }

                /* Event 46: Set HostAck low and accept the data. */
                parport_write_control (port,
                                       ctl | PARPORT_CONTROL_AUTOFD);

                /* If we just read a run-length count, fetch the data. */
                if (command)
                        continue;

                /* If this is the byte after a run-length count, decompress. */
                if (rle) {
                        rle = 0;
                        memset (buf, byte, rle_count);
                        buf += rle_count;
                        count += rle_count;
                        pr_debug("%s: decompressed to %d bytes\n",
                                 port->name, rle_count);
                } else {
                        /* Normal data byte. */
                        *buf = byte;
                        buf++, count++;
                }
        }

 out:
        port->ieee1284.phase = IEEE1284_PH_REV_IDLE;
        return count;
#endif /* IEEE1284 support */
}

/* ECP mode, forward channel, commands. */
size_t parport_ieee1284_ecp_write_addr (struct parport *port,
                                        const void *buffer, size_t len,
                                        int flags)
{
#ifndef CONFIG_PARPORT_1284
        return 0;
#else
        const unsigned char *buf = buffer;
        size_t written;
        int retry;

        port = port->physport;

        if (port->ieee1284.phase != IEEE1284_PH_FWD_IDLE)
                if (ecp_reverse_to_forward (port))
                        return 0;

        port->ieee1284.phase = IEEE1284_PH_FWD_DATA;

        /* HostAck low (command, not data) */
        parport_frob_control (port,
                              PARPORT_CONTROL_AUTOFD
                              | PARPORT_CONTROL_STROBE
                              | PARPORT_CONTROL_INIT,
                              PARPORT_CONTROL_AUTOFD
                              | PARPORT_CONTROL_INIT);
        for (written = 0; written < len; written++, buf++) {
                unsigned long expire = jiffies + port->cad->timeout;
                unsigned char byte;

                byte = *buf;
        try_again:
                parport_write_data (port, byte);
                parport_frob_control (port, PARPORT_CONTROL_STROBE,
                                      PARPORT_CONTROL_STROBE);
                udelay (5);
                for (retry = 0; retry < 100; retry++) {
                        if (!parport_wait_peripheral (port,
                                                      PARPORT_STATUS_BUSY, 0))
                                goto success;

                        if (signal_pending (current)) {
                                parport_frob_control (port,
                                                      PARPORT_CONTROL_STROBE,
                                                      0);
                                break;
                        }
                }

                /* Time for Host Transfer Recovery (page 41 of IEEE1284) */
                pr_debug("%s: ECP transfer stalled!\n", port->name);

                parport_frob_control (port, PARPORT_CONTROL_INIT,
                                      PARPORT_CONTROL_INIT);
                udelay (50);
                if (parport_read_status (port) & PARPORT_STATUS_PAPEROUT) {
                        /* It's buggered. */
                        parport_frob_control (port, PARPORT_CONTROL_INIT, 0);
                        break;
                }

                parport_frob_control (port, PARPORT_CONTROL_INIT, 0);
                udelay (50);
                if (!(parport_read_status (port) & PARPORT_STATUS_PAPEROUT))
                        break;

                pr_debug("%s: Host transfer recovered\n", port->name);

                if (time_after_eq (jiffies, expire)) break;
                goto try_again;
        success:
                parport_frob_control (port, PARPORT_CONTROL_STROBE, 0);
                udelay (5);
                if (parport_wait_peripheral (port,
                                             PARPORT_STATUS_BUSY,
                                             PARPORT_STATUS_BUSY))
                        /* Peripheral hasn't accepted the data. */
                        break;
        }

        port->ieee1284.phase = IEEE1284_PH_FWD_IDLE;

        return written;
#endif /* IEEE1284 support */
}

/***              *
 * EPP functions. *
 *              ***/

/* EPP mode, forward channel, data. */
size_t parport_ieee1284_epp_write_data (struct parport *port,
                                        const void *buffer, size_t len,
                                        int flags)
{
        unsigned char *bp = (unsigned char *) buffer;
        size_t ret = 0;

        /* set EPP idle state (just to make sure) with strobe low */
        parport_frob_control (port,
                              PARPORT_CONTROL_STROBE |
                              PARPORT_CONTROL_AUTOFD |
                              PARPORT_CONTROL_SELECT |
                              PARPORT_CONTROL_INIT,
                              PARPORT_CONTROL_STROBE |
                              PARPORT_CONTROL_INIT);
        port->ops->data_forward (port);
        for (; len > 0; len--, bp++) {
                /* Event 62: Write data and set autofd low */
                parport_write_data (port, *bp);
                parport_frob_control (port, PARPORT_CONTROL_AUTOFD,
                                      PARPORT_CONTROL_AUTOFD);

                /* Event 58: wait for busy (nWait) to go high */
                if (parport_poll_peripheral (port, PARPORT_STATUS_BUSY, 0, 10))
                        break;

                /* Event 63: set nAutoFd (nDStrb) high */
                parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0);

                /* Event 60: wait for busy (nWait) to go low */
                if (parport_poll_peripheral (port, PARPORT_STATUS_BUSY,
                                             PARPORT_STATUS_BUSY, 5))
                        break;

                ret++;
        }

        /* Event 61: set strobe (nWrite) high */
        parport_frob_control (port, PARPORT_CONTROL_STROBE, 0);

        return ret;
}

/* EPP mode, reverse channel, data. */
size_t parport_ieee1284_epp_read_data (struct parport *port,
                                       void *buffer, size_t len,
                                       int flags)
{
        unsigned char *bp = (unsigned char *) buffer;
        unsigned ret = 0;

        /* set EPP idle state (just to make sure) with strobe high */
        parport_frob_control (port,
                              PARPORT_CONTROL_STROBE |
                              PARPORT_CONTROL_AUTOFD |
                              PARPORT_CONTROL_SELECT |
                              PARPORT_CONTROL_INIT,
                              PARPORT_CONTROL_INIT);
        port->ops->data_reverse (port);
        for (; len > 0; len--, bp++) {
                /* Event 67: set nAutoFd (nDStrb) low */
                parport_frob_control (port,
                                      PARPORT_CONTROL_AUTOFD,
                                      PARPORT_CONTROL_AUTOFD);
                /* Event 58: wait for Busy to go high */
                if (parport_wait_peripheral (port, PARPORT_STATUS_BUSY, 0)) {
                        break;
                }

                *bp = parport_read_data (port);

                /* Event 63: set nAutoFd (nDStrb) high */
                parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0);

                /* Event 60: wait for Busy to go low */
                if (parport_poll_peripheral (port, PARPORT_STATUS_BUSY,
                                             PARPORT_STATUS_BUSY, 5)) {
                        break;
                }

                ret++;
        }
        port->ops->data_forward (port);

        return ret;
}

/* EPP mode, forward channel, addresses. */
size_t parport_ieee1284_epp_write_addr (struct parport *port,
                                        const void *buffer, size_t len,
                                        int flags)
{
        unsigned char *bp = (unsigned char *) buffer;
        size_t ret = 0;

        /* set EPP idle state (just to make sure) with strobe low */
        parport_frob_control (port,
                              PARPORT_CONTROL_STROBE |
                              PARPORT_CONTROL_AUTOFD |
                              PARPORT_CONTROL_SELECT |
                              PARPORT_CONTROL_INIT,
                              PARPORT_CONTROL_STROBE |
                              PARPORT_CONTROL_INIT);
        port->ops->data_forward (port);
        for (; len > 0; len--, bp++) {
                /* Event 56: Write data and set nAStrb low. */
                parport_write_data (port, *bp);
                parport_frob_control (port, PARPORT_CONTROL_SELECT,
                                      PARPORT_CONTROL_SELECT);

                /* Event 58: wait for busy (nWait) to go high */
                if (parport_poll_peripheral (port, PARPORT_STATUS_BUSY, 0, 10))
                        break;

                /* Event 59: set nAStrb high */
                parport_frob_control (port, PARPORT_CONTROL_SELECT, 0);

                /* Event 60: wait for busy (nWait) to go low */
                if (parport_poll_peripheral (port, PARPORT_STATUS_BUSY,
                                             PARPORT_STATUS_BUSY, 5))
                        break;

                ret++;
        }

        /* Event 61: set strobe (nWrite) high */
        parport_frob_control (port, PARPORT_CONTROL_STROBE, 0);

        return ret;
}

/* EPP mode, reverse channel, addresses. */
size_t parport_ieee1284_epp_read_addr (struct parport *port,
                                       void *buffer, size_t len,
                                       int flags)
{
        unsigned char *bp = (unsigned char *) buffer;
        unsigned ret = 0;

        /* Set EPP idle state (just to make sure) with strobe high */
        parport_frob_control (port,
                              PARPORT_CONTROL_STROBE |
                              PARPORT_CONTROL_AUTOFD |
                              PARPORT_CONTROL_SELECT |
                              PARPORT_CONTROL_INIT,
                              PARPORT_CONTROL_INIT);
        port->ops->data_reverse (port);
        for (; len > 0; len--, bp++) {
                /* Event 64: set nSelectIn (nAStrb) low */
                parport_frob_control (port, PARPORT_CONTROL_SELECT,
                                      PARPORT_CONTROL_SELECT);

                /* Event 58: wait for Busy to go high */
                if (parport_wait_peripheral (port, PARPORT_STATUS_BUSY, 0)) {
                        break;
                }

                *bp = parport_read_data (port);

                /* Event 59: set nSelectIn (nAStrb) high */
                parport_frob_control (port, PARPORT_CONTROL_SELECT,
                                      0);

                /* Event 60: wait for Busy to go low */
                if (parport_poll_peripheral (port, PARPORT_STATUS_BUSY, 
                                             PARPORT_STATUS_BUSY, 5))
                        break;

                ret++;
        }
        port->ops->data_forward (port);

        return ret;
}

EXPORT_SYMBOL(parport_ieee1284_ecp_write_data);
EXPORT_SYMBOL(parport_ieee1284_ecp_read_data);
EXPORT_SYMBOL(parport_ieee1284_ecp_write_addr);
EXPORT_SYMBOL(parport_ieee1284_write_compat);
EXPORT_SYMBOL(parport_ieee1284_read_nibble);
EXPORT_SYMBOL(parport_ieee1284_read_byte);
EXPORT_SYMBOL(parport_ieee1284_epp_write_data);
EXPORT_SYMBOL(parport_ieee1284_epp_read_data);
EXPORT_SYMBOL(parport_ieee1284_epp_write_addr);
EXPORT_SYMBOL(parport_ieee1284_epp_read_addr);