ntdll: Fix CDROM raw reads.

- Fix incorrect data sizes.
- Fix CDDA addressing.
- Implement XAForm2 mode on Linux.
This commit is contained in:
Petr Tesarik 2006-03-27 10:20:39 +02:00 committed by Alexandre Julliard
parent d74a62cfd7
commit 4c777d7466
1 changed files with 86 additions and 74 deletions

View File

@ -1192,85 +1192,95 @@ static NTSTATUS CDROM_SetVolume(int fd, const VOLUME_CONTROL* vc)
/****************************************************************** /******************************************************************
* CDROM_RawRead * CDROM_RawRead
* *
* Some features of this IOCTL are rather poorly documented and
* not really intuitive either:
*
* 1. Although the DiskOffset parameter is meant to be a
* byte offset into the disk, it is in fact the sector
* number multiplied by 2048 regardless of the actual
* sector size.
*
* 2. The least significant 11 bits of DiskOffset are ignored.
*
* 3. The TrackMode parameter has no effect on the sector
* size. The entire raw sector (i.e. 2352 bytes of data)
* is always returned. IMO the TrackMode is only used
* to check the correct sector type.
* *
*/ */
static NTSTATUS CDROM_RawRead(int fd, const RAW_READ_INFO* raw, void* buffer, DWORD len, DWORD* sz) static NTSTATUS CDROM_RawRead(int fd, const RAW_READ_INFO* raw, void* buffer, DWORD len, DWORD* sz)
{ {
int ret = STATUS_NOT_SUPPORTED; int ret = STATUS_NOT_SUPPORTED;
int io = -1; int io = -1;
DWORD sectSize;
TRACE("RAW_READ_INFO: DiskOffset=%li,%li SectorCount=%li TrackMode=%i\n buffer=%p len=%li sz=%p\n", TRACE("RAW_READ_INFO: DiskOffset=%li,%li SectorCount=%li TrackMode=%i\n buffer=%p len=%li sz=%p\n",
raw->DiskOffset.u.HighPart, raw->DiskOffset.u.LowPart, raw->SectorCount, raw->TrackMode, buffer, len, sz); raw->DiskOffset.u.HighPart, raw->DiskOffset.u.LowPart, raw->SectorCount, raw->TrackMode, buffer, len, sz);
switch (raw->TrackMode) if (len < raw->SectorCount * 2352) return STATUS_BUFFER_TOO_SMALL;
{
case YellowMode2: sectSize = 2336; break;
case XAForm2: sectSize = 2328; break;
case CDDA: sectSize = 2352; break;
default: return STATUS_INVALID_PARAMETER;
}
if (len < raw->SectorCount * sectSize) return STATUS_BUFFER_TOO_SMALL;
/* strangely enough, it seems that sector offsets are always indicated with a size of 2048,
* even if a larger size if read...
*/
#if defined(linux) #if defined(linux)
{ if (raw->DiskOffset.u.HighPart & ~2047) {
struct cdrom_read_audio cdra; WARN("DiskOffset points to a sector >= 2**32\n");
struct cdrom_msf* msf; return ret;
int i; }
LONGLONG t = ((LONGLONG)raw->DiskOffset.u.HighPart << 32) +
raw->DiskOffset.u.LowPart + CD_MSF_OFFSET;
switch (raw->TrackMode) switch (raw->TrackMode)
{ {
case YellowMode2: case YellowMode2:
case XAForm2:
{
DWORD lba = raw->DiskOffset.QuadPart >> 11;
struct cdrom_msf* msf;
PBYTE *bp; /* current buffer pointer */
int i;
if ((lba + raw->SectorCount) >
((1 << 8*sizeof(msf->cdmsf_min0)) * CD_SECS * CD_FRAMES
- CD_MSF_OFFSET)) {
WARN("DiskOffset not accessible with MSF\n");
return ret;
}
/* Linux reads only one sector at a time. /* Linux reads only one sector at a time.
* ioctl CDROMREADMODE2 takes struct cdrom_msf as an argument * ioctl CDROMREADRAW takes struct cdrom_msf as an argument
* on the contrary to what header comments state. * on the contrary to what header comments state.
*/ */
for (i = 0; i < raw->SectorCount; i++, t += sectSize) lba += CD_MSF_OFFSET;
for (i = 0, bp = buffer; i < raw->SectorCount;
i++, lba++, bp += 2352)
{ {
msf = (struct cdrom_msf*)buffer + i * sectSize; msf = (struct cdrom_msf*)bp;
msf->cdmsf_min0 = t / CD_FRAMES / CD_SECS; msf->cdmsf_min0 = lba / CD_FRAMES / CD_SECS;
msf->cdmsf_sec0 = t / CD_FRAMES % CD_SECS; msf->cdmsf_sec0 = lba / CD_FRAMES % CD_SECS;
msf->cdmsf_frame0 = t % CD_FRAMES; msf->cdmsf_frame0 = lba % CD_FRAMES;
io = ioctl(fd, CDROMREADMODE2, msf); io = ioctl(fd, CDROMREADRAW, msf);
if (io != 0) if (io != 0)
{ {
*sz = sectSize * i; *sz = 2352 * i;
return CDROM_GetStatusCode(io); return CDROM_GetStatusCode(io);
} }
} }
break; break;
case XAForm2: }
FIXME("XAForm2: NIY\n");
return ret;
case CDDA: case CDDA:
/* FIXME: the output doesn't seem 100% correct... in fact output is shifted {
* between by NT2K box and this... should check on the same drive... struct cdrom_read_audio cdra;
* otherwise, I fear a 2352/2368 mismatch somewhere in one of the drivers
* (linux/NT). cdra.addr.lba = raw->DiskOffset.QuadPart >> 11;
* Anyway, that's not critical at all. We're talking of 16/32 bytes, we're TRACE("reading at %u\n", cdra.addr.lba);
* talking of 0.2 ms of sound
*/
/* 2048 = 2 ** 11 */
if (raw->DiskOffset.u.HighPart & ~2047) FIXME("Unsupported value\n");
cdra.addr.lba = ((raw->DiskOffset.u.LowPart >> 11) |
(raw->DiskOffset.u.HighPart << (32 - 11))) - 1;
FIXME("reading at %u\n", cdra.addr.lba);
cdra.addr_format = CDROM_LBA; cdra.addr_format = CDROM_LBA;
cdra.nframes = raw->SectorCount; cdra.nframes = raw->SectorCount;
cdra.buf = buffer; cdra.buf = buffer;
io = ioctl(fd, CDROMREADAUDIO, &cdra); io = ioctl(fd, CDROMREADAUDIO, &cdra);
break; break;
}
default: default:
FIXME("NIY: %d\n", raw->TrackMode); FIXME("NIY: %d\n", raw->TrackMode);
return ret; return STATUS_INVALID_PARAMETER;
}
} }
#else #else
{
switch (raw->TrackMode) switch (raw->TrackMode)
{ {
case YellowMode2: case YellowMode2:
@ -1282,11 +1292,13 @@ static NTSTATUS CDROM_RawRead(int fd, const RAW_READ_INFO* raw, void* buffer, DW
case CDDA: case CDDA:
FIXME("CDDA: NIY\n"); FIXME("CDDA: NIY\n");
return ret; return ret;
} default:
FIXME("NIY: %d\n", raw->TrackMode);
return STATUS_INVALID_PARAMETER;
} }
#endif #endif
*sz = sectSize * raw->SectorCount; *sz = 2352 * raw->SectorCount;
ret = CDROM_GetStatusCode(io); ret = CDROM_GetStatusCode(io);
return ret; return ret;
} }