OpenPrinting / ipp-usb

ipp-usb -- HTTP reverse proxy, backed by IPP-over-USB connection to device
BSD 2-Clause "Simplified" License
135 stars 14 forks source link

HP Color LaserJet Enterprise MFP M578 detection during printer start up #64

Open fabled opened 1 year ago

fabled commented 1 year ago

Somewhat related to #32, I detected additional issues in this HP printer.

When the printer is starting up, it behaves as follows during early boot:

This results ipp-usb to reset and retry device initialization. This happens because the EsclService init times out, and the error condition in https://github.com/OpenPrinting/ipp-usb/blob/master/device.go#L120 is triggered.

Later during the startup sequence, the scanner side starts first responding with success, but IPP printer still gives 503. During this condition, ipp-usb is left in hung state. The printer is not detected, because the attribute reading aborts early in https://github.com/OpenPrinting/ipp-usb/blob/master/ipp.go#L155 when error is received. Therefor no timeout happens, and https://github.com/OpenPrinting/ipp-usb/blob/master/usbtransport.go#L550 triggers. This tries to indefinitely drain the response which never comes and the channels is left stuck. (Recovery can be done with restart ipp-usb, or disconnect/connect USB cable.)

Perhaps the device init logic could be adjusted to automatically always do timeout retry if ipp return 503 error (either directly, or via quirk).

I wonder also if the response draining could be also also adjusted to cope better with printer bugs such as this one.

alexpevzner commented 1 year ago

Hi Timo,

Perhaps the device init logic could be adjusted to automatically always do timeout retry if ipp return 503 error (either directly, or via quirk).

Looks like it is not enough, because regardless of status, if ipp-usb can't fetch the entire response, because trailing \n is missed, it will stuck anyway.

May be, for this kind of device, it will be better to simply delay initialization after device was connected, based on quirk?

alexpevzner commented 1 year ago

BTW, ipp-usb already has init-delay quirk, so the idea to delay initialization is simple to test...

fabled commented 1 year ago

Perhaps the device init logic could be adjusted to automatically always do timeout retry if ipp return 503 error (either directly, or via quirk).

Looks like it is not enough, because regardless of status, if ipp-usb can't fetch the entire response, because trailing \n is missed, it will stuck anyway.

As workaround, I'm using now:

--- a/device.go
+++ b/device.go
@@ -84,6 +84,10 @@ func NewDevice(desc UsbDeviceDesc) (*Device, error) {

        if err != nil {
                dev.Log.Error('!', "IPP: %s", err)
+               if strings.StartsWith(err.Error(), "HTTP: 503") {
+                       err = ErrInitTimedOut
+                       goto ERROR
+               }
        }

        log.Flush()

This works because the 503 triggers a device init retry doing a reset. Not perhaps the most elegant, but works.

In any case, not receiving the trailing \n should be eventually timed out. Seems there's too many buggy printers like that out there. Perhaps receiving few zero-length USB URBs should be sufficient for that.

May be, for this kind of device, it will be better to simply delay initialization after device was connected, based on quirk?

This is not a real option. When the device is connected via cable, and you hit power on. It takes about 1-2 minutes for the USB interface to come up, and then it takes 4-6 minutes for the IPP layers to be up. For those 4-6 minutes I see the 503 error.

On the other hand, if the printer is online, and I connect the cable, everything works immediately.

Adding a 6 minute init-delay that is needed only if the device is cold starting, does not sound feasible to me.

alexpevzner commented 1 year ago

Adding a 6 minute init-delay that is needed only if the device is cold starting, does not sound feasible to me.

Yes, 6 minutes looks a little bit too much to wait.

If we suppress the following logic in the device.go, line 144, won't it be enough for this device:

        if len(dnssdServices) == 0 {
                err = ErrUnusable
                goto ERROR
        }
fabled commented 1 year ago

If we suppress the following logic in the device.go, line 144, won't it be enough for this device:

No. See first comment. During cold boot, there in the end will be a state that the printer query will return 503, but scanner will return 200. During this stage it will permanently detect the MFP as having scanner only and printing will not be available.