diff --git a/CMakeLists.txt b/CMakeLists.txt index 6cf33a7..caf0f6a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -362,7 +362,7 @@ set(RAMDISK_ROOT_APPS) set(USER_SHELL_COMMAND_APPS help ls cat pwd cd exec pid spawn wait sleep yield - shutdown restart exit clear ansi ansitest fastfetch memstat fsstat taskstat userstat + shutdown restart exit clear ansi ansitest wavplay fastfetch memstat fsstat taskstat userstat shstat stats tty dmesg kbdstat mkdir touch write append cp mv grep rm ) diff --git a/cleonos/c/apps/help_main.c b/cleonos/c/apps/help_main.c index 4df9b2c..b314777 100644 --- a/cleonos/c/apps/help_main.c +++ b/cleonos/c/apps/help_main.c @@ -10,6 +10,7 @@ static int ush_cmd_help(void) { ush_writeln(" exec|run "); ush_writeln(" clear"); ush_writeln(" ansi / ansitest / color"); + ush_writeln(" wavplay [steps] [ticks] / wavplay --stop"); ush_writeln(" fastfetch [--plain]"); ush_writeln(" memstat / fsstat / taskstat / userstat / shstat / stats"); ush_writeln(" tty [index]"); @@ -78,4 +79,3 @@ int cleonos_app_main(void) { return (success != 0) ? 0 : 1; } - diff --git a/cleonos/c/apps/shell/shell_cmd.c b/cleonos/c/apps/shell/shell_cmd.c index 76be2e9..c19a4b4 100644 --- a/cleonos/c/apps/shell/shell_cmd.c +++ b/cleonos/c/apps/shell/shell_cmd.c @@ -134,6 +134,7 @@ static int ush_cmd_help(void) { ush_writeln(" exec|run "); ush_writeln(" clear"); ush_writeln(" ansi / ansitest / color"); + ush_writeln(" wavplay [steps] [ticks] / wavplay --stop"); ush_writeln(" fastfetch [--plain]"); ush_writeln(" memstat / fsstat / taskstat / userstat / shstat / stats"); ush_writeln(" tty [index]"); diff --git a/cleonos/c/apps/wavplay_main.c b/cleonos/c/apps/wavplay_main.c new file mode 100644 index 0000000..97d605e --- /dev/null +++ b/cleonos/c/apps/wavplay_main.c @@ -0,0 +1,495 @@ +#include "cmd_runtime.h" + +#define USH_WAVPLAY_FILE_MAX 65536ULL +#define USH_WAVPLAY_DEFAULT_STEPS 256ULL +#define USH_WAVPLAY_MAX_STEPS 4096ULL +#define USH_WAVPLAY_DEFAULT_TICKS 1ULL +#define USH_WAVPLAY_MAX_TICKS 64ULL +#define USH_WAVPLAY_RUN_TICK_MAX 512ULL + +typedef struct ush_wav_info { + const unsigned char *data; + u64 data_size; + u64 frame_count; + u64 sample_rate; + u64 channels; + u64 bits_per_sample; + u64 block_align; +} ush_wav_info; + +static unsigned char ush_wavplay_file_buf[USH_WAVPLAY_FILE_MAX + 1ULL]; + +static unsigned int ush_wav_le16(const unsigned char *ptr) { + return (unsigned int)ptr[0] | ((unsigned int)ptr[1] << 8U); +} + +static unsigned int ush_wav_le32(const unsigned char *ptr) { + return (unsigned int)ptr[0] | + ((unsigned int)ptr[1] << 8U) | + ((unsigned int)ptr[2] << 16U) | + ((unsigned int)ptr[3] << 24U); +} + +static int ush_wav_tag_eq(const unsigned char *tag, const char *lit4) { + if (tag == (const unsigned char *)0 || lit4 == (const char *)0) { + return 0; + } + + return (tag[0] == (unsigned char)lit4[0] && + tag[1] == (unsigned char)lit4[1] && + tag[2] == (unsigned char)lit4[2] && + tag[3] == (unsigned char)lit4[3]) + ? 1 + : 0; +} + +static void ush_wavplay_write_u64_dec(u64 value) { + char rev[32]; + u64 len = 0ULL; + + if (value == 0ULL) { + ush_write_char('0'); + return; + } + + while (value > 0ULL && len < (u64)sizeof(rev)) { + rev[len++] = (char)('0' + (value % 10ULL)); + value /= 10ULL; + } + + while (len > 0ULL) { + len--; + ush_write_char(rev[len]); + } +} + +static void ush_wavplay_print_meta(const char *path, const ush_wav_info *info, u64 steps, u64 ticks) { + ush_write("wavplay: "); + ush_writeln(path); + + ush_write(" sample_rate="); + ush_wavplay_write_u64_dec(info->sample_rate); + ush_write("Hz channels="); + ush_wavplay_write_u64_dec(info->channels); + ush_write(" bits="); + ush_wavplay_write_u64_dec(info->bits_per_sample); + ush_write(" frames="); + ush_wavplay_write_u64_dec(info->frame_count); + ush_write_char('\n'); + + ush_write(" play_steps="); + ush_wavplay_write_u64_dec(steps); + ush_write(" ticks_per_step="); + ush_wavplay_write_u64_dec(ticks); + ush_write_char('\n'); +} + +static int ush_wav_parse(const unsigned char *buffer, u64 size, ush_wav_info *out_info) { + u64 offset = 12ULL; + int found_fmt = 0; + int found_data = 0; + u64 sample_rate = 0ULL; + u64 channels = 0ULL; + u64 bits = 0ULL; + u64 block_align = 0ULL; + const unsigned char *data = (const unsigned char *)0; + u64 data_size = 0ULL; + + if (buffer == (const unsigned char *)0 || out_info == (ush_wav_info *)0) { + return 0; + } + + if (size < 12ULL) { + return 0; + } + + if (ush_wav_tag_eq(&buffer[0], "RIFF") == 0 || ush_wav_tag_eq(&buffer[8], "WAVE") == 0) { + return 0; + } + + while (offset + 8ULL <= size) { + const unsigned char *chunk_tag = &buffer[offset]; + u64 chunk_size = (u64)ush_wav_le32(&buffer[offset + 4ULL]); + u64 chunk_data = offset + 8ULL; + u64 next_offset; + + if (chunk_data > size || chunk_size > (size - chunk_data)) { + return 0; + } + + if (ush_wav_tag_eq(chunk_tag, "fmt ") != 0) { + if (chunk_size < 16ULL) { + return 0; + } + + if (ush_wav_le16(&buffer[chunk_data + 0ULL]) != 1U) { + return 0; + } + + channels = (u64)ush_wav_le16(&buffer[chunk_data + 2ULL]); + sample_rate = (u64)ush_wav_le32(&buffer[chunk_data + 4ULL]); + block_align = (u64)ush_wav_le16(&buffer[chunk_data + 12ULL]); + bits = (u64)ush_wav_le16(&buffer[chunk_data + 14ULL]); + found_fmt = 1; + } else if (ush_wav_tag_eq(chunk_tag, "data") != 0) { + data = &buffer[chunk_data]; + data_size = chunk_size; + found_data = 1; + } + + next_offset = chunk_data + chunk_size; + + if ((chunk_size & 1ULL) != 0ULL) { + next_offset++; + } + + if (next_offset <= offset || next_offset > size) { + break; + } + + offset = next_offset; + } + + if (found_fmt == 0 || found_data == 0) { + return 0; + } + + if ((channels != 1ULL && channels != 2ULL) || (bits != 8ULL && bits != 16ULL)) { + return 0; + } + + if (sample_rate == 0ULL || block_align == 0ULL) { + return 0; + } + + if (data_size < block_align) { + return 0; + } + + out_info->data = data; + out_info->data_size = data_size; + out_info->sample_rate = sample_rate; + out_info->channels = channels; + out_info->bits_per_sample = bits; + out_info->block_align = block_align; + out_info->frame_count = data_size / block_align; + + return (out_info->frame_count > 0ULL) ? 1 : 0; +} + +static u64 ush_wav_sample_deviation(const ush_wav_info *info, u64 frame_index) { + const unsigned char *frame; + + if (info == (const ush_wav_info *)0 || info->data == (const unsigned char *)0 || info->frame_count == 0ULL) { + return 0ULL; + } + + if (frame_index >= info->frame_count) { + frame_index = info->frame_count - 1ULL; + } + + frame = &info->data[frame_index * info->block_align]; + + if (info->bits_per_sample == 8ULL) { + unsigned int left = (unsigned int)frame[0]; + unsigned int dev_left = (left >= 128U) ? (left - 128U) : (128U - left); + + if (info->channels == 2ULL && info->block_align >= 2ULL) { + unsigned int right = (unsigned int)frame[1]; + unsigned int dev_right = (right >= 128U) ? (right - 128U) : (128U - right); + return (u64)((dev_left + dev_right) / 2U); + } + + return (u64)dev_left; + } + + if (info->bits_per_sample == 16ULL) { + unsigned int raw_left; + int sample_left; + unsigned int dev_left; + + if (info->block_align < 2ULL) { + return 0ULL; + } + + raw_left = ush_wav_le16(frame); + sample_left = (raw_left >= 32768U) ? ((int)raw_left - 65536) : (int)raw_left; + if (sample_left < 0) { + sample_left = -sample_left; + } + dev_left = (unsigned int)((unsigned int)sample_left >> 8U); + + if (info->channels == 2ULL && info->block_align >= 4ULL) { + unsigned int raw_right = ush_wav_le16(frame + 2ULL); + int sample_right = (raw_right >= 32768U) ? ((int)raw_right - 65536) : (int)raw_right; + unsigned int dev_right; + + if (sample_right < 0) { + sample_right = -sample_right; + } + + dev_right = (unsigned int)((unsigned int)sample_right >> 8U); + return (u64)((dev_left + dev_right) / 2U); + } + + return (u64)dev_left; + } + + return 0ULL; +} + +static int ush_wavplay_parse_args(const char *arg, + char *out_path, + u64 out_path_size, + u64 *out_steps, + u64 *out_ticks, + int *out_stop) { + char first[USH_PATH_MAX]; + char second[32]; + char third[32]; + const char *rest = ""; + const char *rest2 = ""; + const char *rest3 = ""; + + if (out_path == (char *)0 || out_steps == (u64 *)0 || out_ticks == (u64 *)0 || out_stop == (int *)0) { + return 0; + } + + *out_steps = USH_WAVPLAY_DEFAULT_STEPS; + *out_ticks = USH_WAVPLAY_DEFAULT_TICKS; + *out_stop = 0; + out_path[0] = '\0'; + + if (arg == (const char *)0 || arg[0] == '\0') { + return 0; + } + + if (ush_split_first_and_rest(arg, first, (u64)sizeof(first), &rest) == 0) { + return 0; + } + + if (ush_streq(first, "--stop") != 0 || ush_streq(first, "stop") != 0) { + if (rest != (const char *)0 && rest[0] != '\0') { + return 0; + } + + *out_stop = 1; + return 1; + } + + ush_copy(out_path, out_path_size, first); + + if (rest != (const char *)0 && rest[0] != '\0') { + if (ush_split_first_and_rest(rest, second, (u64)sizeof(second), &rest2) == 0) { + return 0; + } + + if (ush_parse_u64_dec(second, out_steps) == 0 || *out_steps == 0ULL || *out_steps > USH_WAVPLAY_MAX_STEPS) { + return 0; + } + + if (rest2 != (const char *)0 && rest2[0] != '\0') { + if (ush_split_first_and_rest(rest2, third, (u64)sizeof(third), &rest3) == 0) { + return 0; + } + + if (ush_parse_u64_dec(third, out_ticks) == 0 || *out_ticks == 0ULL || *out_ticks > USH_WAVPLAY_MAX_TICKS) { + return 0; + } + + if (rest3 != (const char *)0 && rest3[0] != '\0') { + return 0; + } + } + } + + return 1; +} + +static int ush_cmd_wavplay(const ush_state *sh, const char *arg) { + char path_arg[USH_PATH_MAX]; + char abs_path[USH_PATH_MAX]; + ush_wav_info info; + u64 file_size; + u64 got; + u64 steps; + u64 ticks_per_step; + u64 stride; + u64 i; + u64 run_freq = 0ULL; + u64 run_ticks = 0ULL; + int stop_only; + + if (sh == (const ush_state *)0) { + return 0; + } + + if (ush_wavplay_parse_args(arg, path_arg, (u64)sizeof(path_arg), &steps, &ticks_per_step, &stop_only) == 0) { + ush_writeln("wavplay: usage wavplay [steps<=4096] [ticks<=64]"); + ush_writeln("wavplay: usage wavplay --stop"); + return 0; + } + + if (stop_only != 0) { + (void)cleonos_sys_audio_stop(); + ush_writeln("wavplay: stopped"); + return 1; + } + + if (cleonos_sys_audio_available() == 0ULL) { + ush_writeln("wavplay: audio device unavailable"); + return 0; + } + + if (ush_resolve_path(sh, path_arg, abs_path, (u64)sizeof(abs_path)) == 0) { + ush_writeln("wavplay: invalid path"); + return 0; + } + + if (cleonos_sys_fs_stat_type(abs_path) != 1ULL) { + ush_writeln("wavplay: file not found"); + return 0; + } + + file_size = cleonos_sys_fs_stat_size(abs_path); + + if (file_size == (u64)-1 || file_size == 0ULL) { + ush_writeln("wavplay: empty or unreadable file"); + return 0; + } + + if (file_size > USH_WAVPLAY_FILE_MAX) { + ush_writeln("wavplay: file too large (max 65536 bytes)"); + return 0; + } + + got = cleonos_sys_fs_read(abs_path, (char *)ush_wavplay_file_buf, file_size); + + if (got != file_size) { + ush_writeln("wavplay: read failed"); + return 0; + } + + if (ush_wav_parse(ush_wavplay_file_buf, got, &info) == 0) { + ush_writeln("wavplay: unsupported wav (need PCM 8/16-bit, mono/stereo)"); + return 0; + } + + if (steps > info.frame_count) { + steps = info.frame_count; + } + + if (steps == 0ULL) { + ush_writeln("wavplay: nothing to play"); + return 0; + } + + if (steps < 8ULL) { + steps = 8ULL; + + if (steps > info.frame_count) { + steps = info.frame_count; + } + } + + ush_wavplay_print_meta(abs_path, &info, steps, ticks_per_step); + + stride = info.frame_count / steps; + if (stride == 0ULL) { + stride = 1ULL; + } + + for (i = 0ULL; i < steps; i++) { + u64 frame_index = i * stride; + u64 deviation; + u64 freq; + + if (frame_index >= info.frame_count) { + frame_index = info.frame_count - 1ULL; + } + + deviation = ush_wav_sample_deviation(&info, frame_index); + + if (deviation < 4ULL) { + freq = 0ULL; + } else { + freq = 180ULL + (deviation * 12ULL); + } + + if (run_ticks == 0ULL) { + run_freq = freq; + run_ticks = ticks_per_step; + continue; + } + + if (freq == run_freq && (run_ticks + ticks_per_step) <= USH_WAVPLAY_RUN_TICK_MAX) { + run_ticks += ticks_per_step; + continue; + } + + if (cleonos_sys_audio_play_tone(run_freq, run_ticks) == 0ULL) { + ush_writeln("wavplay: playback failed"); + (void)cleonos_sys_audio_stop(); + return 0; + } + + run_freq = freq; + run_ticks = ticks_per_step; + } + + if (run_ticks > 0ULL) { + if (cleonos_sys_audio_play_tone(run_freq, run_ticks) == 0ULL) { + ush_writeln("wavplay: playback failed"); + (void)cleonos_sys_audio_stop(); + return 0; + } + } + + (void)cleonos_sys_audio_stop(); + ush_writeln("wavplay: done"); + return 1; +} + +int cleonos_app_main(void) { + ush_cmd_ctx ctx; + ush_cmd_ret ret; + ush_state sh; + char initial_cwd[USH_PATH_MAX]; + int has_context = 0; + int success = 0; + const char *arg = ""; + + ush_zero(&ctx, (u64)sizeof(ctx)); + ush_zero(&ret, (u64)sizeof(ret)); + ush_init_state(&sh); + ush_copy(initial_cwd, (u64)sizeof(initial_cwd), sh.cwd); + + if (ush_command_ctx_read(&ctx) != 0) { + if (ctx.cmd[0] != '\0' && ush_streq(ctx.cmd, "wavplay") != 0) { + has_context = 1; + arg = ctx.arg; + if (ctx.cwd[0] == '/') { + ush_copy(sh.cwd, (u64)sizeof(sh.cwd), ctx.cwd); + ush_copy(initial_cwd, (u64)sizeof(initial_cwd), sh.cwd); + } + } + } + + success = ush_cmd_wavplay(&sh, arg); + + if (has_context != 0) { + if (ush_streq(sh.cwd, initial_cwd) == 0) { + ret.flags |= USH_CMD_RET_FLAG_CWD; + ush_copy(ret.cwd, (u64)sizeof(ret.cwd), sh.cwd); + } + + if (sh.exit_requested != 0) { + ret.flags |= USH_CMD_RET_FLAG_EXIT; + ret.exit_code = sh.exit_code; + } + + (void)ush_command_ret_write(&ret); + } + + return (success != 0) ? 0 : 1; +} \ No newline at end of file diff --git a/cleonos/c/include/cleonos_syscall.h b/cleonos/c/include/cleonos_syscall.h index 9f361e8..5c9cad7 100644 --- a/cleonos/c/include/cleonos_syscall.h +++ b/cleonos/c/include/cleonos_syscall.h @@ -54,6 +54,9 @@ typedef unsigned long long usize; #define CLEONOS_SYSCALL_YIELD 45ULL #define CLEONOS_SYSCALL_SHUTDOWN 46ULL #define CLEONOS_SYSCALL_RESTART 47ULL +#define CLEONOS_SYSCALL_AUDIO_AVAILABLE 48ULL +#define CLEONOS_SYSCALL_AUDIO_PLAY_TONE 49ULL +#define CLEONOS_SYSCALL_AUDIO_STOP 50ULL u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2); u64 cleonos_sys_log_write(const char *message, u64 length); @@ -103,5 +106,8 @@ u64 cleonos_sys_sleep_ticks(u64 ticks); u64 cleonos_sys_yield(void); u64 cleonos_sys_shutdown(void); u64 cleonos_sys_restart(void); +u64 cleonos_sys_audio_available(void); +u64 cleonos_sys_audio_play_tone(u64 hz, u64 ticks); +u64 cleonos_sys_audio_stop(void); #endif diff --git a/cleonos/c/src/syscall.c b/cleonos/c/src/syscall.c index 46966b4..a6284f9 100644 --- a/cleonos/c/src/syscall.c +++ b/cleonos/c/src/syscall.c @@ -199,3 +199,16 @@ u64 cleonos_sys_shutdown(void) { u64 cleonos_sys_restart(void) { return cleonos_syscall(CLEONOS_SYSCALL_RESTART, 0ULL, 0ULL, 0ULL); } + + +u64 cleonos_sys_audio_available(void) { + return cleonos_syscall(CLEONOS_SYSCALL_AUDIO_AVAILABLE, 0ULL, 0ULL, 0ULL); +} + +u64 cleonos_sys_audio_play_tone(u64 hz, u64 ticks) { + return cleonos_syscall(CLEONOS_SYSCALL_AUDIO_PLAY_TONE, hz, ticks, 0ULL); +} + +u64 cleonos_sys_audio_stop(void) { + return cleonos_syscall(CLEONOS_SYSCALL_AUDIO_STOP, 0ULL, 0ULL, 0ULL); +} diff --git a/clks/drivers/audio/pcspeaker.c b/clks/drivers/audio/pcspeaker.c new file mode 100644 index 0000000..c1aab78 --- /dev/null +++ b/clks/drivers/audio/pcspeaker.c @@ -0,0 +1,126 @@ +#include +#include +#include +#include + +#define CLKS_AUDIO_PIT_BASE_HZ 1193182ULL +#define CLKS_AUDIO_FREQ_MIN 20ULL +#define CLKS_AUDIO_FREQ_MAX 20000ULL +#define CLKS_AUDIO_TICKS_MAX 2048ULL + +static clks_bool clks_audio_ready = CLKS_FALSE; +static u64 clks_audio_played_count = 0ULL; + +#if defined(CLKS_ARCH_X86_64) +static inline void clks_audio_outb(u16 port, u8 value) { + __asm__ volatile("outb %0, %1" : : "a"(value), "Nd"(port)); +} + +static inline u8 clks_audio_inb(u16 port) { + u8 value; + __asm__ volatile("inb %1, %0" : "=a"(value) : "Nd"(port)); + return value; +} + +static void clks_audio_program_pc_speaker(u64 hz) { + u64 divisor64 = CLKS_AUDIO_PIT_BASE_HZ / hz; + u16 divisor; + u8 control; + + if (divisor64 == 0ULL) { + divisor64 = 1ULL; + } + + if (divisor64 > 0xFFFFULL) { + divisor64 = 0xFFFFULL; + } + + divisor = (u16)divisor64; + clks_audio_outb(0x43U, 0xB6U); + clks_audio_outb(0x42U, (u8)(divisor & 0xFFU)); + clks_audio_outb(0x42U, (u8)((divisor >> 8) & 0xFFU)); + + control = clks_audio_inb(0x61U); + clks_audio_outb(0x61U, (u8)(control | 0x03U)); +} +#endif + +static u64 clks_audio_clamp_hz(u64 hz) { + if (hz < CLKS_AUDIO_FREQ_MIN) { + return CLKS_AUDIO_FREQ_MIN; + } + + if (hz > CLKS_AUDIO_FREQ_MAX) { + return CLKS_AUDIO_FREQ_MAX; + } + + return hz; +} + +void clks_audio_init(void) { +#if defined(CLKS_ARCH_X86_64) + clks_audio_ready = CLKS_TRUE; + clks_audio_played_count = 0ULL; + clks_audio_stop(); + + clks_log(CLKS_LOG_INFO, "AUDIO", "PC SPEAKER ONLINE"); + clks_log_hex(CLKS_LOG_INFO, "AUDIO", "PIT_BASE_HZ", CLKS_AUDIO_PIT_BASE_HZ); +#else + clks_audio_ready = CLKS_FALSE; + clks_audio_played_count = 0ULL; + clks_log(CLKS_LOG_WARN, "AUDIO", "AUDIO OUTPUT NOT AVAILABLE ON THIS ARCH"); +#endif +} + +clks_bool clks_audio_available(void) { + return clks_audio_ready; +} + +clks_bool clks_audio_play_tone(u64 hz, u64 ticks) { + if (clks_audio_ready == CLKS_FALSE) { + return CLKS_FALSE; + } + + if (ticks == 0ULL) { + return CLKS_TRUE; + } + + if (ticks > CLKS_AUDIO_TICKS_MAX) { + ticks = CLKS_AUDIO_TICKS_MAX; + } + + if (hz == 0ULL) { + clks_audio_stop(); + (void)clks_exec_sleep_ticks(ticks); + return CLKS_TRUE; + } + + hz = clks_audio_clamp_hz(hz); + +#if defined(CLKS_ARCH_X86_64) + clks_audio_program_pc_speaker(hz); + (void)clks_exec_sleep_ticks(ticks); + clks_audio_stop(); + clks_audio_played_count++; + return CLKS_TRUE; +#else + return CLKS_FALSE; +#endif +} + +void clks_audio_stop(void) { +#if defined(CLKS_ARCH_X86_64) + u8 control; + + if (clks_audio_ready == CLKS_FALSE) { + return; + } + + control = clks_audio_inb(0x61U); + clks_audio_outb(0x61U, (u8)(control & 0xFCU)); +#endif +} + +u64 clks_audio_play_count(void) { + return clks_audio_played_count; +} \ No newline at end of file diff --git a/clks/include/clks/audio.h b/clks/include/clks/audio.h new file mode 100644 index 0000000..279636f --- /dev/null +++ b/clks/include/clks/audio.h @@ -0,0 +1,12 @@ +#ifndef CLKS_AUDIO_H +#define CLKS_AUDIO_H + +#include + +void clks_audio_init(void); +clks_bool clks_audio_available(void); +clks_bool clks_audio_play_tone(u64 hz, u64 ticks); +void clks_audio_stop(void); +u64 clks_audio_play_count(void); + +#endif \ No newline at end of file diff --git a/clks/include/clks/driver.h b/clks/include/clks/driver.h index 8705ce0..9dc9cca 100644 --- a/clks/include/clks/driver.h +++ b/clks/include/clks/driver.h @@ -10,6 +10,7 @@ enum clks_driver_kind { CLKS_DRIVER_KIND_BUILTIN_VIDEO = 2, CLKS_DRIVER_KIND_BUILTIN_TTY = 3, CLKS_DRIVER_KIND_ELF = 4, + CLKS_DRIVER_KIND_BUILTIN_AUDIO = 5, }; enum clks_driver_state { diff --git a/clks/include/clks/syscall.h b/clks/include/clks/syscall.h index 2e123f0..9e15e47 100644 --- a/clks/include/clks/syscall.h +++ b/clks/include/clks/syscall.h @@ -51,6 +51,9 @@ #define CLKS_SYSCALL_YIELD 45ULL #define CLKS_SYSCALL_SHUTDOWN 46ULL #define CLKS_SYSCALL_RESTART 47ULL +#define CLKS_SYSCALL_AUDIO_AVAILABLE 48ULL +#define CLKS_SYSCALL_AUDIO_PLAY_TONE 49ULL +#define CLKS_SYSCALL_AUDIO_STOP 50ULL void clks_syscall_init(void); u64 clks_syscall_dispatch(void *frame_ptr); diff --git a/clks/kernel/driver.c b/clks/kernel/driver.c index 815fc14..047d99a 100644 --- a/clks/kernel/driver.c +++ b/clks/kernel/driver.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -115,6 +116,12 @@ static void clks_driver_register_builtins(void) { clks_driver_push("framebuffer", CLKS_DRIVER_KIND_BUILTIN_VIDEO, CLKS_DRIVER_STATE_FAILED, CLKS_FALSE, 0ULL, 0ULL); clks_driver_push("tty", CLKS_DRIVER_KIND_BUILTIN_TTY, CLKS_DRIVER_STATE_FAILED, CLKS_FALSE, 0ULL, 0ULL); } + + if (clks_audio_available() == CLKS_TRUE) { + clks_driver_push("pcspeaker", CLKS_DRIVER_KIND_BUILTIN_AUDIO, CLKS_DRIVER_STATE_READY, CLKS_FALSE, 0ULL, 0ULL); + } else { + clks_driver_push("pcspeaker", CLKS_DRIVER_KIND_BUILTIN_AUDIO, CLKS_DRIVER_STATE_FAILED, CLKS_FALSE, 0ULL, 0ULL); + } } static void clks_driver_probe_driver_dir(void) { diff --git a/clks/kernel/kmain.c b/clks/kernel/kmain.c index a17de87..0c9085e 100644 --- a/clks/kernel/kmain.c +++ b/clks/kernel/kmain.c @@ -1,6 +1,7 @@ // Kernel main function #include +#include #include #include #include @@ -175,6 +176,7 @@ void clks_kernel_main(void) { } clks_exec_init(); + clks_audio_init(); clks_keyboard_init(); clks_mouse_init(); clks_desktop_init(); diff --git a/clks/kernel/syscall.c b/clks/kernel/syscall.c index 92a3143..c49bb49 100644 --- a/clks/kernel/syscall.c +++ b/clks/kernel/syscall.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -280,6 +281,22 @@ static u64 clks_syscall_restart(void) { } +static u64 clks_syscall_audio_available(void) { + return (clks_audio_available() == CLKS_TRUE) ? 1ULL : 0ULL; +} + +static u64 clks_syscall_audio_play_tone(u64 arg0, u64 arg1) { + if (clks_audio_play_tone(arg0, arg1) == CLKS_FALSE) { + return 0ULL; + } + + return 1ULL; +} + +static u64 clks_syscall_audio_stop(void) { + clks_audio_stop(); + return 1ULL; +} static u64 clks_syscall_fs_stat_type(u64 arg0) { char path[CLKS_SYSCALL_PATH_MAX]; struct clks_fs_node_info info; @@ -582,6 +599,12 @@ u64 clks_syscall_dispatch(void *frame_ptr) { return clks_syscall_shutdown(); case CLKS_SYSCALL_RESTART: return clks_syscall_restart(); + case CLKS_SYSCALL_AUDIO_AVAILABLE: + return clks_syscall_audio_available(); + case CLKS_SYSCALL_AUDIO_PLAY_TONE: + return clks_syscall_audio_play_tone(frame->rbx, frame->rcx); + case CLKS_SYSCALL_AUDIO_STOP: + return clks_syscall_audio_stop(); default: return (u64)-1; } diff --git a/docs/syscall.md b/docs/syscall.md index 3e8ef97..58a5e6c 100644 --- a/docs/syscall.md +++ b/docs/syscall.md @@ -52,7 +52,7 @@ u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2); - `FS_MKDIR` / `FS_WRITE` / `FS_APPEND` / `FS_REMOVE` 仅允许 `/temp` 树下路径。 -## 4. Syscall 列表(0~47) +## 4. Syscall 列表(0~50) ### 0 `CLEONOS_SYSCALL_LOG_WRITE` @@ -350,6 +350,27 @@ u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2); - 返回:理论上不返回;成功路径会触发重启流程(当前 x86_64 走 8042 reset) - 说明:若重启流程未生效,内核会进入 halt 循环。 +### 48 `CLEONOS_SYSCALL_AUDIO_AVAILABLE` + +- 参数:无 +- 返回: +- `1`:音频输出可用 +- `0`:当前平台无音频输出 + +### 49 `CLEONOS_SYSCALL_AUDIO_PLAY_TONE` + +- 参数: +- `arg0`: `u64 hz`(频率,`0` 表示静音等待) +- `arg1`: `u64 ticks`(持续 tick) +- 返回:成功 `1`,失败 `0` +- 说明:当前实现基于 PC Speaker(x86_64),用于最小音频链路。 + +### 50 `CLEONOS_SYSCALL_AUDIO_STOP` + +- 参数:无 +- 返回:当前实现固定返回 `1` +- 说明:立即停止当前音频输出。 + ## 5. 用户态封装函数 用户态封装位于: @@ -366,6 +387,7 @@ u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2); - `cleonos_sys_kbd_get_char()` / `cleonos_sys_kbd_buffered()` - `cleonos_sys_getpid()` / `cleonos_sys_spawn_path()` / `cleonos_sys_wait_pid()` - `cleonos_sys_exit()` / `cleonos_sys_sleep_ticks()` / `cleonos_sys_yield()` / `cleonos_sys_shutdown()` / `cleonos_sys_restart()` +- `cleonos_sys_audio_available()` / `cleonos_sys_audio_play_tone()` / `cleonos_sys_audio_stop()` ## 6. 开发注意事项 diff --git a/ramdisk/test.wav b/ramdisk/test.wav new file mode 100644 index 0000000..44d8638 Binary files /dev/null and b/ramdisk/test.wav differ