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