Livt packages

Livt.Net

Version 1.4.1

Ethernet, IPv4, TCP, UDP, ARP, TFTP, and Telnet networking components for Livt hardware.

Published files (163)

src/drivers/ethernetlite/EthernetLiteTransmitter.lvt

namespace Livt.Net.Drivers.EthernetLite

using Livt.Net

enum EthernetLiteTransmitState
{
	Idle
	Borrowed
	Terminal
}

/**
 * Owns submission/completion metadata and borrows a statically bound source.
 * The driver alone reads pending bytes and publishes terminal outcomes.
 * No copy is required when the application already owns prepared packet storage.
 */
component EthernetLiteTransmitter<S: IPacketData, MAX_LENGTH: int = 1514> : IFrameTransmitter
{
	static assert MAX_LENGTH >= 60 && MAX_LENGTH <= 1514, "TX length must fit a standard EthernetLite frame"

	const LENGTH_BITS: int = Bits.RequiredFor(MAX_LENGTH)

	// Prepared source remains owned by its caller and stable during a borrow.
	source: S
	phase: EthernetLiteTransmitState
	length: logic[LENGTH_BITS]
	ready: bool
	failed: bool
	outcome: FrameTransmitOutcome

	/**
	 * Binds prepared data. Admission remains disabled until MAC initialization succeeds.
	 */
	new(source: S)
	{
		this.source = source
	}

	/**
	 * Composer-supplied Ethernet padding must bring frames to at least 60 bytes.
	 */
	override fn GetMinimumFrameLength() int
	{
		return 60
	}

	/**
	 * Reports the configured device frame limit, excluding FCS.
	 */
	override fn GetMaximumFrameLength() int
	{
		return MAX_LENGTH
	}

	/**
	 * Device transmission cannot be cancelled through this driver.
	 */
	override fn SupportsCancellation() bool
	{
		return false
	}

	/**
	 * Keeps the source borrowed through terminal device acceptance or failure.
	 */
	override fn IsSourceBorrowed() bool
	{
		return this.phase == EthernetLiteTransmitState.Borrowed
	}

	/**
	 * Borrows a prepared frame; acceptance is not delivery.
	 */
	override fn TrySubmit() FrameLinkResult
	{
		if (this.phase != EthernetLiteTransmitState.Idle) {
			return FrameLinkResult.Busy
		}

		if (this.failed) {
			return FrameLinkResult.DeviceFailure
		}

		if (!this.ready) {
			return FrameLinkResult.NotReady
		}

		if (!this.source.IsReady()) {
			return FrameLinkResult.Invalid
		}

		var size = this.source.GetAvailableLength()

		if (size <= 0) {
			return FrameLinkResult.Invalid
		}

		if (size < 60 || size > MAX_LENGTH) {
			return FrameLinkResult.Unsupported
		}

		this.length = size as logic[LENGTH_BITS]
		this.phase = EthernetLiteTransmitState.Borrowed
		return FrameLinkResult.Accepted
	}

	/**
	 * Unsupported cancellation preserves a pending borrow.
	 */
	override fn TryCancel() FrameLinkResult
	{
		if (!this.IsSourceBorrowed()) {
			return FrameLinkResult.Invalid
		}

		return FrameLinkResult.Unsupported
	}

	/**
	 * Reads the stable terminal result without clearing it.
	 */
	override fn TryGetCompletion(outcome: out FrameTransmitOutcome) FrameLinkResult
	{
		outcome = FrameTransmitOutcome.DeviceFailure

		if (this.phase != EthernetLiteTransmitState.Terminal) {
			return FrameLinkResult.NotReady
		}

		outcome = this.outcome
		return FrameLinkResult.Success
	}

	/**
	 * Acknowledges the result and permits the next admission when initialized.
	 */
	override fn TryAcknowledgeCompletion() FrameLinkResult
	{
		if (this.phase != EthernetLiteTransmitState.Terminal) {
			return FrameLinkResult.NotReady
		}

		this.phase = EthernetLiteTransmitState.Idle
		this.length = 0
		return FrameLinkResult.Success
	}

	/**
	 * Driver-only initialization/recovery status, independent of a latched completion.
	 */
	public fn SetDeviceReady(ready: bool, failed: bool)
	{
		this.ready = ready
		this.failed = failed
	}

	/**
	 * Driver-only byte count for the current borrow.
	 */
	public fn GetPendingLength() int
	{
		return (this.length as uint) as int
	}

	/**
	 * Driver-only bounded source read; failure does not release ownership itself.
	 */
	public fn TryReadPending(index: int, value: out byte) PacketDataResult
	{
		value = 0x00

		if (!this.IsSourceBorrowed()) {
			return PacketDataResult.NotReady
		}

		if (index < 0 || index >= ((this.length as uint) as int)) {
			return PacketDataResult.Invalid
		}

		var octet: byte = 0x00
		var result = this.source.TryRead(index, octet)
		value = octet
		return result
	}

	/**
	 * Driver-only terminal transition, after all local source reads have stopped.
	 */
	public fn Complete(outcome: FrameTransmitOutcome)
	{
		if (!this.IsSourceBorrowed()) {
			return
		}

		this.outcome = outcome
		this.phase = EthernetLiteTransmitState.Terminal
	}
}