From 38786cf640dfe126bebd635b732fd69dfce5c0e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Tue, 16 Jun 2026 17:37:10 +0800 Subject: [PATCH] Add USB/IP support for macOS --- adapter/usbip.go | 2 +- cmd/internal/build_libbox/main.go | 2 +- daemon/started_service_usbip.go | 45 +- .../libbox/command_types_usbip_local.go | 46 ++ .../command_types_usbip_local_darwin.go | 538 ++++++++++++++++++ .../libbox/command_types_usbip_local_stub.go | 27 + go.mod | 2 +- go.sum | 4 +- service/usbip/server.go | 2 +- 9 files changed, 645 insertions(+), 23 deletions(-) create mode 100644 experimental/libbox/command_types_usbip_local.go create mode 100644 experimental/libbox/command_types_usbip_local_darwin.go create mode 100644 experimental/libbox/command_types_usbip_local_stub.go diff --git a/adapter/usbip.go b/adapter/usbip.go index e334476d5..e529c458e 100644 --- a/adapter/usbip.go +++ b/adapter/usbip.go @@ -9,7 +9,7 @@ import ( ) type USBIPDynamicServer interface { - AddDevice(info usbip.DynamicDeviceInfo, transport usbip.DeviceTransport) (string, error) + AddDevice(info usbip.ProvidedDeviceInfo, transport usbip.DeviceTransport) (string, error) RemoveDevice(busID string) SubscribeDevices(ctx context.Context, listener func([]usbip.ControlDeviceInfo)) } diff --git a/cmd/internal/build_libbox/main.go b/cmd/internal/build_libbox/main.go index 0f914499e..97dc4a635 100644 --- a/cmd/internal/build_libbox/main.go +++ b/cmd/internal/build_libbox/main.go @@ -63,7 +63,7 @@ func init() { sharedFlags = append(sharedFlags, "-ldflags", "-X github.com/sagernet/sing-box/constant.Version="+currentTag+" -X internal/godebug.defaultGODEBUG=multipathtcp=0 -s -w -buildid= -checklinkname=0") debugFlags = append(debugFlags, "-ldflags", "-X github.com/sagernet/sing-box/constant.Version="+currentTag+" -X internal/godebug.defaultGODEBUG=multipathtcp=0 -checklinkname=0") - sharedTags = append(sharedTags, "with_gvisor", "with_quic", "with_wireguard", "with_utls", "with_naive_outbound", "with_clash_api", "badlinkname", "tfogo_checklinkname0") + sharedTags = append(sharedTags, "with_gvisor", "with_quic", "with_wireguard", "with_utls", "with_naive_outbound", "with_clash_api", "with_usbip", "badlinkname", "tfogo_checklinkname0") darwinTags = append(darwinTags, "with_dhcp", "grpcnotrace") // memcTags = append(memcTags, "with_tailscale") sharedTags = append(sharedTags, "with_tailscale", "ts_omit_logtail", "ts_omit_ssh", "ts_omit_drive", "ts_omit_taildrop", "ts_omit_webclient", "ts_omit_doctor", "ts_omit_capture", "ts_omit_kube", "ts_omit_aws", "ts_omit_synology", "ts_omit_bird") diff --git a/daemon/started_service_usbip.go b/daemon/started_service_usbip.go index 093bcfd08..3e771451b 100644 --- a/daemon/started_service_usbip.go +++ b/daemon/started_service_usbip.go @@ -287,23 +287,8 @@ func addUSBDevice(ctx context.Context, serviceManager adapter.ServiceManager, se ctx: ctx, pending: make(map[uint64]chan *USBURBResponse), } - busID, err := provider.AddDevice(usbip.DynamicDeviceInfo{ - BusID: descriptor.GetDeviceId(), - BusNum: descriptor.GetBusNum(), - DevNum: descriptor.GetDevNum(), - Speed: descriptor.GetSpeed(), - VendorID: uint16(descriptor.GetVendorId()), - ProductID: uint16(descriptor.GetProductId()), - BCDDevice: uint16(descriptor.GetBcdDevice()), - DeviceClass: uint8(descriptor.GetDeviceClass()), - DeviceSubClass: uint8(descriptor.GetDeviceSubClass()), - DeviceProtocol: uint8(descriptor.GetDeviceProtocol()), - ConfigurationValue: uint8(descriptor.GetConfigurationValue()), - NumConfigurations: uint8(descriptor.GetNumConfigurations()), - Interfaces: usbInterfacesFromProto(descriptor.GetInterfaces()), - Serial: descriptor.GetSerial(), - Product: descriptor.GetProduct(), - }, device) + entry := usbDeviceEntryFromDescriptor(descriptor) + busID, err := provider.AddDevice(usbip.ProvidedDeviceInfo{Entry: entry}, device) if err != nil { return nil, err } @@ -311,6 +296,32 @@ func addUSBDevice(ctx context.Context, serviceManager adapter.ServiceManager, se return device, nil } +func usbDeviceEntryFromDescriptor(descriptor *USBDeviceDescriptor) usbip.DeviceEntry { + deviceID := descriptor.GetDeviceId() + interfaces := usbInterfacesFromProto(descriptor.GetInterfaces()) + info := usbip.DeviceInfoTruncated{ + BusNum: descriptor.GetBusNum(), + DevNum: descriptor.GetDevNum(), + Speed: descriptor.GetSpeed(), + IDVendor: uint16(descriptor.GetVendorId()), + IDProduct: uint16(descriptor.GetProductId()), + BCDDevice: uint16(descriptor.GetBcdDevice()), + BDeviceClass: uint8(descriptor.GetDeviceClass()), + BDeviceSubClass: uint8(descriptor.GetDeviceSubClass()), + BDeviceProtocol: uint8(descriptor.GetDeviceProtocol()), + BConfigurationValue: uint8(descriptor.GetConfigurationValue()), + BNumConfigurations: uint8(descriptor.GetNumConfigurations()), + BNumInterfaces: uint8(len(interfaces)), + } + copy(info.BusID[:], deviceID) + return usbip.DeviceEntry{ + Info: info, + Interfaces: interfaces, + Serial: descriptor.GetSerial(), + Product: descriptor.GetProduct(), + } +} + // sing-usbip calls Submit concurrently across endpoints for a single device. type usbProvidedDevice struct { deviceID string diff --git a/experimental/libbox/command_types_usbip_local.go b/experimental/libbox/command_types_usbip_local.go new file mode 100644 index 000000000..aada53483 --- /dev/null +++ b/experimental/libbox/command_types_usbip_local.go @@ -0,0 +1,46 @@ +package libbox + +type USBLocalDeviceInfo struct { + StableID string + BusID string + Backend int32 + BusNum int32 + DevNum int32 + Speed int32 + VendorID int32 + ProductID int32 + BCDDevice int32 + DeviceClass int32 + DeviceSubClass int32 + DeviceProtocol int32 + ConfigurationValue int32 + NumConfigurations int32 + Serial string + Product string + + interfaces []*USBSharedDeviceInterface +} + +func (d *USBLocalDeviceInfo) Interfaces() USBSharedDeviceInterfaceIterator { + return newIterator(d.interfaces) +} + +type USBLocalDeviceInfoIterator interface { + Next() *USBLocalDeviceInfo + HasNext() bool +} + +type USBLocalProvidedDevice struct { + ServerTag string + DeviceID string + LocalDeviceID string + Label string + VendorID int32 + ProductID int32 +} + +type USBLocalProviderHandler interface { + OnDeviceError(serverTag string, deviceID string, message string) + OnSessionError(serverTag string, message string) + OnLocalDevicesChanged() +} diff --git a/experimental/libbox/command_types_usbip_local_darwin.go b/experimental/libbox/command_types_usbip_local_darwin.go new file mode 100644 index 000000000..7f566859f --- /dev/null +++ b/experimental/libbox/command_types_usbip_local_darwin.go @@ -0,0 +1,538 @@ +//go:build with_usbip && darwin && !ios && cgo + +package libbox + +import ( + "context" + "fmt" + "os" + "sync" + "sync/atomic" + + "github.com/sagernet/sing-box/daemon" + "github.com/sagernet/sing-usbip" + E "github.com/sagernet/sing/common/exceptions" +) + +func (c *CommandClient) NewUSBLocalProvider(handler USBLocalProviderHandler) (*USBLocalProviderManager, error) { + ctx, cancel := context.WithCancel(context.Background()) + manager := &USBLocalProviderManager{ + client: c, + handler: handler, + ctx: ctx, + cancel: cancel, + sessions: make(map[string]*usbDarwinLocalProviderSession), + devices: make(map[string]*usbDarwinProvidedDevice), + } + err := usbip.WatchLocalDevices(ctx, func() { + manager.detachMissing() + manager.notifyLocalDevicesChanged() + }) + if err != nil { + cancel() + return nil, E.Cause(err, "watch local usb devices") + } + return manager, nil +} + +type USBLocalProviderManager struct { + client *CommandClient + handler USBLocalProviderHandler + ctx context.Context + cancel context.CancelFunc + + counter atomic.Uint64 + access sync.Mutex + closed bool + sessions map[string]*usbDarwinLocalProviderSession + devices map[string]*usbDarwinProvidedDevice +} + +type usbDarwinLocalProviderSession struct { + tag string + session *USBProviderSession +} + +type usbDarwinProvidedDevice struct { + manager *USBLocalProviderManager + session *usbDarwinLocalProviderSession + local usbip.LocalDevice + info *USBLocalProvidedDevice + queueAccess sync.Mutex + queues map[uint8]chan *USBURBRequest + closed bool + closeOnce sync.Once + closeFinished chan struct{} +} + +func (m *USBLocalProviderManager) ListDevices() (USBLocalDeviceInfoIterator, error) { + devices, err := usbip.ListLocalDevices() + if err != nil { + return nil, err + } + out := make([]*USBLocalDeviceInfo, 0, len(devices)) + for i := range devices { + out = append(out, usbLocalDeviceInfoFromUSBIP(devices[i])) + } + return newIterator(out), nil +} + +func (m *USBLocalProviderManager) Attach(serverTag string, localDeviceID string) (*USBLocalProvidedDevice, error) { + if serverTag == "" { + return nil, E.New("missing usbip-server tag") + } + if localDeviceID == "" { + return nil, E.New("missing local USB device id") + } + session, err := m.ensureSession(serverTag) + if err != nil { + return nil, err + } + localDevice, err := usbip.OpenLocalDevice(localDeviceID, false) + if err != nil { + return nil, err + } + deviceID := fmt.Sprintf("local-%d", m.counter.Add(1)) + localInfo := usbLocalDeviceInfoFromUSBIP(usbip.LocalDeviceInfo{ + StableID: localDevice.StableID(), + Entry: localDevice.Entry(), + }) + descriptor := usbDeviceDescriptorFromLocalInfo(serverTag, deviceID, localInfo) + provided := &USBLocalProvidedDevice{ + ServerTag: serverTag, + DeviceID: deviceID, + LocalDeviceID: localDevice.StableID(), + Label: localInfo.Product, + VendorID: localInfo.VendorID, + ProductID: localInfo.ProductID, + } + device := &usbDarwinProvidedDevice{ + manager: m, + session: session, + local: localDevice, + info: provided, + queues: make(map[uint8]chan *USBURBRequest), + closeFinished: make(chan struct{}), + } + m.access.Lock() + if m.closed { + m.access.Unlock() + _ = localDevice.Close() + return nil, os.ErrClosed + } + m.devices[deviceID] = device + m.access.Unlock() + err = session.session.AttachDevice(descriptor) + if err != nil { + m.removeDevice(deviceID) + device.close(false) + return nil, err + } + return provided, nil +} + +func (m *USBLocalProviderManager) Detach(deviceID string) error { + device := m.removeDevice(deviceID) + if device == nil { + return os.ErrNotExist + } + err := device.detach() + device.close(false) + return err +} + +func (m *USBLocalProviderManager) Close() error { + m.access.Lock() + if m.closed { + m.access.Unlock() + return nil + } + m.closed = true + devices := make([]*usbDarwinProvidedDevice, 0, len(m.devices)) + for _, device := range m.devices { + devices = append(devices, device) + } + m.devices = make(map[string]*usbDarwinProvidedDevice) + sessions := make([]*usbDarwinLocalProviderSession, 0, len(m.sessions)) + for _, session := range m.sessions { + sessions = append(sessions, session) + } + m.sessions = make(map[string]*usbDarwinLocalProviderSession) + m.access.Unlock() + + m.cancel() + for _, device := range devices { + device.close(false) + } + var err error + for _, session := range sessions { + err = E.Append(err, session.session.Close(), func(err error) error { + return E.Cause(err, "close usb provider session ", session.tag) + }) + } + return err +} + +func (m *USBLocalProviderManager) ensureSession(serverTag string) (*usbDarwinLocalProviderSession, error) { + m.access.Lock() + if m.closed { + m.access.Unlock() + return nil, os.ErrClosed + } + existing := m.sessions[serverTag] + m.access.Unlock() + if existing != nil { + return existing, nil + } + session, err := m.client.ProvideUSBDevices(&usbDarwinLocalProviderStreamHandler{ + manager: m, + serverTag: serverTag, + }) + if err != nil { + return nil, err + } + wrapped := &usbDarwinLocalProviderSession{tag: serverTag, session: session} + + m.access.Lock() + defer m.access.Unlock() + if m.closed { + _ = session.Close() + return nil, os.ErrClosed + } + if existing = m.sessions[serverTag]; existing != nil { + _ = session.Close() + return existing, nil + } + m.sessions[serverTag] = wrapped + return wrapped, nil +} + +func (m *USBLocalProviderManager) removeDevice(deviceID string) *usbDarwinProvidedDevice { + m.access.Lock() + device := m.devices[deviceID] + if device != nil { + delete(m.devices, deviceID) + } + m.access.Unlock() + return device +} + +func (m *USBLocalProviderManager) device(deviceID string) *usbDarwinProvidedDevice { + m.access.Lock() + defer m.access.Unlock() + return m.devices[deviceID] +} + +func (m *USBLocalProviderManager) detachMissing() { + devices, err := usbip.ListLocalDevices() + if err != nil { + m.notifySessionError("", E.Cause(err, "list local USB devices").Error()) + return + } + present := make(map[string]struct{}, len(devices)) + for i := range devices { + present[devices[i].StableID] = struct{}{} + } + + var stale []*usbDarwinProvidedDevice + m.access.Lock() + for deviceID, device := range m.devices { + if _, ok := present[device.info.LocalDeviceID]; ok { + continue + } + delete(m.devices, deviceID) + stale = append(stale, device) + } + m.access.Unlock() + + for _, device := range stale { + _ = device.detach() + device.close(false) + m.notifyDeviceError(device.info.ServerTag, device.info.DeviceID, "local USB device disconnected") + } +} + +func (m *USBLocalProviderManager) onURBRequest(request *USBURBRequest) { + device := m.device(request.DeviceID) + if device == nil { + return + } + device.submit(request) +} + +func (m *USBLocalProviderManager) onAbort(deviceID string, endpoint int32) { + device := m.device(deviceID) + if device == nil { + return + } + err := device.local.AbortEndpoint(uint8(endpoint)) + if err != nil { + m.notifyDeviceError(device.info.ServerTag, deviceID, err.Error()) + } +} + +func (m *USBLocalProviderManager) onDeviceError(serverTag string, deviceID string, message string) { + device := m.removeDevice(deviceID) + if device != nil { + device.close(false) + } + m.notifyDeviceError(serverTag, deviceID, message) +} + +func (m *USBLocalProviderManager) onSessionError(serverTag string, message string) { + var affected []*usbDarwinProvidedDevice + m.access.Lock() + delete(m.sessions, serverTag) + for deviceID, device := range m.devices { + if device.info.ServerTag != serverTag { + continue + } + delete(m.devices, deviceID) + affected = append(affected, device) + } + m.access.Unlock() + + for _, device := range affected { + device.close(false) + } + m.notifySessionError(serverTag, message) +} + +func (m *USBLocalProviderManager) notifyLocalDevicesChanged() { + if m.handler != nil { + m.handler.OnLocalDevicesChanged() + } +} + +func (m *USBLocalProviderManager) notifyDeviceError(serverTag string, deviceID string, message string) { + if m.handler != nil { + m.handler.OnDeviceError(serverTag, deviceID, message) + } +} + +func (m *USBLocalProviderManager) notifySessionError(serverTag string, message string) { + if m.handler != nil { + m.handler.OnSessionError(serverTag, message) + } +} + +type usbDarwinLocalProviderStreamHandler struct { + manager *USBLocalProviderManager + serverTag string +} + +func (h *usbDarwinLocalProviderStreamHandler) OnReady(deviceID string, busID string) { +} + +func (h *usbDarwinLocalProviderStreamHandler) OnURBRequest(request *USBURBRequest) { + h.manager.onURBRequest(request) +} + +func (h *usbDarwinLocalProviderStreamHandler) OnAbort(deviceID string, endpoint int32) { + h.manager.onAbort(deviceID, endpoint) +} + +func (h *usbDarwinLocalProviderStreamHandler) OnError(deviceID string, message string) { + if deviceID == "" { + h.manager.onSessionError(h.serverTag, message) + return + } + h.manager.onDeviceError(h.serverTag, deviceID, message) +} + +func (d *usbDarwinProvidedDevice) submit(request *USBURBRequest) { + endpoint := uint8(request.Endpoint) + d.queueAccess.Lock() + if d.closed { + d.queueAccess.Unlock() + d.sendResponse(usbURBErrorResponse(request)) + return + } + queue := d.queues[endpoint] + if queue == nil { + queue = make(chan *USBURBRequest, 64) + d.queues[endpoint] = queue + go d.runQueue(queue) + } + select { + case queue <- request: + d.queueAccess.Unlock() + default: + d.queueAccess.Unlock() + d.sendResponse(usbURBErrorResponse(request)) + } +} + +func (d *usbDarwinProvidedDevice) runQueue(queue <-chan *USBURBRequest) { + for request := range queue { + result := d.local.Submit(usbIPRequestFromLocalProvider(request)) + d.sendResponse(usbURBResponseFromUSBIP(request, result)) + } +} + +func (d *usbDarwinProvidedDevice) detach() error { + return d.session.session.DetachDevice(d.info.DeviceID) +} + +func (d *usbDarwinProvidedDevice) close(detach bool) { + d.closeOnce.Do(func() { + defer close(d.closeFinished) + d.queueAccess.Lock() + d.closed = true + for endpoint, queue := range d.queues { + close(queue) + delete(d.queues, endpoint) + } + d.queueAccess.Unlock() + if detach { + _ = d.detach() + } + _ = d.local.Close() + }) + <-d.closeFinished +} + +func (d *usbDarwinProvidedDevice) sendResponse(response *USBURBResponse) { + err := d.session.session.SendURBResponse(response) + if err != nil { + d.manager.onSessionError(d.info.ServerTag, E.Cause(err, "send USB URB response").Error()) + } +} + +func usbLocalDeviceInfoFromUSBIP(info usbip.LocalDeviceInfo) *USBLocalDeviceInfo { + entry := info.Entry + interfaces := make([]*USBSharedDeviceInterface, 0, len(entry.Interfaces)) + for _, deviceInterface := range entry.Interfaces { + interfaces = append(interfaces, &USBSharedDeviceInterface{ + InterfaceClass: int32(deviceInterface.BInterfaceClass), + InterfaceSubClass: int32(deviceInterface.BInterfaceSubClass), + InterfaceProtocol: int32(deviceInterface.BInterfaceProtocol), + }) + } + return &USBLocalDeviceInfo{ + StableID: info.StableID, + BusID: entry.Info.BusIDString(), + Backend: int32(info.Backend), + BusNum: int32(entry.Info.BusNum), + DevNum: int32(entry.Info.DevNum), + Speed: int32(entry.Info.Speed), + VendorID: int32(entry.Info.IDVendor), + ProductID: int32(entry.Info.IDProduct), + BCDDevice: int32(entry.Info.BCDDevice), + DeviceClass: int32(entry.Info.BDeviceClass), + DeviceSubClass: int32(entry.Info.BDeviceSubClass), + DeviceProtocol: int32(entry.Info.BDeviceProtocol), + ConfigurationValue: int32(entry.Info.BConfigurationValue), + NumConfigurations: int32(entry.Info.BNumConfigurations), + Serial: entry.Serial, + Product: entry.Product, + interfaces: interfaces, + } +} + +func usbDeviceDescriptorFromLocalInfo(serverTag string, deviceID string, info *USBLocalDeviceInfo) *USBDeviceDescriptor { + descriptor := &USBDeviceDescriptor{ + ServerTag: serverTag, + DeviceID: deviceID, + BusNum: info.BusNum, + DevNum: info.DevNum, + Speed: info.Speed, + VendorID: info.VendorID, + ProductID: info.ProductID, + BCDDevice: info.BCDDevice, + DeviceClass: info.DeviceClass, + DeviceSubClass: info.DeviceSubClass, + DeviceProtocol: info.DeviceProtocol, + ConfigurationValue: info.ConfigurationValue, + NumConfigurations: info.NumConfigurations, + Serial: info.Serial, + Product: info.Product, + } + for _, deviceInterface := range info.interfaces { + descriptor.interfaces = append(descriptor.interfaces, &daemon.USBInterface{ + InterfaceClass: uint32(deviceInterface.InterfaceClass), + InterfaceSubClass: uint32(deviceInterface.InterfaceSubClass), + InterfaceProtocol: uint32(deviceInterface.InterfaceProtocol), + }) + } + return descriptor +} + +func usbIPRequestFromLocalProvider(request *USBURBRequest) usbip.URBRequest { + endpoint := uint8(request.Endpoint) + direction := usbip.USBIPDirOut + if request.DirectionIn { + direction = usbip.USBIPDirIn + } + var setup [8]byte + copy(setup[:], request.Setup) + buffer := request.OutData + if request.DirectionIn { + buffer = make([]byte, max(0, int(request.TransferBufferLength))) + } + isoPackets := make([]usbip.IsoPacketDescriptor, 0, request.IsoPacketCount()) + for i := int32(0); i < request.IsoPacketCount(); i++ { + packet := request.GetIsoPacket(i) + if packet == nil { + continue + } + isoPackets = append(isoPackets, usbip.IsoPacketDescriptor{ + Offset: packet.Offset, + Length: packet.Length, + ActualLength: packet.ActualLength, + Status: packet.Status, + }) + } + command := usbip.SubmitCommand{ + Header: usbip.DataHeader{ + Command: usbip.CmdSubmit, + SeqNum: uint32(request.Seq), + Direction: direction, + Endpoint: uint32(endpoint & 0x0f), + }, + TransferFlags: request.TransferFlags, + TransferBufferLength: request.TransferBufferLength, + StartFrame: request.StartFrame, + NumberOfPackets: request.NumberOfPackets, + Interval: request.Interval, + Setup: setup, + Buffer: buffer, + IsoPackets: isoPackets, + } + return usbip.URBRequest{ + Command: command, + Endpoint: endpoint, + Buffer: buffer, + IsoPackets: isoPackets, + } +} + +func usbURBResponseFromUSBIP(request *USBURBRequest, result usbip.URBResponse) *USBURBResponse { + if result.Error != nil { + return usbURBErrorResponse(request) + } + response := NewUSBURBResponse(request.DeviceID, request.Seq) + response.Status = result.Status + response.ActualLength = result.ActualLength + for _, packet := range result.IsoPackets { + response.AddIsoPacket(packet.Offset, packet.Length, packet.ActualLength, packet.Status) + } + if request.DirectionIn && len(result.Buffer) > 0 { + if request.NumberOfPackets > 0 { + response.InData = result.Buffer + } else { + actual := int(result.ActualLength) + if actual < 0 { + actual = 0 + } + response.InData = result.Buffer[:min(actual, len(result.Buffer))] + } + } + return response +} + +func usbURBErrorResponse(request *USBURBRequest) *USBURBResponse { + response := NewUSBURBResponse(request.DeviceID, request.Seq) + response.Status = -5 + return response +} diff --git a/experimental/libbox/command_types_usbip_local_stub.go b/experimental/libbox/command_types_usbip_local_stub.go new file mode 100644 index 000000000..bc4411658 --- /dev/null +++ b/experimental/libbox/command_types_usbip_local_stub.go @@ -0,0 +1,27 @@ +//go:build !with_usbip || !darwin || ios || !cgo + +package libbox + +import "os" + +type USBLocalProviderManager struct{} + +func (c *CommandClient) NewUSBLocalProvider(handler USBLocalProviderHandler) (*USBLocalProviderManager, error) { + return nil, os.ErrInvalid +} + +func (m *USBLocalProviderManager) ListDevices() (USBLocalDeviceInfoIterator, error) { + return nil, os.ErrInvalid +} + +func (m *USBLocalProviderManager) Attach(serverTag string, localDeviceID string) (*USBLocalProvidedDevice, error) { + return nil, os.ErrInvalid +} + +func (m *USBLocalProviderManager) Detach(deviceID string) error { + return os.ErrInvalid +} + +func (m *USBLocalProviderManager) Close() error { + return os.ErrInvalid +} diff --git a/go.mod b/go.mod index 5e36c84aa..8ff011615 100644 --- a/go.mod +++ b/go.mod @@ -146,7 +146,7 @@ require ( github.com/sagernet/cronet-go/lib/windows_arm64 v0.0.0-20260620135226-def9ff0fb992 // indirect github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a // indirect github.com/sagernet/nftables v0.3.0-mod.2 // indirect - github.com/sagernet/sing-usbip v0.0.0-20260615120724-630651f2af95 + github.com/sagernet/sing-usbip v0.0.0-20260616101517-efb91521eddb github.com/spf13/pflag v1.0.9 // indirect github.com/tailscale/certstore v0.1.1-0.20231202035212-d3fa0460f47e // indirect github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55 // indirect diff --git a/go.sum b/go.sum index 4855c3b9a..3bc39f27c 100644 --- a/go.sum +++ b/go.sum @@ -268,8 +268,8 @@ github.com/sagernet/sing-shadowtls v0.2.1 h1:ZiHZdnEnP+YS73NMsxiZmIFCwNd0M4k7PkG github.com/sagernet/sing-shadowtls v0.2.1/go.mod h1:sWqKnGlMipCHaGsw1sTTlimyUpgzP4WP3pjhCsYt9oA= github.com/sagernet/sing-tun v0.8.12-0.20260620141852-d8bba3691575 h1:0K8un73LZ6xOkeX6OQjH9HcKilwMJyZ9sURnsp8tn5U= github.com/sagernet/sing-tun v0.8.12-0.20260620141852-d8bba3691575/go.mod h1:QvarqUtHfj1ULaRR+6kZOS/OoCE+pYGq67A5tyIy+dQ= -github.com/sagernet/sing-usbip v0.0.0-20260615120724-630651f2af95 h1:1z+EwDwim2C1Lt5HRRFeT8MbvEKxb/LSz/F0vbBwm6g= -github.com/sagernet/sing-usbip v0.0.0-20260615120724-630651f2af95/go.mod h1:D4CnJX3MNAAANhbQUxfIRgBdnvlTEaV7h6ojedcs+pw= +github.com/sagernet/sing-usbip v0.0.0-20260616101517-efb91521eddb h1:KEMbfexD4DvrQGYWwx6r+AwH9Veh8z6cnBZmtCS2G+0= +github.com/sagernet/sing-usbip v0.0.0-20260616101517-efb91521eddb/go.mod h1:D4CnJX3MNAAANhbQUxfIRgBdnvlTEaV7h6ojedcs+pw= github.com/sagernet/sing-vmess v0.2.8-0.20250909125414-3aed155119a1 h1:aSwUNYUkVyVvdmBSufR8/nRFonwJeKSIROxHcm5br9o= github.com/sagernet/sing-vmess v0.2.8-0.20250909125414-3aed155119a1/go.mod h1:P11scgTxMxVVQ8dlM27yNm3Cro40mD0+gHbnqrNGDuY= github.com/sagernet/smux v1.5.50-sing-box-mod.1 h1:XkJcivBC9V4wBjiGXIXZ229aZCU1hzcbp6kSkkyQ478= diff --git a/service/usbip/server.go b/service/usbip/server.go index 81e65bb60..d1483d94c 100644 --- a/service/usbip/server.go +++ b/service/usbip/server.go @@ -94,7 +94,7 @@ func (s *ServerService) Close() error { return s.inner.Close() } -func (s *dynamicServerService) AddDevice(info usbip.DynamicDeviceInfo, transport usbip.DeviceTransport) (string, error) { +func (s *dynamicServerService) AddDevice(info usbip.ProvidedDeviceInfo, transport usbip.DeviceTransport) (string, error) { return s.host.AddDevice(info, transport) }