mirror of
https://github.com/saymrwulf/xous-core.git
synced 2026-11-15 08:30:02 +00:00
apps/transientdisk: app to demonstrate userspace usb mass storage api
Exposes a 1.44mb usb drive, that survives until you either shutdown the device, or change app.
This commit is contained in:
parent
2ff7d0a1fb
commit
916c22896a
5 changed files with 330 additions and 0 deletions
16
Cargo.lock
generated
16
Cargo.lock
generated
|
|
@ -4028,6 +4028,22 @@ dependencies = [
|
|||
"tracing-subscriber",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "transientdisk"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"gam",
|
||||
"graphics-server",
|
||||
"log",
|
||||
"num-derive",
|
||||
"num-traits",
|
||||
"usb-device-xous",
|
||||
"xous 0.9.31 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"xous-api-log",
|
||||
"xous-api-names",
|
||||
"xous-ipc 0.9.31 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "trng"
|
||||
version = "0.1.0"
|
||||
|
|
|
|||
|
|
@ -77,6 +77,7 @@ members = [
|
|||
"apps/repl",
|
||||
"apps/vault",
|
||||
"apps/vault/tools/vaultbackup-rs",
|
||||
"apps/transientdisk",
|
||||
"services/libstd-test",
|
||||
"services/ffi-test",
|
||||
"services/tts",
|
||||
|
|
|
|||
|
|
@ -59,5 +59,13 @@
|
|||
}
|
||||
},
|
||||
"submenu": 1
|
||||
},
|
||||
"transientdisk": {
|
||||
"context_name": "Transient USB disk",
|
||||
"menu_name": {
|
||||
"appmenu.transientdisk": {
|
||||
"en": "Transient USB disk"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
18
apps/transientdisk/Cargo.toml
Normal file
18
apps/transientdisk/Cargo.toml
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
[package]
|
||||
name = "transientdisk"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
log = "0.4.14"
|
||||
num-derive = {version = "0.3.3", default-features = false}
|
||||
num-traits = {version = "0.2.14", default-features = false}
|
||||
xous = "0.9.31"
|
||||
xous-ipc = "0.9.31"
|
||||
log-server = { package = "xous-api-log", version = "0.1.26" }
|
||||
xous-names = { package = "xous-api-names", version = "0.9.28" }
|
||||
gam = {path = "../../services/gam" }
|
||||
graphics-server = {path = "../../services/graphics-server" }
|
||||
usb-device-xous = {path="../../services/usb-device-xous"}
|
||||
287
apps/transientdisk/src/main.rs
Normal file
287
apps/transientdisk/src/main.rs
Normal file
|
|
@ -0,0 +1,287 @@
|
|||
#![cfg_attr(target_os = "none", no_std)]
|
||||
#![cfg_attr(target_os = "none", no_main)]
|
||||
|
||||
use core::fmt::Write;
|
||||
use graphics_server::api::GlyphStyle;
|
||||
use graphics_server::{DrawStyle, Gid, PixelColor, Point, Rectangle, TextBounds, TextView};
|
||||
use num_traits::*;
|
||||
|
||||
pub(crate) const SERVER_NAME_TRANSIENTDISK: &str = "_Transient Disk_";
|
||||
|
||||
/// Top level application events.
|
||||
#[derive(Debug, num_derive::FromPrimitive, num_derive::ToPrimitive)]
|
||||
pub(crate) enum TradOp {
|
||||
/// Redraw the screen
|
||||
Redraw = 0,
|
||||
|
||||
FocusChange,
|
||||
|
||||
Read,
|
||||
Write,
|
||||
MaxLba,
|
||||
|
||||
/// Quit the application
|
||||
Quit,
|
||||
}
|
||||
|
||||
// Framework code to draw a string of text on the screen.
|
||||
// Taken from hello app.
|
||||
struct UI {
|
||||
content: Gid,
|
||||
gam: gam::Gam,
|
||||
_gam_token: [u32; 4],
|
||||
screensize: Point,
|
||||
#[cfg(feature = "tts")]
|
||||
tts: TtsFrontend,
|
||||
}
|
||||
|
||||
impl UI {
|
||||
fn new(xns: &xous_names::XousNames, sid: xous::SID) -> Self {
|
||||
let gam = gam::Gam::new(&xns).expect("Can't connect to GAM");
|
||||
let gam_token = gam
|
||||
.register_ux(gam::UxRegistration {
|
||||
app_name: xous_ipc::String::<128>::from_str(gam::APP_NAME_TRANSIENTDISK),
|
||||
ux_type: gam::UxType::Chat,
|
||||
predictor: None,
|
||||
listener: sid.to_array(),
|
||||
redraw_id: TradOp::Redraw.to_u32().unwrap(),
|
||||
gotinput_id: None,
|
||||
audioframe_id: None,
|
||||
rawkeys_id: None,
|
||||
focuschange_id: Some(TradOp::FocusChange.to_u32().unwrap()),
|
||||
})
|
||||
.expect("Could not register GAM UX")
|
||||
.unwrap();
|
||||
|
||||
let content = gam
|
||||
.request_content_canvas(gam_token)
|
||||
.expect("Could not get content canvas");
|
||||
let screensize = gam
|
||||
.get_canvas_bounds(content)
|
||||
.expect("Could not get canvas dimensions");
|
||||
Self {
|
||||
gam,
|
||||
_gam_token: gam_token,
|
||||
content,
|
||||
screensize,
|
||||
}
|
||||
}
|
||||
|
||||
/// Clear the entire screen.
|
||||
fn clear_area(&self) {
|
||||
self.gam
|
||||
.draw_rectangle(
|
||||
self.content,
|
||||
Rectangle::new_with_style(
|
||||
Point::new(0, 0),
|
||||
self.screensize,
|
||||
DrawStyle {
|
||||
fill_color: Some(PixelColor::Light),
|
||||
stroke_color: None,
|
||||
stroke_width: 0,
|
||||
},
|
||||
),
|
||||
)
|
||||
.expect("can't clear content area");
|
||||
}
|
||||
|
||||
/// Redraw the text view onto the screen.
|
||||
fn redraw(&mut self) {
|
||||
self.clear_area();
|
||||
|
||||
let mut text_view = TextView::new(
|
||||
self.content,
|
||||
TextBounds::GrowableFromBr(
|
||||
Point::new(
|
||||
self.screensize.x - 45,
|
||||
self.screensize.y - (self.screensize.y / 2),
|
||||
),
|
||||
(self.screensize.x / 5 * 4) as u16,
|
||||
),
|
||||
);
|
||||
|
||||
text_view.border_width = 1;
|
||||
text_view.draw_border = true;
|
||||
text_view.clear_area = true;
|
||||
text_view.rounded_border = Some(3);
|
||||
text_view.style = GlyphStyle::ExtraLarge;
|
||||
write!(text_view.text, "1.44mb flash drive now available.")
|
||||
.expect("Could not write to text view");
|
||||
|
||||
self.gam
|
||||
.post_textview(&mut text_view)
|
||||
.expect("Could not render text view");
|
||||
self.gam.redraw().expect("Could not redraw screen");
|
||||
}
|
||||
}
|
||||
|
||||
struct FlashDrive {
|
||||
capacity: usize,
|
||||
block_size: usize,
|
||||
memory: xous::MemoryRange,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum FlashDriveError {
|
||||
CapacityNotPageAligned,
|
||||
}
|
||||
|
||||
impl FlashDrive {
|
||||
fn new(capacity: usize, block_size: usize) -> Result<Self, FlashDriveError> {
|
||||
if (capacity % 1024) != 0 {
|
||||
return Err(FlashDriveError::CapacityNotPageAligned);
|
||||
}
|
||||
|
||||
let mut backing = xous::syscall::map_memory(
|
||||
None,
|
||||
None,
|
||||
capacity,
|
||||
xous::MemoryFlags::R | xous::MemoryFlags::W,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// initialize backing slice with bogus data
|
||||
let backing_slice: &mut [u32] = backing.as_slice_mut();
|
||||
for (index, d) in backing_slice.iter_mut().enumerate() {
|
||||
*d = index as u32;
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
capacity,
|
||||
block_size,
|
||||
memory: backing,
|
||||
})
|
||||
}
|
||||
|
||||
fn read(&mut self, lba: usize, data: &mut [u8]) {
|
||||
let backing_slice: &[u8] = self.memory.as_slice();
|
||||
|
||||
let rawdata = &backing_slice[lba * self.block_size..(lba + 1) * self.block_size];
|
||||
|
||||
data[..self.block_size].copy_from_slice(rawdata);
|
||||
}
|
||||
|
||||
fn write(&mut self, lba: usize, data: &mut [u8]) {
|
||||
let backing_slice: &mut [u8] = self.memory.as_slice_mut();
|
||||
backing_slice[lba * self.block_size..(lba + 1) * self.block_size]
|
||||
.copy_from_slice(&data[..self.block_size]);
|
||||
}
|
||||
|
||||
fn max_lba(&self) -> u32 {
|
||||
(self.capacity as u32 / 512) - 1
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> ! {
|
||||
log_server::init_wait().unwrap();
|
||||
log::set_max_level(log::LevelFilter::Info);
|
||||
log::info!("Hello world PID is {}", xous::process::id());
|
||||
|
||||
let xns = xous_names::XousNames::new().unwrap();
|
||||
|
||||
// Register the server with xous
|
||||
let sid = xns
|
||||
.register_name(SERVER_NAME_TRANSIENTDISK, None)
|
||||
.expect("can't register server");
|
||||
|
||||
let mut ui = UI::new(&xns, sid);
|
||||
|
||||
let mut fd = FlashDrive::new(1445888, 512).expect("cannot create flash drive instance");
|
||||
|
||||
#[cfg(all(
|
||||
any(feature = "precursor", feature = "renode"),
|
||||
feature = "mass-storage"
|
||||
))]
|
||||
let usb = usb_device_xous::UsbHid::new();
|
||||
|
||||
#[cfg(all(
|
||||
any(feature = "precursor", feature = "renode"),
|
||||
feature = "mass-storage"
|
||||
))]
|
||||
let core_before_ms = usb.get_current_core().unwrap();
|
||||
|
||||
let mut usb_setup = false;
|
||||
|
||||
loop {
|
||||
let mut msg = xous::receive_message(sid).unwrap();
|
||||
log::debug!("Got message: {:?}", msg);
|
||||
|
||||
match FromPrimitive::from_usize(msg.body.id()) {
|
||||
Some(TradOp::Redraw) => {
|
||||
log::info!("Got redraw");
|
||||
ui.redraw();
|
||||
}
|
||||
Some(TradOp::Quit) => {
|
||||
log::info!("Quitting application");
|
||||
break;
|
||||
}
|
||||
Some(TradOp::FocusChange) => xous::msg_scalar_unpack!(msg, new_state_code, _, _, _, {
|
||||
let new_state = gam::FocusState::convert_focus_change(new_state_code);
|
||||
match new_state {
|
||||
gam::FocusState::Background => {
|
||||
#[cfg(all(
|
||||
any(feature = "precursor", feature = "renode"),
|
||||
feature = "mass-storage"
|
||||
))]
|
||||
{
|
||||
usb.reset_block_device();
|
||||
usb.switch_to_core(core_before_ms).unwrap();
|
||||
}
|
||||
}
|
||||
gam::FocusState::Foreground => {
|
||||
if usb_setup {
|
||||
continue;
|
||||
}
|
||||
|
||||
#[cfg(all(
|
||||
any(feature = "precursor", feature = "renode"),
|
||||
feature = "mass-storage"
|
||||
))]
|
||||
{
|
||||
usb.set_block_device(
|
||||
TradOp::Read.to_usize().unwrap(),
|
||||
TradOp::Write.to_usize().unwrap(),
|
||||
TradOp::MaxLba.to_usize().unwrap(),
|
||||
);
|
||||
|
||||
usb.set_block_device_sid(sid.clone());
|
||||
|
||||
usb.switch_to_core(usb_device_xous::UsbDeviceType::MassStorage)
|
||||
.unwrap();
|
||||
}
|
||||
usb_setup = true;
|
||||
}
|
||||
}
|
||||
}),
|
||||
Some(TradOp::Read) => {
|
||||
let body = msg
|
||||
.body
|
||||
.memory_message_mut()
|
||||
.expect("incorrect message type received");
|
||||
let lba = body.offset.map(|v| v.get()).unwrap_or_default();
|
||||
let data = body.buf.as_slice_mut::<u8>();
|
||||
|
||||
fd.read(lba, data);
|
||||
}
|
||||
Some(TradOp::Write) => {
|
||||
let body = msg
|
||||
.body
|
||||
.memory_message_mut()
|
||||
.expect("incorrect message type received");
|
||||
let lba = body.offset.map(|v| v.get()).unwrap_or_default();
|
||||
let data = body.buf.as_slice_mut::<u8>();
|
||||
|
||||
fd.write(lba, data);
|
||||
}
|
||||
Some(TradOp::MaxLba) => {
|
||||
xous::return_scalar(msg.sender, fd.max_lba() as usize).unwrap();
|
||||
}
|
||||
_ => {
|
||||
log::error!("Got unknown message");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log::info!("Quitting");
|
||||
xous::terminate_process(0)
|
||||
}
|
||||
Loading…
Reference in a new issue