Skip to content

Commit

Permalink
tpi_rs: add serial handlers
Browse files Browse the repository at this point in the history
Fixes #22
  • Loading branch information
ruslashev committed Sep 15, 2023
1 parent ad360f3 commit dcbca5e
Show file tree
Hide file tree
Showing 9 changed files with 394 additions and 28 deletions.
93 changes: 89 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
[workspace]
members = ["bmcd", "tpi_rs"]
resolver = "2"

[workspace.package]
version = "1.3.0"
Expand Down
23 changes: 16 additions & 7 deletions bmcd/src/into_legacy_response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use std::{borrow::Cow, fmt::Display};
pub enum LegacyResponse {
Success(Option<serde_json::Value>),
Error(StatusCode, Cow<'static, str>),
UartData(String),
}

impl LegacyResponse {
Expand Down Expand Up @@ -77,6 +78,7 @@ impl Display for LegacyResponse {
"{}",
s.as_ref().map(|json| json.to_string()).unwrap_or_default()
),
LegacyResponse::UartData(s) => write!(f, "{}", s),
LegacyResponse::Error(_, msg) => write!(f, "{}", msg),
}
}
Expand All @@ -94,19 +96,26 @@ pub type LegacyResult<T> = Result<T, LegacyResponse>;

impl From<LegacyResponse> for HttpResponse {
fn from(value: LegacyResponse) -> Self {
let (response, result) = match value {
let (response, result, is_uart) = match value {
LegacyResponse::Success(None) => {
(StatusCode::OK, serde_json::Value::String("ok".to_string()))
(StatusCode::OK, serde_json::Value::String("ok".to_string()), false)
}
LegacyResponse::Success(Some(body)) => (StatusCode::OK, body),
LegacyResponse::Success(Some(body)) => (StatusCode::OK, body, false),
LegacyResponse::UartData(d) => (StatusCode::OK, serde_json::Value::String(d), true),
LegacyResponse::Error(status_code, msg) => {
(status_code, serde_json::Value::String(msg.into_owned()))
(status_code, serde_json::Value::String(msg.into_owned()), false)
}
};

let msg = json!({
"response": [{ "result": result }]
});
let msg = if is_uart {
json!({
"response": [{ "uart": result }]
})
} else {
json!({
"response": [{ "result": result }]
})
};

HttpResponseBuilder::new(response).json(msg)
}
Expand Down
28 changes: 12 additions & 16 deletions bmcd/src/legacy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,8 @@ async fn api_entry(bmc: web::Data<BmcApplication>, query: Query) -> impl Respond
("reset", true) => reset_node(bmc, query).await.into(),
("sdcard", true) => format_sdcard().into(),
("sdcard", false) => get_sdcard_info(),
("uart", true) => write_to_uart(bmc, query).into(),
("uart", false) => read_from_uart(bmc, query).into(),
("uart", true) => write_to_uart(bmc, query).await.into(),
("uart", false) => read_from_uart(bmc, query).await.into(),
("usb", true) => set_usb_mode(bmc, query).await.into(),
("usb", false) => get_usb_mode(bmc).await.into(),
_ => (
Expand Down Expand Up @@ -283,30 +283,26 @@ fn get_sdcard_fs_stat() -> anyhow::Result<(u64, u64)> {
Ok((total, free))
}

fn write_to_uart(bmc: &BmcApplication, query: Query) -> LegacyResult<()> {
async fn write_to_uart(bmc: &BmcApplication, query: Query) -> LegacyResult<()> {
let node = get_node_param(&query)?;
let Some(cmd) = query.get("cmd") else {
return Err(LegacyResponse::bad_request("Missing `cmd` parameter"));
};
let mut data = cmd.clone();

uart_write(bmc, node, cmd)
data.push_str("\r\n");

bmc.serial_write(node, data.as_bytes())
.await
.context("write over UART")
.map_err(Into::into)
}

fn uart_write(_bmc: &BmcApplication, _node: NodeId, _cmd: &str) -> anyhow::Result<()> {
todo!()
}

fn read_from_uart(bmc: &BmcApplication, query: Query) -> LegacyResult<()> {
async fn read_from_uart(bmc: &BmcApplication, query: Query) -> LegacyResult<LegacyResponse> {
let node = get_node_param(&query)?;
uart_read(bmc, node)
.context("read from UART")
.map_err(Into::into)
}
let data = bmc.serial_read(node).await;

fn uart_read(_bmc: &BmcApplication, _node: NodeId) -> anyhow::Result<()> {
todo!()
Ok(LegacyResponse::UartData(data))
}

async fn set_usb_mode(bmc: &BmcApplication, query: Query) -> LegacyResult<()> {
Expand Down Expand Up @@ -368,7 +364,7 @@ async fn handle_flash_request(
))?;

let size = u64::from_str(size)
.map_err(|_| LegacyResponse::bad_request("`lenght` parameter not a number"))?;
.map_err(|_| LegacyResponse::bad_request("`length` parameter is not a number"))?;

let peer: String = request
.connection_info()
Expand Down
1 change: 1 addition & 0 deletions bmcd/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ async fn main() -> anyhow::Result<()> {
let tls6 = load_tls_configuration(&config.tls.private_key, &config.tls.certificate)?;

let bmc = Arc::new(BmcApplication::new().await?);
bmc.start_serial_workers().await;
run_event_listener(bmc.clone())?;
let flash_service = Data::new(Mutex::new(FlashService::new(bmc.clone())));
let bmc = Data::from(bmc);
Expand Down
1 change: 1 addition & 0 deletions tpi_rs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ rockusb = { version = "0.1.1" }
rusb = "0.9.3"
rustpiboot = { git = "https://github.com/ruslashev/rustpiboot.git", rev="89e6497"}
serde = { version = "1.0.188", features = ["derive"] }
tokio-serial = { version = "5.4.4", features = ["rt", "codec"] }

anyhow.workspace = true
log.workspace = true
Expand Down
21 changes: 20 additions & 1 deletion tpi_rs/src/app/bmc_application.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::middleware::firmware_update::{
use crate::middleware::persistency::app_persistency::ApplicationPersistency;
use crate::middleware::persistency::app_persistency::PersistencyBuilder;
use crate::middleware::power_controller::PowerController;
use crate::middleware::serial::SerialConnections;
use crate::middleware::usbboot;
use crate::middleware::{pin_controller::PinController, NodeId, UsbMode, UsbRoute};
use anyhow::{ensure, Context};
Expand All @@ -13,8 +14,9 @@ use serde::{Deserialize, Serialize};
use std::ops::Deref;
use std::process::Command;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::mpsc;
use tokio::sync::{mpsc, Mutex};
use tokio::time::sleep;
/// Stores which slots are actually used. This information is used to determine
/// for instance, which nodes need to be powered on, when such command is given
Expand Down Expand Up @@ -42,6 +44,7 @@ pub struct BmcApplication {
pub(super) power_controller: PowerController,
pub(super) app_db: ApplicationPersistency,
pub(super) nodes_on: AtomicBool,
serial: Arc<Mutex<SerialConnections>>,
}

impl BmcApplication {
Expand All @@ -53,12 +56,16 @@ impl BmcApplication {
.register_key(USB_CONFIG, &UsbConfig::UsbA(NodeId::Node1, false))
.build()
.await?;
let serial = SerialConnections::new()?;
// This Arc<Mutex<>> is used only to retain interior mutability
let serial = Arc::new(Mutex::new(serial));

let instance = Self {
pin_controller,
power_controller,
app_db,
nodes_on: AtomicBool::new(false),
serial,
};

instance.initialize().await?;
Expand Down Expand Up @@ -285,4 +292,16 @@ impl BmcApplication {
Command::new("shutdown").args(["-r", "now"]).spawn()?;
Ok(())
}

pub async fn start_serial_workers(&self) {
self.serial.lock().await.run();
}

pub async fn serial_read(&self, node: NodeId) -> String {
self.serial.lock().await.read(node).await
}

pub async fn serial_write(&self, node: NodeId, data: &[u8]) -> anyhow::Result<()> {
self.serial.lock().await.write(node, data).await
}
}
1 change: 1 addition & 0 deletions tpi_rs/src/middleware/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ mod helpers;
pub mod persistency;
pub mod pin_controller;
pub mod power_controller;
pub mod serial;
pub mod usbboot;

#[repr(C)]
Expand Down
Loading

0 comments on commit dcbca5e

Please sign in to comment.