File: /usr/src/linux/drivers/parport/probe.c

1     /* $Id: parport_probe.c,v 1.1 1999/07/03 08:56:17 davem Exp $
2      * Parallel port device probing code
3      *
4      * Authors:    Carsten Gross, carsten@sol.wohnheim.uni-ulm.de
5      *             Philip Blundell <Philip.Blundell@pobox.com>
6      */
7     
8     #include <linux/parport.h>
9     #include <linux/ctype.h>
10     #include <asm/uaccess.h>
11     
12     static struct {
13     	char *token;
14     	char *descr;
15     } classes[] = {
16     	{ "",            "Legacy device" },
17     	{ "PRINTER",     "Printer" },
18     	{ "MODEM",       "Modem" },
19     	{ "NET",         "Network device" },
20     	{ "HDC",       	 "Hard disk" },
21     	{ "PCMCIA",      "PCMCIA" },
22     	{ "MEDIA",       "Multimedia device" },
23     	{ "FDC",         "Floppy disk" },
24     	{ "PORTS",       "Ports" },
25     	{ "SCANNER",     "Scanner" },
26     	{ "DIGICAM",     "Digital camera" },
27     	{ "",            "Unknown device" },
28     	{ "",            "Unspecified" },
29     	{ "SCSIADAPTER", "SCSI adapter" },
30     	{ NULL,          NULL }
31     };
32     
33     static void pretty_print(struct parport *port, int device)
34     {
35     	struct parport_device_info *info = &port->probe_info[device + 1];
36     
37     	printk(KERN_INFO "%s", port->name);
38     
39     	if (device >= 0)
40     		printk (" (addr %d)", device);
41     
42     	printk (": %s", classes[info->class].descr);
43     	if (info->class)
44     		printk(", %s %s", info->mfr, info->model);
45     
46     	printk("\n");
47     }
48     
49     static char *strdup(char *str)
50     {
51     	int n = strlen(str)+1;
52     	char *s = kmalloc(n, GFP_KERNEL);
53     	if (!s) return NULL;
54     	return strcpy(s, str);
55     }
56     
57     static void parse_data(struct parport *port, int device, char *str)
58     {
59     	char *txt = kmalloc(strlen(str)+1, GFP_KERNEL);
60     	char *p = txt, *q;
61     	int guessed_class = PARPORT_CLASS_UNSPEC;
62     	struct parport_device_info *info = &port->probe_info[device + 1];
63     
64     	if (!txt) {
65     		printk(KERN_WARNING "%s probe: memory squeeze\n", port->name);
66     		return;
67     	}
68     	strcpy(txt, str);
69     	while (p) {
70     		char *sep;
71     		q = strchr(p, ';');
72     		if (q) *q = 0;
73     		sep = strchr(p, ':');
74     		if (sep) {
75     			char *u;
76     			*(sep++) = 0;
77     			/* Get rid of trailing blanks */
78     			u = sep + strlen (sep) - 1;
79     			while (u >= p && *u == ' ')
80     				*u-- = '\0';
81     			u = p;
82     			while (*u) {
83     				*u = toupper(*u);
84     				u++;
85     			}
86     			if (!strcmp(p, "MFG") || !strcmp(p, "MANUFACTURER")) {
87     				if (info->mfr)
88     					kfree (info->mfr);
89     				info->mfr = strdup(sep);
90     			} else if (!strcmp(p, "MDL") || !strcmp(p, "MODEL")) {
91     				if (info->model)
92     					kfree (info->model);
93     				info->model = strdup(sep);
94     			} else if (!strcmp(p, "CLS") || !strcmp(p, "CLASS")) {
95     				int i;
96     				if (info->class_name)
97     					kfree (info->class_name);
98     				info->class_name = strdup(sep);
99     				for (u = sep; *u; u++)
100     					*u = toupper(*u);
101     				for (i = 0; classes[i].token; i++) {
102     					if (!strcmp(classes[i].token, sep)) {
103     						info->class = i;
104     						goto rock_on;
105     					}
106     				}
107     				printk(KERN_WARNING "%s probe: warning, class '%s' not understood.\n", port->name, sep);
108     				info->class = PARPORT_CLASS_OTHER;
109     			} else if (!strcmp(p, "CMD") ||
110     				   !strcmp(p, "COMMAND SET")) {
111     				if (info->cmdset)
112     					kfree (info->cmdset);
113     				info->cmdset = strdup(sep);
114     				/* if it speaks printer language, it's
115     				   probably a printer */
116     				if (strstr(sep, "PJL") || strstr(sep, "PCL"))
117     					guessed_class = PARPORT_CLASS_PRINTER;
118     			} else if (!strcmp(p, "DES") || !strcmp(p, "DESCRIPTION")) {
119     				if (info->description)
120     					kfree (info->description);
121     				info->description = strdup(sep);
122     			}
123     		}
124     	rock_on:
125     		if (q) p = q+1; else p=NULL;
126     	}
127     
128     	/* If the device didn't tell us its class, maybe we have managed to
129     	   guess one from the things it did say. */
130     	if (info->class == PARPORT_CLASS_UNSPEC)
131     		info->class = guessed_class;
132     
133     	pretty_print (port, device);
134     
135     	kfree(txt);
136     }
137     
138     /* Get Std 1284 Device ID. */
139     ssize_t parport_device_id (int devnum, char *buffer, size_t len)
140     {
141     	ssize_t retval = -ENXIO;
142     	struct pardevice *dev = parport_open (devnum, "Device ID probe",
143     					      NULL, NULL, NULL, 0, NULL);
144     	if (!dev)
145     		return -ENXIO;
146     
147     	parport_claim_or_block (dev);
148     
149     	/* Negotiate to compatibility mode, and then to device ID mode.
150     	 * (This is in case we are already in device ID mode.) */
151     	parport_negotiate (dev->port, IEEE1284_MODE_COMPAT);
152     	retval = parport_negotiate (dev->port,
153     				    IEEE1284_MODE_NIBBLE | IEEE1284_DEVICEID);
154     
155     	if (!retval) {
156     		int idlen;
157     		unsigned char length[2];
158     
159     		/* First two bytes are MSB,LSB of inclusive length. */
160     		retval = parport_read (dev->port, length, 2);
161     
162     		if (retval != 2) goto end_id;
163     
164     		idlen = (length[0] << 8) + length[1] - 2;
165     		if (idlen < len)
166     			len = idlen;
167     		retval = parport_read (dev->port, buffer, len);
168     
169     		if (retval != len)
170     			printk (KERN_DEBUG "%s: only read %Zd of %Zd ID bytes\n",
171     				dev->port->name, retval,
172     				len);
173     
174     		/* Some printer manufacturers mistakenly believe that
175                        the length field is supposed to be _exclusive_.
176     		   In addition, there are broken devices out there
177                        that don't even finish off with a semi-colon. */
178     		if (buffer[len - 1] != ';') {
179     			ssize_t diff;
180     			diff = parport_read (dev->port, buffer + len, 2);
181     			retval += diff;
182     
183     			if (diff)
184     				printk (KERN_DEBUG
185     					"%s: device reported incorrect "
186     					"length field (%d, should be %Zd)\n",
187     					dev->port->name, idlen, retval);
188     			else {
189     				/* One semi-colon short of a device ID. */
190     				buffer[len++] = ';';
191     				printk (KERN_DEBUG "%s: faking semi-colon\n",
192     					dev->port->name);
193     
194     				/* If we get here, I don't think we
195                                        need to worry about the possible
196                                        standard violation of having read
197                                        more than we were told to.  The
198                                        device is non-compliant anyhow. */
199     			}
200     		}
201     
202     	end_id:
203     		buffer[len] = '\0';
204     		parport_negotiate (dev->port, IEEE1284_MODE_COMPAT);
205     	}
206     	parport_release (dev);
207     
208     	if (retval > 2)
209     		parse_data (dev->port, dev->daisy, buffer);
210     
211     	parport_close (dev);
212     	return retval;
213     }
214