]>
Commit | Line | Data |
---|---|---|
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 | ||
56 | int /* O - 0 on success, -1 on failure */ | |
ed486911 | 57 | backendGetDeviceID( |
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 | ||
380 | int /* O - 0 on success, -1 on failure */ | |
ed486911 | 381 | backendGetMakeModel( |
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 | */ |