/*1* USB port backend for CUPS.2*3* This file is included from "usb.c" when compiled on UNIX/Linux.4*5* Copyright © 2007-2013 by Apple Inc.6* Copyright © 1997-2007 by Easy Software Products, all rights reserved.7*8* Licensed under Apache License v2.0. See the file "LICENSE" for more9* information.10*/1112/*13* Include necessary headers.14*/1516#include <sys/select.h>171819/*20* Local functions...21*/2223static int open_device(const char *uri, int *use_bc);24static int side_cb(int print_fd, int device_fd, int snmp_fd,25http_addr_t *addr, int use_bc);262728/*29* 'print_device()' - Print a file to a USB device.30*/3132int /* O - Exit status */33print_device(const char *uri, /* I - Device URI */34const char *hostname, /* I - Hostname/manufacturer */35const char *resource, /* I - Resource/modelname */36char *options, /* I - Device options/serial number */37int print_fd, /* I - File descriptor to print */38int copies, /* I - Copies to print */39int argc, /* I - Number of command-line arguments (6 or 7) */40char *argv[]) /* I - Command-line arguments */41{42int use_bc; /* Use backchannel path? */43int device_fd; /* USB device */44ssize_t tbytes; /* Total number of bytes written */45struct termios opts; /* Parallel port options */464748(void)argc;49(void)argv;5051/*52* Open the USB port device...53*/5455fputs("STATE: +connecting-to-device\n", stderr);5657do58{59#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)60/*61* *BSD's ulpt driver currently does not support the62* back-channel, incorrectly returns data ready on a select(),63* and locks up on read()...64*/6566use_bc = 0;6768#elif defined(__sun)69/*70* CUPS STR #3028: Solaris' usbprn driver apparently does not support71* select() or poll(), so we can't support backchannel...72*/7374use_bc = 0;7576#else77/*78* Disable backchannel data when printing to Brother, Canon, or79* Minolta USB printers - apparently these printers will return80* the IEEE-1284 device ID over and over and over when they get81* a read request...82*/8384use_bc = _cups_strcasecmp(hostname, "Brother") &&85_cups_strcasecmp(hostname, "Canon") &&86_cups_strncasecmp(hostname, "Konica", 6) &&87_cups_strncasecmp(hostname, "Minolta", 7);88#endif /* __FreeBSD__ || __NetBSD__ || __OpenBSD__ || __DragonFly__ */8990if ((device_fd = open_device(uri, &use_bc)) == -1)91{92if (getenv("CLASS") != NULL)93{94/*95* If the CLASS environment variable is set, the job was submitted96* to a class and not to a specific queue. In this case, we want97* to abort immediately so that the job can be requeued on the next98* available printer in the class.99*/100101_cupsLangPrintFilter(stderr, "INFO",102_("Unable to contact printer, queuing on next "103"printer in class."));104105/*106* Sleep 5 seconds to keep the job from requeuing too rapidly...107*/108109sleep(5);110111return (CUPS_BACKEND_FAILED);112}113114if (errno == EBUSY)115{116_cupsLangPrintFilter(stderr, "INFO", _("The printer is in use."));117sleep(10);118}119else if (errno == ENXIO || errno == EIO || errno == ENOENT ||120errno == ENODEV)121{122sleep(30);123}124else125{126_cupsLangPrintError("ERROR", _("Unable to open device file"));127return (CUPS_BACKEND_FAILED);128}129}130}131while (device_fd < 0);132133fputs("STATE: -connecting-to-device\n", stderr);134135/*136* Set any options provided...137*/138139tcgetattr(device_fd, &opts);140141opts.c_lflag &= ~(unsigned)(ICANON | ECHO | ISIG); /* Raw mode */142143/**** No options supported yet ****/144145tcsetattr(device_fd, TCSANOW, &opts);146147/*148* Finally, send the print file...149*/150151tbytes = 0;152153while (copies > 0 && tbytes >= 0)154{155copies --;156157if (print_fd != 0)158{159fputs("PAGE: 1 1\n", stderr);160lseek(print_fd, 0, SEEK_SET);161}162163#ifdef __sun164/*165* CUPS STR #3028: Solaris' usbprn driver apparently does not support166* select() or poll(), so we can't support the sidechannel either...167*/168169tbytes = backendRunLoop(print_fd, device_fd, -1, NULL, use_bc, 1, NULL);170171#else172tbytes = backendRunLoop(print_fd, device_fd, -1, NULL, use_bc, 1, side_cb);173#endif /* __sun */174175if (print_fd != 0 && tbytes >= 0)176_cupsLangPrintFilter(stderr, "INFO", _("Print file sent."));177}178179/*180* Close the USB port and return...181*/182183close(device_fd);184185return (CUPS_BACKEND_OK);186}187188189/*190* 'list_devices()' - List all USB devices.191*/192193void194list_devices(void)195{196#ifdef __linux197int i; /* Looping var */198int fd; /* File descriptor */199char device[255], /* Device filename */200device_id[1024], /* Device ID string */201device_uri[1024], /* Device URI string */202make_model[1024]; /* Make and model */203204205/*206* Try to open each USB device...207*/208209for (i = 0; i < 16; i ++)210{211/*212* Linux has a long history of changing the standard filenames used213* for USB printer devices. We get the honor of trying them all...214*/215216snprintf(device, sizeof(device), "/dev/usblp%d", i);217218if ((fd = open(device, O_RDWR | O_EXCL)) < 0)219{220if (errno != ENOENT)221continue;222223snprintf(device, sizeof(device), "/dev/usb/lp%d", i);224225if ((fd = open(device, O_RDWR | O_EXCL)) < 0)226{227if (errno != ENOENT)228continue;229230snprintf(device, sizeof(device), "/dev/usb/usblp%d", i);231232if ((fd = open(device, O_RDWR | O_EXCL)) < 0)233continue;234}235}236237if (!backendGetDeviceID(fd, device_id, sizeof(device_id),238make_model, sizeof(make_model),239"usb", device_uri, sizeof(device_uri)))240cupsBackendReport("direct", device_uri, make_model, make_model,241device_id, NULL);242243close(fd);244}245#elif defined(__sun) && defined(ECPPIOC_GETDEVID)246int i; /* Looping var */247int fd; /* File descriptor */248char device[255], /* Device filename */249device_id[1024], /* Device ID string */250device_uri[1024], /* Device URI string */251make_model[1024]; /* Make and model */252253254/*255* Open each USB device...256*/257258for (i = 0; i < 8; i ++)259{260snprintf(device, sizeof(device), "/dev/usb/printer%d", i);261262if ((fd = open(device, O_WRONLY | O_EXCL)) >= 0)263{264if (!backendGetDeviceID(fd, device_id, sizeof(device_id),265make_model, sizeof(make_model),266"usb", device_uri, sizeof(device_uri)))267cupsBackendReport("direct", device_uri, make_model, make_model,268device_id, NULL);269270close(fd);271}272}273#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)274int i; /* Looping var */275char device[255]; /* Device filename */276277278for (i = 0; i < 8; i ++)279{280snprintf(device, sizeof(device), "/dev/ulpt%d", i);281if (!access(device, 0))282printf("direct usb:%s \"Unknown\" \"USB Printer #%d\"\n", device, i + 1);283284snprintf(device, sizeof(device), "/dev/unlpt%d", i);285if (!access(device, 0))286printf("direct usb:%s \"Unknown\" \"USB Printer #%d (no reset)\"\n", device, i + 1);287}288#endif289}290291292/*293* 'open_device()' - Open a USB device...294*/295296static int /* O - File descriptor or -1 on error */297open_device(const char *uri, /* I - Device URI */298int *use_bc) /* O - Set to 0 for unidirectional */299{300int fd; /* File descriptor */301302303/*304* The generic implementation just treats the URI as a device filename...305* Specific operating systems may also support using the device serial306* number and/or make/model.307*/308309if (!strncmp(uri, "usb:/dev/", 9))310#ifdef __linux311{312/*313* Do not allow direct devices anymore...314*/315316errno = ENODEV;317return (-1);318}319else if (!strncmp(uri, "usb://", 6))320{321/*322* For Linux, try looking up the device serial number or model...323*/324325int i; /* Looping var */326int busy; /* Are any ports busy? */327char device[255], /* Device filename */328device_id[1024], /* Device ID string */329make_model[1024], /* Make and model */330device_uri[1024]; /* Device URI string */331332333/*334* Find the correct USB device...335*/336337for (;;)338{339for (busy = 0, i = 0; i < 16; i ++)340{341/*342* Linux has a long history of changing the standard filenames used343* for USB printer devices. We get the honor of trying them all...344*/345346snprintf(device, sizeof(device), "/dev/usblp%d", i);347348if ((fd = open(device, O_RDWR | O_EXCL)) < 0 && errno == ENOENT)349{350snprintf(device, sizeof(device), "/dev/usb/lp%d", i);351352if ((fd = open(device, O_RDWR | O_EXCL)) < 0 && errno == ENOENT)353{354snprintf(device, sizeof(device), "/dev/usb/usblp%d", i);355356if ((fd = open(device, O_RDWR | O_EXCL)) < 0 && errno == ENOENT)357continue;358}359}360361if (fd >= 0)362{363backendGetDeviceID(fd, device_id, sizeof(device_id),364make_model, sizeof(make_model),365"usb", device_uri, sizeof(device_uri));366}367else368{369/*370* If the open failed because it was busy, flag it so we retry371* as needed...372*/373374if (errno == EBUSY)375busy = 1;376377device_uri[0] = '\0';378}379380if (!strcmp(uri, device_uri))381{382/*383* Yes, return this file descriptor...384*/385386fprintf(stderr, "DEBUG: Printer using device file \"%s\"...\n",387device);388389return (fd);390}391392/*393* This wasn't the one...394*/395396if (fd >= 0)397close(fd);398}399400/*401* If we get here and at least one of the printer ports showed up402* as "busy", then sleep for a bit and retry...403*/404405if (busy)406_cupsLangPrintFilter(stderr, "INFO", _("The printer is in use."));407408sleep(5);409}410}411#elif defined(__sun) && defined(ECPPIOC_GETDEVID)412{413/*414* Do not allow direct devices anymore...415*/416417errno = ENODEV;418return (-1);419}420else if (!strncmp(uri, "usb://", 6))421{422/*423* For Solaris, try looking up the device serial number or model...424*/425426int i; /* Looping var */427int busy; /* Are any ports busy? */428char device[255], /* Device filename */429device_id[1024], /* Device ID string */430make_model[1024], /* Make and model */431device_uri[1024]; /* Device URI string */432433434/*435* Find the correct USB device...436*/437438do439{440for (i = 0, busy = 0; i < 8; i ++)441{442snprintf(device, sizeof(device), "/dev/usb/printer%d", i);443444if ((fd = open(device, O_WRONLY | O_EXCL)) >= 0)445backendGetDeviceID(fd, device_id, sizeof(device_id),446make_model, sizeof(make_model),447"usb", device_uri, sizeof(device_uri));448else449{450/*451* If the open failed because it was busy, flag it so we retry452* as needed...453*/454455if (errno == EBUSY)456busy = 1;457458device_uri[0] = '\0';459}460461if (!strcmp(uri, device_uri))462{463/*464* Yes, return this file descriptor...465*/466467fputs("DEBUG: Setting use_bc to 0!\n", stderr);468469*use_bc = 0;470471return (fd);472}473474/*475* This wasn't the one...476*/477478if (fd >= 0)479close(fd);480}481482/*483* If we get here and at least one of the printer ports showed up484* as "busy", then sleep for a bit and retry...485*/486487if (busy)488{489_cupsLangPrintFilter(stderr, "INFO", _("The printer is in use."));490sleep(5);491}492}493while (busy);494495/*496* Couldn't find the printer, return "no such device or address"...497*/498499errno = ENODEV;500501return (-1);502}503#else504{505if (*use_bc)506fd = open(uri + 4, O_RDWR | O_EXCL);507else508fd = -1;509510if (fd < 0)511{512fd = open(uri + 4, O_WRONLY | O_EXCL);513*use_bc = 0;514}515516return (fd);517}518#endif /* __linux */519else520{521errno = ENODEV;522return (-1);523}524}525526527/*528* 'side_cb()' - Handle side-channel requests...529*/530531static int /* O - 0 on success, -1 on error */532side_cb(int print_fd, /* I - Print file */533int device_fd, /* I - Device file */534int snmp_fd, /* I - SNMP socket (unused) */535http_addr_t *addr, /* I - Device address (unused) */536int use_bc) /* I - Using back-channel? */537{538cups_sc_command_t command; /* Request command */539cups_sc_status_t status; /* Request/response status */540char data[2048]; /* Request/response data */541int datalen; /* Request/response data size */542543544(void)snmp_fd;545(void)addr;546547datalen = sizeof(data);548549if (cupsSideChannelRead(&command, &status, data, &datalen, 1.0))550return (-1);551552switch (command)553{554case CUPS_SC_CMD_DRAIN_OUTPUT :555if (backendDrainOutput(print_fd, device_fd))556status = CUPS_SC_STATUS_IO_ERROR;557else if (tcdrain(device_fd))558status = CUPS_SC_STATUS_IO_ERROR;559else560status = CUPS_SC_STATUS_OK;561562datalen = 0;563break;564565case CUPS_SC_CMD_GET_BIDI :566status = CUPS_SC_STATUS_OK;567data[0] = use_bc;568datalen = 1;569break;570571case CUPS_SC_CMD_GET_DEVICE_ID :572memset(data, 0, sizeof(data));573574if (backendGetDeviceID(device_fd, data, sizeof(data) - 1,575NULL, 0, NULL, NULL, 0))576{577status = CUPS_SC_STATUS_NOT_IMPLEMENTED;578datalen = 0;579}580else581{582status = CUPS_SC_STATUS_OK;583datalen = strlen(data);584}585break;586587default :588status = CUPS_SC_STATUS_NOT_IMPLEMENTED;589datalen = 0;590break;591}592593return (cupsSideChannelWrite(command, status, data, datalen, 1.0));594}595596597