usbip

package module
v0.0.0-...-60d4072 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 16, 2026 License: GPL-2.0 Imports: 30 Imported by: 0

README

sing-usbip

Cross-platform USB/IP implementation in Go

Backends:

Platform Server (export) Client (import)
Linux usbip-host kernel driver vhci_hcd kernel driver
macOS IOUSBHost (disable SIP required) IOUSBHostControllerInterface
Windows VBoxUSB drivers (embedded) usbip-win2 UDE vhci driver (embedded)

Control extension

Standard USB/IP has no change notifications, so importers must poll for device arrivals and removals. sing-usbip adds a control channel that pushes device state to the client instead. It runs on its own connections alongside the standard protocol, so the server keeps serving standard usbip clients unchanged; the sing-usbip client always imports through the extension and requires a sing-usbip server.

CLI

Export devices (matched by busid, vendor:product, and/or serial):

sing-usbip list
sing-usbip run -s :3240 -d 1-1.2
sing-usbip run -s 127.0.0.1:3240 -d 0bda:8153,serial=001000001

Import devices (without --device, every exported device is imported):

sing-usbip list -c 192.168.1.10
sing-usbip run -c 192.168.1.10 -d 0bda:8153

Exported devices can also be attached by standard usbip clients; importing requires a sing-usbip server.

Documentation

Index

Constants

View Source
const (
	CmdSubmit uint32 = 0x00000001
	CmdUnlink uint32 = 0x00000002
	RetSubmit uint32 = 0x00000003
	RetUnlink uint32 = 0x00000004

	USBIPDirOut uint32 = 0
	USBIPDirIn  uint32 = 1
)
View Source
const (
	DefaultPort = 3240

	ProtocolVersion uint16 = 0x0111

	OpReqDevList uint16 = 0x8005
	OpRepDevList uint16 = 0x0005
	OpReqImport  uint16 = 0x8003
	OpRepImport  uint16 = 0x0003

	OpStatusOK    uint32 = 0
	OpStatusError uint32 = 1
)
View Source
const (
	SpeedUnknown   uint32 = 0
	SpeedLow       uint32 = 1
	SpeedFull      uint32 = 2
	SpeedHigh      uint32 = 3
	SpeedWireless  uint32 = 4
	SpeedSuper     uint32 = 5
	SpeedSuperPlus uint32 = 6
)

Variables

View Source
var (
	ErrPeerClosed error = peerClosedError{}
	ErrCanceled   error = urbCanceledError{}
)

Functions

func ReadOpReqImportBody

func ReadOpReqImportBody(r io.Reader) (string, error)

func RebaseFrame

func RebaseFrame(currentFrame uint64, low8 uint8) uint64

Apple's IOUSBHostCI iso messages only carry the low 8 bits; the host recovers the high bits against the controller's monotonic counter.

func ScatterIsoResponse

func ScatterIsoResponse(dst, payload []byte, packets []IsoPacketDescriptor)

func SelectMatches

func SelectMatches(patterns []DeviceMatch, keys []DeviceKey) []int

func ValidateIsoResponse

func ValidateIsoResponse(requestLen int, actualLength int, packets []IsoPacketDescriptor, payloadLen int) error

func WriteOpHeader

func WriteOpHeader(w io.Writer, code uint16, status uint32) error

func WriteOpRepDevList

func WriteOpRepDevList(w io.Writer, entries []DeviceEntry) error

func WriteOpRepImport

func WriteOpRepImport(w io.Writer, code uint16, status uint32, info *DeviceInfoTruncated) error

func WriteOpReqImport

func WriteOpReqImport(w io.Writer, busid string) error

func WriteSubmitCommand

func WriteSubmitCommand(w io.Writer, command SubmitCommand) error

func WriteSubmitResponse

func WriteSubmitResponse(w io.Writer, response SubmitResponse) error

func WriteUnlinkCommand

func WriteUnlinkCommand(w io.Writer, command UnlinkCommand) error

func WriteUnlinkResponse

func WriteUnlinkResponse(w io.Writer, response UnlinkResponse) error

Types

type AttachedSession

type AttachedSession interface {
	DataSession
	Description() string
}

type BackendID

type BackendID int32
const (
	BackendIDUnspecified BackendID = iota
	BackendIDLinuxSysfs
	BackendIDDynamic
	BackendIDDarwinIOKit
	BackendIDWindowsVBoxUSB
)

func (BackendID) String

func (b BackendID) String() string

type ClientOptions

type ClientOptions struct {
	Logger        logger.ContextLogger
	Dialer        N.Dialer
	ServerAddress M.Socksaddr
	Devices       []DeviceMatch
}

type ClientService

type ClientService struct {
	// contains filtered or unexported fields
}

func NewClientService

func NewClientService(ctx context.Context, options ClientOptions) (*ClientService, error)

func (*ClientService) Close

func (c *ClientService) Close() error

func (*ClientService) Start

func (c *ClientService) Start() error

type ControlDeviceInfo

type ControlDeviceInfo struct {
	BusID              string                   `json:"busid"`
	StableID           string                   `json:"stable_id,omitempty"`
	Backend            BackendID                `json:"backend,omitempty"`
	Path               string                   `json:"path,omitempty"`
	Serial             string                   `json:"serial,omitempty"`
	Product            string                   `json:"product,omitempty"`
	VendorID           uint16                   `json:"vendor_id"`
	ProductID          uint16                   `json:"product_id"`
	BCDDevice          uint16                   `json:"bcd_device,omitempty"`
	BusNum             uint32                   `json:"busnum"`
	DevNum             uint32                   `json:"devnum"`
	Speed              uint32                   `json:"speed"`
	DeviceClass        uint8                    `json:"device_class"`
	DeviceSubClass     uint8                    `json:"device_subclass"`
	DeviceProtocol     uint8                    `json:"device_protocol"`
	ConfigurationValue uint8                    `json:"configuration_value"`
	NumConfigurations  uint8                    `json:"num_configurations"`
	NumInterfaces      uint8                    `json:"num_interfaces"`
	Interfaces         []ControlDeviceInterface `json:"interfaces,omitempty"`
	State              DeviceState              `json:"state"`
	StatusCode         int                      `json:"status_code,omitempty"`
	StatusReason       string                   `json:"status_reason,omitempty"`
}

type ControlDeviceInterface

type ControlDeviceInterface struct {
	Class    uint8 `json:"class"`
	SubClass uint8 `json:"subclass"`
	Protocol uint8 `json:"protocol"`
}

type DataHeader

type DataHeader struct {
	Command   uint32
	SeqNum    uint32
	DevID     uint32
	Direction uint32
	Endpoint  uint32
}

func ReadDataHeader

func ReadDataHeader(r io.Reader) (DataHeader, error)

type DataSession

type DataSession interface {
	Done() <-chan struct{}
	Err() error
	Start() error
	Close() error
}

type DeviceEntry

type DeviceEntry struct {
	Info       DeviceInfoTruncated
	Interfaces []DeviceInterface
	Serial     string
	Product    string
}

func FetchControlDeviceEntries

func FetchControlDeviceEntries(conn net.Conn) ([]DeviceEntry, error)

func ReadOpRepDevListBody

func ReadOpRepDevListBody(r io.Reader) ([]DeviceEntry, error)

type DeviceInfoTruncated

type DeviceInfoTruncated struct {
	Path                [256]byte
	BusID               [32]byte
	BusNum              uint32
	DevNum              uint32
	Speed               uint32
	IDVendor            uint16
	IDProduct           uint16
	BCDDevice           uint16
	BDeviceClass        uint8
	BDeviceSubClass     uint8
	BDeviceProtocol     uint8
	BConfigurationValue uint8
	BNumConfigurations  uint8
	BNumInterfaces      uint8
}

func ReadOpRepImportBody

func ReadOpRepImportBody(r io.Reader) (DeviceInfoTruncated, error)

func (*DeviceInfoTruncated) BusIDString

func (d *DeviceInfoTruncated) BusIDString() string

func (*DeviceInfoTruncated) DevID

func (d *DeviceInfoTruncated) DevID() uint32

func (*DeviceInfoTruncated) PathString

func (d *DeviceInfoTruncated) PathString() string

type DeviceInterface

type DeviceInterface struct {
	BInterfaceClass    uint8
	BInterfaceSubClass uint8
	BInterfaceProtocol uint8
	Padding            uint8
}

type DeviceKey

type DeviceKey struct {
	BusID     string
	VendorID  uint16
	ProductID uint16
	Serial    string
}

type DeviceMatch

type DeviceMatch struct {
	BusID     string
	VendorID  uint16
	ProductID uint16
	Serial    string
}

func (DeviceMatch) IsZero

func (m DeviceMatch) IsZero() bool

type DeviceState

type DeviceState int32
const (
	DeviceStateIdle DeviceState = iota
	DeviceStateAttached
	DeviceStateUnavailable
)

func (DeviceState) String

func (s DeviceState) String() string

type DeviceTransport

type DeviceTransport interface {
	Submit(request URBRequest) URBResponse
	AbortEndpoint(endpoint uint8) error
}

type DynamicHost

type DynamicHost struct {
	// contains filtered or unexported fields
}

func NewDynamicHost

func NewDynamicHost(contextLogger logger.ContextLogger) *DynamicHost

func (*DynamicHost) AddDevice

func (h *DynamicHost) AddDevice(info ProvidedDeviceInfo, transport DeviceTransport) (string, error)

func (*DynamicHost) Close

func (h *DynamicHost) Close() error

func (*DynamicHost) Events

func (h *DynamicHost) Events() (<-chan struct{}, error)

func (*DynamicHost) FinishImport

func (h *DynamicHost) FinishImport(busid string) (bool, error)

func (*DynamicHost) Reconcile

func (h *DynamicHost) Reconcile(isReserved func(busid string) bool) (map[string]Export, []string, error)

func (*DynamicHost) RemoveDevice

func (h *DynamicHost) RemoveDevice(busid string)

func (*DynamicHost) Start

func (h *DynamicHost) Start() error

type Export

type Export interface {
	BusID() string
	Snapshot(busy bool) ExportSnapshot
	DeviceInfo() (DeviceInfoTruncated, error)
	NewServerDataSession(ctx context.Context, conn net.Conn) (DataSession, error)
}

type ExportHost

type ExportHost interface {
	Start() error
	Close() error
	Reconcile(isReserved func(busid string) bool) (snapshot map[string]Export, released []string, err error)
	FinishImport(busid string) (released bool, err error)
	Events() (<-chan struct{}, error)
}

type ExportSnapshot

type ExportSnapshot struct {
	Entry        DeviceEntry
	Backend      BackendID
	StableID     string
	State        DeviceState
	StatusReason string
	RawStatus    int
}

type ImportHost

type ImportHost interface {
	Start() error
	Close() error
	Attach(ctx context.Context, info DeviceInfoTruncated, conn net.Conn) (AttachedSession, error)
}

type IsoPacketDescriptor

type IsoPacketDescriptor struct {
	Offset       int32
	Length       int32
	ActualLength int32
	Status       int32
}

type ListenFunc

type ListenFunc func(ctx context.Context) (net.Listener, error)

type LocalDevice

type LocalDevice interface {
	DeviceTransport
	StableID() string
	Entry() DeviceEntry
	Close() error
}

func OpenLocalDevice

func OpenLocalDevice(id string, capture bool) (LocalDevice, error)

type LocalDeviceInfo

type LocalDeviceInfo struct {
	StableID string
	Backend  BackendID
	Entry    DeviceEntry
}

func ListLocalDevices

func ListLocalDevices() ([]LocalDeviceInfo, error)

type OpHeader

type OpHeader struct {
	Version uint16
	Code    uint16
	Status  uint32
}

func ParseOpHeader

func ParseOpHeader(raw []byte) OpHeader

func ReadOpHeader

func ReadOpHeader(r io.Reader) (OpHeader, error)

type ProvidedDeviceInfo

type ProvidedDeviceInfo struct {
	Entry    DeviceEntry
	StableID string
}

type ServerOptions

type ServerOptions struct {
	Logger        logger.ContextLogger
	Devices       []DeviceMatch
	Listen        ListenFunc
	ListenAddress M.Socksaddr
}

type ServerService

type ServerService struct {
	// contains filtered or unexported fields
}

func NewDynamicServerService

func NewDynamicServerService(ctx context.Context, options ServerOptions, host *DynamicHost) (*ServerService, error)

func NewServerService

func NewServerService(ctx context.Context, options ServerOptions) (*ServerService, error)

func (*ServerService) Close

func (s *ServerService) Close() error

func (*ServerService) DeviceSnapshot

func (s *ServerService) DeviceSnapshot() []ControlDeviceInfo

func (*ServerService) ListenAddr

func (s *ServerService) ListenAddr() net.Addr

func (*ServerService) Start

func (s *ServerService) Start() (err error)

func (*ServerService) SubscribeDevices

func (s *ServerService) SubscribeDevices(ctx context.Context, listener func([]ControlDeviceInfo))

type SubmitCommand

type SubmitCommand struct {
	Header               DataHeader
	TransferFlags        int32
	TransferBufferLength int32
	StartFrame           int32
	NumberOfPackets      int32
	Interval             int32
	Setup                [8]byte
	Buffer               []byte
	IsoPackets           []IsoPacketDescriptor
}

func EncodeIsoSubmit

func EncodeIsoSubmit(currentFrame uint64, base SubmitCommand, ciFrame uint8, asap bool) SubmitCommand

func ReadSubmitCommandBody

func ReadSubmitCommandBody(r io.Reader, header DataHeader) (SubmitCommand, error)

type SubmitResponse

type SubmitResponse struct {
	Header          DataHeader
	Status          int32
	ActualLength    int32
	StartFrame      int32
	NumberOfPackets int32
	ErrorCount      int32
	Setup           [8]byte
	Buffer          []byte
	IsoPackets      []IsoPacketDescriptor
}

func ReadSubmitResponseBody

func ReadSubmitResponseBody(r io.Reader, header DataHeader, requestDirection uint32) (SubmitResponse, error)

The USB/IP response header zeroes direction on the wire; requestDirection must be the original CMD_SUBMIT direction.

type URBEngine

type URBEngine interface {
	Submit(request URBRequest) URBResponse
	AbortEndpoint(endpoint uint8) error
	Close() error
}

type URBRequest

type URBRequest struct {
	Command    SubmitCommand
	Endpoint   uint8
	Buffer     []byte
	IsoPackets []IsoPacketDescriptor
}

type URBResponse

type URBResponse struct {
	Status       int32
	ActualLength int32
	Buffer       []byte
	IsoPackets   []IsoPacketDescriptor
	Error        error
}

type UnlinkCommand

type UnlinkCommand struct {
	Header DataHeader
	SeqNum uint32
}

func ReadUnlinkCommandBody

func ReadUnlinkCommandBody(r io.Reader, header DataHeader) (UnlinkCommand, error)

type UnlinkResponse

type UnlinkResponse struct {
	Header DataHeader
	Status int32
}

func ReadUnlinkResponseBody

func ReadUnlinkResponseBody(r io.Reader, header DataHeader) (UnlinkResponse, error)

type UrbTransaction

type UrbTransaction struct {
	// contains filtered or unexported fields
}

func (*UrbTransaction) Cancel

func (t *UrbTransaction) Cancel() error

func (*UrbTransaction) Direction

func (t *UrbTransaction) Direction() uint32

func (*UrbTransaction) Done

func (t *UrbTransaction) Done() <-chan struct{}

func (*UrbTransaction) SeqNum

func (t *UrbTransaction) SeqNum() uint32

func (*UrbTransaction) Wait

func (t *UrbTransaction) Wait() (SubmitResponse, error)

type UsbIpPeer

type UsbIpPeer struct {
	// contains filtered or unexported fields
}

func NewUsbIpPeer

func NewUsbIpPeer(ctx context.Context, logger logger.ContextLogger, conn net.Conn) *UsbIpPeer

func (*UsbIpPeer) Close

func (p *UsbIpPeer) Close() error

func (*UsbIpPeer) Done

func (p *UsbIpPeer) Done() <-chan struct{}

func (*UsbIpPeer) Err

func (p *UsbIpPeer) Err() error

func (*UsbIpPeer) Submit

func (p *UsbIpPeer) Submit(command SubmitCommand) (*UrbTransaction, error)

Directories

Path Synopsis
internal

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL