summaryrefslogtreecommitdiff
path: root/backend/usb-libusb.c
diff options
context:
space:
mode:
authormsweet <msweet@a1ca3aef-8c08-0410-bb20-df032aa958be>2008-06-27 20:26:20 +0000
committermsweet <msweet@a1ca3aef-8c08-0410-bb20-df032aa958be>2008-06-27 20:26:20 +0000
commit75bd9771f6e44fdd887ee90faac46f403aefc0fc (patch)
tree354e3067522df9490e905772f73b72bfed8733ae /backend/usb-libusb.c
parentdd1abb6b5f145c5e5f279d8848b5f3ec161fd227 (diff)
Merge changes from CUPS 1.4svn-r7696.
git-svn-id: svn+ssh://src.apple.com/svn/cups/easysw/current@818 a1ca3aef-8c08-0410-bb20-df032aa958be
Diffstat (limited to 'backend/usb-libusb.c')
-rw-r--r--backend/usb-libusb.c680
1 files changed, 680 insertions, 0 deletions
diff --git a/backend/usb-libusb.c b/backend/usb-libusb.c
new file mode 100644
index 000000000..a2ec1fa57
--- /dev/null
+++ b/backend/usb-libusb.c
@@ -0,0 +1,680 @@
+/*
+ * "$Id$"
+ *
+ * Libusb interface code for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 2007-2008 by Apple Inc.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * Contents:
+ *
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include <usb.h>
+
+
+/*
+ * Local types...
+ */
+
+typedef struct usb_printer_s /**** USB Printer Data ****/
+{
+ struct usb_device *device; /* Device info */
+ int conf, /* Configuration */
+ iface, /* Interface */
+ altset, /* Alternate setting */
+ write_endp, /* Write endpoint */
+ read_endp; /* Read endpoint */
+ struct usb_dev_handle *handle; /* Open handle to device */
+} usb_printer_t;
+
+typedef int (*usb_cb_t)(usb_printer_t *, const char *, const char *,
+ const void *);
+
+
+/*
+ * Local functions...
+ */
+
+static int close_device(usb_printer_t *printer);
+static usb_printer_t *find_device(usb_cb_t cb, const void *data);
+static int get_device_id(usb_printer_t *printer, char *buffer,
+ size_t bufsize);
+static int list_cb(usb_printer_t *printer, const char *device_uri,
+ const char *device_id, const void *data);
+static char *make_device_uri(usb_printer_t *printer,
+ const char *device_id,
+ char *uri, size_t uri_size);
+static int open_device(usb_printer_t *printer, int verbose);
+static int print_cb(usb_printer_t *printer, const char *device_uri,
+ const char *device_id, const void *data);
+
+
+/*
+ * 'list_devices()' - List the available printers.
+ */
+
+void
+list_devices(void)
+{
+ fputs("DEBUG: list_devices\n", stderr);
+ find_device(list_cb, NULL);
+}
+
+
+/*
+ * 'print_device()' - Print a file to a USB device.
+ */
+
+int /* O - Exit status */
+print_device(const char *uri, /* I - Device URI */
+ const char *hostname, /* I - Hostname/manufacturer */
+ const char *resource, /* I - Resource/modelname */
+ char *options, /* I - Device options/serial number */
+ int print_fd, /* I - File descriptor to print */
+ int copies, /* I - Copies to print */
+ int argc, /* I - Number of command-line arguments (6 or 7) */
+ char *argv[]) /* I - Command-line arguments */
+{
+ usb_printer_t *printer; /* Printer */
+ ssize_t bytes, /* Bytes read/written */
+ tbytes; /* Total bytes written */
+ char buffer[8192]; /* Print data buffer */
+ struct sigaction action; /* Actions for POSIX signals */
+ int read_endp, /* Read endpoint */
+ write_endp; /* Write endpoint */
+
+
+ fputs("DEBUG: print_device\n", stderr);
+
+ /*
+ * Connect to the printer...
+ */
+
+ while ((printer = find_device(print_cb, uri)) == NULL)
+ {
+ _cupsLangPuts(stderr,
+ _("INFO: Waiting for printer to become available...\n"));
+ sleep(5);
+ }
+
+ read_endp = printer->device->config[printer->conf].
+ interface[printer->iface].
+ altsetting[printer->altset].
+ endpoint[printer->read_endp].bEndpointAddress;
+ write_endp = printer->device->config[printer->conf].
+ interface[printer->iface].
+ altsetting[printer->altset].
+ endpoint[printer->write_endp].bEndpointAddress;
+
+ /*
+ * If we are printing data from a print driver on stdin, ignore SIGTERM
+ * so that the driver can finish out any page data, e.g. to eject the
+ * current page. We only do this for stdin printing as otherwise there
+ * is no way to cancel a raw print job...
+ */
+
+ if (!print_fd)
+ {
+ memset(&action, 0, sizeof(action));
+
+ sigemptyset(&action.sa_mask);
+ action.sa_handler = SIG_IGN;
+ sigaction(SIGTERM, &action, NULL);
+ }
+
+ tbytes = 0;
+
+ while (copies > 0 && tbytes >= 0)
+ {
+ copies --;
+
+ if (print_fd != 0)
+ {
+ fputs("PAGE: 1 1\n", stderr);
+ lseek(print_fd, 0, SEEK_SET);
+ }
+
+ /*
+ * TODO: Add side-channel and back-channel support, along with better
+ * error handling for writes.
+ */
+
+ while ((bytes = read(print_fd, buffer, sizeof(buffer))) > 0)
+ {
+ while (usb_bulk_write(printer->handle, write_endp, buffer, bytes,
+ 5000) < 0)
+ {
+ _cupsLangPrintf(stderr,
+ _("ERROR: Unable to write %d bytes to printer!\n"),
+ (int)bytes);
+ tbytes = -1;
+ break;
+ }
+
+ tbytes += bytes;
+ }
+ }
+
+ /*
+ * Close our connection and return...
+ */
+
+ close_device(printer);
+
+ return (CUPS_BACKEND_OK);
+}
+
+
+/*
+ * 'close_device()' - Close the connection to the USB printer.
+ */
+
+static int /* I - 0 on success, -1 on failure */
+close_device(usb_printer_t *printer) /* I - Printer */
+{
+ if (printer->handle)
+ {
+ usb_close(printer->handle);
+ printer->handle = NULL;
+ }
+
+ return (0);
+}
+
+
+/*
+ * 'find_device()' - Find or enumerate USB printers.
+ */
+
+static usb_printer_t * /* O - Found printer */
+find_device(usb_cb_t cb, /* I - Callback function */
+ const void *data) /* I - User data for callback */
+{
+ struct usb_bus *bus; /* Current bus */
+ struct usb_device *device; /* Current device */
+ struct usb_config_descriptor *confptr;/* Pointer to current configuration */
+ struct usb_interface *ifaceptr; /* Pointer to current interface */
+ struct usb_interface_descriptor *altptr;
+ /* Pointer to current alternate setting */
+ struct usb_endpoint_descriptor *endpptr;
+ /* Pointer to current endpoint */
+ int conf, /* Current configuration */
+ iface, /* Current interface */
+ altset, /* Current alternate setting */
+ protocol, /* Current protocol */
+ endp, /* Current endpoint */
+ read_endp, /* Current read endpoint */
+ write_endp; /* Current write endpoint */
+ char device_id[1024],/* IEEE-1284 device ID */
+ device_uri[1024];
+ /* Device URI */
+ static usb_printer_t printer; /* Current printer */
+
+
+ /*
+ * Initialize libusb...
+ */
+
+ usb_init();
+ fprintf(stderr, "DEBUG: usb_find_busses=%d\n", usb_find_busses());
+ fprintf(stderr, "DEBUG: usb_find_devices=%d\n", usb_find_devices());
+
+ /*
+ * Then loop through the devices it found...
+ */
+
+ for (bus = usb_get_busses(); bus; bus = bus->next)
+ for (device = bus->devices; device; device = device->next)
+ {
+ /*
+ * Ignore devices with no configuration data and anything that is not
+ * a printer...
+ */
+
+ if (!device->config || !device->descriptor.idVendor ||
+ !device->descriptor.idProduct)
+ continue;
+
+ for (conf = 0, confptr = device->config;
+ conf < device->descriptor.bNumConfigurations;
+ conf ++, confptr ++)
+ for (iface = 0, ifaceptr = confptr->interface;
+ iface < confptr->bNumInterfaces;
+ iface ++, ifaceptr ++)
+ {
+ /*
+ * Some printers offer multiple interfaces...
+ */
+
+ protocol = 0;
+
+ for (altset = 0, altptr = ifaceptr->altsetting;
+ altset < ifaceptr->num_altsetting;
+ altset ++, altptr ++)
+ {
+ /*
+ * Currently we only support unidirectional and bidirectional
+ * printers. Future versions of this code will support the
+ * 1284.4 (packet mode) protocol as well.
+ */
+
+ if (altptr->bInterfaceClass != USB_CLASS_PRINTER ||
+ altptr->bInterfaceSubClass != 1 ||
+ (altptr->bInterfaceProtocol != 1 && /* Unidirectional */
+ altptr->bInterfaceProtocol != 2) || /* Bidirectional */
+ altptr->bInterfaceProtocol < protocol)
+ continue;
+
+ read_endp = -1;
+ write_endp = -1;
+
+ for (endp = 0, endpptr = altptr->endpoint;
+ endp < altptr->bNumEndpoints;
+ endp ++, endpptr ++)
+ if ((endpptr->bmAttributes & USB_ENDPOINT_TYPE_MASK) ==
+ USB_ENDPOINT_TYPE_BULK)
+ {
+ if (endpptr->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
+ read_endp = endp;
+ else
+ write_endp = endp;
+ }
+
+ if (write_endp >= 0)
+ {
+ /*
+ * Save the best match so far...
+ */
+
+ protocol = altptr->bInterfaceProtocol;
+ printer.altset = altset;
+ printer.write_endp = write_endp;
+ printer.read_endp = read_endp;
+ }
+ }
+
+ if (protocol > 0)
+ {
+ printer.device = device;
+ printer.conf = conf;
+ printer.iface = iface;
+ printer.handle = NULL;
+
+ if (!open_device(&printer, data != NULL))
+ {
+ if (!get_device_id(&printer, device_id, sizeof(device_id)))
+ {
+ make_device_uri(&printer, device_id, device_uri,
+ sizeof(device_uri));
+
+ if ((*cb)(&printer, device_uri, device_id, data))
+ return (&printer);
+ }
+
+ close_device(&printer);
+ }
+ }
+ }
+ }
+
+ /*
+ * If we get this far without returning, then we haven't found a printer
+ * to print to...
+ */
+
+ return (NULL);
+}
+
+
+/*
+ * 'get_device_id()' - Get the IEEE-1284 device ID for the printer.
+ */
+
+static int /* O - 0 on success, -1 on error */
+get_device_id(usb_printer_t *printer, /* I - Printer */
+ char *buffer, /* I - String buffer */
+ size_t bufsize) /* I - Number of bytes in buffer */
+{
+ int length; /* Length of device ID */
+
+
+ if (usb_control_msg(printer->handle,
+ USB_TYPE_CLASS | USB_ENDPOINT_IN | USB_RECIP_INTERFACE,
+ 0, 0,
+ (printer->iface << 8) |
+ printer->device->config[printer->conf].
+ interface[printer->iface].
+ altsetting[printer->altset].bAlternateSetting,
+ buffer, bufsize, 5000) < 0)
+ {
+ *buffer = '\0';
+ return (-1);
+ }
+
+ /*
+ * Extract the length of the device ID string from the first two
+ * bytes. The 1284 spec says the length is stored MSB first...
+ */
+
+ length = (((unsigned)buffer[0] & 255) << 8) +
+ ((unsigned)buffer[1] & 255);
+
+ /*
+ * Check to see if the length is larger than our buffer; first
+ * assume that the vendor incorrectly implemented the 1284 spec,
+ * and then limit the length to the size of our buffer...
+ */
+
+ if (length > (bufsize - 2))
+ length = (((unsigned)buffer[1] & 255) << 8) +
+ ((unsigned)buffer[0] & 255);
+
+ if (length > (bufsize - 2))
+ length = bufsize - 2;
+
+ /*
+ * Copy the device ID text to the beginning of the buffer and
+ * nul-terminate.
+ */
+
+ memmove(buffer, buffer + 2, length);
+ buffer[length] = '\0';
+
+ return (0);
+}
+
+
+/*
+ * 'list_cb()' - List USB printers for discovery.
+ */
+
+static int /* O - 0 to continue, 1 to stop */
+list_cb(usb_printer_t *printer, /* I - Printer */
+ const char *device_uri, /* I - Device URI */
+ const char *device_id, /* I - IEEE-1284 device ID */
+ const void *data) /* I - User data (not used) */
+{
+ char make_model[1024]; /* Make and model */
+
+
+ /*
+ * Get the device URI and make/model strings...
+ */
+
+ backendGetMakeModel(device_id, make_model, sizeof(make_model));
+
+ /*
+ * Report the printer...
+ */
+
+ printf("direct %s \"%s\" \"%s USB\" \"%s\"\n", device_uri, make_model,
+ make_model, device_id);
+ fflush(stdout);
+
+ /*
+ * Keep going...
+ */
+
+ return (0);
+}
+
+
+/*
+ * 'make_device_uri()' - Create a device URI for a USB printer.
+ */
+
+static char * /* O - Device URI */
+make_device_uri(
+ usb_printer_t *printer, /* I - Printer */
+ const char *device_id, /* I - IEEE-1284 device ID */
+ char *uri, /* I - Device URI buffer */
+ size_t uri_size) /* I - Size of device URI buffer */
+{
+ char options[1024]; /* Device URI options */
+ int num_values; /* Number of 1284 parameters */
+ cups_option_t *values; /* 1284 parameters */
+ const char *mfg, /* Manufacturer */
+ *mdl, /* Model */
+ *des, /* Description */
+ *sern; /* Serial number */
+ char tempmfg[256], /* Temporary manufacturer string */
+ tempsern[256], /* Temporary serial number string */
+ *tempptr; /* Pointer into temp string */
+
+
+ /*
+ * Get the make, model, and serial numbers...
+ */
+
+ num_values = _ppdGet1284Values(device_id, &values);
+
+ if ((sern = cupsGetOption("SERIALNUMBER", num_values, values)) == NULL)
+ if ((sern = cupsGetOption("SERN", num_values, values)) == NULL)
+ if ((sern = cupsGetOption("SN", num_values, values)) == NULL)
+ {
+ /*
+ * Try getting the serial number from the device itself...
+ */
+
+ int length = usb_get_string_simple(printer->handle,
+ printer->device->descriptor.
+ iSerialNumber,
+ tempsern, sizeof(tempsern) - 1);
+ if (length > 0)
+ {
+ tempsern[length] = '\0';
+ sern = tempsern;
+ }
+ }
+
+ if ((mfg = cupsGetOption("MANUFACTURER", num_values, values)) == NULL)
+ mfg = cupsGetOption("MFG", num_values, values);
+
+ if ((mdl = cupsGetOption("MODEL", num_values, values)) == NULL)
+ mdl = cupsGetOption("MDL", num_values, values);
+
+#ifdef __APPLE__
+ /*
+ * To maintain compatibility with the original IOKit-based backend on Mac OS X,
+ * don't map manufacturer names...
+ */
+
+ if (!mfg)
+
+#else
+ /*
+ * To maintain compatibility with the original character device backend on
+ * Linux and *BSD, map manufacturer names...
+ */
+
+ if (mfg)
+ {
+ if (!strcasecmp(mfg, "Hewlett-Packard"))
+ mfg = "HP";
+ else if (!strcasecmp(mfg, "Lexmark International"))
+ mfg = "Lexmark";
+ }
+ else
+#endif /* __APPLE__ */
+ {
+ /*
+ * No manufacturer? Use the model string or description...
+ */
+
+ if (mdl)
+ _ppdNormalizeMakeAndModel(mdl, tempmfg, sizeof(tempmfg));
+ else if ((des = cupsGetOption("DESCRIPTION", num_values, values)) != NULL ||
+ (des = cupsGetOption("DES", num_values, values)) != NULL)
+ _ppdNormalizeMakeAndModel(des, tempmfg, sizeof(tempmfg));
+ else
+ strlcpy(tempmfg, "Unknown", sizeof(tempmfg));
+
+ if ((tempptr = strchr(tempmfg, ' ')) != NULL)
+ *tempptr = '\0';
+
+ mfg = tempmfg;
+ }
+
+ /*
+ * Generate the device URI from the manufacturer, model, serial number,
+ * and interface number...
+ */
+
+ if (sern)
+ {
+ if (printer->iface > 0)
+ snprintf(options, sizeof(options), "?serial=%s&interface=%d", sern,
+ printer->iface);
+ else
+ snprintf(options, sizeof(options), "?serial=%s", sern);
+ }
+ else if (printer->iface > 0)
+ snprintf(options, sizeof(options), "?interface=%d", printer->iface);
+ else
+ options[0] = '\0';
+
+ httpAssembleURIf(HTTP_URI_CODING_ALL, uri, uri_size, "usb", NULL, mfg, 0,
+ "/%s%s", mdl, options);
+
+ cupsFreeOptions(num_values, values);
+
+ return (uri);
+}
+
+
+/*
+ * 'open_device()' - Open a connection to the USB printer.
+ */
+
+static int /* O - 0 on success, -1 on error */
+open_device(usb_printer_t *printer, /* I - Printer */
+ int verbose) /* I - Update connecting-to-device state? */
+{
+ int number; /* Configuration/interface/altset numbers */
+
+
+ /*
+ * Return immediately if we are already connected...
+ */
+
+ if (printer->handle)
+ return (0);
+
+ /*
+ * Try opening the printer...
+ */
+
+ if ((printer->handle = usb_open(printer->device)) == NULL)
+ return (-1);
+
+ /*
+ * Then set the desired configuration...
+ */
+
+ if (verbose)
+ fputs("STATE: +connecting-to-device\n", stderr);
+
+ number = printer->device->config[printer->conf].bConfigurationValue;
+ while (usb_set_configuration(printer->handle, number) < 0)
+ {
+ if (errno != EBUSY)
+ fprintf(stderr, "DEBUG: Failed to set configuration %d for %04x:%04x\n",
+ number, printer->device->descriptor.idVendor,
+ printer->device->descriptor.idProduct);
+
+ goto error;
+ }
+
+ /*
+ * Claim interfaces as needed...
+ */
+
+ number = printer->device->config[printer->conf].interface[printer->iface].
+ altsetting[printer->altset].bInterfaceNumber;
+ while (usb_claim_interface(printer->handle, number) < 0)
+ {
+ if (errno != EBUSY)
+ fprintf(stderr, "DEBUG: Failed to claim interface %d for %04x:%04x\n",
+ number, printer->device->descriptor.idVendor,
+ printer->device->descriptor.idProduct);
+
+ goto error;
+ }
+
+ if (number != 0)
+ while (usb_claim_interface(printer->handle, 0) < 0)
+ {
+ if (errno != EBUSY)
+ fprintf(stderr, "DEBUG: Failed to claim interface 0 for %04x:%04x\n",
+ printer->device->descriptor.idVendor,
+ printer->device->descriptor.idProduct);
+
+ goto error;
+ }
+
+ /*
+ * Set alternate setting...
+ */
+
+ number = printer->device->config[printer->conf].interface[printer->iface].
+ altsetting[printer->altset].bAlternateSetting;
+ while (usb_set_altinterface(printer->handle, number) < 0)
+ {
+ if (errno != EBUSY)
+ fprintf(stderr,
+ "DEBUG: Failed to set alternate interface %d for %04x:%04x\n",
+ number, printer->device->descriptor.idVendor,
+ printer->device->descriptor.idProduct);
+
+ goto error;
+ }
+
+ if (verbose)
+ fputs("STATE: -connecting-to-device\n", stderr);
+
+ return (0);
+
+ /*
+ * If we get here, there was a hard error...
+ */
+
+ error:
+
+ if (verbose)
+ fputs("STATE: -connecting-to-device\n", stderr);
+
+ usb_close(printer->handle);
+ printer->handle = NULL;
+
+ return (-1);
+}
+
+
+/*
+ * 'print_cb()' - Find a USB printer for printing.
+ */
+
+static int /* O - 0 to continue, 1 to stop (found) */
+print_cb(usb_printer_t *printer, /* I - Printer */
+ const char *device_uri, /* I - Device URI */
+ const char *device_id, /* I - IEEE-1284 device ID */
+ const void *data) /* I - User data (make, model, S/N) */
+{
+ return (!strcmp((char *)data, device_uri));
+}
+
+
+/*
+ * End of "$Id$".
+ */
+