Skip to content

Commit

Permalink
GH-45 extract raspberry specific to seperate module
Browse files Browse the repository at this point in the history
This task it the first in the sequence that provides the flashing
feature for RK1 compute modules. In this commit raspberry specific code
is moved to a seperate file. Secondly the runway is implemented to
support multiple "drivers" that enable read/write to internal storage of
compute modules. e.g. Writing new firmware to eMMC storage of a module

see:
turing-machines/BMC-Firmware#45
  • Loading branch information
svenrademakers committed Jul 6, 2023
1 parent f1fec6e commit 274d8d7
Show file tree
Hide file tree
Showing 8 changed files with 358 additions and 169 deletions.
32 changes: 16 additions & 16 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ crate-type = ["lib", "staticlib"]

[dependencies]
anyhow = "1.*"
async-trait = "0.1.*"
async-trait = "0.1.70"
bincode = "1.*"
crc = "3.0.1"
evdev = { version = "0.12.*", features = ["tokio"] }
Expand Down
80 changes: 41 additions & 39 deletions src/app/bmc_application.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
use crate::middleware::firmware_update_usb::{
fw_update_factory, FwUpdate, SUPPORTED_DEVICES, SUPPORTED_MSD_DEVICES,
};
use crate::middleware::power_controller::PowerController;
use crate::middleware::usbboot::{FlashProgress, FlashStatus};
use crate::middleware::{
Expand All @@ -13,6 +16,7 @@ use std::process::Command;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::time::Duration;
use tokio::pin;
use tokio::sync::{mpsc, oneshot};
use tokio::time::sleep;

Expand All @@ -24,13 +28,6 @@ const USB_CONFIG: &str = "usb_config";

const REBOOT_DELAY: Duration = Duration::from_millis(500);

const SUPPORTED_DEVICES: [UsbMassStorageProperty; 1] = [UsbMassStorageProperty {
_name: "Raspberry Pi CM4",
vid: 0x0a5c,
pid: 0x2711,
disk_prefix: Some("RPi-MSD-"),
}];

/// Describes the different configuration the USB bus can be setup
#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)]
pub enum UsbConfig {
Expand All @@ -44,14 +41,6 @@ pub enum UsbConfig {
Node(NodeId, UsbRoute),
}

#[derive(Debug)]
struct UsbMassStorageProperty {
pub _name: &'static str,
pub vid: u16,
pub pid: u16,
pub disk_prefix: Option<&'static str>,
}

#[derive(Debug)]
pub struct BmcApplication {
pin_controller: PinController,
Expand Down Expand Up @@ -213,16 +202,14 @@ impl BmcApplication {

pub async fn power_on(&self) -> anyhow::Result<()> {
let activated = self.app_db.get::<u8>(ACTIVATED_NODES_KEY).await?;
self.nodes_on
.store(true, std::sync::atomic::Ordering::Relaxed);
self.nodes_on.store(true, Ordering::Relaxed);
self.power_controller
.set_power_node(activated, 0b1111)
.await
}

pub async fn power_off(&self) -> anyhow::Result<()> {
self.nodes_on
.store(false, std::sync::atomic::Ordering::Relaxed);
self.nodes_on.store(false, Ordering::Relaxed);
self.power_controller.set_power_node(0b0000, 0b1111).await
}

Expand Down Expand Up @@ -256,7 +243,21 @@ impl BmcApplication {
node: NodeId,
router: UsbRoute,
progress_sender: mpsc::Sender<FlashProgress>,
) -> anyhow::Result<PathBuf> {
) -> anyhow::Result<()> {
// The SUPPORTED_MSD_DEVICES list contains vid_pids of usb drivers we know will load the
// storage of a node as a MSD device.
self.configure_node_for_fwupgrade(node, router, progress_sender, &SUPPORTED_MSD_DEVICES)
.await
.map(|_| ())
}

async fn configure_node_for_fwupgrade(
&self,
node: NodeId,
router: UsbRoute,
progress_sender: mpsc::Sender<FlashProgress>,
any_of: &[(u16, u16)],
) -> anyhow::Result<Box<dyn FwUpdate>> {
let mut progress_state = FlashProgress {
message: String::new(),
status: FlashStatus::Idle,
Expand Down Expand Up @@ -293,8 +294,7 @@ impl BmcApplication {
progress_state.message = String::from("Checking for presence of a USB device...");
progress_sender.send(progress_state.clone()).await?;

let matches =
usbboot::get_serials_for_vid_pid(SUPPORTED_DEVICES.iter().map(|d| (d.vid, d.pid)))?;
let matches = usbboot::get_serials_for_vid_pid(any_of)?;
usbboot::verify_one_device(&matches).map_err(|e| {
progress_sender
.try_send(FlashProgress {
Expand All @@ -305,18 +305,13 @@ impl BmcApplication {
e
})?;

progress_state.message = String::from("Rebooting as a USB mass storage device...");
progress_sender.send(progress_state.clone()).await?;

usbboot::boot_node_to_msd(node)?;

sleep(Duration::from_secs(3)).await;
progress_state.message = String::from("Checking for presence of a device file...");
progress_sender.send(progress_state.clone()).await?;

usbboot::get_device_path(SUPPORTED_DEVICES.iter().filter_map(|d| d.disk_prefix))
fw_update_factory(*matches.first().unwrap(), progress_sender)
.ok_or(anyhow::anyhow!(
"no usb driver for {:?}",
*matches.first().unwrap()
))?
.await
.context("error getting device path")
.context("usb driver init error")
}

pub async fn flash_node(
Expand All @@ -325,24 +320,31 @@ impl BmcApplication {
image_path: PathBuf,
progress_sender: mpsc::Sender<FlashProgress>,
) -> anyhow::Result<()> {
let device_path = self
.set_node_in_msd(node, UsbRoute::Bmc, progress_sender.clone())
let driver = self
.configure_node_for_fwupgrade(
node,
UsbRoute::Bmc,
progress_sender.clone(),
&SUPPORTED_DEVICES,
)
.await?;
pin!(driver);

let mut progress_state = FlashProgress {
message: String::new(),
status: FlashStatus::Idle,
status: FlashStatus::Setup,
};
progress_state.message = format!("Writing {:?} to {:?}", image_path, device_path);

progress_state.message = format!("Writing {:?}", image_path);
progress_sender.send(progress_state.clone()).await?;

let (img_len, img_checksum) =
usbboot::write_to_device(image_path, &device_path, &progress_sender).await?;
usbboot::write_to_device(image_path, &mut driver, &progress_sender).await?;

progress_state.message = String::from("Verifying checksum...");
progress_sender.send(progress_state.clone()).await?;

usbboot::verify_checksum(img_checksum, img_len, &device_path, &progress_sender).await?;
usbboot::verify_checksum(img_checksum, img_len, &mut driver, &progress_sender).await?;

progress_state.message = String::from("Flashing successful, restarting device...");
progress_sender.send(progress_state.clone()).await?;
Expand Down
34 changes: 34 additions & 0 deletions src/middleware/firmware_update_usb.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
use futures::future::BoxFuture;
use tokio::{
io::{AsyncRead, AsyncWrite},
sync::mpsc::Sender,
};

pub use super::usbboot::FlashingError;
use super::{rk1_fwudate::Rk1FwUpdateDriver, rpi_fwupdate::RpiFwUpdate, usbboot::FlashProgress};

pub const SUPPORTED_MSD_DEVICES: [(u16, u16); 1] = [RpiFwUpdate::VID_PID];
pub const SUPPORTED_DEVICES: [(u16, u16); 2] = [RpiFwUpdate::VID_PID, Rk1FwUpdateDriver::VID_PID];

pub trait FwUpdate: AsyncRead + AsyncWrite + std::marker::Unpin + Send {}

pub type FactoryItem = BoxFuture<'static, Result<Box<dyn FwUpdate>, FlashingError>>;

pub fn fw_update_factory(
vid_pid: (u16, u16),
logging: Sender<FlashProgress>,
) -> Option<FactoryItem> {
if vid_pid == RpiFwUpdate::VID_PID {
Some(Box::pin(async {
RpiFwUpdate::new(logging)
.await
.map(|u| Box::new(u) as Box<dyn FwUpdate>)
}))
} else if vid_pid == Rk1FwUpdateDriver::VID_PID {
Some(Box::pin(async {
Rk1FwUpdateDriver::new(logging).map(|u| Box::new(u) as Box<dyn FwUpdate>)
}))
} else {
None
}
}
3 changes: 3 additions & 0 deletions src/middleware/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
pub mod app_persistency;
pub mod event_listener;
pub mod firmware_update_usb;
mod gpio_definitions;
pub(crate) mod helpers;
pub mod pin_controller;
pub mod power_controller;
pub mod rk1_fwudate;
pub mod rpi_fwupdate;
pub mod usbboot;

#[repr(C)]
Expand Down
56 changes: 56 additions & 0 deletions src/middleware/rk1_fwudate.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#![allow(unused_variables)]

use super::{
firmware_update_usb::FwUpdate,
usbboot::{FlashProgress, FlashingError},
};
use tokio::{
io::{AsyncRead, AsyncWrite},
sync::mpsc::Sender,
};

#[derive(Debug)]
pub struct Rk1FwUpdateDriver {}

impl Rk1FwUpdateDriver {
pub const VID_PID: (u16, u16) = (0x2207, 0x350b);
pub fn new(logging: Sender<FlashProgress>) -> Result<Self, FlashingError> {
todo!()
}
}

impl FwUpdate for Rk1FwUpdateDriver {}

impl AsyncRead for Rk1FwUpdateDriver {
fn poll_read(
self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
buf: &mut tokio::io::ReadBuf<'_>,
) -> std::task::Poll<std::io::Result<()>> {
todo!()
}
}

impl AsyncWrite for Rk1FwUpdateDriver {
fn poll_write(
self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
buf: &[u8],
) -> std::task::Poll<Result<usize, std::io::Error>> {
todo!()
}

fn poll_flush(
self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Result<(), std::io::Error>> {
todo!()
}

fn poll_shutdown(
self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Result<(), std::io::Error>> {
todo!()
}
}
Loading

0 comments on commit 274d8d7

Please sign in to comment.