patch-2.1.33 linux/drivers/pnp/parport_probe.c

Next file: linux/drivers/pnp/parport_procfs.c
Previous file: linux/drivers/pnp/parport_ll_io.h
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.32/linux/drivers/pnp/parport_probe.c linux/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 <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);
+}

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov