]> git.ipfire.org Git - thirdparty/cups.git/blob - backend/ieee1284.c
Merge changes from CUPS 1.4svn-r7485.
[thirdparty/cups.git] / backend / ieee1284.c
1 /*
2 * "$Id: ieee1284.c 7019 2007-10-10 22:48:52Z mike $"
3 *
4 * IEEE-1284 support functions for the Common UNIX Printing System (CUPS).
5 *
6 * Copyright 2007 by Apple Inc.
7 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
8 *
9 * These coded instructions, statements, and computer programs are the
10 * property of Apple Inc. and are protected by Federal copyright
11 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
12 * "LICENSE" which should have been included with this file. If this
13 * file is missing or damaged, see the license at "http://www.cups.org/".
14 *
15 * This file is subject to the Apple OS-Developed Software exception.
16 *
17 * Contents:
18 *
19 * backendGetDeviceID() - Get the IEEE-1284 device ID string and
20 * corresponding URI.
21 * backendGetMakeModel() - Get the make and model string from the device ID.
22 */
23
24 /*
25 * Include necessary headers.
26 */
27
28 #include "backend-private.h"
29
30 #ifdef __linux
31 # include <sys/ioctl.h>
32 # include <linux/lp.h>
33 # define IOCNR_GET_DEVICE_ID 1
34 # define LPIOC_GET_DEVICE_ID(len) _IOC(_IOC_READ, 'P', IOCNR_GET_DEVICE_ID, len)
35 # include <linux/parport.h>
36 # include <linux/ppdev.h>
37 # include <unistd.h>
38 # include <fcntl.h>
39 #endif /* __linux */
40
41 #ifdef __sun
42 # ifdef __sparc
43 # include <sys/ecppio.h>
44 # else
45 # include <sys/ioccom.h>
46 # include <sys/ecppsys.h>
47 # endif /* __sparc */
48 #endif /* __sun */
49
50
51 /*
52 * 'backendGetDeviceID()' - Get the IEEE-1284 device ID string and
53 * corresponding URI.
54 */
55
56 int /* O - 0 on success, -1 on failure */
57 backendGetDeviceID(
58 int fd, /* I - File descriptor */
59 char *device_id, /* O - 1284 device ID */
60 int device_id_size, /* I - Size of buffer */
61 char *make_model, /* O - Make/model */
62 int make_model_size, /* I - Size of buffer */
63 const char *scheme, /* I - URI scheme */
64 char *uri, /* O - Device URI */
65 int uri_size) /* I - Size of buffer */
66 {
67 #ifdef __APPLE__ /* This function is a no-op */
68 return (-1);
69
70 #else /* Get the device ID from the specified file descriptor... */
71 char *attr, /* 1284 attribute */
72 *delim, /* 1284 delimiter */
73 *uriptr, /* Pointer into URI */
74 manufacturer[256], /* Manufacturer string */
75 serial_number[1024]; /* Serial number string */
76 int manulen; /* Length of manufacturer string */
77 # ifdef __linux
78 int length; /* Length of device ID info */
79 int got_id = 0;
80 # endif /* __linux */
81 # if defined(__sun) && defined(ECPPIOC_GETDEVID)
82 struct ecpp_device_id did; /* Device ID buffer */
83 # endif /* __sun && ECPPIOC_GETDEVID */
84
85
86 DEBUG_printf(("backendGetDeviceID(fd=%d, device_id=%p, device_id_size=%d, "
87 "make_model=%p, make_model_size=%d, scheme=\"%s\", "
88 "uri=%p, uri_size=%d)\n", fd, device_id, device_id_size,
89 make_model, make_model_size, scheme ? scheme : "(null)",
90 uri, uri_size));
91
92 /*
93 * Range check input...
94 */
95
96 if (!device_id || device_id_size < 32)
97 {
98 DEBUG_puts("backendGetDeviceID: Bad args!");
99 return (-1);
100 }
101
102 if (make_model)
103 *make_model = '\0';
104
105 if (fd >= 0)
106 {
107 /*
108 * Get the device ID string...
109 */
110
111 *device_id = '\0';
112
113 # ifdef __linux
114 if (ioctl(fd, LPIOC_GET_DEVICE_ID(device_id_size), device_id))
115 {
116 /*
117 * Linux has to implement things differently for every device it seems.
118 * Since the standard parallel port driver does not provide a simple
119 * ioctl() to get the 1284 device ID, we have to open the "raw" parallel
120 * device corresponding to this port and do some negotiation trickery
121 * to get the current device ID.
122 */
123
124 if (uri && !strncmp(uri, "parallel:/dev/", 14))
125 {
126 char devparport[16]; /* /dev/parportN */
127 int devparportfd, /* File descriptor for raw device */
128 mode; /* Port mode */
129
130
131 /*
132 * Since the Linux parallel backend only supports 4 parallel port
133 * devices, just grab the trailing digit and use it to construct a
134 * /dev/parportN filename...
135 */
136
137 snprintf(devparport, sizeof(devparport), "/dev/parport%s",
138 uri + strlen(uri) - 1);
139
140 if ((devparportfd = open(devparport, O_RDWR | O_NOCTTY)) != -1)
141 {
142 /*
143 * Claim the device...
144 */
145
146 if (!ioctl(devparportfd, PPCLAIM))
147 {
148 fcntl(devparportfd, F_SETFL, fcntl(devparportfd, F_GETFL) | O_NONBLOCK);
149
150 mode = IEEE1284_MODE_COMPAT;
151
152 if (!ioctl(devparportfd, PPNEGOT, &mode))
153 {
154 /*
155 * Put the device into Device ID mode...
156 */
157
158 mode = IEEE1284_MODE_NIBBLE | IEEE1284_DEVICEID;
159
160 if (!ioctl(devparportfd, PPNEGOT, &mode))
161 {
162 /*
163 * Read the 1284 device ID...
164 */
165
166 if ((length = read(devparportfd, device_id,
167 device_id_size - 1)) >= 2)
168 {
169 device_id[length] = '\0';
170 got_id = 1;
171 }
172 }
173 }
174
175 /*
176 * Release the device...
177 */
178
179 ioctl(devparportfd, PPRELEASE);
180 }
181
182 close(devparportfd);
183 }
184 }
185 }
186 else
187 got_id = 1;
188
189 if (got_id)
190 {
191 /*
192 * Extract the length of the device ID string from the first two
193 * bytes. The 1284 spec says the length is stored MSB first...
194 */
195
196 length = (((unsigned)device_id[0] & 255) << 8) +
197 ((unsigned)device_id[1] & 255);
198
199 /*
200 * Check to see if the length is larger than our buffer; first
201 * assume that the vendor incorrectly implemented the 1284 spec,
202 * and then limit the length to the size of our buffer...
203 */
204
205 if (length > (device_id_size - 2))
206 length = (((unsigned)device_id[1] & 255) << 8) +
207 ((unsigned)device_id[0] & 255);
208
209 if (length > (device_id_size - 2))
210 length = device_id_size - 2;
211
212 /*
213 * Copy the device ID text to the beginning of the buffer and
214 * nul-terminate.
215 */
216
217 memmove(device_id, device_id + 2, length);
218 device_id[length] = '\0';
219 }
220 # ifdef DEBUG
221 else
222 printf("backendGetDeviceID: ioctl failed - %s\n", strerror(errno));
223 # endif /* DEBUG */
224 # endif /* __linux */
225
226 # if defined(__sun) && defined(ECPPIOC_GETDEVID)
227 did.mode = ECPP_CENTRONICS;
228 did.len = device_id_size - 1;
229 did.rlen = 0;
230 did.addr = device_id;
231
232 if (!ioctl(fd, ECPPIOC_GETDEVID, &did))
233 {
234 /*
235 * Nul-terminate the device ID text.
236 */
237
238 if (did.rlen < (device_id_size - 1))
239 device_id[did.rlen] = '\0';
240 else
241 device_id[device_id_size - 1] = '\0';
242 }
243 # ifdef DEBUG
244 else
245 printf("backendGetDeviceID: ioctl failed - %s\n", strerror(errno));
246 # endif /* DEBUG */
247 # endif /* __sun && ECPPIOC_GETDEVID */
248 }
249
250 DEBUG_printf(("backendGetDeviceID: device_id=\"%s\"\n", device_id));
251
252 if (scheme && uri)
253 *uri = '\0';
254
255 if (!*device_id)
256 return (-1);
257
258 /*
259 * Get the make and model...
260 */
261
262 if (make_model)
263 backendGetMakeModel(device_id, make_model, make_model_size);
264
265 /*
266 * Then generate a device URI...
267 */
268
269 if (scheme && uri && uri_size > 32)
270 {
271 /*
272 * Look for the serial number field...
273 */
274
275 if ((attr = strstr(device_id, "SERN:")) != NULL)
276 attr += 5;
277 else if ((attr = strstr(device_id, "SERIALNUMBER:")) != NULL)
278 attr += 13;
279 else if ((attr = strstr(device_id, ";SN:")) != NULL)
280 attr += 4;
281
282 if (attr)
283 {
284 strlcpy(serial_number, attr, sizeof(serial_number));
285
286 if ((delim = strchr(serial_number, ';')) != NULL)
287 *delim = '\0';
288 }
289 else
290 serial_number[0] = '\0';
291
292 /*
293 * Generate the device URI from the manufacturer, make_model, and
294 * serial number strings.
295 */
296
297 snprintf(uri, uri_size, "%s://", scheme);
298
299 if ((attr = strstr(device_id, "MANUFACTURER:")) != NULL)
300 attr += 13;
301 else if ((attr = strstr(device_id, "Manufacturer:")) != NULL)
302 attr += 13;
303 else if ((attr = strstr(device_id, "MFG:")) != NULL)
304 attr += 4;
305
306 if (attr)
307 {
308 strlcpy(manufacturer, attr, sizeof(manufacturer));
309
310 if ((delim = strchr(manufacturer, ';')) != NULL)
311 *delim = '\0';
312
313 if (!strcasecmp(manufacturer, "Hewlett-Packard"))
314 strcpy(manufacturer, "HP");
315 else if (!strcasecmp(manufacturer, "Lexmark International"))
316 strcpy(manufacturer, "Lexmark");
317 }
318 else
319 {
320 strlcpy(manufacturer, make_model, sizeof(manufacturer));
321
322 if ((delim = strchr(manufacturer, ' ')) != NULL)
323 *delim = '\0';
324 }
325
326 manulen = strlen(manufacturer);
327
328 for (uriptr = uri + strlen(uri), delim = manufacturer;
329 *delim && uriptr < (uri + uri_size - 3);
330 delim ++)
331 if (*delim == ' ')
332 {
333 *uriptr++ = '%';
334 *uriptr++ = '2';
335 *uriptr++ = '0';
336 }
337 else
338 *uriptr++ = *delim;
339
340 *uriptr++ = '/';
341
342 if (!strncasecmp(make_model, manufacturer, manulen))
343 {
344 delim = make_model + manulen;
345
346 while (isspace(*delim & 255))
347 delim ++;
348 }
349 else
350 delim = make_model;
351
352 for (; *delim && uriptr < (uri + uri_size - 3); delim ++)
353 if (*delim == ' ')
354 {
355 *uriptr++ = '%';
356 *uriptr++ = '2';
357 *uriptr++ = '0';
358 }
359 else
360 *uriptr++ = *delim;
361
362 if (serial_number[0])
363 {
364 /*
365 * Add the serial number to the URI...
366 */
367
368 strlcpy(uriptr, "?serial=", uri_size - (uriptr - uri));
369 strlcat(uriptr, serial_number, uri_size - (uriptr - uri));
370 }
371 else
372 *uriptr = '\0';
373 }
374
375 return (0);
376 #endif /* __APPLE__ */
377 }
378
379
380 /*
381 * 'backendGetMakeModel()' - Get the make and model string from the device ID.
382 */
383
384 int /* O - 0 on success, -1 on failure */
385 backendGetMakeModel(
386 const char *device_id, /* O - 1284 device ID */
387 char *make_model, /* O - Make/model */
388 int make_model_size) /* I - Size of buffer */
389 {
390 char *attr, /* 1284 attribute */
391 *delim, /* 1284 delimiter */
392 *mfg, /* Manufacturer string */
393 *mdl; /* Model string */
394
395
396 DEBUG_printf(("backendGetMakeModel(device_id=\"%s\", "
397 "make_model=%p, make_model_size=%d)\n", device_id,
398 make_model, make_model_size));
399
400 /*
401 * Range check input...
402 */
403
404 if (!device_id || !*device_id || !make_model || make_model_size < 32)
405 {
406 DEBUG_puts("backendGetMakeModel: Bad args!");
407 return (-1);
408 }
409
410 *make_model = '\0';
411
412 /*
413 * Look for the description field...
414 */
415
416 if ((attr = strstr(device_id, "DES:")) != NULL)
417 attr += 4;
418 else if ((attr = strstr(device_id, "DESCRIPTION:")) != NULL)
419 attr += 12;
420
421 if (attr)
422 {
423 /*
424 * Make sure the description contains something useful, since some
425 * printer manufacturers (HP) apparently don't follow the standards
426 * they helped to define...
427 *
428 * Here we require the description to be 8 or more characters in length,
429 * containing at least one space and one letter.
430 */
431
432 if ((delim = strchr(attr, ';')) == NULL)
433 delim = attr + strlen(attr);
434
435 if ((delim - attr) < 8)
436 attr = NULL;
437 else
438 {
439 char *ptr; /* Pointer into description */
440 int letters, /* Number of letters seen */
441 spaces; /* Number of spaces seen */
442
443
444 for (ptr = attr, letters = 0, spaces = 0; ptr < delim; ptr ++)
445 {
446 if (isspace(*ptr & 255))
447 spaces ++;
448 else if (isalpha(*ptr & 255))
449 letters ++;
450
451 if (spaces && letters)
452 break;
453 }
454
455 if (!spaces || !letters)
456 attr = NULL;
457 }
458 }
459
460 if ((mfg = strstr(device_id, "MANUFACTURER:")) != NULL)
461 mfg += 13;
462 else if ((mfg = strstr(device_id, "Manufacturer:")) != NULL)
463 mfg += 13;
464 else if ((mfg = strstr(device_id, "MFG:")) != NULL)
465 mfg += 4;
466
467 if ((mdl = strstr(device_id, "MODEL:")) != NULL)
468 mdl += 6;
469 else if ((mdl = strstr(device_id, "Model:")) != NULL)
470 mdl += 6;
471 else if ((mdl = strstr(device_id, "MDL:")) != NULL)
472 mdl += 4;
473
474 if (mdl)
475 {
476 /*
477 * Build a make-model string from the manufacturer and model attributes...
478 */
479
480 if (mfg)
481 {
482 /*
483 * Skip leading whitespace...
484 */
485
486 while (isspace(*mfg & 255))
487 mfg ++;
488
489 /*
490 * Map common bad names to the ones we use for driver selection...
491 */
492
493 if (!strncasecmp(mfg, "Hewlett-Packard", 15))
494 strlcpy(make_model, "HP", make_model_size);
495 else if (!strncasecmp(mfg, "Lexmark International", 21))
496 strlcpy(make_model, "Lexmark", make_model_size);
497 else
498 {
499 /*
500 * Use the manufacturer that is supplied...
501 */
502
503 strlcpy(make_model, mfg, make_model_size);
504
505 if ((delim = strchr(make_model, ';')) != NULL)
506 *delim = '\0';
507
508 /*
509 * But strip trailing whitespace...
510 */
511
512 for (delim = make_model + strlen(make_model) - 1;
513 delim > make_model && *delim == ' ';
514 delim --)
515 *delim = '\0';
516 }
517
518 if (!strncasecmp(make_model, mdl, strlen(make_model)))
519 {
520 /*
521 * Just copy model string, since it has the manufacturer...
522 */
523
524 strlcpy(make_model, mdl, make_model_size);
525 }
526 else
527 {
528 /*
529 * Concatenate the make and model...
530 */
531
532 while (isspace(*mdl & 255))
533 mdl ++;
534
535 strlcat(make_model, " ", make_model_size);
536 strlcat(make_model, mdl, make_model_size);
537 }
538 }
539 else
540 {
541 /*
542 * Just copy model string, since it has the manufacturer...
543 */
544
545 while (isspace(*mdl & 255))
546 mdl ++;
547
548 strlcpy(make_model, mdl, make_model_size);
549 }
550 }
551 else if (attr)
552 {
553 /*
554 * Use description...
555 */
556
557 while (isspace(*attr & 255))
558 attr ++;
559
560 if (!strncasecmp(attr, "Hewlett-Packard hp ", 19))
561 {
562 /*
563 * Check for a common HP bug...
564 */
565
566 strlcpy(make_model, "HP ", make_model_size);
567 strlcpy(make_model + 3, attr + 19, make_model_size - 3);
568 }
569 else if (!strncasecmp(attr, "Hewlett-Packard ", 16))
570 {
571 strlcpy(make_model, "HP ", make_model_size);
572 strlcpy(make_model + 3, attr + 16, make_model_size - 3);
573 }
574 else
575 {
576 strlcpy(make_model, attr, make_model_size);
577 }
578 }
579 else
580 {
581 /*
582 * Use "Unknown" as the printer make and model...
583 */
584
585 strlcpy(make_model, "Unknown", make_model_size);
586 }
587
588 /*
589 * Strip trailing data...
590 */
591
592 if ((delim = strchr(make_model, ';')) != NULL)
593 *delim = '\0';
594
595 for (delim = make_model + strlen(make_model) - 1;
596 delim > make_model && *delim == ' ';
597 delim --)
598 *delim = '\0';
599
600 /*
601 * Strip trailing whitespace...
602 */
603
604 for (delim = make_model + strlen(make_model) - 1; delim >= make_model; delim --)
605 if (isspace(*delim & 255))
606 *delim = '\0';
607 else
608 break;
609
610 /*
611 * Return...
612 */
613
614 if (make_model[0])
615 return (0);
616 else
617 return (-1);
618 }
619
620
621 /*
622 * End of "$Id: ieee1284.c 7019 2007-10-10 22:48:52Z mike $".
623 */