Skip to content

Commit 5549b91

Browse files
jonathanpallantthejpster
authored andcommitted
Support reading/saving config.
1 parent 92d4f71 commit 5549b91

File tree

2 files changed

+138
-33
lines changed

2 files changed

+138
-33
lines changed

src/config.rs

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ use serde::{Deserialize, Serialize};
88
/// Represents our configuration information that we ask the BIOS to serialise
99
#[derive(Debug, Serialize, Deserialize)]
1010
pub struct Config {
11-
vga_console_on: bool,
12-
serial_console_on: bool,
11+
vga_console: bool,
12+
serial_console: bool,
1313
serial_baud: u32,
1414
}
1515

@@ -29,18 +29,26 @@ impl Config {
2929
let api = API.get();
3030
let mut buffer = [0u8; 64];
3131
let slice = postcard::to_slice(self, &mut buffer).map_err(|_e| "Failed to parse config")?;
32-
(api.configuration_set)(bios::ApiByteSlice::new(slice));
33-
Ok(())
32+
match (api.configuration_set)(bios::ApiByteSlice::new(slice)) {
33+
bios::Result::Ok(_) => Ok(()),
34+
bios::Result::Err(bios::Error::Unimplemented) => Err("BIOS doesn't support this (yet)"),
35+
bios::Result::Err(_) => Err("BIOS reported an error"),
36+
}
3437
}
3538

3639
/// Should this system use the VGA console?
37-
pub fn has_vga_console(&self) -> bool {
38-
self.vga_console_on
40+
pub fn get_vga_console(&self) -> bool {
41+
self.vga_console
42+
}
43+
44+
// Set whether this system should use the VGA console.
45+
pub fn set_vga_console(&mut self, new_value: bool) {
46+
self.vga_console = new_value;
3947
}
4048

4149
/// Should this system use the UART console?
42-
pub fn has_serial_console(&self) -> Option<(u8, bios::serial::Config)> {
43-
if self.serial_console_on {
50+
pub fn get_serial_console(&self) -> Option<(u8, bios::serial::Config)> {
51+
if self.serial_console {
4452
Some((
4553
0,
4654
bios::serial::Config {
@@ -55,13 +63,25 @@ impl Config {
5563
None
5664
}
5765
}
66+
67+
/// Turn the serial console off
68+
pub fn set_serial_console_off(&mut self) {
69+
self.serial_console = false;
70+
self.serial_baud = 0;
71+
}
72+
73+
/// Turn the serial console on
74+
pub fn set_serial_console_on(&mut self, serial_baud: u32) {
75+
self.serial_console = true;
76+
self.serial_baud = serial_baud;
77+
}
5878
}
5979

6080
impl core::default::Default for Config {
6181
fn default() -> Config {
6282
Config {
63-
vga_console_on: true,
64-
serial_console_on: false,
83+
vga_console: true,
84+
serial_console: false,
6585
serial_baud: 115200,
6686
}
6787
}

src/lib.rs

Lines changed: 108 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
// Imports
1212
use core::fmt::Write;
13+
use core::sync::atomic::{AtomicBool, Ordering};
1314
use neotron_common_bios as bios;
1415

1516
mod config;
@@ -31,6 +32,11 @@ static mut VGA_CONSOLE: Option<vgaconsole::VgaConsole> = None;
3132
/// We store our VGA console here.
3233
static mut SERIAL_CONSOLE: Option<SerialConsole> = None;
3334

35+
/// Note if we are panicking right now.
36+
///
37+
/// If so, don't panic if a serial write fails.
38+
static IS_PANIC: AtomicBool = AtomicBool::new(false);
39+
3440
static OS_MENU: menu::Menu<Ctx> = menu::Menu {
3541
label: "root",
3642
items: &[
@@ -58,6 +64,23 @@ static OS_MENU: menu::Menu<Ctx> = menu::Menu {
5864
command: "fill",
5965
help: Some("Fill the screen with characters"),
6066
},
67+
&menu::Item {
68+
item_type: menu::ItemType::Callback {
69+
function: cmd_config,
70+
parameters: &[
71+
menu::Parameter::Optional {
72+
parameter_name: "command",
73+
help: Some("Which operation to perform (try help)"),
74+
},
75+
menu::Parameter::Optional {
76+
parameter_name: "value",
77+
help: Some("new value for the setting"),
78+
},
79+
],
80+
},
81+
command: "config",
82+
help: Some("Handle non-volatile OS configuration"),
83+
},
6184
],
6285
entry: None,
6386
exit: None,
@@ -121,20 +144,25 @@ struct SerialConsole(u8);
121144
impl core::fmt::Write for SerialConsole {
122145
fn write_str(&mut self, data: &str) -> core::fmt::Result {
123146
let api = API.get();
124-
(api.serial_write)(
147+
let is_panic = IS_PANIC.load(Ordering::SeqCst);
148+
let res = (api.serial_write)(
125149
// Which port
126150
self.0,
127151
// Data
128152
bios::ApiByteSlice::new(data.as_bytes()),
129153
// No timeout
130154
bios::Option::None,
131-
)
132-
.unwrap();
155+
);
156+
if !is_panic {
157+
res.unwrap();
158+
}
133159
Ok(())
134160
}
135161
}
136162

137-
struct Ctx;
163+
struct Ctx {
164+
config: config::Config,
165+
}
138166

139167
impl core::fmt::Write for Ctx {
140168
fn write_str(&mut self, data: &str) -> core::fmt::Result {
@@ -191,7 +219,7 @@ pub extern "C" fn main(api: *const bios::Api) -> ! {
191219

192220
let config = config::Config::load().unwrap_or_default();
193221

194-
if config.has_vga_console() {
222+
if config.get_vga_console() {
195223
// Try and set 80x30 mode for maximum compatibility
196224
(api.video_set_mode)(bios::video::Mode::new(
197225
bios::video::Timing::T640x480,
@@ -215,7 +243,7 @@ pub extern "C" fn main(api: *const bios::Api) -> ! {
215243
}
216244
}
217245

218-
if let Some((idx, serial_config)) = config.has_serial_console() {
246+
if let Some((idx, serial_config)) = config.get_serial_console() {
219247
let _ignored = (api.serial_configure)(idx, serial_config);
220248
unsafe { SERIAL_CONSOLE = Some(SerialConsole(idx)) };
221249
println!("Configured Serial console on Serial {}", idx);
@@ -225,29 +253,14 @@ pub extern "C" fn main(api: *const bios::Api) -> ! {
225253
println!("Welcome to {}!", OS_VERSION);
226254
println!("Copyright © Jonathan 'theJPster' Pallant and the Neotron Developers, 2022");
227255

228-
let mut found;
229-
230-
println!("Serial Ports:");
231-
found = false;
232-
for device_idx in 0..=255 {
233-
if let bios::Option::Some(serial) = (api.serial_get_info)(device_idx) {
234-
println!("Serial Device {}: {:?}", device_idx, serial);
235-
found = true;
236-
} else {
237-
// Ran out of serial devices (we assume they are consecutive)
238-
break;
239-
}
240-
}
241-
if !found {
242-
println!("None.");
243-
}
256+
let ctx = Ctx { config };
244257

245258
let mut keyboard = pc_keyboard::EventDecoder::new(
246259
pc_keyboard::layouts::AnyLayout::Uk105Key(pc_keyboard::layouts::Uk105Key),
247260
pc_keyboard::HandleControl::MapLettersToUnicode,
248261
);
249262
let mut buffer = [0u8; 256];
250-
let mut menu = menu::Runner::new(&OS_MENU, &mut buffer, Ctx);
263+
let mut menu = menu::Runner::new(&OS_MENU, &mut buffer, ctx);
251264

252265
loop {
253266
match (api.hid_get_event)() {
@@ -343,10 +356,82 @@ fn cmd_fill(_menu: &menu::Menu<Ctx>, _item: &menu::Item<Ctx>, _args: &[&str], _c
343356
}
344357
}
345358

359+
/// Called when the "config" command is executed.
360+
fn cmd_config(_menu: &menu::Menu<Ctx>, _item: &menu::Item<Ctx>, args: &[&str], context: &mut Ctx) {
361+
let command = args.get(0).cloned().unwrap_or("print");
362+
match command {
363+
"reset" => match config::Config::load() {
364+
Ok(new_config) => {
365+
context.config = new_config;
366+
println!("Loaded OK.");
367+
}
368+
Err(e) => {
369+
println!("Error loading; {}", e);
370+
}
371+
},
372+
"save" => match context.config.save() {
373+
Ok(_) => {
374+
println!("Saved OK.");
375+
}
376+
Err(e) => {
377+
println!("Error saving: {}", e);
378+
}
379+
},
380+
"vga" => match args.get(1).cloned() {
381+
Some("on") => {
382+
context.config.set_vga_console(true);
383+
println!("VGA now on");
384+
}
385+
Some("off") => {
386+
context.config.set_vga_console(false);
387+
println!("VGA now off");
388+
}
389+
_ => {
390+
println!("Give on or off as argument");
391+
}
392+
},
393+
"serial" => match (args.get(1).cloned(), args.get(1).map(|s| s.parse::<u32>())) {
394+
(_, Some(Ok(baud))) => {
395+
println!("Turning serial console on at {} bps", baud);
396+
context.config.set_serial_console_on(baud);
397+
}
398+
(Some("off"), _) => {
399+
println!("Turning serial console off");
400+
context.config.set_serial_console_off();
401+
}
402+
_ => {
403+
println!("Give off or an integer as argument");
404+
}
405+
},
406+
"print" => {
407+
println!("VGA : {}", context.config.get_vga_console());
408+
match context.config.get_serial_console() {
409+
None => {
410+
println!("Serial: off");
411+
}
412+
Some((_port, config)) => {
413+
println!("Serial: {} bps", config.data_rate_bps);
414+
}
415+
}
416+
}
417+
_ => {
418+
println!("config print - print the config");
419+
println!("config help - print this help text");
420+
println!("config reset - load config from BIOS store");
421+
println!("config save - save config to BIOS store");
422+
println!("config vga on - turn VGA on");
423+
println!("config vga off - turn VGA off");
424+
println!("config serial off - turn serial console off");
425+
println!("config serial <baud> - turn serial console on with given baud rate");
426+
}
427+
}
428+
}
429+
346430
/// Called when we have a panic.
347431
#[inline(never)]
348432
#[panic_handler]
349433
fn panic(info: &core::panic::PanicInfo) -> ! {
434+
IS_PANIC.store(true, Ordering::SeqCst);
350435
println!("PANIC!\n{:#?}", info);
351436
let api = API.get();
352437
loop {

0 commit comments

Comments
 (0)