package usb import ( "fmt" "sync" ) // Externally registered devices. // // Normally devices are found by walking sysfs. That is not available to an // unprivileged Android app, which must go through the framework: it enumerates // devices itself, asks the user for permission, and receives an already-open // file descriptor plus the raw descriptor blob. Those devices are registered // here and merged into the enumeration, so everything above this layer works // the same whether a device came from sysfs or from outside. var ( externalMu sync.RWMutex externalDevices = make(map[string]Device) ) // RegisterExternalDevice adds a device that was discovered outside this // process. descriptors is the raw blob (device descriptor followed by // configuration descriptors), exactly what a usbdevfs file read returns and // what Android's UsbDeviceConnection.getRawDescriptors() provides. func RegisterExternalDevice(busID string, descriptors []byte, meta ExternalDeviceMeta) error { if busID == "" { return fmt.Errorf("bus ID is required") } parsed, err := ParseDescriptors(descriptors) if err != nil { return fmt.Errorf("parsing descriptors for %s: %w", busID, err) } cfg := parsed.FindConfig(meta.ConfigValue) if cfg == nil { cfg = &parsed.Configs[0] } dev := Device{ BusID: busID, BusNum: meta.BusNum, DevNum: meta.DevNum, Speed: meta.Speed, VendorID: parsed.VendorID, ProductID: parsed.ProductID, BcdDevice: parsed.BcdDevice, DeviceClass: parsed.DeviceClass, DeviceSubClass: parsed.DeviceSubClass, DeviceProtocol: parsed.DeviceProtocol, ConfigValue: cfg.Value, NumConfigs: parsed.NumConfigs, Manufacturer: meta.Manufacturer, Product: meta.Product, Serial: meta.Serial, Interfaces: cfg.ActiveInterfaces(), Endpoints: cfg.AllEndpoints(), } externalMu.Lock() externalDevices[busID] = dev externalMu.Unlock() return nil } // ExternalDeviceMeta carries the fields that cannot be read from the // descriptor blob because they describe the device's place on the bus or come // from string descriptors the caller already resolved. type ExternalDeviceMeta struct { BusNum uint32 DevNum uint32 Speed uint32 ConfigValue uint8 Manufacturer string Product string Serial string } // UnregisterExternalDevice removes a device registered from outside. func UnregisterExternalDevice(busID string) { externalMu.Lock() delete(externalDevices, busID) externalMu.Unlock() } // ExternalDevices returns a snapshot of the externally registered devices. func ExternalDevices() []Device { externalMu.RLock() defer externalMu.RUnlock() result := make([]Device, 0, len(externalDevices)) for _, dev := range externalDevices { result = append(result, dev) } return result } // HasExternalDevices reports whether any device came from outside. func HasExternalDevices() bool { externalMu.RLock() defer externalMu.RUnlock() return len(externalDevices) > 0 } // mergeExternal appends externally registered devices to a list from sysfs, // letting the external entry win on a bus ID collision — it carries a file // descriptor we can actually use, which the sysfs entry may not. func mergeExternal(devices []Device) []Device { externalMu.RLock() defer externalMu.RUnlock() if len(externalDevices) == 0 { return devices } result := make([]Device, 0, len(devices)+len(externalDevices)) for _, dev := range devices { if _, overridden := externalDevices[dev.BusID]; !overridden { result = append(result, dev) } } for _, dev := range externalDevices { result = append(result, dev) } return result }