Livt packages
Livt.Usb
Version 0.0.5
USB 2.0 low/full-speed PHY, host transfers, device discovery, HID keyboard, and read-only mass storage for Livt.
Published files (57)
src/host/DeviceEnumerator.lvt
namespace Livt.Usb.Host
using Livt.IO
using Livt.Usb
using Livt.Usb.Descriptors
/**
* Bounded direct-device enumeration: address one, first configuration, alternate zero.
* One scheduled owner calls Enumerate and then owns the controller for class traffic.
* Keeps standard descriptor discovery independent of class-specific initialization.
*/
component DeviceEnumerator<LIMIT: int = 512,
ENDPOINT_STORAGE: IRam<byte> & new() = SequentialRam<byte, 32, MemoryStyle.Block>>
{
// Round two milliseconds up to whole ticks, including non-integral-MHz contexts.
const ADDRESS_RECOVERY_TICKS: uint = (this.context.TicksPer(1Hz) - 1) / 500 + 1
const ADDRESS_RECOVERY_BITS: int = Bits.RequiredFor(ADDRESS_RECOVERY_TICKS)
// Successful discovery requests are bounded by LIMIT.
const COUNT_BITS: int = Bits.RequiredFor(LIMIT)
// Borrowed controller and owned parsers/request helper.
controller: IHostController
control: ControlTransfer
device: DeviceDescriptorParser
configuration: ConfigurationDescriptorParser<LIMIT, ENDPOINT_STORAGE>
packetSize: byte
configured: bool
phase: byte
// Read-only discovery observations; configuration is zero until selected.
public identity: out logic[32]
public packetBytes: out byte
public configurationValue: out byte
/**
* Binds a controller with at least LIMIT input bytes for configuration discovery.
*/
new(controller: IHostController)
{
this.controller = controller
control = new ControlTransfer(controller)
device = new DeviceDescriptorParser()
configuration = new ConfigurationDescriptorParser<LIMIT, ENDPOINT_STORAGE>()
}
/**
* Forwards validated descriptor identity and current EP0 geometry without copies.
*/
process Observations[]()
{
identity = 0
packetBytes = packetSize
if (phase >= 4) {
identity = device.identity
}
}
/**
* Returns the discovery phase for diagnostics; 6 means configured, not class-ready.
*/
public fn GetPhase() byte
{
return phase
}
/**
* Reports the current EP0 packet size (initially eight).
*/
public fn GetPacketSize() int
{
return packetSize as int
}
/**
* Reports the validated vendor/product identity (vendor in the upper half).
*/
public fn GetIdentity() logic[32]
{
return identity
}
/**
* Returns the selected configuration value, or zero before successful setup.
*/
public fn GetConfiguration() int
{
if (!configured) {
return 0
}
return configurationValue as int
}
/**
* Returns a bounded standard endpoint field only after configuration succeeds.
*/
public fn GetEndpoint(index: int, field: int) int
{
if (!configured) { return -1 }
return configuration.GetEndpoint(index, field)
}
/**
* Reads retained control data into the selected bounded descriptor parser.
*/
fn Parse(deviceDescriptor: bool)
{
var count = controller.GetActualLength() as logic[COUNT_BITS]
for (var index: logic[COUNT_BITS] = 0; index < count; index++) {
var value: byte
if (!controller.TryReadInput((index as uint) as int, value)) { break }
if (deviceDescriptor) { device.Feed(value) } else { configuration.Feed(value) }
}
controller.TryRelease()
}
/**
* Enumerates one freshly reset device. Every failed request is released; caller
* may wait for unplug or request a new root-port reset before another attempt.
*/
public fn Enumerate() bool
{
configured = false;
packetSize = 8;
configurationValue = 0;
phase = 1
device.Reset()
configuration.Reset()
if (!control.Run(0, 8, 128, 6, 256, 0, 8)) {
controller.TryRelease();
return false
}
Parse(true)
if (!device.HasPacketSize(controller.GetSpeed())) { return false }
packetSize = device.GetPacketSize() as byte
phase = 2
if (!control.Run(0, packetSize as int, 0, 5, 1, 0, 0)) {
controller.TryRelease();
return false
}
controller.TryRelease()
// USB SET_ADDRESS recovery is at least two milliseconds.
for (var cycle: logic[ADDRESS_RECOVERY_BITS] = 0; (cycle as uint) < ADDRESS_RECOVERY_TICKS; cycle++) {
state {}
}
phase = 3
device.Reset()
if (!control.Run(1, packetSize as int, 128, 6, 256, 0, 18)) {
controller.TryRelease();
return false
}
Parse(true)
if (!device.IsValid(controller.GetSpeed())) { return false }
phase = 4
if (!control.Run(1, packetSize as int, 128, 6, 512, 0, 9)) {
controller.TryRelease();
return false
}
Parse(false)
var length = configuration.GetTotalLength()
if (length < 9 || length > controller.GetTransferCapacity()) { return false }
configuration.Reset()
if (!control.Run(1, packetSize as int, 128, 6, 512, 0, length)) {
controller.TryRelease();
return false
}
Parse(false)
if (!configuration.IsValid(controller.GetSpeed())) { return false }
phase = 5
var selectedConfiguration = configuration.GetConfiguration() as byte
if (!control.Run(1, packetSize as int, 0, 9, selectedConfiguration as int, 0, 0)) {
controller.TryRelease();
return false
}
controller.TryRelease()
configurationValue = selectedConfiguration
configured = true;
phase = 6
return true
}
}