xous-core/emulation/peripherals/WF200.cs
Sean Cross 4b3d1503e7 renode: place all peripherals under "Betrusted" ns
Give a "Betrusted" namespace to all of the peripherals. This will enable
us to upstream various modules without breaking compatibility with our
own versions of these peripherals.

Signed-off-by: Sean Cross <sean@xobs.io>
2022-10-20 22:19:40 +08:00

1217 lines
50 KiB
C#

//
// Copyright (c) 2010-2018 Antmicro
// Copyright (c) 2011-2015 Realtime Embedded
//
// This file is licensed under the MIT License.
// Full license text is available in 'licenses/MIT.txt'.
//
using System;
using Antmicro.Renode.Core;
using Antmicro.Renode.Core.Structure;
using Antmicro.Renode.Core.Structure.Registers;
using Antmicro.Renode.Peripherals.SPI;
using Antmicro.Renode.Time;
using System.Collections.Generic;
using Antmicro.Renode.Logging;
using Antmicro.Renode.Network;
using Antmicro.Renode.Utilities;
namespace Antmicro.Renode.Peripherals.Network.Betrusted
{
public class WF200 : ISPIPeripheral, IMACInterface, IProvidesRegisterCollection<DoubleWordRegisterCollection>
{
public WF200()
{
MAC = EmulationManager.Instance.CurrentEmulation.MACRepository.GenerateUniqueMAC();
request = new byte[10240];
response = new byte[10240];
pendingTxMessages = new Queue<WfxMessage>();
IRQ = new GPIO();
RegistersCollection = new DoubleWordRegisterCollection(this);
Registers.Config.Define32(this)
.WithFlag(0, FieldMode.Read | FieldMode.Write, name: "csn_framing_error")
.WithFlag(1, FieldMode.Read | FieldMode.Write, name: "hif_underrun")
.WithFlag(2, FieldMode.Read | FieldMode.Write, name: "msg_underrun")
.WithFlag(3, FieldMode.Read | FieldMode.Write, name: "hif_no_queue")
.WithFlag(4, FieldMode.Read | FieldMode.Write, name: "hif_overrun")
.WithFlag(5, FieldMode.Read | FieldMode.Write, name: "hif_overrun_2")
.WithFlag(6, FieldMode.Read | FieldMode.Write, name: "hif_no_input_queue")
.WithFlag(7, FieldMode.Read | FieldMode.Write, name: "spi_cs_framing_disable")
.WithValueField(8, 2, out ByteOrder, FieldMode.Read | FieldMode.Write, name: "byte_order")
.WithFlag(10, out DirectAccessMode, FieldMode.Read | FieldMode.Write, changeCallback: (_, newMode) =>
{
// When we change from "direct mode" to "queue mode", queue this custom packet
if (!newMode)
{
configurationOffset = 0x08;
this.Log(LogLevel.Noisy, "Moving to Queue mode. Queueing initial startup message.");
byte[] initial_message = new byte[198];
initial_message[0] = (byte)(initial_message.Length - 2);
initial_message[1] = 0x00;
initial_message[2] = (byte)WfxMessage.Indications.Startup;
initial_message[3] = 0x00;
// uint32_t status
initial_message[4] = 0x00;
initial_message[5] = 0x00;
initial_message[6] = 0x00;
initial_message[7] = 0x00;
// uint16_t hardware_id
initial_message[8] = 0x03;
initial_message[9] = 0x10;
// uint8_t opn[14]
initial_message[10] = 0x57;
initial_message[11] = 0x46;
initial_message[12] = 0x32;
initial_message[13] = 0x30;
initial_message[14] = 0x30;
initial_message[15] = 0x44;
initial_message[16] = 0x00;
initial_message[17] = 0x00;
initial_message[18] = 0x00;
initial_message[19] = 0x00;
initial_message[20] = 0x00;
initial_message[21] = 0x00;
initial_message[22] = 0x00;
initial_message[23] = 0x00;
// uint8_t uid[8]
initial_message[24] = 0xC0;
initial_message[25] = 0x02;
initial_message[26] = 0x11;
initial_message[27] = 0x1C;
initial_message[28] = 0x00;
initial_message[29] = 0x76;
initial_message[30] = 0x4A;
initial_message[31] = 0x05;
// uint16_t num_inp_ch_bufs
initial_message[32] = 0x1E;
initial_message[33] = 0x00;
// uint16_t size_inp_ch_buf
initial_message[34] = 0x50;
initial_message[35] = 0x06;
// uint8_t num_links_aP
initial_message[36] = 0x08;
// uint8_t num_interfaces
initial_message[37] = 0x02;
// uint8_t mac_addrs[0][6]
initial_message[38] = MAC.Bytes[0];
initial_message[39] = MAC.Bytes[1];
initial_message[40] = MAC.Bytes[2];
initial_message[41] = MAC.Bytes[3];
initial_message[42] = MAC.Bytes[4];
initial_message[43] = MAC.Bytes[5];
// uint8_t mac_addrs[1][6]
initial_message[44] = 0x84;
initial_message[45] = 0xFD;
initial_message[46] = 0x27;
initial_message[47] = 0x18;
initial_message[48] = 0xE7;
initial_message[49] = 0x47;
// uint8_t api_version_major
initial_message[50] = 0x07;
// uint8_t api_version_minor
initial_message[51] = 0x03;
// sl_wfx_capabilities_s
initial_message[52] = 0x00;
initial_message[53] = 0x00;
initial_message[54] = 0x00;
initial_message[55] = 0x00;
// firmware_build
initial_message[56] = 0x03;
// firmware_minor
initial_message[57] = 0x0C;
// firmware_major
initial_message[58] = 0x03;
// firmware_type
initial_message[59] = 0x01;
// uint8_t disabled_channel_list[2]
initial_message[60] = 0x00;
initial_message[61] = 0x00;
// uint8_t regul_sel_mode_info
initial_message[62] = 0xC0;
// uint8_t otp_phy_info
initial_message[63] = 0x00;
// uint32_t supported_rate_mask
initial_message[64] = 0xCF;
initial_message[65] = 0xFF;
initial_message[66] = 0x3F;
initial_message[67] = 0x00;
// uint8_t firmware_label[128]
initial_message[68] = 0x57;
initial_message[69] = 0x46;
initial_message[70] = 0x32;
initial_message[71] = 0x30;
initial_message[72] = 0x30;
initial_message[73] = 0x5F;
initial_message[74] = 0x41;
initial_message[75] = 0x53;
initial_message[76] = 0x49;
initial_message[77] = 0x43;
initial_message[78] = 0x5F;
initial_message[79] = 0x57;
initial_message[80] = 0x46;
initial_message[81] = 0x4D;
initial_message[82] = 0x5F;
initial_message[83] = 0x28;
initial_message[84] = 0x4A;
initial_message[85] = 0x65;
initial_message[86] = 0x6E;
initial_message[87] = 0x6B;
initial_message[88] = 0x69;
initial_message[89] = 0x6E;
initial_message[90] = 0x73;
initial_message[91] = 0x29;
initial_message[92] = 0x5F;
initial_message[93] = 0x46;
initial_message[94] = 0x57;
initial_message[95] = 0x33;
initial_message[96] = 0x2E;
initial_message[97] = 0x31;
initial_message[98] = 0x32;
initial_message[99] = 0x2E;
initial_message[100] = 0x33;
// Return a faked version of the CONTROL register (the reference code calls this `piggy-backed`)
initial_message[196] = 0x00;
initial_message[197] = 0x30;
pendingTxMessages.Enqueue(new WfxMessage(WfxMessage.IdType.Indication, initial_message));
}
}, name: "direct_access_mode")
.WithReservedBits(11, 1)
.WithFlag(12, out CpuClockDisable, FieldMode.Read | FieldMode.Write, name: "cpu_clk_disable")
// Always return `false` here to fake memory being read back immediately
.WithFlag(13, FieldMode.Read | FieldMode.Write, valueProviderCallback: _ => false, name: "direct_pre_fetch_shared_ram")
.WithFlag(14, out CpuReset, FieldMode.Read | FieldMode.Write, name: "cpu_reset")
.WithFlag(15, FieldMode.Read | FieldMode.Write, name: "disable_dat1_mode")
.WithValueField(16, 2, FieldMode.Read | FieldMode.Write, name: "dout_posedge_ena")
.WithFlag(18, FieldMode.Read | FieldMode.Write, name: "disable_dat1_irq")
.WithFlag(19, FieldMode.Read | FieldMode.Write, name: "sdio_disable_crc_check")
.WithReservedBits(20, 4)
.WithValueField(24, 8, valueProviderCallback: _ => 1, name: "device id")
;
Registers.Control.Define32(this)
.WithValueField(0, 12, FieldMode.Read, valueProviderCallback: _ =>
{
if (pendingTxMessages.Count == 0)
{
return 0;
}
// Return the number of 16-bit words, not including the header
return (uint)pendingTxMessages.Peek().Data.Count / 2 - 1;
}, name: "next_output_queue_item_length")
.WithFlag(12, out WlanReady, FieldMode.Write | FieldMode.Read, name: "wlan_wup")
.WithFlag(13, FieldMode.Read, valueProviderCallback: _ => WlanReady.Value, name: "wlan_rdy")
.WithValueField(14, 2, FieldMode.Read, valueProviderCallback: _ =>
{
if (pendingTxMessages.Count == 0)
{
return 0;
}
return pendingTxMessages.Peek().Id;
}, name: "frame_type_info")
.WithReservedBits(16, 16)
;
Registers.MemoryAddress.Define32(this)
.WithValueField(0, 32, out MemoryAddress, name: "memory_address")
;
Reset();
}
public GPIO IRQ { get; private set; }
public void Reset()
{
mode = Mode.ReadingHeader1;
ByteOrder.Value = 0;
WlanReady.Value = false;
CpuClockDisable.Value = true;
DirectAccessMode.Value = true;
CpuReset.Value = true;
spiHeader = 0;
pendingTxMessages.Clear();
configurationOffset = 0;
}
public MACAddress MAC { get; set; }
public void ReceiveFrame(EthernetFrame frame)
{
if (!frame.DestinationMAC.IsBroadcast && frame.DestinationMAC != MAC)
{
return;
}
// this.Log(LogLevel.Info, "Received frame {0}.", frame);
// sl_wfx_received_ind_body_t
// Strip off the Ethernet footer
var frame_length = frame.Bytes.Length - 4;
var frame_padding = (4 - (frame_length & 3)) & 3;
var frame_bytes = new byte[frame_length + 6 + frame_padding];
// uint8_t frame_type
frame_bytes[0] = 0;
// uint8_t frame_padding
frame_bytes[1] = (byte)frame_padding;
// uint16_t frame_length
frame_bytes[2] = (byte)frame_length;
frame_bytes[3] = (byte)(frame_length >> 8);
// uint8_t padding[frame_padding] -- may be 0, 1, 2, or 3
// uint8_t frame[frame_length]
Array.Copy(frame.Bytes, 0, frame_bytes, 4 + frame_padding, frame_length);
// Faked CONTROL register
frame_bytes[frame_bytes.Length - 1] = 0x30;
var receivedPacketMessage = new WfxMessage(WfxMessage.Ethernet.Received, frame_bytes);
pendingTxMessages.Enqueue(receivedPacketMessage);
}
public byte Transmit(byte data)
{
if (DirectAccessMode.Value)
{
return HandleDirectAccessMode(data);
}
else
{
return HandleDirectAccessMode(data);
}
}
private byte HandleDirectAccessMode(byte data)
{
if (spiLength == 0 && (mode != Mode.ReadingHeader1) && (mode != Mode.ReadingHeader2))
{
this.Log(LogLevel.Error, "Spi had {0} bytes left yet was still in mode {1}", spiLength, mode);
mode = Mode.ReadingHeader1;
}
switch (mode)
{
case Mode.ReadingHeader1:
spiHeader = ((uint)data) << 8;
mode = Mode.ReadingHeader2;
return 0x00;
case Mode.ReadingHeader2:
spiHeader |= ((uint)data) << 0;
// this.Log(LogLevel.Noisy, "SPI header was {0:X4}", spiHeader);
spiLength = BitHelper.GetValue(spiHeader, 0, 12) * 2;
counter = 0;
spiIsRead = BitHelper.IsBitSet(spiHeader, 15);
var addr = BitHelper.GetValue(spiHeader, 12, 3);
if (addr == 7)
{
this.Log(LogLevel.Error, "Invalid register address 7");
mode = Mode.Error;
return 0xff;
}
spiAddress = (Registers)addr;
// this.Log(LogLevel.Noisy, "Detected a {0} on register {1} of {2} bytes, mode {3}", spiIsRead ? "Read" : "Write", spiAddress, spiLength, ByteOrder.Value);
if (spiIsRead)
{
PrepareReadRegister();
mode = Mode.ReadingRegister;
}
else
{
mode = Mode.WritingRegister;
}
return 0x00;
case Mode.ReadingRegister:
var ret = response[counter];
counter += 1;
spiLength -= 1;
if (spiLength == 0)
{
mode = Mode.ReadingHeader1;
}
return ret;
case Mode.WritingRegister:
// this.Log(LogLevel.Noisy, "Appending {0:X2} to request buffer @ {1}", data, counter);
request[counter] = data;
counter += 1;
spiLength -= 1;
if (spiLength == 0)
{
mode = Mode.ReadingHeader1;
HandleWriteRegister();
}
break;
case Mode.Error:
counter += 1;
spiLength -= 1;
if (spiLength == 0)
{
mode = Mode.ReadingHeader1;
}
return 0xff;
default:
this.Log(LogLevel.Error, "Unexpected mode");
break;
}
return 0x00;
}
private void PrepareReadRegister()
{
switch (spiAddress)
{
case Registers.Config:
case Registers.Control:
case Registers.MemoryAddress:
{
var val = RegistersCollection.Read((long)spiAddress);
Array.Clear(response, 0, response.Length);
if ((ByteOrder.Value == 0) || (spiAddress == Registers.Config))
{
if ((spiLength == 4) || (spiLength == 2))
{
response[0] = (byte)(val >> 8);
response[1] = (byte)(val >> 0);
response[2] = (byte)(val >> 24);
response[3] = (byte)(val >> 16);
}
else
{
this.Log(LogLevel.Error, "Unrecognized byte order {0} and byte count {1}", ByteOrder.Value, counter);
}
}
else if (ByteOrder.Value == 1)
{
if (spiLength == 4)
{
response[0] = (byte)(val >> 24);
response[1] = (byte)(val >> 16);
response[2] = (byte)(val >> 8);
response[3] = (byte)(val >> 0);
}
else
{
this.Log(LogLevel.Error, "Unrecognized byte order {0} and byte count {1}", ByteOrder.Value, counter);
}
}
else if (ByteOrder.Value == 2)
{
if ((spiLength == 2) | ((spiLength == 4) && (spiAddress == Registers.Control)))
{
response[1] = (byte)(val >> 8);
response[0] = (byte)(val >> 0);
}
else if (spiLength == 4)
{
response[0] = (byte)(val >> 0);
response[1] = (byte)(val >> 8);
response[2] = (byte)(val >> 16);
response[3] = (byte)(val >> 24);
}
else
{
this.Log(LogLevel.Error, "Unrecognized byte order {0} and byte count {1}", ByteOrder.Value, counter);
}
}
else
{
this.Log(LogLevel.Error, "Unrecognized byte order {0}", ByteOrder.Value);
}
// if (spiLength == 2)
// {
// this.Log(LogLevel.Noisy, "Returning [{0:X2} {1:X2}] (originally \"{2}\" {3:X8})", response[0], response[1], spiAddress, val);
// }
// else if (spiLength == 4)
// {
// this.Log(LogLevel.Noisy, "Returning [{0:X2} {1:X2} {2:X2} {3:X2}] (originally \"{4}\" {5:X8})", response[0], response[1], response[2], response[3], spiAddress, val);
// }
}
break;
case Registers.SharedRamPort:
{
uint val = 0;
switch (MemoryAddress.Value)
{
case 0x09004000:
this.Log(LogLevel.Debug, "Faking return value for bootloader address");
val = 0x23abc88e;
break;
// ADDR_DWL_CTRL_AREA_GET
case 0x0900c008:
// downloadOffset += 0x8000;
this.Log(LogLevel.Debug, "Faking SWL_CTRL_AREA_GET to 0x{0:X08}", downloadOffset);
val = downloadOffset;
break;
// NCP status
case 0x0900c010:
if (ncpStateVal == 0)
{
this.Log(LogLevel.Debug, "Faking NCP_STATE_INFO_READY");
val = 0xBD53EF99;
ncpStateVal = 1;
}
else if (ncpStateVal == 1)
{
this.Log(LogLevel.Debug, "Faking NCP_STATE_READY");
val = 0x87654321;
ncpStateVal = 2;
}
else
{
this.Log(LogLevel.Debug, "Faking NCP_STATE_AUTH_OK");
val = 0xD4C64A99;
ncpStateVal = 0;
}
break;
case 0x0900c0cc:
this.Log(LogLevel.Debug, "Faking WF200 keyset to be 0xA19C9700");
val = 0xA19C9700;
break;
default:
this.Log(LogLevel.Debug, "Unrecognized SRAM port address: {0:08X}", MemoryAddress.Value);
break;
}
// 0x23abc88e
if (ByteOrder.Value == 0)
{
response[0] = (byte)(val >> 8);
response[1] = (byte)(val >> 0);
response[2] = (byte)(val >> 24);
response[3] = (byte)(val >> 16);
}
else if (ByteOrder.Value == 2)
{
response[0] = (byte)(val >> 0);
response[1] = (byte)(val >> 8);
response[2] = (byte)(val >> 16);
response[3] = (byte)(val >> 24);
}
else
{
this.Log(LogLevel.Error, "Unrecognized byte order {0}", ByteOrder.Value);
}
}
break;
case Registers.IoQueue:
if (pendingTxMessages.Count == 0)
{
this.Log(LogLevel.Error, "Tried to transmit a packet when none existed");
break;
}
var message = pendingTxMessages.Dequeue();
// this.Log(LogLevel.Debug, "Copying Pending Tx Message to response buffer: {0}", message);
message.Data.CopyTo(response);
// Increment the `word counter` bits -- it seems to go 0, 8, 10, 18, 20, 28, 30, 38, 0, ...
response[3] |= wordCounter;
wordCounter = (byte)((wordCounter + 0x8) & 0x38);
break;
}
}
private uint ncpStateVal = 0;
private byte wordCounter;
private void HandleWriteRegister()
{
// this.Log(LogLevel.Noisy, "Updating register \"{0}\" with {1} bytes of data", spiAddress, counter);
switch (spiAddress)
{
case Registers.Config:
case Registers.Control:
case Registers.MemoryAddress:
{
var existing = RegistersCollection.Read((long)spiAddress);
// Control register is always Mode 0
var val = GetWriteRegister(existing, spiAddress == Registers.Config ? 0 : ByteOrder.Value);
var logLevel = LogLevel.Debug;
if (spiAddress == Registers.MemoryAddress)
{
logLevel = LogLevel.Noisy;
}
this.Log(logLevel, "Updating value of \"{0}\" from {1:X8} -> {2:X8}", spiAddress, existing, val);
RegistersCollection.Write((long)spiAddress, val);
break;
}
case Registers.GeneralPurpose:
{
var val = GetWriteRegister(0, ByteOrder.Value);
var gpReg = (val >> 24) & 0xff;
this.Log(LogLevel.Debug, "Updating value of GP reg {0} to 0x{1:X8}", gpReg, val);
break;
}
case Registers.SharedRamPort:
if ((counter != 4) && (counter != 2))
{
this.Log(LogLevel.Noisy, "Ingoring non-word write");
break;
}
else
{
var val = GetWriteRegister(0, ByteOrder.Value);
switch (MemoryAddress.Value)
{
// ADDR_DWL_CTRL_AREA_GET
case 0x0900c008:
// ADDR_DWL_CTRL_AREA_PUT
case 0x0900c004:
downloadOffset = val;
this.Log(LogLevel.Noisy, "Setting download address to 0x{0:X08}", downloadOffset);
break;
// ADDR_DWL_CTRL_AREA_HOST_STATUS
case 0x0900c00c:
this.Log(LogLevel.Debug, "Ignoring HOST_STATUS update of {0}", val);
break;
default:
this.Log(LogLevel.Error, "Unrecognized RAM port address 0x{0:X08} -- dropping val 0x{1:X08}", MemoryAddress.Value, val);
break;
}
}
// The MemoryAddress is updated, particularly when doing firmware writes
MemoryAddress.Value += (uint)counter;
break;
case Registers.IoQueue:
// this.Log(LogLevel.Warning, "Recieved request, spiLength: {0} request length: {1}", counter, request.Length);
byte[] request_data = new byte[counter];
Array.Copy(request, request_data, counter);
WfxMessage req = new WfxMessage(WfxMessage.IdType.Request, request_data);
// this.Log(LogLevel.Warning, "Recieved request: {0}", req);
HandleRequest(req);
break;
default:
this.Log(LogLevel.Error, "Unhandled register write: \"{0}\"", spiAddress);
break;
}
}
private void HandleRequest(WfxMessage request)
{
// Default response of "Okay", plus a piggy-backed read of the CONTROL register with no data
var okayResponse = new byte[6];
okayResponse[5] = 0x30;
if (request.Type != WfxMessage.IdType.Request)
{
this.Log(LogLevel.Error, "Message was a {0}, not Request!", request.Type);
return;
}
var req = (WfxMessage.Requests)(request.Id);
if (req == WfxMessage.Requests.Configuration)
{
var message = new WfxMessage(WfxMessage.Confirmations.Configuration, okayResponse, configurationOffset);
configurationOffset += 0x08;
// this.Log(LogLevel.Debug, "Sending response for configuration message: {0}", message);
pendingTxMessages.Enqueue(message);
}
else if (req == WfxMessage.Requests.Connect)
{
var message = new WfxMessage(WfxMessage.Confirmations.Connect, okayResponse);
// this.Log(LogLevel.Debug, "Sending response for connection request: {0}", message);
pendingTxMessages.Enqueue(message);
var connect_ind = new byte[18];
// uint32_t status /* WFM_STATUS_SUCCESS */
connect_ind[0] = 0x00;
connect_ind[1] = 0x00;
connect_ind[2] = 0x00;
connect_ind[3] = 0x00;
// uint8_t mac[6]
connect_ind[4] = 0xF0;
connect_ind[5] = 0x9F;
connect_ind[6] = 0xC2;
connect_ind[7] = 0x24;
connect_ind[8] = 0x2E;
connect_ind[9] = 0x45;
// uint16_t channel
connect_ind[10] = 0x0B;
connect_ind[11] = 0x00;
// uint8_t beacon_interval
connect_ind[12] = 0x64;
// uint8_t dtim_period
connect_ind[13] = 0x03;
// uint16_t max_phy_rate
connect_ind[14] = 0x15;
connect_ind[15] = 0x00;
// uint16_t dummy CONTROL register
connect_ind[16] = 0x00;
connect_ind[17] = 0x30;
message = new WfxMessage(WfxMessage.Indications.Connect, okayResponse);
pendingTxMessages.Enqueue(message);
}
else if (req == WfxMessage.Requests.StartScan)
{
var machine = this.GetMachine();
var time_domain = machine.ElapsedVirtualTime.Domain;
var message = new WfxMessage(WfxMessage.Confirmations.StartScan, okayResponse);
// this.Log(LogLevel.Debug, "Starting a scan and responding with {0}", message);
pendingTxMessages.Enqueue(message);
for (var i = 0; i < 10; i++)
{
var ap1 = new byte[54];
// uint32_t ssid_len
ap1[0] = 8;
// uint8_t ssid[32]
ap1[4] = (byte)'R';
ap1[5] = (byte)'e';
ap1[6] = (byte)'n';
ap1[7] = (byte)'o';
ap1[8] = (byte)'d';
ap1[9] = (byte)'e';
ap1[10] = (byte)' ';
ap1[11] = (byte)(i + 48);
// uint8_t mac[6]
ap1[36] = (byte)(0x14 + i);
ap1[37] = (byte)(0x13 + i);
ap1[38] = (byte)(0x15 + i);
ap1[39] = (byte)(0x26 + i);
ap1[40] = (byte)(0x37 + i);
ap1[41] = (byte)(0x48 + i);
// uint16_t channel
ap1[42] = (byte)(1 + i);
ap1[43] = 0;
// uint8_t security_mode
ap1[44] = (1 << 2) | (1 << 6);
// uint8_t reserved[3]
// uint16_t rcpi
ap1[48] = (byte)(0x6e + i);
ap1[49] = 0x00;
// uint16_t ie_data_length
ap1[50] = 0x00;
ap1[51] = 0x00;
// // uint16_t dummy CONTROL register
ap1[52] = 0x00;
ap1[53] = 0x30;
var reply_message = new WfxMessage(WfxMessage.Indications.ScanResult, ap1);
var ts = new TimeStamp(TimeInterval.FromMilliseconds(50) + machine.ElapsedVirtualTime.TimeElapsed, time_domain);
this.GetMachine().HandleTimeDomainEvent(pendingTxMessages.Enqueue, reply_message, ts);
}
var final_reply_message = new WfxMessage(WfxMessage.Indications.ScanComplete, okayResponse);
var final_ts = new TimeStamp(TimeInterval.FromMilliseconds(700) + machine.ElapsedVirtualTime.TimeElapsed, time_domain);
this.GetMachine().HandleTimeDomainEvent(pendingTxMessages.Enqueue, final_reply_message, final_ts);
}
else if (req == WfxMessage.Requests.SendFrame)
{
var payload = request.Payload();
var sendFrameResponse = new byte[10];
// uint32_t status
sendFrameResponse[0] = 0;
sendFrameResponse[1] = 0;
sendFrameResponse[2] = 0;
sendFrameResponse[3] = 0;
// uint16_t packet_id (from request)
sendFrameResponse[4] = (byte)(payload[2]);
sendFrameResponse[5] = (byte)(payload[3]);
sendFrameResponse[6] = 0;
// uint16_t reserved
sendFrameResponse[7] = 0;
// Dummy CONTROL register
sendFrameResponse[8] = 0;
sendFrameResponse[9] = 0x30;
var frame_data = payload.GetRange(8, payload.Count - 8).ToArray();
var packet_data_length = (payload[4] << 0) | (payload[5] << 8) | (payload[6] << 16) | (payload[7] << 24);
if (frame_data.Length != packet_data_length)
{
this.Log(LogLevel.Error, "Warning: host reported payload length of {0}, but transmitted a payload length of {1}", packet_data_length, frame_data.Length);
}
if (!Misc.TryCreateFrameOrLogWarning(this, frame_data, out var frame, addCrc: true))
{
return;
}
// this.Log(LogLevel.Noisy, "Sending frame {0}.", frame);
FrameReady?.Invoke(frame);
pendingTxMessages.Enqueue(new WfxMessage(WfxMessage.Confirmations.SendFrame, sendFrameResponse));
}
else if (req == WfxMessage.Requests.GetSignalStrength)
{
var response = new byte[10];
// uint32_t status
// uint32_t rcpi
response[4] = 0x54;
response[5] = 0x00;
response[6] = 0x00;
response[7] = 0x00;
response[8] = 0x00;
response[9] = 0x30;
pendingTxMessages.Enqueue(new WfxMessage(WfxMessage.Confirmations.GetSignalStrength, response));
}
else if (req == WfxMessage.Requests.SetArpIpAddress)
{
var message = new WfxMessage(WfxMessage.Confirmations.SetArpIpAddress, okayResponse, configurationOffset);
// this.Log(LogLevel.Info, "Sending response for configuration message: {0}", message);
pendingTxMessages.Enqueue(message);
}
else if (req == WfxMessage.Requests.Disconnect)
{
var message = new WfxMessage(WfxMessage.Confirmations.Disconnect, okayResponse, configurationOffset);
pendingTxMessages.Enqueue(message);
}
else
{
this.Log(LogLevel.Error, "Unhandled request: {0}", request);
}
}
private uint GetWriteRegister(uint val, uint order)
{
if (order == 0)
{
if (counter == 2)
{
val = (val & ~(uint)0x0000ff00) | (((uint)request[0]) << 8);
val = (val & ~(uint)0x000000ff) | (((uint)request[1]) << 0);
}
else if (counter == 4)
{
val = (val & ~(uint)0x0000ff00) | (((uint)request[0]) << 8);
val = (val & ~(uint)0x000000ff) | (((uint)request[1]) << 0);
val = (val & ~(uint)0xff000000) | (((uint)request[2]) << 24);
val = (val & ~(uint)0x00ff0000) | (((uint)request[3]) << 16);
}
else
{
this.Log(LogLevel.Error, "Unrecognized byte order {0} and byte count {1}", ByteOrder.Value, counter);
}
}
else if (order == 1)
{
if (spiLength == 4)
{
val = (val & ~(uint)0xff000000) | (((uint)request[3]) << 24);
val = (val & ~(uint)0x00ff0000) | (((uint)request[2]) << 16);
val = (val & ~(uint)0x0000ff00) | (((uint)request[1]) << 8);
val = (val & ~(uint)0x000000ff) | (((uint)request[0]) << 0);
}
else
{
this.Log(LogLevel.Error, "Unrecognized byte order {0} and byte count {1}", ByteOrder.Value, counter);
}
}
else if (order == 2)
{
if (counter == 2)
{
val = (val & ~(uint)0x000000ff) | (((uint)request[0]) << 0);
val = (val & ~(uint)0x0000ff00) | (((uint)request[1]) << 8);
// response[1] = (byte)(val >> 8);
// response[0] = (byte)(val >> 0);
}
else if (counter == 4)
{
val = (val & ~(uint)0x000000ff) | (((uint)request[0]) << 0);
val = (val & ~(uint)0x0000ff00) | (((uint)request[1]) << 8);
val = (val & ~(uint)0x00ff0000) | (((uint)request[2]) << 16);
val = (val & ~(uint)0xff000000) | (((uint)request[3]) << 24);
}
else if ((spiLength == 1024) && (spiAddress == Registers.MemoryAddress))
{
// this.Log(LogLevel.Noisy, "Ignoring 1024-byte write to memory address @ {0:X08} -- probably a firmware write", MemoryAddress.Value);
}
else
{
this.Log(LogLevel.Error, "Unrecognized byte order {0} and byte count {1}", ByteOrder.Value, counter);
}
}
else
{
this.Log(LogLevel.Error, "Unrecognized byte order {0}", ByteOrder.Value);
}
return val;
}
public void FinishTransmission()
{
if ((mode != Mode.ReadingHeader1) || (spiLength > 0))
{
this.Log(LogLevel.Error, "Finished transmission with data still in the buffer");
}
mode = Mode.ReadingHeader1;
}
public void WriteDoubleWord(long address, uint value)
{
RegistersCollection.Write(address, value);
}
public uint ReadDoubleWord(long offset)
{
return RegistersCollection.Read(offset);
}
public event Action<EthernetFrame> FrameReady;
public DoubleWordRegisterCollection RegistersCollection { get; private set; }
private int counter;
private Mode mode;
private uint spiHeader;
private byte configurationOffset;
private uint spiLength;
private bool spiIsRead;
private Registers spiAddress;
private readonly byte[] request;
private readonly byte[] response;
private IValueRegisterField MemoryAddress;
private IValueRegisterField ByteOrder;
private IFlagRegisterField WlanReady;
private IFlagRegisterField CpuClockDisable;
private IFlagRegisterField DirectAccessMode;
private IFlagRegisterField CpuReset;
private uint downloadOffset;
private Queue<WfxMessage> pendingTxMessages;
private enum Mode
{
ReadingHeader1,
ReadingHeader2,
ReadingRegister,
WritingRegister,
Error
}
private enum Registers
{
Config = 0x00,
Control = 0x01,
IoQueue = 0x02,
AhbDirectAccess = 0x03,
MemoryAddress = 0x04,
SharedRamPort = 0x05,
GeneralPurpose = 0x06,
}
private class WfxMessage
{
public WfxMessage(IdType type, byte[] data)
{
Type = type;
uint length = ((uint)data[0]) | (((uint)data[1]) << 8);
Id = data[2];
Info = data[3];
// Construct the packet
Data = new List<byte>(data);
}
public WfxMessage(Requests request, byte[] data, byte info = (byte)0)
{
Id = (byte)request;
Type = IdType.Request;
Info = info;
Data = new List<byte>();
// Construct the packet
var length = data.Length + 2;
Data.Add((byte)length);
Data.Add((byte)(length >> 8));
Data.Add(Id);
Data.Add(Info);
Data.AddRange(data);
}
public WfxMessage(Indications indication, byte[] data, byte info = (byte)0)
{
Id = (byte)indication;
Type = IdType.Indication;
Info = info;
Data = new List<byte>();
// Construct the packet
var length = data.Length + 2;
Data.Add((byte)length);
Data.Add((byte)(length >> 8));
Data.Add(Id);
Data.Add(Info);
Data.AddRange(data);
}
public WfxMessage(Confirmations confirmation, byte[] data, byte info = (byte)0)
{
Id = (byte)confirmation;
Type = IdType.Confirmation;
Info = info;
Data = new List<byte>();
// Construct the packet
var length = data.Length + 2;
Data.Add((byte)length);
Data.Add((byte)(length >> 8));
Data.Add(Id);
Data.Add(Info);
Data.AddRange(data);
}
public WfxMessage(Ethernet eth, byte[] data, byte info = (byte)0)
{
Id = (byte)eth;
Type = IdType.Ethernet;
Info = info;
Data = new List<byte>();
// Construct the packet
var length = data.Length + 2;
Data.Add((byte)length);
Data.Add((byte)(length >> 8));
Data.Add(Id);
Data.Add(Info);
Data.AddRange(data);
}
public override string ToString()
{
var headerString = "{";
if (Data.Count > 0)
{
headerString += String.Format("Size: {0:X2}", Data[0]);
}
if (Data.Count > 1)
{
headerString += String.Format("{0:X2}", Data[1]);
}
if (Data.Count > 2)
{
headerString += String.Format(" ID:{0:X2}", Data[2]);
}
if (Data.Count > 3)
{
headerString += String.Format(" Flags:{0:X2}", Data[3]);
}
headerString += "}";
var dataString = "{";
var idx = 0;
foreach (var b in Data)
{
idx += 1;
if (idx <= 4)
{
continue;
}
else if (idx == 5)
{
dataString += String.Format("{0:X2}", b);
}
else
{
dataString += String.Format(", {0:X2}", b);
}
}
dataString += "}";
switch (Type)
{
case IdType.Confirmation:
{
var id = (Confirmations)Id;
return $"[WfxMessage<Confirmation> Id=<{id}> Bytes={Data.Count} Header={headerString} Data={dataString}]";
}
case IdType.Indication:
{
var id = (Indications)Id;
return $"[WfxMessage<Indication> Id=<{id}> Bytes={Data.Count} Header={headerString} Data={dataString}]";
}
case IdType.Request:
{
var id = (Requests)Id;
return $"[WfxMessage<Request> Id=<{id}> Bytes={Data.Count} Header={headerString} Data={dataString}]";
}
case IdType.Ethernet:
{
var id = (Ethernet)Id;
return $"[WfxMessage<Ethernet> Id=<{id}> Bytes={Data.Count} Header={headerString} Data={dataString}]";
}
default:
return $"[WfxMessage<Unknown> Id=<{Id}> Bytes={Data.Count} Header={headerString} Data={dataString}]";
}
}
public List<byte> Payload()
{
if (Data.Count <= 4)
{
return new List<byte>();
}
return Data.GetRange(4, Data.Count - 4);
}
// One of `Requests`, `Confirmations`, or `Indications`
public readonly byte Id;
public readonly byte Info;
public readonly IdType Type;
public readonly List<byte> Data;
public enum IdType
{
Confirmation = 0,
Indication = 1,
Management = 2,
Ethernet = 3,
// Requests are made from the host and never by the device
Request = 4,
};
public enum Requests
{
Configuration = 0x09,
ControlGpio = 0x26,
SetSecurelinkMacKey = 0x27,
SecurelinkExchangePubKeys = 0x28,
SecurelinkConfigure = 0x29,
PreventRollback = 0x2a,
PtaSettings = 0x2b,
PtaPriority = 0x2c,
PtaState = 0x2d,
SetCcaConfig = 0x2e,
ShutDown = 0x32,
SetMacAddress = 0x42,
Connect = 0x43,
Disconnect = 0x44,
StartAp = 0x45,
UpdateAp = 0x46,
StopAp = 0x47,
SendFrame = 0x4A,
StartScan = 0x4B,
StopScan = 0x4C,
GetSignalStrength = 0x4E,
DisconnectApClient = 0x4F,
SetPmMode = 0x52,
AddMulticastAddr = 0x53,
RemoveMulticastAddr = 0x54,
SetMaxApClientCount = 0x55,
SetMaxApClientInactivity = 0x56,
SetRoamParameters = 0x57,
SetTxRateParameters = 0x58,
SetArpIpAddress = 0x59,
SetNsIpAddress = 0x5A,
SetBroadcastFilter = 0x5B,
SetScanParameters = 0x5C,
SetUnicastFilter = 0x5D,
AddWhitelistAddr = 0x5E,
AddBlacklistAddr = 0x5F,
SetMaxTxPower = 0x60,
GetMaxTxPower = 0x61,
GetPmk = 0x62,
GetApClientSignalStrength = 0x63,
ExtAuth = 0x64,
};
public enum Confirmations
{
Configuration = 0x09,
ControlGpio = 0x26,
SetSecurelinkMacKey = 0x27,
SecurelinkExchangePubKeys = 0x28,
SecurelinkConfigure = 0x29,
PreventRollback = 0x2a,
PtaSettings = 0x2b,
PtaPriority = 0x2c,
PtaState = 0x2d,
SetCcaConfig = 0x2e,
SetMacAddress = 0x42,
Connect = 0x43,
Disconnect = 0x44,
StartAp = 0x45,
UpdateAp = 0x46,
StopAp = 0x47,
SendFrame = 0x4A,
StartScan = 0x4B,
StopScan = 0x4C,
GetSignalStrength = 0x4E,
DisconnectApClient = 0x4F,
SetPmMode = 0x52,
AddMulticastAddr = 0x53,
RemoveMulticastAddr = 0x54,
SetMaxApClientCount = 0x55,
SetMaxApClientInactivity = 0x56,
SetRoamParameters = 0x57,
SetTxRateParameters = 0x58,
SetArpIpAddress = 0x59,
SetNsIpAddress = 0x5A,
SetBroadcastFilter = 0x5B,
SetScanParameters = 0x5C,
SetUnicastFilter = 0x5D,
AddWhitelistAddr = 0x5E,
AddBlacklistAddr = 0x5F,
SetMaxTxPower = 0x60,
GetMaxTxPower = 0x61,
GetPmk = 0x62,
GetApClientSignalStrength = 0x63,
ExtAuth = 0x64
};
public enum Indications
{
Connect = 0xc3,
Disconnect = 0xc4,
StartAp = 0xc5,
StopAp = 0xc7,
Received = 0xca,
ScanResult = 0xcb,
ScanComplete = 0xcc,
ApClientConnected = 0xcd,
ApClientRejected = 0xce,
ApClientDisconnected = 0xcf,
ExtAuth = 0xd2,
PsModeError = 0xd3,
Exception = 0xe0,
Startup = 0xe1,
Wakeup = 0xe2,
Generic = 0xe3,
Error = 0xe4,
SecurelinkExchagePubKeys = 0xe5,
};
public enum Ethernet
{
Received = 0xca,
};
}
}
}