VirtualBox

source: vbox/trunk/src/VBox/Devices/Storage/ATAPIPassthrough.cpp@ 43570

Last change on this file since 43570 was 43567, checked in by vboxsync, 13 years ago

Storage/ATAPIPassthrough: Start common code for track list handling in passthrough mode (used for multisession disks and audio CD burning)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.4 KB
Line 
1/* $Id: ATAPIPassthrough.cpp 43567 2012-10-08 16:32:26Z vboxsync $ */
2/** @file
3 * VBox storage devices: ATAPI emulation (common code for DevATA and DevAHCI).
4 */
5
6/*
7 * Copyright (C) 2012 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17#define LOG_GROUP LOG_GROUP_DEV_IDE
18#include <iprt/log.h>
19#include <iprt/assert.h>
20#include <iprt/mem.h>
21
22#include <VBox/log.h>
23#include <VBox/err.h>
24#include <VBox/cdefs.h>
25#include <VBox/scsi.h>
26
27#include "ATAPIPassthrough.h"
28
29/** The track was not detected yet. */
30#define TRACK_FLAGS_UNDETECTED RT_BIT_32(0)
31/** The track is the lead in track of the medium. */
32#define TRACK_FLAGS_LEAD_IN RT_BIT_32(1)
33/** The track is the lead out track of the medium. */
34#define TRACK_FLAGS_LEAD_OUT RT_BIT_32(2)
35
36/**
37 * Track main data form.
38 */
39typedef enum TRACKDATAFORM
40{
41 /** Invalid data form. */
42 TRACKDATAFORM_INVALID = 0,
43 /** 2352 bytes of data. */
44 TRACKDATAFORM_CDDA,
45 /** CDDA data is pause. */
46 TRACKDATAFORM_CDDA_PAUSE,
47 /** Mode 1 with 2048 bytes sector size. */
48 TRACKDATAFORM_MODE1_2048,
49 /** Mode 1 with 2352 bytes sector size. */
50 TRACKDATAFORM_MODE1_2352,
51 /** Mode 1 with 0 bytes sector size (generated by the drive). */
52 TRACKDATAFORM_MODE1_0,
53 /** XA Mode with 2336 bytes sector size. */
54 TRACKDATAFORM_XA_2336,
55 /** XA Mode with 2352 bytes sector size. */
56 TRACKDATAFORM_XA_2352,
57 /** XA Mode with 0 bytes sector size (generated by the drive). */
58 TRACKDATAFORM_XA_0,
59 /** Mode 2 with 2336 bytes sector size. */
60 TRACKDATAFORM_MODE2_2336,
61 /** Mode 2 with 2352 bytes sector size. */
62 TRACKDATAFORM_MODE2_2352,
63 /** Mode 2 with 0 bytes sector size (generated by the drive). */
64 TRACKDATAFORM_MODE2_0
65} TRACKDATAFORM;
66
67/**
68 * Subchannel data form.
69 */
70typedef enum SUBCHNDATAFORM
71{
72 /** Invalid subchannel data form. */
73 SUBCHNDATAFORM_INVALID = 0,
74 /** 0 bytes for the subchannel (generated by the drive). */
75 SUBCHNDATAFORM_0,
76 /** 96 bytes of data for the subchannel. */
77 SUBCHNDATAFORM_96
78} SUBCHNDATAFORM;
79
80/**
81 * Track entry.
82 */
83typedef struct TRACK
84{
85 /** Start LBA of the track. */
86 int64_t iLbaStart;
87 /** Number of sectors in the track. */
88 uint32_t cSectors;
89 /** Data form of main data. */
90 TRACKDATAFORM enmMainDataForm;
91 /** Data form of sub channel. */
92 SUBCHNDATAFORM enmSubChnDataForm;
93 /** Flags for the track. */
94 uint32_t fFlags;
95} TRACK, *PTRACK;
96
97/**
98 * Media track list.
99 */
100typedef struct TRACKLIST
101{
102 /** Number of detected tracks of the current medium. */
103 unsigned cTracksCurrent;
104 /** Maximum number of tracks the list can contain. */
105 unsigned cTracksMax;
106 /** Variable list of tracks. */
107 PTRACK paTracks;
108} TRACKLIST, *PTRACKLIST;
109
110DECLINLINE(uint16_t) atapiBE2H_U16(const uint8_t *pbBuf)
111{
112 return (pbBuf[0] << 8) | pbBuf[1];
113}
114
115
116DECLINLINE(uint32_t) atapiBE2H_U24(const uint8_t *pbBuf)
117{
118 return (pbBuf[0] << 16) | (pbBuf[1] << 8) | pbBuf[2];
119}
120
121
122DECLINLINE(uint32_t) atapiBE2H_U32(const uint8_t *pbBuf)
123{
124 return (pbBuf[0] << 24) | (pbBuf[1] << 16) | (pbBuf[2] << 8) | pbBuf[3];
125}
126
127DECLINLINE(int64_t) atapiMSF2LBA(const uint8_t *pbBuf)
128{
129 return ((int64_t)(pbBuf[0] * 60 + pbBuf[1]) * 75 + pbBuf[2]) - 150; /* 2 second pregap */
130}
131
132/**
133 * Reallocate the given track list to be able to hold the given number of tracks.
134 *
135 * @returns VBox status code.
136 * @param pTrackList The track list to reallocate.
137 * @param cTracks Number of tracks the list must be able to hold.
138 */
139static int atapiTrackListReallocate(PTRACKLIST pTrackList, unsigned cTracks)
140{
141 int rc = VINF_SUCCESS;
142
143 ATAPIPassthroughTrackListClear(pTrackList);
144
145 if (pTrackList->cTracksMax < cTracks)
146 {
147 PTRACK paTracksNew = (PTRACK)RTMemRealloc(pTrackList->paTracks, cTracks * sizeof(TRACK));
148 if (paTracksNew)
149 {
150 pTrackList->paTracks = paTracksNew;
151
152 /* Mark new tracks as undetected. */
153 for (unsigned i = pTrackList->cTracksMax; i < cTracks; i++)
154 pTrackList->paTracks[i].fFlags |= TRACK_FLAGS_UNDETECTED;
155
156 pTrackList->cTracksMax = cTracks;
157 }
158 else
159 rc = VERR_NO_MEMORY;
160 }
161
162 pTrackList->cTracksCurrent = cTracks;
163
164 return rc;
165}
166
167/**
168 * Initilizes the given track from the given CUE sheet entry.
169 *
170 * @returns nothing.
171 * @param pTrack The track to initialize.
172 * @param pbCueSheetEntry CUE sheet entry to use.
173 */
174static void atapiTrackListEntryCreateFromCueSheetEntry(PTRACK pTrack, uint8_t *pbCueSheetEntry)
175{
176 TRACKDATAFORM enmTrackDataForm = TRACKDATAFORM_INVALID;
177 SUBCHNDATAFORM enmSubChnDataForm = SUBCHNDATAFORM_INVALID;
178
179 /* Determine size of main data based on the data form field. */
180 switch (pbCueSheetEntry[3] & 0x3f)
181 {
182 case 0x00: /* CD-DA with data. */
183 enmTrackDataForm = TRACKDATAFORM_CDDA;
184 break;
185 case 0x01: /* CD-DA without data (used for pauses between tracks). */
186 enmTrackDataForm = TRACKDATAFORM_CDDA_PAUSE;
187 break;
188 case 0x10: /* CD-ROM mode 1 */
189 case 0x12:
190 enmTrackDataForm = TRACKDATAFORM_MODE1_2048;
191 break;
192 case 0x11:
193 case 0x13:
194 enmTrackDataForm = TRACKDATAFORM_MODE1_2352;
195 break;
196 case 0x14:
197 enmTrackDataForm = TRACKDATAFORM_MODE1_0;
198 break;
199 case 0x20: /* CD-ROM XA, CD-I */
200 case 0x22:
201 enmTrackDataForm = TRACKDATAFORM_XA_2336;
202 break;
203 case 0x21:
204 case 0x23:
205 enmTrackDataForm = TRACKDATAFORM_XA_2352;
206 break;
207 case 0x24:
208 enmTrackDataForm = TRACKDATAFORM_XA_0;
209 break;
210 case 0x31: /* CD-ROM Mode 2 */
211 case 0x33:
212 enmTrackDataForm = TRACKDATAFORM_MODE2_2352;
213 break;
214 case 0x30:
215 case 0x32:
216 enmTrackDataForm = TRACKDATAFORM_MODE2_2336;
217 break;
218 case 0x34:
219 enmTrackDataForm = TRACKDATAFORM_MODE2_0;
220 break;
221 default: /* Reserved, invalid mode. Log and leave default sector size. */
222 LogRel(("ATA: Invalid data form mode %d for current CUE sheet\n",
223 pbCueSheetEntry[3] & 0x3f));
224 }
225
226 /* Determine size of sub channel data based on data form field. */
227 switch ((pbCueSheetEntry[3] & 0xc0) >> 6)
228 {
229 case 0x00: /* Sub channel all zeroes, autogenerated by the drive. */
230 enmSubChnDataForm = SUBCHNDATAFORM_0;
231 break;
232 case 0x01:
233 case 0x03:
234 enmSubChnDataForm = SUBCHNDATAFORM_96;
235 break;
236 default:
237 LogRel(("ATA: Invalid sub-channel data form mode %u for current CUE sheet\n",
238 pbCueSheetEntry[3] & 0xc0));
239 }
240
241 pTrack->enmMainDataForm = enmTrackDataForm;
242 pTrack->enmSubChnDataForm = enmSubChnDataForm;
243 pTrack->iLbaStart = atapiMSF2LBA(&pbCueSheetEntry[5]);
244 if (pbCueSheetEntry[1] != 0xaa)
245 {
246 /* Calculate number of sectors from the next entry. */
247 int64_t iLbaNext = atapiMSF2LBA(&pbCueSheetEntry[5+8]);
248 pTrack->cSectors = iLbaNext - pTrack->iLbaStart;
249 }
250 else
251 {
252 pTrack->fFlags |= TRACK_FLAGS_LEAD_OUT;
253 pTrack->cSectors = 0;
254 }
255 pTrack->fFlags &= ~TRACK_FLAGS_UNDETECTED;
256}
257
258/**
259 * Update the track list from a SEND CUE SHEET request.
260 *
261 * @returns VBox status code.
262 * @param pTrackList Track list to update.
263 * @param pbCDB CDB of the SEND CUE SHEET request.
264 * @param pvBuf The CUE sheet.
265 */
266static int atapiTrackListUpdateFromSendCueSheet(PTRACKLIST pTrackList, uint8_t *pbCDB, void *pvBuf)
267{
268 int rc = VINF_SUCCESS;
269 size_t cbCueSheet = atapiBE2H_U24(pbCDB + 6);
270 unsigned cTracks = cbCueSheet / 8;
271
272 AssertReturn(cbCueSheet % 8 == 0 && cTracks, VERR_INVALID_PARAMETER);
273
274 rc = atapiTrackListReallocate(pTrackList, cTracks);
275 if (RT_SUCCESS(rc))
276 {
277 uint8_t *pbCueSheet = (uint8_t *)pvBuf;
278 PTRACK pTrack = pTrackList->paTracks;
279
280 for (unsigned i = 0; i < cTracks; i++)
281 {
282 atapiTrackListEntryCreateFromCueSheetEntry(pTrack, pbCueSheet);
283 if (i == 0)
284 pTrack->fFlags |= TRACK_FLAGS_LEAD_IN;
285 pTrack++;
286 pbCueSheet += 8;
287 }
288 }
289
290 return rc;
291}
292
293static int atapiTrackListUpdateFromSendDvdStructure(PTRACKLIST pTrackList, uint8_t *pbCDB, void *pvBuf)
294{
295 return VERR_NOT_IMPLEMENTED;
296}
297
298static int atapiTrackListUpdateFromReadTocPmaAtip(PTRACKLIST pTrackList, uint8_t *pbCDB, void *pvBuf)
299{
300 return VERR_NOT_IMPLEMENTED;
301}
302
303static int atapiTrackListUpdateFromReadTrackInformation(PTRACKLIST pTrackList, uint8_t *pbCDB, void *pvBuf)
304{
305 return VERR_NOT_IMPLEMENTED;
306}
307
308static int atapiTrackListUpdateFromReadDvdStructure(PTRACKLIST pTrackList, uint8_t *pbCDB, void *pvBuf)
309{
310 return VERR_NOT_IMPLEMENTED;
311}
312
313static int atapiTrackListUpdateFromReadDiscInformation(PTRACKLIST pTrackList, uint8_t *pbCDB, void *pvBuf)
314{
315 return VERR_NOT_IMPLEMENTED;
316}
317
318/**
319 * Converts the given track data form to a string.
320 *
321 * @returns Track data form as a string.
322 * @param enmTrackDataForm The track main data form.
323 */
324static const char *atapiTrackListMainDataFormToString(TRACKDATAFORM enmTrackDataForm)
325{
326 switch (enmTrackDataForm)
327 {
328 case TRACKDATAFORM_CDDA:
329 return "CD-DA";
330 case TRACKDATAFORM_CDDA_PAUSE:
331 return "CD-DA Pause";
332 case TRACKDATAFORM_MODE1_2048:
333 return "Mode 1 (2048 bytes)";
334 case TRACKDATAFORM_MODE1_2352:
335 return "Mode 1 (2352 bytes)";
336 case TRACKDATAFORM_MODE1_0:
337 return "Mode 1 (0 bytes)";
338 case TRACKDATAFORM_XA_2336:
339 return "XA (2336 bytes)";
340 case TRACKDATAFORM_XA_2352:
341 return "XA (2352 bytes)";
342 case TRACKDATAFORM_XA_0:
343 return "XA (0 bytes)";
344 case TRACKDATAFORM_MODE2_2336:
345 return "Mode 2 (2336 bytes)";
346 case TRACKDATAFORM_MODE2_2352:
347 return "Mode 2 (2352 bytes)";
348 case TRACKDATAFORM_MODE2_0:
349 return "Mode 2 (0 bytes)";
350 case TRACKDATAFORM_INVALID:
351 default:
352 return "Invalid";
353 }
354}
355
356/**
357 * Converts the given subchannel data form to a string.
358 *
359 * @returns Subchannel data form as a string.
360 * @param enmSubChnDataForm The subchannel main data form.
361 */
362static const char *atapiTrackListSubChnDataFormToString(SUBCHNDATAFORM enmSubChnDataForm)
363{
364 switch (enmSubChnDataForm)
365 {
366 case SUBCHNDATAFORM_0:
367 return "0";
368 case SUBCHNDATAFORM_96:
369 return "96";
370 case SUBCHNDATAFORM_INVALID:
371 default:
372 return "Invalid";
373 }
374}
375
376/**
377 * Dump the complete track list to the release log.
378 *
379 * @returns nothing.
380 * @param pTrackList The track list to dump.
381 */
382static void atapiTrackListDump(PTRACKLIST pTrackList)
383{
384 LogRel(("Track List: cTracks=%u\n", pTrackList->cTracksCurrent));
385 for (unsigned i = 0; i < pTrackList->cTracksCurrent; i++)
386 {
387 PTRACK pTrack = &pTrackList->paTracks[i];
388
389 LogRel((" Track %u: LBAStart=%lld cSectors=%u enmMainDataForm=%s enmSubChnDataForm=%s fFlags=[%s%s%s]\n",
390 i, pTrack->iLbaStart, pTrack->cSectors, atapiTrackListMainDataFormToString(pTrack->enmMainDataForm),
391 atapiTrackListSubChnDataFormToString(pTrack->enmSubChnDataForm),
392 pTrack->fFlags & TRACK_FLAGS_UNDETECTED ? "UNDETECTED " : "",
393 pTrack->fFlags & TRACK_FLAGS_LEAD_IN ? "Lead-In " : "",
394 pTrack->fFlags & TRACK_FLAGS_LEAD_OUT ? "Lead-Out" : ""));
395 }
396}
397
398DECLHIDDEN(int) ATAPIPassthroughTrackListCreateEmpty(PTRACKLIST *ppTrackList)
399{
400 int rc = VERR_NO_MEMORY;
401 PTRACKLIST pTrackList = (PTRACKLIST)RTMemAllocZ(sizeof(TRACKLIST));
402
403 if (pTrackList)
404 {
405 rc = VINF_SUCCESS;
406 *ppTrackList = pTrackList;
407 }
408
409 return rc;
410}
411
412DECLHIDDEN(void) ATAPIPassthroughTrackListDestroy(PTRACKLIST pTrackList)
413{
414 if (pTrackList->paTracks)
415 RTMemFree(pTrackList->paTracks);
416 RTMemFree(pTrackList);
417}
418
419DECLHIDDEN(void) ATAPIPassthroughTrackListClear(PTRACKLIST pTrackList)
420{
421 pTrackList->cTracksCurrent = 0;
422
423 /* Mark all tracks as undetected. */
424 for (unsigned i = 0; i < pTrackList->cTracksMax; i++)
425 pTrackList->paTracks[i].fFlags |= TRACK_FLAGS_UNDETECTED;
426}
427
428DECLHIDDEN(int) ATAPIPassthroughTrackListUpdate(PTRACKLIST pTrackList, uint8_t *pbCDB, void *pvBuf)
429{
430 int rc = VINF_SUCCESS;
431
432 switch (pbCDB[0])
433 {
434 case SCSI_SEND_CUE_SHEET:
435 rc = atapiTrackListUpdateFromSendCueSheet(pTrackList, pbCDB, pvBuf);
436 break;
437 case SCSI_SEND_DVD_STRUCTURE:
438 rc = atapiTrackListUpdateFromSendDvdStructure(pTrackList, pbCDB, pvBuf);
439 break;
440 case SCSI_READ_TOC_PMA_ATIP:
441 rc = atapiTrackListUpdateFromReadTocPmaAtip(pTrackList, pbCDB, pvBuf);
442 break;
443 case SCSI_READ_TRACK_INFORMATION:
444 rc = atapiTrackListUpdateFromReadTrackInformation(pTrackList, pbCDB, pvBuf);
445 break;
446 case SCSI_READ_DVD_STRUCTURE:
447 rc = atapiTrackListUpdateFromReadDvdStructure(pTrackList, pbCDB, pvBuf);
448 break;
449 case SCSI_READ_DISC_INFORMATION:
450 rc = atapiTrackListUpdateFromReadDiscInformation(pTrackList, pbCDB, pvBuf);
451 break;
452 default:
453 LogRel(("ATAPI: Invalid opcode %#x while determining media layout\n", pbCDB[0]));
454 rc = VERR_INVALID_PARAMETER;
455 }
456
457 atapiTrackListDump(pTrackList);
458
459 return rc;
460}
461
462DECLHIDDEN(size_t) ATAPIPassthroughTrackListGetSectorSizeFromLba(PTRACKLIST pTrackList, uint32_t iAtapiLba)
463{
464 PTRACK pTrack = NULL;
465 size_t cbAtapiSector = 2048;
466
467 if (pTrackList->cTracksCurrent)
468 {
469 if ( iAtapiLba > UINT32_C(0xffff4fa1)
470 && (int32_t)iAtapiLba < -150)
471 {
472 /* Lead-In area, this is always the first entry in the cue sheet. */
473 pTrack = pTrackList->paTracks;
474 Assert(pTrack->fFlags & TRACK_FLAGS_LEAD_IN);
475 LogFlowFunc(("Selected Lead-In area\n"));
476 }
477 else
478 {
479 int64_t iAtapiLba64 = (int32_t)iAtapiLba;
480 pTrack = &pTrackList->paTracks[1];
481
482 /* Go through the track list and find the correct entry. */
483 for (unsigned i = 1; i < pTrackList->cTracksCurrent - 1; i++)
484 {
485 if (pTrack->fFlags & TRACK_FLAGS_UNDETECTED)
486 continue;
487
488 if ( pTrack->iLbaStart <= iAtapiLba64
489 && iAtapiLba64 < pTrack->iLbaStart + pTrack->cSectors)
490 break;
491
492 pTrack++;
493 }
494 }
495
496 if (pTrack)
497 {
498 switch (pTrack->enmMainDataForm)
499 {
500 case TRACKDATAFORM_CDDA:
501 case TRACKDATAFORM_MODE1_2352:
502 case TRACKDATAFORM_XA_2352:
503 case TRACKDATAFORM_MODE2_2352:
504 cbAtapiSector = 2352;
505 break;
506 case TRACKDATAFORM_MODE1_2048:
507 cbAtapiSector = 2048;
508 break;
509 case TRACKDATAFORM_CDDA_PAUSE:
510 case TRACKDATAFORM_MODE1_0:
511 case TRACKDATAFORM_XA_0:
512 case TRACKDATAFORM_MODE2_0:
513 cbAtapiSector = 0;
514 break;
515 case TRACKDATAFORM_XA_2336:
516 case TRACKDATAFORM_MODE2_2336:
517 cbAtapiSector = 2336;
518 break;
519 case TRACKDATAFORM_INVALID:
520 default:
521 AssertMsgFailed(("Invalid track data form %d\n", pTrack->enmMainDataForm));
522 }
523
524 switch (pTrack->enmSubChnDataForm)
525 {
526 case SUBCHNDATAFORM_0:
527 break;
528 case SUBCHNDATAFORM_96:
529 cbAtapiSector += 96;
530 break;
531 case SUBCHNDATAFORM_INVALID:
532 default:
533 AssertMsgFailed(("Invalid subchannel data form %d\n", pTrack->enmSubChnDataForm));
534 }
535 }
536 }
537
538 return cbAtapiSector;
539}
540
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette