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/TransferStorage.lvt
namespace Livt.Usb.Host
using Livt.IO
using Livt.Usb
using Livt.Usb.Packets
/**
* Owns eight setup registers and configurable payload storage for one transfer.
* The controller serializes access and freezes staging while a request is owned.
* Only initialized bytes are exposed; Clear discards metadata without RAM erasure.
*/
component TransferStorage<STAGING_BYTES: int = 512, STYLE: MemoryStyle = MemoryStyle.Block,
B: IRam<byte> & new() = Ram<byte, STAGING_BYTES, STYLE>>
{
static assert STAGING_BYTES >= 8 && STAGING_BYTES <= 65535, "Transfer capacity must be 8..65535 bytes"
// Fixed setup metadata and its initialized prefix.
setup: byte[8]
setupLength: int
// Owned payload backend; no USB packet-timing guarantee from scheduled access.
payload: PacketBuffer<STAGING_BYTES, STYLE, B>
/**
* Creates empty setup and data storage in the inherited context.
*/
new()
{
payload = new PacketBuffer<STAGING_BYTES, STYLE, B>()
}
/**
* Appends a setup byte, returning false without change when all eight are present.
*/
public fn TryAppendSetup(value: byte) bool
{
if (setupLength == 8) {
return false
}
setup[setupLength] = value
setupLength++
return true
}
/**
* Checks exact setup/data staging and the control direction/wLength fields.
*/
public fn IsValid(kind: TransferKind, direction: TransferDirection, length: int) bool
{
if (kind == TransferKind.Control) {
if (setupLength != 8) {
return false
}
var setupBytes = (setup[6] as int) + (setup[7] as int) * 256
if (setupBytes != length) {
return false
}
if (length > 0) {
var input = (setup[0] as uint) >= 128
if (input != (direction == TransferDirection.DeviceToHost)) {
return false
}
} else {
if (direction != TransferDirection.HostToDevice) {
return false
}
}
} else {
if (setupLength != 0) {
return false
}
}
if (direction == TransferDirection.HostToDevice) {
return payload.GetLength() == length
}
return payload.GetLength() == 0
}
/**
* Appends initialized payload bytes; controller ownership determines their direction.
*/
public fn TryAppendPayload(value: byte) bool
{
return payload.TryAppend(value)
}
/**
* Returns the initialized payload length, excluding setup.
*/
public fn GetLength() int
{
return payload.GetLength()
}
/**
* Freezes payload bytes for readers; the owner calls this once per lifecycle.
*/
public fn Publish()
{
payload.TryPublish()
}
/**
* Reads a published payload byte, returning false and zero on invalid access.
*/
public fn TryReadPayload(index: int, value: out byte) bool
{
return payload.TryRead(index, value)
}
/**
* Reads initialized setup data for the controller; failure returns false and zero.
*/
public fn TryReadSetup(index: int, value: out byte) bool
{
value = 0
if (index < 0 || index >= setupLength) {
return false
}
value = setup[index]
return true
}
/**
* Discards all initialized prefixes after the controller releases the transfer.
*/
public fn Clear()
{
setupLength = 0
payload.Clear()
}
}