Skip to content

Commit

Permalink
Added 'zlp-recv-hack' quirk
Browse files Browse the repository at this point in the history
Some enterprise-level HP devices, during the initialization phase
(which can last several minutes), may respond with an HTTP 503
status or similar, which is expected. However, the response body may
be truncated (typically, the terminating '\n' is lost). In such
cases, `ipp-usb` will wait indefinitely for a response to maintain
synchronization with the device.

At the same time, these devices send a zero-length UDP packet at the
end of the truncated output. If the zlp-recv-hack quirk is enabled,
when ipp-usb receives a zero-length packet from the USB followed by
a receive timeout, it interprets this combination of events as a
valid termination of the response body. It works only at the
initialization time and doesn't affect futher operations.

See discussion under #64 for details.

Related issues are #83 and #32.
  • Loading branch information
alexpevzner committed Dec 8, 2024
1 parent 2b88dca commit 078afca
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 0 deletions.
6 changes: 6 additions & 0 deletions ipp-usb.8
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,12 @@ Delay between subsequent requests\.
.br
Don't use more that N USB interfaces, even if more is available\.
.IP "\(bu" 4
\fBzlp\-recv\-hack = true | false\fR
.br
Some enterprise\-level HP devices, during the initialization phase (which can last several minutes), may respond with an HTTP 503 status or similar, which is expected\. However, the response body may be truncated (typically, the terminating '\en' is lost)\. In such cases, \fBipp\-usb\fR will wait indefinitely for a response to maintain synchronization with the device\.
.IP
At the same time, these devices send a zero\-length UDP packet at the end of the truncated output\. If the \fBzlp\-recv\-hack\fR quirk is enabled, when ipp\-usb receives a zero\-length packet from the USB followed by a receive timeout, it interprets this combination of events as a valid termination of the response body\. It works only at the initialization time and doesn't affect futher operations\.
.IP "\(bu" 4
\fBzlp\-send = true | false\fR
.br
Terminate outgoing transfers that a multiple of the endpoint's packet size win an extra zero length packet\.
Expand Down
15 changes: 15 additions & 0 deletions ipp-usb.8.md
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,21 @@ The following parameters are defined:
* `usb-max-interfaces = N`<br>
Don't use more that N USB interfaces, even if more is available.

* `zlp-recv-hack = true | false`<br>
Some enterprise-level HP devices, during the initialization phase
(which can last several minutes), may respond with an HTTP 503
status or similar, which is expected. However, the response body may
be truncated (typically, the terminating '\n' is lost). In such
cases, `ipp-usb` will wait indefinitely for a response to maintain
synchronization with the device.

At the same time, these devices send a zero-length UDP packet at the
end of the truncated output. If the `zlp-recv-hack` quirk is enabled,
when ipp-usb receives a zero-length packet from the USB followed by
a receive timeout, it interprets this combination of events as a
valid termination of the response body. It works only at the
initialization time and doesn't affect futher operations.

* `zlp-send = true | false`<br>
Terminate outgoing transfers that a multiple of the endpoint's
packet size win an extra zero length packet.
Expand Down
9 changes: 9 additions & 0 deletions quirks.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ const (
QuirkNmInitTimeout = "init-timeout"
QuirkNmRequestDelay = "request-delay"
QuirkNmUsbMaxInterfaces = "usb-max-interfaces"
QuirkNmZlpRecvHack = "zlp-recv-hack"
QuirkNmZlpSend = "zlp-send"
)

Expand All @@ -59,6 +60,7 @@ var quirkParse = map[string]func(*Quirk) error{
QuirkNmInitTimeout: (*Quirk).parseDuration,
QuirkNmRequestDelay: (*Quirk).parseDuration,
QuirkNmUsbMaxInterfaces: (*Quirk).parseUint,
QuirkNmZlpRecvHack: (*Quirk).parseBool,
QuirkNmZlpSend: (*Quirk).parseBool,
}

Expand All @@ -74,6 +76,7 @@ var quirkDefaultStrings = map[string]string{
QuirkNmInitTimeout: DevInitTimeout.String(),
QuirkNmRequestDelay: "0",
QuirkNmUsbMaxInterfaces: "0",
QuirkNmZlpRecvHack: "false",
QuirkNmZlpSend: "false",
}

Expand Down Expand Up @@ -347,6 +350,12 @@ func (quirks Quirks) GetUsbMaxInterfaces() uint {
return quirks.Get(QuirkNmUsbMaxInterfaces).Parsed.(uint)
}

// GetZlpRecvHack returns effective "zlp-send" parameter,
// taking the whole set into consideration.
func (quirks Quirks) GetZlpRecvHack() bool {
return quirks.Get(QuirkNmZlpRecvHack).Parsed.(bool)
}

// GetZlpSend returns effective "zlp-send" parameter,
// taking the whole set into consideration.
func (quirks Quirks) GetZlpSend() bool {
Expand Down
11 changes: 11 additions & 0 deletions quirks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,17 @@ func TestQuirksLookup(t *testing.T) {
origin: "default",
},

{
model: "Unknown Device",
param: QuirkNmZlpRecvHack,
get: func(quirks Quirks) interface{} {
return quirks.GetZlpRecvHack()
},
match: "*",
value: false,
origin: "default",
},

{
model: "Unknown Device",
param: QuirkNmZlpSend,
Expand Down
12 changes: 12 additions & 0 deletions usbtransport.go
Original file line number Diff line number Diff line change
Expand Up @@ -750,6 +750,10 @@ func (conn *usbConn) Read(b []byte) (int, error) {
b = b[0:n]
}

// zlp-recv-hack handling
zlpRecvHack := conn.transport.quirks.GetZlpRecvHack()
zlpRecv := false

// Setup deadline
backoff := time.Millisecond * 10
for {
Expand All @@ -767,6 +771,13 @@ func (conn *usbConn) Read(b []byte) (int, error) {
"USB[%d]: recv: %s", conn.index, err)

if err == context.DeadlineExceeded {
// If we've got read timeout preceded
// by the zero-length packet, interpret
// is as body EOF condition
if zlpRecvHack && zlpRecv {
return 0, io.EOF
}

atomic.StoreUint32(
&conn.transport.timeoutExpired, 1)
}
Expand All @@ -776,6 +787,7 @@ func (conn *usbConn) Read(b []byte) (int, error) {
return n, err
}

zlpRecv = true
conn.transport.log.Debug(' ',
"USB[%d]: zero-size read", conn.index)

Expand Down

0 comments on commit 078afca

Please sign in to comment.