From: kosmirror <kos...@us...> - 2025-05-20 03:16:53
|
This is an automated email from the git hooks/post-receive script. It was generated because a ref change was pushed to the repository containing the project "A pseudo Operating System for the Dreamcast.". The branch, master has been updated via 4d828534c2a40828e141c2d8619ed4ac51b5fb1e (commit) via b80a209710b18034e0e7df316ca949f348050e69 (commit) from 71a97d30c731858205ca9deaaee4bc36ec8db409 (commit) Those revisions listed above that are new to this repository have not appeared on any other notification email; so we list those revisions in full, below. - Log ----------------------------------------------------------------- commit 4d828534c2a40828e141c2d8619ed4ac51b5fb1e Author: DC-SWAT <sw...@21...> Date: Mon May 12 11:55:45 2025 +0700 cdrom: Added streaming example. commit b80a209710b18034e0e7df316ca949f348050e69 Author: DC-SWAT <sw...@21...> Date: Mon May 12 11:55:37 2025 +0700 cdrom: Implemented PIO and DMA stream transfer support ----------------------------------------------------------------------- Summary of changes: examples/dreamcast/cdrom/stream/Makefile | 29 +++ examples/dreamcast/cdrom/stream/cd-stream-test.c | 231 +++++++++++++++++++++++ kernel/arch/dreamcast/exports-pristine.txt | 5 + kernel/arch/dreamcast/hardware/cdrom.c | 224 ++++++++++++++++++++++ kernel/arch/dreamcast/include/dc/cdrom.h | 64 +++++++ 5 files changed, 553 insertions(+) create mode 100644 examples/dreamcast/cdrom/stream/Makefile create mode 100644 examples/dreamcast/cdrom/stream/cd-stream-test.c diff --git a/examples/dreamcast/cdrom/stream/Makefile b/examples/dreamcast/cdrom/stream/Makefile new file mode 100644 index 00000000..a32fe89b --- /dev/null +++ b/examples/dreamcast/cdrom/stream/Makefile @@ -0,0 +1,29 @@ +# KallistiOS ##version## +# +# examples/dreamcast/cd/stream/Makefile +# (c) 2025 Ruslan Rostovtsev +# + +TARGET = cd-stream-test +OBJS = $(TARGET).o + +all: rm-elf $(TARGET).elf + +include $(KOS_BASE)/Makefile.rules + +clean: rm-elf + -rm -f $(OBJS) + +rm-elf: + -rm -f $(TARGET).elf $(TARGET).bin + +$(TARGET).elf: $(OBJS) + kos-cc -o $(TARGET).elf $(OBJS) + +run: $(TARGET).elf + $(KOS_LOADER) $(TARGET).elf + +dist: $(TARGET).elf + -rm -f $(OBJS) + $(KOS_STRIP) $(TARGET).elf + $(KOS_OBJCOPY) -R .stack -O binary $(TARGET).elf $(TARGET).bin diff --git a/examples/dreamcast/cdrom/stream/cd-stream-test.c b/examples/dreamcast/cdrom/stream/cd-stream-test.c new file mode 100644 index 00000000..b3a2044d --- /dev/null +++ b/examples/dreamcast/cdrom/stream/cd-stream-test.c @@ -0,0 +1,231 @@ +/* KallistiOS ##version## + + cd-stream-test.c + Copyright (C) 2025 Ruslan Rostovtsev + + This example program simply shows how CD streams works. +*/ + +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <stdint.h> +#include <errno.h> + +#include <dc/maple.h> +#include <dc/maple/controller.h> +#include <dc/cdrom.h> + +#include <arch/arch.h> +#include <arch/cache.h> + +#include <kos/init.h> +#include <kos/dbgio.h> +#include <kos/dbglog.h> + +#define BUFFER_SIZE (8 << 11) + +static uint8_t dma_buf[BUFFER_SIZE] __attribute__((aligned(32))); +static uint8_t pio_buf[BUFFER_SIZE] __attribute__((aligned(2))); + +static void __attribute__((__noreturn__)) wait_exit(void) { + maple_device_t *dev; + cont_state_t *state; + + printf("Press any button to exit.\n"); + + for(;;) { + dev = maple_enum_type(0, MAPLE_FUNC_CONTROLLER); + + if(dev) { + state = (cont_state_t *)maple_dev_status(dev); + + if(state) { + if(state->buttons) + arch_exit(); + } + } + } +} + +static void cd_stream_callback(void *param) { + (*(size_t *)param)++; +} + +static int cd_stream_test(uint32_t lba, uint8_t *buffer, size_t size, int mode) { + + int rs; + size_t cur_size = 0; + size_t cb_count = 0; + char *stream_name = (mode == CDROM_READ_PIO ? "PIO" : "DMA"); + + dbglog(DBG_DEBUG, "Start %s stream.\n", stream_name); + rs = cdrom_stream_start(lba, size / 2048, mode); + + if (rs != ERR_OK) { + dbglog(DBG_ERROR, "Failed to start stream for %s.\n", stream_name); + return -1; + } + + cdrom_stream_set_callback(cd_stream_callback, (void *)&cb_count); + rs = cdrom_stream_request(buffer, size / 2, 1); + + if (rs != ERR_OK) { + dbglog(DBG_ERROR, "Failed to request %s transfer.\n", stream_name); + return -1; + } + + rs = cdrom_stream_progress(&cur_size); + + if (rs != 0 || cur_size != (size / 2)) { + dbglog(DBG_ERROR, "Failed to check %s transfer: rs=%d sz=%d\n", + stream_name, rs, cur_size); + return -1; + } + + rs = cdrom_stream_request(buffer + (size / 2), size / 2, 1); + + if (rs != ERR_OK) { + dbglog(DBG_ERROR, "Failed to request %s transfer.\n", stream_name); + return -1; + } + + rs = cdrom_stream_progress(&cur_size); + + if (rs != 0 || cur_size != 0) { + dbglog(DBG_ERROR, "Failed to check %s transfer: rs=%d sz=%d\n", + stream_name, rs, cur_size); + return -1; + } + + rs = cdrom_stream_stop(false); + + if (rs != ERR_OK) { + dbglog(DBG_ERROR, "Failed to stop %s stream.\n", stream_name); + return -1; + } + + if (cb_count != 2) { + dbglog(DBG_ERROR, "%s transfer is done, but callback fails: %d\n", + stream_name, cb_count); + return -1; + } + + dbglog(DBG_DEBUG, "%s transfer is done.\n", stream_name); + return 0; +} + +int print_diff(uint8_t *pio_buf, uint8_t *dma_buf, size_t size) { + int i, j, rv = 0; + + for(i = 0; i < size; ++i) { + if (dma_buf[i] != pio_buf[i]) { + rv = i; + if (i >= 8) { + i -= 8; + } + break; + } + } + dbglog(DBG_INFO, "DMA[%d]: ", i); + + for(j = 0; j < 16; ++j) { + dbglog(DBG_INFO, "%02x", dma_buf[i + j]); + } + dbglog(DBG_INFO, "\nPIO[%d]: ", i); + + for(j = 0; j < 16; ++j) { + dbglog(DBG_INFO, "%02x", pio_buf[i + j]); + } + dbglog(DBG_INFO, "\n\n"); + return rv; +} + +int main(int argc, char *argv[]) { + int rs, i; + uint32_t lba; + CDROM_TOC toc; + + dbgio_dev_select("fb"); + dbglog(DBG_INFO, "CD-ROM stream test.\n\n"); + + rs = cdrom_read_toc(&toc, 0); + + if (rs != ERR_OK) { + dbglog(DBG_ERROR, "No disc present.\n"); + goto exit; + } + lba = cdrom_locate_data_track(&toc); + + if (lba == 0) { + dbglog(DBG_ERROR, "No data track on disc.\n"); + goto exit; + } + + memset(dma_buf, 0xff, BUFFER_SIZE); + /* Inside the cdrom driver the cache will be invalidated, + but we need to save what we wrote to it by memset. + In normal cases you don't need to do this. + */ + dcache_purge_range((uintptr_t)dma_buf, BUFFER_SIZE); + + rs = cd_stream_test(lba, dma_buf, BUFFER_SIZE, CDROM_READ_DMA); + + if (rs != ERR_OK) { + dbglog(DBG_ERROR, "DMA stream test failed.\n"); + goto exit; + } + + memset(pio_buf, 0xee, BUFFER_SIZE); + rs = cd_stream_test(lba, pio_buf, BUFFER_SIZE, CDROM_READ_PIO); + + if (rs != ERR_OK) { + dbglog(DBG_ERROR, "PIO stream test failed.\n"); + goto exit; + } + + if (memcmp(dma_buf, pio_buf, BUFFER_SIZE) == 0) { + dbglog(DBG_INFO, "Stream data matched.\n"); + goto exit; + } + + dbglog(DBG_ERROR, "Stream data mismatch:\n"); + i = print_diff(pio_buf, dma_buf, BUFFER_SIZE); + + if (dma_buf[i] == 0xff) { + dbglog(DBG_INFO, "Read DMA data.\n"); + memset(dma_buf, 0xff, BUFFER_SIZE); + /* Inside the cdrom driver the cache will be invalidated, + but we need to save what we wrote to it by memset. + In normal cases you don't need to do this. + */ + dcache_purge_range((uintptr_t)dma_buf, BUFFER_SIZE); + + rs = cdrom_read_sectors_ex(dma_buf, lba, + BUFFER_SIZE >> 11, CDROM_READ_DMA); + } + else { + dbglog(DBG_INFO, "Read PIO data.\n"); + memset(pio_buf, 0xee, BUFFER_SIZE); + rs = cdrom_read_sectors_ex(pio_buf, lba, + BUFFER_SIZE >> 11, CDROM_READ_PIO); + } + + if (rs != ERR_OK) { + dbglog(DBG_ERROR, "%s read sectors failed.\n", + dma_buf[i] == 0xff ? "DMA" : "PIO"); + } + else if (memcmp(dma_buf, pio_buf, BUFFER_SIZE)) { + dbglog(DBG_ERROR, "Stream and read data mismatch:\n"); + print_diff(pio_buf, dma_buf, BUFFER_SIZE); + } + else { + dbglog(DBG_INFO, "Stream and read data matched.\n"); + } + +exit: + dbglog(DBG_INFO, "\n"); + wait_exit(); + return 0; +} diff --git a/kernel/arch/dreamcast/exports-pristine.txt b/kernel/arch/dreamcast/exports-pristine.txt index 55479064..db35ee09 100644 --- a/kernel/arch/dreamcast/exports-pristine.txt +++ b/kernel/arch/dreamcast/exports-pristine.txt @@ -157,6 +157,11 @@ cdrom_get_subcode cdrom_read_toc cdrom_read_sectors cdrom_read_sectors_ex +cdrom_stream_start +cdrom_stream_stop +cdrom_stream_request +cdrom_stream_progress +cdrom_stream_set_callback cdrom_locate_data_track cdrom_cdda_play cdrom_cdda_pause diff --git a/kernel/arch/dreamcast/hardware/cdrom.c b/kernel/arch/dreamcast/hardware/cdrom.c index c60905ed..d76692e4 100644 --- a/kernel/arch/dreamcast/hardware/cdrom.c +++ b/kernel/arch/dreamcast/hardware/cdrom.c @@ -70,6 +70,12 @@ static semaphore_t dma_done = SEM_INITIALIZER(0); static asic_evt_handler_entry_t old_dma_irq = {NULL, NULL}; static int vblank_hnd = -1; +/* Streaming */ +static int stream_mode = -1; +static cdrom_stream_callback_t stream_cb = NULL; +static void *stream_cb_param = NULL; + +/* Initialization */ static bool inited = false; static int cur_sector_size = 2048; @@ -208,6 +214,11 @@ int cdrom_abort_cmd(uint32_t timeout, bool abort_dma) { } while(1); cmd_hnd = 0; + stream_mode = -1; + + if(stream_cb) { + cdrom_stream_set_callback(0, NULL); + } mutex_unlock(&_g1_ata_mutex); return rv; @@ -456,6 +467,216 @@ int cdrom_read_sectors(void *buffer, int sector, int cnt) { return cdrom_read_sectors_ex(buffer, sector, cnt, CDROM_READ_PIO); } +int cdrom_stream_start(int sector, int cnt, int mode) { + struct { + int sec; + int num; + } params; + int rv = ERR_SYS; + + params.sec = sector; + params.num = cnt; + + if(stream_mode != -1) { + cdrom_stream_stop(false); + } + stream_mode = mode; + + if(mode == CDROM_READ_DMA) { + rv = cdrom_exec_cmd_timed(CMD_DMAREAD_STREAM, ¶ms, 0); + } + else if(mode == CDROM_READ_PIO) { + rv = cdrom_exec_cmd_timed(CMD_PIOREAD_STREAM, ¶ms, 0); + } + + if(rv != ERR_OK) { + stream_mode = -1; + } + return rv; +} + +int cdrom_stream_stop(bool abort_dma) { + int rv = ERR_OK; + + if(cmd_hnd <= 0) { + return rv; + } + if(abort_dma && dma_in_progress) { + return cdrom_abort_cmd(1000, true); + } + mutex_lock(&_g1_ata_mutex); + + do { + syscall_gdrom_exec_server(); + cmd_response = syscall_gdrom_check_command(cmd_hnd, cmd_status); + + if(cmd_response < 0) { + rv = ERR_SYS; + break; + } + else if(cmd_response == COMPLETED || cmd_response == NO_ACTIVE) { + break; + } + else if(cmd_response == STREAMING) { + mutex_unlock(&_g1_ata_mutex); + return cdrom_abort_cmd(1000, false); + } + thd_pass(); + } while(1); + + cmd_hnd = 0; + stream_mode = -1; + mutex_unlock(&_g1_ata_mutex); + + if(stream_cb) { + cdrom_stream_set_callback(0, NULL); + } + return rv; +} + +int cdrom_stream_request(void *buffer, size_t size, bool block) { + int rs, rv = ERR_OK; + int32_t params[2]; + size_t check_size = -1; + + if(cmd_hnd <= 0) { + return ERR_NO_ACTIVE; + } + if(dma_in_progress) { + dbglog(DBG_ERROR, "cdrom_stream_request: Previous DMA request is in progress.\n"); + return ERR_SYS; + } + + if(stream_mode == CDROM_READ_DMA) { + params[0] = ((uintptr_t)buffer) & MEM_AREA_CACHE_MASK; + if(params[0] & 0x1f) { + dbglog(DBG_ERROR, "cdrom_stream_request: Unaligned memory for DMA (32-byte).\n"); + return ERR_SYS; + } + if((params[0] >> 24) == 0x0c) { + dcache_inval_range((uintptr_t)buffer, size); + } + } + else { + params[0] = (uintptr_t)buffer; + if(params[0] & 0x01) { + dbglog(DBG_ERROR, "cdrom_stream_request: Unaligned memory for PIO (2-byte).\n"); + return ERR_SYS; + } + } + + params[1] = size; + mutex_lock_scoped(&_g1_ata_mutex); + + if(stream_mode == CDROM_READ_DMA) { + + dma_in_progress = true; + dma_blocking = block; + + if(!block) { + dma_thd = thd_current; + if(irq_inside_int()) { + dma_thd = (kthread_t *)0xFFFFFFFF; + } + } + rs = syscall_gdrom_dma_transfer(cmd_hnd, params); + + if(rs < 0) { + dma_in_progress = false; + dma_blocking = false; + dma_thd = NULL; + return ERR_SYS; + } + if(!block) { + return rv; + } + sem_wait(&dma_done); + + do { + syscall_gdrom_exec_server(); + cmd_response = syscall_gdrom_check_command(cmd_hnd, cmd_status); + + if(cmd_response < 0) { + rv = ERR_SYS; + break; + } + else if(cmd_response == COMPLETED || cmd_response == NO_ACTIVE) { + cmd_hnd = 0; + break; + } + else if(syscall_gdrom_dma_check(cmd_hnd, &check_size) == 0) { + break; + } + thd_pass(); + + } while(1); + } + else if(stream_mode == CDROM_READ_PIO) { + + rs = syscall_gdrom_pio_transfer(cmd_hnd, params); + + if(rs < 0) { + return ERR_SYS; + } + do { + syscall_gdrom_exec_server(); + cmd_response = syscall_gdrom_check_command(cmd_hnd, cmd_status); + + if(cmd_response < 0) { + rv = ERR_SYS; + break; + } + else if(cmd_response == COMPLETED || cmd_response == NO_ACTIVE) { + cmd_hnd = 0; + break; + } + else if(syscall_gdrom_pio_check(cmd_hnd, &check_size) == 0) { + /* Syscalls doesn't call it on last reading in PIO mode. + Looks like a bug, fixing it. */ + if(check_size == 0 && stream_cb) { + stream_cb(stream_cb_param); + } + break; + } + thd_pass(); + } while(1); + } + + return rv; +} + +int cdrom_stream_progress(size_t *size) { + int rv = 0; + size_t check_size = 0; + ...<truncated>... hooks/post-receive -- A pseudo Operating System for the Dreamcast. |