Here are my latest patches to add ECP mode to lp and to deal with
hardware that doesn't like to be disturbed while data is in the FIFO.
parport_pc must be built with CONFIG_PARPORT_PC_FIFO on for these
patches to take effect, and the parport_pc option "irq" must be set to
"auto". If the parport_pc option "dma" is set to "auto", DMA will be
used to write data to the FIFO, otherwise PIO will be used. For
example, in /etc/modules.conf:
options parport_pc irq=auto dma=auto
- or -
options parport_pc irq=auto dma=none
-- Dave Strauss
Index: drivers/parport/parport_pc.c
===================================================================
RCS file: drivers/parport/RCS/parport_pc.c,v
retrieving revision 1.1
diff -up -r1.1 drivers/parport/parport_pc.c
--- drivers/parport/parport_pc.c 2001/09/12 16:42:38 1.1
+++ drivers/parport/parport_pc.c 2001/09/14 21:28:57
@@ -531,7 +531,7 @@ static size_t parport_pc_fifo_write_bloc
if (!time_before (jiffies, expire)) {
/* Timed out. */
printk (KERN_DEBUG "FIFO write timed out\n");
- break;
+ expire = jiffies + port->cad->timeout;
}
ecrval = inb (ECONTROL (port));
if (!(ecrval & (1<<2))) {
@@ -652,7 +652,7 @@ static size_t parport_pc_fifo_write_bloc
if (!time_before (jiffies, expire)) {
/* Timed out. */
printk (KERN_DEBUG "DMA write timed out\n");
- break;
+ expire = jiffies + port->cad->timeout;
}
/* Is serviceIntr set? */
if (!(inb (ECONTROL (port)) & (1<<2))) {
@@ -1717,7 +1717,6 @@ static int __devinit parport_ECP_support
/* Go back to mode 000 */
frob_econtrol (pb, 0xe0, ECR_SPP << 5);
- pb->modes |= PARPORT_MODE_ECP | PARPORT_MODE_COMPAT;
return 1;
}
@@ -2093,6 +2092,7 @@ struct parport *__devinit parport_pc_pro
#ifdef CONFIG_PARPORT_PC_FIFO
if (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;
Index: drivers/char/lp.c
===================================================================
RCS file: drivers/char/RCS/lp.c,v
retrieving revision 1.1
diff -up -r1.1 drivers/char/lp.c
--- drivers/char/lp.c 2001/08/17 15:28:11 1.1
+++ drivers/char/lp.c 2001/09/14 17:49:21
@@ -149,6 +149,10 @@ static unsigned int lp_count = 0;
#undef LP_DEBUG
+/* Bits used to manage claiming the parport device */
+#define LP_PREEMPT_REQUEST 1
+#define LP_PARPORT_CLAIMED 2
+
/* --- low-level port access ----------------------------------- */
#define r_dtr(x) (parport_read_data(lp_table[(x)].dev->port))
@@ -156,15 +160,55 @@ static unsigned int lp_count = 0;
#define w_ctr(x,y) do { parport_write_control(lp_table[(x)].dev->port, (y)); } while (0)
#define w_dtr(x,y) do { parport_write_data(lp_table[(x)].dev->port, (y)); } while (0)
+/* Claim the parport or block trying unless we've already claimed it */
+static void lp_claim_parport_or_block(struct lp_struct *this_lp)
+{
+ if (!test_and_set_bit(LP_PARPORT_CLAIMED, &this_lp->bits)) {
+ parport_claim_or_block (this_lp->dev);
+ }
+}
+
+/* Claim the parport or block trying unless we've already claimed it */
+static void lp_release_parport(struct lp_struct *this_lp)
+{
+ if (test_and_clear_bit(LP_PARPORT_CLAIMED, &this_lp->bits)) {
+ parport_release (this_lp->dev);
+ }
+}
+
+
+
+static int lp_preempt(void *handle)
+{
+ struct lp_struct *this_lp = (struct lp_struct *)handle;
+ set_bit(LP_PREEMPT_REQUEST, &this_lp->bits);
+ return (1);
+}
+
+
+/*
+ * Try to negotiate to a new mode; if unsuccessful negotiate to
+ * compatibility mode. Return the mode we ended up in.
+ */
+static int lp_negotiate(struct parport * port, int mode)
+{
+ if (parport_negotiate (port, mode) != 0) {
+ mode = IEEE1284_MODE_COMPAT;
+ parport_negotiate (port, mode);
+ }
+
+ return (mode);
+}
+
static int lp_reset(int minor)
{
int retval;
- parport_claim_or_block (lp_table[minor].dev);
+ lp_claim_parport_or_block (&lp_table[minor]);
w_ctr(minor, LP_PSELECP);
udelay (LP_DELAY);
w_ctr(minor, LP_PSELECP | LP_PINITP);
retval = r_str(minor);
- parport_release (lp_table[minor].dev);
+ lp_release_parport (&lp_table[minor]);
return retval;
}
@@ -176,10 +220,10 @@ static void lp_error (int minor)
return;
polling = lp_table[minor].dev->port->irq == PARPORT_IRQ_NONE;
- if (polling) parport_release (lp_table[minor].dev);
+ if (polling) lp_release_parport (&lp_table[minor]);
interruptible_sleep_on_timeout (&lp_table[minor].waitq,
LP_TIMEOUT_POLLED);
- if (polling) parport_claim_or_block (lp_table[minor].dev);
+ if (polling) lp_claim_parport_or_block (&lp_table[minor]);
else parport_yield_blocking (lp_table[minor].dev);
}
@@ -225,6 +269,12 @@ static int lp_check_status(int minor)
static int lp_wait_ready(int minor)
{
int error = 0;
+
+ /* If we're not in compatibility mode, we're ready now! */
+ if (lp_table[minor].current_mode != IEEE1284_MODE_COMPAT) {
+ return (0);
+ }
+
do {
error = lp_check_status (minor);
if (error && (LP_F(minor) & LP_ABORT))
@@ -266,10 +316,10 @@ static ssize_t lp_write(struct file * fi
/* Claim Parport or sleep until it becomes available
*/
- parport_claim_or_block (lp_table[minor].dev);
-
- /* Go to compatibility mode. */
- parport_negotiate (port, IEEE1284_MODE_COMPAT);
+ lp_claim_parport_or_block (&lp_table[minor]);
+ /* Go to the proper mode. */
+ lp_table[minor].current_mode = lp_negotiate (port,
+ lp_table[minor].best_mode);
parport_set_timeout (lp_table[minor].dev,
lp_table[minor].timeout);
@@ -294,7 +344,13 @@ static ssize_t lp_write(struct file * fi
if (copy_size > 0) {
/* incomplete write -> check error ! */
- int error = lp_wait_ready (minor);
+ int error;
+
+ parport_negotiate (lp_table[minor].dev->port,
+ IEEE1284_MODE_COMPAT);
+ lp_table[minor].current_mode = IEEE1284_MODE_COMPAT;
+
+ error = lp_wait_ready (minor);
if (error) {
if (retv == 0)
@@ -303,6 +359,10 @@ static ssize_t lp_write(struct file * fi
}
parport_yield_blocking (lp_table[minor].dev);
+ lp_table[minor].current_mode
+ = lp_negotiate (port,
+ lp_table[minor].best_mode);
+
} else if (current->need_resched)
schedule ();
@@ -319,7 +379,14 @@ static ssize_t lp_write(struct file * fi
}
} while (count > 0);
- parport_release (lp_table[minor].dev);
+ if (test_and_clear_bit(LP_PREEMPT_REQUEST,
+ &lp_table[minor].bits)) {
+ printk(KERN_INFO "lp%d releasing parport\n", minor);
+ parport_negotiate (lp_table[minor].dev->port,
+ IEEE1284_MODE_COMPAT);
+ lp_table[minor].current_mode = IEEE1284_MODE_COMPAT;
+ lp_release_parport (&lp_table[minor]);
+ }
up (&lp_table[minor].port_mutex);
@@ -343,9 +410,9 @@ static ssize_t lp_read(struct file * fil
if (down_interruptible (&lp_table[minor].port_mutex))
return -EINTR;
- parport_claim_or_block (lp_table[minor].dev);
+ lp_claim_parport_or_block (&lp_table[minor]);
retval = parport_read (port, kbuf, count);
- parport_release (lp_table[minor].dev);
+ lp_release_parport (&lp_table[minor]);
if (retval > 0 && copy_to_user (buf, kbuf, retval))
retval = -EFAULT;
@@ -375,9 +442,9 @@ static int lp_open(struct inode * inode,
should most likely only ever be used by the tunelp application. */
if ((LP_F(minor) & LP_ABORTOPEN) && !(file->f_flags & O_NONBLOCK)) {
int status;
- parport_claim_or_block (lp_table[minor].dev);
+ lp_claim_parport_or_block (&lp_table[minor]);
status = r_str(minor);
- parport_release (lp_table[minor].dev);
+ lp_release_parport (&lp_table[minor]);
if (status & LP_POUTPA) {
printk(KERN_INFO "lp%d out of paper\n", minor);
LP_F(minor) &= ~LP_BUSY;
@@ -397,6 +464,21 @@ static int lp_open(struct inode * inode,
LP_F(minor) &= ~LP_BUSY;
return -ENOMEM;
}
+ /* Determine if the peripheral supports ECP mode */
+ lp_claim_parport_or_block (&lp_table[minor]);
+ if ( (lp_table[minor].dev->port->modes & PARPORT_MODE_ECP) &&
+ !parport_negotiate (lp_table[minor].dev->port,
+ IEEE1284_MODE_ECP)) {
+ printk (KERN_INFO "lp%d: ECP mode\n", minor);
+ lp_table[minor].best_mode = IEEE1284_MODE_ECP;
+ } else {
+ printk (KERN_INFO "lp%d: compatibility mode\n", minor);
+ lp_table[minor].best_mode = IEEE1284_MODE_COMPAT;
+ }
+ /* Leave peripheral in compatibility mode */
+ parport_negotiate (lp_table[minor].dev->port, IEEE1284_MODE_COMPAT);
+ lp_release_parport (&lp_table[minor]);
+ lp_table[minor].current_mode = IEEE1284_MODE_COMPAT;
return 0;
}
@@ -404,6 +486,10 @@ static int lp_release(struct inode * ino
{
unsigned int minor = MINOR(inode->i_rdev);
+ lp_claim_parport_or_block (&lp_table[minor]);
+ parport_negotiate (lp_table[minor].dev->port, IEEE1284_MODE_COMPAT);
+ lp_table[minor].current_mode = IEEE1284_MODE_COMPAT;
+ lp_release_parport (&lp_table[minor]);
lock_kernel();
kfree(lp_table[minor].lp_buffer);
lp_table[minor].lp_buffer = NULL;
@@ -466,9 +552,9 @@ static int lp_ioctl(struct inode *inode,
return -EFAULT;
break;
case LPGETSTATUS:
- parport_claim_or_block (lp_table[minor].dev);
+ lp_claim_parport_or_block (&lp_table[minor]);
status = r_str(minor);
- parport_release (lp_table[minor].dev);
+ lp_release_parport (&lp_table[minor]);
if (copy_to_user((int *) arg, &status, sizeof(int)))
return -EFAULT;
@@ -657,7 +743,7 @@ static int lp_register(int nr, struct pa
char name[8];
lp_table[nr].dev = parport_register_device(port, "lp",
- NULL, NULL, NULL, 0,
+ lp_preempt, NULL, NULL, 0,
(void *) &lp_table[nr]);
if (lp_table[nr].dev == NULL)
return 1;
Index: include/linux/lp.h
===================================================================
RCS file: include/linux/RCS/lp.h,v
retrieving revision 1.1
diff -up -r1.1 include/linux/lp.h
--- include/linux/lp.h 2001/08/17 16:52:32 1.1
+++ include/linux/lp.h 2001/09/10 20:44:13
@@ -145,6 +145,9 @@ struct lp_struct {
struct semaphore port_mutex;
wait_queue_head_t dataq;
long timeout;
+ unsigned int best_mode;
+ unsigned int current_mode;
+ unsigned long bits;
};
/*
-- To unsubscribe, send mail to: linux-parport-request@torque.net --
-- with the single word "unsubscribe" in the body of the message. --
This archive was generated by hypermail 2b29 : Mon Sep 17 2001 - 16:25:05 EDT