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