Add USB/IP support for macOS
This commit is contained in:
+1
-1
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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=
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user