fix USBDEVFS_DISCONNECT_CLAIM
This commit is contained in:
Binary file not shown.
Binary file not shown.
+63
-15
@@ -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 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{}))
|
||||||
usbdevfsBulk = iowr('U', 2, unsafe.Sizeof(usbdevfsBulkTransfer{}))
|
usbdevfsBulk = iowr('U', 2, unsafe.Sizeof(usbdevfsBulkTransfer{}))
|
||||||
usbdevfsSetInterface = ior('U', 4, unsafe.Sizeof(usbdevfsSetIntf{}))
|
usbdevfsSetInterface = ior('U', 4, unsafe.Sizeof(usbdevfsSetIntf{}))
|
||||||
usbdevfsSetConfig = ior('U', 5, 4)
|
usbdevfsSetConfig = ior('U', 5, 4)
|
||||||
usbdevfsSubmitURB = ior('U', 10, unsafe.Sizeof(usbdevfsURB{}))
|
usbdevfsSubmitURB = ior('U', 10, unsafe.Sizeof(usbdevfsURB{}))
|
||||||
usbdevfsDiscardURB = io_('U', 11)
|
usbdevfsDiscardURB = io_('U', 11)
|
||||||
usbdevfsReapURB = iow('U', 12, unsafe.Sizeof(uintptr(0)))
|
usbdevfsReapURB = iow('U', 12, unsafe.Sizeof(uintptr(0)))
|
||||||
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)
|
||||||
usbdevfsReset = io_('U', 20)
|
usbdevfsIoctl = iowr('U', 18, unsafe.Sizeof(usbdevfsIoctlArg{}))
|
||||||
usbdevfsClearHalt = ior('U', 21, 4)
|
usbdevfsReset = io_('U', 20)
|
||||||
usbdevfsDisconnect = io_('U', 22)
|
usbdevfsClearHalt = ior('U', 21, 4)
|
||||||
usbdevfsConnect = io_('U', 23)
|
usbdevfsDisconnect = io_('U', 22)
|
||||||
usbdevfsGetCapabilities = ior('U', 26, 4)
|
usbdevfsConnect = io_('U', 23)
|
||||||
usbdevfsGetSpeed = io_('U', 31)
|
usbdevfsGetCapabilities = ior('U', 26, 4)
|
||||||
|
usbdevfsDisconnectClaim = ior('U', 27, unsafe.Sizeof(usbdevfsDisconnectClaimArg{}))
|
||||||
|
usbdevfsGetSpeed = io_('U', 31)
|
||||||
)
|
)
|
||||||
|
|
||||||
// URB type constants
|
// URB type constants
|
||||||
@@ -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)))
|
||||||
|
|||||||
+27
-10
@@ -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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Claim all interfaces
|
// Try atomic disconnect+claim first (USBDEVFS_DISCONNECT_CLAIM, Linux 3.7+)
|
||||||
for _, iface := range s.device.Interfaces {
|
err := handle.DisconnectClaimInterface(ifnum)
|
||||||
if err := handle.ClaimInterface(uint32(iface.Number)); err != nil {
|
if err == nil {
|
||||||
log.Printf("[usbip-server] warning: could not claim interface %d: %v", iface.Number, err)
|
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
|
return nil
|
||||||
|
|||||||
Reference in New Issue
Block a user