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