diff --git a/dsk/bin/beep b/dsk/bin/beep new file mode 100644 index 00000000..94912d27 Binary files /dev/null and b/dsk/bin/beep differ diff --git a/dsk/var/pkg/beep b/dsk/var/pkg/beep index ff91e59b..4d90df88 100644 --- a/dsk/var/pkg/beep +++ b/dsk/var/pkg/beep @@ -1,3 +1,4 @@ +/bin/beep /tmp/beep/tetris.sh /tmp/beep/starwars.sh /tmp/beep/mario.sh diff --git a/src/api/fs.rs b/src/api/fs.rs index 3cf255a8..e67e3fe1 100644 --- a/src/api/fs.rs +++ b/src/api/fs.rs @@ -170,6 +170,7 @@ fn device_type(name: &str) -> Result { "vga-font" => Ok(DeviceType::VgaFont), "vga-mode" => Ok(DeviceType::VgaMode), "vga-palette" => Ok(DeviceType::VgaPalette), + "speaker" => Ok(DeviceType::Speaker), "ata" => Ok(DeviceType::Drive), _ => Err(()), } diff --git a/src/bin/beep.rs b/src/bin/beep.rs new file mode 100644 index 00000000..362ebc64 --- /dev/null +++ b/src/bin/beep.rs @@ -0,0 +1,107 @@ +#![no_std] +#![no_main] + +extern crate alloc; + +use moros::entry_point; +use moros::{error, eprintln, eprint, println, print}; + +use moros::api::console::Style; +use moros::api::process::ExitCode; +use moros::api::fs; +use moros::api::syscall; + +use alloc::string::ToString; + +entry_point!(main); + +const SPEAKER: &'static str = "/dev/speaker"; + +fn start_sound(freq: f64) -> Result<(), ()> { + let buf = freq.to_string(); + if !fs::is_device(SPEAKER) || fs::write(SPEAKER, buf.as_bytes()).is_err() { + error!("Could not write to '{}'", SPEAKER); + Err(()) + } else { + Ok(()) + } +} + +fn stop_sound() -> Result<(), ()> { + start_sound(0.0) +} + +fn beep(freq: f64, len: f64) -> Result<(), ()> { + start_sound(freq)?; + syscall::sleep(len); + stop_sound() +} + +pub fn main(args: &[&str]) { + let mut freq = 440.0; + let mut len = 200.0; + let mut i = 1; + let n = args.len(); + while i < n { + match args[i] { + "-h" | "--help" => { + help(); + return; + } + "-f" | "--freq" => { + if i + 1 < n { + i += 1; + if let Ok(value) = args[i].parse() { + freq = value; + } else { + error!("Could not parse freq"); + syscall::exit(ExitCode::Failure); + } + } else { + error!("Missing freq"); + syscall::exit(ExitCode::UsageError); + } + } + "-l" | "--len" => { + if i + 1 < n { + i += 1; + if let Ok(value) = args[i].parse() { + len = value; + } else { + error!("Could not parse len"); + syscall::exit(ExitCode::Failure); + } + } else { + error!("Missing len"); + syscall::exit(ExitCode::UsageError); + } + } + _ => {} + } + i += 1; + } + + if beep(freq, len / 1000.0).is_err() { + syscall::exit(ExitCode::Failure); + } +} + +fn help() { + let csi_option = Style::color("aqua"); + let csi_title = Style::color("yellow"); + let csi_reset = Style::reset(); + println!( + "{}Usage:{} beep {}{1}", + csi_title, csi_reset, csi_option + ); + println!(); + println!("{}Options:{}", csi_title, csi_reset); + println!( + " {0}-f{1}, {0}--freq {1} Tone frequency", + csi_option, csi_reset + ); + println!( + " {0}-l{1}, {0}--len {1} Tone length", + csi_option, csi_reset + ); +} diff --git a/src/sys/clk/timer.rs b/src/sys/clk/timer.rs index 5381cb3a..d5a63dbb 100644 --- a/src/sys/clk/timer.rs +++ b/src/sys/clk/timer.rs @@ -12,7 +12,7 @@ use x86_64::instructions::port::Port; // During init we will change the divider to 1193 to have about 1.000 ms // between ticks to improve time measurements accuracy. const PIT_FREQUENCY: f64 = 3_579_545.0 / 3.0; // 1_193_181.666 Hz -const PIT_DIVIDER: usize = 1193; +const PIT_DIVIDER: u16 = 1193; const PIT_INTERVAL: f64 = (PIT_DIVIDER as f64) / PIT_FREQUENCY; static PIT_TICKS: AtomicUsize = AtomicUsize::new(0); @@ -74,9 +74,9 @@ pub fn rtc_interrupt_handler() { pub fn init() { // PIT timmer - let divider = if PIT_DIVIDER < 65536 { PIT_DIVIDER } else { 0 }; - let channel = 0; - set_pit_frequency(divider as u16, channel); + let divider = PIT_DIVIDER; + let channel = 0; // PIC + set_pit_frequency(divider, channel); sys::idt::set_irq_handler(0, pit_interrupt_handler); // RTC timmer diff --git a/src/sys/fs/device.rs b/src/sys/fs/device.rs index 888a7607..876eeeab 100644 --- a/src/sys/fs/device.rs +++ b/src/sys/fs/device.rs @@ -9,6 +9,7 @@ use crate::sys::console::Console; use crate::sys::net::socket::tcp::TcpSocket; use crate::sys::net::socket::udp::UdpSocket; use crate::sys::rng::Random; +use crate::sys::speaker::Speaker; use crate::sys::vga::{VgaFont, VgaMode, VgaPalette, VgaBuffer}; use alloc::vec; @@ -33,6 +34,7 @@ pub enum DeviceType { VgaFont = 11, VgaMode = 12, VgaPalette = 13, + Speaker = 14, } impl TryFrom<&[u8]> for DeviceType { @@ -54,6 +56,7 @@ impl TryFrom<&[u8]> for DeviceType { 11 => Ok(DeviceType::VgaFont), 12 => Ok(DeviceType::VgaMode), 13 => Ok(DeviceType::VgaPalette), + 14 => Ok(DeviceType::Speaker), _ => Err(()), } } @@ -98,6 +101,7 @@ pub enum Device { VgaFont(VgaFont), VgaMode(VgaMode), VgaPalette(VgaPalette), + Speaker(Speaker), Drive(Drive), } @@ -119,6 +123,7 @@ impl TryFrom<&[u8]> for Device { DeviceType::VgaFont => Ok(Device::VgaFont(VgaFont::new())), DeviceType::VgaMode => Ok(Device::VgaMode(VgaMode::new())), DeviceType::VgaPalette => Ok(Device::VgaPalette(VgaPalette::new())), + DeviceType::Speaker => Ok(Device::Speaker(Speaker::new())), DeviceType::Drive if buf.len() > 2 => { let bus = buf[1]; let dsk = buf[2]; @@ -181,6 +186,7 @@ impl FileIO for Device { Device::VgaFont(io) => io.read(buf), Device::VgaMode(io) => io.read(buf), Device::VgaPalette(io) => io.read(buf), + Device::Speaker(io) => io.read(buf), Device::Drive(io) => io.read(buf), } } @@ -200,6 +206,7 @@ impl FileIO for Device { Device::VgaFont(io) => io.write(buf), Device::VgaMode(io) => io.write(buf), Device::VgaPalette(io) => io.write(buf), + Device::Speaker(io) => io.write(buf), Device::Drive(io) => io.write(buf), } } @@ -219,6 +226,7 @@ impl FileIO for Device { Device::VgaFont(io) => io.close(), Device::VgaMode(io) => io.close(), Device::VgaPalette(io) => io.close(), + Device::Speaker(io) => io.close(), Device::Drive(io) => io.close(), } } @@ -238,6 +246,7 @@ impl FileIO for Device { Device::VgaFont(io) => io.poll(event), Device::VgaMode(io) => io.poll(event), Device::VgaPalette(io) => io.poll(event), + Device::Speaker(io) => io.poll(event), Device::Drive(io) => io.poll(event), } } diff --git a/src/sys/mod.rs b/src/sys/mod.rs index 233c9638..a00aa5b5 100644 --- a/src/sys/mod.rs +++ b/src/sys/mod.rs @@ -54,5 +54,6 @@ pub mod pic; pub mod process; pub mod rng; pub mod serial; +pub mod speaker; pub mod syscall; pub mod vga; diff --git a/src/sys/speaker.rs b/src/sys/speaker.rs new file mode 100644 index 00000000..73337dce --- /dev/null +++ b/src/sys/speaker.rs @@ -0,0 +1,66 @@ +use super::clk; + +use crate::api::fs::{FileIO, IO}; + +use alloc::string::String; +use x86_64::instructions::port::Port; + +#[derive(Debug, Clone)] +pub struct Speaker; + +impl Speaker { + pub fn new() -> Self { + Self {} + } +} + +impl FileIO for Speaker { + fn read(&mut self, _buf: &mut [u8]) -> Result { + Err(()) + } + + fn write(&mut self, buf: &[u8]) -> Result { + if let Ok(s) = String::from_utf8(buf.to_vec()) { + if let Ok(n) = s.parse() { + if n > 0.0 { + start_sound(n); + } else { + stop_sound(); + } + } + return Ok(8); + } + Err(()) + } + + fn close(&mut self) {} + + fn poll(&mut self, event: IO) -> bool { + match event { + IO::Read => false, + IO::Write => true, + } + } +} + +// See: https://wiki.osdev.org/PC_Speaker + +const SPEAKER_PORT: u16 = 0x61; + +fn start_sound(freq: f64) { + let divider = (clk::pit_frequency() / freq) as u16; + let channel = 2; // PC Speaker + clk::set_pit_frequency(divider, channel); + + let mut speaker: Port = Port::new(SPEAKER_PORT); + let tmp = unsafe { speaker.read() }; + if tmp != (tmp | 3) { + unsafe { speaker.write(tmp | 3) }; + } +} + +fn stop_sound() { + let mut speaker: Port = Port::new(SPEAKER_PORT); + let tmp = unsafe { speaker.read() } & 0xFC; + unsafe { speaker.write(tmp) }; +} diff --git a/src/usr/beep.rs b/src/usr/beep.rs index fd049d87..d15f3f98 100644 --- a/src/usr/beep.rs +++ b/src/usr/beep.rs @@ -1,35 +1,30 @@ use crate::api::console::Style; use crate::api::process::ExitCode; -use crate::{api, sys}; +use crate::api::fs; +use crate::api::syscall; -use x86_64::instructions::port::Port; +use alloc::string::ToString; -// See: https://wiki.osdev.org/PC_Speaker +const SPEAKER: &'static str = "/dev/speaker"; -const SPEAKER_PORT: u16 = 0x61; - -fn start_sound(freq: f64) { - let divider = (sys::clk::pit_frequency() / freq) as u16; - let channel = 2; - sys::clk::set_pit_frequency(divider, channel); - - let mut speaker: Port = Port::new(SPEAKER_PORT); - let tmp = unsafe { speaker.read() }; - if tmp != (tmp | 3) { - unsafe { speaker.write(tmp | 3) }; +fn start_sound(freq: f64) -> Result<(), ExitCode> { + let buf = freq.to_string(); + if !fs::is_device(SPEAKER) || fs::write(SPEAKER, buf.as_bytes()).is_err() { + error!("Could not write to '{}'", SPEAKER); + Err(ExitCode::Failure) + } else { + Ok(()) } } -fn stop_sound() { - let mut speaker: Port = Port::new(SPEAKER_PORT); - let tmp = unsafe { speaker.read() } & 0xFC; - unsafe { speaker.write(tmp) }; +fn stop_sound() -> Result<(), ExitCode> { + start_sound(0.0) } -fn beep(freq: f64, len: f64) { - start_sound(freq); - api::syscall::sleep(len); - stop_sound(); +fn beep(freq: f64, len: f64) -> Result<(), ExitCode> { + start_sound(freq)?; + syscall::sleep(len); + stop_sound() } pub fn main(args: &[&str]) -> Result<(), ExitCode> { @@ -75,8 +70,7 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> { i += 1; } - beep(freq, len / 1000.0); - Ok(()) + beep(freq, len / 1000.0) } fn help() -> Result<(), ExitCode> { diff --git a/src/usr/install.rs b/src/usr/install.rs index d199f05e..30d60f32 100644 --- a/src/usr/install.rs +++ b/src/usr/install.rs @@ -57,11 +57,12 @@ pub fn copy_files(verbose: bool) { create_dev("/dev/clk/boot", "clk-boot", verbose); create_dev("/dev/clk/epoch", "clk-epoch", verbose); create_dev("/dev/clk/rtc", "clk-rtc", verbose); - create_dev("/dev/null", "null", verbose); - create_dev("/dev/random", "random", verbose); create_dev("/dev/console", "console", verbose); create_dev("/dev/net/tcp", "tcp", verbose); create_dev("/dev/net/udp", "udp", verbose); + create_dev("/dev/null", "null", verbose); + create_dev("/dev/random", "random", verbose); + create_dev("/dev/speaker", "speaker", verbose); create_dev("/dev/vga/buffer", "vga-buffer", verbose); create_dev("/dev/vga/font", "vga-font", verbose); create_dev("/dev/vga/mode", "vga-mode", verbose); diff --git a/src/usr/mod.rs b/src/usr/mod.rs index cba745ec..26310173 100644 --- a/src/usr/mod.rs +++ b/src/usr/mod.rs @@ -1,4 +1,4 @@ -pub mod beep; +// pub mod beep; // TODO: Remove file pub mod calc; pub mod chess; pub mod copy; @@ -13,7 +13,7 @@ pub mod elf; pub mod encode; pub mod env; pub mod find; -//pub mod geodate; +//pub mod geodate; // TODO: Remove file pub mod hash; pub mod help; pub mod hex; diff --git a/src/usr/shell.rs b/src/usr/shell.rs index 9bf27023..a64e9745 100644 --- a/src/usr/shell.rs +++ b/src/usr/shell.rs @@ -516,7 +516,7 @@ fn dispatch(args: &[&str], config: &mut Config) -> Result<(), ExitCode> { "" => Ok(()), "2048" => usr::pow::main(args), "alias" => cmd_alias(args, config), - "beep" => usr::beep::main(args), + //"beep" => usr::beep::main(args), "calc" => usr::calc::main(args), "chess" => usr::chess::main(args), "copy" => usr::copy::main(args),