ntdll: Fix CDROM raw reads

Petr Tesarik hat at tesarici.cz
Mon Mar 27 02:20:39 CST 2006


Hi folks,

this patch fixes some bugs in raw CDROM reads, and implements the
missing features.

>From my experiments with an XP box and several CDs in various formats,
I have concluded that:

1. DiskOffset is really LBA sector * 2048, regardless of the actual
   physical/data size of the sectors.

2. The lowest 11 bits of DiskOffset are ignored.

3. 2352 bytes of raw data are returned, regardless of the requested
   mode.  IMO the TrackMode field only instructs the driver to check
   that the correct type of sector is present.

4. The CDDA addressing is in fact correct both under Linux and in
   Windows, but unless the drive is "accurate", there is the 1 sec
   uncertainty (see CD-ROM specifications for details).

ChangeLog:

* Fix incorrect data sizes
* Fix CDDA addressing
* Implement XAForm2 mode on Linux
-------------- next part --------------
Index: cdrom.c
===================================================================
RCS file: /home/wine/wine/dlls/ntdll/cdrom.c,v
retrieving revision 1.76
diff -u -r1.76 cdrom.c
--- cdrom.c	11 Feb 2006 19:41:15 -0000	1.76
+++ cdrom.c	27 Mar 2006 08:18:39 -0000
@@ -1192,101 +1192,115 @@
 /******************************************************************
  *		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)
 {
-    int	        ret = STATUS_NOT_SUPPORTED;
+    int         ret = STATUS_NOT_SUPPORTED;
     int         io = -1;
-    DWORD       sectSize;
 
     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);
 
+    if (len < raw->SectorCount * 2352) return STATUS_BUFFER_TOO_SMALL;
+
+#if defined(linux)
+    if (raw->DiskOffset.u.HighPart & ~2047) {
+        WARN("DiskOffset points to a sector >= 2**32\n");
+        return ret;
+    }
+
     switch (raw->TrackMode)
     {
-    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)
+    case YellowMode2:
+    case XAForm2:
     {
-        struct cdrom_read_audio cdra;
         struct cdrom_msf*       msf;
+        DWORD lba = (raw->DiskOffset.u.LowPart >> 11) |
+            (raw->DiskOffset.u.HighPart << (32 - 11));
+        PBYTE                   *bp; /* current buffer pointer */
         int i;
-        LONGLONG t = ((LONGLONG)raw->DiskOffset.u.HighPart << 32) +
-                                raw->DiskOffset.u.LowPart + CD_MSF_OFFSET;
 
-        switch (raw->TrackMode)
+        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.
+         * ioctl CDROMREADRAW takes struct cdrom_msf as an argument
+         * on the contrary to what header comments state.
+         */
+        lba += CD_MSF_OFFSET;
+        for (i = 0, bp = buffer; i < raw->SectorCount;
+             i++, lba++, bp += 2352)
         {
-        case YellowMode2:
-            /* Linux reads only one sector at a time.
-             * ioctl CDROMREADMODE2 takes struct cdrom_msf as an argument
-             * on the contrary to what header comments state.
-             */
-            for (i = 0; i < raw->SectorCount; i++, t += sectSize)
+            msf = (struct cdrom_msf*)bp;
+            msf->cdmsf_min0   = lba / CD_FRAMES / CD_SECS;
+            msf->cdmsf_sec0   = lba / CD_FRAMES % CD_SECS;
+            msf->cdmsf_frame0 = lba % CD_FRAMES;
+            io = ioctl(fd, CDROMREADRAW, msf);
+            if (io != 0)
             {
-                msf = (struct cdrom_msf*)buffer + i * sectSize;
-                msf->cdmsf_min0   = t / CD_FRAMES / CD_SECS;
-                msf->cdmsf_sec0   = t / CD_FRAMES % CD_SECS;
-                msf->cdmsf_frame0 = t % CD_FRAMES;
-                io = ioctl(fd, CDROMREADMODE2, msf);
-                if (io != 0)
-                {
-                    *sz = sectSize * i;
-                    return CDROM_GetStatusCode(io);
-                }
+                *sz = 2352 * i;
+                return CDROM_GetStatusCode(io);
             }
-            break;
-        case XAForm2:
-            FIXME("XAForm2: NIY\n");
-            return ret;
-        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...
-             * otherwise, I fear a 2352/2368 mismatch somewhere in one of the drivers
-             * (linux/NT).
-             * Anyway, that's not critical at all. We're talking of 16/32 bytes, we're
-             * 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.nframes = raw->SectorCount;
-            cdra.buf = buffer;
-            io = ioctl(fd, CDROMREADAUDIO, &cdra);
-            break;
-        default:
-            FIXME("NIY: %d\n", raw->TrackMode);
-            return ret;
         }
+        break;
+    }
+
+    case CDDA:
+    {
+        struct cdrom_read_audio cdra;
+
+        cdra.addr.lba = (raw->DiskOffset.u.LowPart >> 11) |
+            (raw->DiskOffset.u.HighPart << (32 - 11));
+        TRACE("reading at %u\n", cdra.addr.lba);
+        cdra.addr_format = CDROM_LBA;
+        cdra.nframes = raw->SectorCount;
+        cdra.buf = buffer;
+        io = ioctl(fd, CDROMREADAUDIO, &cdra);
+        break;
+    }
+
+    default:
+        FIXME("NIY: %d\n", raw->TrackMode);
+        return STATUS_INVALID_PARAMETER;
     }
 #else
+    switch (raw->TrackMode)
     {
-        switch (raw->TrackMode)
-        {
-        case YellowMode2:
-            FIXME("YellowMode2: NIY\n");
-            return ret;
-        case XAForm2:
-            FIXME("XAForm2: NIY\n");
-            return ret;
-        case CDDA:
-            FIXME("CDDA: NIY\n");
-            return ret;
-        }
+    case YellowMode2:
+        FIXME("YellowMode2: NIY\n");
+        return ret;
+    case XAForm2:
+        FIXME("XAForm2: NIY\n");
+        return ret;
+    case CDDA:
+        FIXME("CDDA: NIY\n");
+        return ret;
+    default:
+        FIXME("NIY: %d\n", raw->TrackMode);
+        return STATUS_INVALID_PARAMETER;
     }
 #endif
 
-    *sz = sectSize * raw->SectorCount;
+    *sz = 2352 * raw->SectorCount;
     ret = CDROM_GetStatusCode(io);
     return ret;
 }


More information about the wine-patches mailing list