Skip to content

Commit

Permalink
Refactoring: use context.Context to specify low-level USB I/O timeout
Browse files Browse the repository at this point in the history
As now low-level USB send and receive operations are cancelable
via context.Context, there is no need to provide a separate
timeout parameter.
  • Loading branch information
alexpevzner committed Dec 2, 2024
1 parent 0c8234a commit b4cac28
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 23 deletions.
24 changes: 11 additions & 13 deletions usbio_libusb.go
Original file line number Diff line number Diff line change
Expand Up @@ -218,21 +218,19 @@ func libusbTransferStatusDecode(ctx context.Context,

case C.LIBUSB_TRANSFER_CANCELLED:
switch {
case ctx.Err() == context.DeadlineExceeded:
// There may be a race between context.Context
// expiration and libusb timeout. Be consistent
// in returned error code.
rc = C.LIBUSB_ERROR_TIMEOUT
case ctx.Err() != nil:
return 0, ctx.Err()
default:
rc = C.LIBUSB_ERROR_IO
}

// Handle other cases
case C.LIBUSB_TRANSFER_TIMED_OUT:
rc = C.LIBUSB_ERROR_TIMEOUT
// There may be a race between context.Context
// expiration and libusb timeout. Be consistent
// in returned error.
return 0, context.DeadlineExceeded

// Handle other cases
case C.LIBUSB_TRANSFER_STALL:
rc = C.LIBUSB_ERROR_PIPE

Expand Down Expand Up @@ -761,8 +759,8 @@ func (iface *UsbInterface) SoftReset() error {

// Send data to interface. Returns count of bytes actually transmitted
// and error, if any
func (iface *UsbInterface) Send(ctx context.Context, data []byte,
timeout time.Duration) (n int, err error) {
func (iface *UsbInterface) Send(ctx context.Context,
data []byte) (n int, err error) {

// Allocate a libusb_transfer.
xfer, doneChan, err := libusbTransferAlloc()
Expand All @@ -781,7 +779,7 @@ func (iface *UsbInterface) Send(ctx context.Context, data []byte,
C.int(len(data)),
C.libusb_transfer_cb_fn(unsafe.Pointer(C.libusbTransferCallback)),
nil,
C.uint(timeout/time.Millisecond),
0,
)

// Submit transfer and wait for completion
Expand All @@ -805,8 +803,8 @@ func (iface *UsbInterface) Send(ctx context.Context, data []byte,
//
// Note, if data size is not 512-byte aligned, and device has more data,
// that fits the provided buffer, LIBUSB_ERROR_OVERFLOW error may occur
func (iface *UsbInterface) Recv(ctx context.Context, data []byte,
timeout time.Duration) (n int, err error) {
func (iface *UsbInterface) Recv(ctx context.Context,
data []byte) (n int, err error) {

// Some versions of Linux kernel don't allow bulk transfers to
// be larger that 16kb per URB, and libusb uses some smart-ass
Expand Down Expand Up @@ -836,7 +834,7 @@ func (iface *UsbInterface) Recv(ctx context.Context, data []byte,
C.int(len(data)),
C.libusb_transfer_cb_fn(unsafe.Pointer(C.libusbTransferCallback)),
nil,
C.uint(timeout/time.Millisecond),
0,
)

// Submit transfer and wait for completion
Expand Down
34 changes: 24 additions & 10 deletions usbtransport.go
Original file line number Diff line number Diff line change
Expand Up @@ -729,14 +729,17 @@ func (conn *usbConn) Read(b []byte) (int, error) {
b = b[0:n]
}

// Setup deadline
ctx := context.Background()
if !conn.transport.deadline.IsZero() {
var cancel context.CancelFunc
ctx, cancel = context.WithDeadline(ctx, conn.transport.deadline)
defer cancel()
}

backoff := time.Millisecond * 10
for {
tm, expired := conn.timeout()
if expired {
return 0, ErrInitTimedOut
}

n, err := conn.iface.Recv(context.Background(), b, tm)
n, err := conn.iface.Recv(ctx, b)
conn.cntRecv += n

conn.transport.log.Add(LogTraceHTTP, '<',
Expand All @@ -748,6 +751,10 @@ func (conn *usbConn) Read(b []byte) (int, error) {
if err != nil {
conn.transport.log.Error('!',
"USB[%d]: recv: %s", conn.index, err)

if err == context.DeadlineExceeded {
err = ErrInitTimedOut
}
}

if n != 0 || err != nil {
Expand All @@ -769,12 +776,15 @@ func (conn *usbConn) Write(b []byte) (int, error) {
conn.transport.connstate.beginWrite(conn)
defer conn.transport.connstate.doneWrite(conn)

tm, expired := conn.timeout()
if expired {
return 0, ErrInitTimedOut
// Setup deadline
ctx := context.Background()
if !conn.transport.deadline.IsZero() {
var cancel context.CancelFunc
ctx, cancel = context.WithDeadline(ctx, conn.transport.deadline)
defer cancel()
}

n, err := conn.iface.Send(context.Background(), b, tm)
n, err := conn.iface.Send(context.Background(), b)
conn.cntSent += n

conn.transport.log.Add(LogTraceHTTP, '>',
Expand All @@ -786,6 +796,10 @@ func (conn *usbConn) Write(b []byte) (int, error) {
if err != nil {
conn.transport.log.Error('!',
"USB[%d]: send: %s", conn.index, err)

if err == context.DeadlineExceeded {
err = ErrInitTimedOut
}
}

return n, err
Expand Down

0 comments on commit b4cac28

Please sign in to comment.