Samsung Linux CUPS USB Printing

By Henry, on October 19th, 2011

I have two Samsung USB laser printers connected to the same machine:

ML-1740

ML-2010

Ever since upgrading from Mandriva 2010.0(?) to 2010.1 (and also 2010.2), both printers have been intermittent. Print jobs would often be silently discarded. CUPS logs show that the print jobs are completed, the printer would warm up, the printer’s LED blinks once or twice, then the print job is “complete”. But nothing gets printed. Unplugging the USB cable will allow printing at least one print job, but sending usb_clear_halt, a printer class SOFT_RESET, nor usb_reset() will get the printer to work.

Since the same behaviour occurs for both printers, and started at the same time (after an upgrade), it’s probably not a random hardware failure.

The common thing in all of these seem to be printing with CUPS using libusb with a Samsung printer (Mandriva removed usblp somewhere around 2010.1). Indeed, the Archlinux thread suggests that moving back to usblp printing solves the problem.

After tons of debugging, the problem appears to be a problem (flaw?) with Samsung’s firmware interacting with some behaviour in the CUPS USB (usb-libusb.c) backend. Hint: usbmon with Wireshark is very useful for capturing USB traffic.

In the current USB (usb-libusb.c) backend, a typical print job performs the following steps:

Loops through all USB buses, devices, configurations, and interfaces, to look for USB printer class interfaces, sends a printer class GET_DEVICE_ID request to it, and tries to find the desired printer based on the returned string.

Opens the device (usb_open)

Sends a Set Configuration request to the device to set it to the desired configuration. (usb_set_configuration)

Claims the desired interface (usb_claim_interface)

Sends a Set Interface request to the device to choose the desired interface. (usb_set_altinterface)

Dumps the print data in 8,192-byte chunks in one or more(?) USB bulk transactions using usb_bulk_write

Releases interfaces that were claimed earlier (usb_release_interface)

Close the device (usb_close)

On my printers (and I expect most other printers too), steps 3 and 5 aren’t really useful. There is only one configuration (bConfiguration=1) and only one interface (bAlternateSetting=0) for that configuration. Normally some part of the OS already sends a Set Configuration to put the device into the Configured state (see USB spec) when the device is plugged in, so there is no real need to again set the configuration to the current value. Similarly, if there is only one altsetting, it is the default and there is no real use to setting it.

According to the USB spec, the above sequence of operations, including setting the configuration and altsetting after the device is already configured, is legal. However, it seems like my Samsung printers do not want to see steps 3 and 5 happen, even though the printer returns a success return code for those requests. It is the presence of the redundant Set Configuration and Set Interface requests that appear to cause the subsequent print job to sometimes/usually be silently discarded. Removing both those requests causes the printer to behave normally.

It appears usblp’s print sequence is much simpler: It sends a GET_DEVICE_ID to the printer, then dumps the print data to the printer using a USB bulk transfer. It does not set configuration or interface, perhaps (correctly) assuming that it was already done when the printer was first enumerated by usblp. This difference would explain why CUPS-usblp works fine but CUPS-libusb does not. However, since both CUPS-usblp and CUPS-libusb follow the USB spec, it’s likely Samsung’s firmware that is flawed here.

One way to work around the problem is to simplify CUPS’s usb-libusb backend to not send Set Configuration or Set Interface requests when it’s not necessary. Samsung printers don’t seem to have a problem with Get Configuration requests, so I first query the current configuration, then change configurations only if the desired configuration is different from the current one.