use serde::Deserialize;
use super::net_handle;
use crate::wire::lenient;
#[derive(Debug, Clone, Deserialize)]
pub struct I2cEffectiveConfig {
#[serde(default, deserialize_with = "lenient::opt_i64")]
pub frequency_hz: Option<i64>,
#[serde(default, deserialize_with = "lenient::opt_bool")]
pub pull_ups: Option<bool>,
}
pub(crate) mod ops {
use serde_json::{json, Map, Value};
use super::I2cEffectiveConfig;
use crate::error::{Error, Result};
use crate::wire::{as_i64, net_command, unit, value_as, CommandResponse, Op, Timeout};
const ROLE: &str = "i2c";
fn parse_bytes(resp: CommandResponse) -> Result<Vec<u8>> {
let arr = resp
.value
.as_ref()
.and_then(Value::as_array)
.ok_or_else(|| Error::Decode(format!("expected byte list, got {:?}", resp.value)))?;
arr.iter()
.map(|v| {
as_i64(v)
.and_then(|n| u8::try_from(n).ok())
.ok_or_else(|| Error::Decode(format!("non-byte element: {v:?}")))
})
.collect()
}
fn parse_addresses(resp: CommandResponse) -> Result<Vec<u16>> {
let arr = resp
.value
.as_ref()
.and_then(Value::as_array)
.ok_or_else(|| Error::Decode(format!("expected address list, got {:?}", resp.value)))?;
arr.iter()
.map(|v| {
as_i64(v)
.and_then(|n| u16::try_from(n).ok())
.ok_or_else(|| Error::Decode(format!("non-address element: {v:?}")))
})
.collect()
}
pub(crate) fn configure(
name: &str,
frequency_hz: Option<u32>,
pull_ups: Option<bool>,
) -> Op<I2cEffectiveConfig> {
let mut params = Map::new();
if let Some(f) = frequency_hz {
params.insert("frequency_hz".to_string(), json!(f));
}
if let Some(p) = pull_ups {
params.insert("pull_ups".to_string(), json!(p));
}
Op {
req: net_command(name, ROLE, "config", Value::Object(params), Timeout::Default),
parse: value_as::<I2cEffectiveConfig>,
}
}
pub(crate) fn scan(name: &str) -> Op<Vec<u16>> {
Op {
req: net_command(name, ROLE, "scan", json!({}), Timeout::Default),
parse: parse_addresses,
}
}
pub(crate) fn scan_range(name: &str, start_addr: u16, end_addr: u16) -> Op<Vec<u16>> {
Op {
req: net_command(
name,
ROLE,
"scan",
json!({ "start_addr": start_addr, "end_addr": end_addr }),
Timeout::Default,
),
parse: parse_addresses,
}
}
pub(crate) fn read(name: &str, address: u16, num_bytes: u32) -> Op<Vec<u8>> {
Op {
req: net_command(
name,
ROLE,
"read",
json!({ "address": address, "num_bytes": num_bytes }),
Timeout::Default,
),
parse: parse_bytes,
}
}
pub(crate) fn write(name: &str, address: u16, data: &[u8]) -> Op<()> {
Op {
req: net_command(
name,
ROLE,
"write",
json!({ "address": address, "data": data }),
Timeout::Default,
),
parse: unit,
}
}
pub(crate) fn write_read(
name: &str,
address: u16,
data: &[u8],
num_bytes: u32,
) -> Op<Vec<u8>> {
Op {
req: net_command(
name,
ROLE,
"transfer",
json!({ "address": address, "data": data, "num_bytes": num_bytes }),
Timeout::Default,
),
parse: parse_bytes,
}
}
}
net_handle! {
sync: I2c,
async: AsyncI2c,
methods: {
fn configure(frequency_hz: Option<u32>, pull_ups: Option<bool>) -> I2cEffectiveConfig = ops::configure;
fn scan() -> Vec<u16> = ops::scan;
fn scan_range(start_addr: u16, end_addr: u16) -> Vec<u16> = ops::scan_range;
fn read(address: u16, num_bytes: u32) -> Vec<u8> = ops::read;
fn write(address: u16, data: &[u8]) -> () = ops::write;
fn write_read(address: u16, data: &[u8], num_bytes: u32) -> Vec<u8> = ops::write_read;
}
}