Add beep sound upon pass or failure, based on PIT channel 2.

Changes by Lionel Debroux:
* disable sound output by default, and make it possible to enable it, but do intentionally not document the feature;
* improve commit message;
* fix data types;
* replace static const variables by #defines - it's a bit early to use C23 constexpr;
* make the coding style consistent with the rest of the memtest86+ code base.
This commit is contained in:
demiurg-spb 2024-05-15 16:40:37 +03:00 committed by Lionel Debroux
parent 9053696e12
commit 8244b5afde
8 changed files with 185 additions and 0 deletions

View File

@ -99,6 +99,7 @@ bool enable_sm = true;
bool enable_bench = true;
bool enable_mch_read = true;
bool enable_numa = false;
bool enable_sound = false;
bool enable_ecc_polling = false;
@ -262,6 +263,8 @@ static void parse_option(const char *option, const char *params)
} else if (strncmp(params, "high", 5) == 0) {
power_save = POWER_SAVE_HIGH;
}
} else if (strncmp(option, "sound", 6) == 0) {
enable_sound = true;
} else if (strncmp(option, "trace", 6) == 0) {
enable_trace = true;
} else if (strncmp(option, "usbdebug", 9) == 0) {

View File

@ -63,6 +63,7 @@ extern bool enable_bench;
extern bool enable_mch_read;
extern bool enable_ecc_polling;
extern bool enable_numa;
extern bool enable_sound;
extern bool pause_at_start;

View File

@ -28,6 +28,8 @@
#include "tests.h"
#include "sound.h"
#include "display.h"
//------------------------------------------------------------------------------
@ -397,6 +399,10 @@ void display_big_status(bool pass)
return;
}
if (enable_sound) {
sound_beep(pass);
}
save_screen_region(POP_STATUS_REGION, popup_status_save_buffer);
set_background_colour(BLACK);
@ -576,6 +582,9 @@ void do_tick(int my_cpu)
if (update_spinner) {
spin_idx = (spin_idx + 1) % NUM_SPIN_STATES;
display_spinner(spin_state[spin_idx]);
if (enable_sound) {
sound_tick_task();
}
}
// This only tick one time per second

47
app/sound.c Normal file
View File

@ -0,0 +1,47 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (C) 2024 Anton Ivanov (aka demiurg_spb+rigler).
// Copyright (C) 2024 Lionel Debroux.
#include <stdbool.h>
#include "pit.h"
#include "sound.h" // self to check prototypes
static bool enabled;
static unsigned int duration;
#define DURATION (7)
static void beep_off()
{
if (enabled) {
enabled = 0;
duration = 0;
pit_off();
}
}
static void beep_on(unsigned f_hz)
{
if (!enabled) {
enabled = 1;
duration = DURATION;
pit_init_square_wave_generator(2, f_hz); // pit-ch2 is connected to buzzer
}
}
void sound_beep(bool ok)
{
#define beep_freq_ok (1100)
#define beep_freq_er ( 380)
beep_on(ok ? beep_freq_ok : beep_freq_er);
}
void sound_tick_task(void)
{
if (duration) {
duration--;
}
else {
beep_off();
}
}

18
app/sound.h Normal file
View File

@ -0,0 +1,18 @@
// SPDX-License-Identifier: GPL-2.0
#ifndef SOUND_H
#define SOUND_H
/**
* \file
*
* Provides types and variables used when performing the memory tests.
*
* Copyright (C) 2024 Anton Ivanov (aka demiurg_spb+rigler).
*/
#include <stdbool.h>
void sound_beep(bool ok);
void sound_tick_task(void);
#endif // SOUND_H

View File

@ -78,6 +78,7 @@ TST_OBJS = tests/addr_walk1.o \
APP_OBJS = app/badram.o \
app/config.o \
app/sound.o \
app/display.o \
app/error.o \
app/interrupt.o \

View File

@ -77,6 +77,7 @@ TST_OBJS = tests/addr_walk1.o \
APP_OBJS = app/badram.o \
app/config.o \
app/sound.o \
app/display.o \
app/error.o \
app/interrupt.o \

105
system/pit.h Normal file
View File

@ -0,0 +1,105 @@
// SPDX-License-Identifier: GPL-2.0
#ifndef PIT_H
#define PIT_H
/**
* \file
*
* Provides types and variables used to work with PIT
*
* This will work on real hardware and in QEMU if it started with -audiodev pa,id=speaker -machine pcspk-audiodev=speaker.
* The code also changes the PIT timer 2 frequency, so you will have to reset that when you're done "beep"ing :)
*
* hints:
* https://wiki.osdev.org/PC_Speaker
* https://wiki.osdev.org/Programmable_Interval_Timer
* https://stackoverflow.com/questions/8960620/low-level-i-o-access-using-outb-and-inb
* https://stackoverflow.com/questions/14194798/is-there-a-specification-of-x86-i-o-port-assignment
* https://stackoverflow.com/questions/22355436/how-to-compile-32-bit-apps-on-64-bit-ubuntu
*
* outb() and friends are hardware-specific. The value argument is
* passed first and the port argument is passed second, which is the
* opposite order from most DOS implementations.
*
* void outb(unsigned char value, unsigned short port);
*
* Copyright (C) 2024 Anton Ivanov (aka demiurg_spb+rigler).
* Copyright (C) 2024 Lionel Debroux.
*/
#include <stdint.h>
#include <sys/io.h> // if file not found for 32bit-target in 64bit OS: "sudo apt install gcc-multilib g++-multilib"
#define PIT_CH0_PORT (0x0040U) // r/w
#define PIT_CH1_PORT (0x0041U) // r/w
#define PIT_CH2_PORT (0x0042U) // r/w
#define PIT_CTL_PORT (0x0043U) // write only
#define PIT_STATUS_PORT (0x0061U)
#define PIT_STATUS_MASK (0b00000011)
#define _PIT_CTL_CH(x) (((x)&3U)<<6) // 0-ch0, 1-ch1, 2-ch2, 3-Read-back command (8254 only)
#define PIT_CTL_CH0 _PIT_CTL_CH(0)
#define PIT_CTL_CH1 _PIT_CTL_CH(1)
#define PIT_CTL_CH2 _PIT_CTL_CH(2)
#define PIT_CTL_READ_BACK _PIT_CTL_CH(3)
#define _PIT_CTL_ACCESS(x) (((x)&3U)<<4) // 0-Latch count value command, 1-lbyte, 2-hbyte, 3-lobyte/hibyte
#define PIT_CTL_ACCESS_LATCH_CNT_VAL _PIT_CTL_ACCESS(0)
#define PIT_CTL_ACCESS_LBYTE _PIT_CTL_ACCESS(1)
#define PIT_CTL_ACCESS_HBYTE _PIT_CTL_ACCESS(2)
#define PIT_CTL_ACCESS_LBYTE_HBYTE _PIT_CTL_ACCESS(3)
#define _PIT_CTL_OP_MODE(x) (((x)&7U)<<1)
#define PIT_CTL_OP_MODE0 _PIT_CTL_OP_MODE(0) // (interrupt on terminal count)
#define PIT_CTL_OP_MODE1 _PIT_CTL_OP_MODE(1) // (hardware re-triggerable one-shot)
#define PIT_CTL_OP_MODE2 _PIT_CTL_OP_MODE(2) // (rate generator)
#define PIT_CTL_OP_MODE3 _PIT_CTL_OP_MODE(3) // (square wave generator)
#define PIT_CTL_OP_MODE4 _PIT_CTL_OP_MODE(4) // (software triggered strobe)
#define PIT_CTL_OP_MODE5 _PIT_CTL_OP_MODE(5) // (hardware triggered strobe)
#define _PIT_CTL_BCD_MODE(x) ((x)&1U)
#define PIT_CTL_BIN16_MODE _PIT_CTL_BCD_MODE(0) // 16-bit binary
#define PIT_CTL_BCD4_MODE _PIT_CTL_BCD_MODE(1) // four-digit BCD
static inline uint32_t pit_freq2div(unsigned frequency)
{
return (uint32_t)(0.5f + 1193180.0f/frequency);
}
static inline void pit_off(void)
{
const unsigned char x = inb(PIT_STATUS_PORT) & ~PIT_STATUS_MASK;
outb(x, PIT_STATUS_PORT);
}
static inline void pit_init_square_wave_generator(unsigned char ch, uint32_t frequency)
{
const uint32_t clk_div = pit_freq2div(frequency);
const unsigned char cfg = PIT_CTL_OP_MODE3 | PIT_CTL_BIN16_MODE | PIT_CTL_ACCESS_LBYTE_HBYTE | _PIT_CTL_CH(ch);
const unsigned short PIT_DATA_PORT = PIT_CH0_PORT + ch; // base + offset
outb(cfg, PIT_CTL_PORT); // 0xb6 = 0b10110110
outb((uint8_t)clk_div, PIT_DATA_PORT); // low byte
outb((uint8_t)(clk_div>>8), PIT_DATA_PORT); // hi byte
unsigned char x = inb(PIT_STATUS_PORT);
if (!(x&PIT_STATUS_MASK)) {
outb(x|PIT_STATUS_MASK, PIT_STATUS_PORT);
}
}
#endif // PIT_H