fix driver rebinding
This commit is contained in:
parent
cc1dfff382
commit
9d10beec9b
BIN
bin/usb-client
BIN
bin/usb-client
Binary file not shown.
BIN
bin/usb-relay
BIN
bin/usb-relay
Binary file not shown.
|
|
@ -276,17 +276,28 @@ func (sm *ShareManager) handleReleaseDevice(busID, fromClient string) {
|
|||
return
|
||||
}
|
||||
|
||||
// Clean up tunnel
|
||||
// Close the tunnel pipe to signal HandleConnection to stop reading
|
||||
var tunnelDone <-chan struct{}
|
||||
if tunnel, ok := sm.tunnels[share.tunnelID]; ok {
|
||||
tunnel.inPipe.Close()
|
||||
tunnelDone = tunnel.done
|
||||
delete(sm.tunnels, share.tunnelID)
|
||||
}
|
||||
|
||||
// Detach device (release interfaces, reconnect kernel driver)
|
||||
share.server.Detach()
|
||||
server := share.server
|
||||
delete(sm.active, busID)
|
||||
sm.mu.Unlock()
|
||||
|
||||
// Wait for HandleConnection goroutine to finish before detaching.
|
||||
// This ensures no more URBs are being submitted when we detach.
|
||||
if tunnelDone != nil {
|
||||
<-tunnelDone
|
||||
log.Printf("[share] HandleConnection goroutine finished for %s", busID)
|
||||
}
|
||||
|
||||
// Now safe to detach - no more USB/IP protocol processing
|
||||
server.Detach()
|
||||
|
||||
// Notify client
|
||||
sm.client.SendJSON(&protocol.DeviceReleased{
|
||||
Type: protocol.MsgDeviceReleased,
|
||||
|
|
|
|||
|
|
@ -8,7 +8,10 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/duffy/usb-server/internal/usb"
|
||||
|
|
@ -95,7 +98,7 @@ func (s *Server) Attach() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Detach releases all interfaces, reconnects kernel driver, and closes the device
|
||||
// Detach releases all interfaces, closes the device, and rebinds kernel drivers.
|
||||
func (s *Server) Detach() {
|
||||
s.mu.Lock()
|
||||
s.closed = true
|
||||
|
|
@ -124,25 +127,60 @@ func (s *Server) Detach() {
|
|||
}
|
||||
}
|
||||
|
||||
// 3. Reset device to force driver re-probing (most reliable method)
|
||||
// A USB reset forces the kernel to re-enumerate and rebind drivers.
|
||||
// This is important for devices like webcams that may be left in a
|
||||
// streaming state after URB transfers.
|
||||
if err := s.handle.ResetDevice(); err != nil {
|
||||
log.Printf("[usbip-server] device reset failed: %v, trying ConnectDriver", err)
|
||||
// Fallback: try to reconnect kernel driver without reset
|
||||
if err := s.handle.ConnectDriver(); err != nil {
|
||||
log.Printf("[usbip-server] ConnectDriver also failed: %v", err)
|
||||
} else {
|
||||
log.Printf("[usbip-server] kernel driver reconnected via ConnectDriver")
|
||||
}
|
||||
} else {
|
||||
log.Printf("[usbip-server] device reset OK, kernel drivers re-bound")
|
||||
}
|
||||
|
||||
// 4. Close the device file descriptor
|
||||
// 3. Close the device file descriptor.
|
||||
// The kernel auto-cancels remaining URBs on close.
|
||||
s.handle.Close()
|
||||
s.handle = nil
|
||||
|
||||
// 4. Force kernel driver re-binding via sysfs authorized toggle.
|
||||
// After USBDEVFS_DISCONNECT_CLAIM, the kernel sets privileges_dropped=true.
|
||||
// This means closing the fd does NOT auto-rebind drivers.
|
||||
// Also USBDEVFS_RESET after ReleaseInterface doesn't rebind because
|
||||
// the kernel sets needs_binding=false on release.
|
||||
// The reliable solution: toggle authorized 0->1 which forces complete
|
||||
// re-enumeration and driver binding.
|
||||
s.rebindDrivers()
|
||||
}
|
||||
|
||||
// rebindDrivers forces the kernel to re-bind drivers to the device
|
||||
// by toggling the sysfs authorized attribute.
|
||||
func (s *Server) rebindDrivers() {
|
||||
authPath := filepath.Join(s.device.SysPath, "authorized")
|
||||
|
||||
// Deauthorize: kernel disconnects device, unbinds all drivers
|
||||
if err := os.WriteFile(authPath, []byte("0"), 0644); err != nil {
|
||||
log.Printf("[usbip-server] sysfs deauthorize failed: %v, trying fallback", err)
|
||||
s.rebindDriversFallback()
|
||||
return
|
||||
}
|
||||
|
||||
// Brief delay for the kernel to process the deauthorization
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
// Re-authorize: kernel re-enumerates device, binds drivers
|
||||
if err := os.WriteFile(authPath, []byte("1"), 0644); err != nil {
|
||||
log.Printf("[usbip-server] sysfs re-authorize failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("[usbip-server] device re-authorized, kernel drivers re-bound")
|
||||
}
|
||||
|
||||
// rebindDriversFallback tries alternative methods to rebind drivers
|
||||
func (s *Server) rebindDriversFallback() {
|
||||
// Try writing bus_id to each original driver's bind file
|
||||
for _, iface := range s.device.Interfaces {
|
||||
if iface.Driver == "" {
|
||||
continue
|
||||
}
|
||||
ifaceName := fmt.Sprintf("%s:%d.%d", s.device.BusID, s.device.ConfigValue, iface.Number)
|
||||
bindPath := filepath.Join("/sys/bus/usb/drivers", iface.Driver, "bind")
|
||||
if err := os.WriteFile(bindPath, []byte(ifaceName), 0644); err != nil {
|
||||
log.Printf("[usbip-server] bind %s to %s failed: %v", ifaceName, iface.Driver, err)
|
||||
} else {
|
||||
log.Printf("[usbip-server] re-bound %s to driver %s", ifaceName, iface.Driver)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// buildEndpointTypeMap builds the endpoint number -> URB type map from device descriptors
|
||||
|
|
@ -504,9 +542,11 @@ func (s *Server) reapLoop(retChan chan<- []byte, done <-chan struct{}) {
|
|||
s.mu.Unlock()
|
||||
return
|
||||
}
|
||||
// Save handle reference under lock to prevent nil deref race
|
||||
handle := s.handle
|
||||
s.mu.Unlock()
|
||||
|
||||
urbInfo, err := s.handle.ReapURBInfo()
|
||||
urbInfo, err := handle.ReapURBInfo()
|
||||
if err != nil {
|
||||
select {
|
||||
case <-done:
|
||||
|
|
|
|||
Loading…
Reference in New Issue