1/* 2 * "$Id: usb-unix.c 11093 2013-07-03 20:48:42Z msweet $" 3 * 4 * USB port backend for CUPS. 5 * 6 * This file is included from "usb.c" when compiled on UNIX/Linux. 7 * 8 * Copyright 2007-2012 by Apple Inc. 9 * Copyright 1997-2007 by Easy Software Products, all rights reserved. 10 * 11 * These coded instructions, statements, and computer programs are the 12 * property of Apple Inc. and are protected by Federal copyright 13 * law. Distribution and use rights are outlined in the file "LICENSE.txt" 14 * "LICENSE" which should have been included with this file. If this 15 * file is missing or damaged, see the license at "http://www.cups.org/". 16 * 17 * This file is subject to the Apple OS-Developed Software exception. 18 * 19 * Contents: 20 * 21 * print_device() - Print a file to a USB device. 22 * list_devices() - List all USB devices. 23 * open_device() - Open a USB device... 24 * side_cb() - Handle side-channel requests... 25 */ 26 27/* 28 * Include necessary headers. 29 */ 30 31#include <sys/select.h> 32 33 34/* 35 * Local functions... 36 */ 37 38static int open_device(const char *uri, int *use_bc); 39static int side_cb(int print_fd, int device_fd, int snmp_fd, 40 http_addr_t *addr, int use_bc); 41 42 43/* 44 * 'print_device()' - Print a file to a USB device. 45 */ 46 47int /* O - Exit status */ 48print_device(const char *uri, /* I - Device URI */ 49 const char *hostname, /* I - Hostname/manufacturer */ 50 const char *resource, /* I - Resource/modelname */ 51 char *options, /* I - Device options/serial number */ 52 int print_fd, /* I - File descriptor to print */ 53 int copies, /* I - Copies to print */ 54 int argc, /* I - Number of command-line arguments (6 or 7) */ 55 char *argv[]) /* I - Command-line arguments */ 56{ 57 int use_bc; /* Use backchannel path? */ 58 int device_fd; /* USB device */ 59 ssize_t tbytes; /* Total number of bytes written */ 60 struct termios opts; /* Parallel port options */ 61 62 63 (void)argc; 64 (void)argv; 65 66 /* 67 * Open the USB port device... 68 */ 69 70 fputs("STATE: +connecting-to-device\n", stderr); 71 72 do 73 { 74#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) 75 /* 76 * *BSD's ulpt driver currently does not support the 77 * back-channel, incorrectly returns data ready on a select(), 78 * and locks up on read()... 79 */ 80 81 use_bc = 0; 82 83#elif defined(__sun) 84 /* 85 * CUPS STR #3028: Solaris' usbprn driver apparently does not support 86 * select() or poll(), so we can't support backchannel... 87 */ 88 89 use_bc = 0; 90 91#else 92 /* 93 * Disable backchannel data when printing to Brother, Canon, or 94 * Minolta USB printers - apparently these printers will return 95 * the IEEE-1284 device ID over and over and over when they get 96 * a read request... 97 */ 98 99 use_bc = _cups_strcasecmp(hostname, "Brother") && 100 _cups_strcasecmp(hostname, "Canon") && 101 _cups_strncasecmp(hostname, "Konica", 6) && 102 _cups_strncasecmp(hostname, "Minolta", 7); 103#endif /* __FreeBSD__ || __NetBSD__ || __OpenBSD__ || __DragonFly__ */ 104 105 if ((device_fd = open_device(uri, &use_bc)) == -1) 106 { 107 if (getenv("CLASS") != NULL) 108 { 109 /* 110 * If the CLASS environment variable is set, the job was submitted 111 * to a class and not to a specific queue. In this case, we want 112 * to abort immediately so that the job can be requeued on the next 113 * available printer in the class. 114 */ 115 116 _cupsLangPrintFilter(stderr, "INFO", 117 _("Unable to contact printer, queuing on next " 118 "printer in class.")); 119 120 /* 121 * Sleep 5 seconds to keep the job from requeuing too rapidly... 122 */ 123 124 sleep(5); 125 126 return (CUPS_BACKEND_FAILED); 127 } 128 129 if (errno == EBUSY) 130 { 131 _cupsLangPrintFilter(stderr, "INFO", _("The printer is in use.")); 132 sleep(10); 133 } 134 else if (errno == ENXIO || errno == EIO || errno == ENOENT || 135 errno == ENODEV) 136 { 137 sleep(30); 138 } 139 else 140 { 141 _cupsLangPrintError("ERROR", _("Unable to open device file")); 142 return (CUPS_BACKEND_FAILED); 143 } 144 } 145 } 146 while (device_fd < 0); 147 148 fputs("STATE: -connecting-to-device\n", stderr); 149 150 /* 151 * Set any options provided... 152 */ 153 154 tcgetattr(device_fd, &opts); 155 156 opts.c_lflag &= ~(ICANON | ECHO | ISIG); /* Raw mode */ 157 158 /**** No options supported yet ****/ 159 160 tcsetattr(device_fd, TCSANOW, &opts); 161 162 /* 163 * Finally, send the print file... 164 */ 165 166 tbytes = 0; 167 168 while (copies > 0 && tbytes >= 0) 169 { 170 copies --; 171 172 if (print_fd != 0) 173 { 174 fputs("PAGE: 1 1\n", stderr); 175 lseek(print_fd, 0, SEEK_SET); 176 } 177 178#ifdef __sun 179 /* 180 * CUPS STR #3028: Solaris' usbprn driver apparently does not support 181 * select() or poll(), so we can't support the sidechannel either... 182 */ 183 184 tbytes = backendRunLoop(print_fd, device_fd, -1, NULL, use_bc, 1, NULL); 185 186#else 187 tbytes = backendRunLoop(print_fd, device_fd, -1, NULL, use_bc, 1, side_cb); 188#endif /* __sun */ 189 190 if (print_fd != 0 && tbytes >= 0) 191 _cupsLangPrintFilter(stderr, "INFO", _("Print file sent.")); 192 } 193 194 /* 195 * Close the USB port and return... 196 */ 197 198 close(device_fd); 199 200 return (CUPS_BACKEND_OK); 201} 202 203 204/* 205 * 'list_devices()' - List all USB devices. 206 */ 207 208void 209list_devices(void) 210{ 211#ifdef __linux 212 int i; /* Looping var */ 213 int fd; /* File descriptor */ 214 char device[255], /* Device filename */ 215 device_id[1024], /* Device ID string */ 216 device_uri[1024], /* Device URI string */ 217 make_model[1024]; /* Make and model */ 218 219 220 /* 221 * Try to open each USB device... 222 */ 223 224 for (i = 0; i < 16; i ++) 225 { 226 /* 227 * Linux has a long history of changing the standard filenames used 228 * for USB printer devices. We get the honor of trying them all... 229 */ 230 231 sprintf(device, "/dev/usblp%d", i); 232 233 if ((fd = open(device, O_RDWR | O_EXCL)) < 0) 234 { 235 if (errno != ENOENT) 236 continue; 237 238 sprintf(device, "/dev/usb/lp%d", i); 239 240 if ((fd = open(device, O_RDWR | O_EXCL)) < 0) 241 { 242 if (errno != ENOENT) 243 continue; 244 245 sprintf(device, "/dev/usb/usblp%d", i); 246 247 if ((fd = open(device, O_RDWR | O_EXCL)) < 0) 248 continue; 249 } 250 } 251 252 if (!backendGetDeviceID(fd, device_id, sizeof(device_id), 253 make_model, sizeof(make_model), 254 "usb", device_uri, sizeof(device_uri))) 255 cupsBackendReport("direct", device_uri, make_model, make_model, 256 device_id, NULL); 257 258 close(fd); 259 } 260#elif defined(__sun) && defined(ECPPIOC_GETDEVID) 261 int i; /* Looping var */ 262 int fd; /* File descriptor */ 263 char device[255], /* Device filename */ 264 device_id[1024], /* Device ID string */ 265 device_uri[1024], /* Device URI string */ 266 make_model[1024]; /* Make and model */ 267 268 269 /* 270 * Open each USB device... 271 */ 272 273 for (i = 0; i < 8; i ++) 274 { 275 sprintf(device, "/dev/usb/printer%d", i); 276 277 if ((fd = open(device, O_WRONLY | O_EXCL)) >= 0) 278 { 279 if (!backendGetDeviceID(fd, device_id, sizeof(device_id), 280 make_model, sizeof(make_model), 281 "usb", device_uri, sizeof(device_uri))) 282 cupsBackendReport("direct", device_uri, make_model, make_model, 283 device_id, NULL); 284 285 close(fd); 286 } 287 } 288#elif defined(__hpux) 289#elif defined(__osf) 290#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) 291 int i; /* Looping var */ 292 char device[255]; /* Device filename */ 293 294 295 for (i = 0; i < 8; i ++) 296 { 297 sprintf(device, "/dev/ulpt%d", i); 298 if (!access(device, 0)) 299 printf("direct usb:%s \"Unknown\" \"USB Printer #%d\"\n", device, i + 1); 300 301 sprintf(device, "/dev/unlpt%d", i); 302 if (!access(device, 0)) 303 printf("direct usb:%s \"Unknown\" \"USB Printer #%d (no reset)\"\n", device, i + 1); 304 } 305#endif 306} 307 308 309/* 310 * 'open_device()' - Open a USB device... 311 */ 312 313static int /* O - File descriptor or -1 on error */ 314open_device(const char *uri, /* I - Device URI */ 315 int *use_bc) /* O - Set to 0 for unidirectional */ 316{ 317 int fd; /* File descriptor */ 318 319 320 /* 321 * The generic implementation just treats the URI as a device filename... 322 * Specific operating systems may also support using the device serial 323 * number and/or make/model. 324 */ 325 326 if (!strncmp(uri, "usb:/dev/", 9)) 327#ifdef __linux 328 { 329 /* 330 * Do not allow direct devices anymore... 331 */ 332 333 errno = ENODEV; 334 return (-1); 335 } 336 else if (!strncmp(uri, "usb://", 6)) 337 { 338 /* 339 * For Linux, try looking up the device serial number or model... 340 */ 341 342 int i; /* Looping var */ 343 int busy; /* Are any ports busy? */ 344 char device[255], /* Device filename */ 345 device_id[1024], /* Device ID string */ 346 make_model[1024], /* Make and model */ 347 device_uri[1024]; /* Device URI string */ 348 349 350 /* 351 * Find the correct USB device... 352 */ 353 354 for (;;) 355 { 356 for (busy = 0, i = 0; i < 16; i ++) 357 { 358 /* 359 * Linux has a long history of changing the standard filenames used 360 * for USB printer devices. We get the honor of trying them all... 361 */ 362 363 sprintf(device, "/dev/usblp%d", i); 364 365 if ((fd = open(device, O_RDWR | O_EXCL)) < 0 && errno == ENOENT) 366 { 367 sprintf(device, "/dev/usb/lp%d", i); 368 369 if ((fd = open(device, O_RDWR | O_EXCL)) < 0 && errno == ENOENT) 370 { 371 sprintf(device, "/dev/usb/usblp%d", i); 372 373 if ((fd = open(device, O_RDWR | O_EXCL)) < 0 && errno == ENOENT) 374 continue; 375 } 376 } 377 378 if (fd >= 0) 379 { 380 backendGetDeviceID(fd, device_id, sizeof(device_id), 381 make_model, sizeof(make_model), 382 "usb", device_uri, sizeof(device_uri)); 383 } 384 else 385 { 386 /* 387 * If the open failed because it was busy, flag it so we retry 388 * as needed... 389 */ 390 391 if (errno == EBUSY) 392 busy = 1; 393 394 device_uri[0] = '\0'; 395 } 396 397 if (!strcmp(uri, device_uri)) 398 { 399 /* 400 * Yes, return this file descriptor... 401 */ 402 403 fprintf(stderr, "DEBUG: Printer using device file \"%s\"...\n", 404 device); 405 406 return (fd); 407 } 408 409 /* 410 * This wasn't the one... 411 */ 412 413 if (fd >= 0) 414 close(fd); 415 } 416 417 /* 418 * If we get here and at least one of the printer ports showed up 419 * as "busy", then sleep for a bit and retry... 420 */ 421 422 if (busy) 423 _cupsLangPrintFilter(stderr, "INFO", _("The printer is in use.")); 424 425 sleep(5); 426 } 427 } 428#elif defined(__sun) && defined(ECPPIOC_GETDEVID) 429 { 430 /* 431 * Do not allow direct devices anymore... 432 */ 433 434 errno = ENODEV; 435 return (-1); 436 } 437 else if (!strncmp(uri, "usb://", 6)) 438 { 439 /* 440 * For Solaris, try looking up the device serial number or model... 441 */ 442 443 int i; /* Looping var */ 444 int busy; /* Are any ports busy? */ 445 char device[255], /* Device filename */ 446 device_id[1024], /* Device ID string */ 447 make_model[1024], /* Make and model */ 448 device_uri[1024]; /* Device URI string */ 449 450 451 /* 452 * Find the correct USB device... 453 */ 454 455 do 456 { 457 for (i = 0, busy = 0; i < 8; i ++) 458 { 459 sprintf(device, "/dev/usb/printer%d", i); 460 461 if ((fd = open(device, O_WRONLY | O_EXCL)) >= 0) 462 backendGetDeviceID(fd, device_id, sizeof(device_id), 463 make_model, sizeof(make_model), 464 "usb", device_uri, sizeof(device_uri)); 465 else 466 { 467 /* 468 * If the open failed because it was busy, flag it so we retry 469 * as needed... 470 */ 471 472 if (errno == EBUSY) 473 busy = 1; 474 475 device_uri[0] = '\0'; 476 } 477 478 if (!strcmp(uri, device_uri)) 479 { 480 /* 481 * Yes, return this file descriptor... 482 */ 483 484 fputs("DEBUG: Setting use_bc to 0!\n", stderr); 485 486 *use_bc = 0; 487 488 return (fd); 489 } 490 491 /* 492 * This wasn't the one... 493 */ 494 495 if (fd >= 0) 496 close(fd); 497 } 498 499 /* 500 * If we get here and at least one of the printer ports showed up 501 * as "busy", then sleep for a bit and retry... 502 */ 503 504 if (busy) 505 { 506 _cupsLangPrintFilter(stderr, "INFO", _("The printer is in use.")); 507 sleep(5); 508 } 509 } 510 while (busy); 511 512 /* 513 * Couldn't find the printer, return "no such device or address"... 514 */ 515 516 errno = ENODEV; 517 518 return (-1); 519 } 520#else 521 { 522 if (*use_bc) 523 fd = open(uri + 4, O_RDWR | O_EXCL); 524 else 525 fd = -1; 526 527 if (fd < 0) 528 { 529 fd = open(uri + 4, O_WRONLY | O_EXCL); 530 *use_bc = 0; 531 } 532 533 return (fd); 534 } 535#endif /* __linux */ 536 else 537 { 538 errno = ENODEV; 539 return (-1); 540 } 541} 542 543 544/* 545 * 'side_cb()' - Handle side-channel requests... 546 */ 547 548static int /* O - 0 on success, -1 on error */ 549side_cb(int print_fd, /* I - Print file */ 550 int device_fd, /* I - Device file */ 551 int snmp_fd, /* I - SNMP socket (unused) */ 552 http_addr_t *addr, /* I - Device address (unused) */ 553 int use_bc) /* I - Using back-channel? */ 554{ 555 cups_sc_command_t command; /* Request command */ 556 cups_sc_status_t status; /* Request/response status */ 557 char data[2048]; /* Request/response data */ 558 int datalen; /* Request/response data size */ 559 560 561 (void)snmp_fd; 562 (void)addr; 563 564 datalen = sizeof(data); 565 566 if (cupsSideChannelRead(&command, &status, data, &datalen, 1.0)) 567 return (-1); 568 569 switch (command) 570 { 571 case CUPS_SC_CMD_DRAIN_OUTPUT : 572 if (backendDrainOutput(print_fd, device_fd)) 573 status = CUPS_SC_STATUS_IO_ERROR; 574 else if (tcdrain(device_fd)) 575 status = CUPS_SC_STATUS_IO_ERROR; 576 else 577 status = CUPS_SC_STATUS_OK; 578 579 datalen = 0; 580 break; 581 582 case CUPS_SC_CMD_GET_BIDI : 583 status = CUPS_SC_STATUS_OK; 584 data[0] = use_bc; 585 datalen = 1; 586 break; 587 588 case CUPS_SC_CMD_GET_DEVICE_ID : 589 memset(data, 0, sizeof(data)); 590 591 if (backendGetDeviceID(device_fd, data, sizeof(data) - 1, 592 NULL, 0, NULL, NULL, 0)) 593 { 594 status = CUPS_SC_STATUS_NOT_IMPLEMENTED; 595 datalen = 0; 596 } 597 else 598 { 599 status = CUPS_SC_STATUS_OK; 600 datalen = strlen(data); 601 } 602 break; 603 604 default : 605 status = CUPS_SC_STATUS_NOT_IMPLEMENTED; 606 datalen = 0; 607 break; 608 } 609 610 return (cupsSideChannelWrite(command, status, data, datalen, 1.0)); 611} 612 613 614/* 615 * End of "$Id: usb-unix.c 11093 2013-07-03 20:48:42Z msweet $". 616 */ 617