diff --git a/src/cdrom/cdrom_ioctl.c b/src/cdrom/cdrom_ioctl.c new file mode 100644 index 000000000..382465637 --- /dev/null +++ b/src/cdrom/cdrom_ioctl.c @@ -0,0 +1,221 @@ +/* + * 86Box A hypervisor and IBM PC system emulator that specializes in + * running old operating systems and software designed for IBM + * PC systems and compatibles from 1981 through fairly recent + * system designs based on the PCI bus. + * + * This file is part of the 86Box distribution. + * + * CD-ROM passthrough support. + * + * + * + * Authors: TheCollector1995, , + * Miran Grca, + * + * Copyright 2023 TheCollector1995. + * Copyright 2023 Miran Grca. + */ +#include +#include +#include +#include +#include +#include +#include +#define HAVE_STDARG_H +#include <86box/86box.h> +#include <86box/config.h> +#include <86box/path.h> +#include <86box/plat.h> +#include <86box/plat_cdrom.h> +#include <86box/scsi_device.h> +#include <86box/cdrom.h> + +#ifdef ENABLE_CDROM_IOCTL_LOG +int cdrom_ioctl_do_log = ENABLE_CDROM_IOCTL_LOG; + +void +cdrom_ioctl_log(const char *fmt, ...) +{ + va_list ap; + + if (cdrom_ioctl_do_log) { + va_start(ap, fmt); + pclog_ex(fmt, ap); + va_end(ap); + } +} +#else +# define cdrom_ioctl_log(fmt, ...) +#endif + +/* The addresses sent from the guest are absolute, ie. a LBA of 0 corresponds to a MSF of 00:00:00. Otherwise, the counter displayed by the guest is wrong: + there is a seeming 2 seconds in which audio plays but counter does not move, while a data track before audio jumps to 2 seconds before the actual start + of the audio while audio still plays. With an absolute conversion, the counter is fine. */ +#define MSFtoLBA(m, s, f) ((((m * 60) + s) * 75) + f) + +static void +cdrom_ioctl_get_tracks(UNUSED(cdrom_t *dev), int *first, int *last) +{ + TMSF tmsf; + + plat_cdrom_get_audio_tracks(first, last, &tmsf); +} + +static void +cdrom_ioctl_get_track_info(UNUSED(cdrom_t *dev), uint32_t track, int end, track_info_t *ti) +{ + TMSF tmsf; + + plat_cdrom_get_audio_track_info(end, track, &ti->number, &tmsf, &ti->attr); + + ti->m = tmsf.min; + ti->s = tmsf.sec; + ti->f = tmsf.fr; +} + +static void +cdrom_ioctl_get_subchannel(UNUSED(cdrom_t *dev), uint32_t lba, subchannel_t *subc) +{ + TMSF rel_pos; + TMSF abs_pos; + + plat_cdrom_get_audio_sub(lba, &subc->attr, &subc->track, &subc->index, + &rel_pos, &abs_pos); + + subc->abs_m = abs_pos.min; + subc->abs_s = abs_pos.sec; + subc->abs_f = abs_pos.fr; + + subc->rel_m = rel_pos.min; + subc->rel_s = rel_pos.sec; + subc->rel_f = rel_pos.fr; +} + +static int +cdrom_ioctl_is_track_audio(uint32_t pos, int ismsf) +{ + uint8_t attr; + int m; + int s; + int f; + int track; + + if (ismsf) { + m = (pos >> 16) & 0xff; + s = (pos >> 8) & 0xff; + f = pos & 0xff; + pos = MSFtoLBA(m, s, f) - 150; + } + + /* GetTrack requires LBA. */ + return plat_cdrom_get_audio_track(pos); +} + +static int +cdrom_ioctl_sector_size(UNUSED(cdrom_t *dev), UNUSED(uint32_t lba)) +{ + return plat_get_sector_size(); +} + +static int +cdrom_ioctl_read_sector(UNUSED(cdrom_t *dev), int type, uint8_t *b, uint32_t lba) +{ + switch (type) { + case CD_READ_DATA: + return plat_cdrom_read_sector(b, 0, lba); + case CD_READ_AUDIO: + return plat_cdrom_read_sector(b, 1, lba); + case CD_READ_RAW: + if (plat_get_sector_size() == 2352) + return plat_cdrom_read_sector(b, 1, lba); + else + return plat_cdrom_read_sector(b, 0, lba); + break; + default: + cdrom_ioctl_log("cdrom_ioctl_read_sector(): Unknown CD read type.\n"); + return 0; + } +} + +static int +cdrom_ioctl_track_type(UNUSED(cdrom_t *dev), uint32_t lba) +{ + if (cdrom_ioctl_is_track_audio(lba, 0)) + return CD_TRACK_AUDIO; + + return 0; +} + +static void +cdrom_ioctl_exit(cdrom_t *dev) +{ + dev->cd_status = CD_STATUS_EMPTY; + + plat_cdrom_exit(); + + dev->ops = NULL; +} + +static const cdrom_ops_t cdrom_ioctl_ops = { + cdrom_ioctl_get_tracks, + cdrom_ioctl_get_track_info, + cdrom_ioctl_get_subchannel, + NULL, + cdrom_ioctl_sector_size, + cdrom_ioctl_read_sector, + cdrom_ioctl_track_type, + cdrom_ioctl_exit +}; + +void +cdrom_ioctl_eject(void) +{ + plat_cdrom_eject(); +} + +void +cdrom_ioctl_load(void) +{ + plat_cdrom_load(); +} + +static int +cdrom_ioctl_open_abort(cdrom_t *dev) +{ + if (dev && dev->ops && dev->ops->exit) + dev->ops->exit(dev); + + dev->ops = NULL; + dev->host_drive = 0; + dev->ioctl_path[0] = 0; + return 1; +} + +int +cdrom_ioctl_open(cdrom_t *dev, const char *path) +{ + /* Open the drive. */ + if (plat_cdrom_open()) + return cdrom_ioctl_open_abort(dev); + + /* Make sure to not STRCPY if the two are pointing + at the same place. */ + if (path != dev->ioctl_path) + strcpy(dev->ioctl_path, path); + + /* All good, reset state. */ + dev->cd_status = CD_STATUS_STOPPED; + dev->is_dir = 0; + dev->seek_pos = 0; + dev->cd_buflen = 0; + plat_cdrom_reset(); + dev->cdrom_capacity = plat_cdrom_get_capacity(); + pclog("CD-ROM capacity: %i sectors (%" PRIi64 " bytes)\n", dev->cdrom_capacity, ((uint64_t) dev->cdrom_capacity) << 11ULL); + + /* Attach this handler to the drive. */ + dev->ops = &cdrom_ioctl_ops; + + return 0; +} diff --git a/src/win/win_cdrom_ioctl.c b/src/win/win_cdrom_ioctl.c new file mode 100644 index 000000000..9eee9c789 --- /dev/null +++ b/src/win/win_cdrom_ioctl.c @@ -0,0 +1,275 @@ +/* + * 86Box A hypervisor and IBM PC system emulator that specializes in + * running old operating systems and software designed for IBM + * PC systems and compatibles from 1981 through fairly recent + * system designs based on the PCI bus. + * + * This file is part of the 86Box distribution. + * + * Win32 CD-ROM support via IOCTL. + * + * + * + * Authors: TheCollector1995, , + * Miran Grca, + * + * Copyright 2023 TheCollector1995. + * Copyright 2023 Miran Grca. + */ +#define UNICODE +#define BITMAP WINDOWS_BITMAP +#include +#include +#undef BITMAP +#include +#include "ntddcdrm.h" +#include "ntddscsi.h" +#include +#include +#include <86box/86box.h> +#include <86box/scsi_device.h> +#include <86box/cdrom.h> +#include <86box/plat_unused.h> +#include <86box/plat_cdrom.h> + +static const char ioctl_path[8]; +static HANDLE hIOCTL; +static CDROM_TOC toc; + +/* The addresses sent from the guest are absolute, ie. a LBA of 0 corresponds to a MSF of 00:00:00. Otherwise, the counter displayed by the guest is wrong: + there is a seeming 2 seconds in which audio plays but counter does not move, while a data track before audio jumps to 2 seconds before the actual start + of the audio while audio still plays. With an absolute conversion, the counter is fine. */ +#define MSFtoLBA(m, s, f) ((((m * 60) + s) * 75) + f) + +static int +plat_cdrom_get_track(uint32_t sector) +{ + int track = 0; + uint32_t track_addr; + + for (int i = toc.FirstTrack; i < toc.LastTrack; i++) { + /* There must be at least two tracks - data and lead out. */ + track_addr = MSFtoLBA(toc.TrackData[i].Address[1], toc.TrackData[i].Address[2], toc.TrackData[i].Address[3]); + if (track_addr <= sector) { + track = i; + } + } + + pclog("GetTrack = %d.\n", track); + return track; +} + +int +plat_cdrom_get_audio_track(uint32_t sector) +{ + int control = 0; + uint32_t track_addr; + + for (int i = 0; toc.TrackData[i].TrackNumber != 0xaa; i++) { + /* There must be at least two tracks - data and lead out. */ + track_addr = MSFtoLBA(toc.TrackData[i].Address[1], toc.TrackData[i].Address[2], toc.TrackData[i].Address[3]); + if ((toc.TrackData[i].TrackNumber >= toc.FirstTrack) && (toc.TrackData[i].TrackNumber <= toc.LastTrack) && + (track_addr >= sector)) { + control = toc.TrackData[i].Control; + break; + } + } + + return (control & 4) ? DATA_TRACK : AUDIO_TRACK; +} + +int +plat_cdrom_get_audio_sub(uint32_t sector, uint8_t *attr, uint8_t *track, uint8_t *index, TMSF *rel_pos, TMSF *abs_pos) +{ + CDROM_SUB_Q_DATA_FORMAT insub; + SUB_Q_CHANNEL_DATA sub; + DWORD size; + int pos = 0; + int cur_track = plat_cdrom_get_track(sector); + + insub.Format = IOCTL_CDROM_CURRENT_POSITION; + if (plat_cdrom_open()) + return 0; + DeviceIoControl(hIOCTL,IOCTL_CDROM_READ_Q_CHANNEL, &insub, sizeof(insub), &sub, sizeof(sub), &size, NULL); + plat_cdrom_exit(); + + *attr = sub.CurrentPosition.Control; + *track = (uint8_t)(cur_track + 1); + *index = sub.CurrentPosition.IndexNumber; + + FRAMES_TO_MSF(sector + 150, &abs_pos->min, &abs_pos->sec, &abs_pos->fr); + + /* Absolute position should be adjusted by 150, not the relative ones. */ + FRAMES_TO_MSF(sector - toc.FirstTrack, &rel_pos->min, &rel_pos->sec, &rel_pos->fr); + + return 1; +} + +int +plat_cdrom_get_audio_tracks(int *st_track, int *end, TMSF *lead_out) +{ + CDROM_TOC toc; + DWORD byteCount; + + *st_track = toc.FirstTrack; + *end = toc.LastTrack; + FRAMES_TO_MSF(toc.TrackData[*end].TrackNumber + 150, &lead_out->min, &lead_out->sec, &lead_out->fr); + + return 1; +} + +/* This replaces both Info and EndInfo, they are specified by a variable. */ +int +plat_cdrom_get_audio_track_info(int end, int track, int *track_num, TMSF *start, uint8_t *attr) +{ + int pos; + DWORD byteCount; + + pclog("plat_cdrom_get_audio_track_info(): start track = %d, last track = %d.\n", track, end); + + if ((track < 1) || (track > end)) + return 0; + + pos = toc.FirstTrack + 150; + + FRAMES_TO_MSF(pos, &start->min, &start->sec, &start->fr); + + *track_num = toc.TrackData[track - 1].TrackNumber; + *attr = toc.TrackData[track - 1].Control; + + return 1; +} + +uint32_t +plat_get_sector_size(void) +{ + DISK_GEOMETRY dgCDROM; + DWORD size; + + if (plat_cdrom_open()) + return 0; + DeviceIoControl(hIOCTL, IOCTL_CDROM_GET_DRIVE_GEOMETRY, NULL, 0, &dgCDROM, sizeof(DISK_GEOMETRY), &size, NULL); + plat_cdrom_exit(); + + if (dgCDROM.MediaType != 11) // Removable Media Check + return 0; + + return dgCDROM.BytesPerSector; +} + +int +plat_cdrom_read_sector(uint8_t *buffer, int raw, uint32_t sector) +{ + int ret; + LARGE_INTEGER pos; + RAW_READ_INFO in; + DWORD byteCount; + pclog("plat_cdrom_read_sector(): raw? = %d, sector = %02x.\n", raw, sector); + + if (raw) { + in.TrackMode = CDDA; + in.SectorCount = 1; + in.DiskOffset.QuadPart = sector * RAW_SECTOR_SIZE; + if (plat_cdrom_open()) + return 0; + ret = DeviceIoControl(hIOCTL, IOCTL_CDROM_RAW_READ, &in, sizeof(RAW_READ_INFO), buffer, RAW_SECTOR_SIZE, &byteCount, NULL); + plat_cdrom_exit(); + return ret; + } else { + pos.QuadPart = sector * COOKED_SECTOR_SIZE; + if (plat_cdrom_open()) + return 0; + SetFilePointer(hIOCTL, pos.LowPart, &pos.HighPart, FILE_BEGIN); + ret = ReadFile(hIOCTL, buffer, COOKED_SECTOR_SIZE, &byteCount, NULL); + plat_cdrom_exit(); + pclog("plat_cdrom_read_sector(): ret = %x.\n", !ret); + return !ret; + } + return 0; +} + +uint32_t +plat_cdrom_get_capacity(void) +{ + DWORD size; + int c; + DISK_GEOMETRY dgCDROM; + uint32_t totals; + + if (plat_cdrom_open()) + return 0; + DeviceIoControl(hIOCTL, IOCTL_CDROM_GET_DRIVE_GEOMETRY, NULL, 0, &dgCDROM, sizeof(DISK_GEOMETRY), &size, NULL); + plat_cdrom_exit(); + + totals = dgCDROM.SectorsPerTrack * dgCDROM.TracksPerCylinder * dgCDROM.Cylinders.QuadPart; + + pclog("Total = %08x.\n", totals); + return totals; +} + +int +plat_cdrom_load(void) +{ + int ret; + DWORD size; + + if (plat_cdrom_open()) + return 0; + DeviceIoControl(hIOCTL, IOCTL_STORAGE_LOAD_MEDIA, NULL, 0, NULL, 0, &size, NULL); + plat_cdrom_exit(); + return 1; +} + +int +plat_cdrom_eject(void) +{ + int ret; + DWORD size; + + if (plat_cdrom_open()) + return 0; + ret = DeviceIoControl(hIOCTL, IOCTL_STORAGE_EJECT_MEDIA, NULL, 0, NULL, 0, &size, NULL); + plat_cdrom_exit(); + return ret; +} + +void +plat_cdrom_exit(void) +{ + if (hIOCTL) { + CloseHandle(hIOCTL); + hIOCTL = NULL; + } +} + +void +plat_cdrom_close(void) +{ + plat_cdrom_exit(); +} + +int +plat_cdrom_reset(void) +{ + CDROM_TOC ltoc; + DWORD size; + + if (plat_cdrom_open()) + return 0; + DeviceIoControl(hIOCTL, IOCTL_CDROM_READ_TOC, NULL, 0, <oc, sizeof(ltoc), &size, NULL); + plat_cdrom_exit(); + + toc = ltoc; + return 1; +} + +int +plat_cdrom_open(void) +{ + plat_cdrom_exit(); + hIOCTL = CreateFile((LPCWSTR)ioctl_path, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (hIOCTL == NULL) + return 1; + + return 0; +}