async ioctls working.

This commit is contained in:
Dario Nieuwenhuis 2022-07-11 00:25:35 +02:00
parent e560415fde
commit 069a57fcf8
4 changed files with 202 additions and 98 deletions

View File

@ -6,11 +6,12 @@ use core::slice;
use defmt::{assert, assert_eq, panic, *};
use embassy::executor::Spawner;
use embassy::util::Forever;
use embassy_rp::gpio::{Flex, Level, Output, Pin};
use embassy_rp::peripherals::{PIN_23, PIN_24, PIN_25, PIN_29};
use embassy_rp::Peripherals;
use {defmt_rtt as _, panic_probe as _};
macro_rules! forever {
($val:expr) => {{
type T = impl Sized;
@ -19,21 +20,29 @@ macro_rules! forever {
}};
}
#[embassy::task]
async fn wifi_task(runner: cyw43::Runner<'static, PIN_23, PIN_25, PIN_29, PIN_24>) -> ! {
runner.run().await
}
#[embassy::main]
async fn main(_spawner: Spawner, p: Peripherals) {
async fn main(spawner: Spawner, p: Peripherals) {
info!("Hello World!");
let (pwr, cs, clk, dio) = (p.PIN_23, p.PIN_25, p.PIN_29, p.PIN_24);
//let (pwr, cs, clk, dio) = (p.PIN_23, p.PIN_0, p.PIN_1, p.PIN_2);
let mut driver = cyw43::Driver::new(
let state = forever!(cyw43::State::new());
let (mut control, runner) = cyw43::new(
state,
Output::new(pwr, Level::Low),
Output::new(cs, Level::High),
Output::new(clk, Level::Low),
Flex::new(dio),
);
)
.await;
driver.init().await;
spawner.spawn(wifi_task(runner)).unwrap();
loop {}
control.init().await;
}

8
rust-toolchain.toml Normal file
View File

@ -0,0 +1,8 @@
# Before upgrading check that everything is available on all tier1 targets here:
# https://rust-lang.github.io/rustup-components-history
[toolchain]
channel = "nightly-2022-07-09"
components = [ "rust-src", "rustfmt" ]
targets = [
"thumbv6m-none-eabi",
]

View File

@ -1,16 +1,20 @@
#![no_std]
#![no_main]
#![feature(type_alias_impl_trait, concat_bytes)]
#![feature(type_alias_impl_trait, concat_bytes, const_slice_from_raw_parts)]
// This mod MUST go first, so that the others see its macros.
pub(crate) mod fmt;
mod structs;
use core::cell::Cell;
use core::slice;
use embassy::time::{block_for, Duration, Timer};
use embassy::util::yield_now;
use embassy_rp::gpio::{Flex, Output, Pin};
use self::structs::*;
fn swap16(x: u32) -> u32 {
(x & 0xFF00FF00) >> 8 | (x & 0x00FF00FF) << 8
}
@ -169,68 +173,73 @@ const CHIP: Chip = Chip {
};
#[derive(Clone, Copy)]
#[repr(C)]
struct SdpcmHeader {
len: u16,
len_inv: u16,
/// Rx/Tx sequence number
sequence: u8,
/// 4 MSB Channel number, 4 LSB arbitrary flag
channel_and_flags: u8,
/// Length of next data frame, reserved for Tx
next_length: u8,
/// Data offset
header_length: u8,
/// Flow control bits, reserved for Tx
wireless_flow_control: u8,
/// Maximum Sequence number allowed by firmware for Tx
bus_data_credit: u8,
/// Reserved
reserved: [u8; 2],
enum IoctlState {
Idle,
Pending {
kind: u32,
cmd: u32,
iface: u32,
buf: *const [u8],
},
Sent,
Done,
}
#[derive(Clone, Copy)]
#[repr(C)]
struct CdcHeader {
cmd: u32,
out_len: u16,
in_len: u16,
flags: u16,
id: u16,
status: u32,
pub struct State {
ioctl_id: Cell<u16>,
ioctl_state: Cell<IoctlState>,
}
#[derive(Clone, Copy)]
#[repr(C)]
struct BdcHeader {
flags: u8,
/// 802.1d Priority (low 3 bits)
priority: u8,
flags2: u8,
/// Offset from end of BDC header to packet data, in 4-uint8_t words. Leaves room for optional headers.
data_offset: u8,
}
macro_rules! impl_bytes {
($t:ident) => {
impl $t {
const SIZE: usize = core::mem::size_of::<Self>();
pub fn to_bytes(&self) -> [u8; Self::SIZE] {
unsafe { core::mem::transmute(*self) }
}
pub fn from_bytes(bytes: &[u8; Self::SIZE]) -> Self {
unsafe { core::mem::transmute(*bytes) }
}
impl State {
pub fn new() -> Self {
Self {
ioctl_id: Cell::new(0),
ioctl_state: Cell::new(IoctlState::Idle),
}
};
}
}
impl_bytes!(SdpcmHeader);
impl_bytes!(CdcHeader);
impl_bytes!(BdcHeader);
pub struct Driver<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> {
pub struct Control<'a> {
state: &'a State,
}
impl<'a> Control<'a> {
pub async fn init(&mut self) {
let clm = unsafe { slice::from_raw_parts(0x10140000 as *const u8, 4752) };
let mut buf = [0; 8 + 12 + 1024];
buf[0..8].copy_from_slice(b"clmload\x00");
buf[8..20].copy_from_slice(b"\x02\x10\x02\x00\x00\x04\x00\x00\x00\x00\x00\x00");
buf[20..].copy_from_slice(&clm[..1024]);
self.ioctl(2, 263, 0, &buf).await;
info!("IOCTL done");
}
async fn ioctl(&mut self, kind: u32, cmd: u32, iface: u32, buf: &[u8]) {
// TODO cancel ioctl on future drop.
while !matches!(self.state.ioctl_state.get(), IoctlState::Idle) {
yield_now().await;
}
self.state.ioctl_id.set(self.state.ioctl_id.get().wrapping_add(1));
self.state
.ioctl_state
.set(IoctlState::Pending { kind, cmd, iface, buf });
while !matches!(self.state.ioctl_state.get(), IoctlState::Done) {
yield_now().await;
}
self.state.ioctl_state.set(IoctlState::Idle);
}
}
pub struct Runner<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> {
state: &'a State,
pwr: Output<'a, PWR>,
/// SPI chip-select.
@ -249,18 +258,29 @@ pub struct Driver<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> {
backplane_window: u32,
}
impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Driver<'a, PWR, CS, CLK, DIO> {
pub fn new(pwr: Output<'a, PWR>, cs: Output<'a, CS>, clk: Output<'a, CLK>, dio: Flex<'a, DIO>) -> Self {
Self {
pwr,
cs,
clk,
dio,
backplane_window: 0xAAAA_AAAA,
}
}
pub async fn new<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin>(
state: &'a State,
pwr: Output<'a, PWR>,
cs: Output<'a, CS>,
clk: Output<'a, CLK>,
dio: Flex<'a, DIO>,
) -> (Control<'a>, Runner<'a, PWR, CS, CLK, DIO>) {
let mut runner = Runner {
state,
pwr,
cs,
clk,
dio,
backplane_window: 0xAAAA_AAAA,
};
pub async fn init(&mut self) {
runner.init().await;
(Control { state }, runner)
}
impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Runner<'a, PWR, CS, CLK, DIO> {
async fn init(&mut self) {
// Set strap to select gSPI mode.
self.dio.set_as_output();
self.dio.set_low();
@ -306,6 +326,8 @@ impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Driver<'a, PWR, CS, CLK, DIO> {
self.bp_write32(CHIP.socsram_base_address + 0x10, 3);
self.bp_write32(CHIP.socsram_base_address + 0x44, 0);
let ram_addr = CHIP.atcm_ram_base_address;
// I'm flashing the firmwares independently at hardcoded addresses, instead of baking them
// into the program with `include_bytes!` or similar, so that flashing the program stays fast.
//
@ -314,9 +336,6 @@ impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Driver<'a, PWR, CS, CLK, DIO> {
// probe-rs-cli download 43439A0.bin --format bin --chip RP2040 --base-address 0x10100000
// probe-rs-cli download 43439A0.clm_blob --format bin --chip RP2040 --base-address 0x10140000
let fw = unsafe { slice::from_raw_parts(0x10100000 as *const u8, 224190) };
let clm = unsafe { slice::from_raw_parts(0x10140000 as *const u8, 4752) };
let ram_addr = CHIP.atcm_ram_base_address;
info!("loading fw");
self.bp_write(ram_addr, fw);
@ -374,17 +393,20 @@ impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Driver<'a, PWR, CS, CLK, DIO> {
let _ = self.read8(FUNC_BACKPLANE, REG_BACKPLANE_PULL_UP);
*/
let mut buf = [0; 8 + 12 + 1024];
buf[0..8].copy_from_slice(b"clmload\x00");
buf[8..20].copy_from_slice(b"\x02\x10\x02\x00\x00\x04\x00\x00\x00\x00\x00\x00");
buf[20..].copy_from_slice(&clm[..1024]);
self.send_ioctl(2, 263, 0, &buf);
info!("init done ");
}
pub async fn run(mut self) -> ! {
let mut old_irq = 0;
let mut buf = [0; 2048];
loop {
// Send stuff
if let IoctlState::Pending { kind, cmd, iface, buf } = self.state.ioctl_state.get() {
self.send_ioctl(kind, cmd, iface, unsafe { &*buf }, self.state.ioctl_id.get());
self.state.ioctl_state.set(IoctlState::Sent);
}
// Receive stuff
let irq = self.read16(FUNC_BUS, REG_BUS_INTERRUPT);
if irq != old_irq {
info!("irq: {:04x}", irq);
@ -419,6 +441,7 @@ impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Driver<'a, PWR, CS, CLK, DIO> {
}
}
// TODO use IRQs
yield_now().await;
}
}
@ -453,14 +476,16 @@ impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Driver<'a, PWR, CS, CLK, DIO> {
let cdc_header =
CdcHeader::from_bytes(packet[SdpcmHeader::SIZE..][..CdcHeader::SIZE].try_into().unwrap());
// TODO check cdc_header.id matches
// TODO check status
if cdc_header.id == self.state.ioctl_id.get() {
assert_eq!(cdc_header.status, 0); // todo propagate error
self.state.ioctl_state.set(IoctlState::Done);
}
}
_ => {}
}
}
fn send_ioctl(&mut self, kind: u32, cmd: u32, iface: u32, data: &[u8]) {
fn send_ioctl(&mut self, kind: u32, cmd: u32, iface: u32, data: &[u8], id: u16) {
let mut buf = [0; 2048];
let total_len = SdpcmHeader::SIZE + CdcHeader::SIZE + data.len();
@ -469,7 +494,7 @@ impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Driver<'a, PWR, CS, CLK, DIO> {
len: total_len as u16,
len_inv: !total_len as u16,
sequence: 0x02, // todo
channel_and_flags: 0, // control channle
channel_and_flags: 0, // control channel
next_length: 0,
header_length: SdpcmHeader::SIZE as _,
wireless_flow_control: 0,
@ -482,7 +507,7 @@ impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Driver<'a, PWR, CS, CLK, DIO> {
out_len: data.len() as _,
in_len: 0,
flags: kind as u16 | (iface as u16) << 12,
id: 1, // todo
id,
status: 0,
};
@ -780,16 +805,13 @@ impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Driver<'a, PWR, CS, CLK, DIO> {
w |= 0x01;
}
self.clk.set_high();
delay();
// falling edge
self.clk.set_low();
delay();
}
*word = w
}
self.clk.set_low();
delay();
}
fn spi_write(&mut self, words: &[u8]) {
@ -804,25 +826,19 @@ impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Driver<'a, PWR, CS, CLK, DIO> {
} else {
self.dio.set_high();
}
delay();
// rising edge
self.clk.set_high();
delay();
word = word << 1;
}
}
self.clk.set_low();
delay();
self.dio.set_as_input();
}
}
fn delay() {
//cortex_m::asm::delay(5);
}
macro_rules! nvram {
($($s:literal,)*) => {
concat_bytes!($($s, b"\x00",)* b"\x00\x00")

71
src/structs.rs Normal file
View File

@ -0,0 +1,71 @@
#[derive(Clone, Copy)]
#[repr(C)]
pub struct SdpcmHeader {
pub len: u16,
pub len_inv: u16,
/// Rx/Tx sequence number
pub sequence: u8,
/// 4 MSB Channel number, 4 LSB arbitrary flag
pub channel_and_flags: u8,
/// Length of next data frame, reserved for Tx
pub next_length: u8,
/// Data offset
pub header_length: u8,
/// Flow control bits, reserved for Tx
pub wireless_flow_control: u8,
/// Maximum Sequence number allowed by firmware for Tx
pub bus_data_credit: u8,
/// Reserved
pub reserved: [u8; 2],
}
#[derive(Clone, Copy)]
#[repr(C)]
pub struct CdcHeader {
pub cmd: u32,
pub out_len: u16,
pub in_len: u16,
pub flags: u16,
pub id: u16,
pub status: u32,
}
#[derive(Clone, Copy)]
#[repr(C)]
pub struct BdcHeader {
pub flags: u8,
/// 802.1d Priority (low 3 bits)
pub priority: u8,
pub flags2: u8,
/// Offset from end of BDC header to packet data, in 4-uint8_t words. Leaves room for optional headers.
pub data_offset: u8,
}
#[derive(Clone, Copy)]
#[repr(C)]
pub struct DownloadHeader {
pub flag: u16,
pub dload_type: u16,
pub len: u32,
pub crc: u32,
}
macro_rules! impl_bytes {
($t:ident) => {
impl $t {
pub const SIZE: usize = core::mem::size_of::<Self>();
pub fn to_bytes(&self) -> [u8; Self::SIZE] {
unsafe { core::mem::transmute(*self) }
}
pub fn from_bytes(bytes: &[u8; Self::SIZE]) -> Self {
unsafe { core::mem::transmute(*bytes) }
}
}
};
}
impl_bytes!(SdpcmHeader);
impl_bytes!(CdcHeader);
impl_bytes!(BdcHeader);
impl_bytes!(DownloadHeader);