diff --git a/src/cdrom-ioctl-linux.c b/src/cdrom-ioctl-linux.c index aad53aa..e4de21e 100644 --- a/src/cdrom-ioctl-linux.c +++ b/src/cdrom-ioctl-linux.c @@ -115,6 +115,38 @@ static int get_track_nr(uint32_t pos) return track; } +static int is_track_audio(uint32_t pos) +{ + int c; + int control = 0; + + if (!tocvalid) + return 0; + + for (c = first_track; c < last_track; c++) + { + uint32_t track_address = toc[c].cdte_addr.msf.frame + + (toc[c].cdte_addr.msf.second * 75) + + (toc[c].cdte_addr.msf.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[c].cdte_ctrl; + } + return (control & 4) ? 0 : 1; +} + +static int ioctl_is_track_audio(uint32_t pos, int ismsf) +{ + if (ismsf) + { + int m = (pos >> 16) & 0xff; + int s = (pos >> 8) & 0xff; + int f = pos & 0xff; + pos = MSFtoLBA(m, s, f); + } + 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); @@ -226,12 +258,48 @@ static int ioctl_ready(void) tocvalid = read_toc(fd); close(fd); - return 0; + return 1; } close(fd); return 1; } +static int ioctl_medium_changed(void) +{ + long size; + int temp; + struct cdrom_tochdr toc_hdr; + struct cdrom_tocentry toc_entry; + int err; + int fd = open("/dev/cdrom", O_RDONLY|O_NONBLOCK); + + if (fd <= 0) + return 0; + + err = ioctl(fd, CDROMREADTOCHDR, &toc_hdr); + if (err == -1) + { + close(fd); + return 0; + } + toc_entry.cdte_track = toc_hdr.cdth_trk1; + toc_entry.cdte_format = CDROM_MSF; + err = ioctl(fd, CDROMREADTOCENTRY, &toc_entry); + if (err == -1) + { + close(fd); + return 0; + } +// pclog("CDROMREADTOCENTRY: addr=%02i:%02i:%02i\n", toc_entry.cdte_addr.msf.minute, toc_entry.cdte_addr.msf.second, toc_entry.cdte_addr.msf.frame); + if ((toc_entry.cdte_addr.msf.minute != toc[toc_hdr.cdth_trk1].cdte_addr.msf.minute) || + (toc_entry.cdte_addr.msf.second != toc[toc_hdr.cdth_trk1].cdte_addr.msf.second) || + (toc_entry.cdte_addr.msf.frame != toc[toc_hdr.cdth_trk1].cdte_addr.msf.frame )) + { + return 1; + } + return 0; +} + static uint8_t ioctl_getcurrentsubchannel(uint8_t *b, int msf) { struct cdrom_subchnl sub; @@ -321,6 +389,35 @@ static void ioctl_readsector(uint8_t *b, int sector) close(cdrom); } +static void lba_to_msf(uint8_t *buf, int lba) +{ + lba += 150; + buf[0] = (lba / 75) / 60; + buf[1] = (lba / 75) % 60; + buf[2] = lba % 75; +} + +static void ioctl_readsector_raw(uint8_t *b, int sector) +{ + int cdrom = open("/dev/cdrom", O_RDONLY|O_NONBLOCK); + if (cdrom <= 0) + return; + lseek(cdrom, sector*2048, SEEK_SET); + read(cdrom, b+16, 2048); + close(cdrom); + + /* sync bytes */ + b[0] = 0; + memset(b + 1, 0xff, 10); + b[11] = 0; + b += 12; + lba_to_msf(b, sector); + b[3] = 1; /* mode 1 data */ + b += 4; + b += 2048; + memset(b, 0, 288); +} + static int ioctl_readtoc(unsigned char *b, unsigned char starttrack, int msf, int maxlen, int single) { int len=4; @@ -400,7 +497,7 @@ static int ioctl_readtoc(unsigned char *b, unsigned char starttrack, int msf, in return len; } -static void ioctl_readtoc_session(unsigned char *b, int msf, int maxlen) +static int ioctl_readtoc_session(unsigned char *b, int msf, int maxlen) { struct cdrom_multisession session; int len = 4; @@ -440,6 +537,61 @@ static void ioctl_readtoc_session(unsigned char *b, int msf, int maxlen) b[len++] = temp >> 8; b[len++] = temp; } + + return len; +} + +static int ioctl_readtoc_raw(unsigned char *b, int maxlen) +{ + struct cdrom_tochdr toc_hdr; + struct cdrom_tocentry toc2[100]; + int track, err; + int len = 4; +//pclog("read_toc\n"); + err = ioctl(fd, CDROMREADTOCHDR, &toc_hdr); + if (err == -1) + { + pclog("read_toc: CDROMREADTOCHDR failed\n"); + return 0; + } + + b[2] = toc_hdr.cdth_trk0; + b[3] = toc_hdr.cdth_trk1; + + //pclog("read_toc: first_track=%i last_track=%i\n", first_track, last_track); + memset(toc, 0, sizeof(toc)); + + for (track = toc_hdr.cdth_trk0; track <= toc_hdr.cdth_trk1; track++) + { + if ((len + 11) > maxlen) + { + pclog("ioctl_readtocraw: This iteration would fill the buffer beyond the bounds, aborting...\n"); + return len; + } + + toc2[track].cdte_track = track; + toc2[track].cdte_format = CDROM_MSF; + err = ioctl(fd, CDROMREADTOCENTRY, &toc2[track]); + if (err == -1) + { +// pclog("read_toc: CDROMREADTOCENTRY failed on track %i\n", track); + return 0; + } +// 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++] = toc2[track].cdte_track; + b[len++]= (toc2[track].cdte_adr << 4) | toc[track].cdte_ctrl; + b[len++]=0; + b[len++]=0; + b[len++]=0; + b[len++]=0; + b[len++]=0; + b[len++]=0; + b[len++] = toc2[track].cdte_addr.msf.minute; + b[len++] = toc2[track].cdte_addr.msf.second; + b[len++] = toc2[track].cdte_addr.msf.frame; + } + return len; } static uint32_t ioctl_size() @@ -451,6 +603,20 @@ static uint32_t ioctl_size() return last_block; } +static int ioctl_status() +{ + if (!(ioctl_ready) && (cdrom_drive <= 0)) return CD_STATUS_EMPTY; + + switch(ioctl_cd_state) + { + case CD_PLAYING: + return CD_STATUS_PLAYING; + case CD_PAUSED: + return CD_STATUS_PAUSED; + case CD_STOPPED: + return CD_STATUS_STOPPED; + } +} void ioctl_reset() { int fd = open("/dev/cdrom", O_RDONLY|O_NONBLOCK); @@ -485,10 +651,13 @@ 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, @@ -496,6 +665,8 @@ static ATAPI ioctl_atapi= ioctl_pause, ioctl_resume, ioctl_size, + ioctl_status, + ioctl_is_track_audio, ioctl_stop, ioctl_exit }; diff --git a/src/cdrom-ioctl.c b/src/cdrom-ioctl.c index fcccd7d..190aac3 100644 --- a/src/cdrom-ioctl.c +++ b/src/cdrom-ioctl.c @@ -12,6 +12,7 @@ #include "cdrom-ioctl.h" int cdrom_drive; +int old_cdrom_drive; #ifndef __MINGW64__ typedef struct _CDROM_TOC_SESSION_DATA { @@ -27,7 +28,7 @@ static ATAPI ioctl_atapi; static uint32_t last_block = 0; static int ioctl_inited = 0; static char ioctl_path[8]; -static void ioctl_close(void); +void ioctl_close(void); static HANDLE hIOCTL; static CDROM_TOC toc; static int tocvalid = 0; @@ -233,11 +234,14 @@ static int ioctl_ready(void) ioctl_close(); if (!temp) return 0; + //pclog("ioctl_ready(): Drive opened successfully\n"); + //if ((cdrom_drive != old_cdrom_drive)) pclog("Drive has changed\n"); if ((ltoc.TrackData[ltoc.LastTrack].Address[1] != toc.TrackData[toc.LastTrack].Address[1]) || (ltoc.TrackData[ltoc.LastTrack].Address[2] != toc.TrackData[toc.LastTrack].Address[2]) || (ltoc.TrackData[ltoc.LastTrack].Address[3] != toc.TrackData[toc.LastTrack].Address[3]) || - !tocvalid) + !tocvalid || (cdrom_drive != old_cdrom_drive)) { + //pclog("ioctl_ready(): Disc or drive changed\n"); ioctl_cd_state = CD_STOPPED; /* pclog("Not ready %02X %02X %02X %02X %02X %02X %i\n",ltoc.TrackData[ltoc.LastTrack].Address[1],ltoc.TrackData[ltoc.LastTrack].Address[2],ltoc.TrackData[ltoc.LastTrack].Address[3], toc.TrackData[ltoc.LastTrack].Address[1], toc.TrackData[ltoc.LastTrack].Address[2], toc.TrackData[ltoc.LastTrack].Address[3],tocvalid);*/ @@ -245,13 +249,44 @@ static int ioctl_ready(void) /* ioctl_open(0); temp=DeviceIoControl(hIOCTL,IOCTL_CDROM_READ_TOC, NULL,0,&toc,sizeof(toc),&size,NULL); ioctl_close();*/ - toc=ltoc; - tocvalid=1; - return 0; + if (cdrom_drive != old_cdrom_drive) + old_cdrom_drive = cdrom_drive; + return 1; } // pclog("IOCTL says ready\n"); +// pclog("ioctl_ready(): All is good\n"); return 1; -// return (temp)?1:0; +} + +static int ioctl_medium_changed(void) +{ + long size; + int temp; + CDROM_TOC ltoc; + if (!cdrom_drive) return 0; /* This will be handled by the not ready handler instead. */ + ioctl_open(0); + temp=DeviceIoControl(hIOCTL,IOCTL_CDROM_READ_TOC, NULL,0,<oc,sizeof(ltoc),&size,NULL); + ioctl_close(); + if (!temp) + return 0; /* Drive empty, a not ready handler matter, not disc change. */ + if (!tocvalid || (cdrom_drive != old_cdrom_drive)) + { + ioctl_cd_state = CD_STOPPED; + toc = ltoc; + tocvalid = 1; + if (cdrom_drive != old_cdrom_drive) + old_cdrom_drive = cdrom_drive; + return 0; + } + if ((ltoc.TrackData[ltoc.LastTrack].Address[1] != toc.TrackData[toc.LastTrack].Address[1]) || + (ltoc.TrackData[ltoc.LastTrack].Address[2] != toc.TrackData[toc.LastTrack].Address[2]) || + (ltoc.TrackData[ltoc.LastTrack].Address[3] != toc.TrackData[toc.LastTrack].Address[3])) + { + ioctl_cd_state = CD_STOPPED; + toc = ltoc; + return 1; /* TOC mismatches. */ + } + return 0; /* None of the above, return 0. */ } static uint8_t ioctl_getcurrentsubchannel(uint8_t *b, int msf) @@ -365,6 +400,10 @@ static void ioctl_readsector(uint8_t *b, int sector) LARGE_INTEGER pos; long size; if (!cdrom_drive) return; + + if (ioctl_cd_state == CD_PLAYING) + return; + ioctl_cd_state = CD_STOPPED; pos.QuadPart=sector*2048; ioctl_open(0); @@ -373,6 +412,74 @@ static void ioctl_readsector(uint8_t *b, int sector) ioctl_close(); } +static void lba_to_msf(uint8_t *buf, int lba) +{ + lba += 150; + buf[0] = (lba / 75) / 60; + buf[1] = (lba / 75) % 60; + buf[2] = lba % 75; +} + +static int is_track_audio(uint32_t pos) +{ + int c; + int control = 0; + + if (!tocvalid) + return 0; + + for (c = toc.FirstTrack; c < toc.LastTrack; c++) + { + uint32_t track_address = toc.TrackData[c].Address[3] + + (toc.TrackData[c].Address[2] * 75) + + (toc.TrackData[c].Address[1] * 75 * 60); + + if (track_address <= pos) + control = toc.TrackData[c].Control; + } + return (control & 4) ? 0 : 1; +} + +static int ioctl_is_track_audio(uint32_t pos, int ismsf) +{ + if (ismsf) + { + int m = (pos >> 16) & 0xff; + int s = (pos >> 8) & 0xff; + int f = pos & 0xff; + pos = MSFtoLBA(m, s, f); + } + return is_track_audio(pos); +} + +static void ioctl_readsector_raw(uint8_t *b, int sector) +{ + LARGE_INTEGER pos; + long size; + uint32_t temp; + if (!cdrom_drive) return; + if (ioctl_cd_state == CD_PLAYING) + return; + + ioctl_cd_state = CD_STOPPED; + pos.QuadPart=sector*2048; + ioctl_open(0); + SetFilePointer(hIOCTL,pos.LowPart,&pos.HighPart,FILE_BEGIN); + ReadFile(hIOCTL,b+16,2048,&size,NULL); + ioctl_close(); + + /* sync bytes */ + b[0] = 0; + memset(b + 1, 0xff, 10); + b[11] = 0; + b += 12; + lba_to_msf(b, sector); + b[3] = 1; /* mode 1 data */ + b += 4; + b += 2048; + memset(b, 0, 288); +} + static int ioctl_readtoc(unsigned char *b, unsigned char starttrack, int msf, int maxlen, int single) { int len=4; @@ -441,7 +548,7 @@ static int ioctl_readtoc(unsigned char *b, unsigned char starttrack, int msf, in return len; } -static void ioctl_readtoc_session(unsigned char *b, int msf, int maxlen) +static int ioctl_readtoc_session(unsigned char *b, int msf, int maxlen) { int len=4; int size; @@ -461,25 +568,71 @@ static void ioctl_readtoc_session(unsigned char *b, int msf, int maxlen) // pclog("Read TOC session - %i %02X %02X %i %i %02X %02X %02X\n",size,toc.Length[0],toc.Length[1],toc.FirstCompleteSession,toc.LastCompleteSession,toc.TrackData[0].Adr,toc.TrackData[0].Control,toc.TrackData[0].TrackNumber); b[2]=toc.FirstCompleteSession; b[3]=toc.LastCompleteSession; - b[len++]=0; /*Reserved*/ - b[len++]=(toc.TrackData[0].Adr<<4)|toc.TrackData[0].Control; - b[len++]=toc.TrackData[0].TrackNumber; - b[len++]=0; /*Reserved*/ - if (msf) - { - b[len++]=toc.TrackData[0].Address[0]; - b[len++]=toc.TrackData[0].Address[1]; - b[len++]=toc.TrackData[0].Address[2]; - b[len++]=toc.TrackData[0].Address[3]; - } - else - { - temp=MSFtoLBA(toc.TrackData[0].Address[1],toc.TrackData[0].Address[2],toc.TrackData[0].Address[3]); - b[len++]=temp>>24; - b[len++]=temp>>16; - b[len++]=temp>>8; - b[len++]=temp; - } + b[len++]=0; /*Reserved*/ + b[len++]=(toc.TrackData[0].Adr<<4)|toc.TrackData[0].Control; + b[len++]=toc.TrackData[0].TrackNumber; + b[len++]=0; /*Reserved*/ + if (msf) + { + b[len++]=toc.TrackData[0].Address[0]; + b[len++]=toc.TrackData[0].Address[1]; + b[len++]=toc.TrackData[0].Address[2]; + b[len++]=toc.TrackData[0].Address[3]; + } + else + { + temp=MSFtoLBA(toc.TrackData[0].Address[1],toc.TrackData[0].Address[2],toc.TrackData[0].Address[3]); + 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 size; + uint32_t temp; + int i; + int BytesRead = 0; + CDROM_READ_TOC_EX toc_ex; + CDROM_TOC_FULL_TOC_DATA toc; + if (!cdrom_drive) return 0; + ioctl_cd_state = CD_STOPPED; + memset(&toc_ex,0,sizeof(toc_ex)); + memset(&toc,0,sizeof(toc)); + toc_ex.Format=CDROM_READ_TOC_EX_FORMAT_FULL_TOC; + toc_ex.Msf=1; + toc_ex.SessionTrack=0; + ioctl_open(0); + DeviceIoControl(hIOCTL,IOCTL_CDROM_READ_TOC_EX, &toc_ex,sizeof(toc_ex),&toc,sizeof(toc),(PDWORD)&size,NULL); + ioctl_close(); +// pclog("Read TOC session - %i %02X %02X %i %i %02X %02X %02X\n",size,toc.Length[0],toc.Length[1],toc.FirstCompleteSession,toc.LastCompleteSession,toc.TrackData[0].Adr,toc.TrackData[0].Control,toc.TrackData[0].TrackNumber); + b[2]=toc.FirstCompleteSession; + b[3]=toc.LastCompleteSession; + + size -= sizeof(CDROM_TOC_FULL_TOC_DATA); + size /= sizeof(toc.Descriptors[0]); + + for (i = 0; i <= size; i++) + { + b[len++]=toc.Descriptors[i].SessionNumber; + b[len++]=(toc.Descriptors[i].Adr<<4)|toc.Descriptors[i].Control; + b[len++]=0; + b[len++]=toc.Descriptors[i].Reserved1; /*Reserved*/ + b[len++]=toc.Descriptors[i].MsfExtra[0]; + b[len++]=toc.Descriptors[i].MsfExtra[1]; + b[len++]=toc.Descriptors[i].MsfExtra[2]; + b[len++]=toc.Descriptors[i].Zero; + b[len++]=toc.Descriptors[i].Msf[0]; + b[len++]=toc.Descriptors[i].Msf[1]; + b[len++]=toc.Descriptors[i].Msf[2]; + } + + return len; } static uint32_t ioctl_size() @@ -491,6 +644,22 @@ static uint32_t ioctl_size() return last_block; } +static int ioctl_status() +{ + if (!(ioctl_ready) && (cdrom_drive <= 0)) + return CD_STATUS_EMPTY; + + switch(ioctl_cd_state) + { + case CD_PLAYING: + return CD_STATUS_PLAYING; + case CD_PAUSED: + return CD_STATUS_PAUSED; + case CD_STOPPED: + return CD_STATUS_STOPPED; + } +} + void ioctl_reset() { CDROM_TOC ltoc; @@ -536,7 +705,7 @@ int ioctl_open(char d) return 0; } -static void ioctl_close(void) +void ioctl_close(void) { if (hIOCTL) { @@ -550,16 +719,18 @@ static void ioctl_exit(void) ioctl_stop(); ioctl_inited=0; tocvalid=0; - ioctl_close(); } 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, @@ -567,6 +738,8 @@ static ATAPI ioctl_atapi= ioctl_pause, ioctl_resume, ioctl_size, + ioctl_status, + ioctl_is_track_audio, ioctl_stop, ioctl_exit }; diff --git a/src/cdrom-ioctl.h b/src/cdrom-ioctl.h index d99c101..a040b83 100644 --- a/src/cdrom-ioctl.h +++ b/src/cdrom-ioctl.h @@ -6,5 +6,6 @@ extern int ioctl_open(char d); extern void ioctl_reset(); +extern void ioctl_close(); #endif /* ! CDROM_IOCTL_H */ diff --git a/src/cdrom-iso.c b/src/cdrom-iso.c index f6c0106..2742adf 100644 --- a/src/cdrom-iso.c +++ b/src/cdrom-iso.c @@ -11,6 +11,7 @@ static uint32_t last_block = 0; static uint64_t image_size = 0; static int iso_inited = 0; char iso_path[1024]; +void iso_close(void); static FILE *iso_image = NULL; static int iso_changed = 0; @@ -65,15 +66,23 @@ static void iso_seek(uint32_t pos) static int iso_ready(void) { - if (iso_changed) - { - iso_changed = 0; + if (strlen(iso_path) == 0) return 0; - } + if (old_cdrom_drive != cdrom_drive) + return 1; + + iso_changed = 0; return 1; } +/* Always return 0, because there is no way to change the ISO without unmounting and remounting it. */ +static int iso_medium_changed(void) +{ + iso_changed = 0; + return 0; +} + static uint8_t iso_getcurrentsubchannel(uint8_t *b, int msf) { pclog("iso_getcurrentsubchannel stub\n"); @@ -102,7 +111,26 @@ static void lba_to_msf(uint8_t *buf, int lba) lba += 150; buf[0] = (lba / 75) / 60; buf[1] = (lba / 75) % 60; - buf[2] = lba & 75; + buf[2] = lba % 75; +} + +static void iso_readsector_raw(uint8_t *b, int sector) +{ + uint32_t temp; + if (!cdrom_drive) return; + fseek(iso_image, sector*2048, SEEK_SET); + fread(b+16, 2048, 1, iso_image); + + /* sync bytes */ + b[0] = 0; + memset(b + 1, 0xff, 10); + b[11] = 0; + b += 12; + lba_to_msf(b, sector); + b[3] = 1; /* mode 1 data */ + b += 4; + b += 2048; + memset(b, 0, 288); } static int iso_readtoc(unsigned char *buf, unsigned char start_track, int msf, int maxlen, int single) @@ -161,7 +189,7 @@ static int iso_readtoc(unsigned char *buf, unsigned char start_track, int msf, i return len; } -static void iso_readtoc_session(unsigned char *buf, int msf, int maxlen) +static int iso_readtoc_session(unsigned char *buf, int msf, int maxlen) { uint8_t *q; @@ -177,6 +205,73 @@ static void iso_readtoc_session(unsigned char *buf, int msf, int maxlen) *q++ = 0; /* sec */ *q++ = 0; /* frame */ *q++ = 0; + + return 12; +} + +static int iso_readtoc_raw(unsigned char *buf, int maxlen) +{ + uint8_t *q; + int len; + + q = buf + 2; + *q++ = 1; /* first session */ + *q++ = 1; /* last session */ + + *q++ = 1; /* session number */ + *q++ = 0x14; /* data track */ + *q++ = 0; /* track number */ + *q++ = 0xa0; /* lead-in */ + *q++ = 0; /* min */ + *q++ = 0; /* sec */ + *q++ = 0; /* frame */ + *q++ = 0; + *q++ = 1; /* first track */ + *q++ = 0x00; /* disk type */ + *q++ = 0x00; + + *q++ = 1; /* session number */ + *q++ = 0x14; /* data track */ + *q++ = 0; /* track number */ + *q++ = 0xa1; + *q++ = 0; /* min */ + *q++ = 0; /* sec */ + *q++ = 0; /* frame */ + *q++ = 0; + *q++ = 1; /* last track */ + *q++ = 0x00; + *q++ = 0x00; + + *q++ = 1; /* session number */ + *q++ = 0x14; /* data track */ + *q++ = 0; /* track number */ + *q++ = 0xa2; /* lead-out */ + *q++ = 0; /* min */ + *q++ = 0; /* sec */ + *q++ = 0; /* frame */ + last_block = image_size >> 11; + /* this is raw, must be msf */ + *q++ = 0; /* reserved */ + lba_to_msf(q, last_block); + q += 3; + + *q++ = 1; /* session number */ + *q++ = 0x14; /* ADR, control */ + *q++ = 0; /* track number */ + *q++ = 1; /* point */ + *q++ = 0; /* min */ + *q++ = 0; /* sec */ + *q++ = 0; /* frame */ + /* same here */ + *q++ = 0; + *q++ = 0; + *q++ = 0; + *q++ = 0; + + len = q - buf; + buf[0] = (uint8_t)(((len-2) >> 8) & 0xff); + buf[1] = (uint8_t)((len-2) & 0xff); + return len; } static uint32_t iso_size() @@ -188,6 +283,14 @@ static uint32_t iso_size() return last_block; } +static int iso_status() +{ + if (!(iso_ready()) && (cdrom_drive != 200)) + return CD_STATUS_EMPTY; + + return CD_STATUS_DATA_ONLY; +} + void iso_reset() { } @@ -199,10 +302,13 @@ int iso_open(char *fn) if (iso_image) fclose(iso_image); - iso_changed = 1; + if (strcmp(fn, iso_path) != 0) + iso_changed = 1; - sprintf(iso_path, "%s", fn); - pclog("Path is %s\n", iso_path); + /* Make sure iso_changed stays when changing from ISO to another ISO. */ + if (cdrom_drive != CDROM_ISO) + iso_changed = 1; + strcpy(iso_path, fn); iso_image = fopen(iso_path, "rb"); atapi = &iso_atapi; @@ -213,29 +319,36 @@ int iso_open(char *fn) return 0; } -static void iso_close(void) +void iso_close(void) { if (iso_image) { fclose(iso_image); iso_image = NULL; } + memset(iso_path, 0, 1024); } static void iso_exit(void) { - iso_stop(); iso_inited = 0; - iso_close(); +} + +static int iso_is_track_audio(uint32_t pos, int ismsf) +{ + return 0; } static ATAPI iso_atapi = { iso_ready, + iso_medium_changed, iso_readtoc, iso_readtoc_session, + iso_readtoc_raw, iso_getcurrentsubchannel, iso_readsector, + iso_readsector_raw, iso_playaudio, iso_seek, iso_load, @@ -243,6 +356,8 @@ static ATAPI iso_atapi = iso_pause, iso_resume, iso_size, + iso_status, + iso_is_track_audio, iso_stop, iso_exit }; diff --git a/src/cdrom-iso.h b/src/cdrom-iso.h index 04352cd..d4efd06 100644 --- a/src/cdrom-iso.h +++ b/src/cdrom-iso.h @@ -8,7 +8,6 @@ extern char iso_path[1024]; extern int iso_open(char *fn); extern void iso_reset(); - -#define CDROM_ISO 200 +extern void iso_close(); #endif /* ! CDROM_ISO_H */ diff --git a/src/cdrom-null.c b/src/cdrom-null.c index 103f183..af8d7a5 100644 --- a/src/cdrom-null.c +++ b/src/cdrom-null.c @@ -40,6 +40,12 @@ static int null_ready(void) return 0; } +/* Always return 0, the contents of a null CD-ROM drive never change. */ +static int null_medium_changed(void) +{ + return 0; +} + static uint8_t null_getcurrentsubchannel(uint8_t *b, int msf) { return 0x13; @@ -57,13 +63,23 @@ static void null_readsector(uint8_t *b, int sector) { } +static void null_readsector_raw(uint8_t *b, int sector) +{ +} + static int null_readtoc(unsigned char *b, unsigned char starttrack, int msf, int maxlen, int single) { return 0; } -static void null_readtoc_session(unsigned char *b, int msf, int maxlen) +static int null_readtoc_session(unsigned char *b, int msf, int maxlen) { + return 0; +} + +static int null_readtoc_raw(unsigned char *b, int maxlen) +{ + return 0; } static uint32_t null_size() @@ -71,6 +87,11 @@ static uint32_t null_size() return 0; } +static int null_status() +{ + return CD_STATUS_EMPTY; +} + void cdrom_null_reset() { } @@ -81,7 +102,7 @@ int cdrom_null_open(char d) return 0; } -static void null_close(void) +void null_close(void) { } @@ -89,13 +110,21 @@ static void null_exit(void) { } +static int null_is_track_audio(uint32_t pos, int ismsf) +{ + return 0; +} + static ATAPI null_atapi = { null_ready, + null_medium_changed, null_readtoc, null_readtoc_session, + null_readtoc_raw, null_getcurrentsubchannel, null_readsector, + null_readsector_raw, null_playaudio, null_seek, null_load, @@ -103,6 +132,8 @@ static ATAPI null_atapi = null_pause, null_resume, null_size, + null_status, + null_is_track_audio, null_stop, null_exit }; diff --git a/src/cdrom-null.h b/src/cdrom-null.h index dee5715..b5a0e1c 100644 --- a/src/cdrom-null.h +++ b/src/cdrom-null.h @@ -6,5 +6,6 @@ extern int cdrom_null_open(char d); extern void cdrom_null_reset(); +extern void null_close(); #endif /* ! CDROM_IOCTL_H */ diff --git a/src/ibm.h b/src/ibm.h index cd93d1e..ba650c6 100644 --- a/src/ibm.h +++ b/src/ibm.h @@ -452,9 +452,18 @@ int keybsenddelay; /*CD-ROM*/ extern int cdrom_drive; +extern int old_cdrom_drive; extern int idecallback[2]; extern int cdrom_enabled; +#define CD_STATUS_EMPTY 0 +#define CD_STATUS_DATA_ONLY 1 +#define CD_STATUS_PLAYING 2 +#define CD_STATUS_PAUSED 3 +#define CD_STATUS_STOPPED 4 + +extern uint32_t atapi_get_cd_volume(int channel); + void pclog(const char *format, ...); extern int nmi; diff --git a/src/ide.c b/src/ide.c index fa7af78..01642aa 100644 --- a/src/ide.c +++ b/src/ide.c @@ -62,26 +62,35 @@ #define WIN_IDENTIFY 0xEC /* Ask drive to identify itself */ /* ATAPI Commands */ +#define GPCMD_TEST_UNIT_READY 0x00 +#define GPCMD_REQUEST_SENSE 0x03 +#define GPCMD_READ_6 0x08 #define GPCMD_INQUIRY 0x12 -#define GPCMD_MODE_SELECT_10 0x55 -#define GPCMD_MODE_SENSE_10 0x5a -#define GPCMD_PAUSE_RESUME 0x4b -#define GPCMD_PLAY_AUDIO_12 0xa5 -#define GPCMD_PLAY_AUDIO_MSF 0x47 -#define GPCMD_PREVENT_REMOVAL 0x1e -#define GPCMD_READ_10 0x28 -#define GPCMD_READ_CD 0xbe +#define GPCMD_MODE_SELECT_6 0x15 +#define GPCMD_MODE_SENSE_6 0x1a +#define GPCMD_START_STOP_UNIT 0x1b +#define GPCMD_PREVENT_REMOVAL 0x1e #define GPCMD_READ_CDROM_CAPACITY 0x25 -#define GPCMD_READ_HEADER 0x44 +#define GPCMD_READ_10 0x28 +#define GPCMD_SEEK 0x2b #define GPCMD_READ_SUBCHANNEL 0x42 #define GPCMD_READ_TOC_PMA_ATIP 0x43 -#define GPCMD_REQUEST_SENSE 0x03 -#define GPCMD_SEEK 0x2b +#define GPCMD_READ_HEADER 0x44 +#define GPCMD_PLAY_AUDIO_10 0x45 +#define GPCMD_GET_CONFIGURATION 0x46 +#define GPCMD_PLAY_AUDIO_MSF 0x47 +#define GPCMD_GET_EVENT_STATUS_NOTIFICATION 0x4a +#define GPCMD_PAUSE_RESUME 0x4b +#define GPCMD_STOP_PLAY_SCAN 0x4e +#define GPCMD_READ_DISC_INFORMATION 0x51 +#define GPCMD_MODE_SELECT_10 0x55 +#define GPCMD_MODE_SENSE_10 0x5a +#define GPCMD_PLAY_AUDIO_12 0xa5 +#define GPCMD_READ_12 0xa8 #define GPCMD_SEND_DVD_STRUCTURE 0xad #define GPCMD_SET_SPEED 0xbb -#define GPCMD_START_STOP_UNIT 0x1b -#define GPCMD_STOP_PLAY_SCAN 0x4e -#define GPCMD_TEST_UNIT_READY 0x00 +#define GPCMD_MECHANISM_STATUS 0xbd +#define GPCMD_READ_CD 0xbe /* Mode page codes for mode sense/set */ #define GPMODE_R_W_ERROR_PAGE 0x01 @@ -91,14 +100,23 @@ #define GPMODE_ALL_PAGES 0x3f /* ATAPI Sense Keys */ -#define SENSE_NONE 0 -#define SENSE_NOT_READY 2 -#define SENSE_ILLEGAL_REQUEST 5 -#define SENSE_UNIT_ATTENTION 6 +#define SENSE_NONE 0 +#define SENSE_NOT_READY 2 +#define SENSE_ILLEGAL_REQUEST 5 +#define SENSE_UNIT_ATTENTION 6 /* ATAPI Additional Sense Codes */ +#define ASC_AUDIO_PLAY_OPERATION 0x00 #define ASC_ILLEGAL_OPCODE 0x20 +#define ASC_INV_FIELD_IN_CMD_PACKET 0x24 +#define ASC_MEDIUM_MAY_HAVE_CHANGED 0x28 #define ASC_MEDIUM_NOT_PRESENT 0x3a +#define ASC_DATA_PHASE_ERROR 0x4b +#define ASC_ILLEGAL_MODE_FOR_THIS_TRACK 0x64 + +#define ASCQ_AUDIO_PLAY_OPERATION_IN_PROGRESS 0x11 +#define ASCQ_AUDIO_PLAY_OPERATION_PAUSED 0x12 +#define ASCQ_AUDIO_PLAY_OPERATION_COMPLETED 0x13 /* Tell RISC OS that we have a 4x CD-ROM drive (600kb/sec data, 706kb/sec raw). Not that it means anything */ @@ -110,8 +128,107 @@ \ (!ide.drive)*/ +/* Event notification classes for GET EVENT STATUS NOTIFICATION */ +#define GESN_NO_EVENTS 0 +#define GESN_OPERATIONAL_CHANGE 1 +#define GESN_POWER_MANAGEMENT 2 +#define GESN_EXTERNAL_REQUEST 3 +#define GESN_MEDIA 4 +#define GESN_MULTIPLE_HOSTS 5 +#define GESN_DEVICE_BUSY 6 + +/* Event codes for MEDIA event status notification */ +#define MEC_NO_CHANGE 0 +#define MEC_EJECT_REQUESTED 1 +#define MEC_NEW_MEDIA 2 +#define MEC_MEDIA_REMOVAL 3 /* only for media changers */ +#define MEC_MEDIA_CHANGED 4 /* only for media changers */ +#define MEC_BG_FORMAT_COMPLETED 5 /* MRW or DVD+RW b/g format completed */ +#define MEC_BG_FORMAT_RESTARTED 6 /* MRW or DVD+RW b/g format restarted */ +#define MS_TRAY_OPEN 1 +#define MS_MEDIA_PRESENT 2 + +#define CHECK_READY 2 +#define ALLOW_UA 1 + +/* Table of all ATAPI commands and their flags, needed for the new disc change / not ready handler. */ +uint8_t atapi_cmd_table[0x100] = +{ + [GPCMD_TEST_UNIT_READY] = CHECK_READY, + [GPCMD_REQUEST_SENSE] = ALLOW_UA, + [GPCMD_READ_6] = CHECK_READY, + [GPCMD_INQUIRY] = ALLOW_UA, + [GPCMD_MODE_SELECT_6] = 0, + [GPCMD_MODE_SENSE_6] = 0, + [GPCMD_START_STOP_UNIT] = 0, + [GPCMD_PREVENT_REMOVAL] = CHECK_READY, + [GPCMD_READ_CDROM_CAPACITY] = CHECK_READY, + [GPCMD_READ_10] = CHECK_READY, + [GPCMD_SEEK] = CHECK_READY, + [GPCMD_READ_SUBCHANNEL] = CHECK_READY, + [GPCMD_READ_TOC_PMA_ATIP] = CHECK_READY | ALLOW_UA, /* Read TOC - can get through UNIT_ATTENTION, per VIDE-CDD.SYS */ + [GPCMD_READ_HEADER] = CHECK_READY, + [GPCMD_PLAY_AUDIO_10] = CHECK_READY, + [GPCMD_GET_CONFIGURATION] = ALLOW_UA, + [GPCMD_PLAY_AUDIO_MSF] = CHECK_READY, + [GPCMD_GET_EVENT_STATUS_NOTIFICATION] = ALLOW_UA, + [GPCMD_PAUSE_RESUME] = CHECK_READY, + [GPCMD_STOP_PLAY_SCAN] = CHECK_READY, + [GPCMD_READ_DISC_INFORMATION] = CHECK_READY, + [GPCMD_MODE_SELECT_10] = 0, + [GPCMD_MODE_SENSE_10] = 0, + [GPCMD_PLAY_AUDIO_12] = CHECK_READY, + [GPCMD_READ_12] = CHECK_READY, + [GPCMD_SEND_DVD_STRUCTURE] = CHECK_READY, /* Read DVD structure (NOT IMPLEMENTED YET) */ + [GPCMD_SET_SPEED] = 0, + [GPCMD_MECHANISM_STATUS] = 0, + [GPCMD_READ_CD] = CHECK_READY, + [0xBF] = CHECK_READY /* Send DVD structure (NOT IMPLEMENTED YET) */ +}; + +#define IMPLEMENTED 1 + +uint8_t mode_sense_pages[0x40] = +{ + [GPMODE_R_W_ERROR_PAGE] = IMPLEMENTED, + [GPMODE_CDROM_PAGE] = IMPLEMENTED, + [GPMODE_CDROM_AUDIO_PAGE] = IMPLEMENTED, + [GPMODE_CAPABILITIES_PAGE] = IMPLEMENTED, + [GPMODE_ALL_PAGES] = IMPLEMENTED +}; + ATAPI *atapi; +int atapi_command = 0; +int readcdmode = 0; + +int cdrom_channel = 2; + +/* Mode sense/select stuff. */ +uint8_t mode_pages_in[256][256]; +#define PAGE_CHANGEABLE 1 +#define PAGE_CHANGED 2 +uint8_t page_flags[256] = +{ + [GPMODE_R_W_ERROR_PAGE] = 0, + [GPMODE_CDROM_PAGE] = 0, + [GPMODE_CDROM_AUDIO_PAGE] = PAGE_CHANGEABLE, + [GPMODE_CAPABILITIES_PAGE] = 0, +}; +uint8_t prefix_len; +uint8_t page_current; + +#define ATAPI_STATUS_IDLE 0 +#define ATAPI_STATUS_COMMAND 1 +#define ATAPI_STATUS_COMPLETE 2 +#define ATAPI_STATUS_DATA 3 +#define ATAPI_STATUS_PACKET_REQ 4 +#define ATAPI_STATUS_PACKET_RECEIVED 5 +#define ATAPI_STATUS_READCD 6 +#define ATAPI_STATUS_REQ_SENSE 7 +#define ATAPI_STATUS_ERROR 0x80 +#define ATAPI_STATUS_ERROR_2 0x81 + enum { IDE_NONE = 0, @@ -135,7 +252,6 @@ typedef struct IDE int packetstatus; int cdpos,cdlen; uint8_t asc; - int discchanged; int reset; FILE *hdfile; uint16_t buffer[65536]; @@ -157,6 +273,7 @@ int (*ide_bus_master_read_sector)(int channel, uint8_t *data); int (*ide_bus_master_write_sector)(int channel, uint8_t *data); void (*ide_bus_master_set_irq)(int channel); +static void callnonreadcd(IDE *ide); static void callreadcd(IDE *ide); static void atapicommand(int ide_board); @@ -164,8 +281,6 @@ int idecallback[2] = {0, 0}; int cur_ide[2]; -int cdrom_channel = 2; - uint8_t getstat(IDE *ide) { return ide->atastat; } static inline void ide_irq_raise(IDE *ide) @@ -346,7 +461,7 @@ static uint32_t ide_atapi_mode_sense(IDE *ide, uint32_t pos, uint8_t type) buf[pos++] = GPMODE_R_W_ERROR_PAGE; buf[pos++] = 6; /* Page length */ buf[pos++] = 0; /* Error recovery parameters */ - buf[pos++] = 3; /* Read retry count */ + buf[pos++] = 5; /* Read retry count */ buf[pos++] = 0; /* Reserved */ buf[pos++] = 0; /* Reserved */ buf[pos++] = 0; /* Reserved */ @@ -368,20 +483,32 @@ static uint32_t ide_atapi_mode_sense(IDE *ide, uint32_t pos, uint8_t type) { /* &0e - CD-ROM Audio Control Parameters */ buf[pos++] = GPMODE_CDROM_AUDIO_PAGE; - buf[pos++] = 0xE; /* Page length */ - buf[pos++] = 4; /* Reserved */ - buf[pos++] = 0; /* Reserved */ - buf[pos++] = 0; /* Reserved */ - buf[pos++] = 0; /* Reserved */ - buf[pos++] = 0; buf[pos++] = 75; /* Logical audio block per second */ - buf[pos++] = 1; /* CDDA Output Port 0 Channel Selection */ - buf[pos++] = 0xFF; /* CDDA Output Port 0 Volume */ - buf[pos++] = 2; /* CDDA Output Port 1 Channel Selection */ - buf[pos++] = 0xFF; /* CDDA Output Port 1 Volume */ - buf[pos++] = 0; /* CDDA Output Port 2 Channel Selection */ - buf[pos++] = 0; /* CDDA Output Port 2 Volume */ - buf[pos++] = 0; /* CDDA Output Port 3 Channel Selection */ - buf[pos++] = 0; /* CDDA Output Port 3 Volume */ + buf[pos++] = 0xE; /* Page length */ + if (page_flags[GPMODE_CDROM_AUDIO_PAGE] & PAGE_CHANGED) + { + int i; + + for (i = 0; i < 14; i++) + { + buf[pos++] = mode_pages_in[GPMODE_CDROM_AUDIO_PAGE][i]; + } + } + else + { + buf[pos++] = 4; /* Reserved */ + buf[pos++] = 0; /* Reserved */ + buf[pos++] = 0; /* Reserved */ + buf[pos++] = 0; /* Reserved */ + buf[pos++] = 0; buf[pos++] = 75; /* Logical audio block per second */ + buf[pos++] = 1; /* CDDA Output Port 0 Channel Selection */ + buf[pos++] = 0xFF; /* CDDA Output Port 0 Volume */ + buf[pos++] = 2; /* CDDA Output Port 1 Channel Selection */ + buf[pos++] = 0xFF; /* CDDA Output Port 1 Volume */ + buf[pos++] = 0; /* CDDA Output Port 2 Channel Selection */ + buf[pos++] = 0; /* CDDA Output Port 2 Volume */ + buf[pos++] = 0; /* CDDA Output Port 3 Channel Selection */ + buf[pos++] = 0; /* CDDA Output Port 3 Volume */ + } } if (type==GPMODE_ALL_PAGES || type==GPMODE_CAPABILITIES_PAGE) @@ -410,6 +537,11 @@ static uint32_t ide_atapi_mode_sense(IDE *ide, uint32_t pos, uint8_t type) return pos; } +uint32_t atapi_get_cd_volume(int channel) +{ + return (page_flags[GPMODE_CDROM_AUDIO_PAGE] & PAGE_CHANGED) ? mode_pages_in[GPMODE_CDROM_AUDIO_PAGE][channel ? 9 : 7] : 0xFF; +} + /* * Return the sector offset for the current register values */ @@ -541,6 +673,15 @@ static void loadhd(IDE *ide, int d, const char *fn) } #endif +void ide_set_signature(IDE *ide) +{ + ide->secount=1; + ide->sector=1; + ide->head=0; + ide->cylinder=(IDE_DRIVE_IS_CDROM(ide) ? 0xEB14 : ((ide->type == IDE_HDD) ? 0 : 0xFFFF)); + if (ide->type == IDE_HDD) ide->drive = 0; +} + void resetide(void) { int d; @@ -556,6 +697,9 @@ void resetide(void) ide_drives[d].service = 0; ide_drives[d].board = (d & 2) ? 1 : 0; } + + page_flags[GPMODE_CDROM_AUDIO_PAGE] &= 0xFD; /* Clear changed flag for CDROM AUDIO mode page. */ + memset(mode_pages_in[GPMODE_CDROM_AUDIO_PAGE], 0, 256); /* Clear the page itself. */ idecallback[0]=idecallback[1]=0; #ifdef RPCEMU_IDE @@ -568,6 +712,8 @@ void resetide(void) #else for (d = 0; d < 4; d++) { + ide_drives[d].packetstatus = 0xFF; + if ((cdrom_channel == d) && cdrom_enabled) { ide_drives[d].type = IDE_CDROM; @@ -576,6 +722,8 @@ void resetide(void) { loadhd(&ide_drives[d], d, ide_fn[d]); } + + ide_set_signature(&ide_drives[d]); } #endif @@ -589,6 +737,10 @@ int idetimes=0; void writeidew(int ide_board, uint16_t val) { IDE *ide = &ide_drives[cur_ide[ide_board]]; + + /*Some software issue excess writes after the 12 bytes required by the command, this will have all of them ignored*/ + if (ide->packetstatus && (ide->packetstatus != ATAPI_STATUS_PACKET_REQ)) + return; #ifndef RPCEMU_IDE /* if (ide_board && (cr0&1) && !(eflags&VM_FLAG)) { @@ -603,11 +755,16 @@ void writeidew(int ide_board, uint16_t val) ide->buffer[ide->pos >> 1] = val; ide->pos+=2; - if (ide->packetstatus==4) + if (ide->packetstatus == ATAPI_STATUS_PACKET_REQ) { + if ((ide->pos>=prefix_len+4) && (page_flags[page_current] & PAGE_CHANGEABLE)) + { + mode_pages_in[page_current][ide->pos - prefix_len - 4] = ((uint8_t *) ide->buffer)[ide->pos - 2]; + mode_pages_in[page_current][ide->pos - prefix_len - 3] = ((uint8_t *) ide->buffer)[ide->pos - 1]; + } if (ide->pos>=(ide->packlen+2)) { - ide->packetstatus=5; + ide->packetstatus = ATAPI_STATUS_PACKET_RECEIVED; timer_process(); idecallback[ide_board]=6*IDE_TIME; timer_update_outstanding(); @@ -616,12 +773,13 @@ void writeidew(int ide_board, uint16_t val) } return; } - else if (ide->packetstatus==5) return; + else if (ide->packetstatus == ATAPI_STATUS_PACKET_RECEIVED) + return; else if (ide->command == WIN_PACKETCMD && ide->pos>=0xC) { ide->pos=0; ide->atastat = BUSY_STAT; - ide->packetstatus=1; + ide->packetstatus = ATAPI_STATUS_COMMAND; /* idecallback[ide_board]=6*IDE_TIME;*/ timer_process(); callbackide(ide_board); @@ -736,7 +894,7 @@ void writeide(int ide_board, uint16_t addr, uint8_t val) ide->head = ide_other->head = 0; ide->cylinder = ide_other->cylinder = 0; ide->reset = ide_other->reset = 0; - ide->blocksize = ide_other->blocksize = 0; + // ide->blocksize = ide_other->blocksize = 0; if (IDE_DRIVE_IS_CDROM(ide)) ide->cylinder=0xEB14; if (IDE_DRIVE_IS_CDROM(ide_other)) @@ -789,7 +947,7 @@ void writeide(int ide_board, uint16_t addr, uint8_t val) return; case WIN_READ_MULTIPLE: - if (!ide->blocksize) + if (!ide->blocksize && (ide->type != IDE_CDROM)) fatal("READ_MULTIPLE - blocksize = 0\n"); #if 0 if (ide->lba) pclog("Read Multiple %i sectors from LBA addr %07X\n",ide->secount,ide->lba_addr); @@ -815,7 +973,7 @@ void writeide(int ide_board, uint16_t addr, uint8_t val) return; case WIN_WRITE_MULTIPLE: - if (!ide->blocksize) + if (!ide->blocksize && (ide->type != IDE_CDROM)) fatal("Write_MULTIPLE - blocksize = 0\n"); #if 0 if (ide->lba) pclog("Write Multiple %i sectors from LBA addr %07X\n",ide->secount,ide->lba_addr); @@ -869,7 +1027,7 @@ void writeide(int ide_board, uint16_t addr, uint8_t val) case WIN_SPECIFY: /* Initialize Drive Parameters */ ide->atastat = BUSY_STAT; timer_process(); - idecallback[ide_board]=200*IDE_TIME; + idecallback[ide_board]=30*IDE_TIME; timer_update_outstanding(); // pclog("SPECIFY\n"); // output=1; @@ -898,7 +1056,7 @@ void writeide(int ide_board, uint16_t addr, uint8_t val) return; case WIN_PACKETCMD: /* ATAPI Packet */ - ide->packetstatus=0; + ide->packetstatus = ATAPI_STATUS_IDLE; ide->atastat = BUSY_STAT; timer_process(); idecallback[ide_board]=1;//30*IDE_TIME; @@ -999,9 +1157,7 @@ uint8_t readide(int ide_board, uint16_t addr) break; } ide_irq_lower(ide); - if (ide->fdisk & 4) - temp = 0x80; - else if (ide->type == IDE_CDROM) + if (ide->type == IDE_CDROM) { // pclog("Read CDROM status %02X\n",(ide->atastat & ~DSC_STAT) | (ide->service ? SERVICE_STAT : 0)); temp = (ide->atastat & ~DSC_STAT) | (ide->service ? SERVICE_STAT : 0); @@ -1062,6 +1218,11 @@ uint16_t readidew(int ide_board) temp=(temp>>8)|(temp<<8); #endif ide->pos+=2; + if ((ide->command == WIN_PACKETCMD) && ((ide->packetstatus == ATAPI_STATUS_REQ_SENSE) || (ide->packetstatus==8))) + { + callnonreadcd(ide); + return temp; + } if ((ide->pos>=512 && ide->command != WIN_PACKETCMD) || (ide->command == WIN_PACKETCMD && ide->pos>=ide->packlen)) { // pclog("Over! packlen %i %i\n",ide->packlen,ide->pos); @@ -1074,7 +1235,7 @@ uint16_t readidew(int ide_board) else { ide->atastat = READY_STAT | DSC_STAT; - ide->packetstatus=0; + ide->packetstatus = ATAPI_STATUS_IDLE; if (ide->command == WIN_READ || ide->command == WIN_READ_NORETRY || ide->command == WIN_READ_MULTIPLE) { ide->secount = (ide->secount - 1) & 0xff; @@ -1137,11 +1298,21 @@ void callbackide(int ide_board) ide->cylinder=0xEB14; atapi->stop(); } + if (ide->type == IDE_NONE) + { + ide->cylinder=0xFFFF; + atapi->stop(); + } if (IDE_DRIVE_IS_CDROM(ide_other)) { ide_other->cylinder=0xEB14; atapi->stop(); } + if (ide_other->type == IDE_NONE) + { + ide_other->cylinder=0xFFFF; + atapi->stop(); + } // pclog("Reset callback\n"); return; } @@ -1152,13 +1323,9 @@ void callbackide(int ide_board) ide->atastat = READY_STAT | DSC_STAT; ide->error=1; /*Device passed*/ ide->secount = ide->sector = 1; - if (IDE_DRIVE_IS_CDROM(ide)) { - ide->cylinder = 0xeb14; - ide->atastat = 0; - atapi->stop(); - } else { - ide->cylinder = 0; - } + ide_set_signature(ide); + if (IDE_DRIVE_IS_CDROM(ide)) + ide->atastat = 0; ide_irq_raise(ide); if (IDE_DRIVE_IS_CDROM(ide)) ide->service = 0; @@ -1178,6 +1345,7 @@ void callbackide(int ide_board) case WIN_READ: case WIN_READ_NORETRY: if (IDE_DRIVE_IS_CDROM(ide)) { + ide_set_signature(ide); goto abort_cmd; } addr = ide_get_sector(ide) * 512; @@ -1378,9 +1546,17 @@ void callbackide(int ide_board) return; case WIN_DRIVE_DIAGNOSTICS: + ide_set_signature(ide); ide->error=1; /*No error detected*/ - ide->atastat = READY_STAT | DSC_STAT; - ide_irq_raise(ide); + if (IDE_DRIVE_IS_CDROM(ide)) + { + ide->atastat = 0; + } + else + { + ide->atastat = READY_STAT | DSC_STAT; + ide_irq_raise(ide); + } return; case WIN_SPECIFY: /* Initialize Drive Parameters */ @@ -1432,15 +1608,17 @@ void callbackide(int ide_board) goto abort_cmd; case WIN_IDENTIFY: /* Identify Device */ - if (IDE_DRIVE_IS_CDROM(ide)) { - ide->secount=1; - ide->sector=1; - ide->cylinder=0xEB14; - ide->drive=ide->head=0; - goto abort_cmd; - } - if (ide->type != IDE_NONE) - { + if (ide->type == IDE_NONE) + { + goto abort_cmd; + } + if (IDE_DRIVE_IS_CDROM(ide)) + { + ide_set_signature(ide); + goto abort_cmd; + } + else + { ide_identify(ide); ide->pos=0; ide->atastat = DRQ_STAT | READY_STAT | DSC_STAT; @@ -1452,22 +1630,24 @@ void callbackide(int ide_board) case WIN_PACKETCMD: /* ATAPI Packet */ if (!IDE_DRIVE_IS_CDROM(ide)) goto abort_cmd; // pclog("Packet callback! %i %08X\n",ide->packetstatus,ide); - if (!ide->packetstatus) + + if (ide->packetstatus == ATAPI_STATUS_IDLE) { + readcdmode=0; ide->pos=0; ide->secount = (uint8_t)((ide->secount&0xF8)|1); - ide->atastat = DRQ_STAT |(ide->atastat&ERR_STAT); + ide->atastat = READY_STAT | DRQ_STAT |(ide->atastat&ERR_STAT); //ide_irq_raise(ide); // pclog("1 Preparing to recieve packet max DRQ count %04X\n",ide->cylinder); } - else if (ide->packetstatus==1) + else if (ide->packetstatus == ATAPI_STATUS_COMMAND) { ide->atastat = BUSY_STAT|(ide->atastat&ERR_STAT); // pclog("Running ATAPI command 2\n"); atapicommand(ide_board); // exit(-1); } - else if (ide->packetstatus==2) + else if (ide->packetstatus == ATAPI_STATUS_COMPLETE) { // pclog("packetstatus==2\n"); ide->atastat = READY_STAT; @@ -1475,45 +1655,59 @@ void callbackide(int ide_board) ide_irq_raise(ide); // if (iomd.irqb.mask&2) output=1; } - else if (ide->packetstatus==3) + else if (ide->packetstatus == ATAPI_STATUS_DATA) { - ide->atastat = DRQ_STAT|(ide->atastat&ERR_STAT); + ide->atastat = READY_STAT|DRQ_STAT|(ide->atastat&ERR_STAT); // rpclog("Recieve data packet 3! %02X\n",ide->atastat); ide_irq_raise(ide); ide->packetstatus=0xFF; } - else if (ide->packetstatus==4) + else if (ide->packetstatus == ATAPI_STATUS_PACKET_REQ) { - ide->atastat = DRQ_STAT|(ide->atastat&ERR_STAT); + ide->atastat = 0x58 | (ide->atastat & ERR_STAT); // pclog("Send data packet 4!\n"); ide_irq_raise(ide); // ide.packetstatus=5; ide->pos=2; } - else if (ide->packetstatus==5) + else if (ide->packetstatus == ATAPI_STATUS_PACKET_RECEIVED) { // pclog("Packetstatus 5 !\n"); atapicommand(ide_board); } - else if (ide->packetstatus==6) /*READ CD callback*/ + else if (ide->packetstatus == ATAPI_STATUS_READCD) /*READ CD callback*/ { ide->atastat = DRQ_STAT|(ide->atastat&ERR_STAT); // pclog("Recieve data packet 6!\n"); ide_irq_raise(ide); // ide.packetstatus=0xFF; } - else if (ide->packetstatus==0x80) /*Error callback*/ + else if (ide->packetstatus == ATAPI_STATUS_REQ_SENSE) /*REQUEST SENSE callback #1*/ + { + pclog("REQUEST SENSE callback #1: setting status to 0x5A\n"); + ide->atastat = 0x58 | (ide->atastat & ERR_STAT); + ide_irq_raise(ide); + } + else if (ide->packetstatus == ATAPI_STATUS_ERROR) /*Error callback*/ { // pclog("Packet error\n"); ide->atastat = READY_STAT | ERR_STAT; ide_irq_raise(ide); } + else if (ide->packetstatus == ATAPI_STATUS_ERROR_2) /*Error callback with atastat already set - needed for the disc change stuff.*/ + { + //pclog("Packet check status\n"); + ide->atastat = ERR_STAT; + ide_irq_raise(ide); + } return; } abort_cmd: + ide->command = 0; ide->atastat = READY_STAT | ERR_STAT | DSC_STAT; ide->error = ABRT_ERR; + ide->pos = 0; ide_irq_raise(ide); } @@ -1529,142 +1723,315 @@ void ide_callback_sec() callbackide(1); } -/*ATAPI CD-ROM emulation - This mostly seems to work. It is implemented only on Windows at the moment as - I haven't had time to implement any interfaces for it in the generic gui. - It mostly depends on driver files - cdrom-iso.c for ISO image files (in theory - on any platform) and cdrom-ioctl.c for Win32 IOCTL access. There's an ATAPI - interface defined in ide.h. - There are a couple of bugs in the CD audio handling. - */ +/*ATAPI CD-ROM emulation*/ struct { int sensekey,asc,ascq; } atapi_sense; -static void atapi_notready(IDE *ide) +static uint32_t atapi_event_status(IDE *ide, uint8_t *buffer) { - /*Medium not present is 02/3A/--*/ - /*cylprecomp is error number*/ - /*SENSE/ASC/ASCQ*/ - ide->atastat = READY_STAT | ERR_STAT; /*CHECK CONDITION*/ - ide->error = (SENSE_NOT_READY << 4) | ABRT_ERR; - if (ide->discchanged) { - ide->error |= MCR_ERR; - atapi_sense.sensekey=6; - atapi_sense.asc=0x28; - } else { - atapi_sense.sensekey=2; - atapi_sense.asc = ASC_MEDIUM_NOT_PRESENT; - } - ide->discchanged=0; - ide->packetstatus=0x80; - idecallback[ide->board]=50*IDE_TIME; + uint8_t event_code, media_status = 0; + + if (buffer[5]) + { + media_status = MS_TRAY_OPEN; + atapi->stop(); + } + else + { + media_status = MS_MEDIA_PRESENT; + } + + event_code = MEC_NO_CHANGE; + if (media_status != MS_TRAY_OPEN) + { + if (!buffer[4]) + { + event_code = MEC_NEW_MEDIA; + atapi->load(); + } + else if (buffer[4]==2) + { + event_code = MEC_EJECT_REQUESTED; + atapi->eject(); + } + } + + buffer[4] = event_code; + buffer[5] = media_status; + buffer[6] = 0; + buffer[7] = 0; + + return 8; } -void atapi_discchanged() +static int changed_status = 0; + +void atapi_cmd_error(IDE *ide, uint8_t sensekey, uint8_t asc) { - ext_ide->discchanged=1; - atapi_sense.sensekey=6; - atapi_sense.asc=0x28; + ide->error = (sensekey << 4); + ide->atastat = READY_STAT | ERR_STAT; + ide->secount = (ide->secount & ~7) | 3; + ide->packetstatus = 0x80; + idecallback[ide->board]=50*IDE_TIME; } uint8_t atapi_prev; int toctimes=0; + +void atapi_insert_cdrom() +{ + atapi_sense.sensekey=SENSE_UNIT_ATTENTION; + atapi_sense.asc=ASC_MEDIUM_MAY_HAVE_CHANGED; + atapi_sense.ascq=0; +} + +void atapi_command_send_init(IDE *ide, uint8_t command, int req_length, int alloc_length) +{ + if (ide->cylinder == 0xffff) + ide->cylinder = 0xfffe; + + if ((ide->cylinder & 1) && !(alloc_length <= ide->cylinder)) + { + pclog("Odd byte count (0x%04x) to ATAPI command 0x%02x, using 0x%04x\n", ide->cylinder, command, ide->cylinder - 1); + ide->cylinder--; + } + + if (alloc_length < 0) + fatal("Allocation length < 0\n"); + if (alloc_length == 0) + alloc_length = ide->cylinder; + + // Status: 0x80 (busy), 0x08 (drq), 0x01 (err) + // Interrupt: 0x02 (i_o), 0x01 (c_d) + /* No atastat setting: PCem actually emulates the callback cycle. */ + ide->secount = 2; + + // no bytes transferred yet + ide->pos = 0; + + if ((ide->cylinder > req_length) || (ide->cylinder == 0)) + ide->cylinder = req_length; + if (ide->cylinder > alloc_length) + ide->cylinder = alloc_length; + + //pclog("atapi_command_send_init(ide, %02X, %04X, %04X)\n", command, req_length, alloc_length); + //pclog("IDE settings: Pos=%08X, Secount=%08X, Cylinder=%08X\n", ide->pos, ide->secount, ide->cylinder); +} + +static void atapi_command_ready(int ide_board, int packlen) +{ + IDE *ide = &ide_drives[cur_ide[ide_board]]; + ide->packetstatus = ATAPI_STATUS_REQ_SENSE; + idecallback[ide_board]=60*IDE_TIME; + ide->packlen=packlen; +} + +static void atapi_sense_clear(int command, int ignore_ua) +{ + if ((atapi_sense.sensekey == SENSE_UNIT_ATTENTION) || ignore_ua) + { + atapi_prev=command; + atapi_sense.sensekey=0; + atapi_sense.asc=0; + atapi_sense.ascq=0; + } +} + +int cd_status = CD_STATUS_EMPTY; +int prev_status; + static void atapicommand(int ide_board) { IDE *ide = &ide_drives[cur_ide[ide_board]]; uint8_t *idebufferb = (uint8_t *) ide->buffer; + uint8_t rcdmode = 0; int c; int len; int msf; int pos=0; unsigned char temp; uint32_t size; + int is_error; + uint8_t page_code; + int max_len; + unsigned idx = 0; + unsigned size_idx; + unsigned preamble_len; + int toc_format; + int temp_command; + int alloc_length; + int completed; + #ifndef RPCEMU_IDE pclog("New ATAPI command %02X %i\n",idebufferb[0],ins); #endif // readflash=1; - msf=idebufferb[1]&2; - ide->cdlen=0; - if (idebufferb[0]!=GPCMD_REQUEST_SENSE) - { - atapi_prev=idebufferb[0]; - atapi_sense.sensekey=0; - atapi_sense.asc=0; - } + msf=idebufferb[1]&2; + ide->cdlen=0; + + is_error = 0; + + if (atapi->medium_changed()) + { + atapi_insert_cdrom(); + } + /*If UNIT_ATTENTION is set, error out with NOT_READY. + VIDE-CDD.SYS will then issue a READ_TOC, which can pass through UNIT_ATTENTION and will clear sense. + NT 3.1 / AZTIDECD.SYS will then issue a REQUEST_SENSE, which can also pass through UNIT_ATTENTION but will clear sense AFTER sending it back. + In any case, if the command cannot pass through, set our state to errored.*/ + if (!(atapi_cmd_table[idebufferb[0]] & ALLOW_UA) && atapi_sense.sensekey == SENSE_UNIT_ATTENTION) + { + atapi_cmd_error(ide, atapi_sense.sensekey, atapi_sense.asc); + is_error = 1; + } + /*Unless the command issued was a REQUEST_SENSE or TEST_UNIT_READY, clear sense. + This is important because both VIDE-CDD.SYS and NT 3.1 / AZTIDECD.SYS rely on this behaving VERY specifically. + VIDE-CDD.SYS will clear sense through READ_TOC, while NT 3.1 / AZTIDECD.SYS will issue a REQUEST_SENSE.*/ + if ((idebufferb[0]!=GPCMD_REQUEST_SENSE) && (idebufferb[0]!=GPCMD_TEST_UNIT_READY)) + { + /* GPCMD_TEST_UNIT_READY is NOT supposed to clear sense! */ + atapi_sense_clear(idebufferb[0], 1); + } + + /*If our state has been set to errored, clear it, and return.*/ + if (is_error) + { + is_error = 0; + return; + } + + if ((atapi_cmd_table[idebufferb[0]] & CHECK_READY) && !atapi->ready()) + { + atapi_cmd_error(ide, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT); + atapi_sense.sensekey = SENSE_NOT_READY; + atapi_sense.asc = ASC_MEDIUM_NOT_PRESENT; + atapi_sense.ascq = 0; + return; + } + + prev_status = cd_status; + cd_status = atapi->status(); + if (((prev_status == CD_STATUS_PLAYING) || (prev_status == CD_STATUS_PAUSED)) && ((cd_status != CD_STATUS_PLAYING) && (cd_status != CD_STATUS_PAUSED))) + { + completed = 1; + } + else + { + completed = 0; + } + switch (idebufferb[0]) { - case GPCMD_TEST_UNIT_READY: - if (!atapi->ready()) { atapi_notready(ide); return; } -// if (atapi->ready()) -// { - ide->packetstatus=2; - idecallback[ide_board]=50*IDE_TIME; -// } -// else -// { -// pclog("Medium not present!\n"); -// } + case GPCMD_TEST_UNIT_READY: + ide->packetstatus = ATAPI_STATUS_COMPLETE; + idecallback[ide_board]=50*IDE_TIME; break; - case GPCMD_REQUEST_SENSE: /* Used by ROS 4+ */ + case GPCMD_REQUEST_SENSE: /* Used by ROS 4+ */ + alloc_length = idebufferb[4]; + temp_command = idebufferb[0]; + atapi_command_send_init(ide, temp_command, 18, alloc_length); + /*Will return 18 bytes of 0*/ memset(idebufferb,0,512); -// switch (atapi_prev) -// { -// case GPCMD_TEST_UNIT_READY: - idebufferb[0]=0x80|0x70; - idebufferb[2]=atapi_sense.sensekey; - idebufferb[12]=atapi_sense.asc; - idebufferb[13]=atapi_sense.ascq; -// break; -// -// default: -// fatal("Bad REQUEST_SENSE following command %02X\n",atapi_prev); -// } - ide->packetstatus=3; - ide->cylinder=18; - ide->secount=2; - ide->pos=0; - idecallback[ide_board]=60*IDE_TIME; - ide->packlen=18; + + idebufferb[0]=0x80|0x70; + + if ((atapi_sense.sensekey > 0) || (cd_status < CD_STATUS_PLAYING)) + { + if (completed) + { + idebufferb[2]=SENSE_ILLEGAL_REQUEST; + idebufferb[12]=ASC_AUDIO_PLAY_OPERATION; + idebufferb[13]=ASCQ_AUDIO_PLAY_OPERATION_COMPLETED; + } + else + { + idebufferb[2]=atapi_sense.sensekey; + idebufferb[12]=atapi_sense.asc; + idebufferb[13]=atapi_sense.ascq; + } + } + else + { + idebufferb[2]=SENSE_ILLEGAL_REQUEST; + idebufferb[12]=ASC_AUDIO_PLAY_OPERATION; + idebufferb[13]=(cd_status == CD_STATUS_PLAYING) ? ASCQ_AUDIO_PLAY_OPERATION_IN_PROGRESS : ASCQ_AUDIO_PLAY_OPERATION_PAUSED; + } + + idebufferb[7]=10; + + // pclog("REQUEST SENSE start\n"); + atapi_command_ready(ide_board, 18); + + /* Clear the sense stuff as per the spec. */ + atapi_sense_clear(temp_command, 0); break; - case GPCMD_SET_SPEED: - ide->packetstatus=2; + case GPCMD_SET_SPEED: + ide->packetstatus = ATAPI_STATUS_COMPLETE; idecallback[ide_board]=50*IDE_TIME; break; - case GPCMD_READ_TOC_PMA_ATIP: + case GPCMD_MECHANISM_STATUS: /*0xbd*/ + len=(idebufferb[7]<<16)|(idebufferb[8]<<8)|idebufferb[9]; + + if (len == 0) + fatal("Zero allocation length to MECHANISM STATUS not impl.\n"); + + atapi_command_send_init(ide, idebufferb[0], 8, alloc_length); + + idebufferb[0] = 0; + idebufferb[1] = 0; + idebufferb[2] = 0; + idebufferb[3] = 0; + idebufferb[4] = 0; + idebufferb[5] = 1; + idebufferb[6] = 0; + idebufferb[7] = 0; + // len = 8; + + atapi_command_ready(ide_board, 8); + break; + + case GPCMD_READ_TOC_PMA_ATIP: // pclog("Read TOC ready? %08X\n",ide); - if (!atapi->ready()) { atapi_notready(ide); return; } toctimes++; // if (toctimes==2) output=3; // pclog("Read TOC %02X\n",idebufferb[9]); - switch (idebufferb[9]>>6) + toc_format = idebufferb[2] & 0xf; + if (toc_format == 0) + toc_format = (idebufferb[9]>>6) & 3; + switch (toc_format) { case 0: /*Normal*/ + // pclog("ATAPI: READ TOC type requested: Normal\n"); len=idebufferb[8]+(idebufferb[7]<<8); len=atapi->readtoc(idebufferb,idebufferb[6],msf,len,0); break; case 1: /*Multi session*/ + // pclog("ATAPI: READ TOC type requested: Multi-session\n"); len=idebufferb[8]+(idebufferb[7]<<8); - atapi->readtoc_session(idebufferb,msf,len); + len=atapi->readtoc_session(idebufferb,msf,len); idebufferb[0]=0; idebufferb[1]=0xA; break; + case 2: /*Raw*/ + // pclog("ATAPI: READ TOC type requested: Raw TOC\n"); + len=idebufferb[8]+(idebufferb[7]<<8); + len=atapi->readtoc_raw(idebufferb,len); + break; default: + // pclog("ATAPI: Unknown READ TOC type requested: %i\n", (idebufferb[9]>>6)); ide->atastat = READY_STAT | ERR_STAT; /*CHECK CONDITION*/ ide->error = (SENSE_ILLEGAL_REQUEST << 4) | ABRT_ERR; - if (ide->discchanged) { + if (atapi_sense.sensekey == SENSE_UNIT_ATTENTION) ide->error |= MCR_ERR; - } - ide->discchanged=0; - atapi_sense.asc = ASC_ILLEGAL_OPCODE; - ide->packetstatus=0x80; + ide->packetstatus = ATAPI_STATUS_ERROR; idecallback[ide_board]=50*IDE_TIME; - break; + return; /* pclog("Bad read TOC format\n"); pclog("Packet data :\n"); for (c=0;c<12;c++) @@ -1673,7 +2040,7 @@ static void atapicommand(int ide_board) exit(-1);*/ } // pclog("ATAPI buffer len %i\n",len); - ide->packetstatus=3; + ide->packetstatus = ATAPI_STATUS_DATA; ide->cylinder=len; ide->secount=2; ide->pos=0; @@ -1682,18 +2049,16 @@ static void atapicommand(int ide_board) return; case GPCMD_READ_CD: - if (!atapi->ready()) { atapi_notready(ide); return; } // pclog("Read CD : start LBA %02X%02X%02X%02X Length %02X%02X%02X Flags %02X\n",idebufferb[2],idebufferb[3],idebufferb[4],idebufferb[5],idebufferb[6],idebufferb[7],idebufferb[8],idebufferb[9]); - if (idebufferb[9]!=0x10) + rcdmode = idebufferb[9] & 0xF8; + if ((rcdmode != 0x10) && (rcdmode != 0xF8)) { ide->atastat = READY_STAT | ERR_STAT; /*CHECK CONDITION*/ ide->error = (SENSE_ILLEGAL_REQUEST << 4) | ABRT_ERR; - if (ide->discchanged) { + if (atapi_sense.sensekey == SENSE_UNIT_ATTENTION) ide->error |= MCR_ERR; - } - ide->discchanged=0; atapi_sense.asc = ASC_ILLEGAL_OPCODE; - ide->packetstatus=0x80; + ide->packetstatus = ATAPI_STATUS_ERROR; idecallback[ide_board]=50*IDE_TIME; break; // pclog("Bad flags bits %02X\n",idebufferb[9]); @@ -1707,31 +2072,53 @@ static void atapicommand(int ide_board) ide->cdlen=(idebufferb[6]<<16)|(idebufferb[7]<<8)|idebufferb[8]; ide->cdpos=(idebufferb[2]<<24)|(idebufferb[3]<<16)|(idebufferb[4]<<8)|idebufferb[5]; // pclog("Read at %08X %08X\n",ide.cdpos,ide.cdpos*2048); - atapi->readsector(idebufferb,ide->cdpos); + if (rcdmode == 0x10) + atapi->readsector(idebufferb,ide->cdpos); + else + atapi->readsector_raw(idebufferb,ide->cdpos); #ifndef RPCEMU_IDE readflash=1; #endif + readcdmode = (rcdmode == 0xF8); ide->cdpos++; ide->cdlen--; - if (ide->cdlen>=0) ide->packetstatus=6; - else ide->packetstatus=3; - ide->cylinder=2048; + if (ide->cdlen >= 0) + ide->packetstatus = ATAPI_STATUS_READCD; + else + ide->packetstatus = ATAPI_STATUS_DATA; + ide->cylinder=(idebufferb[9] == 0x10) ? 2048 : 2352; ide->secount=2; ide->pos=0; idecallback[ide_board]=60*IDE_TIME; - ide->packlen=2048; + ide->packlen=(idebufferb[9] == 0x10) ? 2048 : 2352; return; - case GPCMD_READ_10: - if (!atapi->ready()) { atapi_notready(ide); return; } + case GPCMD_READ_6: + case GPCMD_READ_10: + case GPCMD_READ_12: // pclog("Read 10 : start LBA %02X%02X%02X%02X Length %02X%02X%02X Flags %02X\n",idebufferb[2],idebufferb[3],idebufferb[4],idebufferb[5],idebufferb[6],idebufferb[7],idebufferb[8],idebufferb[9]); - ide->cdlen=(idebufferb[7]<<8)|idebufferb[8]; - ide->cdpos=(idebufferb[2]<<24)|(idebufferb[3]<<16)|(idebufferb[4]<<8)|idebufferb[5]; + readcdmode = 0; + + if (idebufferb[0] == GPCMD_READ_6) + { + ide->cdlen=idebufferb[4]; + ide->cdpos=((((uint32_t) idebufferb[1]) & 0x1f)<<16)|(((uint32_t) idebufferb[2])<<8)|((uint32_t) idebufferb[3]); + } + else if (idebufferb[0] == GPCMD_READ_10) + { + ide->cdlen=(idebufferb[7]<<8)|idebufferb[8]; + ide->cdpos=(idebufferb[2]<<24)|(idebufferb[3]<<16)|(idebufferb[4]<<8)|idebufferb[5]; + } + else + { + ide->cdlen=(((uint32_t) idebufferb[6])<<24)|(((uint32_t) idebufferb[7])<<16)|(((uint32_t) idebufferb[8])<<8)|((uint32_t) idebufferb[9]); + ide->cdpos=(((uint32_t) idebufferb[2])<<24)|(((uint32_t) idebufferb[3])<<16)|(((uint32_t) idebufferb[4])<<8)|((uint32_t) idebufferb[5]); + } if (!ide->cdlen) { // pclog("All done - callback set\n"); - ide->packetstatus=2; + ide->packetstatus = ATAPI_STATUS_COMPLETE; idecallback[ide_board]=20*IDE_TIME; break; } @@ -1742,8 +2129,10 @@ static void atapicommand(int ide_board) #endif ide->cdpos++; ide->cdlen--; - if (ide->cdlen>=0) ide->packetstatus=6; - else ide->packetstatus=3; + if (ide->cdlen >= 0) + ide->packetstatus = ATAPI_STATUS_READCD; + else + ide->packetstatus = ATAPI_STATUS_DATA; ide->cylinder=2048; ide->secount=2; ide->pos=0; @@ -1752,17 +2141,16 @@ static void atapicommand(int ide_board) return; case GPCMD_READ_HEADER: - if (!atapi->ready()) { atapi_notready(ide); return; } if (msf) { ide->atastat = READY_STAT | ERR_STAT; /*CHECK CONDITION*/ ide->error = (SENSE_ILLEGAL_REQUEST << 4) | ABRT_ERR; - if (ide->discchanged) { + if (atapi_sense.sensekey == SENSE_UNIT_ATTENTION) + { ide->error |= MCR_ERR; } - ide->discchanged=0; atapi_sense.asc = ASC_ILLEGAL_OPCODE; - ide->packetstatus=0x80; + ide->packetstatus = ATAPI_STATUS_ERROR; idecallback[ide_board]=50*IDE_TIME; break; // pclog("Read Header MSF!\n"); @@ -1772,7 +2160,7 @@ static void atapicommand(int ide_board) idebufferb[0]=1; /*2048 bytes user data*/ idebufferb[1]=idebufferb[2]=idebufferb[3]=0; - ide->packetstatus=3; + ide->packetstatus = ATAPI_STATUS_DATA; ide->cylinder=8; ide->secount=2; ide->pos=0; @@ -1780,53 +2168,59 @@ static void atapicommand(int ide_board) ide->packlen=8; return; - case GPCMD_MODE_SENSE_10: -// output=3; -// pclog("Mode sense - ready?\n"); - if (!atapi->ready()) { atapi_notready(ide); return; } + case GPCMD_MODE_SENSE_6: + case GPCMD_MODE_SENSE_10: + temp_command = idebufferb[0]; + + if (temp_command == GPCMD_MODE_SENSE_6) + len=idebufferb[4]; + else len=(idebufferb[8]|(idebufferb[7]<<8)); -//pclog("Mode sense %02X %i\n",idebufferb[2],len); - temp=idebufferb[2]; -// switch (idebufferb[2]) -// { -// case GPMODE_ALL_PAGES: -// case GPMODE_CAPABILITIES_PAGE: - for (c=0;c>8; - idebufferb[1]=len&255; - idebufferb[2]=3; /*120mm data CD-ROM*/ -// pclog("ATAPI buffer len %i\n",len); -/* for (c=0;cpacketstatus=3; - ide->cylinder=len; - ide->secount=2; -// ide.atastat = DRQ_STAT; - ide->pos=0; - idecallback[ide_board]=1000*IDE_TIME; - ide->packlen=len; -// pclog("Sending packet\n"); - return; + memset(idebufferb, 0, len); + alloc_length = len; + // for (c=0;catastat = READY_STAT | ERR_STAT; /*CHECK CONDITION*/ + ide->error = (SENSE_ILLEGAL_REQUEST << 4) | ABRT_ERR; + idecallback[ide_board]=50*IDE_TIME; + atapi_cmd_error(ide, atapi_sense.sensekey, atapi_sense.asc); + ide->atastat = 0x53; + ide->packetstatus = ATAPI_STATUS_ERROR; + atapi_sense.sensekey = SENSE_ILLEGAL_REQUEST; + atapi_sense.asc = ASC_INV_FIELD_IN_CMD_PACKET; + atapi_sense.ascq = 0; + return; + } + + if (temp_command == GPCMD_MODE_SENSE_6) + { + len = ide_atapi_mode_sense(ide,4,temp); + idebufferb[0] = len - 1; + idebufferb[1]=3; /*120mm data CD-ROM*/ + } + else + { + len = ide_atapi_mode_sense(ide,8,temp); + idebufferb[0]=(len - 2)>>8; + idebufferb[1]=(len - 2)&255; + idebufferb[2]=3; /*120mm data CD-ROM*/ + } - case GPCMD_MODE_SELECT_10: -// if (!atapi->ready()) { atapi_notready(); return; } - if (ide->packetstatus==5) + atapi_command_send_init(ide, temp_command, len, alloc_length); + + atapi_command_ready(ide_board, len); + return; + + case GPCMD_MODE_SELECT_6: + case GPCMD_MODE_SELECT_10: + if (ide->packetstatus == ATAPI_STATUS_PACKET_RECEIVED) { - ide->atastat = 0; + ide->atastat = READY_STAT; + ide->secount=3; // pclog("Recieve data packet!\n"); ide_irq_raise(ide); ide->packetstatus=0xFF; @@ -1836,12 +2230,24 @@ static void atapicommand(int ide_board) } else { - len=(idebufferb[7]<<8)|idebufferb[8]; - ide->packetstatus=4; + if (idebufferb[0] == GPCMD_MODE_SELECT_6) + { + len=idebufferb[4]; + prefix_len = 6; + } + else + { + len=(idebufferb[7]<<8)|idebufferb[8]; + prefix_len = 10; + } + page_current = idebufferb[2]; + if (page_flags[page_current] & PAGE_CHANGEABLE) + page_flags[GPMODE_CDROM_AUDIO_PAGE] |= PAGE_CHANGED; + ide->packetstatus = ATAPI_STATUS_PACKET_REQ; ide->cylinder=len; ide->secount=0; ide->pos=0; - idecallback[ide_board]=6*IDE_TIME; + idecallback[ide_board]=60*IDE_TIME; ide->packlen=len; /* pclog("Waiting for ARM to send packet %i\n",len); pclog("Packet data :\n"); @@ -1851,40 +2257,188 @@ static void atapicommand(int ide_board) } return; - case GPCMD_PLAY_AUDIO_12: - if (!atapi->ready()) { atapi_notready(ide); return; } - /*This is apparently deprecated in the ATAPI spec, and apparently + case GPCMD_GET_CONFIGURATION: /*0x46*/ + if (idebufferb[2] != 0 || idebufferb[3] != 0) + { + ide->atastat = READY_STAT | ERR_STAT; + ide->error = (SENSE_ILLEGAL_REQUEST << 4) | ABRT_ERR; + if (atapi_sense.sensekey == SENSE_UNIT_ATTENTION) + ide->error |= MCR_ERR; + atapi_sense.asc = ASC_INV_FIELD_IN_CMD_PACKET; + ide->packetstatus = ATAPI_STATUS_ERROR; + idecallback[ide_board]=50*IDE_TIME; + break; + } + /* The number of sectors from the media tells us which profile + to use as current. 0 means there is no media. */ + pos = (idebufferb[2]<<24)|(idebufferb[3]<<16)|(idebufferb[4]<<8)|idebufferb[5]; + len = (idebufferb[7]<<8)|idebufferb[8]; + idebufferb[10] = 0x02 | 0x01; + len = 8 + 4; + ide->packetstatus = ATAPI_STATUS_DATA; + ide->cylinder=len; + ide->secount=2; + ide->pos=0; + idecallback[ide_board]=60*IDE_TIME; + ide->packlen=len; + break; + + case GPCMD_GET_EVENT_STATUS_NOTIFICATION: /*0x4a*/ + temp_command = idebufferb[0]; + alloc_length = len; + + { + struct + { + uint8_t opcode; + uint8_t polled; + uint8_t reserved2[2]; + uint8_t class; + uint8_t reserved3[2]; + uint16_t len; + uint8_t control; + } *gesn_cdb; + + struct + { + uint16_t len; + uint8_t notification_class; + uint8_t supported_events; + } *gesn_event_header; + unsigned int used_len; + + gesn_cdb = (void *)idebufferb; + gesn_event_header = (void *)idebufferb; + + /* It is fine by the MMC spec to not support async mode operations */ + if (!(gesn_cdb->polled & 0x01)) + { /* asynchronous mode */ + /* Only pollign is supported, asynchronous mode is not. */ + ide->error = (SENSE_ILLEGAL_REQUEST << 4) | ABRT_ERR; + idecallback[ide_board]=50*IDE_TIME; + atapi_cmd_error(ide, atapi_sense.sensekey, atapi_sense.asc); + ide->atastat = 0x53; + ide->packetstatus=0x80; + atapi_sense.sensekey = SENSE_ILLEGAL_REQUEST; + atapi_sense.asc = ASC_INV_FIELD_IN_CMD_PACKET; + atapi_sense.ascq = 0; + return; + } + + /* polling mode operation */ + + /* + * These are the supported events. + * + * We currently only support requests of the 'media' type. + * Notification class requests and supported event classes are bitmasks, + * but they are built from the same values as the "notification class" + * field. + */ + gesn_event_header->supported_events = 1 << GESN_MEDIA; + + /* + * We use |= below to set the class field; other bits in this byte + * are reserved now but this is useful to do if we have to use the + * reserved fields later. + */ + gesn_event_header->notification_class = 0; + + /* + * Responses to requests are to be based on request priority. The + * notification_class_request_type enum above specifies the + * priority: upper elements are higher prio than lower ones. + */ + if (gesn_cdb->class & (1 << GESN_MEDIA)) + { + gesn_event_header->notification_class |= GESN_MEDIA; + used_len = atapi_event_status(ide, idebufferb); + } + else + { + gesn_event_header->notification_class = 0x80; /* No event available */ + used_len = sizeof(*gesn_event_header); + } + gesn_event_header->len = used_len - sizeof(*gesn_event_header); + } + + atapi_command_send_init(ide, temp_command, len, alloc_length); + + atapi_command_ready(ide_board, len); + break; + + case GPCMD_READ_DISC_INFORMATION: + idebufferb[1] = 32; + idebufferb[2] = 0xe; /* last session complete, disc finalized */ + idebufferb[3] = 1; /* first track on disc */ + idebufferb[4] = 1; /* # of sessions */ + idebufferb[5] = 1; /* first track of last session */ + idebufferb[6] = 1; /* last track of last session */ + idebufferb[7] = 0x20; /* unrestricted use */ + idebufferb[8] = 0x00; /* CD-ROM */ + + len=34; + ide->packetstatus = ATAPI_STATUS_DATA; + ide->cylinder=len; + ide->secount=2; + ide->pos=0; + idecallback[ide_board]=60*IDE_TIME; + ide->packlen=len; + break; + + case GPCMD_PLAY_AUDIO_10: + case GPCMD_PLAY_AUDIO_12: + case GPCMD_PLAY_AUDIO_MSF: + /*This is apparently deprecated in the ATAPI spec, and apparently has been since 1995 (!). Hence I'm having to guess most of it*/ - pos=(idebufferb[3]<<16)|(idebufferb[4]<<8)|idebufferb[5]; - len=(idebufferb[7]<<16)|(idebufferb[8]<<8)|idebufferb[9]; - atapi->playaudio(pos,len,0); - ide->packetstatus=2; + if (idebufferb[0] == GPCMD_PLAY_AUDIO_10) + { + pos=(idebufferb[2]<<24)|(idebufferb[3]<<16)|(idebufferb[4]<<8)|idebufferb[5]; + len=(idebufferb[7]<<8)|idebufferb[8]; + } + else if (idebufferb[0] == GPCMD_PLAY_AUDIO_MSF) + { + pos=(idebufferb[3]<<16)|(idebufferb[4]<<8)|idebufferb[5]; + len=(idebufferb[6]<<16)|(idebufferb[7]<<8)|idebufferb[8]; + } + else + { + pos=(idebufferb[3]<<16)|(idebufferb[4]<<8)|idebufferb[5]; + len=(idebufferb[7]<<16)|(idebufferb[8]<<8)|idebufferb[9]; + } + + + if ((cdrom_drive < 1) || (cdrom_drive == CDROM_ISO) || (cd_status <= CD_STATUS_DATA_ONLY) || + !atapi->is_track_audio(pos, (idebufferb[0] == GPCMD_PLAY_AUDIO_MSF) ? 1 : 0)) + { + ide->atastat = READY_STAT | ERR_STAT; /*CHECK CONDITION*/ + ide->error = (SENSE_ILLEGAL_REQUEST << 4) | ABRT_ERR; + atapi_sense.sensekey = SENSE_ILLEGAL_REQUEST; + atapi_sense.asc = ASC_ILLEGAL_MODE_FOR_THIS_TRACK; + atapi_sense.ascq = 0; + ide->packetstatus = ATAPI_STATUS_ERROR; + idecallback[ide_board]=50*IDE_TIME; + atapi_cmd_error(ide, atapi_sense.sensekey, atapi_sense.asc); + break; + } + + atapi->playaudio(pos, len, (idebufferb[0] == GPCMD_PLAY_AUDIO_MSF) ? 1 : 0); + ide->packetstatus = ATAPI_STATUS_COMPLETE; idecallback[ide_board]=50*IDE_TIME; break; - case GPCMD_PLAY_AUDIO_MSF: - if (!atapi->ready()) { atapi_notready(ide); return; } - pos=(idebufferb[3]<<16)|(idebufferb[4]<<8)|idebufferb[5]; - len=(idebufferb[6]<<16)|(idebufferb[7]<<8)|idebufferb[8]; - atapi->playaudio(pos,len,1); - ide->packetstatus=2; - idecallback[ide_board]=50*IDE_TIME; - break; - - case GPCMD_READ_SUBCHANNEL: - if (!atapi->ready()) { /*pclog("Read subchannel not ready\n"); */atapi_notready(ide); return; } + case GPCMD_READ_SUBCHANNEL: temp=idebufferb[2]&0x40; if (idebufferb[3]!=1) { // pclog("Read subchannel check condition %02X\n",idebufferb[3]); ide->atastat = READY_STAT | ERR_STAT; /*CHECK CONDITION*/ ide->error = (SENSE_ILLEGAL_REQUEST << 4) | ABRT_ERR; - if (ide->discchanged) { + if (atapi_sense.sensekey == SENSE_UNIT_ATTENTION) ide->error |= MCR_ERR; - } - ide->discchanged=0; + // ide->discchanged=1; /* Fixes some bugs with NT 3.1. */ atapi_sense.asc = ASC_ILLEGAL_OPCODE; - ide->packetstatus=0x80; + ide->packetstatus = ATAPI_STATUS_ERROR; idecallback[ide_board]=50*IDE_TIME; break; /* pclog("Bad read subchannel!\n"); @@ -1903,7 +2457,7 @@ static void atapicommand(int ide_board) // pclog("Read subchannel complete - audio status %02X\n",idebufferb[1]); len=11+5; if (!temp) len=4; - ide->packetstatus=3; + ide->packetstatus = ATAPI_STATUS_DATA; ide->cylinder=len; ide->secount=2; ide->pos=0; @@ -1911,17 +2465,15 @@ static void atapicommand(int ide_board) ide->packlen=len; break; - case GPCMD_START_STOP_UNIT: + case GPCMD_START_STOP_UNIT: if (idebufferb[4]!=2 && idebufferb[4]!=3 && idebufferb[4]) { ide->atastat = READY_STAT | ERR_STAT; /*CHECK CONDITION*/ ide->error = (SENSE_ILLEGAL_REQUEST << 4) | ABRT_ERR; - if (ide->discchanged) { + if (atapi_sense.sensekey == SENSE_UNIT_ATTENTION) ide->error |= MCR_ERR; - } - ide->discchanged=0; atapi_sense.asc = ASC_ILLEGAL_OPCODE; - ide->packetstatus=0x80; + ide->packetstatus = ATAPI_STATUS_ERROR; idecallback[ide_board]=50*IDE_TIME; break; /* pclog("Bad start/stop unit command\n"); @@ -1933,61 +2485,126 @@ static void atapicommand(int ide_board) if (!idebufferb[4]) atapi->stop(); else if (idebufferb[4]==2) atapi->eject(); else atapi->load(); - ide->packetstatus=2; + ide->packetstatus = ATAPI_STATUS_COMPLETE; idecallback[ide_board]=50*IDE_TIME; break; - case GPCMD_INQUIRY: - idebufferb[0] = 5; /*CD-ROM*/ - idebufferb[1] = 0x80; /*Removable*/ - idebufferb[2] = 0; - idebufferb[3] = 0x21; - idebufferb[4] = 31; - idebufferb[5] = 0; - idebufferb[6] = 0; - idebufferb[7] = 0; -#ifdef RPCEMU_IDE - ide_padstr8(idebufferb + 8, 8, "RPCemu"); /* Vendor */ - ide_padstr8(idebufferb + 16, 16, "RPCemuCD"); /* Product */ -#else - ide_padstr8(idebufferb + 8, 8, "PCem"); /* Vendor */ - ide_padstr8(idebufferb + 16, 16, "PCemCD"); /* Product */ -#endif - ide_padstr8(idebufferb + 32, 4, "v1.0"); /* Revision */ + case GPCMD_INQUIRY: + page_code = idebufferb[2]; + max_len = idebufferb[4]; + alloc_length = max_len; + temp_command = idebufferb[0]; + + if (idebufferb[1] & 1) + { + preamble_len = 4; + size_idx = 3; + + idebufferb[idx++] = 05; + idebufferb[idx++] = page_code; + idebufferb[idx++] = 0; + + idx++; + + switch (page_code) + { + case 0x00: + idebufferb[idx++] = 0x00; + idebufferb[idx++] = 0x83; + break; + case 0x83: + if (idx + 24 > max_len) + { + atapi_cmd_error(ide, SENSE_ILLEGAL_REQUEST, ASC_DATA_PHASE_ERROR); + atapi_sense.sensekey = SENSE_ILLEGAL_REQUEST; + atapi_sense.asc = ASC_DATA_PHASE_ERROR; + return; + } + idebufferb[idx++] = 0x02; + idebufferb[idx++] = 0x00; + idebufferb[idx++] = 0x00; + idebufferb[idx++] = 20; + ide_padstr8(idebufferb + idx, 20, "53R141"); /* Serial */ + idx += 20; - len=36; - ide->packetstatus=3; - ide->cylinder=len; - ide->secount=2; - ide->pos=0; - idecallback[ide_board]=60*IDE_TIME; - ide->packlen=len; + if (idx + 72 > max_len) + { + goto atapi_out; + } + idebufferb[idx++] = 0x02; + idebufferb[idx++] = 0x01; + idebufferb[idx++] = 0x00; + idebufferb[idx++] = 68; + ide_padstr8(idebufferb + idx, 8, "PCem"); /* Vendor */ + idx += 8; + ide_padstr8(idebufferb + idx, 40, "PCemCD v1.0"); /* Product */ + idx += 40; + ide_padstr8(idebufferb + idx, 20, "53R141"); /* Product */ + idx += 20; + + break; + default: + atapi_cmd_error(ide, SENSE_ILLEGAL_REQUEST, ASC_INV_FIELD_IN_CMD_PACKET); + atapi_sense.sensekey = SENSE_ILLEGAL_REQUEST; + atapi_sense.asc = ASC_INV_FIELD_IN_CMD_PACKET; + return; + } + } + else + { + preamble_len = 5; + size_idx = 4; + + idebufferb[0] = 5; /*CD-ROM*/ + idebufferb[1] = 0x80; /*Removable*/ + idebufferb[2] = 0; + idebufferb[3] = 0x21; + idebufferb[4] = 31; + idebufferb[5] = 0; + idebufferb[6] = 0; + idebufferb[7] = 0; +#ifdef RPCEMU_IDE + ide_padstr8(idebufferb + 8, 8, "RPCemu"); /* Vendor */ + ide_padstr8(idebufferb + 16, 16, "RPCemuCD"); /* Product */ +#else + ide_padstr8(idebufferb + 8, 8, "PCem"); /* Vendor */ + ide_padstr8(idebufferb + 16, 16, "PCemCD"); /* Product */ +#endif + ide_padstr8(idebufferb + 32, 4, "1.0"); /* Revision */ + + idx = 36; + } + +atapi_out: + idebufferb[size_idx] = idx - preamble_len; + len=idx; + + atapi_command_send_init(ide, temp_command, len, alloc_length); + + atapi_command_ready(ide_board, len); break; - case GPCMD_PREVENT_REMOVAL: - if (!atapi->ready()) { atapi_notready(ide); return; } - ide->packetstatus=2; + case GPCMD_PREVENT_REMOVAL: + ide->packetstatus = ATAPI_STATUS_COMPLETE; idecallback[ide_board]=50*IDE_TIME; break; - case GPCMD_PAUSE_RESUME: - if (!atapi->ready()) { atapi_notready(ide); return; } + case GPCMD_PAUSE_RESUME: if (idebufferb[8]&1) atapi->resume(); else atapi->pause(); - ide->packetstatus=2; + ide->packetstatus = ATAPI_STATUS_COMPLETE; idecallback[ide_board]=50*IDE_TIME; break; - case GPCMD_SEEK: - if (!atapi->ready()) { atapi_notready(ide); return; } + case GPCMD_SEEK: pos=(idebufferb[3]<<16)|(idebufferb[4]<<8)|idebufferb[5]; atapi->seek(pos); - ide->packetstatus=2; + ide->packetstatus = ATAPI_STATUS_COMPLETE; idecallback[ide_board]=50*IDE_TIME; break; - case GPCMD_READ_CDROM_CAPACITY: - if (!atapi->ready()) { atapi_notready(ide); return; } + case GPCMD_READ_CDROM_CAPACITY: + atapi_command_send_init(ide, temp_command, 8, 8); size = atapi->size(); idebufferb[0] = (size >> 24) & 0xff; idebufferb[1] = (size >> 16) & 0xff; @@ -1998,31 +2615,25 @@ static void atapicommand(int ide_board) idebufferb[6] = (2048 >> 8) & 0xff; idebufferb[7] = 2048 & 0xff; len=8; - ide->packetstatus=3; - ide->cylinder=len; - ide->secount=2; - ide->pos=0; - idecallback[ide_board]=60*IDE_TIME; - ide->packlen=len; + atapi_command_ready(ide_board, len); break; - case GPCMD_SEND_DVD_STRUCTURE: - default: - ide->atastat = READY_STAT | ERR_STAT; /*CHECK CONDITION*/ + case GPCMD_SEND_DVD_STRUCTURE: + default: +bad_atapi_command: + ide->atastat = READY_STAT | ERR_STAT; /*CHECK CONDITION*/ ide->error = (SENSE_ILLEGAL_REQUEST << 4) | ABRT_ERR; - if (ide->discchanged) { + if (atapi_sense.sensekey == SENSE_UNIT_ATTENTION) ide->error |= MCR_ERR; - } - ide->discchanged=0; + atapi_sense.sensekey = SENSE_ILLEGAL_REQUEST; atapi_sense.asc = ASC_ILLEGAL_OPCODE; - ide->packetstatus=0x80; + ide->packetstatus = ATAPI_STATUS_ERROR; idecallback[ide_board]=50*IDE_TIME; break; - case GPCMD_STOP_PLAY_SCAN: - if (!atapi->ready()) { atapi_notready(ide); return; } + case GPCMD_STOP_PLAY_SCAN: atapi->stop(); - ide->packetstatus=2; + ide->packetstatus = ATAPI_STATUS_COMPLETE; idecallback[ide_board]=50*IDE_TIME; break; /* default: @@ -2034,31 +2645,54 @@ static void atapicommand(int ide_board) } } +static void callnonreadcd(IDE *ide) /* Callabck for non-Read CD commands */ +{ + ide_irq_lower(ide); + if (ide->pos >= ide->packlen) + { + // pclog("Command finished, setting callback\n"); + ide->packetstatus = ATAPI_STATUS_COMPLETE; + idecallback[ide->board]=20*IDE_TIME; + } + else + { + // pclog("Command not finished, keep sending data\n"); + ide->atastat = BUSY_STAT; + ide->packetstatus = ATAPI_STATUS_REQ_SENSE; + ide->cylinder=2; + ide->secount=2; + idecallback[ide->board]=60*IDE_TIME; + } +} + static void callreadcd(IDE *ide) { ide_irq_lower(ide); if (ide->cdlen<=0) { // pclog("All done - callback set\n"); - ide->packetstatus=2; + ide->packetstatus = ATAPI_STATUS_COMPLETE; idecallback[ide->board]=20*IDE_TIME; return; } // pclog("Continue readcd! %i blocks left\n",ide->cdlen); ide->atastat = BUSY_STAT; - atapi->readsector((uint8_t *) ide->buffer, ide->cdpos); + if (readcdmode) + atapi->readsector_raw((uint8_t *) ide->buffer, ide->cdpos); + else + atapi->readsector((uint8_t *) ide->buffer, ide->cdpos); #ifndef RPCEMU_IDE - readflash=1; + readflash=1; #endif - ide->cdpos++; - ide->cdlen--; - ide->packetstatus=6; - ide->cylinder=2048; - ide->secount=2; - ide->pos=0; - idecallback[ide->board]=60*IDE_TIME; - ide->packlen=2048; + ide->cdpos++; + ide->cdlen--; + ide->packetstatus = ATAPI_STATUS_READCD; + ide->cylinder=readcdmode ? 2352 : 2048; + ide->secount=2; + ide->pos=0; + idecallback[ide->board]=60*IDE_TIME; + ide->packlen=readcdmode ? 2352 : 2048; } diff --git a/src/ide.h b/src/ide.h index 7f9a2ff..1ae14bc 100644 --- a/src/ide.h +++ b/src/ide.h @@ -20,10 +20,13 @@ extern void ide_set_bus_master(int (*read_sector)(int channel, uint8_t *data), i typedef struct ATAPI { int (*ready)(void); + int (*medium_changed)(void); int (*readtoc)(uint8_t *b, uint8_t starttrack, int msf, int maxlen, int single); - void (*readtoc_session)(uint8_t *b, int msf, int maxlen); + int (*readtoc_session)(uint8_t *b, int msf, int maxlen); + int (*readtoc_raw)(uint8_t *b, int maxlen); uint8_t (*getcurrentsubchannel)(uint8_t *b, int msf); void (*readsector)(uint8_t *b, int sector); + void (*readsector_raw)(uint8_t *b, int sector); void (*playaudio)(uint32_t pos, uint32_t len, int ismsf); void (*seek)(uint32_t pos); void (*load)(void); @@ -31,6 +34,8 @@ typedef struct ATAPI void (*pause)(void); void (*resume)(void); uint32_t (*size)(void); + int (*status)(void); + int (*is_track_audio)(uint32_t pos, int ismsf); void (*stop)(void); void (*exit)(void); } ATAPI; @@ -38,6 +43,7 @@ typedef struct ATAPI extern ATAPI *atapi; void atapi_discchanged(); +void atapi_insert_cdrom(); extern int ideboard; @@ -47,4 +53,6 @@ extern char ide_fn[4][512]; extern int cdrom_channel; +#define CDROM_ISO 200 + #endif //__IDE__ diff --git a/src/pc.c b/src/pc.c index 3c242ab..654979a 100644 --- a/src/pc.c +++ b/src/pc.c @@ -276,7 +276,22 @@ void initpc(int argc, char *argv[]) { if (cdrom_drive == CDROM_ISO) { - iso_open(iso_path); + FILE *ff = fopen(iso_path, "rb"); + if (ff) + { + fclose(ff); + iso_open(iso_path); + } + else + { +#if __unix + cdrom_drive = -1; + cdrom_null_open(cdrom_drive); +#else + cdrom_drive = 0; + ioctl_open(cdrom_drive); +#endif + } } else { diff --git a/src/sound.c b/src/sound.c index 7f1d48f..639e736 100644 --- a/src/sound.c +++ b/src/sound.c @@ -110,10 +110,15 @@ static void sound_cd_thread(void *param) ioctl_audio_callback(cd_buffer, CD_BUFLEN*2); if (soundon) { + int32_t atapi_vol_l = atapi_get_cd_volume(0); + int32_t atapi_vol_r = atapi_get_cd_volume(1); + for (c = 0; c < CD_BUFLEN*2; c += 2) { cd_buffer[c] = ((int32_t)cd_buffer[c] * cd_vol_l) / 65535; + cd_buffer[c] = ((int32_t)cd_buffer[c] * atapi_vol_l) / 255; cd_buffer[c+1] = ((int32_t)cd_buffer[c+1] * cd_vol_r) / 65535; + cd_buffer[c+1] = ((int32_t)cd_buffer[c+1] * atapi_vol_r) / 255; } givealbuffer_cd(cd_buffer); } diff --git a/src/win.c b/src/win.c index ab04582..539e79e 100644 --- a/src/win.c +++ b/src/win.c @@ -830,11 +830,25 @@ LRESULT CALLBACK LowLevelKeyboardProc( int nCode, WPARAM wParam, LPARAM lParam ) return CallNextHookEx( hKeyboardHook, nCode, wParam, lParam ); } +void atapi_close(void) +{ + switch (cdrom_drive) + { + case CDROM_ISO: + iso_close(); + break; + default: + ioctl_close(); + break; + } +} + LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { HMENU hmenu; RECT rect; char temp_iso_path[1024]; + int new_cdrom_drive; // pclog("Message %i %08X\n",message,message); switch (message) { @@ -980,12 +994,21 @@ LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM { if (MessageBox(NULL,"This will reset PCem!\nOkay to continue?","PCem",MB_OKCANCEL) != IDOK) break; - } - atapi->exit(); + } + if (!cdrom_enabled) + { + /* Switching from disabled to disabled. Do nothing. */ + break; + } + atapi->exit(); + atapi_close(); + ioctl_open(0); CheckMenuItem(hmenu, IDM_CDROM_REAL + cdrom_drive, MF_UNCHECKED); CheckMenuItem(hmenu, IDM_CDROM_DISABLED, MF_CHECKED); + CheckMenuItem(hmenu, IDM_CDROM_ISO, MF_UNCHECKED); + old_cdrom_drive = cdrom_drive; + cdrom_drive=0; CheckMenuItem(hmenu, IDM_CDROM_EMPTY, MF_UNCHECKED); - CheckMenuItem(hmenu, IDM_CDROM_ISO, MF_UNCHECKED); if (cdrom_enabled) { pause = 1; @@ -1002,12 +1025,24 @@ LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM { if (MessageBox(NULL,"This will reset PCem!\nOkay to continue?","PCem",MB_OKCANCEL) != IDOK) break; - } - atapi->exit(); + } + if ((cdrom_drive == 0) && cdrom_enabled) + { + /* Switch from empty to empty. Do nothing. */ + break; + } + atapi->exit(); + atapi_close(); ioctl_open(0); + if (cdrom_enabled) + { + /* Signal disc change to the emulated machine. */ + atapi_insert_cdrom(); + } CheckMenuItem(hmenu, IDM_CDROM_REAL + cdrom_drive, MF_UNCHECKED); CheckMenuItem(hmenu, IDM_CDROM_DISABLED, MF_UNCHECKED); - CheckMenuItem(hmenu, IDM_CDROM_ISO, MF_UNCHECKED); + CheckMenuItem(hmenu, IDM_CDROM_ISO, MF_UNCHECKED); + old_cdrom_drive = cdrom_drive; cdrom_drive=0; CheckMenuItem(hmenu, IDM_CDROM_EMPTY, MF_CHECKED); saveconfig(); @@ -1030,14 +1065,26 @@ LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM if (MessageBox(NULL,"This will reset PCem!\nOkay to continue?","PCem",MB_OKCANCEL) != IDOK) break; } - atapi->exit(); + old_cdrom_drive = cdrom_drive; strcpy(temp_iso_path, openfilestring); + if ((strcmp(iso_path, temp_iso_path) == 0) && (cdrom_drive == CDROM_ISO) && cdrom_enabled) + { + /* Switching from ISO to the same ISO. Do nothing. */ + break; + } + atapi->exit(); + atapi_close(); + iso_open(temp_iso_path); + if (cdrom_enabled) + { + /* Signal disc change to the emulated machine. */ + atapi_insert_cdrom(); + } CheckMenuItem(hmenu, IDM_CDROM_REAL + cdrom_drive, MF_UNCHECKED); CheckMenuItem(hmenu, IDM_CDROM_DISABLED, MF_UNCHECKED); - CheckMenuItem(hmenu, IDM_CDROM_ISO, MF_UNCHECKED); + CheckMenuItem(hmenu, IDM_CDROM_ISO, MF_UNCHECKED); cdrom_drive = CDROM_ISO; - iso_open(temp_iso_path); - CheckMenuItem(hmenu, IDM_CDROM_ISO, MF_CHECKED); + CheckMenuItem(hmenu, IDM_CDROM_ISO, MF_CHECKED); saveconfig(); if (!cdrom_enabled) { @@ -1058,13 +1105,26 @@ LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM { if (MessageBox(NULL,"This will reset PCem!\nOkay to continue?","PCem",MB_OKCANCEL) != IDOK) break; - } - atapi->exit(); - ioctl_open(LOWORD(wParam)-IDM_CDROM_REAL); + } + new_cdrom_drive = LOWORD(wParam)-IDM_CDROM_REAL; + if ((cdrom_drive == new_cdrom_drive) && cdrom_enabled) + { + /* Switching to the same drive. Do nothing. */ + break; + } + old_cdrom_drive = cdrom_drive; + atapi->exit(); + atapi_close(); + ioctl_open(new_cdrom_drive); + if (cdrom_enabled) + { + /* Signal disc change to the emulated machine. */ + atapi_insert_cdrom(); + } CheckMenuItem(hmenu, IDM_CDROM_REAL + cdrom_drive, MF_UNCHECKED); CheckMenuItem(hmenu, IDM_CDROM_DISABLED, MF_UNCHECKED); - CheckMenuItem(hmenu, IDM_CDROM_ISO, MF_UNCHECKED); - cdrom_drive = LOWORD(wParam) - IDM_CDROM_REAL; + CheckMenuItem(hmenu, IDM_CDROM_ISO, MF_UNCHECKED); + cdrom_drive = new_cdrom_drive; CheckMenuItem(hmenu, IDM_CDROM_REAL + cdrom_drive, MF_CHECKED); saveconfig(); if (!cdrom_enabled)