Skip to content

Commit

Permalink
Add speaker device (#693)
Browse files Browse the repository at this point in the history
* Add speaker device for the beep command

* Use u16 for PIT_DIVIDER

* Use string instead of float

* Move beep program to userspace
  • Loading branch information
vinc authored Oct 24, 2024
1 parent 9584f8a commit 08f7de9
Show file tree
Hide file tree
Showing 12 changed files with 213 additions and 33 deletions.
Binary file added dsk/bin/beep
Binary file not shown.
1 change: 1 addition & 0 deletions dsk/var/pkg/beep
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/bin/beep
/tmp/beep/tetris.sh
/tmp/beep/starwars.sh
/tmp/beep/mario.sh
1 change: 1 addition & 0 deletions src/api/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ fn device_type(name: &str) -> Result<DeviceType, ()> {
"vga-font" => Ok(DeviceType::VgaFont),
"vga-mode" => Ok(DeviceType::VgaMode),
"vga-palette" => Ok(DeviceType::VgaPalette),
"speaker" => Ok(DeviceType::Speaker),
"ata" => Ok(DeviceType::Drive),
_ => Err(()),
}
Expand Down
107 changes: 107 additions & 0 deletions src/bin/beep.rs
Original file line number Diff line number Diff line change
@@ -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 {}<options>{1}",
csi_title, csi_reset, csi_option
);
println!();
println!("{}Options:{}", csi_title, csi_reset);
println!(
" {0}-f{1}, {0}--freq <hertz>{1} Tone frequency",
csi_option, csi_reset
);
println!(
" {0}-l{1}, {0}--len <milliseconds>{1} Tone length",
csi_option, csi_reset
);
}
8 changes: 4 additions & 4 deletions src/sys/clk/timer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand Down
9 changes: 9 additions & 0 deletions src/sys/fs/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -33,6 +34,7 @@ pub enum DeviceType {
VgaFont = 11,
VgaMode = 12,
VgaPalette = 13,
Speaker = 14,
}

impl TryFrom<&[u8]> for DeviceType {
Expand All @@ -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(()),
}
}
Expand Down Expand Up @@ -98,6 +101,7 @@ pub enum Device {
VgaFont(VgaFont),
VgaMode(VgaMode),
VgaPalette(VgaPalette),
Speaker(Speaker),
Drive(Drive),
}

Expand All @@ -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];
Expand Down Expand Up @@ -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),
}
}
Expand All @@ -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),
}
}
Expand All @@ -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(),
}
}
Expand All @@ -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),
}
}
Expand Down
1 change: 1 addition & 0 deletions src/sys/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
66 changes: 66 additions & 0 deletions src/sys/speaker.rs
Original file line number Diff line number Diff line change
@@ -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<usize, ()> {
Err(())
}

fn write(&mut self, buf: &[u8]) -> Result<usize, ()> {
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<u8> = 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<u8> = Port::new(SPEAKER_PORT);
let tmp = unsafe { speaker.read() } & 0xFC;
unsafe { speaker.write(tmp) };
}
42 changes: 18 additions & 24 deletions src/usr/beep.rs
Original file line number Diff line number Diff line change
@@ -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<u8> = 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<u8> = 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> {
Expand Down Expand Up @@ -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> {
Expand Down
5 changes: 3 additions & 2 deletions src/usr/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
4 changes: 2 additions & 2 deletions src/usr/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pub mod beep;
// pub mod beep; // TODO: Remove file
pub mod calc;
pub mod chess;
pub mod copy;
Expand All @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion src/usr/shell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down

0 comments on commit 08f7de9

Please sign in to comment.