Index: /trunk/src/VBox/Devices/Makefile.kmk
===================================================================
--- /trunk/src/VBox/Devices/Makefile.kmk	(revision 43566)
+++ /trunk/src/VBox/Devices/Makefile.kmk	(revision 43567)
@@ -207,4 +207,5 @@
  	Storage/Debug.cpp \
  	Storage/DrvVD.cpp \
+ 	Storage/ATAPIPassthrough.cpp \
  	Network/DrvNetSniffer.cpp \
  	Network/Pcap.cpp
Index: /trunk/src/VBox/Devices/Storage/ATAPIPassthrough.cpp
===================================================================
--- /trunk/src/VBox/Devices/Storage/ATAPIPassthrough.cpp	(revision 43567)
+++ /trunk/src/VBox/Devices/Storage/ATAPIPassthrough.cpp	(revision 43567)
@@ -0,0 +1,540 @@
+/* $Id$ */
+/** @file
+ * VBox storage devices: ATAPI emulation (common code for DevATA and DevAHCI).
+ */
+
+/*
+ * Copyright (C) 2012 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+#define LOG_GROUP LOG_GROUP_DEV_IDE
+#include <iprt/log.h>
+#include <iprt/assert.h>
+#include <iprt/mem.h>
+
+#include <VBox/log.h>
+#include <VBox/err.h>
+#include <VBox/cdefs.h>
+#include <VBox/scsi.h>
+
+#include "ATAPIPassthrough.h"
+
+/** The track was not detected yet. */
+#define TRACK_FLAGS_UNDETECTED   RT_BIT_32(0)
+/** The track is the lead in track of the medium. */
+#define TRACK_FLAGS_LEAD_IN      RT_BIT_32(1)
+/** The track is the lead out track of the medium. */
+#define TRACK_FLAGS_LEAD_OUT     RT_BIT_32(2)
+
+/**
+ * Track main data form.
+ */
+typedef enum TRACKDATAFORM
+{
+    /** Invalid data form. */
+    TRACKDATAFORM_INVALID = 0,
+    /** 2352 bytes of data. */
+    TRACKDATAFORM_CDDA,
+    /** CDDA data is pause. */
+    TRACKDATAFORM_CDDA_PAUSE,
+    /** Mode 1 with 2048 bytes sector size. */
+    TRACKDATAFORM_MODE1_2048,
+    /** Mode 1 with 2352 bytes sector size. */
+    TRACKDATAFORM_MODE1_2352,
+    /** Mode 1 with 0 bytes sector size (generated by the drive). */
+    TRACKDATAFORM_MODE1_0,
+    /** XA Mode with 2336 bytes sector size. */
+    TRACKDATAFORM_XA_2336,
+    /** XA Mode with 2352 bytes sector size. */
+    TRACKDATAFORM_XA_2352,
+    /** XA Mode with 0 bytes sector size (generated by the drive). */
+    TRACKDATAFORM_XA_0,
+    /** Mode 2 with 2336 bytes sector size. */
+    TRACKDATAFORM_MODE2_2336,
+    /** Mode 2 with 2352 bytes sector size. */
+    TRACKDATAFORM_MODE2_2352,
+    /** Mode 2 with 0 bytes sector size (generated by the drive). */
+    TRACKDATAFORM_MODE2_0
+} TRACKDATAFORM;
+
+/**
+ * Subchannel data form.
+ */
+typedef enum SUBCHNDATAFORM
+{
+    /** Invalid subchannel data form. */
+    SUBCHNDATAFORM_INVALID = 0,
+    /** 0 bytes for the subchannel (generated by the drive). */
+    SUBCHNDATAFORM_0,
+    /** 96 bytes of data for the subchannel. */
+    SUBCHNDATAFORM_96
+} SUBCHNDATAFORM;
+
+/**
+ * Track entry.
+ */
+typedef struct TRACK
+{
+    /** Start LBA of the track. */
+    int64_t        iLbaStart;
+    /** Number of sectors in the track. */
+    uint32_t       cSectors;
+    /** Data form of main data. */
+    TRACKDATAFORM  enmMainDataForm;
+    /** Data form of sub channel. */
+    SUBCHNDATAFORM enmSubChnDataForm;
+    /** Flags for the track. */
+    uint32_t       fFlags;
+} TRACK, *PTRACK;
+
+/**
+ * Media track list.
+ */
+typedef struct TRACKLIST
+{
+    /** Number of detected tracks of the current medium. */
+    unsigned    cTracksCurrent;
+    /** Maximum number of tracks the list can contain. */
+    unsigned    cTracksMax;
+    /** Variable list of tracks. */
+    PTRACK      paTracks;
+} TRACKLIST, *PTRACKLIST;
+
+DECLINLINE(uint16_t) atapiBE2H_U16(const uint8_t *pbBuf)
+{
+    return (pbBuf[0] << 8) | pbBuf[1];
+}
+
+
+DECLINLINE(uint32_t) atapiBE2H_U24(const uint8_t *pbBuf)
+{
+    return (pbBuf[0] << 16) | (pbBuf[1] << 8) | pbBuf[2];
+}
+
+
+DECLINLINE(uint32_t) atapiBE2H_U32(const uint8_t *pbBuf)
+{
+    return (pbBuf[0] << 24) | (pbBuf[1] << 16) | (pbBuf[2] << 8) | pbBuf[3];
+}
+
+DECLINLINE(int64_t) atapiMSF2LBA(const uint8_t *pbBuf)
+{
+    return ((int64_t)(pbBuf[0] * 60 + pbBuf[1]) * 75 + pbBuf[2]) - 150; /* 2 second pregap */
+}
+
+/**
+ * Reallocate the given track list to be able to hold the given number of tracks.
+ *
+ * @returns VBox status code.
+ * @param   pTrackList    The track list to reallocate.
+ * @param   cTracks       Number of tracks the list must be able to hold.
+ */
+static int atapiTrackListReallocate(PTRACKLIST pTrackList, unsigned cTracks)
+{
+    int rc = VINF_SUCCESS;
+
+    ATAPIPassthroughTrackListClear(pTrackList);
+
+    if (pTrackList->cTracksMax < cTracks)
+    {
+        PTRACK paTracksNew = (PTRACK)RTMemRealloc(pTrackList->paTracks, cTracks * sizeof(TRACK));
+        if (paTracksNew)
+        {
+            pTrackList->paTracks = paTracksNew;
+
+            /* Mark new tracks as undetected. */
+            for (unsigned i = pTrackList->cTracksMax; i < cTracks; i++)
+                pTrackList->paTracks[i].fFlags |= TRACK_FLAGS_UNDETECTED;
+
+            pTrackList->cTracksMax = cTracks;
+        }
+        else
+            rc = VERR_NO_MEMORY;
+    }
+
+    pTrackList->cTracksCurrent = cTracks;
+
+    return rc;
+}
+
+/**
+ * Initilizes the given track from the given CUE sheet entry.
+ *
+ * @returns nothing.
+ * @param   pTrack             The track to initialize.
+ * @param   pbCueSheetEntry    CUE sheet entry to use.
+ */
+static void atapiTrackListEntryCreateFromCueSheetEntry(PTRACK pTrack, uint8_t *pbCueSheetEntry)
+{
+    TRACKDATAFORM enmTrackDataForm = TRACKDATAFORM_INVALID;
+    SUBCHNDATAFORM enmSubChnDataForm = SUBCHNDATAFORM_INVALID;
+
+    /* Determine size of main data based on the data form field. */
+    switch (pbCueSheetEntry[3] & 0x3f)
+    {
+        case 0x00: /* CD-DA with data. */
+            enmTrackDataForm = TRACKDATAFORM_CDDA;
+            break;
+        case 0x01: /* CD-DA without data (used for pauses between tracks). */
+            enmTrackDataForm = TRACKDATAFORM_CDDA_PAUSE;
+            break;
+        case 0x10: /* CD-ROM mode 1 */
+        case 0x12:
+            enmTrackDataForm = TRACKDATAFORM_MODE1_2048;
+            break;
+        case 0x11:
+        case 0x13:
+            enmTrackDataForm = TRACKDATAFORM_MODE1_2352;
+            break;
+        case 0x14:
+            enmTrackDataForm = TRACKDATAFORM_MODE1_0;
+            break;
+        case 0x20: /* CD-ROM XA, CD-I */
+        case 0x22:
+            enmTrackDataForm = TRACKDATAFORM_XA_2336;
+            break;
+        case 0x21:
+        case 0x23:
+            enmTrackDataForm = TRACKDATAFORM_XA_2352;
+            break;
+        case 0x24:
+            enmTrackDataForm = TRACKDATAFORM_XA_0;
+            break;
+        case 0x31: /* CD-ROM Mode 2 */
+        case 0x33:
+            enmTrackDataForm = TRACKDATAFORM_MODE2_2352;
+            break;
+        case 0x30:
+        case 0x32:
+            enmTrackDataForm = TRACKDATAFORM_MODE2_2336;
+            break;
+        case 0x34:
+            enmTrackDataForm = TRACKDATAFORM_MODE2_0;
+            break;
+        default: /* Reserved, invalid mode. Log and leave default sector size. */
+            LogRel(("ATA: Invalid data form mode %d for current CUE sheet\n",
+                    pbCueSheetEntry[3] & 0x3f));
+    }
+
+    /* Determine size of sub channel data based on data form field. */
+    switch ((pbCueSheetEntry[3] & 0xc0) >> 6)
+    {
+        case 0x00: /* Sub channel all zeroes, autogenerated by the drive. */
+            enmSubChnDataForm = SUBCHNDATAFORM_0;
+            break;
+        case 0x01:
+        case 0x03:
+            enmSubChnDataForm = SUBCHNDATAFORM_96;
+            break;
+        default:
+            LogRel(("ATA: Invalid sub-channel data form mode %u for current CUE sheet\n",
+                    pbCueSheetEntry[3] & 0xc0));
+    }
+
+    pTrack->enmMainDataForm = enmTrackDataForm;
+    pTrack->enmSubChnDataForm = enmSubChnDataForm;
+    pTrack->iLbaStart = atapiMSF2LBA(&pbCueSheetEntry[5]);
+    if (pbCueSheetEntry[1] != 0xaa)
+    {
+        /* Calculate number of sectors from the next entry. */
+        int64_t iLbaNext = atapiMSF2LBA(&pbCueSheetEntry[5+8]);
+        pTrack->cSectors = iLbaNext - pTrack->iLbaStart;
+    }
+    else
+    {
+        pTrack->fFlags |= TRACK_FLAGS_LEAD_OUT;
+        pTrack->cSectors = 0;
+    }
+    pTrack->fFlags &= ~TRACK_FLAGS_UNDETECTED;
+}
+
+/**
+ * Update the track list from a SEND CUE SHEET request.
+ *
+ * @returns VBox status code.
+ * @param   pTrackList    Track list to update.
+ * @param   pbCDB         CDB of the SEND CUE SHEET request.
+ * @param   pvBuf         The CUE sheet.
+ */
+static int atapiTrackListUpdateFromSendCueSheet(PTRACKLIST pTrackList, uint8_t *pbCDB, void *pvBuf)
+{
+    int rc = VINF_SUCCESS;
+    size_t cbCueSheet = atapiBE2H_U24(pbCDB + 6);
+    unsigned cTracks = cbCueSheet / 8;
+
+    AssertReturn(cbCueSheet % 8 == 0 && cTracks, VERR_INVALID_PARAMETER);
+
+    rc = atapiTrackListReallocate(pTrackList, cTracks);
+    if (RT_SUCCESS(rc))
+    {
+        uint8_t *pbCueSheet = (uint8_t *)pvBuf;
+        PTRACK pTrack = pTrackList->paTracks;
+
+        for (unsigned i = 0; i < cTracks; i++)
+        {
+            atapiTrackListEntryCreateFromCueSheetEntry(pTrack, pbCueSheet);
+            if (i == 0)
+                pTrack->fFlags |= TRACK_FLAGS_LEAD_IN;
+            pTrack++;
+            pbCueSheet += 8;
+        }
+    }
+
+    return rc;
+}
+
+static int atapiTrackListUpdateFromSendDvdStructure(PTRACKLIST pTrackList, uint8_t *pbCDB, void *pvBuf)
+{
+    return VERR_NOT_IMPLEMENTED;
+}
+
+static int atapiTrackListUpdateFromReadTocPmaAtip(PTRACKLIST pTrackList, uint8_t *pbCDB, void *pvBuf)
+{
+    return VERR_NOT_IMPLEMENTED;
+}
+
+static int atapiTrackListUpdateFromReadTrackInformation(PTRACKLIST pTrackList, uint8_t *pbCDB, void *pvBuf)
+{
+    return VERR_NOT_IMPLEMENTED;
+}
+
+static int atapiTrackListUpdateFromReadDvdStructure(PTRACKLIST pTrackList, uint8_t *pbCDB, void *pvBuf)
+{
+    return VERR_NOT_IMPLEMENTED;
+}
+
+static int atapiTrackListUpdateFromReadDiscInformation(PTRACKLIST pTrackList, uint8_t *pbCDB, void *pvBuf)
+{
+    return VERR_NOT_IMPLEMENTED;
+}
+
+/**
+ * Converts the given track data form to a string.
+ *
+ * @returns Track data form as a string.
+ * @param   enmTrackDataForm    The track main data form.
+ */
+static const char *atapiTrackListMainDataFormToString(TRACKDATAFORM enmTrackDataForm)
+{
+    switch (enmTrackDataForm)
+    {
+        case TRACKDATAFORM_CDDA:
+            return "CD-DA";
+        case TRACKDATAFORM_CDDA_PAUSE:
+            return "CD-DA Pause";
+        case TRACKDATAFORM_MODE1_2048:
+            return "Mode 1 (2048 bytes)";
+        case TRACKDATAFORM_MODE1_2352:
+            return "Mode 1 (2352 bytes)";
+        case TRACKDATAFORM_MODE1_0:
+            return "Mode 1 (0 bytes)";
+        case TRACKDATAFORM_XA_2336:
+            return "XA (2336 bytes)";
+        case TRACKDATAFORM_XA_2352:
+            return "XA (2352 bytes)";
+        case TRACKDATAFORM_XA_0:
+            return "XA (0 bytes)";
+        case TRACKDATAFORM_MODE2_2336:
+            return "Mode 2 (2336 bytes)";
+        case TRACKDATAFORM_MODE2_2352:
+            return "Mode 2 (2352 bytes)";
+        case TRACKDATAFORM_MODE2_0:
+            return "Mode 2 (0 bytes)";
+        case TRACKDATAFORM_INVALID:
+        default:
+            return "Invalid";
+    }
+}
+
+/**
+ * Converts the given subchannel data form to a string.
+ *
+ * @returns Subchannel data form as a string.
+ * @param   enmSubChnDataForm    The subchannel main data form.
+ */
+static const char *atapiTrackListSubChnDataFormToString(SUBCHNDATAFORM enmSubChnDataForm)
+{
+    switch (enmSubChnDataForm)
+    {
+        case SUBCHNDATAFORM_0:
+            return "0";
+        case SUBCHNDATAFORM_96:
+            return "96";
+        case SUBCHNDATAFORM_INVALID:
+        default:
+            return "Invalid";
+    }
+}
+
+/**
+ * Dump the complete track list to the release log.
+ *
+ * @returns nothing.
+ * @param   pTrackList   The track list to dump.
+ */
+static void atapiTrackListDump(PTRACKLIST pTrackList)
+{
+    LogRel(("Track List: cTracks=%u\n", pTrackList->cTracksCurrent));
+    for (unsigned i = 0; i < pTrackList->cTracksCurrent; i++)
+    {
+        PTRACK pTrack = &pTrackList->paTracks[i];
+
+        LogRel(("    Track %u: LBAStart=%lld cSectors=%u enmMainDataForm=%s enmSubChnDataForm=%s fFlags=[%s%s%s]\n",
+                i, pTrack->iLbaStart, pTrack->cSectors, atapiTrackListMainDataFormToString(pTrack->enmMainDataForm),
+                atapiTrackListSubChnDataFormToString(pTrack->enmSubChnDataForm),
+                pTrack->fFlags & TRACK_FLAGS_UNDETECTED ? "UNDETECTED " : "",
+                pTrack->fFlags & TRACK_FLAGS_LEAD_IN ? "Lead-In " : "",
+                pTrack->fFlags & TRACK_FLAGS_LEAD_OUT ? "Lead-Out" : ""));
+    }
+}
+
+DECLHIDDEN(int) ATAPIPassthroughTrackListCreateEmpty(PTRACKLIST *ppTrackList)
+{
+    int rc = VERR_NO_MEMORY;
+    PTRACKLIST pTrackList = (PTRACKLIST)RTMemAllocZ(sizeof(TRACKLIST));
+
+    if (pTrackList)
+    {
+        rc = VINF_SUCCESS;
+        *ppTrackList = pTrackList;
+    }
+
+    return rc;
+}
+
+DECLHIDDEN(void) ATAPIPassthroughTrackListDestroy(PTRACKLIST pTrackList)
+{
+    if (pTrackList->paTracks)
+        RTMemFree(pTrackList->paTracks);
+    RTMemFree(pTrackList);
+}
+
+DECLHIDDEN(void) ATAPIPassthroughTrackListClear(PTRACKLIST pTrackList)
+{
+    pTrackList->cTracksCurrent = 0;
+
+    /* Mark all tracks as undetected. */
+    for (unsigned i = 0; i < pTrackList->cTracksMax; i++)
+        pTrackList->paTracks[i].fFlags |= TRACK_FLAGS_UNDETECTED;
+}
+
+DECLHIDDEN(int) ATAPIPassthroughTrackListUpdate(PTRACKLIST pTrackList, uint8_t *pbCDB, void *pvBuf)
+{
+    int rc = VINF_SUCCESS;
+
+    switch (pbCDB[0])
+    {
+        case SCSI_SEND_CUE_SHEET:
+            rc = atapiTrackListUpdateFromSendCueSheet(pTrackList, pbCDB, pvBuf);
+            break;
+        case SCSI_SEND_DVD_STRUCTURE:
+            rc = atapiTrackListUpdateFromSendDvdStructure(pTrackList, pbCDB, pvBuf);
+            break;
+        case SCSI_READ_TOC_PMA_ATIP:
+            rc = atapiTrackListUpdateFromReadTocPmaAtip(pTrackList, pbCDB, pvBuf);
+            break;
+        case SCSI_READ_TRACK_INFORMATION:
+            rc = atapiTrackListUpdateFromReadTrackInformation(pTrackList, pbCDB, pvBuf);
+            break;
+        case SCSI_READ_DVD_STRUCTURE:
+            rc = atapiTrackListUpdateFromReadDvdStructure(pTrackList, pbCDB, pvBuf);
+            break;
+        case SCSI_READ_DISC_INFORMATION:
+            rc = atapiTrackListUpdateFromReadDiscInformation(pTrackList, pbCDB, pvBuf);
+            break;
+        default:
+            LogRel(("ATAPI: Invalid opcode %#x while determining media layout\n", pbCDB[0]));
+            rc = VERR_INVALID_PARAMETER;
+    }
+
+    atapiTrackListDump(pTrackList);
+
+    return rc;
+}
+
+DECLHIDDEN(size_t) ATAPIPassthroughTrackListGetSectorSizeFromLba(PTRACKLIST pTrackList, uint32_t iAtapiLba)
+{
+    PTRACK pTrack = NULL;
+    size_t cbAtapiSector = 2048;
+
+    if (pTrackList->cTracksCurrent)
+    {
+        if (   iAtapiLba > UINT32_C(0xffff4fa1)
+            && (int32_t)iAtapiLba < -150)
+        {
+            /* Lead-In area, this is always the first entry in the cue sheet. */
+            pTrack = pTrackList->paTracks;
+            Assert(pTrack->fFlags & TRACK_FLAGS_LEAD_IN);
+            LogFlowFunc(("Selected Lead-In area\n"));
+        }
+        else
+        {
+            int64_t iAtapiLba64 = (int32_t)iAtapiLba;
+            pTrack = &pTrackList->paTracks[1];
+
+            /* Go through the track list and find the correct entry. */
+            for (unsigned i = 1; i < pTrackList->cTracksCurrent - 1; i++)
+            {
+                if (pTrack->fFlags & TRACK_FLAGS_UNDETECTED)
+                    continue;
+
+                if (   pTrack->iLbaStart <= iAtapiLba64
+                    && iAtapiLba64 < pTrack->iLbaStart + pTrack->cSectors)
+                    break;
+
+                pTrack++;
+            }
+        }
+
+        if (pTrack)
+        {
+            switch (pTrack->enmMainDataForm)
+            {
+                case TRACKDATAFORM_CDDA:
+                case TRACKDATAFORM_MODE1_2352:
+                case TRACKDATAFORM_XA_2352:
+                case TRACKDATAFORM_MODE2_2352:
+                    cbAtapiSector = 2352;
+                    break;
+                case TRACKDATAFORM_MODE1_2048:
+                    cbAtapiSector = 2048;
+                    break;
+                case TRACKDATAFORM_CDDA_PAUSE:
+                case TRACKDATAFORM_MODE1_0:
+                case TRACKDATAFORM_XA_0:
+                case TRACKDATAFORM_MODE2_0:
+                    cbAtapiSector = 0;
+                    break;
+                case TRACKDATAFORM_XA_2336:
+                case TRACKDATAFORM_MODE2_2336:
+                    cbAtapiSector = 2336;
+                    break;
+                case TRACKDATAFORM_INVALID:
+                default:
+                    AssertMsgFailed(("Invalid track data form %d\n", pTrack->enmMainDataForm));
+            }
+
+            switch (pTrack->enmSubChnDataForm)
+            {
+                case SUBCHNDATAFORM_0:
+                    break;
+                case SUBCHNDATAFORM_96:
+                    cbAtapiSector += 96;
+                    break;
+                case SUBCHNDATAFORM_INVALID:
+                default:
+                    AssertMsgFailed(("Invalid subchannel data form %d\n", pTrack->enmSubChnDataForm));
+            }
+        }
+    }
+
+    return cbAtapiSector;
+}
+
Index: /trunk/src/VBox/Devices/Storage/ATAPIPassthrough.h
===================================================================
--- /trunk/src/VBox/Devices/Storage/ATAPIPassthrough.h	(revision 43567)
+++ /trunk/src/VBox/Devices/Storage/ATAPIPassthrough.h	(revision 43567)
@@ -0,0 +1,74 @@
+/* $Id$ */
+/** @file
+ * VBox storage devices: ATAPI passthrough helpers (common code for DevATA and DevAHCI).
+ */
+
+/*
+ * Copyright (C) 2012 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+#ifndef __ATAPIPassthrough_h
+#define __ATAPIPassthrough_h
+
+#include <VBox/cdefs.h>
+
+RT_C_DECLS_BEGIN
+
+/**
+ * Opaque media track list.
+ */
+typedef struct TRACKLIST *PTRACKLIST;
+
+/**
+ * Creates an empty track list handle.
+ *
+ * @returns VBox status code.
+ * @param   ppTrackList    Where to store the track list handle on success.
+ */
+DECLHIDDEN(int) ATAPIPassthroughTrackListCreateEmpty(PTRACKLIST *ppTrackList);
+
+/**
+ * Destroys the allocated task list handle.
+ *
+ * @returns nothing.
+ * @param   pTrackList    The track list handle to destroy.
+ */
+DECLHIDDEN(void) ATAPIPassthroughTrackListDestroy(PTRACKLIST pTrackList);
+
+/**
+ * Clears all tracks from the given task list.
+ *
+ * @returns nothing.
+ * @param   pTrackList    The track list to clear.
+ */
+DECLHIDDEN(void) ATAPIPassthroughTrackListClear(PTRACKLIST pTrackList);
+
+/**
+ * Updates the track list from the given CDB and data buffer.
+ *
+ * @returns VBox status code.
+ * @param   pTrackList    The track list to update.
+ * @param   pCDB          The CDB buffer.
+ * @param   pvBuf         The data buffer.
+ */
+DECLHIDDEN(int) ATAPIPassthroughTrackListUpdate(PTRACKLIST pTrackList, uint8_t *pCDB, void *pvBuf);
+
+/**
+ * Return the sector size from the track matching the LBA in the given track list.
+ *
+ * @returns Sector size.
+ * @param   pTrackList    The track list to use.
+ * @param   iAtapiLba     The start LBA to get the sector size for.
+ */
+DECLHIDDEN(size_t) ATAPIPassthroughTrackListGetSectorSizeFromLba(PTRACKLIST pTrackList, uint32_t iAtapiLba);
+
+RT_C_DECLS_END
+
+#endif /* __ATAPIPassthrough_h */
