Add USB/IP support for macOS

This commit is contained in:
世界
2026-06-16 17:37:10 +08:00
parent 1933c19064
commit a9fe3f386e
9 changed files with 645 additions and 23 deletions
+1 -1
View File
@@ -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))
}
+1 -1
View File
@@ -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")
+28 -17
View File
@@ -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
@@ -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()
}
@@ -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
}
@@ -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
}
+1 -1
View File
@@ -146,7 +146,7 @@ require (
github.com/sagernet/cronet-go/lib/windows_arm64 v0.0.0-20260516034431-d86a63399c27 // 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
+2 -2
View File
@@ -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.11-0.20260603045801-6e76db79f94a h1:Wuf1SkZL0rISkm4HwNczFLjl8sWUBFGRGLbgZbO8inQ=
github.com/sagernet/sing-tun v0.8.11-0.20260603045801-6e76db79f94a/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=
+1 -1
View File
@@ -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)
}