Re: [MOD] STR #4128: libusb-based USB backend: Further fixes

Till Kamppeter <[email protected]>
Newsgroups gmane.comp.printing.cups.bugs
Message-ID <[email protected]>
DO NOT REPLY TO THIS MESSAGE.  INSTEAD, POST ANY RESPONSES TO THE LINK BELOW.

[STR New]

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

Link: http://www.cups.org/str.php?L4128
Version: 1.6-current

_______________________________________________
cups-bugs mailing list
[email protected]
http://lists.easysw.com/mailman/listinfo/cups-bugs
usb-libusb-c-update-1-6-x-4.patch (text/plain, 19 KB)
--- 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 <[email protected]> */
+	{ 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...
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.