fix USBDEVFS_DISCONNECT_CLAIM

This commit is contained in:
2026-02-18 22:24:54 +01:00
parent 88564d9837
commit 170f5dabcc
4 changed files with 90 additions and 25 deletions
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+48
View File
@@ -27,6 +27,9 @@ func iow(typ, nr, size uintptr) uintptr { return ioc(iocWrite, typ, nr, size) }
func iowr(typ, nr, size uintptr) uintptr { return ioc(iocRead|iocWrite, typ, nr, size) } func iowr(typ, nr, size uintptr) uintptr { return ioc(iocRead|iocWrite, typ, nr, size) }
func io_(typ, nr uintptr) uintptr { return ioc(iocNone, typ, nr, 0) } func io_(typ, nr uintptr) uintptr { return ioc(iocNone, typ, nr, 0) }
// USBDEVFS_DISCONNECT_CLAIM flags
const disconnectClaimIfDriver = 0x01
// USB device file system ioctl numbers // USB device file system ioctl numbers
var ( var (
usbdevfsControl = iowr('U', 0, unsafe.Sizeof(usbdevfsCtrlTransfer{})) usbdevfsControl = iowr('U', 0, unsafe.Sizeof(usbdevfsCtrlTransfer{}))
@@ -39,11 +42,13 @@ var (
usbdevfsReapURBNDelay = iow('U', 13, unsafe.Sizeof(uintptr(0))) usbdevfsReapURBNDelay = iow('U', 13, unsafe.Sizeof(uintptr(0)))
usbdevfsClaimInterface = ior('U', 15, 4) usbdevfsClaimInterface = ior('U', 15, 4)
usbdevfsReleaseInterface = ior('U', 16, 4) usbdevfsReleaseInterface = ior('U', 16, 4)
usbdevfsIoctl = iowr('U', 18, unsafe.Sizeof(usbdevfsIoctlArg{}))
usbdevfsReset = io_('U', 20) usbdevfsReset = io_('U', 20)
usbdevfsClearHalt = ior('U', 21, 4) usbdevfsClearHalt = ior('U', 21, 4)
usbdevfsDisconnect = io_('U', 22) usbdevfsDisconnect = io_('U', 22)
usbdevfsConnect = io_('U', 23) usbdevfsConnect = io_('U', 23)
usbdevfsGetCapabilities = ior('U', 26, 4) usbdevfsGetCapabilities = ior('U', 26, 4)
usbdevfsDisconnectClaim = ior('U', 27, unsafe.Sizeof(usbdevfsDisconnectClaimArg{}))
usbdevfsGetSpeed = io_('U', 31) usbdevfsGetSpeed = io_('U', 31)
) )
@@ -85,6 +90,20 @@ type usbdevfsISOPacketDesc struct {
Status uint32 Status uint32
} }
// usbdevfsIoctlArg is the argument for USBDEVFS_IOCTL (per-interface sub-ioctl)
type usbdevfsIoctlArg struct {
Ifno int32
IoctlCode int32
Data uintptr
}
// usbdevfsDisconnectClaimArg is the argument for USBDEVFS_DISCONNECT_CLAIM
type usbdevfsDisconnectClaimArg struct {
Interface uint32
Flags uint32
Driver [256]byte
}
type usbdevfsURB struct { type usbdevfsURB struct {
Type uint8 Type uint8
Endpoint uint8 Endpoint uint8
@@ -150,6 +169,35 @@ func (h *DeviceHandle) ConnectDriver() error {
return nil return nil
} }
// DisconnectDriverForInterface disconnects the kernel driver from a specific interface
// Uses USBDEVFS_IOCTL with USBDEVFS_DISCONNECT sub-ioctl
func (h *DeviceHandle) DisconnectDriverForInterface(ifnum uint32) error {
arg := usbdevfsIoctlArg{
Ifno: int32(ifnum),
IoctlCode: int32(usbdevfsDisconnect),
Data: 0,
}
_, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(h.fd), usbdevfsIoctl, uintptr(unsafe.Pointer(&arg)))
if errno != 0 && errno != unix.ENODATA { // ENODATA = no driver bound, that's OK
return fmt.Errorf("USBDEVFS_IOCTL(DISCONNECT, iface %d): %w", ifnum, errno)
}
return nil
}
// DisconnectClaimInterface atomically disconnects kernel driver and claims an interface
// Uses USBDEVFS_DISCONNECT_CLAIM (available since Linux 3.7)
func (h *DeviceHandle) DisconnectClaimInterface(ifnum uint32) error {
arg := usbdevfsDisconnectClaimArg{
Interface: ifnum,
Flags: disconnectClaimIfDriver,
}
_, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(h.fd), usbdevfsDisconnectClaim, uintptr(unsafe.Pointer(&arg)))
if errno != 0 {
return fmt.Errorf("USBDEVFS_DISCONNECT_CLAIM(%d): %w", ifnum, errno)
}
return nil
}
// ClaimInterface claims exclusive access to a USB interface // ClaimInterface claims exclusive access to a USB interface
func (h *DeviceHandle) ClaimInterface(ifnum uint32) error { func (h *DeviceHandle) ClaimInterface(ifnum uint32) error {
_, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(h.fd), usbdevfsClaimInterface, uintptr(unsafe.Pointer(&ifnum))) _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(h.fd), usbdevfsClaimInterface, uintptr(unsafe.Pointer(&ifnum)))
+26 -9
View File
@@ -51,19 +51,36 @@ func (s *Server) Attach() error {
} }
s.handle = handle s.handle = handle
// Disconnect kernel drivers from all interfaces // For each interface: disconnect kernel driver and claim it
for _, iface := range s.device.Interfaces { for _, iface := range s.device.Interfaces {
if iface.Driver != "" && iface.Driver != "(none)" { ifnum := uint32(iface.Number)
// Try to disconnect - ignore errors for already-disconnected interfaces
handle.DisconnectDriver() // Try atomic disconnect+claim first (USBDEVFS_DISCONNECT_CLAIM, Linux 3.7+)
} err := handle.DisconnectClaimInterface(ifnum)
if err == nil {
log.Printf("[usbip-server] interface %d: disconnect+claim OK", ifnum)
continue
} }
// Claim all interfaces // Fallback: disconnect driver per interface, then claim
for _, iface := range s.device.Interfaces { if disconnErr := handle.DisconnectDriverForInterface(ifnum); disconnErr != nil {
if err := handle.ClaimInterface(uint32(iface.Number)); err != nil { log.Printf("[usbip-server] interface %d: disconnect warning: %v", ifnum, disconnErr)
log.Printf("[usbip-server] warning: could not claim interface %d: %v", iface.Number, err)
} }
if claimErr := handle.ClaimInterface(ifnum); claimErr != nil {
log.Printf("[usbip-server] error: could not claim interface %d: %v", ifnum, claimErr)
// This is a critical error - clean up and fail
for _, prev := range s.device.Interfaces {
if uint32(prev.Number) < ifnum {
handle.ReleaseInterface(uint32(prev.Number))
}
}
handle.ConnectDriver()
handle.Close()
s.handle = nil
return fmt.Errorf("claiming interface %d: %w", ifnum, claimErr)
}
log.Printf("[usbip-server] interface %d: fallback disconnect+claim OK", ifnum)
} }
return nil return nil