added isochronous modus
This commit is contained in:
Binary file not shown.
Binary file not shown.
+21
-5
@@ -24,13 +24,29 @@ type Device struct {
|
|||||||
|
|
||||||
// Interface represents a USB interface
|
// Interface represents a USB interface
|
||||||
type Interface struct {
|
type Interface struct {
|
||||||
Number uint8 `json:"number"`
|
Number uint8 `json:"number"`
|
||||||
Class uint8 `json:"class"`
|
Class uint8 `json:"class"`
|
||||||
SubClass uint8 `json:"sub_class"`
|
SubClass uint8 `json:"sub_class"`
|
||||||
Protocol uint8 `json:"protocol"`
|
Protocol uint8 `json:"protocol"`
|
||||||
Driver string `json:"driver"`
|
Driver string `json:"driver"`
|
||||||
|
Endpoints []Endpoint `json:"endpoints"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Endpoint represents a USB endpoint
|
||||||
|
type Endpoint struct {
|
||||||
|
Address uint8 `json:"address"` // bEndpointAddress (bit 7=direction, bits 3:0=number)
|
||||||
|
TransferType uint8 `json:"transfer_type"` // 0=control, 1=iso, 2=bulk, 3=interrupt
|
||||||
|
MaxPacketSize uint16 `json:"max_packet_size"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// USB transfer types (from bmAttributes)
|
||||||
|
const (
|
||||||
|
TransferTypeControl = 0
|
||||||
|
TransferTypeIsochronous = 1
|
||||||
|
TransferTypeBulk = 2
|
||||||
|
TransferTypeInterrupt = 3
|
||||||
|
)
|
||||||
|
|
||||||
// DevID returns the USB/IP device ID (busnum << 16 | devnum)
|
// DevID returns the USB/IP device ID (busnum << 16 | devnum)
|
||||||
func (d *Device) DevID() uint32 {
|
func (d *Device) DevID() uint32 {
|
||||||
return (d.BusNum << 16) | d.DevNum
|
return (d.BusNum << 16) | d.DevNum
|
||||||
|
|||||||
@@ -127,12 +127,55 @@ func readInterfaces(sysPath, busID string) []Interface {
|
|||||||
iface.Driver = filepath.Base(driverLink)
|
iface.Driver = filepath.Base(driverLink)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Read endpoints
|
||||||
|
iface.Endpoints = readEndpoints(ifacePath)
|
||||||
|
|
||||||
ifaces = append(ifaces, iface)
|
ifaces = append(ifaces, iface)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ifaces
|
return ifaces
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func readEndpoints(ifacePath string) []Endpoint {
|
||||||
|
entries, err := os.ReadDir(ifacePath)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var eps []Endpoint
|
||||||
|
for _, entry := range entries {
|
||||||
|
name := entry.Name()
|
||||||
|
if !strings.HasPrefix(name, "ep_") || name == "ep_00" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
epPath := filepath.Join(ifacePath, name)
|
||||||
|
addr, _ := strconv.ParseUint(readString(epPath, "bEndpointAddress"), 16, 8)
|
||||||
|
|
||||||
|
var transferType uint8
|
||||||
|
switch readString(epPath, "type") {
|
||||||
|
case "Control":
|
||||||
|
transferType = TransferTypeControl
|
||||||
|
case "Isoc":
|
||||||
|
transferType = TransferTypeIsochronous
|
||||||
|
case "Bulk":
|
||||||
|
transferType = TransferTypeBulk
|
||||||
|
case "Interrupt":
|
||||||
|
transferType = TransferTypeInterrupt
|
||||||
|
}
|
||||||
|
|
||||||
|
maxPkt := readUint32(epPath, "wMaxPacketSize")
|
||||||
|
|
||||||
|
eps = append(eps, Endpoint{
|
||||||
|
Address: uint8(addr),
|
||||||
|
TransferType: transferType,
|
||||||
|
MaxPacketSize: uint16(maxPkt),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return eps
|
||||||
|
}
|
||||||
|
|
||||||
func readString(dir, attr string) string {
|
func readString(dir, attr string) string {
|
||||||
data, err := os.ReadFile(filepath.Join(dir, attr))
|
data, err := os.ReadFile(filepath.Join(dir, attr))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -348,6 +348,109 @@ func (h *DeviceHandle) DiscardURB(urb *usbdevfsURB) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SubmitISOURBParams holds parameters for async ISO URB submission
|
||||||
|
type SubmitISOURBParams struct {
|
||||||
|
Endpoint uint8
|
||||||
|
Flags uint32
|
||||||
|
Buffer []byte
|
||||||
|
NumberOfPackets int32
|
||||||
|
PacketLengths []uint32 // length of each ISO packet
|
||||||
|
UserContext uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubmitISOURB submits an asynchronous isochronous URB.
|
||||||
|
// Returns the URB pointer and the backing memory slice (must be kept alive until reap).
|
||||||
|
func (h *DeviceHandle) SubmitISOURB(params *SubmitISOURBParams) (urb *usbdevfsURB, mem []byte, err error) {
|
||||||
|
urbSize := unsafe.Sizeof(usbdevfsURB{})
|
||||||
|
isoDescSize := unsafe.Sizeof(usbdevfsISOPacketDesc{})
|
||||||
|
totalSize := urbSize + uintptr(params.NumberOfPackets)*isoDescSize
|
||||||
|
|
||||||
|
mem = make([]byte, totalSize)
|
||||||
|
urb = (*usbdevfsURB)(unsafe.Pointer(&mem[0]))
|
||||||
|
|
||||||
|
var bufPtr uintptr
|
||||||
|
if len(params.Buffer) > 0 {
|
||||||
|
bufPtr = uintptr(unsafe.Pointer(¶ms.Buffer[0]))
|
||||||
|
}
|
||||||
|
|
||||||
|
urb.Type = urbTypeISO
|
||||||
|
urb.Endpoint = params.Endpoint
|
||||||
|
urb.Flags = params.Flags
|
||||||
|
urb.Buffer = bufPtr
|
||||||
|
urb.BufferLength = int32(len(params.Buffer))
|
||||||
|
urb.NumberOfPackets = params.NumberOfPackets
|
||||||
|
urb.UserContext = params.UserContext
|
||||||
|
|
||||||
|
// Fill ISO packet descriptors (immediately following the URB in memory)
|
||||||
|
for i := int32(0); i < params.NumberOfPackets; i++ {
|
||||||
|
descOffset := urbSize + uintptr(i)*isoDescSize
|
||||||
|
desc := (*usbdevfsISOPacketDesc)(unsafe.Pointer(&mem[descOffset]))
|
||||||
|
if i < int32(len(params.PacketLengths)) {
|
||||||
|
desc.Length = params.PacketLengths[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(h.fd), usbdevfsSubmitURB, uintptr(unsafe.Pointer(urb)))
|
||||||
|
if errno != 0 {
|
||||||
|
return nil, nil, fmt.Errorf("USBDEVFS_SUBMITURB (ISO): %w", errno)
|
||||||
|
}
|
||||||
|
return urb, mem, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ISOPacketResult holds the result of one ISO packet after reaping
|
||||||
|
type ISOPacketResult struct {
|
||||||
|
Length uint32
|
||||||
|
ActualLength uint32
|
||||||
|
Status uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadISOResults reads the ISO packet results from a reaped ISO URB's backing memory.
|
||||||
|
func ReadISOResults(mem []byte, numPackets int32) []ISOPacketResult {
|
||||||
|
urbSize := unsafe.Sizeof(usbdevfsURB{})
|
||||||
|
isoDescSize := unsafe.Sizeof(usbdevfsISOPacketDesc{})
|
||||||
|
|
||||||
|
results := make([]ISOPacketResult, numPackets)
|
||||||
|
for i := int32(0); i < numPackets; i++ {
|
||||||
|
offset := urbSize + uintptr(i)*isoDescSize
|
||||||
|
if int(offset+isoDescSize) > len(mem) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
desc := (*usbdevfsISOPacketDesc)(unsafe.Pointer(&mem[offset]))
|
||||||
|
results[i] = ISOPacketResult{
|
||||||
|
Length: desc.Length,
|
||||||
|
ActualLength: desc.ActualLength,
|
||||||
|
Status: desc.Status,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReapedURBInfo holds exported fields from a reaped URB needed for response building
|
||||||
|
type ReapedURBInfo struct {
|
||||||
|
UserContext uintptr
|
||||||
|
Status int32
|
||||||
|
ActualLength int32
|
||||||
|
StartFrame int32
|
||||||
|
ErrorCount int32
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReapURBInfo blocks until a URB completes and returns exported info
|
||||||
|
func (h *DeviceHandle) ReapURBInfo() (*ReapedURBInfo, error) {
|
||||||
|
var urbPtr uintptr
|
||||||
|
_, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(h.fd), usbdevfsReapURB, uintptr(unsafe.Pointer(&urbPtr)))
|
||||||
|
if errno != 0 {
|
||||||
|
return nil, fmt.Errorf("USBDEVFS_REAPURB: %w", errno)
|
||||||
|
}
|
||||||
|
urb := (*usbdevfsURB)(unsafe.Pointer(urbPtr))
|
||||||
|
return &ReapedURBInfo{
|
||||||
|
UserContext: urb.UserContext,
|
||||||
|
Status: urb.Status,
|
||||||
|
ActualLength: urb.ActualLength,
|
||||||
|
StartFrame: urb.StartFrame,
|
||||||
|
ErrorCount: urb.ErrorCount,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// GetFile returns an os.File wrapping the device fd (useful for epoll/select)
|
// GetFile returns an os.File wrapping the device fd (useful for epoll/select)
|
||||||
func (h *DeviceHandle) GetFile() *os.File {
|
func (h *DeviceHandle) GetFile() *os.File {
|
||||||
return os.NewFile(uintptr(h.fd), h.devPath)
|
return os.NewFile(uintptr(h.fd), h.devPath)
|
||||||
|
|||||||
@@ -304,6 +304,56 @@ func BuildRetSubmit(seqNum, devID, direction, endpoint uint32, status int32, dat
|
|||||||
return buf.Bytes(), nil
|
return buf.Bytes(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BuildRetSubmitISO builds a RET_SUBMIT message for isochronous transfers.
|
||||||
|
// The transfer data must already be packed (compact, no gaps).
|
||||||
|
func BuildRetSubmitISO(seqNum, devID, direction, endpoint uint32, status int32,
|
||||||
|
packedData []byte, startFrame uint32, numPackets int32, errorCount int32,
|
||||||
|
isoDescs []ISOPacketDescriptor) ([]byte, error) {
|
||||||
|
|
||||||
|
buf := &bytes.Buffer{}
|
||||||
|
|
||||||
|
hdr := &URBHeader{
|
||||||
|
Command: RetSubmit,
|
||||||
|
SeqNum: seqNum,
|
||||||
|
DevID: devID,
|
||||||
|
Direction: direction,
|
||||||
|
Endpoint: endpoint,
|
||||||
|
}
|
||||||
|
if err := WriteURBHeader(buf, hdr); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
actualLen := uint32(0)
|
||||||
|
if direction == DirIn && packedData != nil {
|
||||||
|
actualLen = uint32(len(packedData))
|
||||||
|
}
|
||||||
|
|
||||||
|
body := &RetSubmitBody{
|
||||||
|
Status: status,
|
||||||
|
ActualLength: actualLen,
|
||||||
|
StartFrame: startFrame,
|
||||||
|
NumberOfPackets: uint32(numPackets),
|
||||||
|
ErrorCount: uint32(errorCount),
|
||||||
|
}
|
||||||
|
if err := WriteRetSubmit(buf, body); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transfer buffer (packed) for IN direction
|
||||||
|
if direction == DirIn && len(packedData) > 0 {
|
||||||
|
buf.Write(packedData)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ISO packet descriptors
|
||||||
|
for _, desc := range isoDescs {
|
||||||
|
if err := binary.Write(buf, binary.BigEndian, &desc); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
// BuildRetUnlink builds a RET_UNLINK message
|
// BuildRetUnlink builds a RET_UNLINK message
|
||||||
func BuildRetUnlink(seqNum, devID uint32, status int32) ([]byte, error) {
|
func BuildRetUnlink(seqNum, devID uint32, status int32) ([]byte, error) {
|
||||||
buf := &bytes.Buffer{}
|
buf := &bytes.Buffer{}
|
||||||
|
|||||||
+211
-55
@@ -19,20 +19,25 @@ import (
|
|||||||
// It manages a single USB device and forwards URBs between
|
// It manages a single USB device and forwards URBs between
|
||||||
// the USB/IP client (via tunnel) and the physical device (via usbdevfs).
|
// the USB/IP client (via tunnel) and the physical device (via usbdevfs).
|
||||||
type Server struct {
|
type Server struct {
|
||||||
device *usb.Device
|
device *usb.Device
|
||||||
handle *usb.DeviceHandle
|
handle *usb.DeviceHandle
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
pendingURBs map[uint32]*pendingURB // seqnum -> pending URB
|
pendingURBs map[uint32]*pendingURB // seqnum -> pending URB
|
||||||
closed bool
|
closed bool
|
||||||
|
epTypes map[uint8]uint8 // endpoint number (1-15) -> usbdevfs URB type
|
||||||
}
|
}
|
||||||
|
|
||||||
type pendingURB struct {
|
type pendingURB struct {
|
||||||
seqNum uint32
|
seqNum uint32
|
||||||
devID uint32
|
devID uint32
|
||||||
direction uint32
|
direction uint32
|
||||||
endpoint uint32
|
endpoint uint32
|
||||||
buffer []byte
|
buffer []byte
|
||||||
urbPtr unsafe.Pointer // pointer to submitted usbdevfs_urb
|
urbPtr unsafe.Pointer // pointer to submitted usbdevfs_urb
|
||||||
|
isISO bool
|
||||||
|
numPackets int32
|
||||||
|
isoMem []byte // keeps ISO URB+descriptors memory alive for GC
|
||||||
|
packetLens []uint32 // original request lengths per ISO packet (for offset computation)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewServer creates a USB/IP server for a specific device
|
// NewServer creates a USB/IP server for a specific device
|
||||||
@@ -40,6 +45,7 @@ func NewServer(dev *usb.Device) *Server {
|
|||||||
return &Server{
|
return &Server{
|
||||||
device: dev,
|
device: dev,
|
||||||
pendingURBs: make(map[uint32]*pendingURB),
|
pendingURBs: make(map[uint32]*pendingURB),
|
||||||
|
epTypes: make(map[uint8]uint8),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,6 +57,9 @@ func (s *Server) Attach() error {
|
|||||||
}
|
}
|
||||||
s.handle = handle
|
s.handle = handle
|
||||||
|
|
||||||
|
// Build endpoint type map from device info (must be done before driver detach)
|
||||||
|
s.buildEndpointTypeMap()
|
||||||
|
|
||||||
// For each interface: disconnect kernel driver and claim it
|
// For each interface: disconnect kernel driver and claim it
|
||||||
for _, iface := range s.device.Interfaces {
|
for _, iface := range s.device.Interfaces {
|
||||||
ifnum := uint32(iface.Number)
|
ifnum := uint32(iface.Number)
|
||||||
@@ -108,6 +117,43 @@ func (s *Server) Detach() {
|
|||||||
s.handle = nil
|
s.handle = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// buildEndpointTypeMap builds the endpoint number -> URB type map from device descriptors
|
||||||
|
func (s *Server) buildEndpointTypeMap() {
|
||||||
|
for _, iface := range s.device.Interfaces {
|
||||||
|
for _, ep := range iface.Endpoints {
|
||||||
|
epNum := ep.Address & 0x0F
|
||||||
|
// Map USB descriptor transfer type to usbdevfs URB type
|
||||||
|
var urbType uint8
|
||||||
|
switch ep.TransferType {
|
||||||
|
case usb.TransferTypeControl:
|
||||||
|
urbType = 2
|
||||||
|
case usb.TransferTypeIsochronous:
|
||||||
|
urbType = 0
|
||||||
|
case usb.TransferTypeBulk:
|
||||||
|
urbType = 3
|
||||||
|
case usb.TransferTypeInterrupt:
|
||||||
|
urbType = 1
|
||||||
|
default:
|
||||||
|
urbType = 3 // default bulk
|
||||||
|
}
|
||||||
|
s.epTypes[epNum] = urbType
|
||||||
|
typeNames := map[uint8]string{0: "ISO", 1: "interrupt", 2: "control", 3: "bulk"}
|
||||||
|
log.Printf("[usbip-server] endpoint %d (0x%02x): %s", epNum, ep.Address, typeNames[urbType])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// getURBType returns the usbdevfs URB type for an endpoint number
|
||||||
|
func (s *Server) getURBType(endpoint uint8) uint8 {
|
||||||
|
if endpoint == 0 {
|
||||||
|
return 2 // control
|
||||||
|
}
|
||||||
|
if t, ok := s.epTypes[endpoint]; ok {
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
return 3 // default: bulk
|
||||||
|
}
|
||||||
|
|
||||||
// BuildDeviceDescriptor creates a USB/IP device descriptor from our device info
|
// BuildDeviceDescriptor creates a USB/IP device descriptor from our device info
|
||||||
func (s *Server) BuildDeviceDescriptor() DeviceDescriptor {
|
func (s *Server) BuildDeviceDescriptor() DeviceDescriptor {
|
||||||
var desc DeviceDescriptor
|
var desc DeviceDescriptor
|
||||||
@@ -211,26 +257,21 @@ func (s *Server) handleCmdSubmit(r io.Reader, hdr *URBHeader, retChan chan<- []b
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Read ISO packet descriptors if present
|
// Read ISO packet descriptors if present
|
||||||
|
var isoDescs []ISOPacketDescriptor
|
||||||
|
numPackets := int32(0)
|
||||||
if body.NumberOfPackets != 0xFFFFFFFF && body.NumberOfPackets > 0 {
|
if body.NumberOfPackets != 0xFFFFFFFF && body.NumberOfPackets > 0 {
|
||||||
isoDescs := make([]ISOPacketDescriptor, body.NumberOfPackets)
|
numPackets = int32(body.NumberOfPackets)
|
||||||
|
isoDescs = make([]ISOPacketDescriptor, numPackets)
|
||||||
if err := binary.Read(r, binary.BigEndian, &isoDescs); err != nil {
|
if err := binary.Read(r, binary.BigEndian, &isoDescs); err != nil {
|
||||||
return fmt.Errorf("reading ISO descriptors: %w", err)
|
return fmt.Errorf("reading ISO descriptors: %w", err)
|
||||||
}
|
}
|
||||||
// TODO: handle ISO transfers properly
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine URB type from endpoint
|
|
||||||
endpoint := uint8(hdr.Endpoint)
|
endpoint := uint8(hdr.Endpoint)
|
||||||
var urbType uint8
|
urbType := s.getURBType(endpoint)
|
||||||
if endpoint == 0 {
|
|
||||||
urbType = 2 // control
|
|
||||||
} else {
|
|
||||||
urbType = 3 // bulk (most common, we'll detect interrupt from endpoint descriptor later)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle control transfers specially (endpoint 0)
|
// Handle control transfers specially (endpoint 0)
|
||||||
if endpoint == 0 && hdr.Direction == DirIn {
|
if endpoint == 0 && hdr.Direction == DirIn {
|
||||||
// Control IN: send setup packet, receive data
|
|
||||||
buf := make([]byte, body.TransferBufferLen)
|
buf := make([]byte, body.TransferBufferLen)
|
||||||
n, err := s.handle.ControlTransfer(
|
n, err := s.handle.ControlTransfer(
|
||||||
body.Setup[0], body.Setup[1],
|
body.Setup[0], body.Setup[1],
|
||||||
@@ -253,7 +294,6 @@ func (s *Server) handleCmdSubmit(r io.Reader, hdr *URBHeader, retChan chan<- []b
|
|||||||
}
|
}
|
||||||
|
|
||||||
if endpoint == 0 && hdr.Direction == DirOut {
|
if endpoint == 0 && hdr.Direction == DirOut {
|
||||||
// Control OUT
|
|
||||||
buf := transferBuf
|
buf := transferBuf
|
||||||
if buf == nil {
|
if buf == nil {
|
||||||
buf = make([]byte, 0)
|
buf = make([]byte, 0)
|
||||||
@@ -277,7 +317,17 @@ func (s *Server) handleCmdSubmit(r io.Reader, hdr *URBHeader, retChan chan<- []b
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// For non-control transfers, submit asynchronously
|
ep := endpoint
|
||||||
|
if hdr.Direction == DirIn {
|
||||||
|
ep |= 0x80
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle isochronous transfers
|
||||||
|
if urbType == 0 && numPackets > 0 {
|
||||||
|
return s.handleISOSubmit(hdr, body, transferBuf, isoDescs, numPackets, ep, retChan)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bulk and interrupt transfers
|
||||||
var buf []byte
|
var buf []byte
|
||||||
if hdr.Direction == DirIn {
|
if hdr.Direction == DirIn {
|
||||||
buf = make([]byte, body.TransferBufferLen)
|
buf = make([]byte, body.TransferBufferLen)
|
||||||
@@ -285,20 +335,14 @@ func (s *Server) handleCmdSubmit(r io.Reader, hdr *URBHeader, retChan chan<- []b
|
|||||||
buf = transferBuf
|
buf = transferBuf
|
||||||
}
|
}
|
||||||
|
|
||||||
ep := endpoint
|
|
||||||
if hdr.Direction == DirIn {
|
|
||||||
ep |= 0x80
|
|
||||||
}
|
|
||||||
|
|
||||||
urb, err := s.handle.SubmitURB(&usb.SubmitURBParams{
|
urb, err := s.handle.SubmitURB(&usb.SubmitURBParams{
|
||||||
Type: urbType,
|
Type: urbType,
|
||||||
Endpoint: ep,
|
Endpoint: ep,
|
||||||
Flags: 0,
|
Flags: 0,
|
||||||
Buffer: buf,
|
Buffer: buf,
|
||||||
UserContext: uintptr(hdr.SeqNum),
|
UserContext: uintptr(hdr.SeqNum),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Submit failed - send error response immediately
|
|
||||||
resp, _ := BuildRetSubmit(hdr.SeqNum, hdr.DevID, hdr.Direction, hdr.Endpoint, -32, nil)
|
resp, _ := BuildRetSubmit(hdr.SeqNum, hdr.DevID, hdr.Direction, hdr.Endpoint, -32, nil)
|
||||||
retChan <- resp
|
retChan <- resp
|
||||||
return nil
|
return nil
|
||||||
@@ -318,6 +362,73 @@ func (s *Server) handleCmdSubmit(r io.Reader, hdr *URBHeader, retChan chan<- []b
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handleISOSubmit handles isochronous URB submission
|
||||||
|
func (s *Server) handleISOSubmit(hdr *URBHeader, body *CmdSubmitBody, transferBuf []byte,
|
||||||
|
isoDescs []ISOPacketDescriptor, numPackets int32, ep uint8, retChan chan<- []byte) error {
|
||||||
|
|
||||||
|
// Collect packet lengths and compute total buffer size
|
||||||
|
packetLens := make([]uint32, numPackets)
|
||||||
|
var totalBufLen uint32
|
||||||
|
for i := int32(0); i < numPackets; i++ {
|
||||||
|
packetLens[i] = isoDescs[i].Length
|
||||||
|
totalBufLen += isoDescs[i].Length
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare buffer
|
||||||
|
var buf []byte
|
||||||
|
if hdr.Direction == DirIn {
|
||||||
|
buf = make([]byte, totalBufLen)
|
||||||
|
} else {
|
||||||
|
// For OUT: the incoming data is packed (compact), expand to sequential layout
|
||||||
|
buf = make([]byte, totalBufLen)
|
||||||
|
if transferBuf != nil {
|
||||||
|
srcOff := uint32(0)
|
||||||
|
dstOff := uint32(0)
|
||||||
|
for i := int32(0); i < numPackets; i++ {
|
||||||
|
pktLen := packetLens[i]
|
||||||
|
if srcOff+pktLen <= uint32(len(transferBuf)) {
|
||||||
|
copy(buf[dstOff:dstOff+pktLen], transferBuf[srcOff:srcOff+pktLen])
|
||||||
|
}
|
||||||
|
srcOff += pktLen
|
||||||
|
dstOff += pktLen
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Submit ISO URB
|
||||||
|
urb, isoMem, err := s.handle.SubmitISOURB(&usb.SubmitISOURBParams{
|
||||||
|
Endpoint: ep,
|
||||||
|
Flags: 0x02, // URB_ISO_ASAP
|
||||||
|
Buffer: buf,
|
||||||
|
NumberOfPackets: numPackets,
|
||||||
|
PacketLengths: packetLens,
|
||||||
|
UserContext: uintptr(hdr.SeqNum),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
// Submit failed - send error response
|
||||||
|
resp, _ := BuildRetSubmit(hdr.SeqNum, hdr.DevID, hdr.Direction, hdr.Endpoint, -32, nil)
|
||||||
|
retChan <- resp
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
s.mu.Lock()
|
||||||
|
s.pendingURBs[hdr.SeqNum] = &pendingURB{
|
||||||
|
seqNum: hdr.SeqNum,
|
||||||
|
devID: hdr.DevID,
|
||||||
|
direction: hdr.Direction,
|
||||||
|
endpoint: hdr.Endpoint,
|
||||||
|
buffer: buf,
|
||||||
|
urbPtr: unsafe.Pointer(urb),
|
||||||
|
isISO: true,
|
||||||
|
numPackets: numPackets,
|
||||||
|
isoMem: isoMem,
|
||||||
|
packetLens: packetLens,
|
||||||
|
}
|
||||||
|
s.mu.Unlock()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Server) handleCmdUnlink(r io.Reader, hdr *URBHeader, retChan chan<- []byte) error {
|
func (s *Server) handleCmdUnlink(r io.Reader, hdr *URBHeader, retChan chan<- []byte) error {
|
||||||
body, err := ReadCmdUnlink(r)
|
body, err := ReadCmdUnlink(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -333,13 +444,10 @@ func (s *Server) handleCmdUnlink(r io.Reader, hdr *URBHeader, retChan chan<- []b
|
|||||||
|
|
||||||
var status int32
|
var status int32
|
||||||
if exists && pending.urbPtr != nil {
|
if exists && pending.urbPtr != nil {
|
||||||
// Try to discard the URB
|
// Try to discard the URB via proper ioctl
|
||||||
// Note: we cast back to the URB type for the ioctl
|
|
||||||
urbForDiscard := (*usbDevfsURBForDiscard)(pending.urbPtr)
|
|
||||||
_, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(s.handle.Fd()),
|
_, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(s.handle.Fd()),
|
||||||
uintptr(0x8000550B), // USBDEVFS_DISCARDURB
|
uintptr(0x8000550B), // USBDEVFS_DISCARDURB
|
||||||
uintptr(pending.urbPtr))
|
uintptr(pending.urbPtr))
|
||||||
_ = urbForDiscard
|
|
||||||
if errno == 0 {
|
if errno == 0 {
|
||||||
status = -104 // -ECONNRESET
|
status = -104 // -ECONNRESET
|
||||||
}
|
}
|
||||||
@@ -354,9 +462,6 @@ func (s *Server) handleCmdUnlink(r io.Reader, hdr *URBHeader, retChan chan<- []b
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// usbDevfsURBForDiscard is a placeholder to make the Go compiler happy
|
|
||||||
type usbDevfsURBForDiscard struct{}
|
|
||||||
|
|
||||||
// reapLoop continuously reaps completed URBs and sends responses
|
// reapLoop continuously reaps completed URBs and sends responses
|
||||||
func (s *Server) reapLoop(retChan chan<- []byte, done <-chan struct{}) {
|
func (s *Server) reapLoop(retChan chan<- []byte, done <-chan struct{}) {
|
||||||
for {
|
for {
|
||||||
@@ -373,9 +478,8 @@ func (s *Server) reapLoop(retChan chan<- []byte, done <-chan struct{}) {
|
|||||||
}
|
}
|
||||||
s.mu.Unlock()
|
s.mu.Unlock()
|
||||||
|
|
||||||
urb, err := s.handle.ReapURB()
|
urbInfo, err := s.handle.ReapURBInfo()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Check if we should stop
|
|
||||||
select {
|
select {
|
||||||
case <-done:
|
case <-done:
|
||||||
return
|
return
|
||||||
@@ -384,7 +488,7 @@ func (s *Server) reapLoop(retChan chan<- []byte, done <-chan struct{}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
seqNum := uint32(urb.UserContext)
|
seqNum := uint32(urbInfo.UserContext)
|
||||||
|
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
pending, exists := s.pendingURBs[seqNum]
|
pending, exists := s.pendingURBs[seqNum]
|
||||||
@@ -397,19 +501,23 @@ func (s *Server) reapLoop(retChan chan<- []byte, done <-chan struct{}) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
var data []byte
|
var resp []byte
|
||||||
if pending.direction == DirIn && urb.ActualLength > 0 {
|
if pending.isISO {
|
||||||
data = pending.buffer[:urb.ActualLength]
|
resp, err = s.buildISOResponse(urbInfo, pending)
|
||||||
|
} else {
|
||||||
|
var data []byte
|
||||||
|
if pending.direction == DirIn && urbInfo.ActualLength > 0 {
|
||||||
|
data = pending.buffer[:urbInfo.ActualLength]
|
||||||
|
}
|
||||||
|
resp, err = BuildRetSubmit(
|
||||||
|
pending.seqNum,
|
||||||
|
pending.devID,
|
||||||
|
pending.direction,
|
||||||
|
pending.endpoint,
|
||||||
|
urbInfo.Status,
|
||||||
|
data,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := BuildRetSubmit(
|
|
||||||
pending.seqNum,
|
|
||||||
pending.devID,
|
|
||||||
pending.direction,
|
|
||||||
pending.endpoint,
|
|
||||||
urb.Status,
|
|
||||||
data,
|
|
||||||
)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -422,6 +530,54 @@ func (s *Server) reapLoop(retChan chan<- []byte, done <-chan struct{}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// buildISOResponse builds a RET_SUBMIT for a completed ISO URB
|
||||||
|
func (s *Server) buildISOResponse(urbInfo *usb.ReapedURBInfo, pending *pendingURB) ([]byte, error) {
|
||||||
|
// Read ISO packet results from the URB memory
|
||||||
|
isoResults := usb.ReadISOResults(pending.isoMem, pending.numPackets)
|
||||||
|
|
||||||
|
// Build USB/IP ISO descriptors and pack transfer data
|
||||||
|
var usbipDescs []ISOPacketDescriptor
|
||||||
|
var packedData []byte
|
||||||
|
bufOffset := uint32(0) // offset in our sequential buffer
|
||||||
|
|
||||||
|
for i := int32(0); i < pending.numPackets; i++ {
|
||||||
|
pktLen := pending.packetLens[i]
|
||||||
|
actualLen := isoResults[i].ActualLength
|
||||||
|
status := isoResults[i].Status
|
||||||
|
|
||||||
|
usbipDescs = append(usbipDescs, ISOPacketDescriptor{
|
||||||
|
Offset: bufOffset,
|
||||||
|
Length: pktLen,
|
||||||
|
ActualLength: actualLen,
|
||||||
|
Status: status,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Pack actual data (compact, no gaps) for IN direction
|
||||||
|
if pending.direction == DirIn && actualLen > 0 {
|
||||||
|
end := bufOffset + actualLen
|
||||||
|
if end > uint32(len(pending.buffer)) {
|
||||||
|
end = uint32(len(pending.buffer))
|
||||||
|
}
|
||||||
|
packedData = append(packedData, pending.buffer[bufOffset:end]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
bufOffset += pktLen
|
||||||
|
}
|
||||||
|
|
||||||
|
return BuildRetSubmitISO(
|
||||||
|
pending.seqNum,
|
||||||
|
pending.devID,
|
||||||
|
pending.direction,
|
||||||
|
pending.endpoint,
|
||||||
|
urbInfo.Status,
|
||||||
|
packedData,
|
||||||
|
uint32(urbInfo.StartFrame),
|
||||||
|
pending.numPackets,
|
||||||
|
urbInfo.ErrorCount,
|
||||||
|
usbipDescs,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// HandleDevlistRequest handles an OP_REQ_DEVLIST for this device
|
// HandleDevlistRequest handles an OP_REQ_DEVLIST for this device
|
||||||
func (s *Server) HandleDevlistRequest() ([]byte, error) {
|
func (s *Server) HandleDevlistRequest() ([]byte, error) {
|
||||||
desc := s.BuildDeviceDescriptor()
|
desc := s.BuildDeviceDescriptor()
|
||||||
|
|||||||
Reference in New Issue
Block a user