use serde::Deserialize;
use serde_json::{Map, Value};
use super::net_handle;
use crate::wire::lenient;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BitOrder {
Msb,
Lsb,
}
impl BitOrder {
pub fn as_str(&self) -> &'static str {
match self {
BitOrder::Msb => "msb",
BitOrder::Lsb => "lsb",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CsActive {
Low,
High,
}
impl CsActive {
pub fn as_str(&self) -> &'static str {
match self {
CsActive::Low => "low",
CsActive::High => "high",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CsMode {
Auto,
Manual,
}
impl CsMode {
pub fn as_str(&self) -> &'static str {
match self {
CsMode::Auto => "auto",
CsMode::Manual => "manual",
}
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct SpiConfig {
pub mode: Option<u8>,
pub bit_order: Option<BitOrder>,
pub frequency_hz: Option<u32>,
pub word_size: Option<u8>,
pub cs_active: Option<CsActive>,
pub cs_mode: Option<CsMode>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct SpiEffectiveConfig {
#[serde(default, deserialize_with = "lenient::opt_i64")]
pub mode: Option<i64>,
#[serde(default, deserialize_with = "lenient::opt_i64")]
pub frequency_hz: Option<i64>,
#[serde(default, deserialize_with = "lenient::opt_i64")]
pub word_size: Option<i64>,
#[serde(default)]
pub bit_order: Option<String>,
#[serde(default)]
pub cs_active: Option<String>,
#[serde(default)]
pub cs_mode: Option<String>,
#[serde(flatten)]
pub extra: Map<String, Value>,
}
#[derive(Debug, Clone)]
pub struct SpiTransfer {
pub words: Vec<u32>,
pub word_size: u32,
}
#[derive(Debug, Clone, Copy)]
pub struct SpiOptions {
pub fill: u32,
pub keep_cs: bool,
}
impl Default for SpiOptions {
fn default() -> Self {
SpiOptions {
fill: 0xFF,
keep_cs: false,
}
}
}
pub(crate) mod ops {
use serde_json::{json, Map, Value};
use super::{SpiConfig, SpiEffectiveConfig, SpiOptions, SpiTransfer};
use crate::error::{Error, Result};
use crate::wire::{
as_i64, net_command, value_as, value_int_list, CommandResponse, Op, Timeout,
};
const ROLE: &str = "spi";
fn parse_transfer(resp: CommandResponse) -> Result<SpiTransfer> {
let word_size = resp
.extra
.get("word_size")
.and_then(as_i64)
.and_then(|n| u32::try_from(n).ok())
.ok_or_else(|| Error::Decode("SPI response missing 'word_size'".to_string()))?;
let words = value_int_list(resp)?;
Ok(SpiTransfer { words, word_size })
}
pub(crate) fn configure(name: &str, config: &SpiConfig) -> Op<SpiEffectiveConfig> {
let mut params = Map::new();
if let Some(m) = config.mode {
params.insert("mode".to_string(), json!(m));
}
if let Some(b) = config.bit_order {
params.insert("bit_order".to_string(), json!(b.as_str()));
}
if let Some(f) = config.frequency_hz {
params.insert("frequency_hz".to_string(), json!(f));
}
if let Some(w) = config.word_size {
params.insert("word_size".to_string(), json!(w));
}
if let Some(c) = config.cs_active {
params.insert("cs_active".to_string(), json!(c.as_str()));
}
if let Some(c) = config.cs_mode {
params.insert("cs_mode".to_string(), json!(c.as_str()));
}
Op {
req: net_command(name, ROLE, "config", Value::Object(params), Timeout::Default),
parse: value_as::<SpiEffectiveConfig>,
}
}
pub(crate) fn read(name: &str, n_words: u32, opts: &SpiOptions) -> Op<SpiTransfer> {
Op {
req: net_command(
name,
ROLE,
"read",
json!({ "n_words": n_words, "fill": opts.fill, "keep_cs": opts.keep_cs }),
Timeout::Default,
),
parse: parse_transfer,
}
}
pub(crate) fn write(name: &str, data: &[u32], opts: &SpiOptions) -> Op<SpiTransfer> {
Op {
req: net_command(
name,
ROLE,
"write",
json!({ "data": data, "keep_cs": opts.keep_cs }),
Timeout::Default,
),
parse: parse_transfer,
}
}
pub(crate) fn read_write(name: &str, data: &[u32], opts: &SpiOptions) -> Op<SpiTransfer> {
Op {
req: net_command(
name,
ROLE,
"read_write",
json!({ "data": data, "keep_cs": opts.keep_cs }),
Timeout::Default,
),
parse: parse_transfer,
}
}
pub(crate) fn transfer(
name: &str,
data: &[u32],
n_words: u32,
opts: &SpiOptions,
) -> Op<SpiTransfer> {
Op {
req: net_command(
name,
ROLE,
"transfer",
json!({
"data": data,
"n_words": n_words,
"fill": opts.fill,
"keep_cs": opts.keep_cs,
}),
Timeout::Default,
),
parse: parse_transfer,
}
}
}
net_handle! {
sync: Spi,
async: AsyncSpi,
methods: {
fn configure(config: &SpiConfig) -> SpiEffectiveConfig = ops::configure;
fn read(n_words: u32, opts: &SpiOptions) -> SpiTransfer = ops::read;
fn write(data: &[u32], opts: &SpiOptions) -> SpiTransfer = ops::write;
fn read_write(data: &[u32], opts: &SpiOptions) -> SpiTransfer = ops::read_write;
fn transfer(data: &[u32], n_words: u32, opts: &SpiOptions) -> SpiTransfer = ops::transfer;
}
}