diff options
| author | Xavier Del Campo Romero <xavi92@disroot.org> | 2025-07-07 13:22:53 +0200 |
|---|---|---|
| committer | Xavier Del Campo Romero <xavi92@disroot.org> | 2025-11-11 00:08:15 +0100 |
| commit | 7861a52adf92a083bb2aed4c35f98d8035dce032 (patch) | |
| tree | 28cd3c40e4c878f730f5df3c1d93bdf91af490c3 /src/drv/ps1/cd | |
| parent | 7fc48e9216ff809da5f8055a50b0be17628ef1df (diff) | |
| download | wnix-7861a52adf92a083bb2aed4c35f98d8035dce032.tar.gz | |
Setup project skeleton
Diffstat (limited to 'src/drv/ps1/cd')
| -rw-r--r-- | src/drv/ps1/cd/CMakeLists.txt | 27 | ||||
| -rw-r--r-- | src/drv/ps1/cd/include/drv/ps1/cd.h | 28 | ||||
| -rw-r--r-- | src/drv/ps1/cd/private_include/drv/ps1/cd/regs.h | 66 | ||||
| -rw-r--r-- | src/drv/ps1/cd/private_include/drv/ps1/cd/routines.h | 37 | ||||
| -rw-r--r-- | src/drv/ps1/cd/private_include/drv/ps1/cd/types.h | 111 | ||||
| -rw-r--r-- | src/drv/ps1/cd/private_include/drv/ps1/cd/virt/types.h | 36 | ||||
| -rw-r--r-- | src/drv/ps1/cd/src/CMakeLists.txt | 33 | ||||
| -rw-r--r-- | src/drv/ps1/cd/src/ensure_event.c | 37 | ||||
| -rw-r--r-- | src/drv/ps1/cd/src/free.c | 23 | ||||
| -rw-r--r-- | src/drv/ps1/cd/src/getstat.c | 70 | ||||
| -rw-r--r-- | src/drv/ps1/cd/src/init.c | 42 | ||||
| -rw-r--r-- | src/drv/ps1/cd/src/next.c | 37 | ||||
| -rw-r--r-- | src/drv/ps1/cd/src/prv.c | 22 | ||||
| -rw-r--r-- | src/drv/ps1/cd/src/read.c | 239 | ||||
| -rw-r--r-- | src/drv/ps1/cd/src/readdebug | 253 | ||||
| -rw-r--r-- | src/drv/ps1/cd/src/send.c | 101 | ||||
| -rw-r--r-- | src/drv/ps1/cd/src/toseekl.c | 54 | ||||
| -rw-r--r-- | src/drv/ps1/cd/src/update.c | 47 | ||||
| -rw-r--r-- | src/drv/ps1/cd/src/virt/CMakeLists.txt | 25 | ||||
| -rw-r--r-- | src/drv/ps1/cd/src/virt/init.c | 41 | ||||
| -rw-r--r-- | src/drv/ps1/cd/src/write.c | 31 |
21 files changed, 1360 insertions, 0 deletions
diff --git a/src/drv/ps1/cd/CMakeLists.txt b/src/drv/ps1/cd/CMakeLists.txt new file mode 100644 index 0000000..9b84541 --- /dev/null +++ b/src/drv/ps1/cd/CMakeLists.txt @@ -0,0 +1,27 @@ +# wnix, a Unix-like operating system for WebAssembly applications. +# Copyright (C) 2025 Xavier Del Campo Romero +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +add_library(drv_ps1_cd) +add_subdirectory(src) +target_include_directories(drv_ps1_cd PUBLIC include PRIVATE private_include) +target_link_libraries(drv_ps1_cd + PUBLIC + c + drv_event + PRIVATE + drv_ps1_bios + drv_ps1_event +) diff --git a/src/drv/ps1/cd/include/drv/ps1/cd.h b/src/drv/ps1/cd/include/drv/ps1/cd.h new file mode 100644 index 0000000..ff57622 --- /dev/null +++ b/src/drv/ps1/cd/include/drv/ps1/cd.h @@ -0,0 +1,28 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef DRV_PS1_CD_H +#define DRV_PS1_CD_H + +#include <drv/event.h> + +struct drv_ps1_cd *drv_ps1_cd_init(const struct drv_event *ev); +int drv_ps1_cd_update(struct drv_ps1_cd *cd); +void drv_ps1_cd_free(struct drv_ps1_cd *cd); + +#endif diff --git a/src/drv/ps1/cd/private_include/drv/ps1/cd/regs.h b/src/drv/ps1/cd/private_include/drv/ps1/cd/regs.h new file mode 100644 index 0000000..37b2d03 --- /dev/null +++ b/src/drv/ps1/cd/private_include/drv/ps1/cd/regs.h @@ -0,0 +1,66 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef DRV_PS1_CD_REGS_H +#define DRV_PS1_CD_REGS_H + +#include <stdint.h> + +struct cd_reg_status +{ + uint8_t index :2; + const uint8_t ADPBUSY :1, PRMEMPT :1, PRMWRDY :1, RSLRRDY :1, DRQSTS :1, + BUSYSTS :1; +}; + +struct cd_reg_cmd +{ + uint8_t cmd; +}; + +struct cd_reg_rsp +{ + uint8_t rsp; +}; + +struct cd_reg_param +{ + uint8_t param; +}; + +union cd_reg_if +{ + const struct + { + uint8_t response :3, :1, cmd_start :1, :3; + } r; + + struct + { + uint8_t ack: 5, :1, CLRPRM :1, :1; + } w; +}; + +#define CD_REG(x) (0x1f801800 + (x)) +#define CD_REG_STATUS ((volatile struct cd_reg_status *)CD_REG(0)) +#define CD_REG_CMD ((volatile struct cd_reg_cmd *)CD_REG(1)) +#define CD_REG_RSP ((const volatile struct cd_reg_rsp *)CD_REG(1)) +#define CD_REG_PARAM ((volatile struct cd_reg_param *)CD_REG(2)) +#define CD_REG_IF ((volatile union cd_reg_if *)CD_REG(3)) + +#endif diff --git a/src/drv/ps1/cd/private_include/drv/ps1/cd/routines.h b/src/drv/ps1/cd/private_include/drv/ps1/cd/routines.h new file mode 100644 index 0000000..0b756de --- /dev/null +++ b/src/drv/ps1/cd/private_include/drv/ps1/cd/routines.h @@ -0,0 +1,37 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef DRV_PS1_CD_ROUTINES_H +#define DRV_PS1_CD_ROUTINES_H + +#include <drv/event.h> +#include <drv/ps1/cd/types.h> +#include <drv/ps1/bios.h> +#include <sys/types.h> +#include <stddef.h> + +int drv_ps1_cd_send(const struct cmd *cmd); +int drv_ps1_cd_getstat(void); +int drv_ps1_cd_read(void *buf, size_t n, off_t offset, + const struct drv_event_done *done, void *args); +int drv_ps1_cd_write(const void *buf, size_t n, + const struct drv_event_done *done, void *args); +int drv_ps1_cd_next(void); +struct CdAsyncSeekL drv_ps1_cd_toseekl(unsigned i); + +#endif diff --git a/src/drv/ps1/cd/private_include/drv/ps1/cd/types.h b/src/drv/ps1/cd/private_include/drv/ps1/cd/types.h new file mode 100644 index 0000000..2d4ae51 --- /dev/null +++ b/src/drv/ps1/cd/private_include/drv/ps1/cd/types.h @@ -0,0 +1,111 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef DRV_PS1_CD_TYPES_H +#define DRV_PS1_CD_TYPES_H + +#include <drv/event.h> +#include <sys/types.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> + +struct drv_ps1_cd +{ + bool available; + struct drv_event ev; +}; + +struct cmd +{ + enum + { + CMD_SYNC, + CMD_GETSTAT, + CMD_SETLOC, + } cmd; + + union cmd_param + { + struct cmd_setloc + { + uint8_t min, sec, sect; + } setloc; + } params; +}; + +struct cd_prv_getstat +{ + union + { + struct + { + uint8_t invalid :1, motor :1, seek_error :1, id_error :1, + shell_open :1, reading :1, seeking :1, playing_cdda :1; + } bits; + + uint8_t byte; + } status; +}; + +struct cd_prv_read +{ + int endevent, errevent; +}; + +struct cd_req_read +{ + void *buf; + size_t n; + off_t offset; +}; + +struct cd_req +{ + union + { + struct cd_req_read read; + } u; + + int (*f)(void); + struct drv_event_done done; + struct cd_req *next; +}; + +enum {CD_SECTOR_SZ = 2048}; + +struct cd_prv +{ + bool available, sector_read; + int (*next)(void); + int event; + struct cmd cmd; + struct cd_req *head, *tail; + char sector[CD_SECTOR_SZ]; + off_t offset; + + union + { + struct cd_prv_getstat getstat; + struct cd_prv_read read; + } u; +}; + +extern struct cd_prv drv_ps1_cd_prv; + +#endif diff --git a/src/drv/ps1/cd/private_include/drv/ps1/cd/virt/types.h b/src/drv/ps1/cd/private_include/drv/ps1/cd/virt/types.h new file mode 100644 index 0000000..59437c0 --- /dev/null +++ b/src/drv/ps1/cd/private_include/drv/ps1/cd/virt/types.h @@ -0,0 +1,36 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef DRV_PS1_CD_VIRT_TYPES_H +#define DRV_PS1_CD_VIRT_TYPES_H + +#include <drv/event.h> + +struct drv_ps1_cd +{ + struct drv_event ev; +}; + +struct cd_prv +{ + int (*next)(void); +}; + +extern struct cd_prv drv_ps1_cd_prv; + +#endif diff --git a/src/drv/ps1/cd/src/CMakeLists.txt b/src/drv/ps1/cd/src/CMakeLists.txt new file mode 100644 index 0000000..df5ecca --- /dev/null +++ b/src/drv/ps1/cd/src/CMakeLists.txt @@ -0,0 +1,33 @@ +# wnix, a Unix-like operating system for WebAssembly applications. +# Copyright (C) 2025 Xavier Del Campo Romero +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +if(WNIX_VIRT_CD) + add_subdirectory(virt) +else() + target_sources(drv_ps1_cd PRIVATE + free.c + getstat.c + init.c + next.c + prv.c + read.c + toseekl.c + update.c + write.c + ) +endif() + +target_link_libraries(drv_ps1_cd PRIVATE kprintf) diff --git a/src/drv/ps1/cd/src/ensure_event.c b/src/drv/ps1/cd/src/ensure_event.c new file mode 100644 index 0000000..592184a --- /dev/null +++ b/src/drv/ps1/cd/src/ensure_event.c @@ -0,0 +1,37 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <drv/ps1/bios.h> +#include <drv/ps1/cd/types.h> +#include <drv/ps1/event.h> +#include <stddef.h> + +int drv_ps1_cd_ensure_event(struct cd_prv *const p) +{ + if (p->event) + return 0; + + const int event = drv_ps1_event_open(CLASS_CDROM, SPEC_COMMAND_COMPLETE, + MODE_READY, NULL); + + if (event == -1) + return -1; + + drv_ps1_event_enable(p->event = event); + return 0; +} diff --git a/src/drv/ps1/cd/src/free.c b/src/drv/ps1/cd/src/free.c new file mode 100644 index 0000000..ed6f347 --- /dev/null +++ b/src/drv/ps1/cd/src/free.c @@ -0,0 +1,23 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <drv/ps1/cd.h> + +void drv_ps1_cd_free(struct drv_ps1_cd *const cd) +{ +} diff --git a/src/drv/ps1/cd/src/getstat.c b/src/drv/ps1/cd/src/getstat.c new file mode 100644 index 0000000..34bdaf5 --- /dev/null +++ b/src/drv/ps1/cd/src/getstat.c @@ -0,0 +1,70 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <drv/ps1/cd/routines.h> +#include <drv/ps1/bios.h> +#include <drv/ps1/event.h> +#include <kprintf.h> + +static int wait_event(void) +{ + struct cd_prv *const p = &drv_ps1_cd_prv; + struct cd_prv_getstat *const g = &p->u.getstat; + + if (drv_ps1_event_test(p->event)) + { + p->available = !g->status.bits.shell_open; + + if (!p->head) + return drv_ps1_cd_getstat(); + + return p->head->f(); + } + + return 0; +} + +static int ensure_event(struct cd_prv *const p) +{ + if (p->event) + return 0; + else if ((p->event = drv_ps1_event_open(CLASS_CDROM, SPEC_COMMAND_COMPLETE, + MODE_READY, NULL)) == -1) + return -1; + + drv_ps1_event_enable(p->event); + return 0; +} + +int drv_ps1_cd_getstat(void) +{ + struct cd_prv *const p = &drv_ps1_cd_prv; + struct cd_prv_getstat *const g = &p->u.getstat; + + /* TODO: CdAsyncGetStatus is *very* slow (~2.5 seconds / 10k calls) */ + if (ensure_event(p)) + return -1; + else if (!CdAsyncGetStatus(&g->status.byte)) + { + kprintf("%s: CdAsyncGetStatus failed\n", __func__); + return -1; + } + + p->next = wait_event; + return 0; +} diff --git a/src/drv/ps1/cd/src/init.c b/src/drv/ps1/cd/src/init.c new file mode 100644 index 0000000..b452e82 --- /dev/null +++ b/src/drv/ps1/cd/src/init.c @@ -0,0 +1,42 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <drv/ps1/cd.h> +#include <drv/ps1/cd/routines.h> +#include <drv/ps1/cd/types.h> +#include <drv/event.h> +#include <stddef.h> +#include <stdlib.h> + +struct drv_ps1_cd *drv_ps1_cd_init(const struct drv_event *const ev) +{ + struct drv_ps1_cd *const ret = malloc(sizeof *ret); + struct cd_prv *const p = &drv_ps1_cd_prv; + + if (!ret) + return NULL; + else if (!p->next && drv_ps1_cd_getstat()) + goto failure; + + *ret = (const struct drv_ps1_cd){.ev = *ev}; + return ret; + +failure: + free(ret); + return NULL; +} diff --git a/src/drv/ps1/cd/src/next.c b/src/drv/ps1/cd/src/next.c new file mode 100644 index 0000000..b26633a --- /dev/null +++ b/src/drv/ps1/cd/src/next.c @@ -0,0 +1,37 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <drv/ps1/cd.h> +#include <drv/ps1/cd/routines.h> +#include <drv/ps1/cd/types.h> +#include <stdlib.h> + +int drv_ps1_cd_next(void) +{ + struct cd_prv *const p = &drv_ps1_cd_prv; + struct cd_req *const next = p->head->next; + + free(p->head); + + if (next && next->f()) + return -1; + else if (!(p->head = next)) + return drv_ps1_cd_getstat(); + + return 0; +} diff --git a/src/drv/ps1/cd/src/prv.c b/src/drv/ps1/cd/src/prv.c new file mode 100644 index 0000000..61ed4b4 --- /dev/null +++ b/src/drv/ps1/cd/src/prv.c @@ -0,0 +1,22 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <drv/ps1/cd.h> +#include <drv/ps1/cd/types.h> + +struct cd_prv drv_ps1_cd_prv; diff --git a/src/drv/ps1/cd/src/read.c b/src/drv/ps1/cd/src/read.c new file mode 100644 index 0000000..d21cbae --- /dev/null +++ b/src/drv/ps1/cd/src/read.c @@ -0,0 +1,239 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <drv/ps1/cd.h> +#include <drv/ps1/cd/routines.h> +#include <drv/ps1/cd/types.h> +#include <drv/ps1/event.h> +#include <drv/event.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +static int seek(void); + +static int deliver_event(struct cd_prv *const p, const int error) +{ + const struct cd_prv_read *const read = &p->u.read; + const struct cd_req *const req = p->head; + const struct drv_event_done *const d = &req->done; + + drv_ps1_event_disable(read->endevent); + drv_ps1_event_close(read->endevent); + drv_ps1_event_disable(read->errevent); + drv_ps1_event_close(read->errevent); + + if (!error) + { + const struct cd_req_read *const rr = &req->u.read; + const long offset = rr->offset % CD_SECTOR_SZ; + + memcpy(rr->buf, p->sector + offset, rr->n); + + if (!((p->offset += rr->n) % CD_SECTOR_SZ)) + p->sector_read = false; + } + + if (d->f(error, d->args) || drv_ps1_cd_next()) + return -1; + + return 0; +} + +static int wait_read(void) +{ + struct cd_prv *const p = &drv_ps1_cd_prv; + const struct cd_prv_read *const read = &p->u.read; + + if (drv_ps1_event_test(p->event)) + { + p->sector_read = true; + return deliver_event(p, SUCCESS); + } + else if (drv_ps1_event_test(read->errevent) + || drv_ps1_event_test(read->endevent)) + return deliver_event(p, EIO); + + return 0; +} + +static int uncached_read(struct cd_prv *const p) +{ + int endevent, errevent; + struct cd_prv_read *const r = &p->u.read; + const struct CdAsyncReadSector_mode mode = + { + .mode.bits.speed = 1, + }; + + endevent = drv_ps1_event_open(CLASS_CDROM, SPEC_DATA_END, MODE_READY, NULL); + errevent = drv_ps1_event_open(CLASS_CDROM, SPEC_ERROR, MODE_READY, NULL); + + if (endevent == -1 || errevent == -1) + goto failure; + + *r = (const struct cd_prv_read) + { + .endevent = endevent, + .errevent = errevent + }; + + drv_ps1_event_enable(endevent); + drv_ps1_event_enable(errevent); + + if (!CdAsyncReadSector(1, p->sector, mode)) + goto failure; + + p->next = wait_read; + return 0; + +failure: + + if (endevent != -1) + { + drv_ps1_event_disable(endevent); + drv_ps1_event_close(endevent); + } + + if (errevent != -1) + { + drv_ps1_event_disable(errevent); + drv_ps1_event_close(errevent); + } + + return -1; +} + +static int cached_read(struct cd_prv *const p) +{ + struct cd_req *const req = p->head; + struct cd_req_read *const rr = &req->u.read; + const struct drv_event_done *const done = &req->done; + const off_t offset = rr->offset % CD_SECTOR_SZ, + rem = CD_SECTOR_SZ - offset, + n = rr->n > rem ? rem : rr->n; + + memcpy(rr->buf, p->sector + offset, n); + + if (!((p->offset += n) % CD_SECTOR_SZ)) + p->sector_read = false; + + if (done->f(SUCCESS, done->args) || drv_ps1_cd_next()) + return -1; + + return 0; +} + +static int wait_seek(void) +{ + struct cd_prv *const p = &drv_ps1_cd_prv; + const struct cd_req *const req = p->head; + const struct cd_req_read *const rr = &req->u.read; + const struct cd_prv_read *const r = &p->u.read; + + if (drv_ps1_event_test(p->event)) + { + drv_ps1_event_disable(r->errevent); + drv_ps1_event_close(r->errevent); + p->offset = rr->offset; + return uncached_read(p); + } + else if (drv_ps1_event_test(r->errevent)) + return deliver_event(p, EIO); + + return 0; +} + +static int seek(void) +{ + struct cd_prv *const p = &drv_ps1_cd_prv; + struct cd_prv_read *const r = &p->u.read; + const struct cd_req_read *rr = &p->head->u.read; + const unsigned sector = rr->offset / CD_SECTOR_SZ; + const struct CdAsyncSeekL seekl = drv_ps1_cd_toseekl(sector); + const int errevent = drv_ps1_event_open(CLASS_CDROM, SPEC_ERROR, + MODE_READY, NULL); + + if (errevent == -1) + goto failure; + + *r = (const struct cd_prv_read){.errevent = errevent}; + p->sector_read = false; + drv_ps1_event_enable(errevent); + + if (!CdAsyncSeekL(&seekl)) + goto failure; + + p->next = wait_seek; + return 0; + +failure: + + if (errevent != -1) + { + drv_ps1_event_disable(errevent); + drv_ps1_event_close(errevent); + } + + return -1; +} + +static int start(void) +{ + struct cd_prv *const p = &drv_ps1_cd_prv; + const struct cd_req *const req = p->head; + const struct cd_req_read *const rr = &req->u.read; + const off_t cursect = p->offset / CD_SECTOR_SZ, + tgtsect = rr->offset / CD_SECTOR_SZ; + + if (cursect != tgtsect || !p->sector_read) + return seek(); + + /* TODO: multi-sector reads, make sure cache can really be used */ + return cached_read(p); +} + +int drv_ps1_cd_read(void *const buf, const size_t n, const off_t offset, + const struct drv_event_done *const done, void *const args) +{ + struct cd_prv *const p = &drv_ps1_cd_prv; + struct cd_req *const r = malloc(sizeof *r); + + if (!r) + return -1; + + *r = (const struct cd_req) + { + .f = start, + .done = *done, + .u.read = + { + .offset = offset, + .buf = buf, + .n = n + } + }; + + if (!p->head) + p->head = r; + else + p->tail->next = r; + + p->tail = r; + return 0; +} diff --git a/src/drv/ps1/cd/src/readdebug b/src/drv/ps1/cd/src/readdebug new file mode 100644 index 0000000..b2c09a7 --- /dev/null +++ b/src/drv/ps1/cd/src/readdebug @@ -0,0 +1,253 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <drv/ps1/cd.h> +#include <drv/ps1/cd/routines.h> +#include <drv/ps1/cd/types.h> +#include <drv/ps1/event.h> +#include <drv/event.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> + + +#include <drv/ps1/bios.h> + +static int seek(void); + +static int deliver_event(struct cd_prv *const p, const int error) +{ + const struct cd_prv_read *const read = &p->u.read; + const struct cd_req *const req = p->head; + const struct drv_event_done *const d = &req->done; + + drv_ps1_event_disable(read->endevent); + drv_ps1_event_close(read->endevent); + drv_ps1_event_disable(read->errevent); + drv_ps1_event_close(read->errevent); + + if (!error) + { + const struct cd_req_read *const rr = &req->u.read; + const long offset = rr->offset % CD_SECTOR_SZ; + + memcpy(rr->buf, p->sector + offset, rr->n); + + if (!((p->offset += rr->n) % CD_SECTOR_SZ)) + p->sector_read = false; + } + + if (d->f(error, d->args) || drv_ps1_cd_next()) + return -1; + + return 0; +} + +static int wait_read(void) +{ + struct cd_prv *const p = &drv_ps1_cd_prv; + const struct cd_prv_read *const read = &p->u.read; + + if (drv_ps1_event_test(p->event)) + { + p->sector_read = true; + return deliver_event(p, SUCCESS); + } + else if (drv_ps1_event_test(read->errevent) + || drv_ps1_event_test(read->endevent)) + return deliver_event(p, EIO); + + return 0; +} + +static int uncached_read(struct cd_prv *const p) +{ + int endevent, errevent; + struct cd_prv_read *const r = &p->u.read; + const struct CdAsyncReadSector_mode mode = + { + .mode.bits.speed = 1, + }; + + endevent = drv_ps1_event_open(CLASS_CDROM, SPEC_DATA_END, MODE_READY, NULL); + errevent = drv_ps1_event_open(CLASS_CDROM, SPEC_ERROR, MODE_READY, NULL); + + if (endevent == -1 || errevent == -1) + goto failure; + + *r = (const struct cd_prv_read) + { + .endevent = endevent, + .errevent = errevent + }; + + drv_ps1_event_enable(endevent); + drv_ps1_event_enable(errevent); + + Printf("preparing async read\n"); + if (!CdAsyncReadSector(1, p->sector, mode)) + goto failure; + + p->next = wait_read; + return 0; + +failure: + + if (endevent != -1) + { + drv_ps1_event_disable(endevent); + drv_ps1_event_close(endevent); + } + + if (errevent != -1) + { + drv_ps1_event_disable(errevent); + drv_ps1_event_close(errevent); + } + + return -1; +} + +static int cached_read(struct cd_prv *const p) +{ + struct cd_req *const req = p->head; + struct cd_req_read *const rr = &req->u.read; + const struct drv_event_done *const done = &req->done; + const off_t offset = rr->offset % CD_SECTOR_SZ, + rem = CD_SECTOR_SZ - offset, + n = rr->n > rem ? rem : rr->n; + + Printf("Doing %lu-byte cached read from offset %lu to %p\n", + (unsigned long) n, (unsigned long) offset, (void *)rr->buf); + memcpy(rr->buf, p->sector + offset, n); + + if (!((p->offset += n) % CD_SECTOR_SZ)) + p->sector_read = false; + + if (done->f(SUCCESS, done->args) || drv_ps1_cd_next()) + return -1; + + return 0; +} + +static int wait_seek(void) +{ + struct cd_prv *const p = &drv_ps1_cd_prv; + const struct cd_req *const req = p->head; + const struct cd_req_read *const rr = &req->u.read; + const struct cd_prv_read *const r = &p->u.read; + + if (drv_ps1_event_test(p->event)) + { + Printf("finished seeking\n"); + drv_ps1_event_disable(r->errevent); + drv_ps1_event_close(r->errevent); + p->offset = rr->offset; + return uncached_read(p); + } + else if (drv_ps1_event_test(r->errevent)) + { + Printf("failed to seek\n"); + return deliver_event(p, EIO); + } + + return 0; +} + +static int seek(void) +{ + struct cd_prv *const p = &drv_ps1_cd_prv; + struct cd_prv_read *const r = &p->u.read; + const struct cd_req_read *rr = &p->head->u.read; + const unsigned sector = rr->offset / CD_SECTOR_SZ; + const struct CdAsyncSeekL seekl = drv_ps1_cd_toseekl(sector); + const int errevent = drv_ps1_event_open(CLASS_CDROM, SPEC_ERROR, + MODE_READY, NULL); + + if (errevent == -1) + goto failure; + + *r = (const struct cd_prv_read){.errevent = errevent}; + p->sector_read = false; + drv_ps1_event_enable(errevent); + + Printf("Seeking to offset %lu\n", (unsigned long)rr->offset); + + if (!CdAsyncSeekL(&seekl)) + goto failure; + + p->next = wait_seek; + return 0; + +failure: + + if (errevent != -1) + { + drv_ps1_event_disable(errevent); + drv_ps1_event_close(errevent); + } + + return -1; +} + +static int start(void) +{ + struct cd_prv *const p = &drv_ps1_cd_prv; + const struct cd_req *const req = p->head; + const struct cd_req_read *const rr = &req->u.read; + const off_t cursect = p->offset / CD_SECTOR_SZ, + tgtsect = rr->offset / CD_SECTOR_SZ; + + if (cursect != tgtsect || !p->sector_read) + return seek(); + + /* TODO: multi-sector reads, make sure cache can really be used */ + return cached_read(p); +} + +int drv_ps1_cd_read(void *const buf, const size_t n, const off_t offset, + const struct drv_event_done *const done, void *const args) +{ + struct cd_prv *const p = &drv_ps1_cd_prv; + struct cd_req *const r = malloc(sizeof *r); + + if (!r) + return -1; + + *r = (const struct cd_req) + { + .f = start, + .done = *done, + .u.read = + { + .offset = offset, + .buf = buf, + .n = n + } + }; + + if (!p->head) + p->head = r; + else + p->tail->next = r; + + p->tail = r; + Printf("Added %p {.buf=%p, .n=%lu} to read reqs\n", (void *)p->tail, buf, + (unsigned long)n); + return 0; +} diff --git a/src/drv/ps1/cd/src/send.c b/src/drv/ps1/cd/src/send.c new file mode 100644 index 0000000..01aa5cb --- /dev/null +++ b/src/drv/ps1/cd/src/send.c @@ -0,0 +1,101 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <drv/ps1/cd.h> +#include <drv/ps1/bios.h> +#include <drv/ps1/cd/regs.h> +#include <drv/ps1/cd/routines.h> +#include <drv/ps1/cd/types.h> +#include <drv/ps1/event.h> +#include <stdint.h> + +static int wait_event(void) +{ + struct cd_prv *const p = &drv_ps1_cd_prv; + + if (drv_ps1_event_test(p->event)) + { + const union cd_prv_getstat g = {.status = CD_REG_RSP->rsp}; + + p->available = !g.bits.shell_open; + } + + return 0; +} + +static void send_params(const struct cd_prv *const p) +{ + static const size_t n[] = + { + [CMD_SETLOC] = sizeof (struct cmd_setloc) + }; + + const uint8_t cmd = p->cmd.cmd, index = CD_REG_STATUS->index; + + if (cmd < sizeof n / sizeof *n) + { + const void *const vbuf = &p->cmd.params; + const uint8_t *buf = vbuf; + + CD_REG_STATUS->index = 0; + + for (size_t i = 0; i < n[cmd]; i++) + CD_REG_PARAM->param = *buf++; + + CD_REG_STATUS->index = index; + } +} + +static int send_data(void) +{ + struct cd_prv *const p = &drv_ps1_cd_prv; + + if (CD_REG_STATUS->BUSYSTS) + return 0; + + send_params(p); + CD_REG_CMD->cmd = p->cmd.cmd; + p->next = wait_event; + return 0; +} + +static int reset_param_fifo(void) +{ + struct cd_prv *const p = &drv_ps1_cd_prv; + + if (CD_REG_STATUS->BUSYSTS) + return 0; + + CD_REG_IF->w.ack = 0x1f; + CD_REG_STATUS->index = 1; + CD_REG_IF->w.CLRPRM = 1; + p->next = send_data; + return 0; +} + +int drv_ps1_cd_send(const struct cmd *const cmd) +{ + struct cd_prv *const p = &drv_ps1_cd_prv; + + if (drv_ps1_cd_ensure_event(p)) + return -1; + + p->cmd = *cmd; + p->next = reset_param_fifo; + return 0; +} diff --git a/src/drv/ps1/cd/src/toseekl.c b/src/drv/ps1/cd/src/toseekl.c new file mode 100644 index 0000000..ee776c9 --- /dev/null +++ b/src/drv/ps1/cd/src/toseekl.c @@ -0,0 +1,54 @@ +/* Original functions from psn00bsdk, commit 5d9aa2d3 + * + * itob extracted from libpsn00b/include/psxcd.h + * CdIntToPos (here renamed to drv_ps1_cd_toseekl) extracted from + * libpsn00b/psxcd/misc.c + * + * Original copyright notice: + * + * PSn00bSDK CD-ROM library + * (C) 2020-2023 Lameguy64, spicyjpeg - MPL licensed + */ + +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <drv/ps1/cd/routines.h> +#include <drv/ps1/bios.h> + +/** + * @brief Translates a decimal value to BCD. + * + * @details Translates a decimal integer in 0-99 range into a BCD format value. + */ +static unsigned itob(const unsigned i) +{ + return ((i / 10) * 16) | (i % 10); +} + +struct CdAsyncSeekL drv_ps1_cd_toseekl(unsigned i) +{ + i += 150; + + return (const struct CdAsyncSeekL) + { + .minutes = itob(i / (75 * 60)), + .seconds = itob((i / 75) % 60), + .frames = itob(i % 75) + }; +} diff --git a/src/drv/ps1/cd/src/update.c b/src/drv/ps1/cd/src/update.c new file mode 100644 index 0000000..148dd87 --- /dev/null +++ b/src/drv/ps1/cd/src/update.c @@ -0,0 +1,47 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <drv/ps1/cd.h> +#include <drv/ps1/cd/types.h> +#include <drv/ps1/cd/routines.h> +#include <drv/event.h> +#include <stdbool.h> + +int drv_ps1_cd_update(struct drv_ps1_cd *const cd) +{ + struct cd_prv *const p = &drv_ps1_cd_prv; + + if (p->next()) + return -1; + else if (p->available ^ cd->available) + { + const struct drv_event *const ev = &cd->ev; + static const struct drv_event_ops ops = + { + .read = drv_ps1_cd_read, + .write = drv_ps1_cd_write + }; + + if (ev->status("cd0", &ops, p->available, 0440, ev->args)) + return -1; + + cd->available = p->available; + } + + return 0; +} diff --git a/src/drv/ps1/cd/src/virt/CMakeLists.txt b/src/drv/ps1/cd/src/virt/CMakeLists.txt new file mode 100644 index 0000000..0e5bd1b --- /dev/null +++ b/src/drv/ps1/cd/src/virt/CMakeLists.txt @@ -0,0 +1,25 @@ +# wnix, a Unix-like operating system for WebAssembly applications. +# Copyright (C) 2025 Xavier Del Campo Romero +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +target_sources(drv_ps1_cd PRIVATE + # free.c + init.c + # next.c + # prv.c + # read.c + # update.c + # write.c +) diff --git a/src/drv/ps1/cd/src/virt/init.c b/src/drv/ps1/cd/src/virt/init.c new file mode 100644 index 0000000..204f7fa --- /dev/null +++ b/src/drv/ps1/cd/src/virt/init.c @@ -0,0 +1,41 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <drv/ps1/cd.h> +#include <drv/ps1/cd/virt/types.h> +#include <drv/event.h> +#include <stddef.h> +#include <stdlib.h> + +struct drv_ps1_cd *drv_ps1_cd_init(const struct drv_event *const ev) +{ + struct drv_ps1_cd *const ret = malloc(sizeof *ret); + struct cd_prv *const p = &drv_ps1_cd_prv; + + if (!ret) + return NULL; + else if (!p->next && drv_ps1_cd_getstat()) + goto failure; + + *ret = (const struct drv_ps1_cd){.ev = *ev}; + return ret; + +failure: + free(ret); + return NULL; +} diff --git a/src/drv/ps1/cd/src/write.c b/src/drv/ps1/cd/src/write.c new file mode 100644 index 0000000..ba47993 --- /dev/null +++ b/src/drv/ps1/cd/src/write.c @@ -0,0 +1,31 @@ +/* + * wnix, a Unix-like operating system for WebAssembly applications. + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <drv/ps1/cd.h> +#include <drv/ps1/cd/routines.h> +#include <drv/ps1/cd/types.h> +#include <drv/event.h> +#include <errno.h> + +int drv_ps1_cd_write(const void *const buf, const size_t n, + const struct drv_event_done *const done, void *const args) +{ + /* TODO: write event callback returning EROFS */ + errno = EROFS; + return -1; +} |
