first commit
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
package usb
|
||||
|
||||
// Device represents a USB device
|
||||
type Device struct {
|
||||
BusID string `json:"bus_id"` // e.g. "1-1.4"
|
||||
BusNum uint32 `json:"bus_num"`
|
||||
DevNum uint32 `json:"dev_num"`
|
||||
Speed uint32 `json:"speed"`
|
||||
VendorID uint16 `json:"vendor_id"`
|
||||
ProductID uint16 `json:"product_id"`
|
||||
BcdDevice uint16 `json:"bcd_device"`
|
||||
DeviceClass uint8 `json:"device_class"`
|
||||
DeviceSubClass uint8 `json:"device_sub_class"`
|
||||
DeviceProtocol uint8 `json:"device_protocol"`
|
||||
ConfigValue uint8 `json:"config_value"`
|
||||
NumConfigs uint8 `json:"num_configs"`
|
||||
Manufacturer string `json:"manufacturer"`
|
||||
Product string `json:"product"`
|
||||
Serial string `json:"serial"`
|
||||
SysPath string `json:"sys_path"` // sysfs path
|
||||
DevPath string `json:"dev_path"` // /dev/bus/usb path
|
||||
Interfaces []Interface `json:"interfaces"`
|
||||
}
|
||||
|
||||
// Interface represents a USB interface
|
||||
type Interface struct {
|
||||
Number uint8 `json:"number"`
|
||||
Class uint8 `json:"class"`
|
||||
SubClass uint8 `json:"sub_class"`
|
||||
Protocol uint8 `json:"protocol"`
|
||||
Driver string `json:"driver"`
|
||||
}
|
||||
|
||||
// DevID returns the USB/IP device ID (busnum << 16 | devnum)
|
||||
func (d *Device) DevID() uint32 {
|
||||
return (d.BusNum << 16) | d.DevNum
|
||||
}
|
||||
|
||||
// IsHub returns true if this is a USB hub
|
||||
func (d *Device) IsHub() bool {
|
||||
return d.DeviceClass == 9
|
||||
}
|
||||
|
||||
// DisplayName returns a human-readable device name
|
||||
func (d *Device) DisplayName() string {
|
||||
if d.Product != "" {
|
||||
if d.Manufacturer != "" {
|
||||
return d.Manufacturer + " " + d.Product
|
||||
}
|
||||
return d.Product
|
||||
}
|
||||
return "Unknown USB Device"
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
//go:build linux
|
||||
|
||||
package usb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const sysfsUSBDevices = "/sys/bus/usb/devices"
|
||||
|
||||
// Enumerate lists all USB devices by reading sysfs
|
||||
func Enumerate() ([]Device, error) {
|
||||
entries, err := os.ReadDir(sysfsUSBDevices)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("reading sysfs: %w", err)
|
||||
}
|
||||
|
||||
var devices []Device
|
||||
for _, entry := range entries {
|
||||
name := entry.Name()
|
||||
|
||||
// Skip interfaces (contain ":") and "usb*" root hubs
|
||||
if strings.Contains(name, ":") || strings.HasPrefix(name, "usb") {
|
||||
continue
|
||||
}
|
||||
|
||||
// Must be a device path like "1-1", "1-1.4", "2-3", etc.
|
||||
if !isDevicePath(name) {
|
||||
continue
|
||||
}
|
||||
|
||||
dev, err := readDevice(name)
|
||||
if err != nil {
|
||||
continue // skip devices we can't read
|
||||
}
|
||||
|
||||
// Skip hubs
|
||||
if dev.IsHub() {
|
||||
continue
|
||||
}
|
||||
|
||||
devices = append(devices, *dev)
|
||||
}
|
||||
|
||||
return devices, nil
|
||||
}
|
||||
|
||||
func isDevicePath(name string) bool {
|
||||
// Device paths look like "1-1", "1-1.4", "2-3.1.2"
|
||||
// First char is a digit (bus number)
|
||||
if len(name) < 3 {
|
||||
return false
|
||||
}
|
||||
if name[0] < '1' || name[0] > '9' {
|
||||
return false
|
||||
}
|
||||
if name[1] != '-' {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func readDevice(busID string) (*Device, error) {
|
||||
sysPath := filepath.Join(sysfsUSBDevices, busID)
|
||||
|
||||
dev := &Device{
|
||||
BusID: busID,
|
||||
SysPath: sysPath,
|
||||
}
|
||||
|
||||
// Read basic attributes
|
||||
dev.BusNum = readUint32(sysPath, "busnum")
|
||||
dev.DevNum = readUint32(sysPath, "devnum")
|
||||
dev.Speed = parseSpeed(readString(sysPath, "speed"))
|
||||
dev.VendorID = readHex16(sysPath, "idVendor")
|
||||
dev.ProductID = readHex16(sysPath, "idProduct")
|
||||
dev.BcdDevice = readHex16(sysPath, "bcdDevice")
|
||||
dev.DeviceClass = readHex8(sysPath, "bDeviceClass")
|
||||
dev.DeviceSubClass = readHex8(sysPath, "bDeviceSubClass")
|
||||
dev.DeviceProtocol = readHex8(sysPath, "bDeviceProtocol")
|
||||
dev.ConfigValue = uint8(readUint32(sysPath, "bConfigurationValue"))
|
||||
dev.NumConfigs = uint8(readUint32(sysPath, "bNumConfigurations"))
|
||||
|
||||
// Read string descriptors
|
||||
dev.Manufacturer = readString(sysPath, "manufacturer")
|
||||
dev.Product = readString(sysPath, "product")
|
||||
dev.Serial = readString(sysPath, "serial")
|
||||
|
||||
// Compute dev path
|
||||
dev.DevPath = fmt.Sprintf("/dev/bus/usb/%03d/%03d", dev.BusNum, dev.DevNum)
|
||||
|
||||
// Read interfaces
|
||||
dev.Interfaces = readInterfaces(sysPath, busID)
|
||||
|
||||
return dev, nil
|
||||
}
|
||||
|
||||
func readInterfaces(sysPath, busID string) []Interface {
|
||||
entries, err := os.ReadDir(sysPath)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var ifaces []Interface
|
||||
for _, entry := range entries {
|
||||
name := entry.Name()
|
||||
// Interface directories look like "1-1.4:1.0"
|
||||
if !strings.HasPrefix(name, busID+":") {
|
||||
continue
|
||||
}
|
||||
|
||||
ifacePath := filepath.Join(sysPath, name)
|
||||
iface := Interface{
|
||||
Number: readHex8(ifacePath, "bInterfaceNumber"),
|
||||
Class: readHex8(ifacePath, "bInterfaceClass"),
|
||||
SubClass: readHex8(ifacePath, "bInterfaceSubClass"),
|
||||
Protocol: readHex8(ifacePath, "bInterfaceProtocol"),
|
||||
}
|
||||
|
||||
// Read driver
|
||||
driverLink, err := os.Readlink(filepath.Join(ifacePath, "driver"))
|
||||
if err == nil {
|
||||
iface.Driver = filepath.Base(driverLink)
|
||||
}
|
||||
|
||||
ifaces = append(ifaces, iface)
|
||||
}
|
||||
|
||||
return ifaces
|
||||
}
|
||||
|
||||
func readString(dir, attr string) string {
|
||||
data, err := os.ReadFile(filepath.Join(dir, attr))
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return strings.TrimSpace(string(data))
|
||||
}
|
||||
|
||||
func readUint32(dir, attr string) uint32 {
|
||||
s := readString(dir, attr)
|
||||
if s == "" {
|
||||
return 0
|
||||
}
|
||||
v, _ := strconv.ParseUint(s, 10, 32)
|
||||
return uint32(v)
|
||||
}
|
||||
|
||||
func readHex16(dir, attr string) uint16 {
|
||||
s := readString(dir, attr)
|
||||
if s == "" {
|
||||
return 0
|
||||
}
|
||||
v, _ := strconv.ParseUint(s, 16, 16)
|
||||
return uint16(v)
|
||||
}
|
||||
|
||||
func readHex8(dir, attr string) uint8 {
|
||||
s := readString(dir, attr)
|
||||
if s == "" {
|
||||
return 0
|
||||
}
|
||||
v, _ := strconv.ParseUint(s, 16, 8)
|
||||
return uint8(v)
|
||||
}
|
||||
|
||||
func parseSpeed(s string) uint32 {
|
||||
switch s {
|
||||
case "1.5":
|
||||
return 1 // Low
|
||||
case "12":
|
||||
return 2 // Full
|
||||
case "480":
|
||||
return 3 // High
|
||||
case "5000":
|
||||
return 5 // Super
|
||||
case "10000":
|
||||
return 6 // Super+
|
||||
case "20000":
|
||||
return 6 // Super+ (USB 3.2 2x1)
|
||||
default:
|
||||
return 0 // Unknown
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
//go:build windows
|
||||
|
||||
package usb
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Enumerate lists all USB devices (Windows stub)
|
||||
func Enumerate() ([]Device, error) {
|
||||
return nil, fmt.Errorf("USB enumeration not yet implemented on Windows")
|
||||
}
|
||||
@@ -0,0 +1,306 @@
|
||||
//go:build linux
|
||||
|
||||
package usb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// ioctl direction constants
|
||||
const (
|
||||
iocNone = 0
|
||||
iocWrite = 1
|
||||
iocRead = 2
|
||||
)
|
||||
|
||||
// ioctl encoding helpers
|
||||
func ioc(dir, typ, nr, size uintptr) uintptr {
|
||||
return (dir << 30) | (size << 16) | (typ << 8) | nr
|
||||
}
|
||||
|
||||
func ior(typ, nr, size uintptr) uintptr { return ioc(iocRead, typ, nr, size) }
|
||||
func iow(typ, nr, size uintptr) uintptr { return ioc(iocWrite, typ, nr, size) }
|
||||
func iowr(typ, nr, size uintptr) uintptr { return ioc(iocRead|iocWrite, typ, nr, size) }
|
||||
func io_(typ, nr uintptr) uintptr { return ioc(iocNone, typ, nr, 0) }
|
||||
|
||||
// USB device file system ioctl numbers
|
||||
var (
|
||||
usbdevfsControl = iowr('U', 0, unsafe.Sizeof(usbdevfsCtrlTransfer{}))
|
||||
usbdevfsBulk = iowr('U', 2, unsafe.Sizeof(usbdevfsBulkTransfer{}))
|
||||
usbdevfsSetInterface = ior('U', 4, unsafe.Sizeof(usbdevfsSetIntf{}))
|
||||
usbdevfsSetConfig = ior('U', 5, 4)
|
||||
usbdevfsSubmitURB = ior('U', 10, unsafe.Sizeof(usbdevfsURB{}))
|
||||
usbdevfsDiscardURB = io_('U', 11)
|
||||
usbdevfsReapURB = iow('U', 12, unsafe.Sizeof(uintptr(0)))
|
||||
usbdevfsReapURBNDelay = iow('U', 13, unsafe.Sizeof(uintptr(0)))
|
||||
usbdevfsClaimInterface = ior('U', 15, 4)
|
||||
usbdevfsReleaseInterface = ior('U', 16, 4)
|
||||
usbdevfsReset = io_('U', 20)
|
||||
usbdevfsClearHalt = ior('U', 21, 4)
|
||||
usbdevfsDisconnect = io_('U', 22)
|
||||
usbdevfsConnect = io_('U', 23)
|
||||
usbdevfsGetCapabilities = ior('U', 26, 4)
|
||||
usbdevfsGetSpeed = io_('U', 31)
|
||||
)
|
||||
|
||||
// URB type constants
|
||||
const (
|
||||
urbTypeISO = 0
|
||||
urbTypeInterrupt = 1
|
||||
urbTypeControl = 2
|
||||
urbTypeBulk = 3
|
||||
)
|
||||
|
||||
// usbdevfs structures for ioctls
|
||||
|
||||
type usbdevfsCtrlTransfer struct {
|
||||
RequestType uint8
|
||||
Request uint8
|
||||
Value uint16
|
||||
Index uint16
|
||||
Length uint16
|
||||
Timeout uint32
|
||||
Data uintptr
|
||||
}
|
||||
|
||||
type usbdevfsBulkTransfer struct {
|
||||
Endpoint uint32
|
||||
Length uint32
|
||||
Timeout uint32
|
||||
Data uintptr
|
||||
}
|
||||
|
||||
type usbdevfsSetIntf struct {
|
||||
Interface uint32
|
||||
AltSetting uint32
|
||||
}
|
||||
|
||||
type usbdevfsISOPacketDesc struct {
|
||||
Length uint32
|
||||
ActualLength uint32
|
||||
Status uint32
|
||||
}
|
||||
|
||||
type usbdevfsURB struct {
|
||||
Type uint8
|
||||
Endpoint uint8
|
||||
Status int32
|
||||
Flags uint32
|
||||
Buffer uintptr
|
||||
BufferLength int32
|
||||
ActualLength int32
|
||||
StartFrame int32
|
||||
NumberOfPackets int32 // or StreamID
|
||||
ErrorCount int32
|
||||
Signr uint32
|
||||
UserContext uintptr
|
||||
// ISO packet descriptors follow in memory if Type == urbTypeISO
|
||||
}
|
||||
|
||||
// DeviceHandle provides low-level USB device access via usbdevfs
|
||||
type DeviceHandle struct {
|
||||
fd int
|
||||
busID string
|
||||
devPath string
|
||||
}
|
||||
|
||||
// OpenDevice opens a USB device file for direct access
|
||||
func OpenDevice(devPath string, busID string) (*DeviceHandle, error) {
|
||||
fd, err := unix.Open(devPath, unix.O_RDWR, 0)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("opening %s: %w", devPath, err)
|
||||
}
|
||||
|
||||
return &DeviceHandle{
|
||||
fd: fd,
|
||||
busID: busID,
|
||||
devPath: devPath,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Close closes the device handle
|
||||
func (h *DeviceHandle) Close() error {
|
||||
return unix.Close(h.fd)
|
||||
}
|
||||
|
||||
// Fd returns the file descriptor
|
||||
func (h *DeviceHandle) Fd() int {
|
||||
return h.fd
|
||||
}
|
||||
|
||||
// DisconnectDriver disconnects the kernel driver from the device
|
||||
func (h *DeviceHandle) DisconnectDriver() error {
|
||||
_, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(h.fd), usbdevfsDisconnect, 0)
|
||||
if errno != 0 {
|
||||
return fmt.Errorf("USBDEVFS_DISCONNECT: %w", errno)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ConnectDriver reconnects the kernel driver
|
||||
func (h *DeviceHandle) ConnectDriver() error {
|
||||
_, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(h.fd), usbdevfsConnect, 0)
|
||||
if errno != 0 {
|
||||
return fmt.Errorf("USBDEVFS_CONNECT: %w", errno)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ClaimInterface claims exclusive access to a USB interface
|
||||
func (h *DeviceHandle) ClaimInterface(ifnum uint32) error {
|
||||
_, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(h.fd), usbdevfsClaimInterface, uintptr(unsafe.Pointer(&ifnum)))
|
||||
if errno != 0 {
|
||||
return fmt.Errorf("USBDEVFS_CLAIMINTERFACE(%d): %w", ifnum, errno)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReleaseInterface releases a claimed interface
|
||||
func (h *DeviceHandle) ReleaseInterface(ifnum uint32) error {
|
||||
_, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(h.fd), usbdevfsReleaseInterface, uintptr(unsafe.Pointer(&ifnum)))
|
||||
if errno != 0 {
|
||||
return fmt.Errorf("USBDEVFS_RELEASEINTERFACE(%d): %w", ifnum, errno)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetConfiguration sets the device configuration
|
||||
func (h *DeviceHandle) SetConfiguration(config uint32) error {
|
||||
_, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(h.fd), usbdevfsSetConfig, uintptr(unsafe.Pointer(&config)))
|
||||
if errno != 0 {
|
||||
return fmt.Errorf("USBDEVFS_SETCONFIGURATION(%d): %w", config, errno)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetInterface sets alternate setting for an interface
|
||||
func (h *DeviceHandle) SetInterface(iface, altSetting uint32) error {
|
||||
si := usbdevfsSetIntf{Interface: iface, AltSetting: altSetting}
|
||||
_, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(h.fd), usbdevfsSetInterface, uintptr(unsafe.Pointer(&si)))
|
||||
if errno != 0 {
|
||||
return fmt.Errorf("USBDEVFS_SETINTERFACE(%d, %d): %w", iface, altSetting, errno)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ClearHalt clears endpoint halt/stall condition
|
||||
func (h *DeviceHandle) ClearHalt(endpoint uint32) error {
|
||||
_, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(h.fd), usbdevfsClearHalt, uintptr(unsafe.Pointer(&endpoint)))
|
||||
if errno != 0 {
|
||||
return fmt.Errorf("USBDEVFS_CLEAR_HALT(%d): %w", endpoint, errno)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ResetDevice resets the USB device
|
||||
func (h *DeviceHandle) ResetDevice() error {
|
||||
_, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(h.fd), usbdevfsReset, 0)
|
||||
if errno != 0 {
|
||||
return fmt.Errorf("USBDEVFS_RESET: %w", errno)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetSpeed returns the device speed
|
||||
func (h *DeviceHandle) GetSpeed() (uint32, error) {
|
||||
r, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(h.fd), usbdevfsGetSpeed, 0)
|
||||
if errno != 0 {
|
||||
return 0, fmt.Errorf("USBDEVFS_GET_SPEED: %w", errno)
|
||||
}
|
||||
return uint32(r), nil
|
||||
}
|
||||
|
||||
// ControlTransfer performs a synchronous control transfer
|
||||
func (h *DeviceHandle) ControlTransfer(requestType, request uint8, value, index, length uint16, timeout uint32, data []byte) (int, error) {
|
||||
var dataPtr uintptr
|
||||
if len(data) > 0 {
|
||||
dataPtr = uintptr(unsafe.Pointer(&data[0]))
|
||||
}
|
||||
|
||||
ct := usbdevfsCtrlTransfer{
|
||||
RequestType: requestType,
|
||||
Request: request,
|
||||
Value: value,
|
||||
Index: index,
|
||||
Length: length,
|
||||
Timeout: timeout,
|
||||
Data: dataPtr,
|
||||
}
|
||||
|
||||
r, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(h.fd), usbdevfsControl, uintptr(unsafe.Pointer(&ct)))
|
||||
if errno != 0 {
|
||||
return 0, fmt.Errorf("USBDEVFS_CONTROL: %w", errno)
|
||||
}
|
||||
return int(r), nil
|
||||
}
|
||||
|
||||
// SubmitURBParams holds parameters for async URB submission
|
||||
type SubmitURBParams struct {
|
||||
Type uint8
|
||||
Endpoint uint8
|
||||
Flags uint32
|
||||
Buffer []byte
|
||||
UserContext uintptr
|
||||
}
|
||||
|
||||
// SubmitURB submits an asynchronous URB
|
||||
func (h *DeviceHandle) SubmitURB(params *SubmitURBParams) (*usbdevfsURB, error) {
|
||||
var bufPtr uintptr
|
||||
if len(params.Buffer) > 0 {
|
||||
bufPtr = uintptr(unsafe.Pointer(¶ms.Buffer[0]))
|
||||
}
|
||||
|
||||
urb := &usbdevfsURB{
|
||||
Type: params.Type,
|
||||
Endpoint: params.Endpoint,
|
||||
Flags: params.Flags,
|
||||
Buffer: bufPtr,
|
||||
BufferLength: int32(len(params.Buffer)),
|
||||
NumberOfPackets: -1, // 0xFFFFFFFF for non-ISO
|
||||
UserContext: params.UserContext,
|
||||
}
|
||||
|
||||
_, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(h.fd), usbdevfsSubmitURB, uintptr(unsafe.Pointer(urb)))
|
||||
if errno != 0 {
|
||||
return nil, fmt.Errorf("USBDEVFS_SUBMITURB: %w", errno)
|
||||
}
|
||||
return urb, nil
|
||||
}
|
||||
|
||||
// ReapURB blocks until a URB completes, then returns it
|
||||
func (h *DeviceHandle) ReapURB() (*usbdevfsURB, 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)
|
||||
}
|
||||
return (*usbdevfsURB)(unsafe.Pointer(urbPtr)), nil
|
||||
}
|
||||
|
||||
// ReapURBNonBlock tries to reap a URB without blocking
|
||||
func (h *DeviceHandle) ReapURBNonBlock() (*usbdevfsURB, error) {
|
||||
var urbPtr uintptr
|
||||
_, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(h.fd), usbdevfsReapURBNDelay, uintptr(unsafe.Pointer(&urbPtr)))
|
||||
if errno != 0 {
|
||||
return nil, fmt.Errorf("USBDEVFS_REAPURBNDELAY: %w", errno)
|
||||
}
|
||||
return (*usbdevfsURB)(unsafe.Pointer(urbPtr)), nil
|
||||
}
|
||||
|
||||
// DiscardURB cancels a submitted URB
|
||||
func (h *DeviceHandle) DiscardURB(urb *usbdevfsURB) error {
|
||||
_, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(h.fd), usbdevfsDiscardURB, uintptr(unsafe.Pointer(urb)))
|
||||
if errno != 0 {
|
||||
return fmt.Errorf("USBDEVFS_DISCARDURB: %w", errno)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetFile returns an os.File wrapping the device fd (useful for epoll/select)
|
||||
func (h *DeviceHandle) GetFile() *os.File {
|
||||
return os.NewFile(uintptr(h.fd), h.devPath)
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
//go:build windows
|
||||
|
||||
package usb
|
||||
|
||||
import "fmt"
|
||||
|
||||
// DeviceHandle provides USB device access (Windows stub)
|
||||
type DeviceHandle struct{}
|
||||
|
||||
func OpenDevice(devPath string, busID string) (*DeviceHandle, error) {
|
||||
return nil, fmt.Errorf("USB device access not yet implemented on Windows")
|
||||
}
|
||||
|
||||
func (h *DeviceHandle) Close() error { return nil }
|
||||
func (h *DeviceHandle) Fd() int { return -1 }
|
||||
func (h *DeviceHandle) DisconnectDriver() error { return fmt.Errorf("not implemented") }
|
||||
func (h *DeviceHandle) ConnectDriver() error { return fmt.Errorf("not implemented") }
|
||||
func (h *DeviceHandle) ClaimInterface(uint32) error { return fmt.Errorf("not implemented") }
|
||||
func (h *DeviceHandle) ReleaseInterface(uint32) error { return fmt.Errorf("not implemented") }
|
||||
Reference in New Issue
Block a user