summaryrefslogtreecommitdiffstats
path: root/drivers/pnp
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1997-04-29 21:13:14 +0000
committer <ralf@linux-mips.org>1997-04-29 21:13:14 +0000
commit19c9bba94152148523ba0f7ef7cffe3d45656b11 (patch)
tree40b1cb534496a7f1ca0f5c314a523c69f1fee464 /drivers/pnp
parent7206675c40394c78a90e74812bbdbf8cf3cca1be (diff)
Import of Linux/MIPS 2.1.36
Diffstat (limited to 'drivers/pnp')
-rw-r--r--drivers/pnp/BUGS-parport13
-rw-r--r--drivers/pnp/Config.in16
-rw-r--r--drivers/pnp/Makefile51
-rw-r--r--drivers/pnp/TODO-parport21
-rw-r--r--drivers/pnp/parport_init.c831
-rw-r--r--drivers/pnp/parport_ll_io.h21
-rw-r--r--drivers/pnp/parport_probe.c266
-rw-r--r--drivers/pnp/parport_procfs.c340
-rw-r--r--drivers/pnp/parport_share.c469
9 files changed, 2028 insertions, 0 deletions
diff --git a/drivers/pnp/BUGS-parport b/drivers/pnp/BUGS-parport
new file mode 100644
index 000000000..6b8420a2c
--- /dev/null
+++ b/drivers/pnp/BUGS-parport
@@ -0,0 +1,13 @@
+Currently known (or at least suspected) bugs in parport:
+
+o /proc/parport is untested under 2.0.XX
+
+o SCSI aborts for PPA under 2.0.29 [reported by jmr]. Under investigation.
+
+o make config (etc) allow you to select CONFIG_PNP_PARPORT=m, CONFIG_PPA=y -
+ the resulting kernel won't link.
+
+o IEEE1284 code does not do the terminating handshake after transfers, which
+ seems to upset some devices.
+
+o lp doesn't allow you to read status while printing is in progress.
diff --git a/drivers/pnp/Config.in b/drivers/pnp/Config.in
new file mode 100644
index 000000000..44bb52d68
--- /dev/null
+++ b/drivers/pnp/Config.in
@@ -0,0 +1,16 @@
+#
+# Plug and Play configuration
+#
+
+mainmenu_option next_comment
+comment 'Plug and Play support'
+
+bool 'Plug and Play support' CONFIG_PNP
+
+if [ "$CONFIG_PNP" = "y" ]; then
+ if [ "$CONFIG_PNP_PARPORT" != "n" ]; then
+ bool ' Auto-probe for parallel devices' CONFIG_PNP_PARPORT_AUTOPROBE
+ fi
+fi
+
+endmenu
diff --git a/drivers/pnp/Makefile b/drivers/pnp/Makefile
new file mode 100644
index 000000000..6153b8da6
--- /dev/null
+++ b/drivers/pnp/Makefile
@@ -0,0 +1,51 @@
+#
+# Makefile for the kernel Plug-and-Play device drivers.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now inherited from the
+# parent makes..
+#
+# Note 3! Plug and Play is the Borg. We have assimilated some other
+# drivers in the `char', `net' and `scsi' directories, but left them
+# there to allay suspicion.
+
+SUB_DIRS :=
+MOD_SUB_DIRS := $(SUB_DIRS)
+ALL_SUB_DIRS := $(SUB_DIRS)
+
+L_TARGET := pnp.a
+MX_OBJS :=
+LX_OBJS :=
+MI_OBJS :=
+MIX_OBJS :=
+
+ifeq ($(CONFIG_PNP_PARPORT),y)
+ L_OBJS += parport_share.o
+ ifeq ($(CONFIG_PROC_FS),y)
+ L_OBJS += parport_procfs.o
+ endif
+ ifeq ($(CONFIG_PNP_PARPORT_AUTOPROBE),y)
+ L_OBJS += parport_probe.o
+ endif
+ LX_OBJS += parport_init.o
+else
+ ifeq ($(CONFIG_PNP_PARPORT),m)
+ MI_OBJS += parport_share.o
+ ifneq ($(CONFIG_PROC_FS),n)
+ MI_OBJS += parport_procfs.o
+ endif
+ ifeq ($(CONFIG_PNP_PARPORT_AUTOPROBE),y)
+ MI_OBJS += parport_probe.o
+ endif
+ MIX_OBJS += parport_init.o
+ M_OBJS += parport.o
+ endif
+endif
+
+include $(TOPDIR)/Rules.make
+
+parport.o: $(MI_OBJS) $(MIX_OBJS)
+ $(LD) $(LD_RFLAG) -r -o $@ $(MI_OBJS) $(MIX_OBJS)
diff --git a/drivers/pnp/TODO-parport b/drivers/pnp/TODO-parport
new file mode 100644
index 000000000..eea6a01fd
--- /dev/null
+++ b/drivers/pnp/TODO-parport
@@ -0,0 +1,21 @@
+Things to be done.
+
+0. Fix the bugs (see BUGS-parport).
+
+1. Proper documentation.
+
+2. Overhaul lp.c:
+
+ a) It's a mess, and there is a lot of code duplication.
+
+ b) ECP support would be nice. This can only work if both the port and
+ the printer support it.
+
+ c) Errors could do with being handled better. There's no point logging a
+ message every 10 seconds when the printer is out of paper.
+
+ d) Handle status readback automatically. IEEE1284 printers can post status
+ bits when they have something to say. We should read out and deal
+ with (maybe just log) whatever the printer wants to tell the world.
+
+3. Assimilate more drivers.
diff --git a/drivers/pnp/parport_init.c b/drivers/pnp/parport_init.c
new file mode 100644
index 000000000..57574b6a5
--- /dev/null
+++ b/drivers/pnp/parport_init.c
@@ -0,0 +1,831 @@
+/* $Id: parport_init.c,v 1.3.2.4 1997/04/16 21:20:44 phil Exp $
+ * Parallel-port initialisation code.
+ *
+ * Authors: David Campbell <campbell@tirian.che.curtin.edu.au>
+ * Tim Waugh <tmw20@cam.ac.uk>
+ * Jose Renau <renau@acm.org>
+ *
+ * based on work by Grant Guenther <grant@torque.net>
+ * and Philip Blundell <Philip.Blundell@pobox.com>
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/tasks.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <linux/parport.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+
+#include "parport_ll_io.h"
+
+static int io[PARPORT_MAX] = { 0, };
+static int irq[PARPORT_MAX] = { PARPORT_IRQ_NONE, };
+static int dma[PARPORT_MAX] = { PARPORT_DMA_NONE, };
+
+/******************************************************
+ * DMA detection section:
+ */
+
+/*
+ * Prepare DMA channels from 0-8 to transmit towards buffer
+ */
+static int parport_prepare_dma(char *buff, int size)
+{
+ int tmp = 0;
+ int i,retv;
+
+ for (i = 0; i < 8; i++) {
+ retv = request_dma(i, "probe");
+ if (retv)
+ continue;
+ tmp |= 1 << i;
+
+ cli();
+ disable_dma(i);
+ clear_dma_ff(i);
+ set_dma_addr(i, virt_to_bus(buff));
+ set_dma_count(i, size);
+ set_dma_mode(i, DMA_MODE_READ);
+ sti();
+ }
+
+ return tmp;
+}
+
+/*
+ * Activate all DMA channels passed in dma
+ */
+static int parport_enable_dma(int dma)
+{
+ int i;
+
+ for (i = 0; i < 8; i++)
+ if (dma & (1 << i)) {
+ cli();
+ enable_dma(i);
+ sti();
+ }
+
+ return dma;
+}
+
+static int parport_detect_dma_transfer(int dma,int size,int *resid)
+{
+ int i,n,retv;
+ int count=0;
+
+ retv = -1;
+ for (i = 0; i < 8; i++)
+ if (dma & (1 << i)) {
+ disable_dma(i);
+ clear_dma_ff(i);
+ n = get_dma_residue(i);
+ if (n != size) {
+ *resid = n;
+ retv = i;
+ if (count > 0) {
+ retv = -1; /* Multiple DMA's */
+ printk(KERN_ERR "parport: multiple DMA detected. Huh?\n");
+ }
+ count++;
+ }
+ free_dma(i);
+ }
+
+ return retv;
+}
+
+/* Only if supports ECP mode */
+static int programmable_dma_support(struct parport *pb)
+{
+ int dma;
+
+ w_ecr(pb,0xE0); /* Configuration MODE */
+
+ dma = r_cnfgB(pb) & 0x07;
+
+ w_ecr(pb,pb->ecr);
+
+ if( dma == 0 || dma == 4 ) /* Jumper selection */
+ return -1;
+ else
+ return dma;
+}
+
+/* Only called if port supports ECP mode.
+ *
+ * The only restriction on DMA channels is that it has to be
+ * between 0 to 7 (inclusive). Used only in an ECP mode, DMAs are
+ * considered a shared resource and hence they should be registered
+ * when needed and then immediately unregistered.
+ *
+ * DMA autoprobes for ECP mode are known not to work for some
+ * main board BIOS configs. I had to remove everything from the
+ * port, set the mode to SPP, reboot to DOS, set the mode to ECP,
+ * and reboot again, then I got IRQ probes and DMA probes to work.
+ * [Is the BIOS doing a device detection?]
+ *
+ * A value of -1 is allowed indicating no DMA support.
+ *
+ * if( 0 < DMA < 4 )
+ * 1Byte DMA transfer
+ * else // 4 < DMA < 8
+ * 2Byte DMA transfer
+ *
+ */
+static int parport_dma_probe(struct parport *pb)
+{
+ int dma,retv;
+ int dsr,dsr_read;
+ char *buff;
+
+ retv = programmable_dma_support(pb);
+ if (retv != -1)
+ return retv;
+
+ if (!(buff = kmalloc(2048, GFP_KERNEL | GFP_DMA))) {
+ printk(KERN_ERR "parport: memory squeeze\n");
+ return -1;
+ }
+
+ dsr = r_ctr(pb);
+ dsr_read = (dsr & ~(0x20)) | 0x04; /* Direction == read */
+
+ w_ecr(pb, 0xc0); /* ECP MODE */
+ w_ctr(pb, dsr_read );
+ dma=parport_prepare_dma(buff,1000);
+ w_ecr(pb, 0xd8); /* ECP FIFO + enable DMA */
+ parport_enable_dma(dma);
+ udelay(500); /* Give some for DMA tranfer */
+ retv = parport_detect_dma_transfer(dma,1000,&pb->speed);
+ pb->speed = pb->speed * 2000; /* 500uSec * 2000 = 1sec */
+
+ /*
+ * National Semiconductors only supports DMA tranfers
+ * in ECP MODE
+ */
+ if (retv == -1) {
+ w_ecr(pb, 0x60); /* ECP MODE */
+ w_ctr(pb, dsr_read );
+ dma=parport_prepare_dma(buff,1000);
+ w_ecr(pb, 0x68); /* ECP FIFO + enable DMA */
+ parport_enable_dma(dma);
+ udelay(500); /* Give some for DMA tranfer */
+ retv = parport_detect_dma_transfer(dma,1000,&pb->speed);
+ pb->speed = pb->speed * 2000; /* 500uSec * 2000 = 1sec */
+ }
+
+ kfree(buff);
+
+ w_ctr(pb, pb->ctr);
+ w_ecr(pb, pb->ecr);
+
+ return retv;
+}
+/******************************************************
+ * MODE detection section:
+ */
+
+/*
+ * Clear TIMEOUT BIT in EPP MODE
+ */
+static int epp_clear_timeout(struct parport *pb)
+{
+ int r;
+
+ if (!(r_str(pb) & 0x01))
+ return 1;
+
+ /* To clear timeout some chips require double read */
+ r_str(pb);
+ r = r_str(pb);
+ w_str(pb, r | 0x01); /* Some reset by writing 1 */
+ w_str(pb, r & 0xfe); /* Others by writing 0 */
+ r = r_str(pb);
+
+ return !(r & 0x01);
+}
+
+
+/*
+ * Checks for port existence, all ports support SPP MODE
+ */
+static int parport_SPP_supported(struct parport *pb)
+{
+ /* Do a simple read-write test to make sure the port exists. */
+
+ w_dtr(pb, 0xaa);
+ if (r_dtr(pb) != 0xaa) return 0;
+
+ w_dtr(pb, 0x55);
+ if (r_dtr(pb) != 0x55) return 0;
+
+ return PARPORT_MODE_SPP;
+}
+
+/* Check for ECP
+ *
+ * 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 will write 0x2c to ECR and 0xcc to CTR since both of these
+ * values are "safe" on the CTR since bits 6-7 of CTR are unused.
+ */
+static int parport_ECR_present(struct parport *pb)
+{
+ int r;
+
+ r= r_ctr(pb);
+ if ((r_ecr(pb) & 0x03) == (r & 0x03)) {
+ w_ctr(pb, r ^ 0x03 ); /* Toggle bits 0-1 */
+
+ r= r_ctr(pb);
+ if ((r_ecr(pb) & 0x03) == (r & 0x03))
+ return 0; /* Sure that no ECR register exists */
+ }
+
+ if ((r_ecr(pb) & 0x03 ) != 0x01)
+ return 0;
+
+ w_ecr(pb,0x34);
+ if (r_ecr(pb) != 0x35)
+ return 0;
+
+ w_ecr(pb,pb->ecr);
+ w_ctr(pb,pb->ctr);
+
+ return PARPORT_MODE_ECR;
+}
+
+static int parport_ECP_supported(struct parport *pb)
+{
+ int i;
+
+ /* If there is no ECR, we have no hope of supporting ECP. */
+ if (!(pb->modes & PARPORT_MODE_ECR))
+ return 0;
+
+ /*
+ * Using LGS chipset it uses ECR register, but
+ * it doesn't support ECP or FIFO MODE
+ */
+
+ w_ecr(pb, 0xc0); /* TEST FIFO */
+ for (i=0; i < 1024 && (r_ecr(pb) & 0x01); i++)
+ w_fifo(pb, 0xaa);
+
+ w_ecr(pb, pb->ecr);
+
+ if (i == 1024)
+ return 0;
+
+ return PARPORT_MODE_ECP;
+}
+
+/* EPP mode detection
+ * 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.
+ */
+static int parport_EPP_supported(struct parport *pb)
+{
+ /* If EPP timeout bit clear then EPP available */
+ if (!epp_clear_timeout(pb))
+ return 0; /* No way to clear timeout */
+
+ w_ctr(pb, r_ctr(pb) | 0x20);
+ w_ctr(pb, r_ctr(pb) | 0x10);
+ epp_clear_timeout(pb);
+
+ r_epp(pb);
+ udelay(30); /* Wait for possible EPP timeout */
+
+ if (r_str(pb) & 0x01) {
+ epp_clear_timeout(pb);
+ return PARPORT_MODE_EPP;
+ }
+
+ return 0;
+}
+
+static int parport_ECPEPP_supported(struct parport *pb)
+{
+ int mode;
+
+ if (!(pb->modes & PARPORT_MODE_ECR))
+ return 0;
+
+ /* Search for SMC style EPP+ECP mode */
+ w_ecr(pb, 0x80);
+
+ mode = parport_EPP_supported(pb);
+
+ w_ecr(pb, pb->ecr);
+
+ if (mode)
+ return PARPORT_MODE_ECPEPP;
+
+ return 0;
+}
+
+/* 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;
+
+ epp_clear_timeout(pb);
+
+ w_ctr(pb, pb->ctr | 0x20); /* try to tri-state the buffer */
+
+ w_dtr(pb, 0x55);
+ if (r_dtr(pb) != 0x55) ok++;
+
+ w_dtr(pb, 0xaa);
+ if (r_dtr(pb) != 0xaa) ok++;
+
+ w_ctr(pb, pb->ctr); /* cancel input mode */
+
+ return ok?PARPORT_MODE_PS2:0;
+}
+
+static int parport_ECPPS2_supported(struct parport *pb)
+{
+ int mode;
+
+ if (!(pb->modes & PARPORT_MODE_ECR))
+ return 0;
+
+ w_ecr(pb, 0x20);
+
+ mode = parport_PS2_supported(pb);
+
+ w_ecr(pb,pb->ecr);
+
+ if (mode)
+ return PARPORT_MODE_ECPPS2;
+
+ return 0;
+}
+
+/******************************************************
+ * IRQ detection section:
+ *
+ * This code is for detecting ECP interrupts (due to problems with the
+ * monolithic interrupt probing routines).
+ *
+ * In short this is a voting system where the interrupt with the most
+ * "votes" is the elected interrupt (it SHOULD work...)
+ *
+ * This is horribly x86-specific at the moment. I'm not convinced it
+ * belongs at all.
+ */
+
+static int intr_vote[16];
+
+static void parport_vote_intr_func(int irq, void *dev_id, struct pt_regs *regs)
+{
+ intr_vote[irq]++;
+ return;
+}
+
+static long open_intr_election(void)
+{
+ long tmp = 0;
+ int i;
+
+ /* We ignore the timer - irq 0 */
+ for (i = 1; i < 16; i++) {
+ intr_vote[i] = 0;
+ if (request_irq(i, parport_vote_intr_func,
+ SA_INTERRUPT, "probe", intr_vote) == 0)
+ tmp |= 1 << i;
+ }
+ return tmp;
+}
+
+static int close_intr_election(long tmp)
+{
+ int irq = PARPORT_IRQ_NONE;
+ int i;
+
+ /* We ignore the timer - irq 0 */
+ for (i = 1; i < 16; i++)
+ if (tmp & (1 << i)) {
+ if (intr_vote[i]) {
+ if (irq != PARPORT_IRQ_NONE)
+ /* More than one interrupt */
+ return PARPORT_IRQ_NONE;
+ irq = i;
+ }
+ free_irq(i, intr_vote);
+ }
+ return irq;
+}
+
+/* Only if supports ECP mode */
+static int programmable_irq_support(struct parport *pb)
+{
+ int irq;
+
+ w_ecr(pb,0xE0); /* Configuration MODE */
+
+ irq = (r_cnfgB(pb) >> 3) & 0x07;
+
+ switch(irq){
+ case 2:
+ irq = 9;
+ break;
+ case 7:
+ irq = 5;
+ break;
+ case 0:
+ irq = -1;
+ break;
+ default:
+ irq += 7;
+ }
+
+ w_ecr(pb,pb->ecr);
+
+ return irq;
+}
+
+static int irq_probe_ECP(struct parport *pb)
+{
+ int irqs,i;
+
+ probe_irq_off(probe_irq_on()); /* Clear any interrupts */
+ irqs = open_intr_election();
+
+ w_ecr(pb, 0x00); /* Reset FIFO */
+ w_ctr(pb, pb->ctr ); /* Force direction = 0 */
+ w_ecr(pb, 0xd0); /* TEST FIFO + nErrIntrEn */
+
+ /* If Full FIFO sure that WriteIntrThresold is generated */
+ for( i=0 ; i < 1024 && !(r_ecr(pb) & 0x02) ; i++ ){
+ w_fifo(pb, 0xaa);
+ }
+
+ pb->irq = close_intr_election(irqs);
+
+ w_ecr(pb, pb->ecr);
+
+ return pb->irq;
+}
+
+/*
+ * This detection seems that only works in National Semiconductors
+ * This doesn't work in SMC, LGS, and Winbond
+ */
+static int irq_probe_EPP(struct parport *pb)
+{
+ int irqs;
+
+#ifndef ADVANCED_DETECT
+ return -1;
+#endif
+
+ probe_irq_off(probe_irq_on()); /* Clear any interrupts */
+ irqs = open_intr_election();
+
+ if( pb->modes & PARPORT_MODE_ECR )
+ w_ecr(pb, r_ecr(pb) | 0x10 );
+
+ epp_clear_timeout(pb);
+ w_ctr(pb, r_ctr(pb) | 0x20);
+ w_ctr(pb, r_ctr(pb) | 0x10);
+ epp_clear_timeout(pb);
+
+ /* Device isn't expecting an EPP read
+ * and generates an IRQ.
+ */
+ r_epp(pb);
+ udelay(20);
+
+ pb->irq = close_intr_election(irqs);
+
+ w_ctr(pb,pb->ctr);
+
+ return pb->irq;
+}
+
+static int irq_probe_SPP(struct parport *pb)
+{
+ int irqs;
+
+#ifndef ADVANCED_DETECT
+ return -1;
+#endif
+
+ probe_irq_off(probe_irq_on()); /* Clear any interrupts */
+ irqs = probe_irq_on();
+
+ if( pb->modes & PARPORT_MODE_ECR )
+ w_ecr(pb, 0x10 );
+
+ w_dtr(pb,0x00);
+ w_ctr(pb,0x00);
+ w_ctr(pb,0x0c);
+ udelay(5);
+ w_ctr(pb,0x0d);
+ udelay(5);
+ w_ctr(pb,0x0c);
+ udelay(25);
+ w_ctr(pb,0x08);
+ udelay(25);
+ w_ctr(pb,0x0c);
+ udelay(50);
+
+ pb->irq = probe_irq_off(irqs);
+ if (pb->irq <= 0)
+ pb->irq = PARPORT_IRQ_NONE; /* No interrupt detected */
+
+ w_ctr(pb,pb->ctr);
+
+ return pb->irq;
+}
+
+/* We will attempt to share interrupt requests since other devices
+ * such as sound cards and network cards seem to like using the
+ * printer IRQs.
+ *
+ * When ECP is available we can autoprobe for IRQs.
+ * NOTE: If we can autoprobe it, we can register the IRQ.
+ */
+static int parport_irq_probe(struct parport *pb)
+{
+ if (pb->modes & PARPORT_MODE_ECR)
+ pb->irq = programmable_irq_support(pb);
+
+ if (pb->modes & PARPORT_MODE_ECP)
+ pb->irq = irq_probe_ECP(pb);
+
+ if (pb->irq == PARPORT_IRQ_NONE && (pb->modes & PARPORT_MODE_ECPEPP)) {
+ w_ecr(pb,0x80);
+ pb->irq = irq_probe_EPP(pb);
+ w_ecr(pb,pb->ecr);
+ }
+
+ epp_clear_timeout(pb);
+
+ if (pb->irq == PARPORT_IRQ_NONE && (pb->modes & PARPORT_MODE_EPP))
+ pb->irq = irq_probe_EPP(pb);
+
+ epp_clear_timeout(pb);
+
+ if (pb->irq == PARPORT_IRQ_NONE)
+ pb->irq = irq_probe_SPP(pb);
+
+ return pb->irq;
+}
+
+
+int initialize_parport(struct parport *pb, unsigned long base, int irq, int dma, int count)
+{
+ /* Check some parameters */
+ if (dma < -2) {
+ printk(KERN_ERR "parport: Invalid DMA[%d] at base 0x%lx\n",dma,base);
+ return 0;
+ }
+
+ if (irq < -2) {
+ printk(KERN_ERR "parport: Invalid IRQ[%d] at base 0x%lx\n",irq,base);
+ return 0;
+ }
+
+ /* Init our structure */
+ memset(pb, 0, sizeof(struct parport));
+ pb->base = base;
+ pb->irq = irq;
+ pb->dma = dma;
+ pb->modes = 0;
+ pb->next = NULL;
+ pb->devices = pb->cad = pb->lurker = NULL;
+ pb->flags = 0;
+
+ /* Before we start, set the control registers to something sensible. */
+ pb->ecr = 0xc;
+ pb->ctr = 0xc;
+
+ pb->name = kmalloc(15, GFP_KERNEL);
+ if (!pb->name) {
+ printk(KERN_ERR "parport: memory squeeze\n");
+ return 0;
+ }
+ sprintf(pb->name, "parport%d", count);
+
+ if (!parport_SPP_supported(pb)) {
+ epp_clear_timeout(pb);
+ if (!parport_SPP_supported(pb)) {
+ kfree(pb->name);
+ return 0;
+ }
+ }
+
+ pb->modes |= PARPORT_MODE_SPP; /* All ports support SPP mode. */
+ pb->modes |= parport_PS2_supported(pb);
+
+ if (pb->base != 0x3bc) {
+ pb->modes |= parport_ECR_present(pb);
+ pb->modes |= parport_ECP_supported(pb);
+ pb->modes |= parport_ECPPS2_supported(pb);
+ pb->modes |= parport_EPP_supported(pb);
+ pb->modes |= parport_ECPEPP_supported(pb);
+ }
+
+ /* Now register regions */
+ if ((pb->modes & (PARPORT_MODE_EPP | PARPORT_MODE_ECPEPP)) &&
+ (check_region(pb->base, 8))) {
+ printk(KERN_INFO "%s: EPP disabled due to port conflict at %x.\n", pb->name, pb->base + 3);
+ pb->modes &= ~(PARPORT_MODE_EPP | PARPORT_MODE_ECPEPP);
+ }
+ pb->size = (pb->modes & (PARPORT_MODE_EPP | PARPORT_MODE_ECPEPP)) ? 8 : 3;
+
+ request_region(pb->base, pb->size, pb->name);
+ if (pb->modes & PARPORT_MODE_ECR)
+ request_region(pb->base+0x400, 3, pb->name);
+
+ /* DMA check */
+ if (pb->modes & PARPORT_MODE_ECP) {
+ if (pb->dma == PARPORT_DMA_NONE)
+ pb->dma = parport_dma_probe(pb);
+ else if (pb->dma == -2)
+ pb->dma = PARPORT_DMA_NONE;
+ }
+
+ /* IRQ check */
+ if (pb->irq == PARPORT_IRQ_NONE)
+ pb->irq = parport_irq_probe(pb);
+ else if (pb->irq == -2)
+ pb->irq = PARPORT_IRQ_NONE;
+
+ return 1;
+}
+
+#ifndef MODULE
+static int parport_setup_ptr = 0;
+
+void parport_setup(char *str, int *ints)
+{
+ if (ints[0] == 0 || ints[1] == 0) {
+ /* Disable parport if "parport=" or "parport=0" in cmdline */
+ io[0] = PARPORT_DISABLE;
+ return;
+ }
+ if (parport_setup_ptr < PARPORT_MAX) {
+ io[parport_setup_ptr] = ints[1];
+ if (ints[0]>1) {
+ irq[parport_setup_ptr] = ints[2];
+ if (ints[0]>2) dma[parport_setup_ptr] = ints[3];
+ }
+ parport_setup_ptr++;
+ } else {
+ printk(KERN_ERR "parport=0x%x", ints[1]);
+ if (ints[0]>1) {
+ printk(",%d", ints[2]);
+ if (ints[0]>2) printk(",%d", ints[3]);
+ }
+ printk(" ignored, too many ports.\n");
+ }
+}
+#endif
+
+#ifdef CONFIG_PNP_PARPORT_AUTOPROBE
+extern void parport_probe_one(struct parport *port);
+#endif
+
+#ifdef MODULE
+MODULE_PARM(io, "1-" __MODULE_STRING(PARPORT_MAX) "i");
+MODULE_PARM(irq, "1-" __MODULE_STRING(PARPORT_MAX) "i");
+MODULE_PARM(dma, "1-" __MODULE_STRING(PARPORT_MAX) "i");
+
+int init_module(void)
+#else
+int pnp_parport_init(void)
+#endif /* MODULE */
+{
+ struct parport *pb;
+
+ printk(KERN_INFO "Parallel port sharing: %s\n",
+ "$Revision: 1.3.2.4 $");
+
+ if (io[0] == PARPORT_DISABLE) return 1;
+
+#ifdef CONFIG_PROC_FS
+ parport_proc_init();
+#endif
+
+ /* Run probes to ensure parport does exist */
+#define PORT(a,b,c) \
+ if ((pb = parport_register_port((a), (b), (c)))) \
+ parport_destroy(pb);
+
+
+ if (io[0]) {
+ /* If the user specified any ports, use them */
+ int i;
+ for (i = 0; io[i] && i < PARPORT_MAX; i++) {
+ PORT(io[i], irq[i], dma[i]);
+ }
+ } else {
+ /* Go for the standard ports. */
+ PORT(0x378, PARPORT_IRQ_NONE, PARPORT_DMA_NONE);
+ PORT(0x278, PARPORT_IRQ_NONE, PARPORT_DMA_NONE);
+ PORT(0x3bc, PARPORT_IRQ_NONE, PARPORT_DMA_NONE);
+#undef PORT
+ }
+
+#if defined(CONFIG_PNP_PARPORT_AUTOPROBE) || defined(CONFIG_PROC_FS)
+ for (pb = parport_enumerate(); pb; pb = pb->next) {
+#ifdef CONFIG_PNP_PARPORT_AUTOPROBE
+ parport_probe_one(pb);
+#endif
+#ifdef CONFIG_PROC_FS
+ parport_proc_register(pb);
+#endif
+ }
+#endif
+
+ return 0;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+ struct parport *port, *next;
+
+ for (port = parport_enumerate(); port; port = next) {
+ next = port->next;
+ parport_destroy(port);
+ parport_proc_unregister(port);
+ kfree(port->name);
+ kfree(port);
+ }
+
+ parport_proc_cleanup();
+}
+#endif
+
+/* Exported symbols for modules. */
+
+EXPORT_SYMBOL(parport_claim);
+EXPORT_SYMBOL(parport_release);
+EXPORT_SYMBOL(parport_register_port);
+EXPORT_SYMBOL(parport_destroy);
+EXPORT_SYMBOL(parport_register_device);
+EXPORT_SYMBOL(parport_unregister_device);
+EXPORT_SYMBOL(parport_enumerate);
+EXPORT_SYMBOL(parport_ieee1284_nibble_mode_ok);
+
+#ifdef CONFIG_PNP_PARPORT_AUTOPROBE
+EXPORT_SYMBOL(parport_probe);
+EXPORT_SYMBOL(parport_probe_one);
+#endif
+
+void inc_parport_count(void)
+{
+#ifdef MODULE
+ MOD_INC_USE_COUNT;
+#endif
+}
+
+void dec_parport_count(void)
+{
+#ifdef MODULE
+ MOD_DEC_USE_COUNT;
+#endif
+}
diff --git a/drivers/pnp/parport_ll_io.h b/drivers/pnp/parport_ll_io.h
new file mode 100644
index 000000000..c2592aff6
--- /dev/null
+++ b/drivers/pnp/parport_ll_io.h
@@ -0,0 +1,21 @@
+/* $Id: parport_ll_io.h,v 1.1.2.1 1997/03/26 13:01:09 phil Exp $
+ * David Campbell's "favourite IO routines" for parallel ports
+ */
+
+#define r_dtr(x) inb((x)->base)
+#define r_str(x) inb((x)->base+1)
+#define r_ctr(x) inb((x)->base+2)
+#define r_epp(x) inb((x)->base+4)
+#define r_fifo(x) inb((x)->base+0x400)
+#define r_ecr(x) inb((x)->base+0x402)
+#define r_cnfgA(x) inb((x)->base+0x400)
+#define r_cnfgB(x) inb((x)->base+0x401)
+
+#define w_dtr(x,y) outb((y), (x)->base)
+#define w_str(x,y) outb((y), (x)->base+1)
+#define w_ctr(x,y) outb((y), (x)->base+2)
+#define w_epp(x,y) outb((y), (x)->base+4)
+#define w_fifo(x,y) outb((y), (x)->base+0x400)
+#define w_ecr(x,y) outb((y), (x)->base+0x402)
+#define w_cnfgA(x,y) outb((y), (x)->base+0x400)
+#define w_cnfgB(x,y) outb((y), (x)->base+0x401)
diff --git a/drivers/pnp/parport_probe.c b/drivers/pnp/parport_probe.c
new file mode 100644
index 000000000..b40164496
--- /dev/null
+++ b/drivers/pnp/parport_probe.c
@@ -0,0 +1,266 @@
+/* $Id: parport_probe.c,v 1.1.2.9 1997/03/29 21:08:16 phil Exp $
+ * Parallel port device probing code
+ *
+ * Authors: Carsten Gross, carsten@sol.wohnheim.uni-ulm.de
+ * Philip Blundell <Philip.Blundell@pobox.com>
+ */
+
+#include <linux/tasks.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <linux/parport.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+#include <linux/ctype.h>
+
+#include <linux/lp.h>
+
+#include <asm/uaccess.h>
+#undef DEBUG_PROBE
+
+static inline int read_nibble(struct parport *port)
+{
+ unsigned char i;
+ i = parport_r_status(port)>>3;
+ i&=~8;
+ if ( ( i & 0x10) == 0) i|=8;
+ return(i & 0x0f);
+}
+
+static void read_terminate(struct parport *port) {
+ parport_w_ctrl(port, (parport_r_ctrl(port) & ~2) | 8);
+ /* SelectIN high, AutoFeed low */
+ if (parport_wait_peripheral(port, 0x80, 0))
+ /* timeout, SelectIN high, Autofeed low */
+ return;
+ parport_w_ctrl(port, parport_r_ctrl(port) | 2);
+ /* AutoFeed high */
+ parport_wait_peripheral(port, 0x80, 0x80);
+ /* no timeout possible, Autofeed low, SelectIN high */
+ parport_w_ctrl(port, (parport_r_ctrl(port) & ~2) | 8);
+ return;
+}
+
+static long read_polled(struct parport *port, char *buf,
+ unsigned long length)
+{
+ int i;
+ char *temp=buf;
+ int count = 0;
+ unsigned char z=0;
+ unsigned char Byte=0;
+
+ for (i=0; ; i++) {
+ parport_w_ctrl(port, parport_r_ctrl(port) | 2); /* AutoFeed high */
+ if (parport_wait_peripheral(port, 0x40, 0)) {
+ printk("%s: read1 timeout.\n", port->name);
+ parport_w_ctrl(port, parport_r_ctrl(port) & ~2);
+ break;
+ }
+ z = read_nibble(port);
+ parport_w_ctrl(port, parport_r_ctrl(port) & ~2); /* AutoFeed low */
+ if (parport_wait_peripheral(port, 0x40, 0x40)) {
+ printk("%s: read2 timeout.\n", port->name);
+ break;
+ }
+ if (( i & 1) != 0) {
+ Byte= (Byte | z<<4);
+ if (temp)
+ *(temp++) = Byte;
+ if (count++ == length)
+ temp = NULL;
+ /* Does the error line indicate end of data? */
+ if ((parport_r_status(port) & LP_PERRORP) == LP_PERRORP)
+ break;
+ } else Byte=z;
+ }
+ read_terminate(port);
+ return count;
+}
+
+static struct wait_queue *wait_q = NULL;
+
+static int wakeup(void *ref)
+{
+ struct ppd **dev = (struct ppd **)ref;
+
+ if (!wait_q || parport_claim(*dev))
+ return 1;
+
+ wake_up(&wait_q);
+ return 0;
+}
+
+int parport_probe(struct parport *port, char *buffer, int len)
+{
+ struct ppd *dev = parport_register_device(port, "IEEE 1284 probe",
+ NULL, wakeup, NULL,
+ PARPORT_DEV_TRAN, &dev);
+
+ int result = 0;
+
+ if (!dev) {
+ printk("%s: unable to register for probe.\n", port->name);
+ return -EINVAL;
+ }
+
+ if (parport_claim(dev)) {
+ sleep_on(&wait_q);
+ wait_q = NULL;
+ }
+
+ switch (parport_ieee1284_nibble_mode_ok(port, 4)) {
+ case 1:
+ current->state=TASK_INTERRUPTIBLE;
+ current->timeout=jiffies+1;
+ schedule(); /* HACK: wait 10ms because printer seems to
+ * ack wrong */
+ result = read_polled(port, buffer, len);
+ break;
+ case 0:
+ result = -EIO;
+ break;
+ }
+
+ parport_release(dev);
+ parport_unregister_device(dev);
+
+ return result;
+}
+
+static struct {
+ char *token;
+ char *descr;
+} classes[] = {
+ { "", "Legacy device" },
+ { "PRINTER", "Printer" },
+ { "MODEM", "Modem" },
+ { "NET", "Network device" },
+ { "HDC", "Hard disk" },
+ { "PCMCIA", "PCMCIA" },
+ { "MEDIA", "Multimedia device" },
+ { "FDC", "Floppy disk" },
+ { "PORTS", "Ports" },
+ { "SCANNER", "Scanner" },
+ { "DIGICAM", "Digital camera" },
+ { "", "Unknown device" },
+ { "", "Unspecified" },
+ { NULL, NULL }
+};
+
+static char *strdup(char *str)
+{
+ int n = strlen(str)+1;
+ char *s = kmalloc(n, GFP_KERNEL);
+ if (!s) return NULL;
+ return strcpy(s, str);
+}
+
+static void parse_data(struct parport *port, char *str)
+{
+ char *txt = kmalloc(strlen(str)+1, GFP_KERNEL);
+ char *p = txt, *q;
+ int guessed_class = PARPORT_CLASS_UNSPEC;
+
+ if (!txt) {
+ printk("%s probe: memory squeeze\n", port->name);
+ return;
+ }
+ strcpy(txt, str);
+ while (p) {
+ char *sep;
+ q = strchr(p, ';');
+ if (q) *q = 0;
+ sep = strchr(p, ':');
+ if (sep) {
+ char *u = p;
+ *(sep++) = 0;
+ while (*u) {
+ *u = toupper(*u);
+ u++;
+ }
+ if (!strcmp(p, "MFG") || !strcmp(p, "MANUFACTURER")) {
+ port->probe_info.mfr = strdup(sep);
+ } else if (!strcmp(p, "MDL") || !strcmp(p, "MODEL")) {
+ port->probe_info.model = strdup(sep);
+ } else if (!strcmp(p, "CLS") || !strcmp(p, "CLASS")) {
+ int i;
+ for (u = sep; *u; u++)
+ *u = toupper(*u);
+ for (i = 0; classes[i].token; i++) {
+ if (!strcmp(classes[i].token, sep)) {
+ port->probe_info.class = i;
+ goto rock_on;
+ }
+ }
+ printk(KERN_WARNING "%s probe: warning, class '%s' not understood.\n", port->name, sep);
+ port->probe_info.class = PARPORT_CLASS_OTHER;
+ } else if (!strcmp(p, "CMD") || !strcmp(p, "COMMAND SET")) {
+ /* if it speaks printer language, it's
+ probably a printer */
+ if (strstr(sep, "PJL") || strstr(sep, "PCL"))
+ guessed_class = PARPORT_CLASS_PRINTER;
+ } else if (!strcmp(p, "DES") || !strcmp(p, "DESCRIPTION")) {
+ port->probe_info.description = strdup(sep);
+ }
+ }
+ rock_on:
+ if (q) p = q+1; else p=NULL;
+ }
+
+ /* If the device didn't tell us its class, maybe we have managed to
+ guess one from the things it did say. */
+ if (port->probe_info.class == PARPORT_CLASS_UNSPEC)
+ port->probe_info.class = guessed_class;
+
+ kfree(txt);
+}
+
+static void pretty_print(struct parport *port)
+{
+ printk(KERN_INFO "%s: %s", port->name, classes[port->probe_info.class].descr);
+ if (port->probe_info.class) {
+ printk(", %s (%s)", port->probe_info.model, port->probe_info.mfr);
+ }
+ printk("\n");
+}
+
+void parport_probe_one(struct parport *port)
+{
+ char *buffer = kmalloc(2048, GFP_KERNEL);
+ int r;
+
+ port->probe_info.model = "Unknown device";
+ port->probe_info.mfr = "Unknown vendor";
+ port->probe_info.description = NULL;
+ port->probe_info.class = PARPORT_CLASS_UNSPEC;
+
+ if (!buffer) {
+ printk(KERN_ERR "%s probe: Memory squeeze.\n", port->name);
+ return;
+ }
+
+ r = parport_probe(port, buffer, 2047);
+
+ if (r < 0) {
+ printk(KERN_INFO "%s: no IEEE-1284 device present.\n",
+ port->name);
+ port->probe_info.class = PARPORT_CLASS_LEGACY;
+ } else if (r == 0) {
+ printk(KERN_INFO "%s: no ID data returned by device.\n",
+ port->name);
+ } else {
+ buffer[r] = 0;
+#ifdef DEBUG_PROBE
+ printk("%s id: %s\n", port->name, buffer+2);
+#endif
+ parse_data(port, buffer+2);
+ pretty_print(port);
+ }
+ kfree(buffer);
+}
diff --git a/drivers/pnp/parport_procfs.c b/drivers/pnp/parport_procfs.c
new file mode 100644
index 000000000..896abbdd4
--- /dev/null
+++ b/drivers/pnp/parport_procfs.c
@@ -0,0 +1,340 @@
+/* $Id: parport_procfs.c,v 1.3.2.6 1997/04/16 21:30:38 phil Exp $
+ * Parallel port /proc interface code.
+ *
+ * Authors: David Campbell <campbell@tirian.che.curtin.edu.au>
+ * Tim Waugh <tmw20@cam.ac.uk>
+ *
+ * based on work by Grant Guenther <grant@torque.net>
+ * and Philip Blundell <Philip.Blundell@pobox.com>
+ */
+
+#include <linux/tasks.h>
+#include <asm/ptrace.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <linux/config.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+
+#include <linux/proc_fs.h>
+
+#include <linux/parport.h>
+#include "parport_ll_io.h"
+
+#undef PARPORT_INCLUDE_BENCH
+
+struct proc_dir_entry *base=NULL;
+
+void parport_null_intr_func(int irq, void *dev_id, struct pt_regs *regs);
+
+static int irq_write_proc(struct file *file, const char *buffer,
+ unsigned long count, void *data)
+{
+ int newirq;
+ struct parport *pp = (struct parport *)data;
+
+ if (count > 4 ) /* more than 4 digits for a irq 0x?? 0?? ?? */
+ return(-EOVERFLOW);
+
+ if (buffer[0] < 32 || !strncmp(buffer, "none", 4)) {
+ newirq = PARPORT_IRQ_NONE;
+ } else {
+ if (buffer[0] == '0') {
+ if( buffer[1] == 'x' )
+ newirq = simple_strtoul(&buffer[2],0,16);
+ else
+ newirq = simple_strtoul(&buffer[1],0,8);
+ } else {
+ newirq = simple_strtoul(buffer,0,10);
+ }
+ }
+
+ if (pp->irq != PARPORT_IRQ_NONE && !(pp->flags & PARPORT_FLAG_COMA))
+ free_irq(pp->irq, pp);
+
+ pp->irq = newirq;
+
+ if (pp->irq != PARPORT_IRQ_NONE && !(pp->flags & PARPORT_FLAG_COMA)) {
+ struct ppd *pd = pp->cad;
+
+ if (pd == NULL) {
+ pd = pp->devices;
+ if (pd != NULL)
+ request_irq(pp->irq, pd->irq_func ?
+ pd->irq_func :
+ parport_null_intr_func,
+ SA_INTERRUPT, pd->name, pd->port);
+ } else {
+ request_irq(pp->irq, pd->irq_func ? pd->irq_func :
+ parport_null_intr_func,
+ SA_INTERRUPT, pp->name, pd->port);
+ }
+ }
+
+ return count;
+}
+
+static int irq_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ struct parport *pp = (struct parport *)data;
+ int len;
+
+ if (pp->irq == PARPORT_IRQ_NONE)
+ len = sprintf(page, "none\n");
+ else
+ len = sprintf(page, "%d\n", pp->irq);
+
+ *start = 0;
+ *eof = 1;
+ return len;
+}
+
+static int devices_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ struct parport *pp = (struct parport *)data;
+ struct ppd *pd1;
+ int len=0;
+
+ for (pd1 = pp->devices; pd1 ; pd1 = pd1->next) {
+ if (pd1 == pp->cad)
+ len += sprintf(page+len, "+");
+ else
+ len += sprintf(page+len, " ");
+
+ len += sprintf(page+len, "%s",pd1->name);
+
+ if (pd1 == pp->lurker)
+ len += sprintf(page+len, " LURK");
+
+ len += sprintf(page+len,"\n");
+ }
+
+ *start = 0;
+ *eof = 1;
+ return len;
+}
+
+static int hardware_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ struct parport *pp = (struct parport *)data;
+ int len=0;
+
+ len += sprintf(page+len, "base:\t0x%x\n",pp->base);
+ if (pp->irq == PARPORT_IRQ_NONE)
+ len += sprintf(page+len, "irq:\tnone\n");
+ else
+ len += sprintf(page+len, "irq:\t%d\n",pp->irq);
+ len += sprintf(page+len, "dma:\t%d\n",pp->dma);
+
+ len += sprintf(page+len, "modes:\t");
+ {
+#define printmode(x) {if(pp->modes&PARPORT_MODE_##x){len+=sprintf(page+len,"%s%s",f?",":"",#x);f++;}}
+ int f = 0;
+ printmode(SPP);
+ printmode(PS2);
+ printmode(EPP);
+ printmode(ECP);
+ printmode(ECPEPP);
+ printmode(ECPPS2);
+#undef printmode
+ }
+ len += sprintf(page+len, "\n");
+
+ len += sprintf(page+len, "mode:\t");
+ if (pp->modes & PARPORT_MODE_ECR) {
+ switch (r_ecr(pp) >> 5) {
+ case 0:
+ len += sprintf(page+len, "SPP");
+ if( pp->modes & PARPORT_MODE_PS2 )
+ len += sprintf(page+len, ",PS2");
+ if( pp->modes & PARPORT_MODE_EPP )
+ len += sprintf(page+len, ",EPP");
+ break;
+ case 1:
+ len += sprintf(page+len, "ECPPS2");
+ break;
+ case 2:
+ len += sprintf(page+len, "DATAFIFO");
+ break;
+ case 3:
+ len += sprintf(page+len, "ECP");
+ break;
+ case 4:
+ len += sprintf(page+len, "ECPEPP");
+ break;
+ case 5:
+ len += sprintf(page+len, "Reserved?");
+ break;
+ case 6:
+ len += sprintf(page+len, "TEST");
+ break;
+ case 7:
+ len += sprintf(page+len, "Configuration");
+ break;
+ }
+ } else {
+ len += sprintf(page+len, "SPP");
+ if (pp->modes & PARPORT_MODE_PS2)
+ len += sprintf(page+len, ",PS2");
+ if (pp->modes & PARPORT_MODE_EPP)
+ len += sprintf(page+len, ",EPP");
+ }
+ len += sprintf(page+len, "\n");
+
+#if 0
+ /* Now no detection, please fix with an external function */
+ len += sprintf(page+len, "chipset:\tunknown\n");
+#endif
+#ifdef PARPORT_INCLUDE_BENCHMARK
+ if (pp->speed)
+ len += sprintf(page+len, "bench:\t%d Bytes/s\n",pp->speed);
+ else
+ len += sprintf(page+len, "bench:\tunknown\n");
+#endif
+
+ *start = 0;
+ *eof = 1;
+ return len;
+}
+
+static struct proc_dir_entry *new_proc_entry(const char *name, mode_t mode,
+ struct proc_dir_entry *parent,
+ unsigned short ino)
+{
+ struct proc_dir_entry *ent;
+
+ ent = kmalloc(sizeof(struct proc_dir_entry), GFP_KERNEL);
+ if (!ent)
+ return NULL;
+ memset(ent, 0, sizeof(struct proc_dir_entry));
+
+ if (mode == S_IFDIR)
+ mode |= S_IRUGO | S_IXUGO;
+ else if (mode == 0)
+ mode = S_IFREG | S_IRUGO;
+
+
+ ent->low_ino = ino;
+ ent->name = name;
+ ent->namelen = strlen(name);
+ ent->mode = mode;
+ if (S_ISDIR(mode))
+ ent->nlink = 2;
+ else
+ ent->nlink = 1;
+
+ proc_register(parent, ent);
+
+ return ent;
+}
+
+
+int parport_proc_init()
+{
+ base = new_proc_entry("parport", S_IFDIR, &proc_root,PROC_PARPORT);
+
+ if (base)
+ return 1;
+ else {
+ printk(KERN_ERR "parport: Error creating proc entry /proc/parport\n");
+ return 0;
+ }
+}
+
+int parport_proc_cleanup()
+{
+ if (base)
+ proc_unregister(&proc_root,base->low_ino);
+
+ base = NULL;
+
+ return 0;
+}
+
+int parport_proc_register(struct parport *pp)
+{
+ struct proc_dir_entry *ent;
+ static int conta=0;
+ char *name;
+
+ memset(&pp->pdir,0,sizeof(struct parport_dir));
+
+ if (!base) {
+ printk(KERN_ERR "parport: Error entry /proc/parport, not generated?\n");
+ return 1;
+ }
+
+ name = pp->pdir.name;
+ sprintf(name,"%d",conta++);
+
+ ent = new_proc_entry(name, S_IFDIR, base,0);
+ if (!ent) {
+ printk(KERN_ERR "parport: Error registering proc_entry /proc/%s\n",name);
+ return 1;
+ }
+ pp->pdir.entry = ent;
+
+ ent = new_proc_entry("irq", S_IFREG | S_IRUGO | S_IWUSR, pp->pdir.entry,0);
+ if (!ent) {
+ printk(KERN_ERR "parport: Error registering proc_entry /proc/%s/irq\n",name);
+ return 1;
+ }
+ ent->read_proc = irq_read_proc;
+ ent->write_proc= irq_write_proc;
+ ent->data = pp;
+ pp->pdir.irq = ent;
+
+ ent = new_proc_entry("devices", 0, pp->pdir.entry,0);
+ if (!ent) {
+ printk(KERN_ERR "parport: Error registering proc_entry /proc/%s/devices\n",name);
+ return 1;
+ }
+ ent->read_proc = devices_read_proc;
+ ent->data = pp;
+ pp->pdir.devices = ent;
+
+ ent = new_proc_entry("hardware", 0, pp->pdir.entry,0);
+ if (!ent) {
+ printk(KERN_ERR "parport: Error registering proc_entry /proc/%s/hardware\n",name);
+ return 1;
+ }
+ ent->read_proc = hardware_read_proc;
+ ent->data = pp;
+ pp->pdir.hardware = ent;
+ return 0;
+}
+
+int parport_proc_unregister(struct parport *pp)
+{
+ if (pp->pdir.entry) {
+ if (pp->pdir.irq) {
+ proc_unregister(pp->pdir.entry, pp->pdir.irq->low_ino);
+ kfree(pp->pdir.irq);
+ }
+
+ if (pp->pdir.devices) {
+ proc_unregister(pp->pdir.entry,
+ pp->pdir.devices->low_ino);
+ kfree(pp->pdir.devices);
+ }
+
+ if (pp->pdir.hardware) {
+ proc_unregister(pp->pdir.entry,
+ pp->pdir.hardware->low_ino);
+ kfree(pp->pdir.hardware);
+ }
+
+ proc_unregister(base, pp->pdir.entry->low_ino);
+ kfree(pp->pdir.entry);
+ }
+
+ return 0;
+}
diff --git a/drivers/pnp/parport_share.c b/drivers/pnp/parport_share.c
new file mode 100644
index 000000000..fc19fffd4
--- /dev/null
+++ b/drivers/pnp/parport_share.c
@@ -0,0 +1,469 @@
+/* $Id: parport_share.c,v 1.3.2.5 1997/04/16 21:20:44 phil Exp $
+ * Parallel-port resource manager code.
+ *
+ * Authors: David Campbell <campbell@tirian.che.curtin.edu.au>
+ * Tim Waugh <tmw20@cam.ac.uk>
+ * Jose Renau <renau@acm.org>
+ *
+ * based on work by Grant Guenther <grant@torque.net>
+ * and Philip Blundell <Philip.Blundell@pobox.com>
+ */
+
+#include <linux/tasks.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <linux/parport.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+
+#undef PARPORT_PARANOID
+
+#include "parport_ll_io.h"
+
+static struct parport *portlist = NULL, *portlist_tail = NULL;
+static int portcount = 0;
+
+/* from parport_init.c */
+extern int initialize_parport(struct parport *, unsigned long base,
+ int irq, int dma, int count);
+
+/* Return a list of all the ports we know about. */
+struct parport *parport_enumerate(void)
+{
+ return portlist;
+}
+
+void parport_null_intr_func(int irq, void *dev_id, struct pt_regs *regs)
+{
+ /* NULL function - Does nothing */
+ return;
+}
+
+struct parport *parport_register_port(unsigned long base, int irq, int dma)
+{
+ struct parport new, *tmp;
+
+ /* Check for a previously registered port.
+ * NOTE: we will ignore irq and dma if we find a previously
+ * registered device.
+ */
+ for (tmp = portlist; tmp; tmp = tmp->next) {
+ if (tmp->base == base)
+ return tmp;
+ }
+
+ /* Has someone grabbed the address yet? */
+ if (check_region(base, 3))
+ return NULL;
+
+ if (!initialize_parport(&new,base,irq,dma,portcount))
+ return NULL;
+
+ if (new.dma >= 0) {
+ if (request_dma(new.dma, new.name)) {
+ printk(KERN_INFO "%s: unable to claim DMA %d\n",
+ new.name, new.dma);
+ release_region(new.base, new.size);
+ if( new.modes & PARPORT_MODE_ECR )
+ release_region(new.base+0x400, 3);
+ kfree(new.name);
+ return NULL;
+ }
+ }
+
+ tmp = kmalloc(sizeof(struct parport), GFP_KERNEL);
+ if (!tmp) {
+ printk(KERN_WARNING "parport: memory squeeze\n");
+ release_region(new.base, new.size);
+ if( new.modes & PARPORT_MODE_ECR )
+ release_region(new.base+0x400, 3);
+ kfree(new.name);
+ return NULL;
+ }
+ memcpy(tmp, &new, sizeof(struct parport));
+
+ if (new.irq != PARPORT_IRQ_NONE) {
+ if (request_irq(new.irq, parport_null_intr_func,
+ SA_INTERRUPT, new.name, tmp) != 0) {
+ printk(KERN_INFO "%s: unable to claim IRQ %d\n",
+ new.name, new.irq);
+ kfree(tmp);
+ release_region(new.base, new.size);
+ if( new.modes & PARPORT_MODE_ECR )
+ release_region(new.base+0x400, 3);
+ kfree(new.name);
+ return NULL;
+ }
+ }
+
+ /* Here we chain the entry to our list. */
+ if (portlist_tail)
+ portlist_tail->next = tmp;
+ portlist_tail = tmp;
+ if (!portlist)
+ portlist = tmp;
+
+ printk(KERN_INFO "%s at 0x%x", tmp->name, tmp->base);
+ if (tmp->irq >= 0)
+ printk(", irq %d", tmp->irq);
+ if (tmp->dma >= 0)
+ printk(", dma %d", tmp->dma);
+ printk(" [");
+ {
+ /* Ugh! */
+#define printmode(x) {if(tmp->modes&PARPORT_MODE_##x){printk("%s%s",f?",":"",#x);f++;}}
+ int f = 0;
+ printmode(SPP);
+ printmode(PS2);
+ printmode(EPP);
+ printmode(ECP);
+ printmode(ECPEPP);
+ printmode(ECPPS2);
+#undef printmode
+ }
+ printk("]\n");
+ portcount++;
+
+ /* Restore device back to default conditions */
+ if (tmp->modes & PARPORT_MODE_ECR)
+ w_ecr(tmp, tmp->ecr);
+ w_ctr(tmp, tmp->ctr);
+
+ tmp->probe_info.class = PARPORT_CLASS_LEGACY; /* assume the worst */
+ return tmp;
+}
+
+void parport_destroy(struct parport *port)
+{
+ /* Dangerous to try destroying a port if its friends are nearby. */
+ if (port->devices) {
+ printk("%s: attempt to release active port\n", port->name);
+ return; /* Devices still present */
+ }
+
+ /* No point in further destroying a port that already lies in ruins. */
+ if (port->flags & PARPORT_FLAG_COMA)
+ return;
+
+ /* Now clean out the port entry */
+ if (port->irq >= 0)
+ free_irq(port->irq, port);
+ if (port->dma >= 0)
+ free_dma(port->dma);
+ release_region(port->base, port->size);
+ if( port->modes & PARPORT_MODE_ECR )
+ release_region(port->base+0x400, 3);
+ port->flags |= PARPORT_FLAG_COMA;
+}
+
+struct ppd *parport_register_device(struct parport *port, const char *name,
+ callback_func pf, callback_func kf,
+ irq_handler_func irq_func, int flags,
+ void *handle)
+{
+ struct ppd *tmp;
+
+ /* We only allow one lurker device (eg PLIP) */
+ if (flags & PARPORT_DEV_LURK) {
+ if (port->lurker) {
+ printk(KERN_INFO "%s: refused to register second lurker (%s)\n",
+ port->name, name);
+ return NULL;
+ }
+ if (!pf || !kf) {
+ printk(KERN_INFO "%s: refused to register lurking device (%s) without callbacks\n"
+ ,port->name, name);
+ return NULL;
+ }
+ }
+
+ /* We may need to claw back the port hardware. */
+ if (port->flags & PARPORT_FLAG_COMA) {
+ if (check_region(port->base, 3)) {
+ return NULL;
+ }
+ request_region(port->base, port->size, port->name);
+ if( port->modes & PARPORT_MODE_ECR )
+ request_region(port->base+0x400, 3,port->name);
+
+ if (port->dma >= 0) {
+ if (request_dma(port->dma, port->name)) {
+ release_region(port->base, port->size);
+ if( port->modes & PARPORT_MODE_ECR )
+ release_region(port->base+0x400, 3);
+ return NULL;
+ }
+ }
+ if (port->irq != PARPORT_IRQ_NONE) {
+ if (request_irq(port->irq, parport_null_intr_func,
+ SA_INTERRUPT, port->name,
+ port) != 0) {
+ release_region(port->base, port->size);
+ if( port->modes & PARPORT_MODE_ECR )
+ release_region(port->base+0x400, 3);
+ if (port->dma >= 0)
+ free_dma(port->dma);
+ return NULL;
+ }
+ }
+ port->flags &= ~PARPORT_FLAG_COMA;
+ }
+
+ tmp = kmalloc(sizeof(struct ppd), GFP_KERNEL);
+ tmp->name = (char *) name;
+ tmp->port = port;
+ tmp->preempt = pf;
+ tmp->wakeup = kf;
+ tmp->private = handle;
+ tmp->irq_func = irq_func;
+ tmp->ctr = port->ctr;
+ tmp->ecr = port->ecr;
+
+ /* Chain this onto the list */
+ tmp->prev = NULL;
+ tmp->next = port->devices;
+ if (port->devices)
+ port->devices->prev = tmp;
+ port->devices = tmp;
+
+ if (flags & PARPORT_DEV_LURK)
+ port->lurker = tmp;
+
+ inc_parport_count();
+
+ return tmp;
+}
+
+void parport_unregister_device(struct ppd *dev)
+{
+ struct parport *port;
+
+ if (!dev) {
+ printk(KERN_ERR "parport_unregister_device: passed NULL\n");
+ return;
+ }
+
+ port = dev->port;
+
+ if (port->cad == dev) {
+ printk(KERN_INFO "%s: refused to unregister currently active device %s\n", port->name, dev->name);
+ return;
+ }
+
+ if (port->lurker == dev)
+ port->lurker = NULL;
+
+ if (dev->next)
+ dev->next->prev = dev->prev;
+ if (dev->prev)
+ dev->prev->next = dev->next;
+ else
+ port->devices = dev->next;
+
+ kfree(dev);
+
+ dec_parport_count();
+
+ /* If there are no more devices, put the port to sleep. */
+ if (!port->devices)
+ parport_destroy(port);
+
+ return;
+}
+
+int parport_claim(struct ppd *dev)
+{
+ struct ppd *pd1;
+
+ if (dev->port->cad == dev) {
+ printk(KERN_INFO "%s: %s already owner\n",
+ dev->port->name,dev->name);
+ return 0;
+ }
+
+ /* Preempt any current device */
+ pd1 = dev->port->cad;
+ if (dev->port->cad) {
+ if (dev->port->cad->preempt) {
+ /* Now try to preempt */
+ if (dev->port->cad->preempt(dev->port->cad->private))
+ return -EAGAIN;
+
+ /* Save control registers */
+ if (dev->port->modes & PARPORT_MODE_ECR)
+ dev->port->cad->ecr = dev->port->ecr =
+ r_ecr(dev->port);
+ if (dev->port->modes & PARPORT_MODE_SPP)
+ dev->port->cad->ctr = dev->port->ctr =
+ r_ctr(dev->port);
+ } else
+ return -EAGAIN;
+ }
+
+ /* Watch out for bad things */
+ if (dev->port->cad != pd1) {
+ printk(KERN_WARNING "%s: death while preempting %s\n",
+ dev->port->name, dev->name);
+ if (dev->port->cad)
+ return -EAGAIN;
+ }
+
+ /* Now we do the change of devices */
+ dev->port->cad = dev;
+
+ if (dev->port->irq >= 0) {
+ free_irq(dev->port->irq, dev->port);
+ request_irq(dev->port->irq, dev->irq_func ? dev->irq_func :
+ parport_null_intr_func, SA_INTERRUPT, dev->name,
+ dev->port);
+ }
+
+ /* Restore control registers */
+ if (dev->port->modes & PARPORT_MODE_ECR)
+ if (dev->ecr != dev->port->ecr) w_ecr(dev->port, dev->ecr);
+ if (dev->port->modes & PARPORT_MODE_SPP)
+ if (dev->ctr != dev->port->ctr) w_ctr(dev->port, dev->ctr);
+
+ return 0;
+}
+
+void parport_release(struct ppd *dev)
+{
+ struct ppd *pd1;
+
+ /* Make sure that dev is the current device */
+ if (dev->port->cad != dev) {
+ printk(KERN_WARNING "%s: %s tried to release parport when not owner\n", dev->port->name, dev->name);
+ return;
+ }
+ dev->port->cad = NULL;
+
+ /* Save control registers */
+ if (dev->port->modes & PARPORT_MODE_ECR)
+ dev->ecr = dev->port->ecr = r_ecr(dev->port);
+ if (dev->port->modes & PARPORT_MODE_SPP)
+ dev->ctr = dev->port->ctr = r_ctr(dev->port);
+
+ if (dev->port->irq >= 0) {
+ free_irq(dev->port->irq, dev->port);
+ request_irq(dev->port->irq, parport_null_intr_func,
+ SA_INTERRUPT, dev->port->name, dev->port);
+ }
+
+ /* Walk the list, offering a wakeup callback to everybody other
+ * than the lurker and the device that called us.
+ */
+ for (pd1 = dev->next; pd1; pd1 = pd1->next) {
+ if (!(pd1->flags & PARPORT_DEV_LURK)) {
+ if (pd1->wakeup) {
+ pd1->wakeup(pd1->private);
+ if (dev->port->cad)
+ return;
+ }
+ }
+ }
+
+ for (pd1 = dev->port->devices; pd1 && pd1 != dev; pd1 = pd1->next) {
+ if (!(pd1->flags & PARPORT_DEV_LURK)) {
+ if (pd1->wakeup) {
+ pd1->wakeup(pd1->private);
+ if (dev->port->cad)
+ return;
+ }
+ }
+ }
+
+ /* Now give the lurker a chance.
+ * There should be a wakeup callback because we checked for it
+ * at registration.
+ */
+ if (dev->port->lurker && (dev->port->lurker != dev)) {
+ if (dev->port->lurker->wakeup) {
+ dev->port->lurker->wakeup(dev->port->lurker->private);
+ }
+#ifdef PARPORT_PARANOID
+ else { /* can't happen */
+ printk(KERN_DEBUG
+ "%s (%s): lurker's wakeup callback went away!\n",
+ dev->port->name, dev->name);
+ }
+#endif
+ }
+}
+
+/* The following read funktions are an implementation of a status readback
+ * and device id request confirming to IEEE1284-1994.
+ *
+ * These probably ought to go in some seperate file, so people like the SPARC
+ * don't have to pull them in.
+ */
+
+/* Wait for Status line(s) to change in 35 ms - see IEEE1284-1994 page 24 to
+ * 25 for this. After this time we can create a timeout because the
+ * peripheral doesn't conform to IEEE1284. We want to save CPU time: we are
+ * waiting a maximum time of 500 us busy (this is for speed). If there is
+ * not the right answer in this time, we call schedule and other processes
+ * are able "to eat" the time up to 30ms. So the maximum load avarage can't
+ * get above 5% for a read even if the peripheral is really slow. (but your
+ * read gets very slow then - only about 10 characters per second. This
+ * should be tuneable). Thanks to Andreas who pointed me to this and ordered
+ * the documentation.
+ */
+
+int parport_wait_peripheral(struct parport *port, unsigned char mask,
+ unsigned char result)
+{
+ int counter=0;
+ unsigned char status;
+
+ do {
+ status = parport_r_status(port);
+ udelay(25);
+ counter++;
+ if (need_resched)
+ schedule();
+ } while ( ((status & mask) != result) && (counter < 20) );
+ if ( (counter == 20) && ((status & mask) != result) ) {
+ current->state=TASK_INTERRUPTIBLE;
+ current->timeout=jiffies+4;
+ schedule(); /* wait for 4 scheduler runs (40ms) */
+ status = parport_r_status(port);
+ if ((status & mask) != result) return 1; /* timeout */
+ }
+ return 0; /* okay right response from device */
+}
+
+/* Test if nibble mode for status readback is okay. Returns the value false
+ * if the printer doesn't support readback at all. If it supports readbacks
+ * and printer data is available the function returns 1, otherwise 2. The
+ * only valid values for "mode" are 0 and 4. 0 requests normal nibble mode,
+ * 4 is for "request device id using nibble mode". The request for the
+ * device id is best done in an ioctl (or at bootup time). There is no
+ * check for an invalid value, the only function using this call at the
+ * moment is lp_read and the ioctl LPGETDEVICEID both fixed calls from
+ * trusted kernel.
+ */
+int parport_ieee1284_nibble_mode_ok(struct parport *port, unsigned char mode)
+{
+ parport_w_data(port, mode);
+ udelay(5);
+ parport_w_ctrl(port, parport_r_ctrl(port) & ~8); /* SelectIN low */
+ parport_w_ctrl(port, parport_r_ctrl(port) | 2); /* AutoFeed high */
+ if (parport_wait_peripheral(port, 0x78, 0x38)) { /* timeout? */
+ parport_w_ctrl(port, (parport_r_ctrl(port) & ~2) | 8);
+ return 0; /* first stage of negotiation failed,
+ * no IEEE1284 compliant device on this port
+ */
+ }
+ parport_w_ctrl(port, parport_r_ctrl(port) | 1); /* Strobe high */
+ udelay(5); /* Strobe wait */
+ parport_w_ctrl(port, parport_r_ctrl(port) & ~1); /* Strobe low */
+ udelay(5);
+ parport_w_ctrl(port, parport_r_ctrl(port) & ~2); /* AutoFeed low */
+ return (parport_wait_peripheral(port, 0x20, 0))?2:1;
+}