fix USBDEVFS_DISCONNECT_CLAIM
This commit is contained in:
parent
88564d9837
commit
170f5dabcc
BIN
bin/usb-client
BIN
bin/usb-client
Binary file not shown.
BIN
bin/usb-relay
BIN
bin/usb-relay
Binary file not shown.
|
|
@ -27,24 +27,29 @@ 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 io_(typ, nr uintptr) uintptr { return ioc(iocNone, typ, nr, 0) }
|
||||
|
||||
// USBDEVFS_DISCONNECT_CLAIM flags
|
||||
const disconnectClaimIfDriver = 0x01
|
||||
|
||||
// USB device file system ioctl numbers
|
||||
var (
|
||||
usbdevfsControl = iowr('U', 0, unsafe.Sizeof(usbdevfsCtrlTransfer{}))
|
||||
usbdevfsBulk = iowr('U', 2, unsafe.Sizeof(usbdevfsBulkTransfer{}))
|
||||
usbdevfsSetInterface = ior('U', 4, unsafe.Sizeof(usbdevfsSetIntf{}))
|
||||
usbdevfsSetConfig = ior('U', 5, 4)
|
||||
usbdevfsSubmitURB = ior('U', 10, unsafe.Sizeof(usbdevfsURB{}))
|
||||
usbdevfsDiscardURB = io_('U', 11)
|
||||
usbdevfsReapURB = iow('U', 12, unsafe.Sizeof(uintptr(0)))
|
||||
usbdevfsReapURBNDelay = iow('U', 13, unsafe.Sizeof(uintptr(0)))
|
||||
usbdevfsClaimInterface = ior('U', 15, 4)
|
||||
usbdevfsControl = iowr('U', 0, unsafe.Sizeof(usbdevfsCtrlTransfer{}))
|
||||
usbdevfsBulk = iowr('U', 2, unsafe.Sizeof(usbdevfsBulkTransfer{}))
|
||||
usbdevfsSetInterface = ior('U', 4, unsafe.Sizeof(usbdevfsSetIntf{}))
|
||||
usbdevfsSetConfig = ior('U', 5, 4)
|
||||
usbdevfsSubmitURB = ior('U', 10, unsafe.Sizeof(usbdevfsURB{}))
|
||||
usbdevfsDiscardURB = io_('U', 11)
|
||||
usbdevfsReapURB = iow('U', 12, unsafe.Sizeof(uintptr(0)))
|
||||
usbdevfsReapURBNDelay = iow('U', 13, unsafe.Sizeof(uintptr(0)))
|
||||
usbdevfsClaimInterface = ior('U', 15, 4)
|
||||
usbdevfsReleaseInterface = ior('U', 16, 4)
|
||||
usbdevfsReset = io_('U', 20)
|
||||
usbdevfsClearHalt = ior('U', 21, 4)
|
||||
usbdevfsDisconnect = io_('U', 22)
|
||||
usbdevfsConnect = io_('U', 23)
|
||||
usbdevfsGetCapabilities = ior('U', 26, 4)
|
||||
usbdevfsGetSpeed = io_('U', 31)
|
||||
usbdevfsIoctl = iowr('U', 18, unsafe.Sizeof(usbdevfsIoctlArg{}))
|
||||
usbdevfsReset = io_('U', 20)
|
||||
usbdevfsClearHalt = ior('U', 21, 4)
|
||||
usbdevfsDisconnect = io_('U', 22)
|
||||
usbdevfsConnect = io_('U', 23)
|
||||
usbdevfsGetCapabilities = ior('U', 26, 4)
|
||||
usbdevfsDisconnectClaim = ior('U', 27, unsafe.Sizeof(usbdevfsDisconnectClaimArg{}))
|
||||
usbdevfsGetSpeed = io_('U', 31)
|
||||
)
|
||||
|
||||
// URB type constants
|
||||
|
|
@ -85,6 +90,20 @@ type usbdevfsISOPacketDesc struct {
|
|||
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 uint8
|
||||
Endpoint uint8
|
||||
|
|
@ -150,6 +169,35 @@ func (h *DeviceHandle) ConnectDriver() error {
|
|||
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
|
||||
func (h *DeviceHandle) ClaimInterface(ifnum uint32) error {
|
||||
_, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(h.fd), usbdevfsClaimInterface, uintptr(unsafe.Pointer(&ifnum)))
|
||||
|
|
|
|||
|
|
@ -51,19 +51,36 @@ func (s *Server) Attach() error {
|
|||
}
|
||||
s.handle = handle
|
||||
|
||||
// Disconnect kernel drivers from all interfaces
|
||||
// For each interface: disconnect kernel driver and claim it
|
||||
for _, iface := range s.device.Interfaces {
|
||||
if iface.Driver != "" && iface.Driver != "(none)" {
|
||||
// Try to disconnect - ignore errors for already-disconnected interfaces
|
||||
handle.DisconnectDriver()
|
||||
}
|
||||
}
|
||||
ifnum := uint32(iface.Number)
|
||||
|
||||
// Claim all interfaces
|
||||
for _, iface := range s.device.Interfaces {
|
||||
if err := handle.ClaimInterface(uint32(iface.Number)); err != nil {
|
||||
log.Printf("[usbip-server] warning: could not claim interface %d: %v", iface.Number, err)
|
||||
// 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
|
||||
}
|
||||
|
||||
// Fallback: disconnect driver per interface, then claim
|
||||
if disconnErr := handle.DisconnectDriverForInterface(ifnum); disconnErr != nil {
|
||||
log.Printf("[usbip-server] interface %d: disconnect warning: %v", ifnum, disconnErr)
|
||||
}
|
||||
|
||||
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
|
||||
|
|
|
|||
Loading…
Reference in New Issue