diff --git a/bin/usb-client b/bin/usb-client index 59653e1..679a21c 100755 Binary files a/bin/usb-client and b/bin/usb-client differ diff --git a/bin/usb-relay b/bin/usb-relay index 051bc20..a803d9a 100755 Binary files a/bin/usb-relay and b/bin/usb-relay differ diff --git a/internal/usbip/server.go b/internal/usbip/server.go index 281449f..9f62f6a 100644 --- a/internal/usbip/server.go +++ b/internal/usbip/server.go @@ -360,21 +360,59 @@ func (s *Server) handleCmdSubmit(r io.Reader, hdr *URBHeader, retChan chan<- []b } if endpoint == 0 && hdr.Direction == DirOut { - buf := transferBuf - if buf == nil { - buf = make([]byte, 0) - } - _, err := s.handle.ControlTransfer( - body.Setup[0], body.Setup[1], - binary.LittleEndian.Uint16(body.Setup[2:4]), - binary.LittleEndian.Uint16(body.Setup[4:6]), - binary.LittleEndian.Uint16(body.Setup[6:8]), - 5000, buf, - ) + bmRequestType := body.Setup[0] + bRequest := body.Setup[1] + wValue := binary.LittleEndian.Uint16(body.Setup[2:4]) + wIndex := binary.LittleEndian.Uint16(body.Setup[4:6]) + var status int32 - if err != nil { - status = -32 // -EPIPE + + // Intercept standard USB requests that require special usbdevfs ioctls. + // Raw control transfers via USBDEVFS_CONTROL don't update kernel state. + switch { + case bmRequestType == 0x01 && bRequest == 0x0B: + // SET_INTERFACE (Standard, Interface recipient) + // MUST use USBDEVFS_SETINTERFACE so the kernel updates endpoint state + // and allocates bandwidth for ISO endpoints (critical for webcams). + if err := s.handle.SetInterface(uint32(wIndex), uint32(wValue)); err != nil { + log.Printf("[usbip-server] SET_INTERFACE(iface=%d, alt=%d) failed: %v", wIndex, wValue, err) + status = -32 // -EPIPE + } else { + log.Printf("[usbip-server] SET_INTERFACE(iface=%d, alt=%d) OK", wIndex, wValue) + } + + case bmRequestType == 0x00 && bRequest == 0x09: + // SET_CONFIGURATION (Standard, Device recipient) + if err := s.handle.SetConfiguration(uint32(wValue)); err != nil { + log.Printf("[usbip-server] SET_CONFIGURATION(%d) failed: %v", wValue, err) + status = -32 + } else { + log.Printf("[usbip-server] SET_CONFIGURATION(%d) OK", wValue) + } + + case bmRequestType == 0x02 && bRequest == 0x01 && wValue == 0x0000: + // CLEAR_FEATURE(ENDPOINT_HALT) (Standard, Endpoint recipient) + if err := s.handle.ClearHalt(uint32(wIndex)); err != nil { + log.Printf("[usbip-server] CLEAR_HALT(ep=0x%02x) failed: %v", wIndex, err) + status = -32 + } + + default: + // Generic OUT control transfer + buf := transferBuf + if buf == nil { + buf = make([]byte, 0) + } + _, err := s.handle.ControlTransfer( + bmRequestType, bRequest, wValue, wIndex, + binary.LittleEndian.Uint16(body.Setup[6:8]), + 5000, buf, + ) + if err != nil { + status = -32 // -EPIPE + } } + resp, err := BuildRetSubmit(hdr.SeqNum, hdr.DevID, hdr.Direction, hdr.Endpoint, status, nil) if err != nil { return err @@ -388,8 +426,11 @@ func (s *Server) handleCmdSubmit(r io.Reader, hdr *URBHeader, retChan chan<- []b ep |= 0x80 } - // Handle isochronous transfers - if urbType == 0 && numPackets > 0 { + // Handle isochronous transfers. + // Trust the USB/IP NumberOfPackets field rather than our endpoint type map, + // because the map is built at enumeration time (alternate setting 0) and + // webcams only activate ISO endpoints after SET_INTERFACE to alt > 0. + if numPackets > 0 { return s.handleISOSubmit(hdr, body, transferBuf, isoDescs, numPackets, ep, retChan) }