]>
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 | * |
71e16022 | 6 | * Copyright 2007-2010 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 MS |
48 | #ifdef __APPLE__ /* This function is a no-op */ |
49 | return (-1); | |
50 | ||
51 | #else /* Get the device ID from the specified file descriptor... */ | |
db0bd74a | 52 | # ifdef __linux |
ef416fc2 | 53 | int length; /* Length of device ID info */ |
2e4ff8af | 54 | int got_id = 0; |
db0bd74a MS |
55 | # endif /* __linux */ |
56 | # if defined(__sun) && defined(ECPPIOC_GETDEVID) | |
ef416fc2 | 57 | struct ecpp_device_id did; /* Device ID buffer */ |
db0bd74a | 58 | # endif /* __sun && ECPPIOC_GETDEVID */ |
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 |
5eb9da71 | 89 | if (ioctl(fd, LPIOC_GET_DEVICE_ID(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 | ||
141 | if ((length = read(devparportfd, device_id, | |
142 | device_id_size - 1)) >= 2) | |
143 | { | |
144 | device_id[length] = '\0'; | |
145 | got_id = 1; | |
146 | } | |
2e4ff8af MS |
147 | } |
148 | } | |
2e4ff8af | 149 | |
5eb9da71 MS |
150 | /* |
151 | * Release the device... | |
152 | */ | |
2e4ff8af | 153 | |
5eb9da71 MS |
154 | ioctl(devparportfd, PPRELEASE); |
155 | } | |
2e4ff8af | 156 | |
5eb9da71 MS |
157 | close(devparportfd); |
158 | } | |
2e4ff8af MS |
159 | } |
160 | } | |
5eb9da71 MS |
161 | else |
162 | got_id = 1; | |
2e4ff8af | 163 | |
5eb9da71 | 164 | if (got_id) |
f7deaa1a | 165 | { |
166 | /* | |
167 | * Extract the length of the device ID string from the first two | |
168 | * bytes. The 1284 spec says the length is stored MSB first... | |
169 | */ | |
ef416fc2 | 170 | |
f7deaa1a | 171 | length = (((unsigned)device_id[0] & 255) << 8) + |
172 | ((unsigned)device_id[1] & 255); | |
ef416fc2 | 173 | |
f7deaa1a | 174 | /* |
175 | * Check to see if the length is larger than our buffer; first | |
176 | * assume that the vendor incorrectly implemented the 1284 spec, | |
177 | * and then limit the length to the size of our buffer... | |
178 | */ | |
ef416fc2 | 179 | |
ee6ddad2 | 180 | if (length > device_id_size) |
f7deaa1a | 181 | length = (((unsigned)device_id[1] & 255) << 8) + |
182 | ((unsigned)device_id[0] & 255); | |
ef416fc2 | 183 | |
ee6ddad2 MS |
184 | if (length > device_id_size) |
185 | length = device_id_size; | |
186 | ||
187 | /* | |
188 | * The length field counts the number of bytes in the string | |
189 | * including the length field itself (2 bytes). | |
190 | */ | |
191 | ||
192 | length -= 2; | |
f7deaa1a | 193 | |
194 | /* | |
195 | * Copy the device ID text to the beginning of the buffer and | |
196 | * nul-terminate. | |
197 | */ | |
198 | ||
199 | memmove(device_id, device_id + 2, length); | |
200 | device_id[length] = '\0'; | |
201 | } | |
db0bd74a | 202 | # ifdef DEBUG |
f7deaa1a | 203 | else |
557dde9f MS |
204 | DEBUG_printf(("backendGetDeviceID: ioctl failed - %s\n", |
205 | strerror(errno))); | |
db0bd74a MS |
206 | # endif /* DEBUG */ |
207 | # endif /* __linux */ | |
ef416fc2 | 208 | |
db0bd74a | 209 | # if defined(__sun) && defined(ECPPIOC_GETDEVID) |
f7deaa1a | 210 | did.mode = ECPP_CENTRONICS; |
211 | did.len = device_id_size - 1; | |
212 | did.rlen = 0; | |
213 | did.addr = device_id; | |
ef416fc2 | 214 | |
f7deaa1a | 215 | if (!ioctl(fd, ECPPIOC_GETDEVID, &did)) |
216 | { | |
217 | /* | |
218 | * Nul-terminate the device ID text. | |
219 | */ | |
ef416fc2 | 220 | |
f7deaa1a | 221 | if (did.rlen < (device_id_size - 1)) |
222 | device_id[did.rlen] = '\0'; | |
223 | else | |
224 | device_id[device_id_size - 1] = '\0'; | |
225 | } | |
db0bd74a | 226 | # ifdef DEBUG |
f7deaa1a | 227 | else |
557dde9f MS |
228 | DEBUG_printf(("backendGetDeviceID: ioctl failed - %s\n", |
229 | strerror(errno))); | |
db0bd74a MS |
230 | # endif /* DEBUG */ |
231 | # endif /* __sun && ECPPIOC_GETDEVID */ | |
f7deaa1a | 232 | } |
ef416fc2 | 233 | |
ed486911 | 234 | DEBUG_printf(("backendGetDeviceID: device_id=\"%s\"\n", device_id)); |
ef416fc2 | 235 | |
2e4ff8af MS |
236 | if (scheme && uri) |
237 | *uri = '\0'; | |
238 | ||
ef416fc2 | 239 | if (!*device_id) |
240 | return (-1); | |
241 | ||
89d46774 | 242 | /* |
243 | * Get the make and model... | |
244 | */ | |
245 | ||
f7deaa1a | 246 | if (make_model) |
247 | backendGetMakeModel(device_id, make_model, make_model_size); | |
89d46774 | 248 | |
249 | /* | |
250 | * Then generate a device URI... | |
251 | */ | |
252 | ||
253 | if (scheme && uri && uri_size > 32) | |
254 | { | |
5eb9da71 MS |
255 | int num_values; /* Number of keys and values */ |
256 | cups_option_t *values; /* Keys and values in device ID */ | |
257 | const char *mfg, /* Manufacturer */ | |
258 | *mdl, /* Model */ | |
259 | *sern; /* Serial number */ | |
260 | char temp[256], /* Temporary manufacturer string */ | |
261 | *tempptr; /* Pointer into temp string */ | |
89d46774 | 262 | |
89d46774 | 263 | |
264 | /* | |
5eb9da71 | 265 | * Get the make, model, and serial numbers... |
89d46774 | 266 | */ |
267 | ||
f8b3a85b | 268 | num_values = _cupsGet1284Values(device_id, &values); |
89d46774 | 269 | |
5eb9da71 MS |
270 | if ((sern = cupsGetOption("SERIALNUMBER", num_values, values)) == NULL) |
271 | if ((sern = cupsGetOption("SERN", num_values, values)) == NULL) | |
272 | sern = cupsGetOption("SN", num_values, values); | |
89d46774 | 273 | |
5eb9da71 MS |
274 | if ((mfg = cupsGetOption("MANUFACTURER", num_values, values)) == NULL) |
275 | mfg = cupsGetOption("MFG", num_values, values); | |
89d46774 | 276 | |
5eb9da71 MS |
277 | if ((mdl = cupsGetOption("MODEL", num_values, values)) == NULL) |
278 | mdl = cupsGetOption("MDL", num_values, values); | |
89d46774 | 279 | |
5eb9da71 MS |
280 | if (mfg) |
281 | { | |
282 | if (!strcasecmp(mfg, "Hewlett-Packard")) | |
283 | mfg = "HP"; | |
284 | else if (!strcasecmp(mfg, "Lexmark International")) | |
285 | mfg = "Lexmark"; | |
89d46774 | 286 | } |
287 | else | |
288 | { | |
5eb9da71 | 289 | strlcpy(temp, make_model, sizeof(temp)); |
89d46774 | 290 | |
5eb9da71 MS |
291 | if ((tempptr = strchr(temp, ' ')) != NULL) |
292 | *tempptr = '\0'; | |
89d46774 | 293 | |
5eb9da71 | 294 | mfg = temp; |
89d46774 | 295 | } |
89d46774 | 296 | |
4a4b4f99 MS |
297 | if (!strncasecmp(mdl, mfg, strlen(mfg))) |
298 | { | |
299 | mdl += strlen(mfg); | |
300 | ||
301 | while (isspace(*mdl & 255)) | |
302 | mdl ++; | |
303 | } | |
304 | ||
5eb9da71 MS |
305 | /* |
306 | * Generate the device URI from the manufacturer, make_model, and | |
307 | * serial number strings. | |
308 | */ | |
89d46774 | 309 | |
5eb9da71 MS |
310 | httpAssembleURIf(HTTP_URI_CODING_ALL, uri, uri_size, scheme, NULL, mfg, 0, |
311 | "/%s%s%s", mdl, sern ? "?serial=" : "", sern ? sern : ""); | |
89d46774 | 312 | |
5eb9da71 | 313 | cupsFreeOptions(num_values, values); |
89d46774 | 314 | } |
315 | ||
316 | return (0); | |
db0bd74a | 317 | #endif /* __APPLE__ */ |
89d46774 | 318 | } |
89d46774 | 319 | |
320 | ||
321 | /* | |
ed486911 | 322 | * 'backendGetMakeModel()' - Get the make and model string from the device ID. |
89d46774 | 323 | */ |
324 | ||
325 | int /* O - 0 on success, -1 on failure */ | |
ed486911 | 326 | backendGetMakeModel( |
89d46774 | 327 | const char *device_id, /* O - 1284 device ID */ |
328 | char *make_model, /* O - Make/model */ | |
329 | int make_model_size) /* I - Size of buffer */ | |
330 | { | |
5eb9da71 MS |
331 | int num_values; /* Number of keys and values */ |
332 | cups_option_t *values; /* Keys and values */ | |
333 | const char *mfg, /* Manufacturer string */ | |
334 | *mdl, /* Model string */ | |
335 | *des; /* Description string */ | |
89d46774 | 336 | |
337 | ||
ed486911 | 338 | DEBUG_printf(("backendGetMakeModel(device_id=\"%s\", " |
89d46774 | 339 | "make_model=%p, make_model_size=%d)\n", device_id, |
340 | make_model, make_model_size)); | |
341 | ||
342 | /* | |
343 | * Range check input... | |
344 | */ | |
345 | ||
346 | if (!device_id || !*device_id || !make_model || make_model_size < 32) | |
347 | { | |
ed486911 | 348 | DEBUG_puts("backendGetMakeModel: Bad args!"); |
89d46774 | 349 | return (-1); |
350 | } | |
351 | ||
352 | *make_model = '\0'; | |
353 | ||
ef416fc2 | 354 | /* |
355 | * Look for the description field... | |
356 | */ | |
357 | ||
f8b3a85b | 358 | num_values = _cupsGet1284Values(device_id, &values); |
ef416fc2 | 359 | |
5eb9da71 MS |
360 | if ((mdl = cupsGetOption("MODEL", num_values, values)) == NULL) |
361 | mdl = cupsGetOption("MDL", num_values, values); | |
ef416fc2 | 362 | |
89d46774 | 363 | if (mdl) |
ef416fc2 | 364 | { |
365 | /* | |
89d46774 | 366 | * Build a make-model string from the manufacturer and model attributes... |
ef416fc2 | 367 | */ |
368 | ||
5eb9da71 MS |
369 | if ((mfg = cupsGetOption("MANUFACTURER", num_values, values)) == NULL) |
370 | mfg = cupsGetOption("MFG", num_values, values); | |
db0bd74a | 371 | |
5eb9da71 MS |
372 | if (!mfg || !strncasecmp(mdl, mfg, strlen(mfg))) |
373 | { | |
db0bd74a | 374 | /* |
5eb9da71 | 375 | * Just copy the model string, since it has the manufacturer... |
db0bd74a MS |
376 | */ |
377 | ||
5eb9da71 | 378 | _ppdNormalizeMakeAndModel(mdl, make_model, make_model_size); |
ef416fc2 | 379 | } |
380 | else | |
381 | { | |
89d46774 | 382 | /* |
5eb9da71 | 383 | * Concatenate the make and model... |
89d46774 | 384 | */ |
385 | ||
5eb9da71 | 386 | char temp[1024]; /* Temporary make and model */ |
db0bd74a | 387 | |
5eb9da71 MS |
388 | snprintf(temp, sizeof(temp), "%s %s", mfg, mdl); |
389 | _ppdNormalizeMakeAndModel(temp, make_model, make_model_size); | |
ef416fc2 | 390 | } |
391 | } | |
5eb9da71 MS |
392 | else if ((des = cupsGetOption("DESCRIPTION", num_values, values)) != NULL || |
393 | (des = cupsGetOption("DES", num_values, values)) != NULL) | |
ef416fc2 | 394 | { |
395 | /* | |
5eb9da71 MS |
396 | * Make sure the description contains something useful, since some |
397 | * printer manufacturers (HP) apparently don't follow the standards | |
398 | * they helped to define... | |
399 | * | |
400 | * Here we require the description to be 8 or more characters in length, | |
401 | * containing at least one space and one letter. | |
ef416fc2 | 402 | */ |
403 | ||
5eb9da71 | 404 | if (strlen(des) >= 8) |
ef416fc2 | 405 | { |
5eb9da71 MS |
406 | const char *ptr; /* Pointer into description */ |
407 | int letters, /* Number of letters seen */ | |
408 | spaces; /* Number of spaces seen */ | |
ef416fc2 | 409 | |
5eb9da71 MS |
410 | |
411 | for (ptr = des, letters = 0, spaces = 0; *ptr; ptr ++) | |
412 | { | |
413 | if (isspace(*ptr & 255)) | |
414 | spaces ++; | |
415 | else if (isalpha(*ptr & 255)) | |
416 | letters ++; | |
417 | ||
418 | if (spaces && letters) | |
419 | break; | |
420 | } | |
421 | ||
422 | if (spaces && letters) | |
423 | _ppdNormalizeMakeAndModel(des, make_model, make_model_size); | |
ef416fc2 | 424 | } |
425 | } | |
5eb9da71 MS |
426 | |
427 | if (!make_model[0]) | |
ef416fc2 | 428 | { |
429 | /* | |
430 | * Use "Unknown" as the printer make and model... | |
431 | */ | |
432 | ||
433 | strlcpy(make_model, "Unknown", make_model_size); | |
434 | } | |
435 | ||
5eb9da71 | 436 | cupsFreeOptions(num_values, values); |
ef416fc2 | 437 | |
5eb9da71 | 438 | return (0); |
ef416fc2 | 439 | } |
440 | ||
441 | ||
442 | /* | |
75bd9771 | 443 | * End of "$Id: ieee1284.c 7687 2008-06-24 01:28:36Z mike $". |
ef416fc2 | 444 | */ |