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
|
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 {
|
if tunnel, ok := sm.tunnels[share.tunnelID]; ok {
|
||||||
tunnel.inPipe.Close()
|
tunnel.inPipe.Close()
|
||||||
|
tunnelDone = tunnel.done
|
||||||
delete(sm.tunnels, share.tunnelID)
|
delete(sm.tunnels, share.tunnelID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Detach device (release interfaces, reconnect kernel driver)
|
server := share.server
|
||||||
share.server.Detach()
|
|
||||||
delete(sm.active, busID)
|
delete(sm.active, busID)
|
||||||
sm.mu.Unlock()
|
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
|
// Notify client
|
||||||
sm.client.SendJSON(&protocol.DeviceReleased{
|
sm.client.SendJSON(&protocol.DeviceReleased{
|
||||||
Type: protocol.MsgDeviceReleased,
|
Type: protocol.MsgDeviceReleased,
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,10 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"github.com/duffy/usb-server/internal/usb"
|
"github.com/duffy/usb-server/internal/usb"
|
||||||
|
|
@ -95,7 +98,7 @@ func (s *Server) Attach() error {
|
||||||
return nil
|
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() {
|
func (s *Server) Detach() {
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
s.closed = true
|
s.closed = true
|
||||||
|
|
@ -124,25 +127,60 @@ func (s *Server) Detach() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Reset device to force driver re-probing (most reliable method)
|
// 3. Close the device file descriptor.
|
||||||
// A USB reset forces the kernel to re-enumerate and rebind drivers.
|
// The kernel auto-cancels remaining URBs on close.
|
||||||
// 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
|
|
||||||
s.handle.Close()
|
s.handle.Close()
|
||||||
s.handle = nil
|
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
|
// 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()
|
s.mu.Unlock()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// Save handle reference under lock to prevent nil deref race
|
||||||
|
handle := s.handle
|
||||||
s.mu.Unlock()
|
s.mu.Unlock()
|
||||||
|
|
||||||
urbInfo, err := s.handle.ReapURBInfo()
|
urbInfo, err := handle.ReapURBInfo()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
select {
|
select {
|
||||||
case <-done:
|
case <-done:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue