]>
Commit | Line | Data |
---|---|---|
ef416fc2 | 1 | /* |
75bd9771 | 2 | * "$Id: ieee1284.c 7687 2008-06-24 01:28:36Z mike $" |
ef416fc2 | 3 | * |
71e16022 | 4 | * IEEE-1284 support functions for CUPS. |
ef416fc2 | 5 | * |
88f9aafc | 6 | * Copyright 2007-2011 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" |
71e16022 | 29 | #include <cups/cups-private.h> |
ef416fc2 | 30 | |
ef416fc2 | 31 | |
32 | /* | |
ed486911 | 33 | * 'backendGetDeviceID()' - Get the IEEE-1284 device ID string and |
34 | * corresponding URI. | |
ef416fc2 | 35 | */ |
36 | ||
37 | int /* O - 0 on success, -1 on failure */ | |
ed486911 | 38 | backendGetDeviceID( |
ef416fc2 | 39 | int fd, /* I - File descriptor */ |
40 | char *device_id, /* O - 1284 device ID */ | |
41 | int device_id_size, /* I - Size of buffer */ | |
42 | char *make_model, /* O - Make/model */ | |
43 | int make_model_size, /* I - Size of buffer */ | |
44 | const char *scheme, /* I - URI scheme */ | |
45 | char *uri, /* O - Device URI */ | |
46 | int uri_size) /* I - Size of buffer */ | |
47 | { | |
db0bd74a | 48 | #ifdef __APPLE__ /* This function is a no-op */ |
321d8d57 MS |
49 | (void)fd; |
50 | (void)device_id; | |
51 | (void)device_id_size; | |
52 | (void)make_model; | |
53 | (void)make_model_size; | |
54 | (void)scheme; | |
55 | (void)uri; | |
56 | (void)uri_size; | |
57 | ||
db0bd74a MS |
58 | return (-1); |
59 | ||
60 | #else /* Get the device ID from the specified file descriptor... */ | |
db0bd74a | 61 | # ifdef __linux |
ef416fc2 | 62 | int length; /* Length of device ID info */ |
2e4ff8af | 63 | int got_id = 0; |
db0bd74a MS |
64 | # endif /* __linux */ |
65 | # if defined(__sun) && defined(ECPPIOC_GETDEVID) | |
ef416fc2 | 66 | struct ecpp_device_id did; /* Device ID buffer */ |
db0bd74a | 67 | # endif /* __sun && ECPPIOC_GETDEVID */ |
f99f3698 | 68 | char *ptr; /* Pointer into device ID */ |
ef416fc2 | 69 | |
89d46774 | 70 | |
ed486911 | 71 | DEBUG_printf(("backendGetDeviceID(fd=%d, device_id=%p, device_id_size=%d, " |
ef416fc2 | 72 | "make_model=%p, make_model_size=%d, scheme=\"%s\", " |
73 | "uri=%p, uri_size=%d)\n", fd, device_id, device_id_size, | |
74 | make_model, make_model_size, scheme ? scheme : "(null)", | |
75 | uri, uri_size)); | |
76 | ||
77 | /* | |
78 | * Range check input... | |
79 | */ | |
80 | ||
f7deaa1a | 81 | if (!device_id || device_id_size < 32) |
ef416fc2 | 82 | { |
ed486911 | 83 | DEBUG_puts("backendGetDeviceID: Bad args!"); |
ef416fc2 | 84 | return (-1); |
85 | } | |
86 | ||
f7deaa1a | 87 | if (make_model) |
88 | *make_model = '\0'; | |
ef416fc2 | 89 | |
f7deaa1a | 90 | if (fd >= 0) |
ef416fc2 | 91 | { |
92 | /* | |
f7deaa1a | 93 | * Get the device ID string... |
ef416fc2 | 94 | */ |
95 | ||
f7deaa1a | 96 | *device_id = '\0'; |
ef416fc2 | 97 | |
db0bd74a | 98 | # ifdef __linux |
5eb9da71 | 99 | if (ioctl(fd, LPIOC_GET_DEVICE_ID(device_id_size), device_id)) |
2e4ff8af | 100 | { |
2e4ff8af | 101 | /* |
5eb9da71 MS |
102 | * Linux has to implement things differently for every device it seems. |
103 | * Since the standard parallel port driver does not provide a simple | |
104 | * ioctl() to get the 1284 device ID, we have to open the "raw" parallel | |
105 | * device corresponding to this port and do some negotiation trickery | |
106 | * to get the current device ID. | |
2e4ff8af MS |
107 | */ |
108 | ||
5eb9da71 | 109 | if (uri && !strncmp(uri, "parallel:/dev/", 14)) |
2e4ff8af | 110 | { |
5eb9da71 MS |
111 | char devparport[16]; /* /dev/parportN */ |
112 | int devparportfd, /* File descriptor for raw device */ | |
113 | mode; /* Port mode */ | |
114 | ||
115 | ||
2e4ff8af | 116 | /* |
5eb9da71 MS |
117 | * Since the Linux parallel backend only supports 4 parallel port |
118 | * devices, just grab the trailing digit and use it to construct a | |
119 | * /dev/parportN filename... | |
120 | */ | |
2e4ff8af | 121 | |
5eb9da71 MS |
122 | snprintf(devparport, sizeof(devparport), "/dev/parport%s", |
123 | uri + strlen(uri) - 1); | |
2e4ff8af | 124 | |
5eb9da71 MS |
125 | if ((devparportfd = open(devparport, O_RDWR | O_NOCTTY)) != -1) |
126 | { | |
127 | /* | |
128 | * Claim the device... | |
129 | */ | |
2e4ff8af | 130 | |
5eb9da71 | 131 | if (!ioctl(devparportfd, PPCLAIM)) |
2e4ff8af | 132 | { |
5eb9da71 | 133 | fcntl(devparportfd, F_SETFL, fcntl(devparportfd, F_GETFL) | O_NONBLOCK); |
2e4ff8af | 134 | |
5eb9da71 | 135 | mode = IEEE1284_MODE_COMPAT; |
2e4ff8af MS |
136 | |
137 | if (!ioctl(devparportfd, PPNEGOT, &mode)) | |
138 | { | |
139 | /* | |
5eb9da71 | 140 | * Put the device into Device ID mode... |
2e4ff8af MS |
141 | */ |
142 | ||
5eb9da71 MS |
143 | mode = IEEE1284_MODE_NIBBLE | IEEE1284_DEVICEID; |
144 | ||
145 | if (!ioctl(devparportfd, PPNEGOT, &mode)) | |
146 | { | |
147 | /* | |
148 | * Read the 1284 device ID... | |
149 | */ | |
150 | ||
151 | if ((length = read(devparportfd, device_id, | |
152 | device_id_size - 1)) >= 2) | |
153 | { | |
154 | device_id[length] = '\0'; | |
155 | got_id = 1; | |
156 | } | |
2e4ff8af MS |
157 | } |
158 | } | |
2e4ff8af | 159 | |
5eb9da71 MS |
160 | /* |
161 | * Release the device... | |
162 | */ | |
2e4ff8af | 163 | |
5eb9da71 MS |
164 | ioctl(devparportfd, PPRELEASE); |
165 | } | |
2e4ff8af | 166 | |
5eb9da71 MS |
167 | close(devparportfd); |
168 | } | |
2e4ff8af MS |
169 | } |
170 | } | |
5eb9da71 MS |
171 | else |
172 | got_id = 1; | |
2e4ff8af | 173 | |
5eb9da71 | 174 | if (got_id) |
f7deaa1a | 175 | { |
176 | /* | |
177 | * Extract the length of the device ID string from the first two | |
178 | * bytes. The 1284 spec says the length is stored MSB first... | |
179 | */ | |
ef416fc2 | 180 | |
f7deaa1a | 181 | length = (((unsigned)device_id[0] & 255) << 8) + |
182 | ((unsigned)device_id[1] & 255); | |
ef416fc2 | 183 | |
f7deaa1a | 184 | /* |
185 | * Check to see if the length is larger than our buffer; first | |
186 | * assume that the vendor incorrectly implemented the 1284 spec, | |
187 | * and then limit the length to the size of our buffer... | |
188 | */ | |
ef416fc2 | 189 | |
f99f3698 | 190 | if (length > device_id_size || length < 14) |
f7deaa1a | 191 | length = (((unsigned)device_id[1] & 255) << 8) + |
192 | ((unsigned)device_id[0] & 255); | |
ef416fc2 | 193 | |
ee6ddad2 MS |
194 | if (length > device_id_size) |
195 | length = device_id_size; | |
196 | ||
197 | /* | |
198 | * The length field counts the number of bytes in the string | |
39ff2fe7 MS |
199 | * including the length field itself (2 bytes). The minimum |
200 | * length for a valid/usable device ID is 14 bytes: | |
201 | * | |
202 | * <LENGTH> MFG: <MFG> ;MDL: <MDL> ; | |
203 | * 2 + 4 + 1 + 5 + 1 + 1 | |
ee6ddad2 MS |
204 | */ |
205 | ||
39ff2fe7 MS |
206 | if (length < 14) |
207 | { | |
208 | /* | |
209 | * Can't use this device ID, so don't try to copy it... | |
210 | */ | |
f7deaa1a | 211 | |
39ff2fe7 MS |
212 | device_id[0] = '\0'; |
213 | got_id = 0; | |
214 | } | |
215 | else | |
216 | { | |
217 | /* | |
218 | * Copy the device ID text to the beginning of the buffer and | |
219 | * nul-terminate. | |
220 | */ | |
f7deaa1a | 221 | |
39ff2fe7 MS |
222 | length -= 2; |
223 | ||
224 | memmove(device_id, device_id + 2, length); | |
225 | device_id[length] = '\0'; | |
226 | } | |
f7deaa1a | 227 | } |
f7deaa1a | 228 | else |
f99f3698 | 229 | { |
557dde9f MS |
230 | DEBUG_printf(("backendGetDeviceID: ioctl failed - %s\n", |
231 | strerror(errno))); | |
f99f3698 MS |
232 | *device_id = '\0'; |
233 | } | |
db0bd74a | 234 | # endif /* __linux */ |
ef416fc2 | 235 | |
db0bd74a | 236 | # if defined(__sun) && defined(ECPPIOC_GETDEVID) |
f7deaa1a | 237 | did.mode = ECPP_CENTRONICS; |
238 | did.len = device_id_size - 1; | |
239 | did.rlen = 0; | |
240 | did.addr = device_id; | |
ef416fc2 | 241 | |
f7deaa1a | 242 | if (!ioctl(fd, ECPPIOC_GETDEVID, &did)) |
243 | { | |
244 | /* | |
245 | * Nul-terminate the device ID text. | |
246 | */ | |
ef416fc2 | 247 | |
f7deaa1a | 248 | if (did.rlen < (device_id_size - 1)) |
249 | device_id[did.rlen] = '\0'; | |
250 | else | |
251 | device_id[device_id_size - 1] = '\0'; | |
252 | } | |
db0bd74a | 253 | # ifdef DEBUG |
f7deaa1a | 254 | else |
557dde9f MS |
255 | DEBUG_printf(("backendGetDeviceID: ioctl failed - %s\n", |
256 | strerror(errno))); | |
db0bd74a MS |
257 | # endif /* DEBUG */ |
258 | # endif /* __sun && ECPPIOC_GETDEVID */ | |
f7deaa1a | 259 | } |
ef416fc2 | 260 | |
f99f3698 MS |
261 | /* |
262 | * Check whether device ID is valid. Turn line breaks and tabs to spaces and | |
263 | * reject device IDs with non-printable characters. | |
264 | */ | |
265 | ||
266 | for (ptr = device_id; *ptr; ptr ++) | |
267 | if (_cups_isspace(*ptr)) | |
268 | *ptr = ' '; | |
269 | else if ((*ptr & 255) < ' ' || *ptr == 127) | |
270 | { | |
271 | DEBUG_printf(("backendGetDeviceID: Bad device_id character %d.", | |
272 | *ptr & 255)); | |
273 | *device_id = '\0'; | |
274 | break; | |
275 | } | |
276 | ||
ed486911 | 277 | DEBUG_printf(("backendGetDeviceID: device_id=\"%s\"\n", device_id)); |
ef416fc2 | 278 | |
2e4ff8af MS |
279 | if (scheme && uri) |
280 | *uri = '\0'; | |
281 | ||
ef416fc2 | 282 | if (!*device_id) |
283 | return (-1); | |
284 | ||
89d46774 | 285 | /* |
286 | * Get the make and model... | |
287 | */ | |
288 | ||
f7deaa1a | 289 | if (make_model) |
290 | backendGetMakeModel(device_id, make_model, make_model_size); | |
89d46774 | 291 | |
292 | /* | |
293 | * Then generate a device URI... | |
294 | */ | |
295 | ||
296 | if (scheme && uri && uri_size > 32) | |
297 | { | |
5eb9da71 MS |
298 | int num_values; /* Number of keys and values */ |
299 | cups_option_t *values; /* Keys and values in device ID */ | |
300 | const char *mfg, /* Manufacturer */ | |
301 | *mdl, /* Model */ | |
302 | *sern; /* Serial number */ | |
303 | char temp[256], /* Temporary manufacturer string */ | |
304 | *tempptr; /* Pointer into temp string */ | |
89d46774 | 305 | |
89d46774 | 306 | |
307 | /* | |
5eb9da71 | 308 | * Get the make, model, and serial numbers... |
89d46774 | 309 | */ |
310 | ||
f8b3a85b | 311 | num_values = _cupsGet1284Values(device_id, &values); |
89d46774 | 312 | |
5eb9da71 MS |
313 | if ((sern = cupsGetOption("SERIALNUMBER", num_values, values)) == NULL) |
314 | if ((sern = cupsGetOption("SERN", num_values, values)) == NULL) | |
315 | sern = cupsGetOption("SN", num_values, values); | |
89d46774 | 316 | |
5eb9da71 MS |
317 | if ((mfg = cupsGetOption("MANUFACTURER", num_values, values)) == NULL) |
318 | mfg = cupsGetOption("MFG", num_values, values); | |
89d46774 | 319 | |
5eb9da71 MS |
320 | if ((mdl = cupsGetOption("MODEL", num_values, values)) == NULL) |
321 | mdl = cupsGetOption("MDL", num_values, values); | |
89d46774 | 322 | |
5eb9da71 MS |
323 | if (mfg) |
324 | { | |
88f9aafc | 325 | if (!_cups_strcasecmp(mfg, "Hewlett-Packard")) |
5eb9da71 | 326 | mfg = "HP"; |
88f9aafc | 327 | else if (!_cups_strcasecmp(mfg, "Lexmark International")) |
5eb9da71 | 328 | mfg = "Lexmark"; |
89d46774 | 329 | } |
330 | else | |
331 | { | |
5eb9da71 | 332 | strlcpy(temp, make_model, sizeof(temp)); |
89d46774 | 333 | |
5eb9da71 MS |
334 | if ((tempptr = strchr(temp, ' ')) != NULL) |
335 | *tempptr = '\0'; | |
89d46774 | 336 | |
5eb9da71 | 337 | mfg = temp; |
89d46774 | 338 | } |
89d46774 | 339 | |
39ff2fe7 MS |
340 | if (!mdl) |
341 | mdl = ""; | |
342 | ||
88f9aafc | 343 | if (!_cups_strncasecmp(mdl, mfg, strlen(mfg))) |
4a4b4f99 MS |
344 | { |
345 | mdl += strlen(mfg); | |
346 | ||
347 | while (isspace(*mdl & 255)) | |
348 | mdl ++; | |
349 | } | |
350 | ||
5eb9da71 MS |
351 | /* |
352 | * Generate the device URI from the manufacturer, make_model, and | |
353 | * serial number strings. | |
354 | */ | |
89d46774 | 355 | |
5eb9da71 MS |
356 | httpAssembleURIf(HTTP_URI_CODING_ALL, uri, uri_size, scheme, NULL, mfg, 0, |
357 | "/%s%s%s", mdl, sern ? "?serial=" : "", sern ? sern : ""); | |
89d46774 | 358 | |
5eb9da71 | 359 | cupsFreeOptions(num_values, values); |
89d46774 | 360 | } |
361 | ||
362 | return (0); | |
db0bd74a | 363 | #endif /* __APPLE__ */ |
89d46774 | 364 | } |
89d46774 | 365 | |
366 | ||
367 | /* | |
ed486911 | 368 | * 'backendGetMakeModel()' - Get the make and model string from the device ID. |
89d46774 | 369 | */ |
370 | ||
371 | int /* O - 0 on success, -1 on failure */ | |
ed486911 | 372 | backendGetMakeModel( |
89d46774 | 373 | const char *device_id, /* O - 1284 device ID */ |
374 | char *make_model, /* O - Make/model */ | |
375 | int make_model_size) /* I - Size of buffer */ | |
376 | { | |
5eb9da71 MS |
377 | int num_values; /* Number of keys and values */ |
378 | cups_option_t *values; /* Keys and values */ | |
379 | const char *mfg, /* Manufacturer string */ | |
380 | *mdl, /* Model string */ | |
381 | *des; /* Description string */ | |
89d46774 | 382 | |
383 | ||
ed486911 | 384 | DEBUG_printf(("backendGetMakeModel(device_id=\"%s\", " |
89d46774 | 385 | "make_model=%p, make_model_size=%d)\n", device_id, |
386 | make_model, make_model_size)); | |
387 | ||
388 | /* | |
389 | * Range check input... | |
390 | */ | |
391 | ||
392 | if (!device_id || !*device_id || !make_model || make_model_size < 32) | |
393 | { | |
ed486911 | 394 | DEBUG_puts("backendGetMakeModel: Bad args!"); |
89d46774 | 395 | return (-1); |
396 | } | |
397 | ||
398 | *make_model = '\0'; | |
399 | ||
ef416fc2 | 400 | /* |
401 | * Look for the description field... | |
402 | */ | |
403 | ||
f8b3a85b | 404 | num_values = _cupsGet1284Values(device_id, &values); |
ef416fc2 | 405 | |
5eb9da71 MS |
406 | if ((mdl = cupsGetOption("MODEL", num_values, values)) == NULL) |
407 | mdl = cupsGetOption("MDL", num_values, values); | |
ef416fc2 | 408 | |
89d46774 | 409 | if (mdl) |
ef416fc2 | 410 | { |
411 | /* | |
89d46774 | 412 | * Build a make-model string from the manufacturer and model attributes... |
ef416fc2 | 413 | */ |
414 | ||
5eb9da71 MS |
415 | if ((mfg = cupsGetOption("MANUFACTURER", num_values, values)) == NULL) |
416 | mfg = cupsGetOption("MFG", num_values, values); | |
db0bd74a | 417 | |
88f9aafc | 418 | if (!mfg || !_cups_strncasecmp(mdl, mfg, strlen(mfg))) |
5eb9da71 | 419 | { |
db0bd74a | 420 | /* |
5eb9da71 | 421 | * Just copy the model string, since it has the manufacturer... |
db0bd74a MS |
422 | */ |
423 | ||
5eb9da71 | 424 | _ppdNormalizeMakeAndModel(mdl, make_model, make_model_size); |
ef416fc2 | 425 | } |
426 | else | |
427 | { | |
89d46774 | 428 | /* |
5eb9da71 | 429 | * Concatenate the make and model... |
89d46774 | 430 | */ |
431 | ||
5eb9da71 | 432 | char temp[1024]; /* Temporary make and model */ |
db0bd74a | 433 | |
321d8d57 | 434 | snprintf(temp, sizeof(temp), "%s %s", mfg, mdl); |
39ff2fe7 | 435 | |
5eb9da71 | 436 | _ppdNormalizeMakeAndModel(temp, make_model, make_model_size); |
ef416fc2 | 437 | } |
438 | } | |
5eb9da71 MS |
439 | else if ((des = cupsGetOption("DESCRIPTION", num_values, values)) != NULL || |
440 | (des = cupsGetOption("DES", num_values, values)) != NULL) | |
ef416fc2 | 441 | { |
442 | /* | |
5eb9da71 MS |
443 | * Make sure the description contains something useful, since some |
444 | * printer manufacturers (HP) apparently don't follow the standards | |
445 | * they helped to define... | |
446 | * | |
447 | * Here we require the description to be 8 or more characters in length, | |
448 | * containing at least one space and one letter. | |
ef416fc2 | 449 | */ |
450 | ||
5eb9da71 | 451 | if (strlen(des) >= 8) |
ef416fc2 | 452 | { |
5eb9da71 MS |
453 | const char *ptr; /* Pointer into description */ |
454 | int letters, /* Number of letters seen */ | |
455 | spaces; /* Number of spaces seen */ | |
ef416fc2 | 456 | |
5eb9da71 MS |
457 | |
458 | for (ptr = des, letters = 0, spaces = 0; *ptr; ptr ++) | |
459 | { | |
460 | if (isspace(*ptr & 255)) | |
461 | spaces ++; | |
462 | else if (isalpha(*ptr & 255)) | |
463 | letters ++; | |
464 | ||
465 | if (spaces && letters) | |
466 | break; | |
467 | } | |
468 | ||
469 | if (spaces && letters) | |
470 | _ppdNormalizeMakeAndModel(des, make_model, make_model_size); | |
ef416fc2 | 471 | } |
472 | } | |
5eb9da71 MS |
473 | |
474 | if (!make_model[0]) | |
ef416fc2 | 475 | { |
476 | /* | |
477 | * Use "Unknown" as the printer make and model... | |
478 | */ | |
479 | ||
480 | strlcpy(make_model, "Unknown", make_model_size); | |
481 | } | |
482 | ||
5eb9da71 | 483 | cupsFreeOptions(num_values, values); |
ef416fc2 | 484 | |
5eb9da71 | 485 | return (0); |
ef416fc2 | 486 | } |
487 | ||
488 | ||
489 | /* | |
75bd9771 | 490 | * End of "$Id: ieee1284.c 7687 2008-06-24 01:28:36Z mike $". |
ef416fc2 | 491 | */ |