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

Let the "usb" backend use libusb instead of the usblp kernel module #1575

Closed
michaelrsweet opened this issue Apr 20, 2006 · 16 comments
Closed
Labels
enhancement New feature or request
Milestone

Comments

@michaelrsweet
Copy link
Collaborator

Version: 1.4-feature
CUPS.org User: till.kamppeter

As discussed in this thread on linuxprinting.org

http://linuxprinting.org/pipermail/inkjet-list/2006q2/000778.html
http://linuxprinting.org/pipermail/inkjet-list/2006q2/000779.html
http://linuxprinting.org/pipermail/inkjet-list/2006q2/000780.html

and also due to problems reported with Epson multi-function devices and recent kernels it can make sense to let the "usb" backend of CUPS not use the "usblp" kernel module any more, but libusb (SANE also switched from a kernel module for USB scanners to libusb, also HPLIP from HP did so).

Our Mandriva contributor Couriousous (couriousous at mandriva dot org, couriousous at sceen dot net) has sent to me a libusb-based USB CUPS backend once (attached), which could be the start for the development of a new USB backend for CUPS.

Build instructions:

  • Install libusb-devel (or libusb-dev)
  • Uncompress CUPS source code
  • Copy the attached file into the backend/ directory
  • Edit backend/usb.c modifying the include of usb-unix.c to usb-libusb.c
  • Let compilation being done with "-lusb" (by LDFLAGS environment
    variable)
  • Install CUPS as usual (or simply replace your "usb" backend by the new
    one).

My suggestion would be either to switch the current backend to libusb-only or better to make it runtime-configurable, so that the user can choose between using the kernel module or libusb, simply by a switch in /etc/cups/cupsd.conf or /etc/cups/usb.conf.

@michaelrsweet
Copy link
Collaborator Author

CUPS.org User: till.kamppeter

Note that the usb-libusb.c was developed with CUPS 1.1.23, a slight change has to be done for CUPS 1.2 to compile it. See attached patch. You need to add "-lusb" to the "LDFLAGS" line in Makedefs after running "./configure" and before running "make". Then you get the libusb-based backend as backend/usb. Replace the original backend in /usr/lib/cups/backend by it.

Now it auto-detects the HP PhotoSmart 2600 on USB as follows:

[root@majax c]# /usr/lib/cups/backend/usb
direct usb://HP/Photosmart%202600%20series?serial=MY53OK70V10400 "HP Photosmart 2600 series" "USB Printer #1002"
[root@majax c]#

There is no 1284 device ID, but the web interface still assigns the correct PPD file from HPLIP.

The problem is that this backend does not actually print. When printing the test page via the web interface button I get

Unable to find USB device "usb://HP/Photosmart%202600%20series?serial=MY53OK70V10400"

and the queue gets stopped due to backend failure.

So the backend seems to need some further adaptation to CUPS 1.2.

@michaelrsweet
Copy link
Collaborator Author

CUPS.org User: mike

Reassigned to -feature; this will not be in 1.2...

@michaelrsweet
Copy link
Collaborator Author

CUPS.org User: twaugh.redhat

I've attached a patch against 1.2.0 based on the original usb-libusb.c file. It doesn't attempt to read back-channel data.

@michaelrsweet
Copy link
Collaborator Author

CUPS.org User: twaugh.redhat

Oh, I should point out: I made it runtime configurable. Put 'Libusb On' in /etc/cups/usb.conf to make it use libusb.

@michaelrsweet
Copy link
Collaborator Author

CUPS.org User: till.kamppeter

I have tried to auto-detect some USB printers using the new backend in libusb mode (works correctly in usblp kernel module mode) and it is still buggy.

On the HP PhotoSmart 2600 the device ID is cut off and also the model name (which is taken from the device ID). It seems that the device ID is read into a too small buffer (exactly 32 characters). Device IDs can be long, the buffer should be dynamically sized or have at least 1024 characters.

Output on HP PhotoSmart 2600:

[root@majax c]# /usr/lib/cups/backend/usb
DEBUG: detaching kernel driver
direct usb://HP/Photosmart%202600%20serie "HP Photosmart 2600 serie" "HP Photosmart 2600 serie USB 054" "MFG:HP;MDL:Photosmart 2600 serie"
[root@majax c]#

In addition, the internal USB device number ("054" here) should not be included in the output. It changes every time when one disconnects or power-cycles the device.

On the HP PSC 950xi the backend was not able to read the device ID at all:

[root@majax c]# /usr/lib/cups/backend/usb
DEBUG: detaching kernel driver
DEBUG: Cannot get DeviceID string device 1008:7697
direct usb:001052 "Unknown" "USB Printer #1052"
[root@majax c]#

I did not get any better results by unloading the usblp kernel module, shutting down the HPLIP daemons and power-cycling the devices.

@michaelrsweet
Copy link
Collaborator Author

CUPS.org User: till.kamppeter

Printing also does not work with this backend:

[root@majax c]# lpinfo -v
...
direct usb://HP/Photosmart%202600%20serie
...
[root@majax c]# lpadmin -p libusb -E -v usb://HP/Photosmart%202600%20serie -m foomatic:HP-PhotoSmart_2600-hpijs.ppd -o PageSize=A4
[root@majax c]# lpr -P libusb /usr/share/cups/data/testprint.ps
[root@majax c]# lpstat -p libusb
printer libusb disabled since Fri 12 May 2006 09:07:21 PM CEST -
Unable to find USB device "usb://HP/Photosmart%202600%20serie"
[root@majax c]#

@michaelrsweet
Copy link
Collaborator Author

CUPS.org User: mike

New code from Couriousous that uses the direct kernel interface instead of libusb...

@michaelrsweet
Copy link
Collaborator Author

CUPS.org User: mike

Assigned to 1.3.

@michaelrsweet
Copy link
Collaborator Author

CUPS.org User: mike

We'll need a copy of the code that has an explicit copyright assignment to Apple now; none of the included code has any copyright notices... :(

Thanks...

@michaelrsweet
Copy link
Collaborator Author

CUPS.org User: mike

Pushing this to CUPS 1.4.

Ultimately, we need a copyright notice at the top, whether the original author assigns the copyright (or licenses the code) to Apple or we just have the standard GPL header with the author's copyright notice. Otherwise, we can't include the code in CUPS.

@michaelrsweet
Copy link
Collaborator Author

CUPS.org User: till.kamppeter

I have tried out Couriousous' alternative USB backend as there are several bug reports about USB printers not being detected correctly:

https://bugs.launchpad.net/ubuntu/+source/linux-source-2.6.22/+bug/35638
https://bugs.launchpad.net/ubuntu/+source/linux-source-2.6.22/+bug/113640
https://bugs.launchpad.net/ubuntu/+source/cupsys/+bug/135044

As one cannot exclude that the "usblp" kernel module is the culprit, I wanted to try whether surrounding the module could solve these problems. Therefore I tried to build CUPS 1.3.0 with usb-linux.c instead of usb-unix.c, the method of directly talking to the kernel, without libusb and without the "usblp" module.

There were some problems in the backend which I have fixed:

  • Made URIs being exactly the same as the original usb-unix.c-based backend produces.
  • Made the output of the call of the backend without command line options being exactly the same as the output of the original usb-unix.c-based backend.
  • Made the backend searching /dev/bus/usb also if an empty /proc/bus/usb directory exists.
  • Let usb-linux.c include the header for USB access from /usr/include/linux/..., not from the kernel headers.

The first two are to assure maximum compatibility to printer setup tools which are developed around the original backend (including the web interface). The third is a kernel bug workaround, as the kernel seems to make an empty /proc/bus/usb in the /proc file system. The last item makes the code simply more friendly for integrating in distros.

Installation:

Download usb-linux_v2.c from this STR and put it into the backend/ directory of the CUPS source tree. Edit backend/usb.c replacing '#include "usb-unix.c"' by '#include "usb-linux_v2.c"'. Have the "linux-libc-dev" package installed (or the one containing /usr/include/linux/usbdevice_fs.h and /usr/include/linux/usb/ch9.h). Now compile and install CUPS as usual.

Usage:

Uninstall the "usblp" kernel module via

rmmod usblp

Then CUPS should work as usual. The new USB backend does not detect any printers nor does it print with the "usblp" module loaded. Blacklist the module if you want to switch to this USB backend permanently.

Problems:

  • HAL (at least under Ubuntu Gutsy) uses the "usblp" kernel module to detect USB printers and automatic print queue setup via hal-cups-utils only works with the module loaded.
  • The new backend uses only one method and the original backend the other. Nice would be a merge of the two and the method is selected on whether the kernel module is loaded.

There is no problem with HPLIP. The HPLIP CUPS backend works independent whether the "usblp" module is loaded or not.

@michaelrsweet
Copy link
Collaborator Author

CUPS.org User: psmedley

Is this still planned to be implemented? OS/2 has libusb support but not usblp.

It would be nice to not have to code a native OS/2 usb backend and isntead use libusb :)

@michaelrsweet
Copy link
Collaborator Author

CUPS.org User: mike

Current trunk contains a basic libusb implementation - no side- or back-channel support yet,,.

@michaelrsweet
Copy link
Collaborator Author

CUPS.org User: mike

OK, I've implemented everything that will likely be implemented in CUPS 1.4. What is not implemented currently is back-channel support (STR #2890) and support for setting the bidirectional IO mode (STR #2869).

@michaelrsweet
Copy link
Collaborator Author

"usb-libusb-cups-1.2.patch":

--- /home/tkamppeter/usb-libusb.c 2006-04-20 16:42:32.000000000 +0200
+++ usb-libusb.c 2006-04-20 16:48:53.000000000 +0200
@@ -485,7 +485,9 @@
const char resource, / I - Resource/modelname /
const char *options, /
I - Device options/serial number /
int fp, /
I - File descriptor to print */

  •   int        copies)         /\* I - Copies to print */
    
  •    int        copies,         /\* I - Copies to print */
    
  •    int        argc,       /\* I - Number of command-line arguments (6 or 7) */
    
  •    char       _argv[])        /_ I - Command-line arguments */
    
    {
    struct usb_device *dev;
    usb_dev_handle *devhdl;

@michaelrsweet
Copy link
Collaborator Author

"cups-libusb.patch":

--- cups-1.2.0/backend/Makefile.libusb 2006-04-27 14:58:43.000000000 +0100
+++ cups-1.2.0/backend/Makefile 2006-05-12 12:41:01.000000000 +0100
@@ -198,7 +198,7 @@

usb: usb.o ../cups/$(LIBCUPS)
echo Linking $@...

  • $(CC) $(LDFLAGS) -o usb usb.o $(BACKLIBS) $(LIBS)
  • $(CC) $(LDFLAGS) -o usb usb.o $(BACKLIBS) $(LIBS) -lusb
    usb.o: usb.c usb-darwin.c usb-unix.c ieee1284.c

--- cups-1.2.0/backend/usb-unix.c.libusb 2006-05-12 12:22:58.000000000 +0100
+++ cups-1.2.0/backend/usb-unix.c 2006-05-12 16:26:33.000000000 +0100
@@ -32,19 +32,727 @@
*/

/*

  • * Extra SELinux policy required:

  • * allow cupsd_t usb_device_t:chr_file { ioctl read write };

  • /
    +
    +/

    • Include necessary headers.
      */

    #include "ieee1284.c"
    #include <sys/select.h>
    +#include <usb.h>
    +
    +#define LIBUSB_TIMEOUT 5000 /* 5 sec */
    +

+/*

  • * Local globals...
  • /
    +
    +static int use_libusb = 0;
    +static int have_read_usb_conf = 0;
    +
    +/
    list imported from usblp kernel module /
    +#define USBLP_QUIRK_BIDIR 0x1 /
    reports bidir but requires *
  •                \* unidirectional mode (no    *
    
  •                \* INs/reads) */
    
    +struct quirk_printer_struct {
  • u_int16_t vendorId;
  •    u_int16_t productId;
    
  • unsigned int quirks;
    +};

+static 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) */
    
  •    { 0, 0 }
    

    +};

    /*

    • Local functions...
      */

+static unsigned int
+usblp_quirks (u_int16_t vendor,

  •     u_int16_t 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;
    +}

int open_device(const char *uri);
+static usb_dev_handle *open_device_bydev(struct usb_device *dev,

  •                int conf,
    
  •                int interface,
    
  •                int alt_set);
    
    +/*
  • * 'read_usb_conf()' - Read the usb.conf file.
  • */
    +
    +static void
    +read_usb_conf(void)
    +{
  • cups_file_t fp; / File pointer */
  • char filename[1024], /* Filename */
  •   line[1024],     /\* Line from file */
    
  •   _value;         /_ Value on line */
    
  • int linenum; /* Line number */
  • const char cups_serverroot; / CUPS_SERVERROOT env var */
  • if (have_read_usb_conf)
  • return;
  • if ((cups_serverroot = getenv("CUPS_SERVERROOT")) == NULL)
  • cups_serverroot = CUPS_SERVERROOT;
  • snprintf(filename, sizeof(filename), "%s/usb.conf", cups_serverroot);
  • if ((fp = cupsFileOpen(filename, "r")) != NULL)
  • {
  • /*
  • * Read the snmp.conf file...
  • */
  • linenum = 0;
  • while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
  • {
  •  if (!value)
    
  •    fprintf(stderr, "ERROR: Missing value on line %d of %s!\n", linenum,
    
  •       filename);
    
  •  else if (!strcasecmp(line, "Libusb"))
    
  •    use_libusb = (!strcasecmp(value, "on") ||
    
  •         !strcasecmp(value, "yes") ||
    
  •         !strcasecmp(value, "true"));
    
  •  else
    
  •    fprintf(stderr, "ERROR: Unknown directive %s on line %d of %s!\n",
    
  •       line, linenum, filename);
    
  • }
  • cupsFileClose(fp);
  • }
  • have_read_usb_conf = 1;
    +}

+static int
+get_device_id_libusb(struct usb_device *dev,

  •        usb_dev_handle *devhdl,
    
  •        int conf,
    
  •        int interface,
    
  •        int alt_set,
    
  •        char *device_id,
    
  •        int device_id_size,
    
  •        char *make_model,
    
  •        int make_model_size,
    
  •        const char *scheme,
    
  •        char *uri,
    
  •        int uri_size)
    
    +{
  • struct usb_interface_descriptor *pi;
  • int ret;
  • int length;
  • pi = &dev->config[conf].interface[interface].altsetting[alt_set];
  • ret = usb_control_msg(devhdl,
  •       (USB_TYPE_CLASS | USB_ENDPOINT_IN |
    
  •        USB_RECIP_INTERFACE),
    
  •       0, 0,
    
  •       interface << 8 | pi->bAlternateSetting,
    
  •       device_id, device_id_size - 1, LIBUSB_TIMEOUT);
    
  • if (ret < 0)
  • return ret;
  • ////
  • // Copied from ieee1284.c's get_device_id(). That function needs
  • // splitting out into two parts.
  • ////
  • *device_id = '\0';
  • *make_model = '\0';
  • /*
  • * Extract the length of the device ID string from the first two
  • * bytes. The 1284 spec says the length is stored MSB first...
  • */
  • length = (((unsigned)device_id[0] & 255) << 8) +
  •    ((unsigned)device_id[1] & 255);
    
  • /*
  • * Check to see if the length is larger than our buffer; first
  • * assume that the vendor incorrectly implemented the 1284 spec,
  • * and then limit the length to the size of our buffer...
  • */
  • if (length > (device_id_size - 2))
  • length = (((unsigned)device_id[1] & 255) << 8) +
  •  ((unsigned)device_id[0] & 255);
    
  • if (length > (device_id_size - 2))
  • length = device_id_size - 2;
  • /*
  • * Copy the device ID text to the beginning of the buffer and
  • * nul-terminate.
  • */
  • memmove(device_id, device_id + 2, length);
  • device_id[length] = '\0';
  • if (!*device_id)
  • return (-1);
  • /*
  • * Get the make and model...
  • */
  • get_make_model(device_id, make_model, make_model_size);
  • /*
  • * Then generate a device URI...
  • */
  • if (scheme && uri && uri_size > 32)
  • {
  • char attr, / 1284 attribute */
  •   _delim,         /_ 1284 delimiter */
    
  •   _uriptr,        /_ Pointer into URI */
    
  •   manufacturer[256],  /\* Manufacturer string */
    
  •   serial_number[1024];    /\* Serial number string */
    
  • int manulen; /* Length of manufacturer string */
  • /*
  • \* Look for the serial number field...
    
  • */
    
  • if ((attr = strstr(device_id, "SERN:")) != NULL)
  •  attr += 5;
    
  • else if ((attr = strstr(device_id, "SERIALNUMBER:")) != NULL)
  •  attr += 13;
    
  • else if ((attr = strstr(device_id, ";SN:")) != NULL)
  •  attr += 4;
    
  • if (attr)
  • {
  •  strlcpy(serial_number, attr, sizeof(serial_number));
    
  •  if ((delim = strchr(serial_number, ';')) != NULL)
    
  • *delim = '\0';
  • }
  • else
  •  serial_number[0] = '\0';
    
  • /*
  • * Generate the device URI from the manufacturer, make_model, and
  • * serial number strings.
  • */
  • snprintf(uri, uri_size, "%s://", scheme);
  • if ((attr = strstr(device_id, "MANUFACTURER:")) != NULL)
  •  attr += 13;
    
  • else if ((attr = strstr(device_id, "Manufacturer:")) != NULL)
  •  attr += 13;
    
  • else if ((attr = strstr(device_id, "MFG:")) != NULL)
  •  attr += 4;
    
  • if (attr)
  • {
  •  strlcpy(manufacturer, attr, sizeof(manufacturer));
    
  •  if ((delim = strchr(manufacturer, ';')) != NULL)
    
  •    *delim = '\0';
    
  •  if (!strcasecmp(manufacturer, "Hewlett-Packard"))
    
  •    strcpy(manufacturer, "HP");
    
  • }
  • else
  • {
  •  strlcpy(manufacturer, make_model, sizeof(manufacturer));
    
  •  if ((delim = strchr(manufacturer, ' ')) != NULL)
    
  •    *delim = '\0';
    
  • }
  • manulen = strlen(manufacturer);
  • for (uriptr = uri + strlen(uri), delim = manufacturer;
  • *delim && uriptr < (uri + uri_size - 3);
  • delim ++)
  •  if (*delim == ' ')
    
  •  {
    
  • *uriptr++ = '%';
  • *uriptr++ = '2';
  • *uriptr++ = '0';
  •  }
    
  •  else
    
  • *uriptr++ = *delim;
  • *uriptr++ = '/';
  • if (!strncasecmp(make_model, manufacturer, manulen))
  • {
  •  delim = make_model + manulen;
    
  •  while (isspace(*delim & 255))
    
  •    delim ++;
    
  • }
  • else
  •  delim = make_model;
    
  • for (; *delim && uriptr < (uri + uri_size - 3); delim ++)
  •  if (*delim == ' ')
    
  •  {
    
  • *uriptr++ = '%';
  • *uriptr++ = '2';
  • *uriptr++ = '0';
  •  }
    
  •  else
    
  • *uriptr++ = *delim;
  • if (serial_number[0])
  • {
  • /*
    
  •  \* Add the serial number to the URI...
    
  •  */
    
  •  strlcpy(uriptr, "?serial=", uri_size - (uriptr - uri));
    
  •  strlcat(uriptr, serial_number, uri_size - (uriptr - uri));
    
  • }
  • else
  •  *uriptr = '\0';
    
  • }
  • return (0);
    +}

+static int
+check_detected_device(struct usb_device *dev,

  •         int conf,
    
  •         int interface,
    
  •         int alt_set,
    
  •         int ep,
    
  •         const void \* arg)
    
    +{
  • char device_id[1024], /* Device ID string */
  • device_uri[1024], /* Device URI string */
  • make_model[1024]; /* Make and model */
  • usb_dev_handle *devhdl;
  • devhdl = open_device_bydev(dev, conf, interface, alt_set);
  • if (!devhdl)
  • return -1;
  • if (get_device_id_libusb(dev, devhdl, conf, interface,
  •          alt_set,
    
  •          device_id, sizeof(device_id),
    
  •          make_model, sizeof(make_model),
    
  •          "usb", device_uri, sizeof(device_uri)) < 0)
    
  • {
  • fprintf(stderr, "DEBUG: Cannot get DeviceID string device %d:%d\n",
  •   dev->descriptor.idVendor, dev->descriptor.idProduct);
    
  • snprintf(device_uri, sizeof(device_uri), "usb:%s%s",
  •    dev->bus->dirname, dev->filename);
    
  • }
  • usb_close(devhdl);
  • if (!strcmp (device_uri, (char *) arg))
  • return 0;
  • return -1;
    +}

+/* this is a helper function which call a callback for every printer on the bus.

  • * usefull for seaching a device or listing all devices
  • * callback declaration
  • int cb(struct usb_device * dev, int conf, int interface, int alt_set, int ep, void * arg)
  • * arg is the "arg" argument supplied to find_device.
  • *
  • * when the callback return 0, the search is stopped and the function end
  • * return 0 when printer found( and argument are populated ), -1 otherwise
  • _/
    +static int
    +find_device(struct usb_device *_pdev,
  •   int *pconf,
    
  •   int *pinterface,
    
  •   int *palt_set,
    
  •   int *pep,
    
  •   int (*cb)(struct usb_device *, int, int, int, int, const void *),
    
  •   const void \* arg)
    
    +{
  • struct usb_bus *bus;
  • struct usb_device *dev;
  • for (bus = usb_get_busses (); bus; bus = bus->next)
  • {
  • for (dev = bus->devices; dev; dev = dev->next)
  • {
  •  int conf = 0;
    
  •  int alt_set = 0;
    
  •  int interface = 0;
    
  •  int protocol = 0;
    
  •  int found = 0;
    
  •  int ep = 0;
    
  •  /\* temporary struct to stock every protocol supported by printer so 
    
  •   \* we can choose the best one */
    
  •  struct
    
  •  {
    
  •     int epread;
    
  •     int epwrite;
    
  •     int alt_set;
    
  •  } dev_proto[3] =
    
  • {
  • {-1,-1,-1},{-1,-1,-1},{-1,-1,-1}
    
  • };
  •  if (!dev->config)
    
  • continue;
  •  if (dev->descriptor.idVendor == 0 || dev->descriptor.idProduct == 0)
    
  • /* root hub */
  • continue;
  •  for(conf = 0;
    
  • conf < dev->descriptor.bNumConfigurations && !found;
    
  • conf++)
    
  •  {
    
  • for (interface = 0;
  •    interface < dev->config[conf].bNumInterfaces && !found;
    
  •    interface++)
    
  • {
  • for (alt_set = 0; 
    
  •      alt_set < dev->config[conf].interface[interface].num_altsetting; 
    
  •      alt_set++)
    
  • {
    
  •   struct usb_interface_descriptor *pi;
    
  •   pi = &dev->config[conf].interface[interface].altsetting[alt_set];
    
  •   if (pi->bInterfaceClass != USB_CLASS_PRINTER ||
    
  •   pi->bInterfaceSubClass != 1)
    
  •     continue;
    
  •   protocol = pi->bInterfaceProtocol;
    
  •   if (protocol < 1 || protocol > 3)
    
  •     continue;
    
  •   dev_proto[protocol-1].alt_set = alt_set;
    
  •   /\* look for bulk write and read endpoint */ 
    
  •   for (ep = 0;
    
  •    ep < pi->bNumEndpoints;
    
  •    ep++)
    
  •   {
    
  •     struct usb_endpoint_descriptor *epd = &pi->endpoint[ep];
    
  •     if ((epd->bmAttributes & USB_ENDPOINT_TYPE_MASK)
    
  •    != USB_ENDPOINT_TYPE_BULK)
    
  •   continue;
    
  •     if (!(epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK))
    
  •     {
    
  •   if (dev_proto[protocol-1].epwrite == -1) 
    
  •     dev_proto[protocol-1].epwrite = ep;
    
  •     }
    
  •     else
    
  •     { 
    
  •   if (dev_proto[protocol-1].epread == -1)
    
  •     dev_proto[protocol-1].epread = ep;
    
  •     }
    
  •   }
    
  • }
    
  • /\* check if 7/1/2 is ok */
    
  • if (dev_proto[1].epread != -1 && dev_proto[1].epwrite != -1)
    
  • {
    
  •   alt_set = dev_proto[1].alt_set;
    
  •   ep = dev_proto[1].epwrite;
    
  •   found = 1;
    
  •   continue;
    
  • }
    
  • /\* check if 7/1/1 is ok */
    
  • if (dev_proto[0].epwrite != -1)
    
  • {
    
  •   alt_set = dev_proto[0].alt_set;
    
  •   ep = dev_proto[0].epwrite;
    
  •   found = 1;
    
  •   continue;
    
  • }
    
  • /\* check if 7/1/3 is ok */
    
  • if (dev_proto[2].epwrite != -1 && dev_proto[2].epread != -1)
    
  • {
    
  •   alt_set = dev_proto[2].alt_set;
    
  •   ep = dev_proto[2].epwrite;
    
  •   found = 1; 
    
  •   continue;
    
  • }
    
  • }
  •  }
    
  •  if (found)
    
  •  {
    
  • if (!cb(dev, conf - 1, interface - 1, alt_set, ep, arg))
  • {
  • if (pdev)
    
  •   *pdev = dev;
    
  • if (pconf)
    
  •   *pconf = conf - 1;
    
  • if (pinterface)
    
  •   *pinterface = interface - 1;
    
  • if (palt_set)
    
  •   *palt_set = alt_set;
    
  • if (pep)
    
  •   *pep = ep;
    
  • return 0;
    
  • }
  •  }
    
  • }
  • }
  • if (pdev)
  • *pdev = NULL;
  • if (pconf)
  • *pconf = 0;
  • if (pinterface)
  • *pinterface = 0;
  • if (palt_set)
  • *palt_set = 0;
  • if (pep)
  • *pep = 0;
  • return -1;
    +}

+static int
+device_get_status(usb_dev_handle *devhdl,

  •     int interface,
    
  •     int *status)
    
    +{
  • int ret;
  • ret = usb_control_msg(devhdl,
  •       (USB_TYPE_CLASS |
    
  •        USB_ENDPOINT_IN |
    
  •        USB_RECIP_INTERFACE),
    
  •       0x01, 0x0, interface, (char *)status,
    
  •       1, LIBUSB_TIMEOUT); 
    
  • if (ret < 0)
  • {
  • fprintf(stderr,"DEBUG: device_get_status failed\n");
  • return ret;
  • }
  • return 0;
    +}

+/*

  • * 'print_device_usb()' - Print a file using libusb.
  • /
    +static int /
    O - Exit status /
    +print_device_libusb(const char *uri, /
    I - Device URI */
  •       const char _hostname,/_ I- Hostname/manufacturer */
    
  •       const char _resource,/_ I- Resource/modelname */
    
  •       const char _options,/_ I - Device options/serial number */
    
  •       int        fp,  /\* I - File descriptor to print */
    
  •       int        copies,  /\* I - Copies to print */
    
  •       int argc,       /\* I - Number of command-line arguments (6 or 7) */
    
  •       char    _argv[])/_ I - Command-line arguments */
    
    +{
  • struct usb_interface_descriptor *pi;
  • struct usb_device *dev;
  • usb_dev_handle *devhdl;
  • int conf, interface, alt_set, status;
  • char buffer[8192]; /* Output buffer */
  • char bufptr; / Pointer into buffer */
  • size_t nbytes, tbytes;
  • int wbytes;
  • int epwrite;
  • int paperout = 0;
  • unsigned int quirks;
    +#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
  • struct sigaction action; /* Actions for POSIX signals /
    +#endif /
    HAVE_SIGACTION && !HAVE_SIGSET */
  • /*
  • * Open the USB port device...
  • */
  • fputs("STATE: +connecting-to-device\n", stderr);
  • usb_init();
  • usb_find_busses();
  • usb_find_devices();
  • if (find_device(&dev, &conf, &interface, &alt_set, &epwrite,
  •     check_detected_device, uri) != 0)
    
  • {
  • fprintf (stderr, "ERROR: Unable to find USB device "%s"\n", uri);
  • return CUPS_BACKEND_FAILED;
  • }
  • devhdl = open_device_bydev(dev, conf, interface, alt_set);
  • if (!devhdl)
  • {
  • fprintf(stderr, "ERROR: Unable to open USB device "%s"\n", uri);
  • return CUPS_BACKEND_FAILED;
  • }
  • fputs("STATE: -connecting-to-device\n", stderr);
  • quirks = usblp_quirks(dev->descriptor.idVendor,
  •       dev->descriptor.idProduct);
    
  • pi = &dev->config[conf].interface[interface].altsetting[alt_set];
  • epwrite = pi->endpoint[epwrite].bEndpointAddress;
  • while (!device_get_status(devhdl, interface, &status))
  • {
  • fprintf(stderr, "DEBUG: Got %02X status\n", status);
  • if (status & LP_POUTPA)
  • {
  •  fputs("WARNING: Out of paper!\n", stderr);
    
  •  fputs("STATUS: +media-tray-empty-error\n", stderr);
    
  •  paperout = 1;
    
  • }
  • if (!(status & LP_PERRORP))
  •  fputs("WARNING: Printer fault!\n", stderr);
    
  • else if (!(status & LP_PSELECD))
  •  fputs("WARNING: Printer off-line.\n", stderr);
    
  • else
  •  break;
    
  • sleep(5);
  • }
  • if (fp)
  • {
    +#ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
  • sigset(SIGTERM, SIG_IGN);
    +#elif defined(HAVE_SIGACTION)
  • memset(&action, 0, sizeof(action));
  • sigemptyset(&action.sa_mask);
  • action.sa_handler = SIG_IGN;
  • sigaction(SIGTERM, &action, NULL);
    +#else
  • signal(SIGTERM, SIG_IGN);
    +#endif /* HAVE_SIGSET */
  • }
  • wbytes = 0;
  • while (copies > 0)
  • {
  • copies --;
  • if (fp != 0)
  • {
  •  fputs("PAGE: 1 1\n", stderr);
    
  •  lseek(fp, 0, SEEK_SET);
    
  • }
  • tbytes = 0;
  • while ((nbytes = read(fp, buffer, sizeof(buffer))) > 0)
  • {
  •  /*
    
  •   \* Write the print data to the printer...
    
  •   */
    
  •  tbytes += nbytes;
    
  •  bufptr = buffer;
    
  •  while (nbytes > 0)
    
  •  {
    
  • wbytes = usb_bulk_write(devhdl, epwrite, bufptr,
  •           nbytes, LIBUSB_TIMEOUT);
    
  • if (wbytes < 0)
  • {
  • if (wbytes == -ETIMEDOUT)
    
  • {
    
  •   /\* We've timed out, so it's been 5 seconds now since we
    
  •    \* tried the write. */
    
  •   if (quirks & USBLP_QUIRK_BIDIR)
    
  •   {
    
  •     /\* Warning ! _BUG_ with some bidir HP printers !  They
    
  •      \* will put the status answer in the print buffer if it
    
  •      \* is full. On those printers, we will not ask the
    
  •      \* device status and will loop until it answer */
    
  •     fputs("WARNING: Printing is not responding! "
    
  •        "Will retry in 5 seonds\n", stderr);
    
  •     continue;
    
  •   }
    
  •   if (device_get_status(devhdl, interface, &status) == 0)
    
  •   {
    
  •     if (status & LP_POUTPA)
    
  •     {
    
  •   fputs("ERROR: Out of paper!\n", stderr);
    
  •   fputs("STATUS: +media-tray-empty-error\n", stderr);
    
  •   paperout = 1;
    
  •     }
    
  •     else if (!(status & LP_PERRORP))
    
  •     {
    
  •   fputs("WARNING: Printer fault!\n", stderr);
    
  •   break;
    
  •     }
    
  •     else if (!(status & LP_PSELECD))
    
  •     {
    
  •   fputs("WARNING: Printer off-line.\n", stderr);
    
  •   break;
    
  •     }
    
  •   }
    
  • }
    
  • }
  • else
  • {
  • if (paperout)
    
  • {
    
  •   fputs("STATUS: -media-tray-empty-error\n", stderr);
    
  •   paperout = 0;
    
  • }
    
  • nbytes -= wbytes;
    
  • bufptr += wbytes;
    
  • }
  •  }
    
  •  if (wbytes < 0)
    
  • break;
  •  if (fp)
    
  • fprintf(stderr, "INFO: Sending print file, %lu bytes...\n",
  •   (unsigned long)tbytes);
    
  • }
  • }
  • usb_close (devhdl);
  • return (wbytes < 0 ? CUPS_BACKEND_FAILED : CUPS_BACKEND_OK);
    +}

/*

  • 'print_device()' - Print a file to a USB device.
    @@ -82,6 +790,11 @@
    (void)argc;
    (void)argv;
  • read_usb_conf ();
  • if (use_libusb)
  • return print_device_libusb (uri, hostname, resource, options, fp, copies,
  •           argc, argv);
    
    /*
    • Open the USB port device...
      */
      @@ -322,6 +1035,153 @@
      return (wbytes < 0 ? CUPS_BACKEND_FAILED : CUPS_BACKEND_OK);
      }

+static usb_dev_handle *
+open_device_bydev(struct usb_device *dev,

  •     int conf,
    
  •     int interface,
    
  •     int alt_set)
    
    +{
  • usb_dev_handle *devhdl;
  • struct usb_interface_descriptor *pi;
  • devhdl = usb_open(dev);
  • if (!devhdl)
  • {
  • fprintf(stderr,"DEBUG: Cannot open device %d:%d\n",
  •   dev->descriptor.idVendor, dev->descriptor.idProduct);
    
  • return NULL;
  • }
  • pi = &dev->config[conf].interface[interface].altsetting[alt_set];
    +#ifdef LIBUSB_HAS_DETACH_KERNEL_DRIVER_NP
  • fprintf (stderr, "DEBUG: detaching kernel driver\n");
  • usb_detach_kernel_driver_np(devhdl, pi->bInterfaceNumber);
    +#endif
  • // This can sometimes fail (e.g. HP PSC devices). Ignore failures?
  • usb_set_configuration(devhdl, dev->config[conf].bConfigurationValue);
  • /* we always claim interface 0 even if the printer is not there,
  • we use it for control */
    
  • if (pi->bInterfaceNumber != 0)
  • {
  • while (usb_claim_interface (devhdl,pi->bInterfaceNumber) < 0)
  • {
  •  if (errno == EBUSY)
    
  •  {
    
  • fprintf(stderr,
  •   "INFO: USB printer is busy; will retry in 5 seconds...\n");
    
  • sleep(5);

+#ifdef LIBUSB_HAS_DETACH_KERNEL_DRIVER_NP

  • usb_detach_kernel_driver_np(devhdl, pi->bInterfaceNumber);
    +#endif
  • continue;
  •  }
    
  •  else
    
  •  {
    
  • fprintf(stderr, "DEBUG: Cannot claim interface %d device %d:%d\n",
  •   interface, dev->descriptor.idVendor,
    
  •   dev->descriptor.idProduct);
    
  • return NULL;
  •  }
    
  • }
  • }
  • while (usb_claim_interface (devhdl, 0) < 0)
  • {
  • if (errno == EBUSY)
  • {
  •  fprintf(stderr,
    
  •     "INFO: USB printer is busy; will retry in 5 seconds...\n");
    
  •  sleep(5);
    
    +#ifdef LIBUSB_HAS_DETACH_KERNEL_DRIVER_NP
  •  usb_detach_kernel_driver_np(devhdl, pi->bInterfaceNumber);
    
    +#endif
  •  continue;
    
  • }
  • else
  • {
  •  fprintf(stderr, "DEBUG: Cannot claim interface %d device %d:%d\n",
    
  •     interface, dev->descriptor.idVendor, dev->descriptor.idProduct);
    
  •  usb_close(devhdl);
    
  •  return NULL;
    
  • }
  • }
  • while (usb_set_altinterface(devhdl, pi->bAlternateSetting) < 0 )
  • {
  • if (errno == EBUSY)
  • {
  •  fprintf(stderr,
    
  •     "INFO: USB printer is busy; will retry in 5 seconds...\n");
    
  •  sleep(5);
    
    +#ifdef LIBUSB_HAS_DETACH_KERNEL_DRIVER_NP
  •  usb_detach_kernel_driver_np(devhdl, pi->bInterfaceNumber);
    
    +#endif
  •  continue;
    
  • }
  • else
  • {
  •  fprintf(stderr, "DEBUG: Cannot set alternate setting %d device %d:%d\n",
    
  •     alt_set, dev->descriptor.idVendor, dev->descriptor.idProduct);
    
  •  usb_close(devhdl);
    
  •  return NULL;
    
  • }
  • }
  • return devhdl;
    +}

+static int
+print_detected_device (struct usb_device *dev,

  •          int conf,
    
  •          int interface,
    
  •          int alt_set,
    
  •          int ep,
    
  •          const void \* arg)
    
    +{
  • usb_dev_handle *devhdl;
  • char device_id[1024], /* Device ID string */
  • device_uri[1024], /* Device URI string */
  • make_model[1024]; /* Make and model */
  • devhdl = open_device_bydev(dev, conf, interface, alt_set);
  • if (!devhdl)
  • return -1;
  • if (get_device_id_libusb(dev, devhdl, conf, interface,
  •          alt_set,
    
  •          device_id, sizeof(device_id),
    
  •          make_model, sizeof(make_model),
    
  •          "usb", device_uri, sizeof(device_uri)) < 0)
    
  • {
  • fprintf(stderr, "DEBUG: Cannot get DeviceID string device %d:%d\n",
  •   dev->descriptor.idVendor, dev->descriptor.idProduct);
    
  • printf("direct usb:%s%s "Unknown" "USB Printer #%s%s"\n",
  •  dev->bus->dirname, dev->filename, dev->bus->dirname, dev->filename);
    
  • }
  • else
  • printf("direct %s "%s" "%s USB %s" "%s"\n", device_uri,
  •  make_model, make_model, dev->filename, device_id);
    
  • usb_close(devhdl);
  • return -1;
    +}

+/*

  • * 'list_devices_libusb()' - List devices using libusb.
  • */
    +static void
    +list_devices_libusb(void)
    +{
  • usb_init();
  • usb_find_busses();
  • usb_find_devices();
  • find_device(NULL, NULL, NULL, NULL, NULL, print_detected_device, NULL);
    +}

/*

  • 'list_devices()' - List all USB devices.
    @@ -330,6 +1190,13 @@
    void
    list_devices(void)
    {
  • read_usb_conf ();
  • if (use_libusb)
  • {
  • list_devices_libusb ();
  • return;
  • }

#ifdef __linux
int i; /* Looping var /
int fd; /
File descriptor */

@michaelrsweet michaelrsweet added the enhancement New feature or request label Mar 17, 2016
@michaelrsweet michaelrsweet added this to the Stable milestone Mar 17, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

1 participant