use crate::persistency::app_persistency::PersistencyBuilder;
use crate::usb_boot::NodeDrivers;
use crate::utils::get_timestamp_unix;
+use crate::{
+ app::usb_gadget::append_msd_config_to_usb_gadget,
+ app::usb_gadget::remove_msd_function_from_usb_gadget,
+};
+
use anyhow::{ensure, Context};
+use log::info;
use log::{debug, trace};
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
UsbConfig::Node(host, route) => (UsbMode::Host, host, route),
};
+ if mode != UsbMode::Flash {
+ if let Err(e) = remove_msd_function_from_usb_gadget().await {
+ log::error!("{:#}", e);
+ }
+ }
+
self.pin_controller.set_usb_route(route).await?;
self.pin_controller.select_usb(dest, mode)?;
}
pub async fn node_in_msd(&self, node: NodeId) -> anyhow::Result<PathBuf> {
+ // stop_usb_gadget_if_running().await?;
+
self.reboot_into_usb(node, UsbConfig::Flashing(node, UsbRoute::Bmc))
.await?;
- Ok(self.node_drivers.load_as_block_device().await?)
+ let blk_dev = self.node_drivers.load_as_block_device().await?;
+
+ if let Err(e) = append_msd_config_to_usb_gadget(&blk_dev).await {
+ log::error!("msd usb-gadget: {:#}", e);
+ } else {
+ info!("BMC-OTG: Node mass storage CDC enabled");
+ }
+
+ Ok(blk_dev)
}
pub async fn node_in_flash(
--- /dev/null
+// Copyright 2023 Turing Machines
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+use anyhow::{anyhow, bail, Context};
+use std::{
+ ops::Deref,
+ path::Path,
+ process::{Command, Stdio},
+};
+use tokio::{
+ fs::{symlink, OpenOptions},
+ io::AsyncWriteExt,
+ task::spawn_blocking,
+};
+
+use crate::utils::logging_sink_stdio;
+
+const BMC_USB_OTG: &str = "/sys/kernel/config/usb_gadget/g1";
+
+pub async fn append_msd_config_to_usb_gadget(block_device: &Path) -> anyhow::Result<()> {
+ if is_gadget_running().await? {
+ remove_msd_function_from_usb_gadget().await?;
+ usb_gadget_service(GadgetCmd::Stop)
+ .await
+ .context("usb_gadget")?;
+ }
+
+ let usb_gadget = Path::new(BMC_USB_OTG);
+ if !usb_gadget.exists() {
+ bail!("{} does not exist", BMC_USB_OTG);
+ }
+
+ let config = usb_gadget.join("configs/c.1");
+
+ let mass_storage_function = usb_gadget.join("functions/mass_storage.0");
+ tokio::fs::create_dir_all(&mass_storage_function)
+ .await
+ .with_context(|| mass_storage_function.to_string_lossy().to_string())?;
+
+ let lun0 = mass_storage_function.join("lun.0/file");
+ let mut file = OpenOptions::new()
+ .write(true)
+ .truncate(true)
+ .create(true)
+ .open(lun0)
+ .await?;
+
+ file.write_all(
+ block_device
+ .to_str()
+ .ok_or(anyhow!(
+ "{} not convertable to string",
+ block_device.to_str().unwrap_or_default()
+ ))?
+ .as_bytes(),
+ )
+ .await?;
+
+ symlink(&mass_storage_function, &config.join("mass_storage.0"))
+ .await
+ .with_context(|| {
+ format!(
+ "symlink {} to {}",
+ mass_storage_function.to_string_lossy(),
+ config.to_string_lossy()
+ )
+ })?;
+
+ usb_gadget_service(GadgetCmd::Start)
+ .await
+ .context("usb_gadget")?;
+
+ Ok(())
+}
+
+pub async fn remove_msd_function_from_usb_gadget() -> anyhow::Result<()> {
+ let mass_storage_config = Path::new(BMC_USB_OTG).join("configs/c.1/mass_storage.0");
+ if mass_storage_config.exists() {
+ usb_gadget_service(GadgetCmd::Stop).await?;
+ tokio::fs::remove_file(&mass_storage_config)
+ .await
+ .with_context(|| mass_storage_config.to_string_lossy().to_string())?;
+ usb_gadget_service(GadgetCmd::Start).await?;
+ }
+ Ok(())
+}
+
+async fn is_gadget_running() -> anyhow::Result<bool> {
+ let udc = Path::new(BMC_USB_OTG).join("UDC");
+ Ok(tokio::fs::read_to_string(udc)
+ .await
+ .map(|s| !s.trim().is_empty())?)
+}
+
+enum GadgetCmd {
+ Start,
+ Stop,
+}
+
+impl Deref for GadgetCmd {
+ type Target = str;
+
+ fn deref(&self) -> &Self::Target {
+ match self {
+ GadgetCmd::Start => "start",
+ GadgetCmd::Stop => "stop",
+ }
+ }
+}
+
+async fn usb_gadget_service(command: GadgetCmd) -> anyhow::Result<()> {
+ let output = spawn_blocking(move || {
+ Command::new("/etc/init.d/S11bmc-otg")
+ .arg(command.deref())
+ .stdout(Stdio::piped())
+ .stderr(Stdio::piped())
+ .output()
+ })
+ .await??;
+
+ logging_sink_stdio(&output).await?;
+
+ if !output.status.success() {
+ bail!("S11bmc-otg: {}", output.status);
+ }
+
+ Ok(())
+}
// limitations under the License.
mod rockusb;
mod rpiboot;
+use self::{rockusb::RockusbBoot, rpiboot::RpiBoot};
use async_trait::async_trait;
use log::info;
use rusb::GlobalContext;
use thiserror::Error;
use tokio::io::{AsyncRead, AsyncSeek, AsyncWrite};
-use self::{rockusb::RockusbBoot, rpiboot::RpiBoot};
-
pub trait DataTransport: AsyncRead + AsyncWrite + AsyncSeek + Send + Unpin {}
impl DataTransport for tokio::fs::File {}
/// This function tries to find the first USB device which exist a backend for.
fn find_first(&self) -> Result<(rusb::Device<GlobalContext>, &dyn UsbBoot), UsbBootError> {
log::info!("Checking for presence of a USB device...");
- let devices = rusb::devices().map_err(UsbBootError::internal_error)?;
+ let devices = rusb::devices()?;
let mut backends = self.backends.iter().filter_map(|backend| {
let found = devices.iter().find(|dev| {
let Ok(descriptor) = dev.device_descriptor() else {
mod event_listener;
mod io;
-use std::path::PathBuf;
-use std::time::{SystemTime, UNIX_EPOCH};
-
use anyhow::bail;
+use std::time::{SystemTime, UNIX_EPOCH};
#[doc(inline)]
pub use event_listener::*;
pub use io::*;
+use std::{path::PathBuf, process::Output};
+use tokio::io::AsyncBufReadExt;
pub fn string_from_utf16(bytes: &[u8], little_endian: bool) -> String {
let u16s = bytes.chunks_exact(2).map(|pair| {
.ok()
.map(|x| x.as_secs())
}
+
+pub async fn logging_sink_stdio(output: &Output) -> std::io::Result<()> {
+ let mut lines = output.stdout.lines();
+ while let Some(line) = lines.next_line().await? {
+ log::info!("{}", line);
+ }
+
+ let mut lines = output.stderr.lines();
+ while let Some(line) = lines.next_line().await? {
+ log::error!("{}", line);
+ }
+ Ok(())
+}