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