Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

libusb-based USB backend: Further fixes #4128

Closed
michaelrsweet opened this issue Jun 22, 2012 · 19 comments
Closed

libusb-based USB backend: Further fixes #4128

michaelrsweet opened this issue Jun 22, 2012 · 19 comments

Comments

@michaelrsweet
Copy link
Collaborator

Version: 1.6-current
CUPS.org User: till.kamppeter

Based on work together with Ubuntu bug reporters I succeeded to fix several issues with the libusb-based CUPS backend. Now practically all real USB printers (no USB->Parallel adapters) work.

Patches for 1.6.x and 1.5.x are attached.

Following Ubuntu bugs are solved:

https://bugs.launchpad.net/ubuntu/+source/cups/+bug/1000253
Brother HL-1440 printing extra page with PJL codes

https://bugs.launchpad.net/ubuntu/+source/cups/+bug/995111
Print failure since upgrade to 12.04

For following bug I have added a configuration option:

https://bugs.launchpad.net/ubuntu/+source/cups/+bug/1001028
Delay after every print job on USB

Blacklisting the usblp kernel module is not needed any more, the usblp backend correctly detaches and re-attaches the module.

The changes I have done are:

  • Support for mon-directional devices, both protocol-1 devices and
    devices where no read endpoint is found.
  • Soft reset specific to the "PRINTER" device class. This allows a
    reset without reconnecting.
  • When closing the device, it will also get reset to its original
    configuration, before re-attaching the usblp kernel module.
  • Added option "usb-unidir" to force the backend into uni-directional
    mode. This allows to work around problems with bi-di communications,
    especially also a delay at the end of the job caused by closing the
    read channel (happens only for some devices). Also useful for
    debugging.
  • Some extra debug messages.
  • Added a missing libusb_free_config_descriptor().

Can this be put into CUPS 1.6.x/1.5.x? Thanks.

@michaelrsweet
Copy link
Collaborator Author

CUPS.org User: upscope

Tim been have problems printing Landscape with brothers printer on usb (CUPS 1.5.0 for openSUSE 12.1. Do you think its worth trying this patch? Also is there a document on appling patches?

@michaelrsweet
Copy link
Collaborator Author

CUPS.org User: till.kamppeter

upscope, to apply a patch uncompress the source .tar.gz (or .tar.bz2) file of CUPS, go into the main directory of the source and run the command

patch -p0 < file.patch

with "file.patch" replaced by the name of the patch file. Patches for both CUPS 1.5.x and 1.6.x are attached to this STR. After that compile and install as usual.

@michaelrsweet
Copy link
Collaborator Author

CUPS.org User: till.kamppeter

I have continued the development of the USB CUPS backend, especially with a Prolific Technology USB -> Parallel adapter at hand.

The new patches are

usb-libusb-c-update-1-6-x-2.patch
usb-libusb-c-update-1-5-x-2.patch

The added changes are:

  • Added the quirk management of the usblp kernel module. So the problems
    of all printers which were worked around in the kernel module are
    also worked around in the libusb-based CUPS backend now.
  • Added new quirk type to quirk manager: Printers for which the usblp
    kernel module should not get reattached after printing a job.
  • Added additional quirks for the Prolific Technology USB -> Parallel
    adapter, as the adapter needs uni-directional mode to be forced and
    also does not like re-attaching the usblp kernel module after the
    job (last third of last page gets cut off, re-attaching probably
    sends a reset to the printer while there is still data to be printed
    in the printer's internal buffer.
  • Do not restore the configuration setting when the old configuration
    was zero, as zero means "unconfigured".
  • Added missing braces around code for restoring device's configuration
    in close_device().
  • Printer-class-specific soft reset is now fully compliant to USB spec:
    It tries both possible request types now.

Can you please apply these updated patches to CUPS 1.6.x and 1.5.x? This should solve most problems.

@michaelrsweet
Copy link
Collaborator Author

CUPS.org User: till.kamppeter

Another small update:

The new patches are

usb-libusb-c-update-1-6-x-3.patch
usb-libusb-c-update-1-5-x-3.patch

The added changes are:

  • Added the command line option "usb-no-reattach". With the option set
    the usblp kernel module does not get reattached after a job has been
    printed. Some printers cut off the end of the job or even crash by
    re-attaching the module. This is a development/debug mode to test
    whether re-attaching was the culprit of a problem. Users should
    report such issues so that their printers can get added to the quirk
    list.

Can you please apply these updated patches to CUPS 1.6.x and 1.5.x?

This change and the previous changes should be considered as bug fixes and preparation for future debugging.

@michaelrsweet
Copy link
Collaborator Author

CUPS.org User: till.kamppeter

Another small update:

The new patches are

usb-libusb-c-update-1-6-x-4.patch
usb-libusb-c-update-1-5-x-4.patch

The added changes are:

  • Perform a USB device reset after finishing each job. This cleans
    up after the job and prevents a crash of the printer before the next
    job in many cases. Especially this makes the Prolific USB -> Parallel
    adaptor work (https://bugs.launchpad.net/bugs/987485) and makes it
    unnecessary to blacklist the usblp kernel module for some
    printers (https://bugs.launchpad.net/bugs/997040).

Can you please apply these updated patches to CUPS 1.6.x and 1.5.x?

This change and the previous changes should be considered as bug fixes and preparation for future debugging.

@michaelrsweet
Copy link
Collaborator Author

CUPS.org User: jsmeix.suse

FYI regarding "USB device reset after finishing each job":

On all-in-one USB devices a USB device reset (i.e. a reset of
the whole USB device) may have bad side-effects, see
https://bugzilla.novell.com/show_bug.cgi?id=751712#c10

In this case an all-in-one USB device reset because
of an issue of the mass-storage unit caused an abort
of the print job which printed at the same time.

In other words: A reset of a whole USB all-in-one device
may cause abort of whatever runs at the same time on
whatever other units of the all-in-one USB device.

According to
https://bugzilla.novell.com/show_bug.cgi?id=751712#c12
the root cause of such bad side-effects seems to be
an insufficiency in the USB all-in-one device but
as far as I see this STR is mainly about how to deal
gracefully with insufficiencies in USB devices.

I don't know if particular hardware like the Prolific
USB->Parallel adaptor justifies to do a USB device reset
in any case after finishing each job.

Till,
is the "quirks"-behaviour of the libusb-based USB backend
configurable by the admin (e.g. via DeviceURI options)
or is it hardcoded?

@michaelrsweet
Copy link
Collaborator Author

CUPS.org User: till.kamppeter

Johannes, what I could do to improve that is making the after-job reset a quirk option, currently only applying to the Prolific USB -> Parallel adaptor.

The quirk behavior is hard-coded, it is the same as in the usblp kernel module with some added quirk rules. For some quirk time there are also admin-settable options:

  • Forced uni-directional communication: "lpadmin -p -o
    usb-unidir-default=true"
  • Suppress re-attachment of the usblp kernel module after the job:
    "lpadmin -p -o usb-no-reattach-default=true"

@michaelrsweet
Copy link
Collaborator Author

CUPS.org User: jsmeix.suse

Regarding hard-coded quirk behavior as in usblp kernel module:

Quirks in the kernel must be hard-coded because the kernel
cannot read its quirk behavior from a config file
(e.g. from /boot/vmlinuz--quirks.conf)
because of security issues (tons of possible security related
stuff would have to be coded in the kernel when it reads files)
and because filesystem access may not yet be possible when the
quirks are needed (think about booting from an USB mass storage
device where quirks for USB are needed to access the USB mass
storage device).

In contrast quirks in application programs (from the kernel's
point of view CUPS' libusb-based USB backend is an application)
do not need to be hard-coded because applications can read
their quirk behavior from a config file (e.g. from
/etc/cups/usb-quirks.conf) or via whatever method that is
appropriate (e.g. DeviceURI options or print queue options)
or even a combination (e.g. system default behavior from
/etc/cups/usb-quirks.conf and queue specific behavior via
DeviceURI and/or print queue options plus hard-coded fallback
behavior).

For debugging of issues that happen on other user's systems
it would help a lot if the libusb-based USB backend provides "DEBUG:..." or "DEBUG2:..." output for every quirk
(perhaps for log level "debug" this is too much but at least
with log level "debug2" it should become really verbose).

Furthermore for debugging the libusb-based USB backend
it may be useful when its debug messages are sub-prefixed
with a fixed string that identifies the libusb-based USB
backend (e.g. something like "DEBUG: [usb(libusb)backend] ...")
so that it is easy to "grep" them from /var/log/cups/error_log

@michaelrsweet
Copy link
Collaborator Author

CUPS.org User: mike

Fixed in Subversion repository.

Please open a new bug with new patches when you decide how you want to deal with the different quirk modes.

@michaelrsweet
Copy link
Collaborator Author

"usb-libusb-c-update-1-6-x.patch":

--- backend/usb-libusb.c 2012-06-22 11:53:18.043277991 +0200
+++ backend/usb-libusb.c 2012-06-22 11:52:50.723163432 +0200
@@ -22,6 +22,8 @@

  • make_device_uri() - Create a device URI for a USB printer.
  • open_device() - Open a connection to the USB printer.
  • print_cb() - Find a USB printer for printing.
  • * printer_class_soft_reset()' - Do the soft reset request specific to
  • * printers
  • read_thread() - Thread to read the backchannel data on.
  • sidechannel_thread() - Handle side-channel requests.
  • soft_reset() - Send a soft reset to the device.
    @@ -60,12 +62,13 @@
    {
    struct libusb_device device; / Device info /
    int conf, /
    Configuration */
  •       origconf,   /\* Original configuration _/
    iface,      /_ Interface _/
    altset,     /_ Alternate setting _/
    write_endp, /_ Write endpoint */
    
  •                    read_endp, /\* Read endpoint */
    
  •       read_endp,  /\* Read endpoint _/
    protocol,   /_ Protocol: 1 = Uni-di, 2 = Bi-di. */
    
  •                    usblp_attached; /\* Is the "usblp" kernel module
    
  •       usblp_attached; /\* Is the "usblp" kernel module
               attached? _/
    
    struct libusb_device_handle *handle; /_ Open handle to device /
    } usb_printer_t;
    @@ -124,6 +127,7 @@
    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);
    +static int printer_class_soft_reset(usb_printer_t *printer);
    static void *read_thread(void *reference);
    static void *sidechannel_thread(void *reference);
    static void soft_reset(void);
    @@ -163,7 +167,8 @@
    iostatus; /
    Current IO status /
    pthread_t read_thread_id, /
    Read thread /
    sidechannel_thread_id; /
    Side-channel thread */
  • int have_sidechannel = 0; /* Was the side-channel thread started? */
  • int have_sidechannel = 0, /* Was the side-channel thread started? */
  •   have_backchannel = 0;   /\* Do we have a back channel? _/
    
    struct stat sidechannel_info; /_ Side-channel file descriptor info /
    unsigned char print_buffer[8192], /
    Print data buffer /
    *print_ptr; /
    Pointer into print data buffer /
    @@ -172,6 +177,9 @@
    struct timeval *timeout, /
    Timeout pointer /
    tv; /
    Time value /
    struct timespec cond_timeout; /
    pthread condition timeout */
  • int num_opts; /* Number of options */
  • cups_option_t opts; / Options */
  • const char val; / Option value */

/*
@@ -187,6 +195,7 @@

  • Connect to the printer...
    */
  • fprintf(stderr, "DEBUG: Printing on printer with URI: %s\n", uri);
    while ((g.printer = find_device(print_cb, uri)) == NULL)
    {
    _cupsLangPrintFilter(stderr, "INFO",
    @@ -240,24 +249,47 @@
    }

/*

  • * Get the read thread going...
  • * Debug mode: If option "usb-unidir" is given, always deactivate
    • backchannel
      */
  • g.read_thread_stop = 0;
  • g.read_thread_done = 0;
  • num_opts = cupsParseOptions(argv[5], 0, &opts);
  • val = cupsGetOption("usb-unidir", num_opts, opts);
  • if (val && strcasecmp(val, "no") && strcasecmp(val, "off") &&
  •  strcasecmp(val, "false"))
    
  • {
  • g.printer->read_endp = -1;
  • fprintf(stderr, "DEBUG: Forced uni-directional communication "
  •   "via \"usb-unidir\" option.\n");
    
  • }
  • pthread_cond_init(&g.read_thread_cond, NULL);
  • pthread_mutex_init(&g.read_thread_mutex, NULL);
  • /*
  • * Get the read thread going...
  • */
  • if (pthread_create(&read_thread_id, NULL, read_thread, NULL))
  • if (g.printer->read_endp != -1)
    {
  • fprintf(stderr, "DEBUG: Fatal USB error.\n");
  • _cupsLangPrintFilter(stderr, "ERROR",
  •                     _("There was an unrecoverable USB error."));
    
  • fputs("DEBUG: Couldn't create read thread.\n", stderr);
  • close_device(g.printer);
  • return (CUPS_BACKEND_STOP);
  • have_backchannel = 1;
  • g.read_thread_stop = 0;
  • g.read_thread_done = 0;
  • pthread_cond_init(&g.read_thread_cond, NULL);
  • pthread_mutex_init(&g.read_thread_mutex, NULL);
  • if (pthread_create(&read_thread_id, NULL, read_thread, NULL))

  • {

  •  fprintf(stderr, "DEBUG: Fatal USB error.\n");
    
  •  _cupsLangPrintFilter(stderr, "ERROR",
    
  •          _("There was an unrecoverable USB error."));
    
  •  fputs("DEBUG: Couldn't create read thread.\n", stderr);
    
  •  close_device(g.printer);
    
  •  return (CUPS_BACKEND_STOP);
    
  • }
    }

  • else

  • fprintf(stderr, "DEBUG: Uni-directional device/mode, back channel "

  •   "deactivated.\n");
    

    /*

    • The main thread sends the print file...
      @@ -515,38 +547,18 @@
    • Signal the read thread to exit then wait 7 seconds for it to complete...
      */

- g.read_thread_stop = 1;

- pthread_mutex_lock(&g.read_thread_mutex);

  • if (!g.read_thread_done)
  • if (have_backchannel)
    {

- fputs("DEBUG: Waiting for read thread to exit...\n", stderr);

  • gettimeofday(&tv, NULL);
  • cond_timeout.tv_sec = tv.tv_sec + WAIT_EOF_DELAY;
  • cond_timeout.tv_nsec = tv.tv_usec * 1000;
  • g.read_thread_stop = 1;
  • while (!g.read_thread_done)
  • {
  •  if (pthread_cond_timedwait(&g.read_thread_cond, &g.read_thread_mutex,
    
  •            &cond_timeout) != 0)
    
  • break;

- }

  • /*
  • * If it didn't exit abort the pending read and wait an additional second...
  • */
  • pthread_mutex_lock(&g.read_thread_mutex);

if (!g.read_thread_done)
{

  •  fputs("DEBUG: Read thread still active, aborting the pending read...\n",
    

- stderr);

  •  g.wait_eof = 0;
    
  •  fputs("DEBUG: Waiting for read thread to exit...\n", stderr);
    

    gettimeofday(&tv, NULL);

  •  cond_timeout.tv_sec  = tv.tv_sec + 1;
    
  •  cond_timeout.tv_sec  = tv.tv_sec + WAIT_EOF_DELAY;
    

    cond_timeout.tv_nsec = tv.tv_usec * 1000;

    while (!g.read_thread_done)
    @@ -555,10 +567,34 @@
    &cond_timeout) != 0)
    break;
    }
    +

  •  /*
    
  •   \* If it didn't exit abort the pending read and wait an additional
    
  •   \* second...
    
  •   */
    
  •  if (!g.read_thread_done)
    
  •  {
    
  • fputs("DEBUG: Read thread still active, aborting the pending read...\n",

  •     stderr);
    
  • g.wait_eof = 0;

  • gettimeofday(&tv, NULL);
  • cond_timeout.tv_sec = tv.tv_sec + 1;
  • cond_timeout.tv_nsec = tv.tv_usec * 1000;
  • while (!g.read_thread_done)
  • {
  • if (pthread_cond_timedwait(&g.read_thread_cond, &g.read_thread_mutex,
    
  •                &cond_timeout) != 0)
    
  •   break;
    
  • }
  •  }
    
    }
  • }
  • pthread_mutex_unlock(&g.read_thread_mutex);
  • pthread_mutex_unlock(&g.read_thread_mutex);
  • }

if (print_fd)
close(print_fd);
@@ -601,24 +637,51 @@
*/

int errcode; /* Return value of libusb function */

  • int number; /* Interface number */

  • int number1, /* Interface number */

  • number2; /* Configuration number */

  • errcode =

  • errcode =
    libusb_get_config_descriptor (printer->device, printer->conf, &confptr);
    if (errcode >= 0)
    {

  •  number = confptr->interface[printer->iface].
    
  •  number1 = confptr->interface[printer->iface].
    

    altsetting[printer->altset].bInterfaceNumber;

  •  libusb_release_interface(printer->handle, number);
    
  •  if (number != 0)
    
  • libusb_release_interface(printer->handle, 0);

  •  libusb_release_interface(printer->handle, number1);
    
  •  number2 = confptr->bConfigurationValue;
    
  •  libusb_free_config_descriptor(confptr);
    
  • /*
    
  •  \* If we have changed the configuration, restore the old one
    
  •  */
    
  •  if (printer->origconf != number2)
    
  • fprintf(stderr, "DEBUG: Restoring USB device configuration: %d -> %d\n",

  •   number2, printer->origconf);
    
  • if ((errcode = libusb_set_configuration(printer->handle,

  •                   printer->origconf)) < 0)
    
  • {

  • if (errcode != LIBUSB_ERROR_BUSY)
    
  • {
    
  •   errcode =
    
  •     libusb_get_device_descriptor (printer->device, &devdesc);
    
  •   if (errcode < 0)
    
  •     fprintf(stderr,
    
  •         "DEBUG: Failed to set configuration %d\n",
    
  •         printer->origconf);
    
  •   else
    
  •     fprintf(stderr,
    
  •         "DEBUG: Failed to set configuration %d for %04x:%04x\n",
    
  •         printer->origconf, devdesc.idVendor, devdesc.idProduct);
    
  • }
    
  • }

    /*

  • Re-attach "usblp" kernel module if it was attached before using this

  • device
    */
    if (printer->usblp_attached == 1)

  • if (libusb_attach_kernel_driver(printer->handle, printer->iface) < 0)

  • if (libusb_attach_kernel_driver(printer->handle, number1) < 0)
    {
    errcode = libusb_get_device_descriptor (printer->device, &devdesc);
    if (errcode < 0)
    @@ -629,9 +692,11 @@
    "DEBUG: Failed to re-attach "usblp" kernel module to "
    "%04x:%04x\n", devdesc.idVendor, devdesc.idProduct);
    }

  •  libusb_free_config_descriptor(confptr);
    

    }

  • else

  •  fprintf(stderr,
    
  •     "DEBUG: Failed to get configuration descriptor %d\n",
    
  •     printer->conf);
    

    /*

    • Close the interface and return...
      @@ -764,7 +829,10 @@
      protocol = altptr->bInterfaceProtocol;
      printer.altset = altset;
      printer.write_endp = write_endp;
  •     printer.read_endp  = read_endp;
    
  •     if (protocol > 1)
    
  •   printer.read_endp = read_endp;
    
  •     else
    
  •   printer.read_endp = -1;
    }
    

    }

@@ -782,16 +850,29 @@
make_device_uri(&printer, device_id, device_uri,
sizeof(device_uri));

  •     fprintf(stderr, "DEBUG: Printer found with device ID: %s "
    
  •         "Device URI: %s\n",
    
  •         device_id, device_uri);
    
    • if ((*cb)(&printer, device_uri, device_id, data))
      {
      
  •   printer.read_endp  = confptr->interface[printer.iface].
    
  •                  altsetting[printer.altset].
    
  •                  endpoint[printer.read_endp].
    
  •                  bEndpointAddress;
    
  •   fprintf(stderr, "DEBUG: Device protocol: %d\n",
    
  •       printer.protocol);
    
  •   if (printer.read_endp != -1)
    
  •   {
    
  •     printer.read_endp = confptr->interface[printer.iface].
    
  •                   altsetting[printer.altset].
    
  •                   endpoint[printer.read_endp].
    
  •                   bEndpointAddress;
    
  •   }
    
  •   else
    
  •     fprintf(stderr, "DEBUG: Uni-directional USB communication " 
    
  •         "only!\n");
    printer.write_endp = confptr->interface[printer.iface].
                   altsetting[printer.altset].
                   endpoint[printer.write_endp].
                   bEndpointAddress;
    
  •   libusb_free_config_descriptor(confptr);
    return (&printer);
           }
    

@@ -1095,8 +1176,12 @@

  • Try opening the printer...
    */
  • if (libusb_open(printer->device, &printer->handle) < 0)
  • if ((errcode = libusb_open(printer->device, &printer->handle)) < 0)
  • {
  • fprintf(stderr, "DEBUG: Failed to open device, code: %d\n",
  •   errcode);
    
    return (-1);
  • }

printer->usblp_attached = 0;

@@ -1132,14 +1217,9 @@
else
{

printer->usblp_attached = 0;

  • if (errcode != LIBUSB_ERROR_NOT_SUPPORTED)
  • {
  •  fprintf(stderr,
    
  •     "DEBUG: Failed to check whether %04x:%04x has the \"usblp\" "
    
  •     "kernel module attached\n", devdesc.idVendor, devdesc.idProduct);
    
  •  goto error;
    
  • }
  • fprintf(stderr, "DEBUG: Failed to check whether %04x:%04x has the "usblp" kernel module attached\n",
  •     devdesc.idVendor, devdesc.idProduct);
    
  • goto error;
    }

/*
@@ -1156,7 +1236,9 @@
0, 0, (unsigned char )&current, 1, 5000) < 0)
current = 0; /
Assume not configured */

  • if ((errcode =
  • printer->origconf = current;
  • if ((errcode =
    libusb_get_config_descriptor (printer->device, printer->conf, &confptr))
    < 0)
    {
    @@ -1168,6 +1250,8 @@

if (number1 != current)
{

  • fprintf(stderr, "DEBUG: Switching USB device configuration: %d -> %d\n",

  •   current, number1);
    

    if ((errcode = libusb_set_configuration(printer->handle, number1)) < 0)
    {
    /*
    @@ -1347,6 +1431,39 @@

    /*

  • * 'printer_class_soft_reset()' - Do the soft reset request specific to printers

  • * This soft reset is specific to the printer device class and is much less

  • * invasive than the general USB reset libusb_reset_device(). Especially it

  • * does never happen that the USB addressing and configuration changes. What

  • * is actually done is that all buffers get flushed and the bulk IN and OUT

  • * pipes get reset to their default states. This clears all stall conditions.

  • * See http://cholla.mmto.org/computers/linux/usb/usbprint11.pdf

  • /
    +
    +static int /
    O - 0 on success, < 0 on error /
    +printer_class_soft_reset(usb_printer_t *printer) /
    I - Printer */
    +{

  • struct libusb_config_descriptor *confptr = NULL;

  •                                    /\* Pointer to current configuration */
    
  • int interface;

  • if (libusb_get_config_descriptor (printer->device, printer->conf, &confptr)
  •  < 0)
    
  • interface = printer->iface;
  • else
  • interface = confptr->interface[printer->iface].
  •  altsetting[printer->altset].bInterfaceNumber;
    
  • libusb_free_config_descriptor(confptr);
  • return libusb_control_transfer(printer->handle,
  •            LIBUSB_REQUEST_TYPE_CLASS |
    
  •            LIBUSB_ENDPOINT_OUT |
    
  •            LIBUSB_RECIPIENT_INTERFACE,
    
  •            2, 0, interface, NULL, 0, 5000);
    
    +}
    +
    +
    +/*
    • 'read_thread()' - Thread to read the backchannel data on.
      */

@@ -1620,7 +1737,7 @@

  • Send the reset...
    */
  • libusb_reset_device (g.printer->handle);
  • printer_class_soft_reset (g.printer);

/*

  • Release the I/O lock...

@michaelrsweet
Copy link
Collaborator Author

"usb-libusb-c-update-1-5-x.patch":

--- backend/usb-libusb.c 2012-06-22 11:53:36.103353715 +0200
+++ backend/usb-libusb.c 2012-06-22 11:52:50.723163432 +0200
@@ -22,6 +22,8 @@

  • make_device_uri() - Create a device URI for a USB printer.
  • open_device() - Open a connection to the USB printer.
  • print_cb() - Find a USB printer for printing.
  • * printer_class_soft_reset()' - Do the soft reset request specific to
  • * printers
  • read_thread() - Thread to read the backchannel data on.
  • sidechannel_thread() - Handle side-channel requests.
  • soft_reset() - Send a soft reset to the device.
    @@ -60,12 +62,13 @@
    {
    struct libusb_device device; / Device info /
    int conf, /
    Configuration */
  •       origconf,   /\* Original configuration _/
    iface,      /_ Interface _/
    altset,     /_ Alternate setting _/
    write_endp, /_ Write endpoint */
    
  •                    read_endp, /\* Read endpoint */
    
  •       read_endp,  /\* Read endpoint _/
    protocol,   /_ Protocol: 1 = Uni-di, 2 = Bi-di. */
    
  •                    usblp_attached; /\* Is the "usblp" kernel module
    
  •       usblp_attached; /\* Is the "usblp" kernel module
               attached? _/
    
    struct libusb_device_handle *handle; /_ Open handle to device /
    } usb_printer_t;
    @@ -124,6 +127,7 @@
    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);
    +static int printer_class_soft_reset(usb_printer_t *printer);
    static void *read_thread(void *reference);
    static void *sidechannel_thread(void *reference);
    static void soft_reset(void);
    @@ -163,7 +167,8 @@
    iostatus; /
    Current IO status /
    pthread_t read_thread_id, /
    Read thread /
    sidechannel_thread_id; /
    Side-channel thread */
  • int have_sidechannel = 0; /* Was the side-channel thread started? */
  • int have_sidechannel = 0, /* Was the side-channel thread started? */
  •   have_backchannel = 0;   /\* Do we have a back channel? _/
    
    struct stat sidechannel_info; /_ Side-channel file descriptor info /
    unsigned char print_buffer[8192], /
    Print data buffer /
    *print_ptr; /
    Pointer into print data buffer /
    @@ -172,6 +177,9 @@
    struct timeval *timeout, /
    Timeout pointer /
    tv; /
    Time value /
    struct timespec cond_timeout; /
    pthread condition timeout */
  • int num_opts; /* Number of options */
  • cups_option_t opts; / Options */
  • const char val; / Option value */

/*
@@ -187,6 +195,7 @@

  • Connect to the printer...
    */
  • fprintf(stderr, "DEBUG: Printing on printer with URI: %s\n", uri);
    while ((g.printer = find_device(print_cb, uri)) == NULL)
    {
    _cupsLangPrintFilter(stderr, "INFO",
    @@ -240,24 +249,47 @@
    }

/*

  • * Get the read thread going...
  • * Debug mode: If option "usb-unidir" is given, always deactivate
    • backchannel
      */
  • g.read_thread_stop = 0;
  • g.read_thread_done = 0;
  • num_opts = cupsParseOptions(argv[5], 0, &opts);
  • val = cupsGetOption("usb-unidir", num_opts, opts);
  • if (val && strcasecmp(val, "no") && strcasecmp(val, "off") &&
  •  strcasecmp(val, "false"))
    
  • {
  • g.printer->read_endp = -1;
  • fprintf(stderr, "DEBUG: Forced uni-directional communication "
  •   "via \"usb-unidir\" option.\n");
    
  • }
  • pthread_cond_init(&g.read_thread_cond, NULL);
  • pthread_mutex_init(&g.read_thread_mutex, NULL);
  • /*
  • * Get the read thread going...
  • */
  • if (pthread_create(&read_thread_id, NULL, read_thread, NULL))
  • if (g.printer->read_endp != -1)
    {
  • fprintf(stderr, "DEBUG: Fatal USB error.\n");
  • _cupsLangPrintFilter(stderr, "ERROR",
  •                     _("There was an unrecoverable USB error."));
    
  • fputs("DEBUG: Couldn't create read thread.\n", stderr);
  • close_device(g.printer);
  • return (CUPS_BACKEND_STOP);
  • have_backchannel = 1;
  • g.read_thread_stop = 0;
  • g.read_thread_done = 0;
  • pthread_cond_init(&g.read_thread_cond, NULL);
  • pthread_mutex_init(&g.read_thread_mutex, NULL);
  • if (pthread_create(&read_thread_id, NULL, read_thread, NULL))

  • {

  •  fprintf(stderr, "DEBUG: Fatal USB error.\n");
    
  •  _cupsLangPrintFilter(stderr, "ERROR",
    
  •          _("There was an unrecoverable USB error."));
    
  •  fputs("DEBUG: Couldn't create read thread.\n", stderr);
    
  •  close_device(g.printer);
    
  •  return (CUPS_BACKEND_STOP);
    
  • }
    }

  • else

  • fprintf(stderr, "DEBUG: Uni-directional device/mode, back channel "

  •   "deactivated.\n");
    

    /*

    • The main thread sends the print file...
      @@ -515,50 +547,54 @@
    • Signal the read thread to exit then wait 7 seconds for it to complete...
      */

- g.read_thread_stop = 1;

- pthread_mutex_lock(&g.read_thread_mutex);

  • if (!g.read_thread_done)
  • if (have_backchannel)
    {

- fputs("DEBUG: Waiting for read thread to exit...\n", stderr);

  • gettimeofday(&tv, NULL);
  • cond_timeout.tv_sec = tv.tv_sec + WAIT_EOF_DELAY;
  • cond_timeout.tv_nsec = tv.tv_usec * 1000;
  • g.read_thread_stop = 1;
  • while (!g.read_thread_done)
  • {
  •  if (pthread_cond_timedwait(&g.read_thread_cond, &g.read_thread_mutex,
    
  •            &cond_timeout) != 0)
    
  • break;
  • }
  • pthread_mutex_lock(&g.read_thread_mutex);
  • /*
  • * If it didn't exit abort the pending read and wait an additional second...

- */

 if (!g.read_thread_done)
 {
  •  fputs("DEBUG: Read thread still active, aborting the pending read...\n", 
    

- stderr);

  •  g.wait_eof = 0;
    
  •  fputs("DEBUG: Waiting for read thread to exit...\n", stderr);
    

    gettimeofday(&tv, NULL);

  •  cond_timeout.tv_sec  = tv.tv_sec + 1;
    
  •  cond_timeout.tv_sec  = tv.tv_sec + WAIT_EOF_DELAY;
    

    cond_timeout.tv_nsec = tv.tv_usec * 1000;

    • while (!g.read_thread_done)
      {
      if (pthread_cond_timedwait(&g.read_thread_cond, &g.read_thread_mutex,
      &cond_timeout) != 0)
      break;
      }
  •  /*
    
  •   \* If it didn't exit abort the pending read and wait an additional
    
  •   \* second...
    
  •   */
    
  •  if (!g.read_thread_done)
    
  •  {
    
  • fputs("DEBUG: Read thread still active, aborting the pending read...\n",

  •     stderr);
    
  • g.wait_eof = 0;

  • gettimeofday(&tv, NULL);
  • cond_timeout.tv_sec = tv.tv_sec + 1;
  • cond_timeout.tv_nsec = tv.tv_usec * 1000;
  • while (!g.read_thread_done)
  • {
  • if (pthread_cond_timedwait(&g.read_thread_cond, &g.read_thread_mutex,
    
  •                &cond_timeout) != 0)
    
  •   break;
    
  • }
  •  }
    
    }
  • }
  • pthread_mutex_unlock(&g.read_thread_mutex);
  • pthread_mutex_unlock(&g.read_thread_mutex);
  • }

if (print_fd)
close(print_fd);
@@ -601,24 +637,51 @@
*/

int errcode; /* Return value of libusb function */

  • int number; /* Interface number */
  • int number1, /* Interface number */
  • number2; /* Configuration number */

errcode =
libusb_get_config_descriptor (printer->device, printer->conf, &confptr);
if (errcode >= 0)
{

  •  number = confptr->interface[printer->iface].
    
  •  number1 = confptr->interface[printer->iface].
    

    altsetting[printer->altset].bInterfaceNumber;

  •  libusb_release_interface(printer->handle, number);
    
  •  if (number != 0)
    
  • libusb_release_interface(printer->handle, 0);

  •  libusb_release_interface(printer->handle, number1);
    
  •  number2 = confptr->bConfigurationValue;
    
  •  libusb_free_config_descriptor(confptr);
    
  • /*
    
  •  \* If we have changed the configuration, restore the old one
    
  •  */
    
  •  if (printer->origconf != number2)
    
  • fprintf(stderr, "DEBUG: Restoring USB device configuration: %d -> %d\n",

  •   number2, printer->origconf);
    
  • if ((errcode = libusb_set_configuration(printer->handle,

  •                   printer->origconf)) < 0)
    
  • {

  • if (errcode != LIBUSB_ERROR_BUSY)
    
  • {
    
  •   errcode =
    
  •     libusb_get_device_descriptor (printer->device, &devdesc);
    
  •   if (errcode < 0)
    
  •     fprintf(stderr,
    
  •         "DEBUG: Failed to set configuration %d\n",
    
  •         printer->origconf);
    
  •   else
    
  •     fprintf(stderr,
    
  •         "DEBUG: Failed to set configuration %d for %04x:%04x\n",
    
  •         printer->origconf, devdesc.idVendor, devdesc.idProduct);
    
  • }
    
  • }

    /*

  • Re-attach "usblp" kernel module if it was attached before using this

  • device
    */
    if (printer->usblp_attached == 1)

  • if (libusb_attach_kernel_driver(printer->handle, printer->iface) < 0)

  • if (libusb_attach_kernel_driver(printer->handle, number1) < 0)
    {
    errcode = libusb_get_device_descriptor (printer->device, &devdesc);
    if (errcode < 0)
    @@ -629,9 +692,11 @@
    "DEBUG: Failed to re-attach "usblp" kernel module to "
    "%04x:%04x\n", devdesc.idVendor, devdesc.idProduct);
    }

  •  libusb_free_config_descriptor(confptr);
    

    }

  • else

  •  fprintf(stderr,
    
  •     "DEBUG: Failed to get configuration descriptor %d\n",
    
  •     printer->conf);
    

    /*

    • Close the interface and return...
      @@ -764,7 +829,10 @@
      protocol = altptr->bInterfaceProtocol;
      printer.altset = altset;
      printer.write_endp = write_endp;
  •     printer.read_endp  = read_endp;
    
  •     if (protocol > 1)
    
  •   printer.read_endp = read_endp;
    
  •     else
    
  •   printer.read_endp = -1;
    }
    

    }

@@ -782,16 +850,29 @@
make_device_uri(&printer, device_id, device_uri,
sizeof(device_uri));

  •     fprintf(stderr, "DEBUG: Printer found with device ID: %s "
    
  •         "Device URI: %s\n",
    
  •         device_id, device_uri);
    
    • if ((*cb)(&printer, device_uri, device_id, data))
      {
      
  •   printer.read_endp  = confptr->interface[printer.iface].
    
  •                  altsetting[printer.altset].
    
  •                  endpoint[printer.read_endp].
    
  •                  bEndpointAddress;
    
  •   fprintf(stderr, "DEBUG: Device protocol: %d\n",
    
  •       printer.protocol);
    
  •   if (printer.read_endp != -1)
    
  •   {
    
  •     printer.read_endp = confptr->interface[printer.iface].
    
  •                   altsetting[printer.altset].
    
  •                   endpoint[printer.read_endp].
    
  •                   bEndpointAddress;
    
  •   }
    
  •   else
    
  •     fprintf(stderr, "DEBUG: Uni-directional USB communication " 
    
  •         "only!\n");
    printer.write_endp = confptr->interface[printer.iface].
                   altsetting[printer.altset].
                   endpoint[printer.write_endp].
                   bEndpointAddress;
    
  •   libusb_free_config_descriptor(confptr);
    return (&printer);
           }
    

@@ -1095,8 +1176,12 @@

  • Try opening the printer...
    */
  • if (libusb_open(printer->device, &printer->handle) < 0)
  • if ((errcode = libusb_open(printer->device, &printer->handle)) < 0)
  • {
  • fprintf(stderr, "DEBUG: Failed to open device, code: %d\n",
  •   errcode);
    
    return (-1);
  • }

printer->usblp_attached = 0;

@@ -1132,14 +1217,9 @@
else
{

printer->usblp_attached = 0;

  • if (errcode != LIBUSB_ERROR_NOT_SUPPORTED)
  • {
  •  fprintf(stderr,
    
  •     "DEBUG: Failed to check whether %04x:%04x has the \"usblp\" "
    
  •     "kernel module attached\n", devdesc.idVendor, devdesc.idProduct);
    
  •  goto error;
    
  • }
  • fprintf(stderr, "DEBUG: Failed to check whether %04x:%04x has the "usblp" kernel module attached\n",
  •     devdesc.idVendor, devdesc.idProduct);
    
  • goto error;
    }

/*
@@ -1156,6 +1236,8 @@
0, 0, (unsigned char )&current, 1, 5000) < 0)
current = 0; /
Assume not configured */

  • printer->origconf = current;

if ((errcode =
libusb_get_config_descriptor (printer->device, printer->conf, &confptr))
< 0)
@@ -1168,6 +1250,8 @@

if (number1 != current)
{

  • fprintf(stderr, "DEBUG: Switching USB device configuration: %d -> %d\n",

  •   current, number1);
    

    if ((errcode = libusb_set_configuration(printer->handle, number1)) < 0)
    {
    /*
    @@ -1347,6 +1431,39 @@

    /*

  • * 'printer_class_soft_reset()' - Do the soft reset request specific to printers

  • * This soft reset is specific to the printer device class and is much less

  • * invasive than the general USB reset libusb_reset_device(). Especially it

  • * does never happen that the USB addressing and configuration changes. What

  • * is actually done is that all buffers get flushed and the bulk IN and OUT

  • * pipes get reset to their default states. This clears all stall conditions.

  • * See http://cholla.mmto.org/computers/linux/usb/usbprint11.pdf

  • /
    +
    +static int /
    O - 0 on success, < 0 on error /
    +printer_class_soft_reset(usb_printer_t *printer) /
    I - Printer */
    +{

  • struct libusb_config_descriptor *confptr = NULL;

  •                                    /\* Pointer to current configuration */
    
  • int interface;

  • if (libusb_get_config_descriptor (printer->device, printer->conf, &confptr)
  •  < 0)
    
  • interface = printer->iface;
  • else
  • interface = confptr->interface[printer->iface].
  •  altsetting[printer->altset].bInterfaceNumber;
    
  • libusb_free_config_descriptor(confptr);
  • return libusb_control_transfer(printer->handle,
  •            LIBUSB_REQUEST_TYPE_CLASS |
    
  •            LIBUSB_ENDPOINT_OUT |
    
  •            LIBUSB_RECIPIENT_INTERFACE,
    
  •            2, 0, interface, NULL, 0, 5000);
    
    +}
    +
    +
    +/*
    • 'read_thread()' - Thread to read the backchannel data on.
      */

@@ -1620,7 +1737,7 @@

  • Send the reset...
    */
  • libusb_reset_device (g.printer->handle);
  • printer_class_soft_reset (g.printer);

/*

  • Release the I/O lock...

@michaelrsweet
Copy link
Collaborator Author

"usb-libusb-c-update-1-6-x-2.patch":

--- backend/usb-libusb.c 2012-06-22 11:53:18.043277991 +0200
+++ backend/usb-libusb.c 2012-06-27 17:56:04.801648562 +0200
@@ -22,6 +22,9 @@

  • make_device_uri() - Create a device URI for a USB printer.
  • open_device() - Open a connection to the USB printer.
  • print_cb() - Find a USB printer for printing.
  • * printer_class_soft_reset()' - Do the soft reset request specific to
  • * printers
  • * quirks() - Get the known quirks of a given printer model
  • read_thread() - Thread to read the backchannel data on.
  • sidechannel_thread() - Handle side-channel requests.
  • soft_reset() - Send a soft reset to the device.
    @@ -60,13 +63,14 @@
    {
    struct libusb_device device; / Device info /
    int conf, /
    Configuration */
  •       origconf,   /\* Original configuration _/
    iface,      /_ Interface _/
    altset,     /_ Alternate setting _/
    write_endp, /_ Write endpoint */
    
  •                    read_endp, /\* Read endpoint */
    
  •       read_endp,  /\* Read endpoint _/
    protocol,   /_ Protocol: 1 = Uni-di, 2 = Bi-di. */
    
  •                    usblp_attached; /\* Is the "usblp" kernel module
    
  •                  attached? */
    
  •       usblp_attached; /\* "usblp" kernel module attached? */
    
  • unsigned int quirks; /* Quirks flags /
    struct libusb_device_handle *handle; /
    Open handle to device */
    } usb_printer_t;

@@ -99,6 +103,54 @@
int sidechannel_thread_done;
} usb_globals_t;

+/*

  • * Quirks: various printer quirks are handled by this table & its flags.
  • * This is copied from the usblp kernel module. So we can easily copy and paste
  • * new quirks from the module.
  • */
    +
    +struct quirk_printer_struct {
  • int vendorId;
  • int productId;
  • unsigned int quirks;
    +};

+#define USBLP_QUIRK_BIDIR 0x1 /* reports bidir but requires

  •                  unidirectional mode (no INs/reads) _/
    
    +#define USBLP_QUIRK_USB_INIT 0x2 /_ needs vendor USB init string /
    +#define USBLP_QUIRK_BAD_CLASS 0x4 /
    descriptor uses vendor-specific
  •                  Class or SubClass _/
    
    +#define USBLP_QUIRK_NO_REATTACH 0x8000 /_ After printing we cannot re-attach
  •                  the usblp kernel module */
    
    +static const struct quirk_printer_struct quirk_printers[] = {
  • { 0x03f0, 0x0004, USBLP_QUIRK_BIDIR }, /* HP DeskJet 895C */
  • { 0x03f0, 0x0104, USBLP_QUIRK_BIDIR }, /* HP DeskJet 880C */
  • { 0x03f0, 0x0204, USBLP_QUIRK_BIDIR }, /* HP DeskJet 815C */
  • { 0x03f0, 0x0304, USBLP_QUIRK_BIDIR }, /* HP DeskJet 810C/812C */
  • { 0x03f0, 0x0404, USBLP_QUIRK_BIDIR }, /* HP DeskJet 830C */
  • { 0x03f0, 0x0504, USBLP_QUIRK_BIDIR }, /* HP DeskJet 885C */
  • { 0x03f0, 0x0604, USBLP_QUIRK_BIDIR }, /* HP DeskJet 840C */
  • { 0x03f0, 0x0804, USBLP_QUIRK_BIDIR }, /* HP DeskJet 816C */
  • { 0x03f0, 0x1104, USBLP_QUIRK_BIDIR }, /* HP Deskjet 959C */
  • { 0x0409, 0xefbe, USBLP_QUIRK_BIDIR }, /* NEC Picty900 (HP OEM) */
  • { 0x0409, 0xbef4, USBLP_QUIRK_BIDIR }, /* NEC Picty760 (HP OEM) */
  • { 0x0409, 0xf0be, USBLP_QUIRK_BIDIR }, /* NEC Picty920 (HP OEM) */
  • { 0x0409, 0xf1be, USBLP_QUIRK_BIDIR }, /* NEC Picty800 (HP OEM) */
  • { 0x0482, 0x0010, USBLP_QUIRK_BIDIR }, /* Kyocera Mita FS 820,
  •                     by zut kernel@zut.de */
    
  • { 0x04f9, 0x000d, USBLP_QUIRK_BIDIR }, /* Brother Industries, Ltd
  •                     HL-1440 Laser Printer */
    
  • { 0x04b8, 0x0202, USBLP_QUIRK_BAD_CLASS }, /* Seiko Epson Receipt
  •                         Printer M129C */
    
  • { 0x067b, 0x2305, USBLP_QUIRK_BIDIR |
  •         USBLP_QUIRK_NO_REATTACH },
    
  • /* Prolific Technology, Inc. PL2305 Parallel Port
  •  (USB -> Parallel adapter) */
    
  • { 0, 0 }
    +};

/*

  • Globals...
    @@ -124,6 +176,8 @@
    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);
    +static int printer_class_soft_reset(usb_printer_t *printer);
    +static unsigned int quirks(int vendor, int product);
    static void *read_thread(void *reference);
    static void *sidechannel_thread(void *reference);
    static void soft_reset(void);
    @@ -163,7 +217,8 @@
    iostatus; /
    Current IO status /
    pthread_t read_thread_id, /
    Read thread /
    sidechannel_thread_id; /
    Side-channel thread */
  • int have_sidechannel = 0; /* Was the side-channel thread started? */
  • int have_sidechannel = 0, /* Was the side-channel thread started? */
  •   have_backchannel = 0;   /\* Do we have a back channel? _/
    
    struct stat sidechannel_info; /_ Side-channel file descriptor info /
    unsigned char print_buffer[8192], /
    Print data buffer /
    *print_ptr; /
    Pointer into print data buffer /
    @@ -172,6 +227,9 @@
    struct timeval *timeout, /
    Timeout pointer /
    tv; /
    Time value /
    struct timespec cond_timeout; /
    pthread condition timeout */
  • int num_opts; /* Number of options */
  • cups_option_t opts; / Options */
  • const char val; / Option value */

/*
@@ -187,6 +245,7 @@

  • Connect to the printer...
    */
  • fprintf(stderr, "DEBUG: Printing on printer with URI: %s\n", uri);
    while ((g.printer = find_device(print_cb, uri)) == NULL)
    {
    _cupsLangPrintFilter(stderr, "INFO",
    @@ -240,24 +299,47 @@
    }

/*

  • * Get the read thread going...
  • * Debug mode: If option "usb-unidir" is given, always deactivate
    • backchannel
      */
  • g.read_thread_stop = 0;
  • g.read_thread_done = 0;
  • num_opts = cupsParseOptions(argv[5], 0, &opts);
  • val = cupsGetOption("usb-unidir", num_opts, opts);
  • if (val && strcasecmp(val, "no") && strcasecmp(val, "off") &&
  •  strcasecmp(val, "false"))
    
  • {
  • g.printer->read_endp = -1;
  • fprintf(stderr, "DEBUG: Forced uni-directional communication "
  •   "via \"usb-unidir\" option.\n");
    
  • }
  • pthread_cond_init(&g.read_thread_cond, NULL);
  • pthread_mutex_init(&g.read_thread_mutex, NULL);
  • /*
  • * Get the read thread going...
  • */
  • if (pthread_create(&read_thread_id, NULL, read_thread, NULL))
  • if (g.printer->read_endp != -1)
    {
  • fprintf(stderr, "DEBUG: Fatal USB error.\n");
  • _cupsLangPrintFilter(stderr, "ERROR",
  •                     _("There was an unrecoverable USB error."));
    
  • fputs("DEBUG: Couldn't create read thread.\n", stderr);
  • close_device(g.printer);
  • return (CUPS_BACKEND_STOP);
  • have_backchannel = 1;
  • g.read_thread_stop = 0;
  • g.read_thread_done = 0;
  • pthread_cond_init(&g.read_thread_cond, NULL);
  • pthread_mutex_init(&g.read_thread_mutex, NULL);
  • if (pthread_create(&read_thread_id, NULL, read_thread, NULL))

  • {

  •  fprintf(stderr, "DEBUG: Fatal USB error.\n");
    
  •  _cupsLangPrintFilter(stderr, "ERROR",
    
  •          _("There was an unrecoverable USB error."));
    
  •  fputs("DEBUG: Couldn't create read thread.\n", stderr);
    
  •  close_device(g.printer);
    
  •  return (CUPS_BACKEND_STOP);
    
  • }
    }

  • else

  • fprintf(stderr, "DEBUG: Uni-directional device/mode, back channel "

  •   "deactivated.\n");
    

    /*

    • The main thread sends the print file...
      @@ -515,38 +597,18 @@
    • Signal the read thread to exit then wait 7 seconds for it to complete...
      */

- g.read_thread_stop = 1;

- pthread_mutex_lock(&g.read_thread_mutex);

  • if (!g.read_thread_done)
  • if (have_backchannel)
    {
  • fputs("DEBUG: Waiting for read thread to exit...\n", stderr);
  • g.read_thread_stop = 1;
  • gettimeofday(&tv, NULL);
  • cond_timeout.tv_sec = tv.tv_sec + WAIT_EOF_DELAY;

- cond_timeout.tv_nsec = tv.tv_usec * 1000;

  • while (!g.read_thread_done)
  • {
  •  if (pthread_cond_timedwait(&g.read_thread_cond, &g.read_thread_mutex,
    
  •            &cond_timeout) != 0)
    
  • break;

- }

  • /*
  • * If it didn't exit abort the pending read and wait an additional second...
  • */
  • pthread_mutex_lock(&g.read_thread_mutex);

if (!g.read_thread_done)
{

  •  fputs("DEBUG: Read thread still active, aborting the pending read...\n",
    

- stderr);

  •  g.wait_eof = 0;
    
  •  fputs("DEBUG: Waiting for read thread to exit...\n", stderr);
    

    gettimeofday(&tv, NULL);

  •  cond_timeout.tv_sec  = tv.tv_sec + 1;
    
  •  cond_timeout.tv_sec  = tv.tv_sec + WAIT_EOF_DELAY;
    

    cond_timeout.tv_nsec = tv.tv_usec * 1000;

    while (!g.read_thread_done)
    @@ -555,10 +617,34 @@
    &cond_timeout) != 0)
    break;
    }
    +

  •  /*
    
  •   \* If it didn't exit abort the pending read and wait an additional
    
  •   \* second...
    
  •   */
    
  •  if (!g.read_thread_done)
    
  •  {
    
  • fputs("DEBUG: Read thread still active, aborting the pending read...\n",

  •     stderr);
    
  • g.wait_eof = 0;

  • gettimeofday(&tv, NULL);
  • cond_timeout.tv_sec = tv.tv_sec + 1;
  • cond_timeout.tv_nsec = tv.tv_usec * 1000;
  • while (!g.read_thread_done)
  • {
  • if (pthread_cond_timedwait(&g.read_thread_cond, &g.read_thread_mutex,
    
  •                &cond_timeout) != 0)
    
  •   break;
    
  • }
  •  }
    
    }
  • }
  • pthread_mutex_unlock(&g.read_thread_mutex);
  • pthread_mutex_unlock(&g.read_thread_mutex);
  • }

if (print_fd)
close(print_fd);
@@ -601,24 +687,54 @@
*/

int errcode; /* Return value of libusb function */

  • int number; /* Interface number */

  • int number1, /* Interface number */

  • number2; /* Configuration number */

  • errcode =

  • errcode =
    libusb_get_config_descriptor (printer->device, printer->conf, &confptr);
    if (errcode >= 0)
    {

  •  number = confptr->interface[printer->iface].
    
  •  number1 = confptr->interface[printer->iface].
    

    altsetting[printer->altset].bInterfaceNumber;

  •  libusb_release_interface(printer->handle, number);
    
  •  if (number != 0)
    
  • libusb_release_interface(printer->handle, 0);

  •  libusb_release_interface(printer->handle, number1);
    
  •  number2 = confptr->bConfigurationValue;
    
  •  libusb_free_config_descriptor(confptr);
    
  • /*
    
  •  \* If we have changed the configuration from one valid configuration
    
  •  \* to another, restore the old one
    
  •  */
    
  •  if (printer->origconf > 0 && printer->origconf != number2)
    
  •  {
    
  • fprintf(stderr, "DEBUG: Restoring USB device configuration: %d -> %d\n",

  •   number2, printer->origconf);
    
  • if ((errcode = libusb_set_configuration(printer->handle,

  •                   printer->origconf)) < 0)
    
  • {

  • if (errcode != LIBUSB_ERROR_BUSY)
    
  • {
    
  •   errcode =
    
  •     libusb_get_device_descriptor (printer->device, &devdesc);
    
  •   if (errcode < 0)
    
  •     fprintf(stderr,
    
  •         "DEBUG: Failed to set configuration %d\n",
    
  •         printer->origconf);
    
  •   else
    
  •     fprintf(stderr,
    
  •         "DEBUG: Failed to set configuration %d for %04x:%04x\n",
    
  •         printer->origconf, devdesc.idVendor, devdesc.idProduct);
    
  • }
    
  • }

  •  }
    

    /*

    • Re-attach "usblp" kernel module if it was attached before using this
    • device
      */
      if (printer->usblp_attached == 1)
  • if (libusb_attach_kernel_driver(printer->handle, printer->iface) < 0)

  • if (libusb_attach_kernel_driver(printer->handle, number1) < 0)
    {
    errcode = libusb_get_device_descriptor (printer->device, &devdesc);
    if (errcode < 0)
    @@ -629,9 +745,11 @@
    "DEBUG: Failed to re-attach "usblp" kernel module to "
    "%04x:%04x\n", devdesc.idVendor, devdesc.idProduct);
    }

  •  libusb_free_config_descriptor(confptr);
    

    }

  • else

  •  fprintf(stderr,
    
  •     "DEBUG: Failed to get configuration descriptor %d\n",
    
  •     printer->conf);
    

    /*

    • Close the interface and return...
      @@ -709,9 +827,11 @@
      !devdesc.idProduct)
      continue;
  •  printer.quirks   = quirks(devdesc.idVendor, devdesc.idProduct);
    
    • for (conf = 0; conf < devdesc.bNumConfigurations; conf ++)
      {
  • if (libusb_get_config_descriptor(device, conf, &confptr) < 0)

  • if (libusb_get_config_descriptor (device, conf, &confptr) < 0)
    continue;
    for (iface = 0, ifaceptr = confptr->interface;
    iface < confptr->bNumInterfaces;
    @@ -733,13 +853,18 @@
    * 1284.4 (packet mode) protocol as well.
    */

  •   if (altptr->bInterfaceClass != LIBUSB_CLASS_PRINTER ||
    
  •       altptr->bInterfaceSubClass != 1 ||
    
  •   if (((altptr->bInterfaceClass != LIBUSB_CLASS_PRINTER ||
    
  •     altptr->bInterfaceSubClass != 1) && 
    
  •    ((printer.quirks & USBLP_QUIRK_BAD_CLASS) == 0)) ||
    (altptr->bInterfaceProtocol != 1 && /* Unidirectional */
     altptr->bInterfaceProtocol != 2) ||    /* Bidirectional */
    altptr->bInterfaceProtocol < protocol)
      continue;
    
  •   if (printer.quirks & USBLP_QUIRK_BAD_CLASS)
    
  •     fprintf(stderr, "DEBUG: Printer does not report class 7 and/or "
    
  •         "subclass 1 but works as a printer anyway\n");
    
    • read_endp = -1;
      write_endp = -1;

@@ -764,7 +889,10 @@
protocol = altptr->bInterfaceProtocol;
printer.altset = altset;
printer.write_endp = write_endp;

  •     printer.read_endp  = read_endp;
    
  •     if (protocol > 1)
    
  •   printer.read_endp = read_endp;
    
  •     else
    
  •   printer.read_endp = -1;
    }
    
    }

@@ -782,16 +910,41 @@
make_device_uri(&printer, device_id, device_uri,
sizeof(device_uri));

  •     fprintf(stderr, "DEBUG2: Printer found with device ID: %s "
    
  •         "Device URI: %s\n",
    
  •         device_id, device_uri);
    
    • if ((*cb)(&printer, device_uri, device_id, data))
      {
      
  •   printer.read_endp  = confptr->interface[printer.iface].
    
  •                  altsetting[printer.altset].
    
  •                  endpoint[printer.read_endp].
    
  •                  bEndpointAddress;
    
  •   fprintf(stderr, "DEBUG: Device protocol: %d\n",
    
  •       printer.protocol);
    
  •   if (printer.quirks & USBLP_QUIRK_BIDIR)
    
  •   {
    
  •     printer.read_endp = -1;
    
  •     fprintf(stderr, "DEBUG: Printer reports bi-di support "
    
  •         "but in reality works only uni-directionally\n");
    
  •   }
    
  •   if (printer.read_endp != -1)
    
  •   {
    
  •     printer.read_endp = confptr->interface[printer.iface].
    
  •                   altsetting[printer.altset].
    
  •                   endpoint[printer.read_endp].
    
  •                   bEndpointAddress;
    
  •   }
    
  •   else
    
  •     fprintf(stderr, "DEBUG: Uni-directional USB communication " 
    
  •         "only!\n");
    printer.write_endp = confptr->interface[printer.iface].
                   altsetting[printer.altset].
                   endpoint[printer.write_endp].
                   bEndpointAddress;
    
  •   if (printer.quirks & USBLP_QUIRK_NO_REATTACH)
    
  •   {
    
  •     printer.usblp_attached = 0;
    
  •     fprintf(stderr, "DEBUG: Printer does not like usblp "
    
  •         "kernel module to be re-attached after job\n");
    
  •   }
    
  •   libusb_free_config_descriptor(confptr);
    return (&printer);
           }
    

@@ -1095,8 +1248,12 @@

  • Try opening the printer...
    */
  • if (libusb_open(printer->device, &printer->handle) < 0)
  • if ((errcode = libusb_open(printer->device, &printer->handle)) < 0)
  • {
  • fprintf(stderr, "DEBUG: Failed to open device, code: %d\n",
  •   errcode);
    
    return (-1);
  • }

printer->usblp_attached = 0;

@@ -1132,14 +1289,9 @@
else
{

printer->usblp_attached = 0;

  • if (errcode != LIBUSB_ERROR_NOT_SUPPORTED)
  • {
  •  fprintf(stderr,
    
  •     "DEBUG: Failed to check whether %04x:%04x has the \"usblp\" "
    
  •     "kernel module attached\n", devdesc.idVendor, devdesc.idProduct);
    
  •  goto error;
    
  • }
  • fprintf(stderr, "DEBUG: Failed to check whether %04x:%04x has the "usblp" kernel module attached\n",
  •     devdesc.idVendor, devdesc.idProduct);
    
  • goto error;
    }

/*
@@ -1156,7 +1308,9 @@
0, 0, (unsigned char )&current, 1, 5000) < 0)
current = 0; /
Assume not configured */

  • if ((errcode =
  • printer->origconf = current;
  • if ((errcode =
    libusb_get_config_descriptor (printer->device, printer->conf, &confptr))
    < 0)
    {
    @@ -1168,6 +1322,8 @@

if (number1 != current)
{

  • fprintf(stderr, "DEBUG: Switching USB device configuration: %d -> %d\n",

  •   current, number1);
    

    if ((errcode = libusb_set_configuration(printer->handle, number1)) < 0)
    {
    /*
    @@ -1347,6 +1503,64 @@

    /*

  • * 'printer_class_soft_reset()' - Do the soft reset request specific to printers

  • * This soft reset is specific to the printer device class and is much less

  • * invasive than the general USB reset libusb_reset_device(). Especially it

  • * does never happen that the USB addressing and configuration changes. What

  • * is actually done is that all buffers get flushed and the bulk IN and OUT

  • * pipes get reset to their default states. This clears all stall conditions.

  • * See http://cholla.mmto.org/computers/linux/usb/usbprint11.pdf

  • /
    +
    +static int /
    O - 0 on success, < 0 on error /
    +printer_class_soft_reset(usb_printer_t *printer) /
    I - Printer */
    +{

  • struct libusb_config_descriptor *confptr = NULL;

  •                                    /\* Pointer to current configuration */
    
  • int interface,

  •  errcode;
    
  • if (libusb_get_config_descriptor (printer->device, printer->conf, &confptr)

  •  < 0)
    
  • interface = printer->iface;

  • else

  • interface = confptr->interface[printer->iface].

  •  altsetting[printer->altset].bInterfaceNumber;
    
  • libusb_free_config_descriptor(confptr);

  • if ((errcode = libusb_control_transfer(printer->handle,

  •                LIBUSB_REQUEST_TYPE_CLASS |
    
  •                LIBUSB_ENDPOINT_OUT |
    
  •                LIBUSB_RECIPIENT_OTHER,
    
  •                2, 0, interface, NULL, 0, 5000)) < 0)
    
  • errcode = libusb_control_transfer(printer->handle,

  •                 LIBUSB_REQUEST_TYPE_CLASS |
    
  •                 LIBUSB_ENDPOINT_OUT |
    
  •                 LIBUSB_RECIPIENT_INTERFACE,
    
  •                 2, 0, interface, NULL, 0, 5000);
    
  • return errcode;
    +}

+/*

  • * 'quirks()' - Get the known quirks of a given printer model
  • */
    +
    +static unsigned int quirks(int vendor, int product)
    +{
  • int i;
  • for (i = 0; quirk_printers[i].vendorId; i++)
  • {
  • if (vendor == quirk_printers[i].vendorId &&
  • product == quirk_printers[i].productId)
  •  return quirk_printers[i].quirks;
    
  • }
  • return 0;
    +}

+/*

  • 'read_thread()' - Thread to read the backchannel data on.
    */

@@ -1620,7 +1834,7 @@

  • Send the reset...
    */
  • libusb_reset_device (g.printer->handle);
  • printer_class_soft_reset (g.printer);

/*

  • Release the I/O lock...

@michaelrsweet
Copy link
Collaborator Author

"usb-libusb-c-update-1-5-x-2.patch":

--- backend/usb-libusb.c 2012-06-22 11:53:36.103353715 +0200
+++ backend/usb-libusb.c 2012-06-27 17:56:04.801648562 +0200
@@ -22,6 +22,9 @@

  • make_device_uri() - Create a device URI for a USB printer.
  • open_device() - Open a connection to the USB printer.
  • print_cb() - Find a USB printer for printing.
  • * printer_class_soft_reset()' - Do the soft reset request specific to
  • * printers
  • * quirks() - Get the known quirks of a given printer model
  • read_thread() - Thread to read the backchannel data on.
  • sidechannel_thread() - Handle side-channel requests.
  • soft_reset() - Send a soft reset to the device.
    @@ -60,13 +63,14 @@
    {
    struct libusb_device device; / Device info /
    int conf, /
    Configuration */
  •       origconf,   /\* Original configuration _/
    iface,      /_ Interface _/
    altset,     /_ Alternate setting _/
    write_endp, /_ Write endpoint */
    
  •                    read_endp, /\* Read endpoint */
    
  •       read_endp,  /\* Read endpoint _/
    protocol,   /_ Protocol: 1 = Uni-di, 2 = Bi-di. */
    
  •                    usblp_attached; /\* Is the "usblp" kernel module
    
  •                  attached? */
    
  •       usblp_attached; /\* "usblp" kernel module attached? */
    
  • unsigned int quirks; /* Quirks flags /
    struct libusb_device_handle *handle; /
    Open handle to device */
    } usb_printer_t;

@@ -99,6 +103,54 @@
int sidechannel_thread_done;
} usb_globals_t;

+/*

  • * Quirks: various printer quirks are handled by this table & its flags.
  • * This is copied from the usblp kernel module. So we can easily copy and paste
  • * new quirks from the module.
  • */
    +
    +struct quirk_printer_struct {
  • int vendorId;
  • int productId;
  • unsigned int quirks;
    +};

+#define USBLP_QUIRK_BIDIR 0x1 /* reports bidir but requires

  •                  unidirectional mode (no INs/reads) _/
    
    +#define USBLP_QUIRK_USB_INIT 0x2 /_ needs vendor USB init string /
    +#define USBLP_QUIRK_BAD_CLASS 0x4 /
    descriptor uses vendor-specific
  •                  Class or SubClass _/
    
    +#define USBLP_QUIRK_NO_REATTACH 0x8000 /_ After printing we cannot re-attach
  •                  the usblp kernel module */
    
    +static const struct quirk_printer_struct quirk_printers[] = {
  • { 0x03f0, 0x0004, USBLP_QUIRK_BIDIR }, /* HP DeskJet 895C */
  • { 0x03f0, 0x0104, USBLP_QUIRK_BIDIR }, /* HP DeskJet 880C */
  • { 0x03f0, 0x0204, USBLP_QUIRK_BIDIR }, /* HP DeskJet 815C */
  • { 0x03f0, 0x0304, USBLP_QUIRK_BIDIR }, /* HP DeskJet 810C/812C */
  • { 0x03f0, 0x0404, USBLP_QUIRK_BIDIR }, /* HP DeskJet 830C */
  • { 0x03f0, 0x0504, USBLP_QUIRK_BIDIR }, /* HP DeskJet 885C */
  • { 0x03f0, 0x0604, USBLP_QUIRK_BIDIR }, /* HP DeskJet 840C */
  • { 0x03f0, 0x0804, USBLP_QUIRK_BIDIR }, /* HP DeskJet 816C */
  • { 0x03f0, 0x1104, USBLP_QUIRK_BIDIR }, /* HP Deskjet 959C */
  • { 0x0409, 0xefbe, USBLP_QUIRK_BIDIR }, /* NEC Picty900 (HP OEM) */
  • { 0x0409, 0xbef4, USBLP_QUIRK_BIDIR }, /* NEC Picty760 (HP OEM) */
  • { 0x0409, 0xf0be, USBLP_QUIRK_BIDIR }, /* NEC Picty920 (HP OEM) */
  • { 0x0409, 0xf1be, USBLP_QUIRK_BIDIR }, /* NEC Picty800 (HP OEM) */
  • { 0x0482, 0x0010, USBLP_QUIRK_BIDIR }, /* Kyocera Mita FS 820,
  •                     by zut kernel@zut.de */
    
  • { 0x04f9, 0x000d, USBLP_QUIRK_BIDIR }, /* Brother Industries, Ltd
  •                     HL-1440 Laser Printer */
    
  • { 0x04b8, 0x0202, USBLP_QUIRK_BAD_CLASS }, /* Seiko Epson Receipt
  •                         Printer M129C */
    
  • { 0x067b, 0x2305, USBLP_QUIRK_BIDIR |
  •         USBLP_QUIRK_NO_REATTACH },
    
  • /* Prolific Technology, Inc. PL2305 Parallel Port
  •  (USB -> Parallel adapter) */
    
  • { 0, 0 }
    +};

/*

  • Globals...
    @@ -124,6 +176,8 @@
    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);
    +static int printer_class_soft_reset(usb_printer_t *printer);
    +static unsigned int quirks(int vendor, int product);
    static void *read_thread(void *reference);
    static void *sidechannel_thread(void *reference);
    static void soft_reset(void);
    @@ -163,7 +217,8 @@
    iostatus; /
    Current IO status /
    pthread_t read_thread_id, /
    Read thread /
    sidechannel_thread_id; /
    Side-channel thread */
  • int have_sidechannel = 0; /* Was the side-channel thread started? */
  • int have_sidechannel = 0, /* Was the side-channel thread started? */
  •   have_backchannel = 0;   /\* Do we have a back channel? _/
    
    struct stat sidechannel_info; /_ Side-channel file descriptor info /
    unsigned char print_buffer[8192], /
    Print data buffer /
    *print_ptr; /
    Pointer into print data buffer /
    @@ -172,6 +227,9 @@
    struct timeval *timeout, /
    Timeout pointer /
    tv; /
    Time value /
    struct timespec cond_timeout; /
    pthread condition timeout */
  • int num_opts; /* Number of options */
  • cups_option_t opts; / Options */
  • const char val; / Option value */

/*
@@ -187,6 +245,7 @@

  • Connect to the printer...
    */
  • fprintf(stderr, "DEBUG: Printing on printer with URI: %s\n", uri);
    while ((g.printer = find_device(print_cb, uri)) == NULL)
    {
    _cupsLangPrintFilter(stderr, "INFO",
    @@ -240,24 +299,47 @@
    }

/*

  • * Get the read thread going...
  • * Debug mode: If option "usb-unidir" is given, always deactivate
    • backchannel
      */
  • g.read_thread_stop = 0;
  • g.read_thread_done = 0;
  • num_opts = cupsParseOptions(argv[5], 0, &opts);
  • val = cupsGetOption("usb-unidir", num_opts, opts);
  • if (val && strcasecmp(val, "no") && strcasecmp(val, "off") &&
  •  strcasecmp(val, "false"))
    
  • {
  • g.printer->read_endp = -1;
  • fprintf(stderr, "DEBUG: Forced uni-directional communication "
  •   "via \"usb-unidir\" option.\n");
    
  • }
  • pthread_cond_init(&g.read_thread_cond, NULL);
  • pthread_mutex_init(&g.read_thread_mutex, NULL);
  • /*
  • * Get the read thread going...
  • */
  • if (pthread_create(&read_thread_id, NULL, read_thread, NULL))
  • if (g.printer->read_endp != -1)
    {
  • fprintf(stderr, "DEBUG: Fatal USB error.\n");
  • _cupsLangPrintFilter(stderr, "ERROR",
  •                     _("There was an unrecoverable USB error."));
    
  • fputs("DEBUG: Couldn't create read thread.\n", stderr);
  • close_device(g.printer);
  • return (CUPS_BACKEND_STOP);
  • have_backchannel = 1;
  • g.read_thread_stop = 0;
  • g.read_thread_done = 0;
  • pthread_cond_init(&g.read_thread_cond, NULL);
  • pthread_mutex_init(&g.read_thread_mutex, NULL);
  • if (pthread_create(&read_thread_id, NULL, read_thread, NULL))

  • {

  •  fprintf(stderr, "DEBUG: Fatal USB error.\n");
    
  •  _cupsLangPrintFilter(stderr, "ERROR",
    
  •          _("There was an unrecoverable USB error."));
    
  •  fputs("DEBUG: Couldn't create read thread.\n", stderr);
    
  •  close_device(g.printer);
    
  •  return (CUPS_BACKEND_STOP);
    
  • }
    }

  • else

  • fprintf(stderr, "DEBUG: Uni-directional device/mode, back channel "

  •   "deactivated.\n");
    

    /*

    • The main thread sends the print file...
      @@ -515,50 +597,54 @@
    • Signal the read thread to exit then wait 7 seconds for it to complete...
      */

- g.read_thread_stop = 1;

- pthread_mutex_lock(&g.read_thread_mutex);

  • if (!g.read_thread_done)
  • if (have_backchannel)
    {
  • fputs("DEBUG: Waiting for read thread to exit...\n", stderr);
  • g.read_thread_stop = 1;
  • gettimeofday(&tv, NULL);
  • cond_timeout.tv_sec = tv.tv_sec + WAIT_EOF_DELAY;
  • cond_timeout.tv_nsec = tv.tv_usec * 1000;
  • pthread_mutex_lock(&g.read_thread_mutex);
  • while (!g.read_thread_done)
  • {
  •  if (pthread_cond_timedwait(&g.read_thread_cond, &g.read_thread_mutex,
    
  •            &cond_timeout) != 0)
    
  • break;

- }

  • /*
  • * If it didn't exit abort the pending read and wait an additional second...

- */

 if (!g.read_thread_done)
 {
  •  fputs("DEBUG: Read thread still active, aborting the pending read...\n", 
    

- stderr);

  •  g.wait_eof = 0;
    
  •  fputs("DEBUG: Waiting for read thread to exit...\n", stderr);
    

    gettimeofday(&tv, NULL);

  •  cond_timeout.tv_sec  = tv.tv_sec + 1;
    
  •  cond_timeout.tv_sec  = tv.tv_sec + WAIT_EOF_DELAY;
    

    cond_timeout.tv_nsec = tv.tv_usec * 1000;

    • while (!g.read_thread_done)
      {
      if (pthread_cond_timedwait(&g.read_thread_cond, &g.read_thread_mutex,
      &cond_timeout) != 0)
      break;
      }
  •  /*
    
  •   \* If it didn't exit abort the pending read and wait an additional
    
  •   \* second...
    
  •   */
    
  •  if (!g.read_thread_done)
    
  •  {
    
  • fputs("DEBUG: Read thread still active, aborting the pending read...\n",

  •     stderr);
    
  • g.wait_eof = 0;

  • gettimeofday(&tv, NULL);
  • cond_timeout.tv_sec = tv.tv_sec + 1;
  • cond_timeout.tv_nsec = tv.tv_usec * 1000;
  • while (!g.read_thread_done)
  • {
  • if (pthread_cond_timedwait(&g.read_thread_cond, &g.read_thread_mutex,
    
  •                &cond_timeout) != 0)
    
  •   break;
    
  • }
  •  }
    
    }
  • }
  • pthread_mutex_unlock(&g.read_thread_mutex);
  • pthread_mutex_unlock(&g.read_thread_mutex);
  • }

if (print_fd)
close(print_fd);
@@ -601,24 +687,54 @@
*/

int errcode; /* Return value of libusb function */

  • int number; /* Interface number */
  • int number1, /* Interface number */
  • number2; /* Configuration number */

errcode =
libusb_get_config_descriptor (printer->device, printer->conf, &confptr);
if (errcode >= 0)
{

  •  number = confptr->interface[printer->iface].
    
  •  number1 = confptr->interface[printer->iface].
    

    altsetting[printer->altset].bInterfaceNumber;

  •  libusb_release_interface(printer->handle, number);
    
  •  if (number != 0)
    
  • libusb_release_interface(printer->handle, 0);

  •  libusb_release_interface(printer->handle, number1);
    
  •  number2 = confptr->bConfigurationValue;
    
  •  libusb_free_config_descriptor(confptr);
    
  • /*
    
  •  \* If we have changed the configuration from one valid configuration
    
  •  \* to another, restore the old one
    
  •  */
    
  •  if (printer->origconf > 0 && printer->origconf != number2)
    
  •  {
    
  • fprintf(stderr, "DEBUG: Restoring USB device configuration: %d -> %d\n",

  •   number2, printer->origconf);
    
  • if ((errcode = libusb_set_configuration(printer->handle,

  •                   printer->origconf)) < 0)
    
  • {

  • if (errcode != LIBUSB_ERROR_BUSY)
    
  • {
    
  •   errcode =
    
  •     libusb_get_device_descriptor (printer->device, &devdesc);
    
  •   if (errcode < 0)
    
  •     fprintf(stderr,
    
  •         "DEBUG: Failed to set configuration %d\n",
    
  •         printer->origconf);
    
  •   else
    
  •     fprintf(stderr,
    
  •         "DEBUG: Failed to set configuration %d for %04x:%04x\n",
    
  •         printer->origconf, devdesc.idVendor, devdesc.idProduct);
    
  • }
    
  • }

  •  }
    

    /*

    • Re-attach "usblp" kernel module if it was attached before using this
    • device
      */
      if (printer->usblp_attached == 1)
  • if (libusb_attach_kernel_driver(printer->handle, printer->iface) < 0)

  • if (libusb_attach_kernel_driver(printer->handle, number1) < 0)
    {
    errcode = libusb_get_device_descriptor (printer->device, &devdesc);
    if (errcode < 0)
    @@ -629,9 +745,11 @@
    "DEBUG: Failed to re-attach "usblp" kernel module to "
    "%04x:%04x\n", devdesc.idVendor, devdesc.idProduct);
    }

  •  libusb_free_config_descriptor(confptr);
    

    }

  • else

  •  fprintf(stderr,
    
  •     "DEBUG: Failed to get configuration descriptor %d\n",
    
  •     printer->conf);
    

    /*

    • Close the interface and return...
      @@ -709,6 +827,8 @@
      !devdesc.idProduct)
      continue;
  •  printer.quirks   = quirks(devdesc.idVendor, devdesc.idProduct);
    
    • for (conf = 0; conf < devdesc.bNumConfigurations; conf ++)
      {
      if (libusb_get_config_descriptor (device, conf, &confptr) < 0)
      @@ -733,13 +853,18 @@
      * 1284.4 (packet mode) protocol as well.
      */
  •   if (altptr->bInterfaceClass != LIBUSB_CLASS_PRINTER ||
    
  •       altptr->bInterfaceSubClass != 1 ||
    
  •   if (((altptr->bInterfaceClass != LIBUSB_CLASS_PRINTER ||
    
  •     altptr->bInterfaceSubClass != 1) && 
    
  •    ((printer.quirks & USBLP_QUIRK_BAD_CLASS) == 0)) ||
    (altptr->bInterfaceProtocol != 1 && /* Unidirectional */
     altptr->bInterfaceProtocol != 2) ||    /* Bidirectional */
    altptr->bInterfaceProtocol < protocol)
      continue;
    
  •   if (printer.quirks & USBLP_QUIRK_BAD_CLASS)
    
  •     fprintf(stderr, "DEBUG: Printer does not report class 7 and/or "
    
  •         "subclass 1 but works as a printer anyway\n");
    
    • read_endp = -1;
      write_endp = -1;

@@ -764,7 +889,10 @@
protocol = altptr->bInterfaceProtocol;
printer.altset = altset;
printer.write_endp = write_endp;

  •     printer.read_endp  = read_endp;
    
  •     if (protocol > 1)
    
  •   printer.read_endp = read_endp;
    
  •     else
    
  •   printer.read_endp = -1;
    }
    
    }

@@ -782,16 +910,41 @@
make_device_uri(&printer, device_id, device_uri,
sizeof(device_uri));

  •     fprintf(stderr, "DEBUG2: Printer found with device ID: %s "
    
  •         "Device URI: %s\n",
    
  •         device_id, device_uri);
    
    • if ((*cb)(&printer, device_uri, device_id, data))
      {
      
  •   printer.read_endp  = confptr->interface[printer.iface].
    
  •                  altsetting[printer.altset].
    
  •                  endpoint[printer.read_endp].
    
  •                  bEndpointAddress;
    
  •   fprintf(stderr, "DEBUG: Device protocol: %d\n",
    
  •       printer.protocol);
    
  •   if (printer.quirks & USBLP_QUIRK_BIDIR)
    
  •   {
    
  •     printer.read_endp = -1;
    
  •     fprintf(stderr, "DEBUG: Printer reports bi-di support "
    
  •         "but in reality works only uni-directionally\n");
    
  •   }
    
  •   if (printer.read_endp != -1)
    
  •   {
    
  •     printer.read_endp = confptr->interface[printer.iface].
    
  •                   altsetting[printer.altset].
    
  •                   endpoint[printer.read_endp].
    
  •                   bEndpointAddress;
    
  •   }
    
  •   else
    
  •     fprintf(stderr, "DEBUG: Uni-directional USB communication " 
    
  •         "only!\n");
    printer.write_endp = confptr->interface[printer.iface].
                   altsetting[printer.altset].
                   endpoint[printer.write_endp].
                   bEndpointAddress;
    
  •   if (printer.quirks & USBLP_QUIRK_NO_REATTACH)
    
  •   {
    
  •     printer.usblp_attached = 0;
    
  •     fprintf(stderr, "DEBUG: Printer does not like usblp "
    
  •         "kernel module to be re-attached after job\n");
    
  •   }
    
  •   libusb_free_config_descriptor(confptr);
    return (&printer);
           }
    

@@ -1095,8 +1248,12 @@

  • Try opening the printer...
    */
  • if (libusb_open(printer->device, &printer->handle) < 0)
  • if ((errcode = libusb_open(printer->device, &printer->handle)) < 0)
  • {
  • fprintf(stderr, "DEBUG: Failed to open device, code: %d\n",
  •   errcode);
    
    return (-1);
  • }

printer->usblp_attached = 0;

@@ -1132,14 +1289,9 @@
else
{

printer->usblp_attached = 0;

  • if (errcode != LIBUSB_ERROR_NOT_SUPPORTED)
  • {
  •  fprintf(stderr,
    
  •     "DEBUG: Failed to check whether %04x:%04x has the \"usblp\" "
    
  •     "kernel module attached\n", devdesc.idVendor, devdesc.idProduct);
    
  •  goto error;
    
  • }
  • fprintf(stderr, "DEBUG: Failed to check whether %04x:%04x has the "usblp" kernel module attached\n",
  •     devdesc.idVendor, devdesc.idProduct);
    
  • goto error;
    }

/*
@@ -1156,6 +1308,8 @@
0, 0, (unsigned char )&current, 1, 5000) < 0)
current = 0; /
Assume not configured */

  • printer->origconf = current;

if ((errcode =
libusb_get_config_descriptor (printer->device, printer->conf, &confptr))
< 0)
@@ -1168,6 +1322,8 @@

if (number1 != current)
{

  • fprintf(stderr, "DEBUG: Switching USB device configuration: %d -> %d\n",

  •   current, number1);
    

    if ((errcode = libusb_set_configuration(printer->handle, number1)) < 0)
    {
    /*
    @@ -1347,6 +1503,64 @@

    /*

  • * 'printer_class_soft_reset()' - Do the soft reset request specific to printers

  • * This soft reset is specific to the printer device class and is much less

  • * invasive than the general USB reset libusb_reset_device(). Especially it

  • * does never happen that the USB addressing and configuration changes. What

  • * is actually done is that all buffers get flushed and the bulk IN and OUT

  • * pipes get reset to their default states. This clears all stall conditions.

  • * See http://cholla.mmto.org/computers/linux/usb/usbprint11.pdf

  • /
    +
    +static int /
    O - 0 on success, < 0 on error /
    +printer_class_soft_reset(usb_printer_t *printer) /
    I - Printer */
    +{

  • struct libusb_config_descriptor *confptr = NULL;

  •                                    /\* Pointer to current configuration */
    
  • int interface,

  •  errcode;
    
  • if (libusb_get_config_descriptor (printer->device, printer->conf, &confptr)

  •  < 0)
    
  • interface = printer->iface;

  • else

  • interface = confptr->interface[printer->iface].

  •  altsetting[printer->altset].bInterfaceNumber;
    
  • libusb_free_config_descriptor(confptr);

  • if ((errcode = libusb_control_transfer(printer->handle,

  •                LIBUSB_REQUEST_TYPE_CLASS |
    
  •                LIBUSB_ENDPOINT_OUT |
    
  •                LIBUSB_RECIPIENT_OTHER,
    
  •                2, 0, interface, NULL, 0, 5000)) < 0)
    
  • errcode = libusb_control_transfer(printer->handle,

  •                 LIBUSB_REQUEST_TYPE_CLASS |
    
  •                 LIBUSB_ENDPOINT_OUT |
    
  •                 LIBUSB_RECIPIENT_INTERFACE,
    
  •                 2, 0, interface, NULL, 0, 5000);
    
  • return errcode;
    +}

+/*

  • * 'quirks()' - Get the known quirks of a given printer model
  • */
    +
    +static unsigned int quirks(int vendor, int product)
    +{
  • int i;
  • for (i = 0; quirk_printers[i].vendorId; i++)
  • {
  • if (vendor == quirk_printers[i].vendorId &&
  • product == quirk_printers[i].productId)
  •  return quirk_printers[i].quirks;
    
  • }
  • return 0;
    +}

+/*

  • 'read_thread()' - Thread to read the backchannel data on.
    */

@@ -1620,7 +1834,7 @@

  • Send the reset...
    */
  • libusb_reset_device (g.printer->handle);
  • printer_class_soft_reset (g.printer);

/*

  • Release the I/O lock...

@michaelrsweet
Copy link
Collaborator Author

"usb-libusb-c-update-1-6-x-3.patch":

--- backend/usb-libusb.c 2012-06-22 11:53:18.043277991 +0200
+++ backend/usb-libusb.c 2012-06-28 13:18:22.868430817 +0200
@@ -22,6 +22,9 @@

  • make_device_uri() - Create a device URI for a USB printer.
  • open_device() - Open a connection to the USB printer.
  • print_cb() - Find a USB printer for printing.
  • * printer_class_soft_reset()' - Do the soft reset request specific to
  • * printers
  • * quirks() - Get the known quirks of a given printer model
  • read_thread() - Thread to read the backchannel data on.
  • sidechannel_thread() - Handle side-channel requests.
  • soft_reset() - Send a soft reset to the device.
    @@ -60,13 +63,14 @@
    {
    struct libusb_device device; / Device info /
    int conf, /
    Configuration */
  •       origconf,   /\* Original configuration _/
    iface,      /_ Interface _/
    altset,     /_ Alternate setting _/
    write_endp, /_ Write endpoint */
    
  •                    read_endp, /\* Read endpoint */
    
  •       read_endp,  /\* Read endpoint _/
    protocol,   /_ Protocol: 1 = Uni-di, 2 = Bi-di. */
    
  •                    usblp_attached; /\* Is the "usblp" kernel module
    
  •                  attached? */
    
  •       usblp_attached; /\* "usblp" kernel module attached? */
    
  • unsigned int quirks; /* Quirks flags /
    struct libusb_device_handle *handle; /
    Open handle to device */
    } usb_printer_t;

@@ -99,6 +103,54 @@
int sidechannel_thread_done;
} usb_globals_t;

+/*

  • * Quirks: various printer quirks are handled by this table & its flags.
  • * This is copied from the usblp kernel module. So we can easily copy and paste
  • * new quirks from the module.
  • */
    +
    +struct quirk_printer_struct {
  • int vendorId;
  • int productId;
  • unsigned int quirks;
    +};

+#define USBLP_QUIRK_BIDIR 0x1 /* reports bidir but requires

  •                  unidirectional mode (no INs/reads) _/
    
    +#define USBLP_QUIRK_USB_INIT 0x2 /_ needs vendor USB init string /
    +#define USBLP_QUIRK_BAD_CLASS 0x4 /
    descriptor uses vendor-specific
  •                  Class or SubClass _/
    
    +#define USBLP_QUIRK_NO_REATTACH 0x8000 /_ After printing we cannot re-attach
  •                  the usblp kernel module */
    
    +static const struct quirk_printer_struct quirk_printers[] = {
  • { 0x03f0, 0x0004, USBLP_QUIRK_BIDIR }, /* HP DeskJet 895C */
  • { 0x03f0, 0x0104, USBLP_QUIRK_BIDIR }, /* HP DeskJet 880C */
  • { 0x03f0, 0x0204, USBLP_QUIRK_BIDIR }, /* HP DeskJet 815C */
  • { 0x03f0, 0x0304, USBLP_QUIRK_BIDIR }, /* HP DeskJet 810C/812C */
  • { 0x03f0, 0x0404, USBLP_QUIRK_BIDIR }, /* HP DeskJet 830C */
  • { 0x03f0, 0x0504, USBLP_QUIRK_BIDIR }, /* HP DeskJet 885C */
  • { 0x03f0, 0x0604, USBLP_QUIRK_BIDIR }, /* HP DeskJet 840C */
  • { 0x03f0, 0x0804, USBLP_QUIRK_BIDIR }, /* HP DeskJet 816C */
  • { 0x03f0, 0x1104, USBLP_QUIRK_BIDIR }, /* HP Deskjet 959C */
  • { 0x0409, 0xefbe, USBLP_QUIRK_BIDIR }, /* NEC Picty900 (HP OEM) */
  • { 0x0409, 0xbef4, USBLP_QUIRK_BIDIR }, /* NEC Picty760 (HP OEM) */
  • { 0x0409, 0xf0be, USBLP_QUIRK_BIDIR }, /* NEC Picty920 (HP OEM) */
  • { 0x0409, 0xf1be, USBLP_QUIRK_BIDIR }, /* NEC Picty800 (HP OEM) */
  • { 0x0482, 0x0010, USBLP_QUIRK_BIDIR }, /* Kyocera Mita FS 820,
  •                     by zut kernel@zut.de */
    
  • { 0x04f9, 0x000d, USBLP_QUIRK_BIDIR }, /* Brother Industries, Ltd
  •                     HL-1440 Laser Printer */
    
  • { 0x04b8, 0x0202, USBLP_QUIRK_BAD_CLASS }, /* Seiko Epson Receipt
  •                         Printer M129C */
    
  • { 0x067b, 0x2305, USBLP_QUIRK_BIDIR |
  •         USBLP_QUIRK_NO_REATTACH },
    
  • /* Prolific Technology, Inc. PL2305 Parallel Port
  •  (USB -> Parallel adapter) */
    
  • { 0, 0 }
    +};

/*

  • Globals...
    @@ -124,6 +176,8 @@
    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);
    +static int printer_class_soft_reset(usb_printer_t *printer);
    +static unsigned int quirks(int vendor, int product);
    static void *read_thread(void *reference);
    static void *sidechannel_thread(void *reference);
    static void soft_reset(void);
    @@ -163,7 +217,8 @@
    iostatus; /
    Current IO status /
    pthread_t read_thread_id, /
    Read thread /
    sidechannel_thread_id; /
    Side-channel thread */
  • int have_sidechannel = 0; /* Was the side-channel thread started? */
  • int have_sidechannel = 0, /* Was the side-channel thread started? */
  •   have_backchannel = 0;   /\* Do we have a back channel? _/
    
    struct stat sidechannel_info; /_ Side-channel file descriptor info /
    unsigned char print_buffer[8192], /
    Print data buffer /
    *print_ptr; /
    Pointer into print data buffer /
    @@ -172,6 +227,9 @@
    struct timeval *timeout, /
    Timeout pointer /
    tv; /
    Time value /
    struct timespec cond_timeout; /
    pthread condition timeout */
  • int num_opts; /* Number of options */
  • cups_option_t opts; / Options */
  • const char val; / Option value */

/*
@@ -187,6 +245,7 @@

  • Connect to the printer...
    */
  • fprintf(stderr, "DEBUG: Printing on printer with URI: %s\n", uri);
    while ((g.printer = find_device(print_cb, uri)) == NULL)
    {
    _cupsLangPrintFilter(stderr, "INFO",
    @@ -240,24 +299,61 @@
    }

/*

  • * Get the read thread going...
  • * Debug mode: If option "usb-unidir" is given, always deactivate
  • * backchannel
  • */
  • num_opts = cupsParseOptions(argv[5], 0, &opts);
  • val = cupsGetOption("usb-unidir", num_opts, opts);
  • if (val && strcasecmp(val, "no") && strcasecmp(val, "off") &&
  •  strcasecmp(val, "false"))
    
  • {
  • g.printer->read_endp = -1;
  • fprintf(stderr, "DEBUG: Forced uni-directional communication "
  •   "via \"usb-unidir\" option.\n");
    
  • }
  • /*
  • * Debug mode: If option "usb-no-reattach" is given, do not re-attach
    • the usblp kernel module after the job has completed.
      */
  • g.read_thread_stop = 0;
  • g.read_thread_done = 0;
  • val = cupsGetOption("usb-no-reattach", num_opts, opts);
  • if (val && strcasecmp(val, "no") && strcasecmp(val, "off") &&
  •  strcasecmp(val, "false"))
    
  • {
  • g.printer->usblp_attached = 0;
  • fprintf(stderr, "DEBUG: Forced not re-attaching the usblp kernel module "
  •   "after the job via \"usb-no-reattach\" option.\n");
    
  • }
  • pthread_cond_init(&g.read_thread_cond, NULL);
  • pthread_mutex_init(&g.read_thread_mutex, NULL);
  • /*
  • * Get the read thread going...
  • */
  • if (pthread_create(&read_thread_id, NULL, read_thread, NULL))
  • if (g.printer->read_endp != -1)
    {
  • fprintf(stderr, "DEBUG: Fatal USB error.\n");
  • _cupsLangPrintFilter(stderr, "ERROR",
  •                     _("There was an unrecoverable USB error."));
    
  • fputs("DEBUG: Couldn't create read thread.\n", stderr);
  • close_device(g.printer);
  • return (CUPS_BACKEND_STOP);
  • have_backchannel = 1;
  • g.read_thread_stop = 0;
  • g.read_thread_done = 0;
  • pthread_cond_init(&g.read_thread_cond, NULL);
  • pthread_mutex_init(&g.read_thread_mutex, NULL);
  • if (pthread_create(&read_thread_id, NULL, read_thread, NULL))

  • {

  •  fprintf(stderr, "DEBUG: Fatal USB error.\n");
    
  •  _cupsLangPrintFilter(stderr, "ERROR",
    
  •          _("There was an unrecoverable USB error."));
    
  •  fputs("DEBUG: Couldn't create read thread.\n", stderr);
    
  •  close_device(g.printer);
    
  •  return (CUPS_BACKEND_STOP);
    
  • }
    }

  • else

  • fprintf(stderr, "DEBUG: Uni-directional device/mode, back channel "

  •   "deactivated.\n");
    

    /*

    • The main thread sends the print file...
      @@ -515,38 +611,18 @@
    • Signal the read thread to exit then wait 7 seconds for it to complete...
      */

- g.read_thread_stop = 1;

- pthread_mutex_lock(&g.read_thread_mutex);

  • if (!g.read_thread_done)
  • if (have_backchannel)
    {

- fputs("DEBUG: Waiting for read thread to exit...\n", stderr);

  • gettimeofday(&tv, NULL);
  • cond_timeout.tv_sec = tv.tv_sec + WAIT_EOF_DELAY;

- cond_timeout.tv_nsec = tv.tv_usec * 1000;

  • while (!g.read_thread_done)
  • {
  •  if (pthread_cond_timedwait(&g.read_thread_cond, &g.read_thread_mutex,
    
  •            &cond_timeout) != 0)
    
  • break;
  • }
  • g.read_thread_stop = 1;
  • /*
  • * If it didn't exit abort the pending read and wait an additional second...
  • */
  • pthread_mutex_lock(&g.read_thread_mutex);

if (!g.read_thread_done)
{

  •  fputs("DEBUG: Read thread still active, aborting the pending read...\n",
    

- stderr);

  •  g.wait_eof = 0;
    
  •  fputs("DEBUG: Waiting for read thread to exit...\n", stderr);
    

    gettimeofday(&tv, NULL);

  •  cond_timeout.tv_sec  = tv.tv_sec + 1;
    
  •  cond_timeout.tv_sec  = tv.tv_sec + WAIT_EOF_DELAY;
    

    cond_timeout.tv_nsec = tv.tv_usec * 1000;

    while (!g.read_thread_done)
    @@ -555,10 +631,34 @@
    &cond_timeout) != 0)
    break;
    }
    +

  •  /*
    
  •   \* If it didn't exit abort the pending read and wait an additional
    
  •   \* second...
    
  •   */
    
  •  if (!g.read_thread_done)
    
  •  {
    
  • fputs("DEBUG: Read thread still active, aborting the pending read...\n",

  •     stderr);
    
  • g.wait_eof = 0;

  • gettimeofday(&tv, NULL);
  • cond_timeout.tv_sec = tv.tv_sec + 1;
  • cond_timeout.tv_nsec = tv.tv_usec * 1000;
  • while (!g.read_thread_done)
  • {
  • if (pthread_cond_timedwait(&g.read_thread_cond, &g.read_thread_mutex,
    
  •                &cond_timeout) != 0)
    
  •   break;
    
  • }
  •  }
    
    }
  • }
  • pthread_mutex_unlock(&g.read_thread_mutex);
  • pthread_mutex_unlock(&g.read_thread_mutex);
  • }

if (print_fd)
close(print_fd);
@@ -601,24 +701,54 @@
*/

int errcode; /* Return value of libusb function */

  • int number; /* Interface number */
  • int number1, /* Interface number */
  • number2; /* Configuration number */

errcode =

  •  libusb_get_config_descriptor (printer->device, printer->conf, &confptr);
    
  •  libusb_get_config_descriptor(printer->device, printer->conf, &confptr);
    

    if (errcode >= 0)
    {

  •  number = confptr->interface[printer->iface].
    
  •  number1 = confptr->interface[printer->iface].
    

    altsetting[printer->altset].bInterfaceNumber;

  •  libusb_release_interface(printer->handle, number);
    
  •  if (number != 0)
    
  • libusb_release_interface(printer->handle, 0);

  •  libusb_release_interface(printer->handle, number1);
    
  •  number2 = confptr->bConfigurationValue;
    
  •  libusb_free_config_descriptor(confptr);
    
  • /*
    
  •  \* If we have changed the configuration from one valid configuration
    
  •  \* to another, restore the old one
    
  •  */
    
  •  if (printer->origconf > 0 && printer->origconf != number2)
    
  •  {
    
  • fprintf(stderr, "DEBUG: Restoring USB device configuration: %d -> %d\n",

  •   number2, printer->origconf);
    
  • if ((errcode = libusb_set_configuration(printer->handle,

  •                   printer->origconf)) < 0)
    
  • {

  • if (errcode != LIBUSB_ERROR_BUSY)
    
  • {
    
  •   errcode =
    
  •     libusb_get_device_descriptor (printer->device, &devdesc);
    
  •   if (errcode < 0)
    
  •     fprintf(stderr,
    
  •         "DEBUG: Failed to set configuration %d\n",
    
  •         printer->origconf);
    
  •   else
    
  •     fprintf(stderr,
    
  •         "DEBUG: Failed to set configuration %d for %04x:%04x\n",
    
  •         printer->origconf, devdesc.idVendor, devdesc.idProduct);
    
  • }
    
  • }

  •  }
    

    /*

    • Re-attach "usblp" kernel module if it was attached before using this
    • device
      */
      if (printer->usblp_attached == 1)
  • if (libusb_attach_kernel_driver(printer->handle, printer->iface) < 0)

  • if (libusb_attach_kernel_driver(printer->handle, number1) < 0)
    {
    errcode = libusb_get_device_descriptor (printer->device, &devdesc);
    if (errcode < 0)
    @@ -629,9 +759,11 @@
    "DEBUG: Failed to re-attach "usblp" kernel module to "
    "%04x:%04x\n", devdesc.idVendor, devdesc.idProduct);
    }

  •  libusb_free_config_descriptor(confptr);
    

    }

  • else

  •  fprintf(stderr,
    
  •     "DEBUG: Failed to get configuration descriptor %d\n",
    
  •     printer->conf);
    

    /*

    • Close the interface and return...
      @@ -702,13 +834,15 @@
      • a printer...
        */
  •  if (libusb_get_device_descriptor (device, &devdesc) < 0)
    
  •  if (libusb_get_device_descriptor(device, &devdesc) < 0)
    

    continue;

    if (!devdesc.bNumConfigurations || !devdesc.idVendor ||
    !devdesc.idProduct)
    continue;

  •  printer.quirks   = quirks(devdesc.idVendor, devdesc.idProduct);
    
    • for (conf = 0; conf < devdesc.bNumConfigurations; conf ++)
      {
      if (libusb_get_config_descriptor(device, conf, &confptr) < 0)
      @@ -733,13 +867,18 @@
      * 1284.4 (packet mode) protocol as well.
      */
  •   if (altptr->bInterfaceClass != LIBUSB_CLASS_PRINTER ||
    
  •       altptr->bInterfaceSubClass != 1 ||
    
  •   if (((altptr->bInterfaceClass != LIBUSB_CLASS_PRINTER ||
    
  •     altptr->bInterfaceSubClass != 1) && 
    
  •    ((printer.quirks & USBLP_QUIRK_BAD_CLASS) == 0)) ||
    (altptr->bInterfaceProtocol != 1 && /* Unidirectional */
     altptr->bInterfaceProtocol != 2) ||    /* Bidirectional */
    altptr->bInterfaceProtocol < protocol)
      continue;
    
  •   if (printer.quirks & USBLP_QUIRK_BAD_CLASS)
    
  •     fprintf(stderr, "DEBUG: Printer does not report class 7 and/or "
    
  •         "subclass 1 but works as a printer anyway\n");
    
    • read_endp = -1;
      write_endp = -1;

@@ -764,7 +903,10 @@
protocol = altptr->bInterfaceProtocol;
printer.altset = altset;
printer.write_endp = write_endp;

  •     printer.read_endp  = read_endp;
    
  •     if (protocol > 1)
    
  •   printer.read_endp = read_endp;
    
  •     else
    
  •   printer.read_endp = -1;
    }
    
    }

@@ -782,16 +924,41 @@
make_device_uri(&printer, device_id, device_uri,
sizeof(device_uri));

  •     fprintf(stderr, "DEBUG2: Printer found with device ID: %s "
    
  •         "Device URI: %s\n",
    
  •         device_id, device_uri);
    
    • if ((*cb)(&printer, device_uri, device_id, data))
      {
      
  •   printer.read_endp  = confptr->interface[printer.iface].
    
  •                  altsetting[printer.altset].
    
  •                  endpoint[printer.read_endp].
    
  •                  bEndpointAddress;
    
  •   fprintf(stderr, "DEBUG: Device protocol: %d\n",
    
  •       printer.protocol);
    
  •   if (printer.quirks & USBLP_QUIRK_BIDIR)
    
  •   {
    
  •     printer.read_endp = -1;
    
  •     fprintf(stderr, "DEBUG: Printer reports bi-di support "
    
  •         "but in reality works only uni-directionally\n");
    
  •   }
    
  •   if (printer.read_endp != -1)
    
  •   {
    
  •     printer.read_endp = confptr->interface[printer.iface].
    
  •                   altsetting[printer.altset].
    
  •                   endpoint[printer.read_endp].
    
  •                   bEndpointAddress;
    
  •   }
    
  •   else
    
  •     fprintf(stderr, "DEBUG: Uni-directional USB communication " 
    
  •         "only!\n");
    printer.write_endp = confptr->interface[printer.iface].
                   altsetting[printer.altset].
                   endpoint[printer.write_endp].
                   bEndpointAddress;
    
  •   if (printer.quirks & USBLP_QUIRK_NO_REATTACH)
    
  •   {
    
  •     printer.usblp_attached = 0;
    
  •     fprintf(stderr, "DEBUG: Printer does not like usblp "
    
  •         "kernel module to be re-attached after job\n");
    
  •   }
    
  •   libusb_free_config_descriptor(confptr);
    return (&printer);
           }
    

@@ -1095,15 +1262,19 @@

  • Try opening the printer...
    */
  • if (libusb_open(printer->device, &printer->handle) < 0)
  • if ((errcode = libusb_open(printer->device, &printer->handle)) < 0)
  • {
  • fprintf(stderr, "DEBUG: Failed to open device, code: %d\n",
  •   errcode);
    
    return (-1);
  • }

printer->usblp_attached = 0;

if (verbose)
fputs("STATE: +connecting-to-device\n", stderr);

  • if ((errcode = libusb_get_device_descriptor (printer->device, &devdesc)) < 0)
  • if ((errcode = libusb_get_device_descriptor(printer->device, &devdesc)) < 0)
    {
    fprintf(stderr, "DEBUG: Failed to get device descriptor, code: %d\n",
    errcode);
    @@ -1132,14 +1303,9 @@
    else
    {
    printer->usblp_attached = 0;
  • if (errcode != LIBUSB_ERROR_NOT_SUPPORTED)
  • {
  •  fprintf(stderr,
    
  •     "DEBUG: Failed to check whether %04x:%04x has the \"usblp\" "
    
  •     "kernel module attached\n", devdesc.idVendor, devdesc.idProduct);
    
  •  goto error;
    
  • }
  • fprintf(stderr, "DEBUG: Failed to check whether %04x:%04x has the "usblp" kernel module attached\n",
  •     devdesc.idVendor, devdesc.idProduct);
    
  • goto error;
    }

/*
@@ -1156,7 +1322,9 @@
0, 0, (unsigned char )&current, 1, 5000) < 0)
current = 0; /
Assume not configured */

  • if ((errcode =
  • printer->origconf = current;
  • if ((errcode =
    libusb_get_config_descriptor (printer->device, printer->conf, &confptr))
    < 0)
    {
    @@ -1168,6 +1336,8 @@

if (number1 != current)
{

  • fprintf(stderr, "DEBUG: Switching USB device configuration: %d -> %d\n",

  •   current, number1);
    

    if ((errcode = libusb_set_configuration(printer->handle, number1)) < 0)
    {
    /*
    @@ -1347,6 +1517,64 @@

    /*

  • * 'printer_class_soft_reset()' - Do the soft reset request specific to printers

  • * This soft reset is specific to the printer device class and is much less

  • * invasive than the general USB reset libusb_reset_device(). Especially it

  • * does never happen that the USB addressing and configuration changes. What

  • * is actually done is that all buffers get flushed and the bulk IN and OUT

  • * pipes get reset to their default states. This clears all stall conditions.

  • * See http://cholla.mmto.org/computers/linux/usb/usbprint11.pdf

  • /
    +
    +static int /
    O - 0 on success, < 0 on error /
    +printer_class_soft_reset(usb_printer_t *printer) /
    I - Printer */
    +{

  • struct libusb_config_descriptor *confptr = NULL;

  •                                    /\* Pointer to current configuration */
    
  • int interface,

  •  errcode;
    
  • if (libusb_get_config_descriptor(printer->device, printer->conf, &confptr)

  •  < 0)
    
  • interface = printer->iface;

  • else

  • interface = confptr->interface[printer->iface].

  •  altsetting[printer->altset].bInterfaceNumber;
    
  • libusb_free_config_descriptor(confptr);

  • if ((errcode = libusb_control_transfer(printer->handle,

  •                LIBUSB_REQUEST_TYPE_CLASS |
    
  •                LIBUSB_ENDPOINT_OUT |
    
  •                LIBUSB_RECIPIENT_OTHER,
    
  •                2, 0, interface, NULL, 0, 5000)) < 0)
    
  • errcode = libusb_control_transfer(printer->handle,

  •                 LIBUSB_REQUEST_TYPE_CLASS |
    
  •                 LIBUSB_ENDPOINT_OUT |
    
  •                 LIBUSB_RECIPIENT_INTERFACE,
    
  •                 2, 0, interface, NULL, 0, 5000);
    
  • return errcode;
    +}

+/*

  • * 'quirks()' - Get the known quirks of a given printer model
  • */
    +
    +static unsigned int quirks(int vendor, int product)
    +{
  • int i;
  • for (i = 0; quirk_printers[i].vendorId; i++)
  • {
  • if (vendor == quirk_printers[i].vendorId &&
  • product == quirk_printers[i].productId)
  •  return quirk_printers[i].quirks;
    
  • }
  • return 0;
    +}

+/*

  • 'read_thread()' - Thread to read the backchannel data on.
    */

@@ -1620,7 +1848,7 @@

  • Send the reset...
    */
  • libusb_reset_device (g.printer->handle);
  • printer_class_soft_reset(g.printer);

/*

  • Release the I/O lock...

@michaelrsweet
Copy link
Collaborator Author

"usb-libusb-c-update-1-5-x-3.patch":

--- backend/usb-libusb.c 2012-06-22 11:53:36.103353715 +0200
+++ backend/usb-libusb.c 2012-06-28 13:18:22.868430817 +0200
@@ -22,6 +22,9 @@

  • make_device_uri() - Create a device URI for a USB printer.
  • open_device() - Open a connection to the USB printer.
  • print_cb() - Find a USB printer for printing.
  • * printer_class_soft_reset()' - Do the soft reset request specific to
  • * printers
  • * quirks() - Get the known quirks of a given printer model
  • read_thread() - Thread to read the backchannel data on.
  • sidechannel_thread() - Handle side-channel requests.
  • soft_reset() - Send a soft reset to the device.
    @@ -60,13 +63,14 @@
    {
    struct libusb_device device; / Device info /
    int conf, /
    Configuration */
  •       origconf,   /\* Original configuration _/
    iface,      /_ Interface _/
    altset,     /_ Alternate setting _/
    write_endp, /_ Write endpoint */
    
  •                    read_endp, /\* Read endpoint */
    
  •       read_endp,  /\* Read endpoint _/
    protocol,   /_ Protocol: 1 = Uni-di, 2 = Bi-di. */
    
  •                    usblp_attached; /\* Is the "usblp" kernel module
    
  •                  attached? */
    
  •       usblp_attached; /\* "usblp" kernel module attached? */
    
  • unsigned int quirks; /* Quirks flags /
    struct libusb_device_handle *handle; /
    Open handle to device */
    } usb_printer_t;

@@ -99,6 +103,54 @@
int sidechannel_thread_done;
} usb_globals_t;

+/*

  • * Quirks: various printer quirks are handled by this table & its flags.
  • * This is copied from the usblp kernel module. So we can easily copy and paste
  • * new quirks from the module.
  • */
    +
    +struct quirk_printer_struct {
  • int vendorId;
  • int productId;
  • unsigned int quirks;
    +};

+#define USBLP_QUIRK_BIDIR 0x1 /* reports bidir but requires

  •                  unidirectional mode (no INs/reads) _/
    
    +#define USBLP_QUIRK_USB_INIT 0x2 /_ needs vendor USB init string /
    +#define USBLP_QUIRK_BAD_CLASS 0x4 /
    descriptor uses vendor-specific
  •                  Class or SubClass _/
    
    +#define USBLP_QUIRK_NO_REATTACH 0x8000 /_ After printing we cannot re-attach
  •                  the usblp kernel module */
    
    +static const struct quirk_printer_struct quirk_printers[] = {
  • { 0x03f0, 0x0004, USBLP_QUIRK_BIDIR }, /* HP DeskJet 895C */
  • { 0x03f0, 0x0104, USBLP_QUIRK_BIDIR }, /* HP DeskJet 880C */
  • { 0x03f0, 0x0204, USBLP_QUIRK_BIDIR }, /* HP DeskJet 815C */
  • { 0x03f0, 0x0304, USBLP_QUIRK_BIDIR }, /* HP DeskJet 810C/812C */
  • { 0x03f0, 0x0404, USBLP_QUIRK_BIDIR }, /* HP DeskJet 830C */
  • { 0x03f0, 0x0504, USBLP_QUIRK_BIDIR }, /* HP DeskJet 885C */
  • { 0x03f0, 0x0604, USBLP_QUIRK_BIDIR }, /* HP DeskJet 840C */
  • { 0x03f0, 0x0804, USBLP_QUIRK_BIDIR }, /* HP DeskJet 816C */
  • { 0x03f0, 0x1104, USBLP_QUIRK_BIDIR }, /* HP Deskjet 959C */
  • { 0x0409, 0xefbe, USBLP_QUIRK_BIDIR }, /* NEC Picty900 (HP OEM) */
  • { 0x0409, 0xbef4, USBLP_QUIRK_BIDIR }, /* NEC Picty760 (HP OEM) */
  • { 0x0409, 0xf0be, USBLP_QUIRK_BIDIR }, /* NEC Picty920 (HP OEM) */
  • { 0x0409, 0xf1be, USBLP_QUIRK_BIDIR }, /* NEC Picty800 (HP OEM) */
  • { 0x0482, 0x0010, USBLP_QUIRK_BIDIR }, /* Kyocera Mita FS 820,
  •                     by zut kernel@zut.de */
    
  • { 0x04f9, 0x000d, USBLP_QUIRK_BIDIR }, /* Brother Industries, Ltd
  •                     HL-1440 Laser Printer */
    
  • { 0x04b8, 0x0202, USBLP_QUIRK_BAD_CLASS }, /* Seiko Epson Receipt
  •                         Printer M129C */
    
  • { 0x067b, 0x2305, USBLP_QUIRK_BIDIR |
  •         USBLP_QUIRK_NO_REATTACH },
    
  • /* Prolific Technology, Inc. PL2305 Parallel Port
  •  (USB -> Parallel adapter) */
    
  • { 0, 0 }
    +};

/*

  • Globals...
    @@ -124,6 +176,8 @@
    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);
    +static int printer_class_soft_reset(usb_printer_t *printer);
    +static unsigned int quirks(int vendor, int product);
    static void *read_thread(void *reference);
    static void *sidechannel_thread(void *reference);
    static void soft_reset(void);
    @@ -163,7 +217,8 @@
    iostatus; /
    Current IO status /
    pthread_t read_thread_id, /
    Read thread /
    sidechannel_thread_id; /
    Side-channel thread */
  • int have_sidechannel = 0; /* Was the side-channel thread started? */
  • int have_sidechannel = 0, /* Was the side-channel thread started? */
  •   have_backchannel = 0;   /\* Do we have a back channel? _/
    
    struct stat sidechannel_info; /_ Side-channel file descriptor info /
    unsigned char print_buffer[8192], /
    Print data buffer /
    *print_ptr; /
    Pointer into print data buffer /
    @@ -172,6 +227,9 @@
    struct timeval *timeout, /
    Timeout pointer /
    tv; /
    Time value /
    struct timespec cond_timeout; /
    pthread condition timeout */
  • int num_opts; /* Number of options */
  • cups_option_t opts; / Options */
  • const char val; / Option value */

/*
@@ -187,6 +245,7 @@

  • Connect to the printer...
    */
  • fprintf(stderr, "DEBUG: Printing on printer with URI: %s\n", uri);
    while ((g.printer = find_device(print_cb, uri)) == NULL)
    {
    _cupsLangPrintFilter(stderr, "INFO",
    @@ -240,26 +299,63 @@
    }

/*

  • * Get the read thread going...

  • * Debug mode: If option "usb-unidir" is given, always deactivate

    • backchannel
      */
  • g.read_thread_stop = 0;

  • g.read_thread_done = 0;

  • num_opts = cupsParseOptions(argv[5], 0, &opts);

  • val = cupsGetOption("usb-unidir", num_opts, opts);

  • if (val && strcasecmp(val, "no") && strcasecmp(val, "off") &&

  •  strcasecmp(val, "false"))
    
  • {

  • g.printer->read_endp = -1;

  • fprintf(stderr, "DEBUG: Forced uni-directional communication "

  •   "via \"usb-unidir\" option.\n");
    
  • }

  • pthread_cond_init(&g.read_thread_cond, NULL);

  • pthread_mutex_init(&g.read_thread_mutex, NULL);

  • /*

  • * Debug mode: If option "usb-no-reattach" is given, do not re-attach

  • * the usblp kernel module after the job has completed.

  • */

  • if (pthread_create(&read_thread_id, NULL, read_thread, NULL))

  • val = cupsGetOption("usb-no-reattach", num_opts, opts);

  • if (val && strcasecmp(val, "no") && strcasecmp(val, "off") &&

  •  strcasecmp(val, "false"))
    

    {

  • fprintf(stderr, "DEBUG: Fatal USB error.\n");

  • _cupsLangPrintFilter(stderr, "ERROR",

  •                     _("There was an unrecoverable USB error."));
    
  • fputs("DEBUG: Couldn't create read thread.\n", stderr);

  • close_device(g.printer);

  • return (CUPS_BACKEND_STOP);

  • g.printer->usblp_attached = 0;

  • fprintf(stderr, "DEBUG: Forced not re-attaching the usblp kernel module "

  •   "after the job via \"usb-no-reattach\" option.\n");
    

    }

    /*

  • * Get the read thread going...

  • */

  • if (g.printer->read_endp != -1)
  • {
  • have_backchannel = 1;
  • g.read_thread_stop = 0;
  • g.read_thread_done = 0;
  • pthread_cond_init(&g.read_thread_cond, NULL);
  • pthread_mutex_init(&g.read_thread_mutex, NULL);
  • if (pthread_create(&read_thread_id, NULL, read_thread, NULL))
  • {
  •  fprintf(stderr, "DEBUG: Fatal USB error.\n");
    
  •  _cupsLangPrintFilter(stderr, "ERROR",
    
  •          _("There was an unrecoverable USB error."));
    
  •  fputs("DEBUG: Couldn't create read thread.\n", stderr);
    
  •  close_device(g.printer);
    
  •  return (CUPS_BACKEND_STOP);
    
  • }
  • }
  • else
  • fprintf(stderr, "DEBUG: Uni-directional device/mode, back channel "
  •   "deactivated.\n");
    
  • /*
    • The main thread sends the print file...
      */

@@ -515,50 +611,54 @@

  • Signal the read thread to exit then wait 7 seconds for it to complete...
    */

- g.read_thread_stop = 1;

- pthread_mutex_lock(&g.read_thread_mutex);

  • if (!g.read_thread_done)
  • if (have_backchannel)
    {
  • fputs("DEBUG: Waiting for read thread to exit...\n", stderr);
  • g.read_thread_stop = 1;
  • gettimeofday(&tv, NULL);
  • cond_timeout.tv_sec = tv.tv_sec + WAIT_EOF_DELAY;

- cond_timeout.tv_nsec = tv.tv_usec * 1000;

  • while (!g.read_thread_done)
  • {
  •  if (pthread_cond_timedwait(&g.read_thread_cond, &g.read_thread_mutex,
    
  •            &cond_timeout) != 0)
    
  • break;
  • }
  • pthread_mutex_lock(&g.read_thread_mutex);
  • /*
  • * If it didn't exit abort the pending read and wait an additional second...

- */

 if (!g.read_thread_done)
 {
  •  fputs("DEBUG: Read thread still active, aborting the pending read...\n", 
    

- stderr);

  •  g.wait_eof = 0;
    
  •  fputs("DEBUG: Waiting for read thread to exit...\n", stderr);
    

    gettimeofday(&tv, NULL);

  •  cond_timeout.tv_sec  = tv.tv_sec + 1;
    
  •  cond_timeout.tv_sec  = tv.tv_sec + WAIT_EOF_DELAY;
    

    cond_timeout.tv_nsec = tv.tv_usec * 1000;

    • while (!g.read_thread_done)
      {
      if (pthread_cond_timedwait(&g.read_thread_cond, &g.read_thread_mutex,
      &cond_timeout) != 0)
      break;
      }
  •  /*
    
  •   \* If it didn't exit abort the pending read and wait an additional
    
  •   \* second...
    
  •   */
    
  •  if (!g.read_thread_done)
    
  •  {
    
  • fputs("DEBUG: Read thread still active, aborting the pending read...\n",

  •     stderr);
    
  • g.wait_eof = 0;

  • gettimeofday(&tv, NULL);
  • cond_timeout.tv_sec = tv.tv_sec + 1;
  • cond_timeout.tv_nsec = tv.tv_usec * 1000;
  • while (!g.read_thread_done)
  • {
  • if (pthread_cond_timedwait(&g.read_thread_cond, &g.read_thread_mutex,
    
  •                &cond_timeout) != 0)
    
  •   break;
    
  • }
  •  }
    
    }
  • }
  • pthread_mutex_unlock(&g.read_thread_mutex);
  • pthread_mutex_unlock(&g.read_thread_mutex);
  • }

if (print_fd)
close(print_fd);
@@ -601,24 +701,54 @@
*/

int errcode; /* Return value of libusb function */

  • int number; /* Interface number */

  • int number1, /* Interface number */

  • number2; /* Configuration number */

  • errcode =

  •  libusb_get_config_descriptor (printer->device, printer->conf, &confptr);
    
  • errcode =

  •  libusb_get_config_descriptor(printer->device, printer->conf, &confptr);
    

    if (errcode >= 0)
    {

  •  number = confptr->interface[printer->iface].
    
  •  number1 = confptr->interface[printer->iface].
    

    altsetting[printer->altset].bInterfaceNumber;

  •  libusb_release_interface(printer->handle, number);
    
  •  if (number != 0)
    
  • libusb_release_interface(printer->handle, 0);

  •  libusb_release_interface(printer->handle, number1);
    
  •  number2 = confptr->bConfigurationValue;
    
  •  libusb_free_config_descriptor(confptr);
    
  • /*
    
  •  \* If we have changed the configuration from one valid configuration
    
  •  \* to another, restore the old one
    
  •  */
    
  •  if (printer->origconf > 0 && printer->origconf != number2)
    
  •  {
    
  • fprintf(stderr, "DEBUG: Restoring USB device configuration: %d -> %d\n",

  •   number2, printer->origconf);
    
  • if ((errcode = libusb_set_configuration(printer->handle,

  •                   printer->origconf)) < 0)
    
  • {

  • if (errcode != LIBUSB_ERROR_BUSY)
    
  • {
    
  •   errcode =
    
  •     libusb_get_device_descriptor (printer->device, &devdesc);
    
  •   if (errcode < 0)
    
  •     fprintf(stderr,
    
  •         "DEBUG: Failed to set configuration %d\n",
    
  •         printer->origconf);
    
  •   else
    
  •     fprintf(stderr,
    
  •         "DEBUG: Failed to set configuration %d for %04x:%04x\n",
    
  •         printer->origconf, devdesc.idVendor, devdesc.idProduct);
    
  • }
    
  • }

  •  }
    

    /*

    • Re-attach "usblp" kernel module if it was attached before using this
    • device
      */
      if (printer->usblp_attached == 1)
  • if (libusb_attach_kernel_driver(printer->handle, printer->iface) < 0)

  • if (libusb_attach_kernel_driver(printer->handle, number1) < 0)
    {
    errcode = libusb_get_device_descriptor (printer->device, &devdesc);
    if (errcode < 0)
    @@ -629,9 +759,11 @@
    "DEBUG: Failed to re-attach "usblp" kernel module to "
    "%04x:%04x\n", devdesc.idVendor, devdesc.idProduct);
    }

  •  libusb_free_config_descriptor(confptr);
    

    }

  • else

  •  fprintf(stderr,
    
  •     "DEBUG: Failed to get configuration descriptor %d\n",
    
  •     printer->conf);
    

    /*

    • Close the interface and return...
      @@ -702,16 +834,18 @@
      • a printer...
        */
  •  if (libusb_get_device_descriptor (device, &devdesc) < 0)
    
  •  if (libusb_get_device_descriptor(device, &devdesc) < 0)
    

    continue;

    if (!devdesc.bNumConfigurations || !devdesc.idVendor ||
    !devdesc.idProduct)
    continue;

  •  printer.quirks   = quirks(devdesc.idVendor, devdesc.idProduct);
    
    • for (conf = 0; conf < devdesc.bNumConfigurations; conf ++)
      {
  • if (libusb_get_config_descriptor (device, conf, &confptr) < 0)

  • if (libusb_get_config_descriptor(device, conf, &confptr) < 0)
    continue;
    for (iface = 0, ifaceptr = confptr->interface;
    iface < confptr->bNumInterfaces;
    @@ -733,13 +867,18 @@
    * 1284.4 (packet mode) protocol as well.
    */

  •   if (altptr->bInterfaceClass != LIBUSB_CLASS_PRINTER ||
    
  •       altptr->bInterfaceSubClass != 1 ||
    
  •   if (((altptr->bInterfaceClass != LIBUSB_CLASS_PRINTER ||
    
  •     altptr->bInterfaceSubClass != 1) && 
    
  •    ((printer.quirks & USBLP_QUIRK_BAD_CLASS) == 0)) ||
    (altptr->bInterfaceProtocol != 1 && /* Unidirectional */
     altptr->bInterfaceProtocol != 2) ||    /* Bidirectional */
    altptr->bInterfaceProtocol < protocol)
      continue;
    
  •   if (printer.quirks & USBLP_QUIRK_BAD_CLASS)
    
  •     fprintf(stderr, "DEBUG: Printer does not report class 7 and/or "
    
  •         "subclass 1 but works as a printer anyway\n");
    
    • read_endp = -1;
      write_endp = -1;

@@ -764,7 +903,10 @@
protocol = altptr->bInterfaceProtocol;
printer.altset = altset;
printer.write_endp = write_endp;

  •     printer.read_endp  = read_endp;
    
  •     if (protocol > 1)
    
  •   printer.read_endp = read_endp;
    
  •     else
    
  •   printer.read_endp = -1;
    }
    
    }

@@ -782,16 +924,41 @@
make_device_uri(&printer, device_id, device_uri,
sizeof(device_uri));

  •     fprintf(stderr, "DEBUG2: Printer found with device ID: %s "
    
  •         "Device URI: %s\n",
    
  •         device_id, device_uri);
    
    • if ((*cb)(&printer, device_uri, device_id, data))
      {
      
  •   printer.read_endp  = confptr->interface[printer.iface].
    
  •                  altsetting[printer.altset].
    
  •                  endpoint[printer.read_endp].
    
  •                  bEndpointAddress;
    
  •   fprintf(stderr, "DEBUG: Device protocol: %d\n",
    
  •       printer.protocol);
    
  •   if (printer.quirks & USBLP_QUIRK_BIDIR)
    
  •   {
    
  •     printer.read_endp = -1;
    
  •     fprintf(stderr, "DEBUG: Printer reports bi-di support "
    
  •         "but in reality works only uni-directionally\n");
    
  •   }
    
  •   if (printer.read_endp != -1)
    
  •   {
    
  •     printer.read_endp = confptr->interface[printer.iface].
    
  •                   altsetting[printer.altset].
    
  •                   endpoint[printer.read_endp].
    
  •                   bEndpointAddress;
    
  •   }
    
  •   else
    
  •     fprintf(stderr, "DEBUG: Uni-directional USB communication " 
    
  •         "only!\n");
    printer.write_endp = confptr->interface[printer.iface].
                   altsetting[printer.altset].
                   endpoint[printer.write_endp].
                   bEndpointAddress;
    
  •   if (printer.quirks & USBLP_QUIRK_NO_REATTACH)
    
  •   {
    
  •     printer.usblp_attached = 0;
    
  •     fprintf(stderr, "DEBUG: Printer does not like usblp "
    
  •         "kernel module to be re-attached after job\n");
    
  •   }
    
  •   libusb_free_config_descriptor(confptr);
    return (&printer);
           }
    

@@ -959,7 +1126,7 @@
if ((sern = cupsGetOption("SERIALNUMBER", num_values, values)) == NULL)
if ((sern = cupsGetOption("SERN", num_values, values)) == NULL)
if ((sern = cupsGetOption("SN", num_values, values)) == NULL &&

  • ((libusb_get_device_descriptor (printer->device, &devdesc) >= 0) &&
    
  • ((libusb_get_device_descriptor(printer->device, &devdesc) >= 0) &&
    

    devdesc.iSerialNumber))
    {
    /*
    @@ -1095,15 +1262,19 @@

    • Try opening the printer...
      */
  • if (libusb_open(printer->device, &printer->handle) < 0)

  • if ((errcode = libusb_open(printer->device, &printer->handle)) < 0)

  • {

  • fprintf(stderr, "DEBUG: Failed to open device, code: %d\n",

  •   errcode);
    

    return (-1);

  • }

printer->usblp_attached = 0;

if (verbose)
fputs("STATE: +connecting-to-device\n", stderr);

  • if ((errcode = libusb_get_device_descriptor (printer->device, &devdesc)) < 0)
  • if ((errcode = libusb_get_device_descriptor(printer->device, &devdesc)) < 0)
    {
    fprintf(stderr, "DEBUG: Failed to get device descriptor, code: %d\n",
    errcode);
    @@ -1132,14 +1303,9 @@
    else
    {
    printer->usblp_attached = 0;
  • if (errcode != LIBUSB_ERROR_NOT_SUPPORTED)
  • {
  •  fprintf(stderr,
    
  •     "DEBUG: Failed to check whether %04x:%04x has the \"usblp\" "
    
  •     "kernel module attached\n", devdesc.idVendor, devdesc.idProduct);
    
  •  goto error;
    
  • }
  • fprintf(stderr, "DEBUG: Failed to check whether %04x:%04x has the "usblp" kernel module attached\n",
  •     devdesc.idVendor, devdesc.idProduct);
    
  • goto error;
    }

/*
@@ -1156,6 +1322,8 @@
0, 0, (unsigned char )&current, 1, 5000) < 0)
current = 0; /
Assume not configured */

  • printer->origconf = current;

if ((errcode =
libusb_get_config_descriptor (printer->device, printer->conf, &confptr))
< 0)
@@ -1168,6 +1336,8 @@

if (number1 != current)
{

  • fprintf(stderr, "DEBUG: Switching USB device configuration: %d -> %d\n",

  •   current, number1);
    

    if ((errcode = libusb_set_configuration(printer->handle, number1)) < 0)
    {
    /*
    @@ -1347,6 +1517,64 @@

    /*

  • * 'printer_class_soft_reset()' - Do the soft reset request specific to printers

  • * This soft reset is specific to the printer device class and is much less

  • * invasive than the general USB reset libusb_reset_device(). Especially it

  • * does never happen that the USB addressing and configuration changes. What

  • * is actually done is that all buffers get flushed and the bulk IN and OUT

  • * pipes get reset to their default states. This clears all stall conditions.

  • * See http://cholla.mmto.org/computers/linux/usb/usbprint11.pdf

  • /
    +
    +static int /
    O - 0 on success, < 0 on error /
    +printer_class_soft_reset(usb_printer_t *printer) /
    I - Printer */
    +{

  • struct libusb_config_descriptor *confptr = NULL;

  •                                    /\* Pointer to current configuration */
    
  • int interface,

  •  errcode;
    
  • if (libusb_get_config_descriptor(printer->device, printer->conf, &confptr)

  •  < 0)
    
  • interface = printer->iface;

  • else

  • interface = confptr->interface[printer->iface].

  •  altsetting[printer->altset].bInterfaceNumber;
    
  • libusb_free_config_descriptor(confptr);

  • if ((errcode = libusb_control_transfer(printer->handle,

  •                LIBUSB_REQUEST_TYPE_CLASS |
    
  •                LIBUSB_ENDPOINT_OUT |
    
  •                LIBUSB_RECIPIENT_OTHER,
    
  •                2, 0, interface, NULL, 0, 5000)) < 0)
    
  • errcode = libusb_control_transfer(printer->handle,

  •                 LIBUSB_REQUEST_TYPE_CLASS |
    
  •                 LIBUSB_ENDPOINT_OUT |
    
  •                 LIBUSB_RECIPIENT_INTERFACE,
    
  •                 2, 0, interface, NULL, 0, 5000);
    
  • return errcode;
    +}

+/*

  • * 'quirks()' - Get the known quirks of a given printer model
  • */
    +
    +static unsigned int quirks(int vendor, int product)
    +{
  • int i;
  • for (i = 0; quirk_printers[i].vendorId; i++)
  • {
  • if (vendor == quirk_printers[i].vendorId &&
  • product == quirk_printers[i].productId)
  •  return quirk_printers[i].quirks;
    
  • }
  • return 0;
    +}

+/*

  • 'read_thread()' - Thread to read the backchannel data on.
    */

@@ -1620,7 +1848,7 @@

  • Send the reset...
    */
  • libusb_reset_device (g.printer->handle);
  • printer_class_soft_reset(g.printer);

/*

  • Release the I/O lock...

@michaelrsweet
Copy link
Collaborator Author

"usb-libusb-c-update-1-6-x-4.patch":

--- backend/usb-libusb.c 2012-06-22 11:53:18.043277991 +0200
+++ backend/usb-libusb.c 2012-07-10 16:57:35.032520971 +0200
@@ -22,6 +22,9 @@

  • make_device_uri() - Create a device URI for a USB printer.
  • open_device() - Open a connection to the USB printer.
  • print_cb() - Find a USB printer for printing.
  • * printer_class_soft_reset()' - Do the soft reset request specific to
  • * printers
  • * quirks() - Get the known quirks of a given printer model
  • read_thread() - Thread to read the backchannel data on.
  • sidechannel_thread() - Handle side-channel requests.
  • soft_reset() - Send a soft reset to the device.
    @@ -60,13 +63,15 @@
    {
    struct libusb_device device; / Device info /
    int conf, /
    Configuration */
  •       origconf,   /\* Original configuration _/
    iface,      /_ Interface _/
    altset,     /_ Alternate setting _/
    write_endp, /_ Write endpoint */
    
  •                    read_endp, /\* Read endpoint */
    
  •       read_endp,  /\* Read endpoint _/
    protocol,   /_ Protocol: 1 = Uni-di, 2 = Bi-di. */
    
  •                    usblp_attached; /\* Is the "usblp" kernel module
    
  •                  attached? */
    
  •       usblp_attached, /\* "usblp" kernel module attached? */
    
  •       opened_for_job; /\* Set to 1 by print_device() */
    
  • unsigned int quirks; /* Quirks flags /
    struct libusb_device_handle *handle; /
    Open handle to device */
    } usb_printer_t;

@@ -99,6 +104,55 @@
int sidechannel_thread_done;
} usb_globals_t;

+/*

  • * Quirks: various printer quirks are handled by this table & its flags.
  • * This is copied from the usblp kernel module. So we can easily copy and paste
  • * new quirks from the module.
  • */
    +
    +struct quirk_printer_struct {
  • int vendorId;
  • int productId;
  • unsigned int quirks;
    +};

+#define USBLP_QUIRK_BIDIR 0x1 /* reports bidir but requires

  •                  unidirectional mode (no INs/reads) _/
    
    +#define USBLP_QUIRK_USB_INIT 0x2 /_ needs vendor USB init string /
    +#define USBLP_QUIRK_BAD_CLASS 0x4 /
    descriptor uses vendor-specific
  •                  Class or SubClass _/
    
    +#define USBLP_QUIRK_NO_REATTACH 0x8000 /_ After printing we cannot re-attach
  •                  the usblp kernel module */
    
    +static const struct quirk_printer_struct quirk_printers[] = {
  • { 0x03f0, 0x0004, USBLP_QUIRK_BIDIR }, /* HP DeskJet 895C */
  • { 0x03f0, 0x0104, USBLP_QUIRK_BIDIR }, /* HP DeskJet 880C */
  • { 0x03f0, 0x0204, USBLP_QUIRK_BIDIR }, /* HP DeskJet 815C */
  • { 0x03f0, 0x0304, USBLP_QUIRK_BIDIR }, /* HP DeskJet 810C/812C */
  • { 0x03f0, 0x0404, USBLP_QUIRK_BIDIR }, /* HP DeskJet 830C */
  • { 0x03f0, 0x0504, USBLP_QUIRK_BIDIR }, /* HP DeskJet 885C */
  • { 0x03f0, 0x0604, USBLP_QUIRK_BIDIR }, /* HP DeskJet 840C */
  • { 0x03f0, 0x0804, USBLP_QUIRK_BIDIR }, /* HP DeskJet 816C */
  • { 0x03f0, 0x1104, USBLP_QUIRK_BIDIR }, /* HP Deskjet 959C */
  • { 0x0409, 0xefbe, USBLP_QUIRK_BIDIR }, /* NEC Picty900 (HP OEM) */
  • { 0x0409, 0xbef4, USBLP_QUIRK_BIDIR }, /* NEC Picty760 (HP OEM) */
  • { 0x0409, 0xf0be, USBLP_QUIRK_BIDIR }, /* NEC Picty920 (HP OEM) */
  • { 0x0409, 0xf1be, USBLP_QUIRK_BIDIR }, /* NEC Picty800 (HP OEM) */
  • { 0x0482, 0x0010, USBLP_QUIRK_BIDIR }, /* Kyocera Mita FS 820,
  •                     by zut kernel@zut.de */
    
  • { 0x04f9, 0x000d, USBLP_QUIRK_BIDIR |
  •         USBLP_QUIRK_NO_REATTACH }, /\* Brother Industries, Ltd
    
  •                     HL-1440 Laser Printer */
    
  • { 0x04b8, 0x0202, USBLP_QUIRK_BAD_CLASS }, /* Seiko Epson Receipt
  •                         Printer M129C */
    
  • { 0x067b, 0x2305, USBLP_QUIRK_BIDIR |
  •         USBLP_QUIRK_NO_REATTACH },
    
  • /* Prolific Technology, Inc. PL2305 Parallel Port
  •  (USB -> Parallel adapter) */
    
  • { 0, 0 }
    +};

/*

  • Globals...
    @@ -124,6 +178,8 @@
    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);
    +static int printer_class_soft_reset(usb_printer_t *printer);
    +static unsigned int quirks(int vendor, int product);
    static void *read_thread(void *reference);
    static void *sidechannel_thread(void *reference);
    static void soft_reset(void);
    @@ -163,7 +219,8 @@
    iostatus; /
    Current IO status /
    pthread_t read_thread_id, /
    Read thread /
    sidechannel_thread_id; /
    Side-channel thread */
  • int have_sidechannel = 0; /* Was the side-channel thread started? */
  • int have_sidechannel = 0, /* Was the side-channel thread started? */
  •   have_backchannel = 0;   /\* Do we have a back channel? _/
    
    struct stat sidechannel_info; /_ Side-channel file descriptor info /
    unsigned char print_buffer[8192], /
    Print data buffer /
    *print_ptr; /
    Pointer into print data buffer /
    @@ -172,6 +229,9 @@
    struct timeval *timeout, /
    Timeout pointer /
    tv; /
    Time value /
    struct timespec cond_timeout; /
    pthread condition timeout */
  • int num_opts; /* Number of options */
  • cups_option_t opts; / Options */
  • const char val; / Option value */

/*
@@ -187,6 +247,7 @@

  • Connect to the printer...
    */
  • fprintf(stderr, "DEBUG: Printing on printer with URI: %s\n", uri);
    while ((g.printer = find_device(print_cb, uri)) == NULL)
    {
    _cupsLangPrintFilter(stderr, "INFO",
    @@ -195,6 +256,7 @@
    }

g.print_fd = print_fd;

  • g.printer->opened_for_job = 1;

/*

  • If we are printing data from a print driver on stdin, ignore SIGTERM
    @@ -240,26 +302,63 @@
    }

/*

  • * Get the read thread going...

  • * Debug mode: If option "usb-unidir" is given, always deactivate

    • backchannel
      */
  • g.read_thread_stop = 0;

  • g.read_thread_done = 0;

  • num_opts = cupsParseOptions(argv[5], 0, &opts);

  • val = cupsGetOption("usb-unidir", num_opts, opts);

  • if (val && strcasecmp(val, "no") && strcasecmp(val, "off") &&

  •  strcasecmp(val, "false"))
    
  • {

  • g.printer->read_endp = -1;

  • fprintf(stderr, "DEBUG: Forced uni-directional communication "

  •   "via \"usb-unidir\" option.\n");
    
  • }

  • pthread_cond_init(&g.read_thread_cond, NULL);

  • pthread_mutex_init(&g.read_thread_mutex, NULL);

  • /*

  • * Debug mode: If option "usb-no-reattach" is given, do not re-attach

  • * the usblp kernel module after the job has completed.

  • */

  • if (pthread_create(&read_thread_id, NULL, read_thread, NULL))

  • val = cupsGetOption("usb-no-reattach", num_opts, opts);

  • if (val && strcasecmp(val, "no") && strcasecmp(val, "off") &&

  •  strcasecmp(val, "false"))
    

    {

  • fprintf(stderr, "DEBUG: Fatal USB error.\n");

  • _cupsLangPrintFilter(stderr, "ERROR",

  •                     _("There was an unrecoverable USB error."));
    
  • fputs("DEBUG: Couldn't create read thread.\n", stderr);

  • close_device(g.printer);

  • return (CUPS_BACKEND_STOP);

  • g.printer->usblp_attached = 0;

  • fprintf(stderr, "DEBUG: Forced not re-attaching the usblp kernel module "

  •   "after the job via \"usb-no-reattach\" option.\n");
    

    }

    /*

  • * Get the read thread going...

  • */

  • if (g.printer->read_endp != -1)
  • {
  • have_backchannel = 1;
  • g.read_thread_stop = 0;
  • g.read_thread_done = 0;
  • pthread_cond_init(&g.read_thread_cond, NULL);
  • pthread_mutex_init(&g.read_thread_mutex, NULL);
  • if (pthread_create(&read_thread_id, NULL, read_thread, NULL))
  • {
  •  fprintf(stderr, "DEBUG: Fatal USB error.\n");
    
  •  _cupsLangPrintFilter(stderr, "ERROR",
    
  •          _("There was an unrecoverable USB error."));
    
  •  fputs("DEBUG: Couldn't create read thread.\n", stderr);
    
  •  close_device(g.printer);
    
  •  return (CUPS_BACKEND_STOP);
    
  • }
  • }
  • else
  • fprintf(stderr, "DEBUG: Uni-directional device/mode, back channel "
  •   "deactivated.\n");
    
  • /*
    • The main thread sends the print file...
      */

@@ -515,38 +614,18 @@

  • Signal the read thread to exit then wait 7 seconds for it to complete...
    */

- g.read_thread_stop = 1;

- pthread_mutex_lock(&g.read_thread_mutex);

  • if (!g.read_thread_done)
  • if (have_backchannel)
    {

- fputs("DEBUG: Waiting for read thread to exit...\n", stderr);

  • gettimeofday(&tv, NULL);
  • cond_timeout.tv_sec = tv.tv_sec + WAIT_EOF_DELAY;

- cond_timeout.tv_nsec = tv.tv_usec * 1000;

  • while (!g.read_thread_done)
  • {
  •  if (pthread_cond_timedwait(&g.read_thread_cond, &g.read_thread_mutex,
    
  •            &cond_timeout) != 0)
    
  • break;
  • }
  • g.read_thread_stop = 1;
  • /*
  • * If it didn't exit abort the pending read and wait an additional second...
  • */
  • pthread_mutex_lock(&g.read_thread_mutex);

if (!g.read_thread_done)
{

  •  fputs("DEBUG: Read thread still active, aborting the pending read...\n",
    

- stderr);

  •  g.wait_eof = 0;
    
  •  fputs("DEBUG: Waiting for read thread to exit...\n", stderr);
    

    gettimeofday(&tv, NULL);

  •  cond_timeout.tv_sec  = tv.tv_sec + 1;
    
  •  cond_timeout.tv_sec  = tv.tv_sec + WAIT_EOF_DELAY;
    

    cond_timeout.tv_nsec = tv.tv_usec * 1000;

    while (!g.read_thread_done)
    @@ -555,10 +634,34 @@
    &cond_timeout) != 0)
    break;
    }
    +

  •  /*
    
  •   \* If it didn't exit abort the pending read and wait an additional
    
  •   \* second...
    
  •   */
    
  •  if (!g.read_thread_done)
    
  •  {
    
  • fputs("DEBUG: Read thread still active, aborting the pending read...\n",

  •     stderr);
    
  • g.wait_eof = 0;

  • gettimeofday(&tv, NULL);
  • cond_timeout.tv_sec = tv.tv_sec + 1;
  • cond_timeout.tv_nsec = tv.tv_usec * 1000;
  • while (!g.read_thread_done)
  • {
  • if (pthread_cond_timedwait(&g.read_thread_cond, &g.read_thread_mutex,
    
  •                &cond_timeout) != 0)
    
  •   break;
    
  • }
  •  }
    
    }
  • }
  • pthread_mutex_unlock(&g.read_thread_mutex);
  • pthread_mutex_unlock(&g.read_thread_mutex);
  • }

if (print_fd)
close(print_fd);
@@ -601,24 +704,54 @@
*/

int errcode; /* Return value of libusb function */

  • int number; /* Interface number */
  • int number1, /* Interface number */
  • number2; /* Configuration number */

errcode =

  •  libusb_get_config_descriptor (printer->device, printer->conf, &confptr);
    
  •  libusb_get_config_descriptor(printer->device, printer->conf, &confptr);
    

    if (errcode >= 0)
    {

  •  number = confptr->interface[printer->iface].
    
  •  number1 = confptr->interface[printer->iface].
    

    altsetting[printer->altset].bInterfaceNumber;

  •  libusb_release_interface(printer->handle, number);
    
  •  if (number != 0)
    
  • libusb_release_interface(printer->handle, 0);

  •  libusb_release_interface(printer->handle, number1);
    
  •  number2 = confptr->bConfigurationValue;
    
  •  libusb_free_config_descriptor(confptr);
    
  • /*
    
  •  \* If we have changed the configuration from one valid configuration
    
  •  \* to another, restore the old one
    
  •  */
    
  •  if (printer->origconf > 0 && printer->origconf != number2)
    
  •  {
    
  • fprintf(stderr, "DEBUG: Restoring USB device configuration: %d -> %d\n",

  •   number2, printer->origconf);
    
  • if ((errcode = libusb_set_configuration(printer->handle,

  •                   printer->origconf)) < 0)
    
  • {

  • if (errcode != LIBUSB_ERROR_BUSY)
    
  • {
    
  •   errcode =
    
  •     libusb_get_device_descriptor (printer->device, &devdesc);
    
  •   if (errcode < 0)
    
  •     fprintf(stderr,
    
  •         "DEBUG: Failed to set configuration %d\n",
    
  •         printer->origconf);
    
  •   else
    
  •     fprintf(stderr,
    
  •         "DEBUG: Failed to set configuration %d for %04x:%04x\n",
    
  •         printer->origconf, devdesc.idVendor, devdesc.idProduct);
    
  • }
    
  • }

  •  }
    

    /*

    • Re-attach "usblp" kernel module if it was attached before using this
    • device
      */
      if (printer->usblp_attached == 1)
  • if (libusb_attach_kernel_driver(printer->handle, printer->iface) < 0)

  • if (libusb_attach_kernel_driver(printer->handle, number1) < 0)
    {
    errcode = libusb_get_device_descriptor (printer->device, &devdesc);
    if (errcode < 0)
    @@ -629,8 +762,25 @@
    "DEBUG: Failed to re-attach "usblp" kernel module to "
    "%04x:%04x\n", devdesc.idVendor, devdesc.idProduct);
    }

  • }

  • else

  •  fprintf(stderr,
    
  •     "DEBUG: Failed to get configuration descriptor %d\n",
    
  •     printer->conf);
    
  •  libusb_free_config_descriptor(confptr);
    
  • /*

  • * Reset the device to clean up after the job

  • */

  • if (printer->opened_for_job == 1)

  • {

  •  if ((errcode = libusb_reset_device(printer->handle)) < 0)
    
  • fprintf(stderr,

  •   "DEBUG: Device reset failed, error code: %d\n",
    
  •   errcode);
    
  •  else
    
  • fprintf(stderr,

  •   "DEBUG: Resetting printer.\n");
    

    }

    /*
    @@ -702,13 +852,15 @@

    • a printer...
      */
  •  if (libusb_get_device_descriptor (device, &devdesc) < 0)
    
  •  if (libusb_get_device_descriptor(device, &devdesc) < 0)
    

    continue;

    if (!devdesc.bNumConfigurations || !devdesc.idVendor ||
    !devdesc.idProduct)
    continue;

  •  printer.quirks   = quirks(devdesc.idVendor, devdesc.idProduct);
    
    • for (conf = 0; conf < devdesc.bNumConfigurations; conf ++)
      {
      if (libusb_get_config_descriptor(device, conf, &confptr) < 0)
      @@ -733,13 +885,18 @@
      * 1284.4 (packet mode) protocol as well.
      */
  •   if (altptr->bInterfaceClass != LIBUSB_CLASS_PRINTER ||
    
  •       altptr->bInterfaceSubClass != 1 ||
    
  •   if (((altptr->bInterfaceClass != LIBUSB_CLASS_PRINTER ||
    
  •     altptr->bInterfaceSubClass != 1) && 
    
  •    ((printer.quirks & USBLP_QUIRK_BAD_CLASS) == 0)) ||
    (altptr->bInterfaceProtocol != 1 && /* Unidirectional */
     altptr->bInterfaceProtocol != 2) ||    /* Bidirectional */
    altptr->bInterfaceProtocol < protocol)
      continue;
    
  •   if (printer.quirks & USBLP_QUIRK_BAD_CLASS)
    
  •     fprintf(stderr, "DEBUG: Printer does not report class 7 and/or "
    
  •         "subclass 1 but works as a printer anyway\n");
    
    • read_endp = -1;
      write_endp = -1;

@@ -764,7 +921,10 @@
protocol = altptr->bInterfaceProtocol;
printer.altset = altset;
printer.write_endp = write_endp;

  •     printer.read_endp  = read_endp;
    
  •     if (protocol > 1)
    
  •   printer.read_endp = read_endp;
    
  •     else
    
  •   printer.read_endp = -1;
    }
    
    }

@@ -782,16 +942,41 @@
make_device_uri(&printer, device_id, device_uri,
sizeof(device_uri));

  •     fprintf(stderr, "DEBUG2: Printer found with device ID: %s "
    
  •         "Device URI: %s\n",
    
  •         device_id, device_uri);
    
    • if ((*cb)(&printer, device_uri, device_id, data))
      {
      
  •   printer.read_endp  = confptr->interface[printer.iface].
    
  •                  altsetting[printer.altset].
    
  •                  endpoint[printer.read_endp].
    
  •                  bEndpointAddress;
    
  •   fprintf(stderr, "DEBUG: Device protocol: %d\n",
    
  •       printer.protocol);
    
  •   if (printer.quirks & USBLP_QUIRK_BIDIR)
    
  •   {
    
  •     printer.read_endp = -1;
    
  •     fprintf(stderr, "DEBUG: Printer reports bi-di support "
    
  •         "but in reality works only uni-directionally\n");
    
  •   }
    
  •   if (printer.read_endp != -1)
    
  •   {
    
  •     printer.read_endp = confptr->interface[printer.iface].
    
  •                   altsetting[printer.altset].
    
  •                   endpoint[printer.read_endp].
    
  •                   bEndpointAddress;
    
  •   }
    
  •   else
    
  •     fprintf(stderr, "DEBUG: Uni-directional USB communication " 
    
  •         "only!\n");
    printer.write_endp = confptr->interface[printer.iface].
                   altsetting[printer.altset].
                   endpoint[printer.write_endp].
                   bEndpointAddress;
    
  •   if (printer.quirks & USBLP_QUIRK_NO_REATTACH)
    
  •   {
    
  •     printer.usblp_attached = 0;
    
  •     fprintf(stderr, "DEBUG: Printer does not like usblp "
    
  •         "kernel module to be re-attached after job\n");
    
  •   }
    
  •   libusb_free_config_descriptor(confptr);
    return (&printer);
           }
    

@@ -1095,15 +1280,20 @@

  • Try opening the printer...
    */
  • if (libusb_open(printer->device, &printer->handle) < 0)
  • if ((errcode = libusb_open(printer->device, &printer->handle)) < 0)
  • {
  • fprintf(stderr, "DEBUG: Failed to open device, code: %d\n",
  •   errcode);
    
    return (-1);
  • }

printer->usblp_attached = 0;

  • printer->opened_for_job = 0;

if (verbose)
fputs("STATE: +connecting-to-device\n", stderr);

  • if ((errcode = libusb_get_device_descriptor (printer->device, &devdesc)) < 0)
  • if ((errcode = libusb_get_device_descriptor(printer->device, &devdesc)) < 0)
    {
    fprintf(stderr, "DEBUG: Failed to get device descriptor, code: %d\n",
    errcode);
    @@ -1132,14 +1322,9 @@
    else
    {
    printer->usblp_attached = 0;
  • if (errcode != LIBUSB_ERROR_NOT_SUPPORTED)
  • {
  •  fprintf(stderr,
    
  •     "DEBUG: Failed to check whether %04x:%04x has the \"usblp\" "
    
  •     "kernel module attached\n", devdesc.idVendor, devdesc.idProduct);
    
  •  goto error;
    
  • }
  • fprintf(stderr, "DEBUG: Failed to check whether %04x:%04x has the "usblp" kernel module attached\n",
  •     devdesc.idVendor, devdesc.idProduct);
    
  • goto error;
    }

/*
@@ -1156,7 +1341,9 @@
0, 0, (unsigned char )&current, 1, 5000) < 0)
current = 0; /
Assume not configured */

  • if ((errcode =
  • printer->origconf = current;
  • if ((errcode =
    libusb_get_config_descriptor (printer->device, printer->conf, &confptr))
    < 0)
    {
    @@ -1168,6 +1355,8 @@

if (number1 != current)
{

  • fprintf(stderr, "DEBUG: Switching USB device configuration: %d -> %d\n",

  •   current, number1);
    

    if ((errcode = libusb_set_configuration(printer->handle, number1)) < 0)
    {
    /*
    @@ -1347,6 +1536,64 @@

    /*

  • * 'printer_class_soft_reset()' - Do the soft reset request specific to printers

  • * This soft reset is specific to the printer device class and is much less

  • * invasive than the general USB reset libusb_reset_device(). Especially it

  • * does never happen that the USB addressing and configuration changes. What

  • * is actually done is that all buffers get flushed and the bulk IN and OUT

  • * pipes get reset to their default states. This clears all stall conditions.

  • * See http://cholla.mmto.org/computers/linux/usb/usbprint11.pdf

  • /
    +
    +static int /
    O - 0 on success, < 0 on error /
    +printer_class_soft_reset(usb_printer_t *printer) /
    I - Printer */
    +{

  • struct libusb_config_descriptor *confptr = NULL;

  •                                    /\* Pointer to current configuration */
    
  • int interface,

  •  errcode;
    
  • if (libusb_get_config_descriptor(printer->device, printer->conf, &confptr)

  •  < 0)
    
  • interface = printer->iface;

  • else

  • interface = confptr->interface[printer->iface].

  •  altsetting[printer->altset].bInterfaceNumber;
    
  • libusb_free_config_descriptor(confptr);

  • if ((errcode = libusb_control_transfer(printer->handle,

  •                LIBUSB_REQUEST_TYPE_CLASS |
    
  •                LIBUSB_ENDPOINT_OUT |
    
  •                LIBUSB_RECIPIENT_OTHER,
    
  •                2, 0, interface, NULL, 0, 5000)) < 0)
    
  • errcode = libusb_control_transfer(printer->handle,

  •                 LIBUSB_REQUEST_TYPE_CLASS |
    
  •                 LIBUSB_ENDPOINT_OUT |
    
  •                 LIBUSB_RECIPIENT_INTERFACE,
    
  •                 2, 0, interface, NULL, 0, 5000);
    
  • return errcode;
    +}

+/*

  • * 'quirks()' - Get the known quirks of a given printer model
  • */
    +
    +static unsigned int quirks(int vendor, int product)
    +{
  • int i;
  • for (i = 0; quirk_printers[i].vendorId; i++)
  • {
  • if (vendor == quirk_printers[i].vendorId &&
  • product == quirk_printers[i].productId)
  •  return quirk_printers[i].quirks;
    
  • }
  • return 0;
    +}

+/*

  • 'read_thread()' - Thread to read the backchannel data on.
    */

@@ -1620,7 +1867,7 @@

  • Send the reset...
    */
  • libusb_reset_device (g.printer->handle);
  • printer_class_soft_reset(g.printer);

/*

  • Release the I/O lock...

@michaelrsweet
Copy link
Collaborator Author

"usb-libusb-c-update-1-5-x-4.patch":

--- backend/usb-libusb.c 2012-06-22 11:53:36.103353715 +0200
+++ backend/usb-libusb.c 2012-07-10 16:57:35.032520971 +0200
@@ -22,6 +22,9 @@

  • make_device_uri() - Create a device URI for a USB printer.
  • open_device() - Open a connection to the USB printer.
  • print_cb() - Find a USB printer for printing.
  • * printer_class_soft_reset()' - Do the soft reset request specific to
  • * printers
  • * quirks() - Get the known quirks of a given printer model
  • read_thread() - Thread to read the backchannel data on.
  • sidechannel_thread() - Handle side-channel requests.
  • soft_reset() - Send a soft reset to the device.
    @@ -60,13 +63,15 @@
    {
    struct libusb_device device; / Device info /
    int conf, /
    Configuration */
  •       origconf,   /\* Original configuration _/
    iface,      /_ Interface _/
    altset,     /_ Alternate setting _/
    write_endp, /_ Write endpoint */
    
  •                    read_endp, /\* Read endpoint */
    
  •       read_endp,  /\* Read endpoint _/
    protocol,   /_ Protocol: 1 = Uni-di, 2 = Bi-di. */
    
  •                    usblp_attached; /\* Is the "usblp" kernel module
    
  •                  attached? */
    
  •       usblp_attached, /\* "usblp" kernel module attached? */
    
  •       opened_for_job; /\* Set to 1 by print_device() */
    
  • unsigned int quirks; /* Quirks flags /
    struct libusb_device_handle *handle; /
    Open handle to device */
    } usb_printer_t;

@@ -99,6 +104,55 @@
int sidechannel_thread_done;
} usb_globals_t;

+/*

  • * Quirks: various printer quirks are handled by this table & its flags.
  • * This is copied from the usblp kernel module. So we can easily copy and paste
  • * new quirks from the module.
  • */
    +
    +struct quirk_printer_struct {
  • int vendorId;
  • int productId;
  • unsigned int quirks;
    +};

+#define USBLP_QUIRK_BIDIR 0x1 /* reports bidir but requires

  •                  unidirectional mode (no INs/reads) _/
    
    +#define USBLP_QUIRK_USB_INIT 0x2 /_ needs vendor USB init string /
    +#define USBLP_QUIRK_BAD_CLASS 0x4 /
    descriptor uses vendor-specific
  •                  Class or SubClass _/
    
    +#define USBLP_QUIRK_NO_REATTACH 0x8000 /_ After printing we cannot re-attach
  •                  the usblp kernel module */
    
    +static const struct quirk_printer_struct quirk_printers[] = {
  • { 0x03f0, 0x0004, USBLP_QUIRK_BIDIR }, /* HP DeskJet 895C */
  • { 0x03f0, 0x0104, USBLP_QUIRK_BIDIR }, /* HP DeskJet 880C */
  • { 0x03f0, 0x0204, USBLP_QUIRK_BIDIR }, /* HP DeskJet 815C */
  • { 0x03f0, 0x0304, USBLP_QUIRK_BIDIR }, /* HP DeskJet 810C/812C */
  • { 0x03f0, 0x0404, USBLP_QUIRK_BIDIR }, /* HP DeskJet 830C */
  • { 0x03f0, 0x0504, USBLP_QUIRK_BIDIR }, /* HP DeskJet 885C */
  • { 0x03f0, 0x0604, USBLP_QUIRK_BIDIR }, /* HP DeskJet 840C */
  • { 0x03f0, 0x0804, USBLP_QUIRK_BIDIR }, /* HP DeskJet 816C */
  • { 0x03f0, 0x1104, USBLP_QUIRK_BIDIR }, /* HP Deskjet 959C */
  • { 0x0409, 0xefbe, USBLP_QUIRK_BIDIR }, /* NEC Picty900 (HP OEM) */
  • { 0x0409, 0xbef4, USBLP_QUIRK_BIDIR }, /* NEC Picty760 (HP OEM) */
  • { 0x0409, 0xf0be, USBLP_QUIRK_BIDIR }, /* NEC Picty920 (HP OEM) */
  • { 0x0409, 0xf1be, USBLP_QUIRK_BIDIR }, /* NEC Picty800 (HP OEM) */
  • { 0x0482, 0x0010, USBLP_QUIRK_BIDIR }, /* Kyocera Mita FS 820,
  •                     by zut kernel@zut.de */
    
  • { 0x04f9, 0x000d, USBLP_QUIRK_BIDIR |
  •         USBLP_QUIRK_NO_REATTACH }, /\* Brother Industries, Ltd
    
  •                     HL-1440 Laser Printer */
    
  • { 0x04b8, 0x0202, USBLP_QUIRK_BAD_CLASS }, /* Seiko Epson Receipt
  •                         Printer M129C */
    
  • { 0x067b, 0x2305, USBLP_QUIRK_BIDIR |
  •         USBLP_QUIRK_NO_REATTACH },
    
  • /* Prolific Technology, Inc. PL2305 Parallel Port
  •  (USB -> Parallel adapter) */
    
  • { 0, 0 }
    +};

/*

  • Globals...
    @@ -124,6 +178,8 @@
    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);
    +static int printer_class_soft_reset(usb_printer_t *printer);
    +static unsigned int quirks(int vendor, int product);
    static void *read_thread(void *reference);
    static void *sidechannel_thread(void *reference);
    static void soft_reset(void);
    @@ -163,7 +219,8 @@
    iostatus; /
    Current IO status /
    pthread_t read_thread_id, /
    Read thread /
    sidechannel_thread_id; /
    Side-channel thread */
  • int have_sidechannel = 0; /* Was the side-channel thread started? */
  • int have_sidechannel = 0, /* Was the side-channel thread started? */
  •   have_backchannel = 0;   /\* Do we have a back channel? _/
    
    struct stat sidechannel_info; /_ Side-channel file descriptor info /
    unsigned char print_buffer[8192], /
    Print data buffer /
    *print_ptr; /
    Pointer into print data buffer /
    @@ -172,6 +229,9 @@
    struct timeval *timeout, /
    Timeout pointer /
    tv; /
    Time value /
    struct timespec cond_timeout; /
    pthread condition timeout */
  • int num_opts; /* Number of options */
  • cups_option_t opts; / Options */
  • const char val; / Option value */

/*
@@ -187,6 +247,7 @@

  • Connect to the printer...
    */
  • fprintf(stderr, "DEBUG: Printing on printer with URI: %s\n", uri);
    while ((g.printer = find_device(print_cb, uri)) == NULL)
    {
    _cupsLangPrintFilter(stderr, "INFO",
    @@ -195,6 +256,7 @@
    }

g.print_fd = print_fd;

  • g.printer->opened_for_job = 1;

/*

  • If we are printing data from a print driver on stdin, ignore SIGTERM
    @@ -240,24 +302,61 @@
    }

/*

  • * Get the read thread going...
  • * Debug mode: If option "usb-unidir" is given, always deactivate
    • backchannel
      */
  • g.read_thread_stop = 0;
  • g.read_thread_done = 0;
  • num_opts = cupsParseOptions(argv[5], 0, &opts);
  • val = cupsGetOption("usb-unidir", num_opts, opts);
  • if (val && strcasecmp(val, "no") && strcasecmp(val, "off") &&
  •  strcasecmp(val, "false"))
    
  • {
  • g.printer->read_endp = -1;
  • fprintf(stderr, "DEBUG: Forced uni-directional communication "
  •   "via \"usb-unidir\" option.\n");
    
  • }
  • pthread_cond_init(&g.read_thread_cond, NULL);
  • pthread_mutex_init(&g.read_thread_mutex, NULL);
  • /*
  • * Debug mode: If option "usb-no-reattach" is given, do not re-attach
  • * the usblp kernel module after the job has completed.
  • */
  • val = cupsGetOption("usb-no-reattach", num_opts, opts);
  • if (val && strcasecmp(val, "no") && strcasecmp(val, "off") &&
  •  strcasecmp(val, "false"))
    
  • {
  • g.printer->usblp_attached = 0;
  • fprintf(stderr, "DEBUG: Forced not re-attaching the usblp kernel module "
  •   "after the job via \"usb-no-reattach\" option.\n");
    
  • }
  • /*
  • * Get the read thread going...
  • */
  • if (pthread_create(&read_thread_id, NULL, read_thread, NULL))
  • if (g.printer->read_endp != -1)
    {
  • fprintf(stderr, "DEBUG: Fatal USB error.\n");
  • _cupsLangPrintFilter(stderr, "ERROR",
  •                     _("There was an unrecoverable USB error."));
    
  • fputs("DEBUG: Couldn't create read thread.\n", stderr);
  • close_device(g.printer);
  • return (CUPS_BACKEND_STOP);
  • have_backchannel = 1;
  • g.read_thread_stop = 0;
  • g.read_thread_done = 0;
  • pthread_cond_init(&g.read_thread_cond, NULL);
  • pthread_mutex_init(&g.read_thread_mutex, NULL);
  • if (pthread_create(&read_thread_id, NULL, read_thread, NULL))

  • {

  •  fprintf(stderr, "DEBUG: Fatal USB error.\n");
    
  •  _cupsLangPrintFilter(stderr, "ERROR",
    
  •          _("There was an unrecoverable USB error."));
    
  •  fputs("DEBUG: Couldn't create read thread.\n", stderr);
    
  •  close_device(g.printer);
    
  •  return (CUPS_BACKEND_STOP);
    
  • }
    }

  • else

  • fprintf(stderr, "DEBUG: Uni-directional device/mode, back channel "

  •   "deactivated.\n");
    

    /*

    • The main thread sends the print file...
      @@ -515,50 +614,54 @@
    • Signal the read thread to exit then wait 7 seconds for it to complete...
      */

- g.read_thread_stop = 1;

- pthread_mutex_lock(&g.read_thread_mutex);

  • if (!g.read_thread_done)
  • if (have_backchannel)
    {

- fputs("DEBUG: Waiting for read thread to exit...\n", stderr);

  • gettimeofday(&tv, NULL);
  • cond_timeout.tv_sec = tv.tv_sec + WAIT_EOF_DELAY;
  • cond_timeout.tv_nsec = tv.tv_usec * 1000;
  • g.read_thread_stop = 1;
  • while (!g.read_thread_done)
  • {
  •  if (pthread_cond_timedwait(&g.read_thread_cond, &g.read_thread_mutex,
    
  •            &cond_timeout) != 0)
    
  • break;
  • }
  • pthread_mutex_lock(&g.read_thread_mutex);
  • /*
  • * If it didn't exit abort the pending read and wait an additional second...

- */

 if (!g.read_thread_done)
 {
  •  fputs("DEBUG: Read thread still active, aborting the pending read...\n", 
    

- stderr);

  •  g.wait_eof = 0;
    
  •  fputs("DEBUG: Waiting for read thread to exit...\n", stderr);
    

    gettimeofday(&tv, NULL);

  •  cond_timeout.tv_sec  = tv.tv_sec + 1;
    
  •  cond_timeout.tv_sec  = tv.tv_sec + WAIT_EOF_DELAY;
    

    cond_timeout.tv_nsec = tv.tv_usec * 1000;

    • while (!g.read_thread_done)
      {
      if (pthread_cond_timedwait(&g.read_thread_cond, &g.read_thread_mutex,
      &cond_timeout) != 0)
      break;
      }
  •  /*
    
  •   \* If it didn't exit abort the pending read and wait an additional
    
  •   \* second...
    
  •   */
    
  •  if (!g.read_thread_done)
    
  •  {
    
  • fputs("DEBUG: Read thread still active, aborting the pending read...\n",

  •     stderr);
    
  • g.wait_eof = 0;

  • gettimeofday(&tv, NULL);
  • cond_timeout.tv_sec = tv.tv_sec + 1;
  • cond_timeout.tv_nsec = tv.tv_usec * 1000;
  • while (!g.read_thread_done)
  • {
  • if (pthread_cond_timedwait(&g.read_thread_cond, &g.read_thread_mutex,
    
  •                &cond_timeout) != 0)
    
  •   break;
    
  • }
  •  }
    
    }
  • }
  • pthread_mutex_unlock(&g.read_thread_mutex);
  • pthread_mutex_unlock(&g.read_thread_mutex);
  • }

if (print_fd)
close(print_fd);
@@ -601,24 +704,54 @@
*/

int errcode; /* Return value of libusb function */

  • int number; /* Interface number */

  • int number1, /* Interface number */

  • number2; /* Configuration number */

  • errcode =

  •  libusb_get_config_descriptor (printer->device, printer->conf, &confptr);
    
  • errcode =

  •  libusb_get_config_descriptor(printer->device, printer->conf, &confptr);
    

    if (errcode >= 0)
    {

  •  number = confptr->interface[printer->iface].
    
  •  number1 = confptr->interface[printer->iface].
    

    altsetting[printer->altset].bInterfaceNumber;

  •  libusb_release_interface(printer->handle, number);
    
  •  if (number != 0)
    
  • libusb_release_interface(printer->handle, 0);

  •  libusb_release_interface(printer->handle, number1);
    
  •  number2 = confptr->bConfigurationValue;
    
  •  libusb_free_config_descriptor(confptr);
    
  • /*
    
  •  \* If we have changed the configuration from one valid configuration
    
  •  \* to another, restore the old one
    
  •  */
    
  •  if (printer->origconf > 0 && printer->origconf != number2)
    
  •  {
    
  • fprintf(stderr, "DEBUG: Restoring USB device configuration: %d -> %d\n",

  •   number2, printer->origconf);
    
  • if ((errcode = libusb_set_configuration(printer->handle,

  •                   printer->origconf)) < 0)
    
  • {

  • if (errcode != LIBUSB_ERROR_BUSY)
    
  • {
    
  •   errcode =
    
  •     libusb_get_device_descriptor (printer->device, &devdesc);
    
  •   if (errcode < 0)
    
  •     fprintf(stderr,
    
  •         "DEBUG: Failed to set configuration %d\n",
    
  •         printer->origconf);
    
  •   else
    
  •     fprintf(stderr,
    
  •         "DEBUG: Failed to set configuration %d for %04x:%04x\n",
    
  •         printer->origconf, devdesc.idVendor, devdesc.idProduct);
    
  • }
    
  • }

  •  }
    

    /*

    • Re-attach "usblp" kernel module if it was attached before using this
    • device
      */
      if (printer->usblp_attached == 1)
  • if (libusb_attach_kernel_driver(printer->handle, printer->iface) < 0)

  • if (libusb_attach_kernel_driver(printer->handle, number1) < 0)
    {
    errcode = libusb_get_device_descriptor (printer->device, &devdesc);
    if (errcode < 0)
    @@ -629,8 +762,25 @@
    "DEBUG: Failed to re-attach "usblp" kernel module to "
    "%04x:%04x\n", devdesc.idVendor, devdesc.idProduct);
    }

  • }

  • else

  •  fprintf(stderr,
    
  •     "DEBUG: Failed to get configuration descriptor %d\n",
    
  •     printer->conf);
    
  •  libusb_free_config_descriptor(confptr);
    
  • /*

  • * Reset the device to clean up after the job

  • */

  • if (printer->opened_for_job == 1)

  • {

  •  if ((errcode = libusb_reset_device(printer->handle)) < 0)
    
  • fprintf(stderr,

  •   "DEBUG: Device reset failed, error code: %d\n",
    
  •   errcode);
    
  •  else
    
  • fprintf(stderr,

  •   "DEBUG: Resetting printer.\n");
    

    }

    /*
    @@ -702,16 +852,18 @@

    • a printer...
      */
  •  if (libusb_get_device_descriptor (device, &devdesc) < 0)
    
  •  if (libusb_get_device_descriptor(device, &devdesc) < 0)
    

    continue;

    if (!devdesc.bNumConfigurations || !devdesc.idVendor ||
    !devdesc.idProduct)
    continue;

  •  printer.quirks   = quirks(devdesc.idVendor, devdesc.idProduct);
    
    • for (conf = 0; conf < devdesc.bNumConfigurations; conf ++)
      {
  • if (libusb_get_config_descriptor (device, conf, &confptr) < 0)

  • if (libusb_get_config_descriptor(device, conf, &confptr) < 0)
    continue;
    for (iface = 0, ifaceptr = confptr->interface;
    iface < confptr->bNumInterfaces;
    @@ -733,13 +885,18 @@
    * 1284.4 (packet mode) protocol as well.
    */

  •   if (altptr->bInterfaceClass != LIBUSB_CLASS_PRINTER ||
    
  •       altptr->bInterfaceSubClass != 1 ||
    
  •   if (((altptr->bInterfaceClass != LIBUSB_CLASS_PRINTER ||
    
  •     altptr->bInterfaceSubClass != 1) && 
    
  •    ((printer.quirks & USBLP_QUIRK_BAD_CLASS) == 0)) ||
    (altptr->bInterfaceProtocol != 1 && /* Unidirectional */
     altptr->bInterfaceProtocol != 2) ||    /* Bidirectional */
    altptr->bInterfaceProtocol < protocol)
      continue;
    
  •   if (printer.quirks & USBLP_QUIRK_BAD_CLASS)
    
  •     fprintf(stderr, "DEBUG: Printer does not report class 7 and/or "
    
  •         "subclass 1 but works as a printer anyway\n");
    
    • read_endp = -1;
      write_endp = -1;

@@ -764,7 +921,10 @@
protocol = altptr->bInterfaceProtocol;
printer.altset = altset;
printer.write_endp = write_endp;

  •     printer.read_endp  = read_endp;
    
  •     if (protocol > 1)
    
  •   printer.read_endp = read_endp;
    
  •     else
    
  •   printer.read_endp = -1;
    }
    
    }

@@ -782,16 +942,41 @@
make_device_uri(&printer, device_id, device_uri,
sizeof(device_uri));

  •     fprintf(stderr, "DEBUG2: Printer found with device ID: %s "
    
  •         "Device URI: %s\n",
    
  •         device_id, device_uri);
    
    • if ((*cb)(&printer, device_uri, device_id, data))
      {
      
  •   printer.read_endp  = confptr->interface[printer.iface].
    
  •                  altsetting[printer.altset].
    
  •                  endpoint[printer.read_endp].
    
  •                  bEndpointAddress;
    
  •   fprintf(stderr, "DEBUG: Device protocol: %d\n",
    
  •       printer.protocol);
    
  •   if (printer.quirks & USBLP_QUIRK_BIDIR)
    
  •   {
    
  •     printer.read_endp = -1;
    
  •     fprintf(stderr, "DEBUG: Printer reports bi-di support "
    
  •         "but in reality works only uni-directionally\n");
    
  •   }
    
  •   if (printer.read_endp != -1)
    
  •   {
    
  •     printer.read_endp = confptr->interface[printer.iface].
    
  •                   altsetting[printer.altset].
    
  •                   endpoint[printer.read_endp].
    
  •                   bEndpointAddress;
    
  •   }
    
  •   else
    
  •     fprintf(stderr, "DEBUG: Uni-directional USB communication " 
    
  •         "only!\n");
    printer.write_endp = confptr->interface[printer.iface].
                   altsetting[printer.altset].
                   endpoint[printer.write_endp].
                   bEndpointAddress;
    
  •   if (printer.quirks & USBLP_QUIRK_NO_REATTACH)
    
  •   {
    
  •     printer.usblp_attached = 0;
    
  •     fprintf(stderr, "DEBUG: Printer does not like usblp "
    
  •         "kernel module to be re-attached after job\n");
    
  •   }
    
  •   libusb_free_config_descriptor(confptr);
    return (&printer);
           }
    

@@ -1095,15 +1280,20 @@

  • Try opening the printer...
    */
  • if (libusb_open(printer->device, &printer->handle) < 0)
  • if ((errcode = libusb_open(printer->device, &printer->handle)) < 0)
  • {
  • fprintf(stderr, "DEBUG: Failed to open device, code: %d\n",
  •   errcode);
    
    return (-1);
  • }

printer->usblp_attached = 0;

  • printer->opened_for_job = 0;

if (verbose)
fputs("STATE: +connecting-to-device\n", stderr);

  • if ((errcode = libusb_get_device_descriptor (printer->device, &devdesc)) < 0)
  • if ((errcode = libusb_get_device_descriptor(printer->device, &devdesc)) < 0)
    {
    fprintf(stderr, "DEBUG: Failed to get device descriptor, code: %d\n",
    errcode);
    @@ -1132,14 +1322,9 @@
    else
    {
    printer->usblp_attached = 0;
  • if (errcode != LIBUSB_ERROR_NOT_SUPPORTED)
  • {
  •  fprintf(stderr,
    
  •     "DEBUG: Failed to check whether %04x:%04x has the \"usblp\" "
    
  •     "kernel module attached\n", devdesc.idVendor, devdesc.idProduct);
    
  •  goto error;
    
  • }
  • fprintf(stderr, "DEBUG: Failed to check whether %04x:%04x has the "usblp" kernel module attached\n",
  •     devdesc.idVendor, devdesc.idProduct);
    
  • goto error;
    }

/*
@@ -1156,6 +1341,8 @@
0, 0, (unsigned char )&current, 1, 5000) < 0)
current = 0; /
Assume not configured */

  • printer->origconf = current;

if ((errcode =
libusb_get_config_descriptor (printer->device, printer->conf, &confptr))
< 0)
@@ -1168,6 +1355,8 @@

if (number1 != current)
{

  • fprintf(stderr, "DEBUG: Switching USB device configuration: %d -> %d\n",

  •   current, number1);
    

    if ((errcode = libusb_set_configuration(printer->handle, number1)) < 0)
    {
    /*
    @@ -1347,6 +1536,64 @@

    /*

  • * 'printer_class_soft_reset()' - Do the soft reset request specific to printers

  • * This soft reset is specific to the printer device class and is much less

  • * invasive than the general USB reset libusb_reset_device(). Especially it

  • * does never happen that the USB addressing and configuration changes. What

  • * is actually done is that all buffers get flushed and the bulk IN and OUT

  • * pipes get reset to their default states. This clears all stall conditions.

  • * See http://cholla.mmto.org/computers/linux/usb/usbprint11.pdf

  • /
    +
    +static int /
    O - 0 on success, < 0 on error /
    +printer_class_soft_reset(usb_printer_t *printer) /
    I - Printer */
    +{

  • struct libusb_config_descriptor *confptr = NULL;

  •                                    /\* Pointer to current configuration */
    
  • int interface,

  •  errcode;
    
  • if (libusb_get_config_descriptor(printer->device, printer->conf, &confptr)

  •  < 0)
    
  • interface = printer->iface;

  • else

  • interface = confptr->interface[printer->iface].

  •  altsetting[printer->altset].bInterfaceNumber;
    
  • libusb_free_config_descriptor(confptr);

  • if ((errcode = libusb_control_transfer(printer->handle,

  •                LIBUSB_REQUEST_TYPE_CLASS |
    
  •                LIBUSB_ENDPOINT_OUT |
    
  •                LIBUSB_RECIPIENT_OTHER,
    
  •                2, 0, interface, NULL, 0, 5000)) < 0)
    
  • errcode = libusb_control_transfer(printer->handle,

  •                 LIBUSB_REQUEST_TYPE_CLASS |
    
  •                 LIBUSB_ENDPOINT_OUT |
    
  •                 LIBUSB_RECIPIENT_INTERFACE,
    
  •                 2, 0, interface, NULL, 0, 5000);
    
  • return errcode;
    +}

+/*

  • * 'quirks()' - Get the known quirks of a given printer model
  • */
    +
    +static unsigned int quirks(int vendor, int product)
    +{
  • int i;
  • for (i = 0; quirk_printers[i].vendorId; i++)
  • {
  • if (vendor == quirk_printers[i].vendorId &&
  • product == quirk_printers[i].productId)
  •  return quirk_printers[i].quirks;
    
  • }
  • return 0;
    +}

+/*

  • 'read_thread()' - Thread to read the backchannel data on.
    */

@@ -1620,7 +1867,7 @@

  • Send the reset...
    */
  • libusb_reset_device (g.printer->handle);
  • printer_class_soft_reset(g.printer);

/*

  • Release the I/O lock...

@averagejoey2000
Copy link

I came here from https://wiki.archlinux.org/index.php/CUPS/Troubleshooting#USB_printers
I have a Panasonic KX-MB2030 multi-function single sided grayscale printer. I am the maintainer of https://aur.archlinux.org/packages/mccgdi/
I had to blacklist the usblp kernel module for cups to recognize the printer. At the time, I hadn't noticed the warning about needing to write a thorough bug report, so I don't have any of my logs. I can give the outputs of diagnostic commands as well. I'm not entirely sure what to be saying here, just that the wiki said to tell you.

@michaelrsweet
Copy link
Collaborator Author

@averagejoey2000 At this point I have nothing I can help you with. If there is something we can change in the backend to help you, we are happy to accommodate you and the other Linux distributions, but right now it looks like the right answer is to simply blacklist the usblp module since the USB backend is a complete replacement for general printing purposes...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants