fix driver rebinding

This commit is contained in:
duffyduck 2026-02-18 23:12:19 +01:00
parent cc1dfff382
commit 9d10beec9b
4 changed files with 73 additions and 22 deletions

Binary file not shown.

Binary file not shown.

View File

@ -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,

View File

@ -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: