pcem/src/cdrom-ioctl-osx.c
2018-03-06 21:59:22 +00:00

779 lines
23 KiB
C

#include <fcntl.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include "ibm.h"
#include "ide.h"
#include "cdrom-ioctl.h"
#include <util.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/IOBSD.h>
#include <IOKit/storage/IOCDMedia.h>
#include <IOKit/storage/IOMedia.h>
#include <IOKit/storage/IOCDTypes.h>
#include <IOKit/storage/IOCDMediaBSDClient.h>
#include <IOKit/storage/IOMedia.h>
#include <errno.h>
static ATAPI ioctl_atapi;
int old_cdrom_drive;
static uint32_t cdrom_capacity = 0;
static int tocvalid = 0;
static int toc_tracks;
static int first_track, last_track;
static CDTOC *toc = NULL;
static CDDiscInfo disc_info;
static int track_addr[256];
static int msf_to_lba(CDMSF *msf)
{
return msf->frame + (msf->second * 75) + (msf->minute * 60 * 75);
}
enum
{
CD_STOPPED = 0,
CD_PLAYING,
CD_PAUSED
};
static int ioctl_cd_state = CD_STOPPED;
static uint32_t ioctl_cd_pos = 0, ioctl_cd_end = 0;
#define BUF_SIZE 32768
static int16_t cd_buffer[BUF_SIZE];
static int cd_buflen = 0;
static int cd_drive = -1;
static int cd_open()
{
char s[80];
if (cd_drive < 0)
return 0;
sprintf(s, "disk%i", cd_drive);
return opendev(s, O_RDONLY, 0, NULL);
}
static void cd_close(int fd)
{
close(fd);
}
void ioctl_audio_callback(int16_t *output, int len)
{
int ioctl_fd;
// pclog("Audio callback %08X %08X %i %i %i %04X %i\n", ioctl_cd_pos, ioctl_cd_end, ioctl_cd_state, cd_buflen, len, cd_buffer[4], GetTickCount());
if (ioctl_cd_state != CD_PLAYING)
{
memset(output, 0, len * 2);
return;
}
ioctl_fd = cd_open();
if (ioctl_fd <= 0)
{
memset(output, 0, len * 2);
return;
}
while (cd_buflen < len)
{
if (ioctl_cd_pos < ioctl_cd_end)
{
dk_cd_read_t cd_read;
bzero(&cd_read, sizeof(cd_read));
cd_read.offset = (ioctl_cd_pos-150)*kCDSectorSizeCDDA;
cd_read.buffer = &cd_buffer[cd_buflen];
cd_read.bufferLength = kCDSectorSizeCDDA;
cd_read.sectorType = kCDSectorTypeCDDA;
cd_read.sectorArea = kCDSectorAreaUser;
//pclog("CDDA read %llx %d %d\n", cd_read.offset, cd_read.bufferLength, ioctl_cd_pos);
if (ioctl(ioctl_fd, DKIOCCDREAD, &cd_read) < 0)
{
//pclog("DeviceIoControl returned false %d\n", errno);
memset(&cd_buffer[cd_buflen], 0, (BUF_SIZE - cd_buflen) * 2);
ioctl_cd_state = CD_STOPPED;
cd_buflen = len;
}
else
{
//pclog("DeviceIoControl returned true\n");
ioctl_cd_pos++;
cd_buflen += (2352 / 2);
}
}
else
{
memset(&cd_buffer[cd_buflen], 0, (BUF_SIZE - cd_buflen) * 2);
ioctl_cd_state = CD_STOPPED;
cd_buflen = len;
}
}
cd_close(ioctl_fd);
memcpy(output, cd_buffer, len * 2);
// for (c = 0; c < BUF_SIZE - len; c++)
// cd_buffer[c] = cd_buffer[c + cd_buflen];
memcpy(&cd_buffer[0], &cd_buffer[len], (BUF_SIZE - len) * 2);
cd_buflen -= len;
// pclog("Done %i\n", GetTickCount());
}
void ioctl_audio_stop()
{
ioctl_cd_state = CD_STOPPED;
}
static int get_track_nr(uint32_t pos)
{
int c;
int track = 0;
if (!tocvalid)
return 0;
for (c = first_track; c < last_track; c++)
{
uint32_t track_address = toc->descriptors[c].p.frame +
(toc->descriptors[c].p.second * 75) +
(toc->descriptors[c].p.minute * 75 * 60);
//pclog("get_track_nr: track=%i pos=%x track_address=%x\n", c, pos, track_address);
if (track_address <= pos)
track = c;
}
return track;
}
static int is_track_audio(uint32_t pos)
{
int c;
int control = 0;
//pclog("is_track_audio: %08x %i\n", pos, tocvalid);
if (!tocvalid)
return 0;
//pclog(" scan tracks %i-%i\n", first_track, last_track);
for (c = first_track; c < last_track; c++)
{
uint32_t track_address = toc->descriptors[c].p.frame +
(toc->descriptors[c].p.second * 75) +
(toc->descriptors[c].p.minute * 75 * 60);
//pclog("get_track_nr: track=%i pos=%x track_address=%x\n", c, pos, track_address);
if (track_address <= pos)
control = toc->descriptors[c].control;
}
return (control & 4) ? 0 : 1;
}
static int ioctl_is_track_audio(uint32_t pos, int ismsf)
{
if (ismsf)
{
CDMSF msf;
msf.minute = (pos >> 16) & 0xff;
msf.second = (pos >> 8) & 0xff;
msf.frame = pos & 0xff;
pos = msf_to_lba(&msf);
}
return is_track_audio(pos);
}
static void ioctl_playaudio(uint32_t pos, uint32_t len, int ismsf)
{
//pclog("Play audio - %08X %08X %i\n", pos, len, ismsf);
if (ismsf)
{
pos = (pos & 0xff) + (((pos >> 8) & 0xff) * 75) + (((pos >> 16) & 0xff) * 75 * 60);
len = (len & 0xff) + (((len >> 8) & 0xff) * 75) + (((len >> 16) & 0xff) * 75 * 60);
//pclog("MSF - pos = %08X len = %08X\n", pos, len);
}
else
len += pos;
ioctl_cd_pos = pos;// + 150;
ioctl_cd_end = pos+len;// + 150;
ioctl_cd_state = CD_PLAYING;
if (ioctl_cd_pos < 150)
ioctl_cd_pos = 150;
// pclog("Audio start %08X %08X %i %i %i\n", ioctl_cd_pos, ioctl_cd_end, ioctl_cd_state, 0, len);
}
static void ioctl_pause(void)
{
if (ioctl_cd_state == CD_PLAYING)
ioctl_cd_state = CD_PAUSED;
}
static void ioctl_resume(void)
{
if (ioctl_cd_state == CD_PAUSED)
ioctl_cd_state = CD_PLAYING;
}
static void ioctl_stop(void)
{
ioctl_cd_state = CD_STOPPED;
}
static void ioctl_seek(uint32_t pos)
{
// pclog("Seek %08X\n", pos);
ioctl_cd_pos = pos;
ioctl_cd_state = CD_STOPPED;
}
static int read_toc(int fd)
{
dk_cd_read_toc_t cd_read_toc;
dk_cd_read_disc_info_t cd_read_disc_info;
int result;
int c;
bzero(&cd_read_toc, sizeof(cd_read_toc));
bzero(&cd_read_disc_info, sizeof(cd_read_disc_info));
bzero(toc, sizeof(CDTOC) + sizeof(CDTOCDescriptor)*100);
cd_read_toc.format = kCDTOCFormatTOC;
cd_read_toc.buffer = toc;
cd_read_toc.bufferLength = 2048;//sizeof(CDTOC) + sizeof(CDTOCDescriptor)*100;
//pclog("buffer=%p bufferLength=%u\n", cd_read_toc.buffer, cd_read_toc.bufferLength);
result = ioctl(fd, DKIOCCDREADTOC, &cd_read_toc);
//pclog("Read_toc - fd=%d result=%d errno=%d toc=%p\n", fd, result, errno, toc);
if (result < 0)
{
//pclog("read_toc: DKIOCCDREADTOC failed\n");
return 0;
}
//pclog("toc length=%u first=%u last=%u\n", toc->length, toc->sessionFirst, toc->sessionFirst);
first_track = 1;
last_track = 0;
toc_tracks = CDTOCGetDescriptorCount(toc);
for (c = 0; c < toc_tracks; c++)
{
if (toc->descriptors[c].session > 1)
continue;
track_addr[c] = msf_to_lba(&toc->descriptors[c].p);
//pclog("Track %i %02x addr %08x\n", c, toc->descriptors[c].point, track_addr[c]);
if (toc->descriptors[c].point <= 99 && toc->descriptors[c].adr == 1)
last_track++;
}
//pclog("Tracks %i %i %i\n", first_track, last_track, toc_tracks);
cd_read_disc_info.buffer = &disc_info;
cd_read_disc_info.bufferLength = sizeof(disc_info);
result = ioctl(fd, DKIOCCDREADDISCINFO, &cd_read_disc_info);
if (result < 0)
{
//pclog("read_toc: DKIOCCDREADDISCINFO failed\n");
return 0;
}
return 1;
}
static int ioctl_ready(void)
{
int ioctl_fd = cd_open();
//pclog("ioctl_ready - %d %d\n", ioctl_fd, tocvalid);
if (ioctl_fd <= 0)
return 0;
cd_close(ioctl_fd);
if (tocvalid)
return 1;
return 0;
}
static int ioctl_get_last_block(unsigned char starttrack, int msf, int maxlen, int single)
{
int c;
int lb = 0;
int tv = 0;
int ioctl_fd;
ioctl_fd = cd_open();
if (ioctl_fd <= 0)
return 0;
//ioctl_cd_state = CD_STOPPED;
if (!tocvalid)
tv = read_toc(ioctl_fd);
cd_close(ioctl_fd);
if (!tv)
return 0;
for (c = 0; c <= toc_tracks; c++)
{
if (track_addr[c] > lb)
lb = track_addr[c];
}
return lb;
}
static int ioctl_medium_changed(void)
{
dk_cd_read_disc_info_t cd_read_disc_info;
CDDiscInfo new_disc_info;
int result;
int ioctl_fd;
ioctl_fd = cd_open();
if (ioctl_fd <= 0)
return 1;
bzero(&cd_read_disc_info, sizeof(cd_read_disc_info));
cd_read_disc_info.buffer = &new_disc_info;
cd_read_disc_info.bufferLength = sizeof(new_disc_info);
result = ioctl(ioctl_fd, DKIOCCDREADDISCINFO, &cd_read_disc_info);
if (result < 0)
{
//pclog("medium_changed: DKIOCCDREADDISCINFO failed\n");
ioctl_cd_state = CD_STOPPED;
tocvalid = 0;
cd_close(ioctl_fd);
return 1;
}
if (memcmp(&disc_info, &new_disc_info, sizeof(CDDiscInfo)))
{
//pclog("medium_changed: disc changed\n");
ioctl_cd_state = CD_STOPPED;
tocvalid = read_toc(ioctl_fd);
cd_close(ioctl_fd);
return 1;
}
cd_close(ioctl_fd);
return 0;
}
static uint8_t ioctl_getcurrentsubchannel(uint8_t *b, int msf)
{
uint32_t cdpos = ioctl_cd_pos;
int track = get_track_nr(cdpos);
uint32_t track_address = track_addr[track];
long size;
int pos=0;
int err;
uint8_t ret;
if (ioctl_cd_state == CD_PLAYING)
ret = 0x11;
else if (ioctl_cd_state == CD_PAUSED)
ret = 0x12;
else
ret = 0x13;
b[pos++] = (toc->descriptors[track].adr << 4) | toc->descriptors[track].control;
b[pos++] = toc->descriptors[track].point;
b[pos++] = 0;
if (msf)
{
uint32_t dat = cdpos;
b[pos + 3] = (uint8_t)(dat % 75); dat /= 75;
b[pos + 2] = (uint8_t)(dat % 60); dat /= 60;
b[pos + 1] = (uint8_t)dat;
b[pos] = 0;
pos += 4;
dat = cdpos - track_address;
b[pos + 3] = (uint8_t)(dat % 75); dat /= 75;
b[pos + 2] = (uint8_t)(dat % 60); dat /= 60;
b[pos + 1] = (uint8_t)dat;
b[pos] = 0;
pos += 4;
}
else
{
b[pos++] = (cdpos >> 24) & 0xff;
b[pos++] = (cdpos >> 16) & 0xff;
b[pos++] = (cdpos >> 8) & 0xff;
b[pos++] = cdpos & 0xff;
cdpos -= track_address;
b[pos++] = (cdpos >> 24) & 0xff;
b[pos++] = (cdpos >> 16) & 0xff;
b[pos++] = (cdpos >> 8) & 0xff;
b[pos++] = cdpos & 0xff;
}
//pclog("ioctl_getsubchannel: cdpos=%x track_address=%x track=%i state=%i ret=%02x\n", cdpos, track_address, track, ioctl_cd_state, ret);
return ret;
}
static void ioctl_eject(void)
{
}
static void ioctl_load(void)
{
}
static int ioctl_readsector(uint8_t *b, int sector, int count)
{
dk_cd_read_t cd_read;
int res;
int ioctl_fd;
ioctl_fd = cd_open();
if (ioctl_fd <= 0)
return -1;
//pclog("Read sector %x %i\n", sector, count);
bzero(&cd_read, sizeof(cd_read));
cd_read.offset = sector*2048;
cd_read.buffer = b;
cd_read.bufferLength = count*2048;
cd_read.sectorType = kCDSectorTypeMode1;
cd_read.sectorArea = kCDSectorAreaUser;
res = ioctl(ioctl_fd, DKIOCCDREAD, &cd_read);
//pclog("read res = %i %i %i\n", res, cd_read.bufferLength, errno);
cd_close(ioctl_fd);
return 0;
}
static void ioctl_readsector_raw(uint8_t *b, int sector)
{
dk_cd_read_t cd_read;
int res;
int ioctl_fd;
ioctl_fd = cd_open();
if (ioctl_fd <= 0)
return;
//pclog("Read sector %x %i\n", sector, count);
bzero(&cd_read, sizeof(cd_read));
cd_read.offset = sector*kCDSectorSizeWhole;
cd_read.buffer = b;
cd_read.bufferLength = kCDSectorSizeWhole;
cd_read.sectorType = kCDSectorTypeUnknown;
cd_read.sectorArea = kCDSectorAreaUser;
res = ioctl(ioctl_fd, DKIOCCDREAD, &cd_read);
//pclog("read res = %i %i %i\n", res, cd_read.bufferLength, errno);
cd_close(ioctl_fd);
}
static int ioctl_readtoc(unsigned char *b, unsigned char starttrack, int msf, int maxlen, int single)
{
int len=4;
int c,d;
uint32_t temp;
uint32_t last_address = 0;
int seenAA = 0;
pclog("ioctl_readtoc\n");
//ioctl_cd_state = CD_STOPPED;
if (!tocvalid)
{
int ioctl_fd = cd_open();
if (ioctl_fd <= 0)
return 4;
tocvalid = read_toc(ioctl_fd);
cd_close(ioctl_fd);
}
if (!tocvalid)
return 4;
//pclog("Read TOC done! %i\n",single);
b[2] = first_track;
b[3] = last_track;
d = 0;
//pclog("Read TOC starttrack=%i\n", starttrack);
for (c = 0; c <= toc_tracks; c++)
{
if (toc->descriptors[c].point && toc->descriptors[c].point >= starttrack && toc->descriptors[c].point <= 99)
{
d = c;
break;
}
}
//pclog("Start from track %i\n",d);
b[2] = toc->descriptors[c].point;
//last_block = 0;
for (c = d; c <= toc_tracks; c++)
{
uint32_t address;
if ((len + 8) > maxlen)
break;
//pclog("Len %i max %i Track %02X - %02X %02X %02i:%02i:%02i %08X\n",len,maxlen,toc->descriptors[c].point,toc->descriptors[c].adr,toc->descriptors[c].control,toc->descriptors[c].p.minute, toc->descriptors[c].p.second, toc->descriptors[c].p.frame,track_addr[c]);
address = track_addr[c];
if (address < last_address)
continue;
if (toc->descriptors[c].point == 0xaa)
seenAA = 1;
last_address = address;
b[len++] = 0; /*Reserved*/
b[len++] = (toc->descriptors[c].adr << 4) | toc->descriptors[c].control;
b[len++] = toc->descriptors[c].point;
b[len++] = 0; /*Reserved*/
//if (address > last_block)
// last_block = address;
if (msf)
{
b[len++] = 0;
b[len++] = toc->descriptors[c].p.minute;
b[len++] = toc->descriptors[c].p.second;
b[len++] = toc->descriptors[c].p.frame;
}
else
{
temp = track_addr[c];
b[len++] = temp >> 24;
b[len++] = temp >> 16;
b[len++] = temp >> 8;
b[len++] = temp;
}
if (single)
break;
}
if (!seenAA && !single)
{
uint32_t lb = 0;
for (c = 0; c <= toc_tracks; c++)
{
if (track_addr[c] > lb)
lb = track_addr[c];
}
//pclog("Track AA - %08x %i\n", lb, len);
b[len++] = 0; /*Reserved*/
b[len++] = (1 << 4) | 1;
b[len++] = 0xaa;
b[len++] = 0; /*Reserved*/
if (msf)
{
b[len++] = 0;
b[len++] = (lb / 60) / 75;
b[len++] = (lb / 75) % 60;
b[len++] = lb % 75;
}
else
{
temp = track_addr[c];
b[len++] = lb >> 24;
b[len++] = lb >> 16;
b[len++] = lb >> 8;
b[len++] = lb;
}
}
b[0] = (uint8_t)(((len-2) >> 8) & 0xff);
b[1] = (uint8_t)((len-2) & 0xff);
/* pclog("Table of Contents (%i bytes) : \n", size);
pclog("First track - %02X\n", first_track);
pclog("Last track - %02X\n", last_track);
for (c = 0; c <= last_track; c++)
pclog("Track %02X - number %02X control %02X adr %02X address %02X %02X %02X %02X\n", c, toc[c].cdte_track, toc[c].cdte_ctrl, toc[c].cdte_adr, 0, toc[c].cdte_addr.msf.minute, toc[c].cdte_addr.msf.second, toc[c].cdte_addr.msf.frame);
for (c = 0;c <= last_track; c++)
pclog("Track %02X - number %02X control %02X adr %02X address %06X\n", c, toc[c].cdte_track, toc[c].cdte_ctrl, toc[c].cdte_adr, MSFtoLBA(toc[c].cdte_addr.msf.minute, toc[c].cdte_addr.msf.second, toc[c].cdte_addr.msf.frame));*/
return len;
}
static int ioctl_readtoc_session(unsigned char *b, int msf, int maxlen)
{
int len = 4;
int session = 0, first_track_nr = 0;
int c, d;
if (!tocvalid)
{
int ioctl_fd = cd_open();
if (ioctl_fd <= 0)
return 4;
tocvalid = read_toc(ioctl_fd);
cd_close(ioctl_fd);
}
if (!tocvalid)
return 4;
for (c = 0; c <= toc_tracks; c++)
{
if (toc->descriptors[c].point && toc->descriptors[c].point <= 99)
{
if (toc->descriptors[c].session > session || (toc->descriptors[c].session == session && toc->descriptors[c].point < first_track_nr))
{
session = toc->descriptors[c].session;
first_track_nr = toc->descriptors[c].point;
d = c;
}
}
}
b[2] = toc->sessionFirst;
b[3] = toc->sessionLast;
b[len++] = 0; /*Reserved*/
b[len++] = (toc->descriptors[d].adr << 4) | toc->descriptors[d].control;
b[len++] = toc->descriptors[d].point;
b[len++] = 0; /*Reserved*/
if (msf)
{
b[len++] = 0;
b[len++] = toc->descriptors[d].p.minute;
b[len++] = toc->descriptors[d].p.second;
b[len++] = toc->descriptors[d].p.frame;
}
else
{
uint32_t temp = track_addr[d];
b[len++] = temp >> 24;
b[len++] = temp >> 16;
b[len++] = temp >> 8;
b[len++] = temp;
}
return len;
}
static int ioctl_readtoc_raw(unsigned char *b, int maxlen)
{
int len = 4;
int ioctl_fd;
int track;
//pclog("read_toc\n");
if (!tocvalid)
return 0;
ioctl_fd = cd_open();
if (ioctl_fd <= 0)
return 0;
cd_close(ioctl_fd);
b[2] = toc->sessionFirst;
b[3] = toc->sessionLast;
for (track = 0; track <= toc_tracks; track++)
{
if ((len + 11) > maxlen)
{
pclog("ioctl_readtocraw: This iteration would fill the buffer beyond the bounds, aborting...\n");
return len;
}
// pclog("read_toc: Track %02X - number %02X control %02X adr %02X address %02X %02X %02X %02X\n", track, toc[track].cdte_track, toc[track].cdte_ctrl, toc[track].cdte_adr, 0, toc[track].cdte_addr.msf.minute, toc[track].cdte_addr.msf.second, toc[track].cdte_addr.msf.frame);
b[len++] = toc->descriptors[track].session;
b[len++] = (toc->descriptors[track].adr << 4) | toc->descriptors[track].control;
b[len++] = toc->descriptors[track].tno;
b[len++] = toc->descriptors[track].point;
b[len++] = toc->descriptors[track].address.minute;
b[len++] = toc->descriptors[track].address.second;
b[len++] = toc->descriptors[track].address.frame;
b[len++] = toc->descriptors[track].zero;
b[len++] = toc->descriptors[track].p.minute;
b[len++] = toc->descriptors[track].p.second;
b[len++] = toc->descriptors[track].p.frame;
}
return len;
}
static uint32_t ioctl_size()
{
return cdrom_capacity;
}
static int ioctl_status()
{
if (!ioctl_ready)
return CD_STATUS_EMPTY;
return CD_STATUS_STOPPED;
}
void ioctl_reset()
{
int ioctl_fd;
tocvalid = 0;
ioctl_fd = cd_open();
if (ioctl_fd <= 0)
return;
tocvalid = read_toc(ioctl_fd);
cd_close(ioctl_fd);
}
void ioctl_set_drive(char d)
{
ioctl_close();
atapi=&ioctl_atapi;
ioctl_open(d);
}
int ioctl_open(char d)
{
atapi = &ioctl_atapi;
toc = malloc(2048);
cd_drive = d;
return 0;
}
void ioctl_close(void)
{
if (toc)
{
free(toc);
toc = NULL;
}
}
static void ioctl_exit(void)
{
}
static ATAPI ioctl_atapi=
{
ioctl_ready,
ioctl_medium_changed,
ioctl_readtoc,
ioctl_readtoc_session,
ioctl_readtoc_raw,
ioctl_getcurrentsubchannel,
ioctl_readsector,
ioctl_readsector_raw,
ioctl_playaudio,
ioctl_seek,
ioctl_load,
ioctl_eject,
ioctl_pause,
ioctl_resume,
ioctl_size,
ioctl_status,
ioctl_is_track_audio,
ioctl_stop,
ioctl_exit
};