]> git.ipfire.org Git - thirdparty/cups.git/blame - backend/ieee1284.c
Merge changes from CUPS 1.4svn-r7199.
[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 */
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
379int /* O - 0 on success, -1 on failure */
ed486911 380backendGetMakeModel(
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 */