VirtualBox

source: vbox/trunk/src/VBox/Devices/Storage/VSCSI/VSCSILunMmc.cpp@ 77010

Last change on this file since 77010 was 77010, checked in by vboxsync, 6 years ago

DevATA: Clear interrupt when command register is written (ATA requirement). Also rely on the internal interrupt pending state more, to avoid unnecessary PDM calls (see bugref:5869).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 70.8 KB
Line 
1/* $Id: VSCSILunMmc.cpp 77010 2019-01-27 09:45:05Z vboxsync $ */
2/** @file
3 * Virtual SCSI driver: MMC LUN implementation (CD/DVD-ROM)
4 */
5
6/*
7 * Copyright (C) 2006-2019 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
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_VSCSI
23#include <VBox/log.h>
24#include <VBox/err.h>
25#include <VBox/types.h>
26#include <VBox/vscsi.h>
27#include <iprt/asm.h>
28#include <iprt/assert.h>
29#include <iprt/mem.h>
30#include <iprt/string.h>
31
32#include "VSCSIInternal.h"
33
34
35/*********************************************************************************************************************************
36* Structures and Typedefs *
37*********************************************************************************************************************************/
38
39/**
40 * Different event status types.
41 */
42typedef enum MMCEVENTSTATUSTYPE
43{
44 /** Medium event status not changed. */
45 MMCEVENTSTATUSTYPE_UNCHANGED = 0,
46 /** New medium inserted. */
47 MMCEVENTSTATUSTYPE_MEDIA_NEW,
48 /** Medium removed. */
49 MMCEVENTSTATUSTYPE_MEDIA_REMOVED,
50 /** Medium was removed + new medium was inserted. */
51 MMCEVENTSTATUSTYPE_MEDIA_CHANGED,
52 /** Medium eject requested (eject button pressed). */
53 MMCEVENTSTATUSTYPE_MEDIA_EJECT_REQUESTED,
54 /** 32bit hack. */
55 MMCEVENTSTATUSTYPE_32BIT_HACK = 0x7fffffff
56} MMCEVENTSTATUSTYPE;
57
58/** @name Media track types.
59 * @{ */
60/** Unknown media type. */
61#define MMC_MEDIA_TYPE_UNKNOWN 0
62/** Medium is a DVD. */
63#define MMC_MEDIA_TYPE_DVD 2
64/** Door closed, no media. */
65#define MMC_MEDIA_TYPE_NO_DISC 0x70
66/** @} */
67
68
69/**
70 * MMC LUN instance
71 */
72typedef struct VSCSILUNMMC
73{
74 /** Core LUN structure */
75 VSCSILUNINT Core;
76 /** Size of the virtual disk. */
77 uint64_t cSectors;
78 /** Medium locked indicator. */
79 bool fLocked;
80 /** Media event status. */
81 volatile MMCEVENTSTATUSTYPE MediaEventStatus;
82 /** Media track type. */
83 volatile uint32_t u32MediaTrackType;
84} VSCSILUNMMC, *PVSCSILUNMMC;
85
86
87/**
88 * Callback to fill a feature for a GET CONFIGURATION request.
89 *
90 * @returns Number of bytes used for this feature in the buffer.
91 * @param pbBuf The buffer to use.
92 * @param cbBuf Size of the buffer.
93 */
94typedef DECLCALLBACK(size_t) FNVSCSILUNMMCFILLFEATURE(uint8_t *pbBuf, size_t cbBuf);
95/** Pointer to a fill feature callback. */
96typedef FNVSCSILUNMMCFILLFEATURE *PFNVSCSILUNMMCFILLFEATURE;
97
98/**
99 * VSCSI MMC feature descriptor.
100 */
101typedef struct VSCSILUNMMCFEATURE
102{
103 /** The feature number. */
104 uint16_t u16Feat;
105 /** The callback to call for this feature. */
106 PFNVSCSILUNMMCFILLFEATURE pfnFeatureFill;
107} VSCSILUNMMCFEATURE;
108/** Pointer to a VSCSI MMC feature descriptor. */
109typedef VSCSILUNMMCFEATURE *PVSCSILUNMMCFEATURE;
110/** Pointer to a const VSCSI MMC feature descriptor. */
111typedef const VSCSILUNMMCFEATURE *PCVSCSILUNMMCFEATURE;
112
113
114
115/*********************************************************************************************************************************
116* Internal Functions *
117*********************************************************************************************************************************/
118RT_C_DECLS_BEGIN
119static DECLCALLBACK(size_t) vscsiLunMmcGetConfigurationFillFeatureListProfiles(uint8_t *pbBuf, size_t cbBuf);
120static DECLCALLBACK(size_t) vscsiLunMmcGetConfigurationFillFeatureCore(uint8_t *pbBuf, size_t cbBuf);
121static DECLCALLBACK(size_t) vscsiLunMmcGetConfigurationFillFeatureMorphing(uint8_t *pbBuf, size_t cbBuf);
122static DECLCALLBACK(size_t) vscsiLunMmcGetConfigurationFillFeatureRemovableMedium(uint8_t *pbBuf, size_t cbBuf);
123static DECLCALLBACK(size_t) vscsiLunMmcGetConfigurationFillFeatureRandomReadable(uint8_t *pbBuf, size_t cbBuf);
124static DECLCALLBACK(size_t) vscsiLunMmcGetConfigurationFillFeatureCDRead(uint8_t *pbBuf, size_t cbBuf);
125static DECLCALLBACK(size_t) vscsiLunMmcGetConfigurationFillFeaturePowerManagement(uint8_t *pbBuf, size_t cbBuf);
126static DECLCALLBACK(size_t) vscsiLunMmcGetConfigurationFillFeatureTimeout(uint8_t *pbBuf, size_t cbBuf);
127RT_C_DECLS_END
128
129/**
130 * List of supported MMC features.
131 */
132static const VSCSILUNMMCFEATURE g_aVScsiMmcFeatures[] =
133{
134 { 0x0000, vscsiLunMmcGetConfigurationFillFeatureListProfiles},
135 { 0x0001, vscsiLunMmcGetConfigurationFillFeatureCore},
136 { 0x0002, vscsiLunMmcGetConfigurationFillFeatureMorphing},
137 { 0x0003, vscsiLunMmcGetConfigurationFillFeatureRemovableMedium},
138 { 0x0010, vscsiLunMmcGetConfigurationFillFeatureRandomReadable},
139 { 0x001e, vscsiLunMmcGetConfigurationFillFeatureCDRead},
140 { 0x0100, vscsiLunMmcGetConfigurationFillFeaturePowerManagement},
141 { 0x0105, vscsiLunMmcGetConfigurationFillFeatureTimeout}
142};
143
144/* Fabricate normal TOC information. */
145static int mmcReadTOCNormal(PVSCSILUNINT pVScsiLun, PVSCSIREQINT pVScsiReq, uint16_t cbMaxTransfer, bool fMSF)
146{
147 uint8_t aReply[2+99*8 + 32]; RT_ZERO(aReply); /* Maximum possible reply plus some safety. */
148 uint8_t *pbBuf = aReply;
149 uint8_t *q;
150 uint8_t iStartTrack;
151 uint32_t cbSize;
152 uint32_t cTracks = vscsiLunMediumGetRegionCount(pVScsiLun);
153
154 iStartTrack = pVScsiReq->pbCDB[6];
155 if (iStartTrack == 0)
156 iStartTrack = 1;
157 if (iStartTrack > cTracks && iStartTrack != 0xaa)
158 return vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
159
160 q = pbBuf + 2;
161 *q++ = iStartTrack; /* first track number */
162 *q++ = cTracks; /* last track number */
163 for (uint32_t iTrack = iStartTrack; iTrack <= cTracks; iTrack++)
164 {
165 uint64_t uLbaStart = 0;
166 VDREGIONDATAFORM enmDataForm = VDREGIONDATAFORM_MODE1_2048;
167
168 int rc = vscsiLunMediumQueryRegionProperties(pVScsiLun, iTrack - 1, &uLbaStart,
169 NULL, NULL, &enmDataForm);
170 if (rc == VERR_NOT_FOUND || rc == VERR_MEDIA_NOT_PRESENT)
171 return vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_NOT_READY,
172 SCSI_ASC_MEDIUM_NOT_PRESENT, 0x00);
173 else
174 AssertRC(rc);
175
176 *q++ = 0; /* reserved */
177
178 if (enmDataForm == VDREGIONDATAFORM_CDDA)
179 *q++ = 0x10; /* ADR, control */
180 else
181 *q++ = 0x14; /* ADR, control */
182
183 *q++ = (uint8_t)iTrack; /* track number */
184 *q++ = 0; /* reserved */
185 if (fMSF)
186 {
187 *q++ = 0; /* reserved */
188 scsiLBA2MSF(q, (uint32_t)uLbaStart);
189 q += 3;
190 }
191 else
192 {
193 /* sector 0 */
194 scsiH2BE_U32(q, (uint32_t)uLbaStart);
195 q += 4;
196 }
197 }
198 /* lead out track */
199 *q++ = 0; /* reserved */
200 *q++ = 0x14; /* ADR, control */
201 *q++ = 0xaa; /* track number */
202 *q++ = 0; /* reserved */
203
204 /* Query start and length of last track to get the start of the lead out track. */
205 uint64_t uLbaStart = 0;
206 uint64_t cBlocks = 0;
207
208 int rc = vscsiLunMediumQueryRegionProperties(pVScsiLun, cTracks - 1, &uLbaStart,
209 &cBlocks, NULL, NULL);
210 if (rc == VERR_NOT_FOUND || rc == VERR_MEDIA_NOT_PRESENT)
211 return vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_NOT_READY,
212 SCSI_ASC_MEDIUM_NOT_PRESENT, 0x00);
213 else
214 AssertRC(rc);
215
216 uLbaStart += cBlocks;
217 if (fMSF)
218 {
219 *q++ = 0; /* reserved */
220 scsiLBA2MSF(q, (uint32_t)uLbaStart);
221 q += 3;
222 }
223 else
224 {
225 scsiH2BE_U32(q, (uint32_t)uLbaStart);
226 q += 4;
227 }
228 cbSize = q - pbBuf;
229 Assert(cbSize <= sizeof(aReply));
230 scsiH2BE_U16(pbBuf, cbSize - 2);
231 if (cbSize < cbMaxTransfer)
232 cbMaxTransfer = cbSize;
233
234 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, cbMaxTransfer);
235 return vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
236}
237
238/* Fabricate session information. */
239static int mmcReadTOCMulti(PVSCSILUNINT pVScsiLun, PVSCSIREQINT pVScsiReq, uint16_t cbMaxTransfer, bool fMSF)
240{
241 RT_NOREF1(cbMaxTransfer);
242 uint8_t aReply[32];
243 uint8_t *pbBuf = aReply;
244
245 /* multi session: only a single session defined */
246 memset(pbBuf, 0, 12);
247 pbBuf[1] = 0x0a;
248 pbBuf[2] = 0x01; /* first complete session number */
249 pbBuf[3] = 0x01; /* last complete session number */
250
251 VDREGIONDATAFORM enmDataForm = VDREGIONDATAFORM_MODE1_2048;
252 int rc = vscsiLunMediumQueryRegionProperties(pVScsiLun, 0, NULL,
253 NULL, NULL, &enmDataForm);
254 if (rc == VERR_NOT_FOUND || rc == VERR_MEDIA_NOT_PRESENT)
255 return vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_NOT_READY,
256 SCSI_ASC_MEDIUM_NOT_PRESENT, 0x00);
257 else
258 AssertRC(rc);
259
260 if (enmDataForm == VDREGIONDATAFORM_CDDA)
261 pbBuf[5] = 0x10; /* ADR, control */
262 else
263 pbBuf[5] = 0x14; /* ADR, control */
264
265 pbBuf[6] = 1; /* first track in last complete session */
266
267 if (fMSF)
268 {
269 pbBuf[8] = 0; /* reserved */
270 scsiLBA2MSF(pbBuf + 8, 0);
271 }
272 else
273 {
274 /* sector 0 */
275 scsiH2BE_U32(pbBuf + 8, 0);
276 }
277
278 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, 12);
279
280 return vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
281}
282
283/**
284 * Create raw TOC data information.
285 *
286 * @returns SCSI status code.
287 * @param pVScsiLun The LUN instance.
288 * @param pVScsiReq The VSCSI request.
289 * @param cbMaxTransfer The maximum transfer size.
290 * @param fMSF Flag whether to use MSF format to encode sector numbers.
291 */
292static int mmcReadTOCRaw(PVSCSILUNINT pVScsiLun, PVSCSIREQINT pVScsiReq, uint16_t cbMaxTransfer, bool fMSF)
293{
294 PVSCSILUNMMC pVScsiLunMmc = (PVSCSILUNMMC)pVScsiLun;
295 uint8_t aReply[50]; /* Counted a maximum of 45 bytes but better be on the safe side. */
296 uint32_t cbSize;
297 uint8_t *pbBuf = &aReply[0] + 2;
298
299 *pbBuf++ = 1; /* first session */
300 *pbBuf++ = 1; /* last session */
301
302 *pbBuf++ = 1; /* session number */
303 *pbBuf++ = 0x14; /* data track */
304 *pbBuf++ = 0; /* track number */
305 *pbBuf++ = 0xa0; /* first track in program area */
306 *pbBuf++ = 0; /* min */
307 *pbBuf++ = 0; /* sec */
308 *pbBuf++ = 0; /* frame */
309 *pbBuf++ = 0;
310 *pbBuf++ = 1; /* first track */
311 *pbBuf++ = 0x00; /* disk type CD-DA or CD data */
312 *pbBuf++ = 0;
313
314 *pbBuf++ = 1; /* session number */
315 *pbBuf++ = 0x14; /* data track */
316 *pbBuf++ = 0; /* track number */
317 *pbBuf++ = 0xa1; /* last track in program area */
318 *pbBuf++ = 0; /* min */
319 *pbBuf++ = 0; /* sec */
320 *pbBuf++ = 0; /* frame */
321 *pbBuf++ = 0;
322 *pbBuf++ = 1; /* last track */
323 *pbBuf++ = 0;
324 *pbBuf++ = 0;
325
326 *pbBuf++ = 1; /* session number */
327 *pbBuf++ = 0x14; /* data track */
328 *pbBuf++ = 0; /* track number */
329 *pbBuf++ = 0xa2; /* lead-out */
330 *pbBuf++ = 0; /* min */
331 *pbBuf++ = 0; /* sec */
332 *pbBuf++ = 0; /* frame */
333 if (fMSF)
334 {
335 *pbBuf++ = 0; /* reserved */
336 scsiLBA2MSF(pbBuf, pVScsiLunMmc->cSectors);
337 pbBuf += 3;
338 }
339 else
340 {
341 scsiH2BE_U32(pbBuf, pVScsiLunMmc->cSectors);
342 pbBuf += 4;
343 }
344
345 *pbBuf++ = 1; /* session number */
346 *pbBuf++ = 0x14; /* ADR, control */
347 *pbBuf++ = 0; /* track number */
348 *pbBuf++ = 1; /* point */
349 *pbBuf++ = 0; /* min */
350 *pbBuf++ = 0; /* sec */
351 *pbBuf++ = 0; /* frame */
352 if (fMSF)
353 {
354 *pbBuf++ = 0; /* reserved */
355 scsiLBA2MSF(pbBuf, 0);
356 pbBuf += 3;
357 }
358 else
359 {
360 /* sector 0 */
361 scsiH2BE_U32(pbBuf, 0);
362 pbBuf += 4;
363 }
364
365 cbSize = pbBuf - aReply;
366 scsiH2BE_U16(&aReply[0], cbSize - 2);
367
368 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, RT_MIN(cbMaxTransfer, cbSize));
369 return vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
370}
371
372static DECLCALLBACK(size_t) vscsiLunMmcGetConfigurationFillFeatureListProfiles(uint8_t *pbBuf, size_t cbBuf)
373{
374 if (cbBuf < 3*4)
375 return 0;
376
377 scsiH2BE_U16(pbBuf, 0x0); /* feature 0: list of profiles supported */
378 pbBuf[2] = (0 << 2) | (1 << 1) | (1 << 0); /* version 0, persistent, current */
379 pbBuf[3] = 8; /* additional bytes for profiles */
380 /* The MMC-3 spec says that DVD-ROM read capability should be reported
381 * before CD-ROM read capability. */
382 scsiH2BE_U16(pbBuf + 4, 0x10); /* profile: read-only DVD */
383 pbBuf[6] = (0 << 0); /* NOT current profile */
384 scsiH2BE_U16(pbBuf + 8, 0x08); /* profile: read only CD */
385 pbBuf[10] = (1 << 0); /* current profile */
386
387 return 3*4; /* Header + 2 profiles entries */
388}
389
390static DECLCALLBACK(size_t) vscsiLunMmcGetConfigurationFillFeatureCore(uint8_t *pbBuf, size_t cbBuf)
391{
392 if (cbBuf < 12)
393 return 0;
394
395 scsiH2BE_U16(pbBuf, 0x1); /* feature 0001h: Core Feature */
396 pbBuf[2] = (0x2 << 2) | RT_BIT(1) | RT_BIT(0); /* Version | Persistent | Current */
397 pbBuf[3] = 8; /* Additional length */
398 scsiH2BE_U16(pbBuf + 4, 0x00000002); /* Physical interface ATAPI. */
399 pbBuf[8] = RT_BIT(0); /* DBE */
400 /* Rest is reserved. */
401
402 return 12;
403}
404
405static DECLCALLBACK(size_t) vscsiLunMmcGetConfigurationFillFeatureMorphing(uint8_t *pbBuf, size_t cbBuf)
406{
407 if (cbBuf < 8)
408 return 0;
409
410 scsiH2BE_U16(pbBuf, 0x2); /* feature 0002h: Morphing Feature */
411 pbBuf[2] = (0x1 << 2) | RT_BIT(1) | RT_BIT(0); /* Version | Persistent | Current */
412 pbBuf[3] = 4; /* Additional length */
413 pbBuf[4] = RT_BIT(1) | 0x0; /* OCEvent | !ASYNC */
414 /* Rest is reserved. */
415
416 return 8;
417}
418
419static DECLCALLBACK(size_t) vscsiLunMmcGetConfigurationFillFeatureRemovableMedium(uint8_t *pbBuf, size_t cbBuf)
420{
421 if (cbBuf < 8)
422 return 0;
423
424 scsiH2BE_U16(pbBuf, 0x3); /* feature 0003h: Removable Medium Feature */
425 pbBuf[2] = (0x2 << 2) | RT_BIT(1) | RT_BIT(0); /* Version | Persistent | Current */
426 pbBuf[3] = 4; /* Additional length */
427 /* Tray type loading | Load | Eject | !Pvnt Jmpr | !DBML | Lock */
428 pbBuf[4] = (0x2 << 5) | RT_BIT(4) | RT_BIT(3) | (0x0 << 2) | (0x0 << 1) | RT_BIT(0);
429 /* Rest is reserved. */
430
431 return 8;
432}
433
434static DECLCALLBACK(size_t) vscsiLunMmcGetConfigurationFillFeatureRandomReadable(uint8_t *pbBuf, size_t cbBuf)
435{
436 if (cbBuf < 12)
437 return 0;
438
439 scsiH2BE_U16(pbBuf, 0x10); /* feature 0010h: Random Readable Feature */
440 pbBuf[2] = (0x0 << 2) | RT_BIT(1) | RT_BIT(0); /* Version | Persistent | Current */
441 pbBuf[3] = 8; /* Additional length */
442 scsiH2BE_U32(pbBuf + 4, 2048); /* Logical block size. */
443 scsiH2BE_U16(pbBuf + 8, 0x10); /* Blocking (0x10 for DVD, CD is not defined). */
444 pbBuf[10] = 0; /* PP not present */
445 /* Rest is reserved. */
446
447 return 12;
448}
449
450static DECLCALLBACK(size_t) vscsiLunMmcGetConfigurationFillFeatureCDRead(uint8_t *pbBuf, size_t cbBuf)
451{
452 if (cbBuf < 8)
453 return 0;
454
455 scsiH2BE_U16(pbBuf, 0x1e); /* feature 001Eh: CD Read Feature */
456 pbBuf[2] = (0x2 << 2) | RT_BIT(1) | RT_BIT(0); /* Version | Persistent | Current */
457 pbBuf[3] = 0; /* Additional length */
458 pbBuf[4] = (0x0 << 7) | (0x0 << 1) | 0x0; /* !DAP | !C2-Flags | !CD-Text. */
459 /* Rest is reserved. */
460
461 return 8;
462}
463
464static DECLCALLBACK(size_t) vscsiLunMmcGetConfigurationFillFeaturePowerManagement(uint8_t *pbBuf, size_t cbBuf)
465{
466 if (cbBuf < 4)
467 return 0;
468
469 scsiH2BE_U16(pbBuf, 0x100); /* feature 0100h: Power Management Feature */
470 pbBuf[2] = (0x0 << 2) | RT_BIT(1) | RT_BIT(0); /* Version | Persistent | Current */
471 pbBuf[3] = 0; /* Additional length */
472
473 return 4;
474}
475
476static DECLCALLBACK(size_t) vscsiLunMmcGetConfigurationFillFeatureTimeout(uint8_t *pbBuf, size_t cbBuf)
477{
478 if (cbBuf < 8)
479 return 0;
480
481 scsiH2BE_U16(pbBuf, 0x105); /* feature 0105h: Timeout Feature */
482 pbBuf[2] = (0x0 << 2) | RT_BIT(1) | RT_BIT(0); /* Version | Persistent | Current */
483 pbBuf[3] = 4; /* Additional length */
484 pbBuf[4] = 0x0; /* !Group3 */
485
486 return 8;
487}
488
489/**
490 * Processes the GET CONFIGURATION SCSI request.
491 *
492 * @returns SCSI status code.
493 * @param pVScsiLunMmc The MMC LUN instance.
494 * @param pVScsiReq The VSCSI request.
495 * @param cbMaxTransfer The maximum transfer size.
496 */
497static int vscsiLunMmcGetConfiguration(PVSCSILUNMMC pVScsiLunMmc, PVSCSIREQINT pVScsiReq, size_t cbMaxTransfer)
498{
499 uint8_t aReply[80];
500 uint8_t *pbBuf = &aReply[0];
501 size_t cbBuf = sizeof(aReply);
502 size_t cbCopied = 0;
503 uint16_t u16Sfn = scsiBE2H_U16(&pVScsiReq->pbCDB[2]);
504 uint8_t u8Rt = pVScsiReq->pbCDB[1] & 0x03;
505
506 /* Accept valid request types only. */
507 if (u8Rt == 3)
508 return vscsiLunReqSenseErrorSet(&pVScsiLunMmc->Core, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST,
509 SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
510
511 /** @todo implement switching between CD-ROM and DVD-ROM profile (the only
512 * way to differentiate them right now is based on the image size). */
513 if (pVScsiLunMmc->cSectors)
514 scsiH2BE_U16(pbBuf + 6, 0x08); /* current profile: read-only CD */
515 else
516 scsiH2BE_U16(pbBuf + 6, 0x00); /* current profile: none -> no media */
517 cbBuf -= 8;
518 pbBuf += 8;
519
520 if (u8Rt == 0x2)
521 {
522 for (uint32_t i = 0; i < RT_ELEMENTS(g_aVScsiMmcFeatures); i++)
523 {
524 if (g_aVScsiMmcFeatures[i].u16Feat == u16Sfn)
525 {
526 cbCopied = g_aVScsiMmcFeatures[i].pfnFeatureFill(pbBuf, cbBuf);
527 cbBuf -= cbCopied;
528 pbBuf += cbCopied;
529 break;
530 }
531 }
532 }
533 else
534 {
535 for (uint32_t i = 0; i < RT_ELEMENTS(g_aVScsiMmcFeatures); i++)
536 {
537 if (g_aVScsiMmcFeatures[i].u16Feat > u16Sfn)
538 {
539 cbCopied = g_aVScsiMmcFeatures[i].pfnFeatureFill(pbBuf, cbBuf);
540 cbBuf -= cbCopied;
541 pbBuf += cbCopied;
542 }
543 }
544 }
545
546 /* Set data length now. */
547 scsiH2BE_U32(&aReply[0], (uint32_t)(sizeof(aReply) - cbBuf));
548
549 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, RT_MIN(cbMaxTransfer, sizeof(aReply) - cbBuf));
550 return vscsiLunReqSenseOkSet(&pVScsiLunMmc->Core, pVScsiReq);
551}
552
553/**
554 * Processes the READ DVD STRUCTURE SCSI request.
555 *
556 * @returns SCSI status code.
557 * @param pVScsiLunMmc The MMC LUN instance.
558 * @param pVScsiReq The VSCSI request.
559 * @param cbMaxTransfer The maximum transfer size.
560 */
561static int vscsiLunMmcReadDvdStructure(PVSCSILUNMMC pVScsiLunMmc, PVSCSIREQINT pVScsiReq, size_t cbMaxTransfer)
562{
563 uint8_t aReply[25]; /* Counted a maximum of 20 bytes but better be on the safe side. */
564
565 RT_ZERO(aReply);
566
567 /* Act according to the indicated format. */
568 switch (pVScsiReq->pbCDB[7])
569 {
570 case 0x00:
571 case 0x01:
572 case 0x02:
573 case 0x03:
574 case 0x04:
575 case 0x05:
576 case 0x06:
577 case 0x07:
578 case 0x08:
579 case 0x09:
580 case 0x0a:
581 case 0x0b:
582 case 0x0c:
583 case 0x0d:
584 case 0x0e:
585 case 0x0f:
586 case 0x10:
587 case 0x11:
588 case 0x30:
589 case 0x31:
590 /* For a CD, these must fail. */
591#if 0
592 if (pVScsiLunMmc->u32MediaTrackType != MMC_MEDIA_TYPE_DVD)
593 return vscsiLunReqSenseErrorSet(&pVScsiLunMmc->Core, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST,
594 SCSI_ASC_CANNOT_READ_MEDIUM, SCSI_ASCQ_INCOMPATIBLE_FORMAT);
595#endif
596 RT_FALL_THRU();
597 case 0xff:
598 if (pVScsiReq->pbCDB[1] == 0)
599 {
600 int uASC = SCSI_ASC_NONE;
601
602 switch (pVScsiReq->pbCDB[7])
603 {
604 case 0x0: /* Physical format information */
605 {
606 uint8_t uLayer = pVScsiReq->pbCDB[6];
607 uint64_t cTotalSectors;
608
609 if (uLayer != 0)
610 {
611 uASC = -SCSI_ASC_INV_FIELD_IN_CMD_PACKET;
612 break;
613 }
614
615 cTotalSectors = pVScsiLunMmc->cSectors;
616 cTotalSectors >>= 2;
617 if (cTotalSectors == 0)
618 {
619 uASC = -SCSI_ASC_MEDIUM_NOT_PRESENT;
620 break;
621 }
622
623 aReply[4] = 1; /* DVD-ROM, part version 1 */
624 aReply[5] = 0xf; /* 120mm disc, minimum rate unspecified */
625 aReply[6] = 1; /* one layer, read-only (per MMC-2 spec) */
626 aReply[7] = 0; /* default densities */
627
628 /* FIXME: 0x30000 per spec? */
629 scsiH2BE_U32(&aReply[8], 0); /* start sector */
630 scsiH2BE_U32(&aReply[12], cTotalSectors - 1); /* end sector */
631 scsiH2BE_U32(&aReply[16], cTotalSectors - 1); /* l0 end sector */
632
633 /* Size of buffer, not including 2 byte size field */
634 scsiH2BE_U32(&aReply[0], 2048 + 2);
635
636 /* 2k data + 4 byte header */
637 uASC = (2048 + 4);
638 break;
639 }
640 case 0x01: /* DVD copyright information */
641 aReply[4] = 0; /* no copyright data */
642 aReply[5] = 0; /* no region restrictions */
643
644 /* Size of buffer, not including 2 byte size field */
645 scsiH2BE_U16(&aReply[0], 4 + 2);
646
647 /* 4 byte header + 4 byte data */
648 uASC = (4 + 4);
649 break;
650
651 case 0x03: /* BCA information - invalid field for no BCA info */
652 uASC = -SCSI_ASC_INV_FIELD_IN_CMD_PACKET;
653 break;
654
655 case 0x04: /* DVD disc manufacturing information */
656 /* Size of buffer, not including 2 byte size field */
657 scsiH2BE_U16(&aReply[0], 2048 + 2);
658
659 /* 2k data + 4 byte header */
660 uASC = (2048 + 4);
661 break;
662 case 0xff:
663 /*
664 * This lists all the command capabilities above. Add new ones
665 * in order and update the length and buffer return values.
666 */
667
668 aReply[4] = 0x00; /* Physical format */
669 aReply[5] = 0x40; /* Not writable, is readable */
670 scsiH2BE_U16(&aReply[6], 2048 + 4);
671
672 aReply[8] = 0x01; /* Copyright info */
673 aReply[9] = 0x40; /* Not writable, is readable */
674 scsiH2BE_U16(&aReply[10], 4 + 4);
675
676 aReply[12] = 0x03; /* BCA info */
677 aReply[13] = 0x40; /* Not writable, is readable */
678 scsiH2BE_U16(&aReply[14], 188 + 4);
679
680 aReply[16] = 0x04; /* Manufacturing info */
681 aReply[17] = 0x40; /* Not writable, is readable */
682 scsiH2BE_U16(&aReply[18], 2048 + 4);
683
684 /* Size of buffer, not including 2 byte size field */
685 scsiH2BE_U16(&aReply[0], 16 + 2);
686
687 /* data written + 4 byte header */
688 uASC = (16 + 4);
689 break;
690 default: /** @todo formats beyond DVD-ROM requires */
691 uASC = -SCSI_ASC_INV_FIELD_IN_CMD_PACKET;
692 }
693
694 if (uASC < 0)
695 return vscsiLunReqSenseErrorSet(&pVScsiLunMmc->Core, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST,
696 -uASC, 0x00);
697 break;
698 }
699 /** @todo BD support, fall through for now */
700 RT_FALL_THRU();
701
702 /* Generic disk structures */
703 case 0x80: /** @todo AACS volume identifier */
704 case 0x81: /** @todo AACS media serial number */
705 case 0x82: /** @todo AACS media identifier */
706 case 0x83: /** @todo AACS media key block */
707 case 0x90: /** @todo List of recognized format layers */
708 case 0xc0: /** @todo Write protection status */
709 default:
710 return vscsiLunReqSenseErrorSet(&pVScsiLunMmc->Core, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST,
711 SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
712 }
713
714 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, RT_MIN(cbMaxTransfer, sizeof(aReply)));
715 return vscsiLunReqSenseOkSet(&pVScsiLunMmc->Core, pVScsiReq);
716}
717
718/**
719 * Processes the MODE SENSE 10 SCSI request.
720 *
721 * @returns SCSI status code.
722 * @param pVScsiLunMmc The MMC LUN instance.
723 * @param pVScsiReq The VSCSI request.
724 * @param cbMaxTransfer The maximum transfer size.
725 */
726static int vscsiLunMmcModeSense10(PVSCSILUNMMC pVScsiLunMmc, PVSCSIREQINT pVScsiReq, size_t cbMaxTransfer)
727{
728 int rcReq;
729 uint8_t uPageControl = pVScsiReq->pbCDB[2] >> 6;
730 uint8_t uPageCode = pVScsiReq->pbCDB[2] & 0x3f;
731
732 switch (uPageControl)
733 {
734 case SCSI_PAGECONTROL_CURRENT:
735 switch (uPageCode)
736 {
737 case SCSI_MODEPAGE_ERROR_RECOVERY:
738 {
739 uint8_t aReply[16];
740
741 scsiH2BE_U16(&aReply[0], 16 + 6);
742 aReply[2] = (uint8_t)pVScsiLunMmc->u32MediaTrackType;
743 aReply[3] = 0;
744 aReply[4] = 0;
745 aReply[5] = 0;
746 aReply[6] = 0;
747 aReply[7] = 0;
748
749 aReply[8] = 0x01;
750 aReply[9] = 0x06;
751 aReply[10] = 0x00;
752 aReply[11] = 0x05;
753 aReply[12] = 0x00;
754 aReply[13] = 0x00;
755 aReply[14] = 0x00;
756 aReply[15] = 0x00;
757 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, RT_MIN(cbMaxTransfer, sizeof(aReply)));
758 rcReq = vscsiLunReqSenseOkSet(&pVScsiLunMmc->Core, pVScsiReq);
759 break;
760 }
761 case SCSI_MODEPAGE_CD_STATUS:
762 {
763 uint8_t aReply[40];
764
765 scsiH2BE_U16(&aReply[0], 38);
766 aReply[2] = (uint8_t)pVScsiLunMmc->u32MediaTrackType;
767 aReply[3] = 0;
768 aReply[4] = 0;
769 aReply[5] = 0;
770 aReply[6] = 0;
771 aReply[7] = 0;
772
773 aReply[8] = 0x2a;
774 aReply[9] = 30; /* page length */
775 aReply[10] = 0x08; /* DVD-ROM read support */
776 aReply[11] = 0x00; /* no write support */
777 /* The following claims we support audio play. This is obviously false,
778 * but the Linux generic CDROM support makes many features depend on this
779 * capability. If it's not set, this causes many things to be disabled. */
780 aReply[12] = 0x71; /* multisession support, mode 2 form 1/2 support, audio play */
781 aReply[13] = 0x00; /* no subchannel reads supported */
782 aReply[14] = (1 << 0) | (1 << 3) | (1 << 5); /* lock supported, eject supported, tray type loading mechanism */
783 if (pVScsiLunMmc->fLocked)
784 aReply[14] |= 1 << 1; /* report lock state */
785 aReply[15] = 0; /* no subchannel reads supported, no separate audio volume control, no changer etc. */
786 scsiH2BE_U16(&aReply[16], 5632); /* (obsolete) claim 32x speed support */
787 scsiH2BE_U16(&aReply[18], 2); /* number of audio volume levels */
788 scsiH2BE_U16(&aReply[20], 128); /* buffer size supported in Kbyte - We don't have a buffer because we write directly into guest memory.
789 Just write some dummy value. */
790 scsiH2BE_U16(&aReply[22], 5632); /* (obsolete) current read speed 32x */
791 aReply[24] = 0; /* reserved */
792 aReply[25] = 0; /* reserved for digital audio (see idx 15) */
793 scsiH2BE_U16(&aReply[26], 0); /* (obsolete) maximum write speed */
794 scsiH2BE_U16(&aReply[28], 0); /* (obsolete) current write speed */
795 scsiH2BE_U16(&aReply[30], 0); /* copy management revision supported 0=no CSS */
796 aReply[32] = 0; /* reserved */
797 aReply[33] = 0; /* reserved */
798 aReply[34] = 0; /* reserved */
799 aReply[35] = 1; /* rotation control CAV */
800 scsiH2BE_U16(&aReply[36], 0); /* current write speed */
801 scsiH2BE_U16(&aReply[38], 0); /* number of write speed performance descriptors */
802 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, RT_MIN(cbMaxTransfer, sizeof(aReply)));
803 rcReq = vscsiLunReqSenseOkSet(&pVScsiLunMmc->Core, pVScsiReq);
804 break;
805 }
806 default:
807 rcReq = vscsiLunReqSenseErrorSet(&pVScsiLunMmc->Core, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST,
808 SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
809 break;
810 }
811 break;
812 case SCSI_PAGECONTROL_CHANGEABLE:
813 case SCSI_PAGECONTROL_DEFAULT:
814 rcReq = vscsiLunReqSenseErrorSet(&pVScsiLunMmc->Core, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST,
815 SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
816 break;
817 default:
818 case SCSI_PAGECONTROL_SAVED:
819 rcReq = vscsiLunReqSenseErrorSet(&pVScsiLunMmc->Core, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST,
820 SCSI_ASC_SAVING_PARAMETERS_NOT_SUPPORTED, 0x00);
821 break;
822 }
823
824 return rcReq;
825}
826
827/**
828 * Processes the GET EVENT STATUS NOTIFICATION SCSI request.
829 *
830 * @returns SCSI status code.
831 * @param pVScsiLunMmc The MMC LUN instance.
832 * @param pVScsiReq The VSCSI request.
833 * @param cbMaxTransfer The maximum transfer size.
834 */
835static int vscsiLunMmcGetEventStatusNotification(PVSCSILUNMMC pVScsiLunMmc, PVSCSIREQINT pVScsiReq,
836 size_t cbMaxTransfer)
837{
838 uint32_t OldStatus;
839 uint32_t NewStatus;
840 uint8_t aReply[8];
841 RT_ZERO(aReply);
842
843 LogFlowFunc(("pVScsiLunMmc=%#p pVScsiReq=%#p cbMaxTransfer=%zu\n",
844 pVScsiLunMmc, pVScsiReq, cbMaxTransfer));
845
846 do
847 {
848 OldStatus = ASMAtomicReadU32((volatile uint32_t *)&pVScsiLunMmc->MediaEventStatus);
849 NewStatus = MMCEVENTSTATUSTYPE_UNCHANGED;
850
851 switch (OldStatus)
852 {
853 case MMCEVENTSTATUSTYPE_MEDIA_NEW:
854 /* mount */
855 scsiH2BE_U16(&aReply[0], 6);
856 aReply[2] = 0x04; /* media */
857 aReply[3] = 0x5e; /* supported = busy|media|external|power|operational */
858 aReply[4] = 0x02; /* new medium */
859 aReply[5] = 0x02; /* medium present / door closed */
860 aReply[6] = 0x00;
861 aReply[7] = 0x00;
862 pVScsiLunMmc->Core.fReady = true;
863 break;
864
865 case MMCEVENTSTATUSTYPE_MEDIA_CHANGED:
866 case MMCEVENTSTATUSTYPE_MEDIA_REMOVED:
867 /* umount */
868 scsiH2BE_U16(&aReply[0], 6);
869 aReply[2] = 0x04; /* media */
870 aReply[3] = 0x5e; /* supported = busy|media|external|power|operational */
871 aReply[4] = 0x03; /* media removal */
872 aReply[5] = 0x00; /* medium absent / door closed */
873 aReply[6] = 0x00;
874 aReply[7] = 0x00;
875 if (OldStatus == MMCEVENTSTATUSTYPE_MEDIA_CHANGED)
876 NewStatus = MMCEVENTSTATUSTYPE_MEDIA_NEW;
877 break;
878
879 case MMCEVENTSTATUSTYPE_MEDIA_EJECT_REQUESTED: /* currently unused */
880 scsiH2BE_U16(&aReply[0], 6);
881 aReply[2] = 0x04; /* media */
882 aReply[3] = 0x5e; /* supported = busy|media|external|power|operational */
883 aReply[4] = 0x01; /* eject requested (eject button pressed) */
884 aReply[5] = 0x02; /* medium present / door closed */
885 aReply[6] = 0x00;
886 aReply[7] = 0x00;
887 break;
888
889 case MMCEVENTSTATUSTYPE_UNCHANGED:
890 default:
891 scsiH2BE_U16(&aReply[0], 6);
892 aReply[2] = 0x01; /* operational change request / notification */
893 aReply[3] = 0x5e; /* supported = busy|media|external|power|operational */
894 aReply[4] = 0x00;
895 aReply[5] = 0x00;
896 aReply[6] = 0x00;
897 aReply[7] = 0x00;
898 break;
899 }
900
901 LogFlowFunc(("OldStatus=%u NewStatus=%u\n", OldStatus, NewStatus));
902
903 } while (!ASMAtomicCmpXchgU32((volatile uint32_t *)&pVScsiLunMmc->MediaEventStatus, NewStatus, OldStatus));
904
905 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, RT_MIN(cbMaxTransfer, sizeof(aReply)));
906 return vscsiLunReqSenseOkSet(&pVScsiLunMmc->Core, pVScsiReq);
907}
908
909/**
910 * Processes a READ TRACK INFORMATION SCSI request.
911 *
912 * @returns SCSI status code.
913 * @param pVScsiLunMmc The MMC LUN instance.
914 * @param pVScsiReq The VSCSI request.
915 * @param cbMaxTransfer The maximum transfer size.
916 */
917static int vscsiLunMmcReadTrackInformation(PVSCSILUNMMC pVScsiLunMmc, PVSCSIREQINT pVScsiReq,
918 size_t cbMaxTransfer)
919{
920 int rcReq;
921 uint32_t u32LogAddr = scsiBE2H_U32(&pVScsiReq->pbCDB[2]);
922 uint8_t u8LogAddrType = pVScsiReq->pbCDB[1] & 0x03;
923
924 int rc = VINF_SUCCESS;
925 uint64_t u64LbaStart = 0;
926 uint32_t uRegion = 0;
927 uint64_t cBlocks = 0;
928 uint64_t cbBlock = 0;
929 VDREGIONDATAFORM enmDataForm = VDREGIONDATAFORM_INVALID;
930
931 switch (u8LogAddrType)
932 {
933 case 0x00:
934 rc = vscsiLunMediumQueryRegionPropertiesForLba(&pVScsiLunMmc->Core, u32LogAddr, &uRegion,
935 NULL, NULL, NULL);
936 if (RT_SUCCESS(rc))
937 rc = vscsiLunMediumQueryRegionProperties(&pVScsiLunMmc->Core, uRegion, &u64LbaStart,
938 &cBlocks, &cbBlock, &enmDataForm);
939 break;
940 case 0x01:
941 {
942 if (u32LogAddr >= 1)
943 {
944 uRegion = u32LogAddr - 1;
945 rc = vscsiLunMediumQueryRegionProperties(&pVScsiLunMmc->Core, uRegion, &u64LbaStart,
946 &cBlocks, &cbBlock, &enmDataForm);
947 }
948 else
949 rc = VERR_NOT_FOUND; /** @todo Return lead-in information. */
950 break;
951 }
952 case 0x02:
953 default:
954 rc = VERR_INVALID_PARAMETER;
955 }
956
957 if (RT_SUCCESS(rc))
958 {
959 uint8_t u8DataMode = 0xf; /* Unknown data mode. */
960 uint8_t u8TrackMode = 0;
961 uint8_t aReply[36];
962 RT_ZERO(aReply);
963
964 switch (enmDataForm)
965 {
966 case VDREGIONDATAFORM_MODE1_2048:
967 case VDREGIONDATAFORM_MODE1_2352:
968 case VDREGIONDATAFORM_MODE1_0:
969 u8DataMode = 1;
970 break;
971 case VDREGIONDATAFORM_XA_2336:
972 case VDREGIONDATAFORM_XA_2352:
973 case VDREGIONDATAFORM_XA_0:
974 case VDREGIONDATAFORM_MODE2_2336:
975 case VDREGIONDATAFORM_MODE2_2352:
976 case VDREGIONDATAFORM_MODE2_0:
977 u8DataMode = 2;
978 break;
979 default:
980 u8DataMode = 0xf;
981 }
982
983 if (enmDataForm == VDREGIONDATAFORM_CDDA)
984 u8TrackMode = 0x0;
985 else
986 u8TrackMode = 0x4;
987
988 scsiH2BE_U16(&aReply[0], 34);
989 aReply[2] = uRegion + 1; /* track number (LSB) */
990 aReply[3] = 1; /* session number (LSB) */
991 aReply[5] = (0 << 5) | (0 << 4) | u8TrackMode; /* not damaged, primary copy, data track */
992 aReply[6] = (0 << 7) | (0 << 6) | (0 << 5) | (0 << 6) | u8DataMode; /* not reserved track, not blank, not packet writing, not fixed packet, data mode 1 */
993 aReply[7] = (0 << 1) | (0 << 0); /* last recorded address not valid, next recordable address not valid */
994 scsiH2BE_U32(&aReply[8], (uint32_t)u64LbaStart); /* track start address is 0 */
995 scsiH2BE_U32(&aReply[24], (uint32_t)cBlocks); /* track size */
996 aReply[32] = 0; /* track number (MSB) */
997 aReply[33] = 0; /* session number (MSB) */
998
999 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, RT_MIN(sizeof(aReply), cbMaxTransfer));
1000 rcReq = vscsiLunReqSenseOkSet(&pVScsiLunMmc->Core, pVScsiReq);
1001 }
1002 else
1003 rcReq = vscsiLunReqSenseErrorSet(&pVScsiLunMmc->Core, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST,
1004 SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
1005
1006 return rcReq;
1007}
1008
1009static DECLCALLBACK(int) vscsiLunMmcInit(PVSCSILUNINT pVScsiLun)
1010{
1011 PVSCSILUNMMC pVScsiLunMmc = (PVSCSILUNMMC)pVScsiLun;
1012 int rc = VINF_SUCCESS;
1013
1014 ASMAtomicWriteU32((volatile uint32_t *)&pVScsiLunMmc->MediaEventStatus, MMCEVENTSTATUSTYPE_UNCHANGED);
1015 pVScsiLunMmc->u32MediaTrackType = MMC_MEDIA_TYPE_UNKNOWN;
1016 pVScsiLunMmc->cSectors = 0;
1017
1018 uint32_t cTracks = vscsiLunMediumGetRegionCount(pVScsiLun);
1019 if (cTracks)
1020 {
1021 for (uint32_t i = 0; i < cTracks; i++)
1022 {
1023 uint64_t cBlocks = 0;
1024 rc = vscsiLunMediumQueryRegionProperties(pVScsiLun, i, NULL, &cBlocks,
1025 NULL, NULL);
1026 AssertRC(rc);
1027
1028 pVScsiLunMmc->cSectors += cBlocks;
1029 }
1030
1031 pVScsiLunMmc->Core.fMediaPresent = true;
1032 pVScsiLunMmc->Core.fReady = false;
1033 }
1034 else
1035 {
1036 pVScsiLunMmc->Core.fMediaPresent = false;
1037 pVScsiLunMmc->Core.fReady = false;
1038 }
1039
1040 return rc;
1041}
1042
1043static DECLCALLBACK(int) vscsiLunMmcDestroy(PVSCSILUNINT pVScsiLun)
1044{
1045 RT_NOREF1(pVScsiLun);
1046 return VINF_SUCCESS;
1047}
1048
1049static DECLCALLBACK(int) vscsiLunMmcReqProcess(PVSCSILUNINT pVScsiLun, PVSCSIREQINT pVScsiReq)
1050{
1051 PVSCSILUNMMC pVScsiLunMmc = (PVSCSILUNMMC)pVScsiLun;
1052 VSCSIIOREQTXDIR enmTxDir = VSCSIIOREQTXDIR_INVALID;
1053 uint64_t uLbaStart = 0;
1054 uint32_t cSectorTransfer = 0;
1055 size_t cbSector = 0;
1056 int rc = VINF_SUCCESS;
1057 int rcReq = SCSI_STATUS_OK;
1058 unsigned uCmd = pVScsiReq->pbCDB[0];
1059 PCRTSGSEG paSegs = pVScsiReq->SgBuf.paSegs;
1060 unsigned cSegs = pVScsiReq->SgBuf.cSegs;
1061
1062 LogFlowFunc(("pVScsiLun=%#p{.fReady=%RTbool, .fMediaPresent=%RTbool} pVScsiReq=%#p{.pbCdb[0]=%#x}\n",
1063 pVScsiLun, pVScsiLun->fReady, pVScsiLun->fMediaPresent, pVScsiReq, uCmd));
1064
1065 /*
1066 * GET CONFIGURATION, GET EVENT/STATUS NOTIFICATION, INQUIRY, and REQUEST SENSE commands
1067 * operate even when a unit attention condition exists for initiator; every other command
1068 * needs to report CHECK CONDITION in that case.
1069 */
1070 if ( !pVScsiLunMmc->Core.fReady
1071 && uCmd != SCSI_INQUIRY
1072 && uCmd != SCSI_GET_CONFIGURATION
1073 && uCmd != SCSI_GET_EVENT_STATUS_NOTIFICATION)
1074 {
1075 /*
1076 * A note on media changes: As long as a medium is not present, the unit remains in
1077 * the 'not ready' state. Technically the unit becomes 'ready' soon after a medium
1078 * is inserted; however, we internally keep the 'not ready' state until we've had
1079 * a chance to report the UNIT ATTENTION status indicating a media change.
1080 */
1081 if (pVScsiLunMmc->Core.fMediaPresent)
1082 {
1083 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_UNIT_ATTENTION,
1084 SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED, 0x00);
1085 pVScsiLunMmc->Core.fReady = true;
1086 }
1087 else
1088 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_NOT_READY,
1089 SCSI_ASC_MEDIUM_NOT_PRESENT, 0x00);
1090 }
1091 else
1092 {
1093 switch (uCmd)
1094 {
1095 case SCSI_TEST_UNIT_READY:
1096 Assert(!pVScsiLunMmc->Core.fReady); /* Only should get here if LUN isn't ready. */
1097 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT, 0x00);
1098 break;
1099
1100 case SCSI_INQUIRY:
1101 {
1102 SCSIINQUIRYDATA ScsiInquiryReply;
1103
1104 vscsiReqSetXferSize(pVScsiReq, scsiBE2H_U16(&pVScsiReq->pbCDB[3]));
1105 memset(&ScsiInquiryReply, 0, sizeof(ScsiInquiryReply));
1106
1107 ScsiInquiryReply.cbAdditional = 31;
1108 ScsiInquiryReply.fRMB = 1; /* Removable. */
1109 ScsiInquiryReply.u5PeripheralDeviceType = SCSI_INQUIRY_DATA_PERIPHERAL_DEVICE_TYPE_CD_DVD;
1110 ScsiInquiryReply.u3PeripheralQualifier = SCSI_INQUIRY_DATA_PERIPHERAL_QUALIFIER_CONNECTED;
1111 ScsiInquiryReply.u3AnsiVersion = 0x05; /* MMC-?? compliant */
1112 ScsiInquiryReply.fCmdQue = 1; /* Command queuing supported. */
1113 ScsiInquiryReply.fWBus16 = 1;
1114
1115 const char *pszVendorId = "VBOX";
1116 const char *pszProductId = "CD-ROM";
1117 const char *pszProductLevel = "1.0";
1118 int rcTmp = vscsiLunQueryInqStrings(pVScsiLun, &pszVendorId, &pszProductId, &pszProductLevel);
1119 Assert(RT_SUCCESS(rcTmp) || rcTmp == VERR_NOT_FOUND); RT_NOREF(rcTmp);
1120
1121 scsiPadStrS(ScsiInquiryReply.achVendorId, pszVendorId, 8);
1122 scsiPadStrS(ScsiInquiryReply.achProductId, pszProductId, 16);
1123 scsiPadStrS(ScsiInquiryReply.achProductLevel, pszProductLevel, 4);
1124
1125 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, (uint8_t *)&ScsiInquiryReply, sizeof(SCSIINQUIRYDATA));
1126 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
1127 break;
1128 }
1129 case SCSI_READ_CAPACITY:
1130 {
1131 uint8_t aReply[8];
1132 memset(aReply, 0, sizeof(aReply));
1133 vscsiReqSetXferSize(pVScsiReq, sizeof(aReply));
1134
1135 /*
1136 * If sector size exceeds the maximum value that is
1137 * able to be stored in 4 bytes return 0xffffffff in this field
1138 */
1139 if (pVScsiLunMmc->cSectors > UINT32_C(0xffffffff))
1140 scsiH2BE_U32(aReply, UINT32_C(0xffffffff));
1141 else
1142 scsiH2BE_U32(aReply, pVScsiLunMmc->cSectors - 1);
1143 scsiH2BE_U32(&aReply[4], _2K);
1144 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply));
1145 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
1146 break;
1147 }
1148 case SCSI_MODE_SENSE_6:
1149 {
1150 uint8_t uModePage = pVScsiReq->pbCDB[2] & 0x3f;
1151 uint8_t aReply[24];
1152 uint8_t *pu8ReplyPos;
1153 bool fValid = false;
1154
1155 vscsiReqSetXferSize(pVScsiReq, pVScsiReq->pbCDB[4]);
1156 memset(aReply, 0, sizeof(aReply));
1157 aReply[0] = 4; /* Reply length 4. */
1158 aReply[1] = 0; /* Default media type. */
1159 aReply[2] = RT_BIT(4); /* Caching supported. */
1160 aReply[3] = 0; /* Block descriptor length. */
1161
1162 pu8ReplyPos = aReply + 4;
1163
1164 if ((uModePage == 0x08) || (uModePage == 0x3f))
1165 {
1166 memset(pu8ReplyPos, 0, 20);
1167 *pu8ReplyPos++ = 0x08; /* Page code. */
1168 *pu8ReplyPos++ = 0x12; /* Size of the page. */
1169 *pu8ReplyPos++ = 0x4; /* Write cache enabled. */
1170 fValid = true;
1171 } else if (uModePage == 0) {
1172 fValid = true;
1173 }
1174
1175 /* Querying unknown pages must fail. */
1176 if (fValid) {
1177 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply));
1178 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
1179 } else {
1180 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
1181 }
1182 break;
1183 }
1184 case SCSI_MODE_SENSE_10:
1185 {
1186 size_t cbMax = scsiBE2H_U16(&pVScsiReq->pbCDB[7]);
1187 vscsiReqSetXferSize(pVScsiReq, cbMax);
1188 rcReq = vscsiLunMmcModeSense10(pVScsiLunMmc, pVScsiReq, cbMax);
1189 break;
1190 }
1191 case SCSI_SEEK_10:
1192 {
1193 uint32_t uLba = scsiBE2H_U32(&pVScsiReq->pbCDB[2]);
1194 if (uLba > pVScsiLunMmc->cSectors)
1195 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST,
1196 SCSI_ASC_LOGICAL_BLOCK_OOR, 0x00);
1197 else
1198 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
1199 break;
1200 }
1201 case SCSI_MODE_SELECT_6:
1202 {
1203 /** @todo implement!! */
1204 vscsiReqSetXferSize(pVScsiReq, pVScsiReq->pbCDB[4]);
1205 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
1206 break;
1207 }
1208 case SCSI_READ_6:
1209 {
1210 enmTxDir = VSCSIIOREQTXDIR_READ;
1211 uLbaStart = ((uint64_t) pVScsiReq->pbCDB[3]
1212 | (pVScsiReq->pbCDB[2] << 8)
1213 | ((pVScsiReq->pbCDB[1] & 0x1f) << 16));
1214 cSectorTransfer = pVScsiReq->pbCDB[4];
1215 cbSector = _2K;
1216 break;
1217 }
1218 case SCSI_READ_10:
1219 {
1220 enmTxDir = VSCSIIOREQTXDIR_READ;
1221 uLbaStart = scsiBE2H_U32(&pVScsiReq->pbCDB[2]);
1222 cSectorTransfer = scsiBE2H_U16(&pVScsiReq->pbCDB[7]);
1223 cbSector = _2K;
1224 break;
1225 }
1226 case SCSI_READ_12:
1227 {
1228 enmTxDir = VSCSIIOREQTXDIR_READ;
1229 uLbaStart = scsiBE2H_U32(&pVScsiReq->pbCDB[2]);
1230 cSectorTransfer = scsiBE2H_U32(&pVScsiReq->pbCDB[6]);
1231 cbSector = _2K;
1232 break;
1233 }
1234 case SCSI_READ_16:
1235 {
1236 enmTxDir = VSCSIIOREQTXDIR_READ;
1237 uLbaStart = scsiBE2H_U64(&pVScsiReq->pbCDB[2]);
1238 cSectorTransfer = scsiBE2H_U32(&pVScsiReq->pbCDB[10]);
1239 cbSector = _2K;
1240 break;
1241 }
1242 case SCSI_READ_CD:
1243 {
1244 uLbaStart = scsiBE2H_U32(&pVScsiReq->pbCDB[2]);
1245 cSectorTransfer = (pVScsiReq->pbCDB[6] << 16) | (pVScsiReq->pbCDB[7] << 8) | pVScsiReq->pbCDB[8];
1246
1247 /*
1248 * If the LBA is in an audio track we are required to ignore pretty much all
1249 * of the channel selection values (except 0x00) and map everything to 0x10
1250 * which means read user data with a sector size of 2352 bytes.
1251 *
1252 * (MMC-6 chapter 6.19.2.6)
1253 */
1254 uint8_t uChnSel = pVScsiReq->pbCDB[9] & 0xf8;
1255 VDREGIONDATAFORM enmDataForm;
1256 uint64_t cbSectorRegion = 0;
1257 rc = vscsiLunMediumQueryRegionPropertiesForLba(pVScsiLun, uLbaStart,
1258 NULL, NULL, &cbSectorRegion,
1259 &enmDataForm);
1260 AssertRC(rc);
1261
1262 if (enmDataForm == VDREGIONDATAFORM_CDDA)
1263 {
1264 if (uChnSel == 0)
1265 {
1266 /* nothing */
1267 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
1268 }
1269 else
1270 {
1271 enmTxDir = VSCSIIOREQTXDIR_READ;
1272 cbSector = 2352;
1273 }
1274 }
1275 else
1276 {
1277 switch (uChnSel)
1278 {
1279 case 0x00:
1280 /* nothing */
1281 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
1282 break;
1283 case 0x10:
1284 /* normal read */
1285 enmTxDir = VSCSIIOREQTXDIR_READ;
1286 cbSector = _2K;
1287 break;
1288 case 0xf8:
1289 {
1290 if (cbSectorRegion == 2048)
1291 {
1292 /*
1293 * Read all data, sector size is 2352.
1294 * Rearrange the buffer and fill the gaps with the sync bytes.
1295 */
1296 /* Count the number of segments for the buffer we require. */
1297 RTSGBUF SgBuf;
1298 bool fBufTooSmall = false;
1299 uint32_t cSegsNew = 0;
1300 RTSgBufClone(&SgBuf, &pVScsiReq->SgBuf);
1301 for (uint32_t uLba = (uint32_t)uLbaStart; uLba < uLbaStart + cSectorTransfer; uLba++)
1302 {
1303 size_t cbTmp = RTSgBufAdvance(&SgBuf, 16);
1304 if (cbTmp < 16)
1305 {
1306 fBufTooSmall = true;
1307 break;
1308 }
1309
1310 cbTmp = 2048;
1311 while (cbTmp)
1312 {
1313 size_t cbBuf = cbTmp;
1314 RTSgBufGetNextSegment(&SgBuf, &cbBuf);
1315 if (!cbBuf)
1316 {
1317 fBufTooSmall = true;
1318 break;
1319 }
1320
1321 cbTmp -= cbBuf;
1322 cSegsNew++;
1323 }
1324
1325 cbTmp = RTSgBufAdvance(&SgBuf, 280);
1326 if (cbTmp < 280)
1327 {
1328 fBufTooSmall = true;
1329 break;
1330 }
1331 }
1332
1333 if (!fBufTooSmall)
1334 {
1335 PRTSGSEG paSegsNew = (PRTSGSEG)RTMemAllocZ(cSegsNew * sizeof(RTSGSEG));
1336 if (paSegsNew)
1337 {
1338 enmTxDir = VSCSIIOREQTXDIR_READ;
1339
1340 uint32_t idxSeg = 0;
1341 for (uint32_t uLba = (uint32_t)uLbaStart; uLba < uLbaStart + cSectorTransfer; uLba++)
1342 {
1343 /* Sync bytes, see 4.2.3.8 CD Main Channel Block Formats */
1344 uint8_t abBuf[16];
1345 abBuf[0] = 0x00;
1346 memset(&abBuf[1], 0xff, 10);
1347 abBuf[11] = 0x00;
1348 /* MSF */
1349 scsiLBA2MSF(&abBuf[12], uLba);
1350 abBuf[15] = 0x01; /* mode 1 data */
1351 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, &abBuf[0], sizeof(abBuf));
1352
1353 size_t cbTmp = 2048;
1354 while (cbTmp)
1355 {
1356 size_t cbBuf = cbTmp;
1357 paSegsNew[idxSeg].pvSeg = RTSgBufGetNextSegment(&pVScsiReq->SgBuf, &cbBuf);
1358 paSegsNew[idxSeg].cbSeg = cbBuf;
1359 idxSeg++;
1360
1361 cbTmp -= cbBuf;
1362 }
1363
1364 /**
1365 * @todo: maybe compute ECC and parity, layout is:
1366 * 2072 4 EDC
1367 * 2076 172 P parity symbols
1368 * 2248 104 Q parity symbols
1369 */
1370 RTSgBufSet(&pVScsiReq->SgBuf, 0, 280);
1371 }
1372
1373 paSegs = paSegsNew;
1374 cSegs = cSegsNew;
1375 pVScsiReq->pvLun = paSegsNew;
1376 }
1377 else
1378 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST,
1379 SCSI_ASC_LOGICAL_BLOCK_OOR, 0x00);
1380 }
1381 else
1382 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST,
1383 SCSI_ASC_LOGICAL_BLOCK_OOR, 0x00);
1384 }
1385 else if (cbSectorRegion == 2352)
1386 {
1387 /* Sector size matches what is read. */
1388 cbSector = cbSectorRegion;
1389 enmTxDir = VSCSIIOREQTXDIR_READ;
1390 }
1391 break;
1392 }
1393 default:
1394 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST,
1395 SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
1396 break;
1397 }
1398 }
1399 break;
1400 }
1401 case SCSI_READ_BUFFER:
1402 {
1403 uint8_t uDataMode = pVScsiReq->pbCDB[1] & 0x1f;
1404
1405 vscsiReqSetXferSize(pVScsiReq, scsiBE2H_U16(&pVScsiReq->pbCDB[6]));
1406
1407 switch (uDataMode)
1408 {
1409 case 0x00:
1410 case 0x01:
1411 case 0x02:
1412 case 0x03:
1413 case 0x0a:
1414 break;
1415 case 0x0b:
1416 {
1417 uint8_t aReply[4];
1418 RT_ZERO(aReply);
1419
1420 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply));
1421 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
1422 break;
1423 }
1424 case 0x1a:
1425 case 0x1c:
1426 break;
1427 default:
1428 AssertMsgFailed(("Invalid data mode\n"));
1429 }
1430 break;
1431 }
1432 case SCSI_VERIFY_10:
1433 case SCSI_START_STOP_UNIT:
1434 {
1435 int rc2 = VINF_SUCCESS;
1436 switch (pVScsiReq->pbCDB[4] & 3)
1437 {
1438 case 0: /* 00 - Stop motor */
1439 case 1: /* 01 - Start motor */
1440 break;
1441 case 2: /* 10 - Eject media */
1442 rc2 = vscsiLunMediumEject(pVScsiLun);
1443 break;
1444 case 3: /* 11 - Load media */
1445 /** @todo */
1446 break;
1447 }
1448 if (RT_SUCCESS(rc2))
1449 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
1450 else
1451 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_MEDIA_LOAD_OR_EJECT_FAILED, 0x02);
1452 break;
1453 }
1454 case SCSI_LOG_SENSE:
1455 {
1456 uint8_t uPageCode = pVScsiReq->pbCDB[2] & 0x3f;
1457 uint8_t uSubPageCode = pVScsiReq->pbCDB[3];
1458
1459 vscsiReqSetXferSize(pVScsiReq, scsiBE2H_U16(&pVScsiReq->pbCDB[7]));
1460
1461 switch (uPageCode)
1462 {
1463 case 0x00:
1464 {
1465 if (uSubPageCode == 0)
1466 {
1467 uint8_t aReply[4];
1468
1469 aReply[0] = 0;
1470 aReply[1] = 0;
1471 aReply[2] = 0;
1472 aReply[3] = 0;
1473
1474 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply));
1475 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
1476 break;
1477 }
1478 }
1479 RT_FALL_THRU();
1480 default:
1481 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
1482 }
1483 break;
1484 }
1485 case SCSI_SERVICE_ACTION_IN_16:
1486 {
1487 switch (pVScsiReq->pbCDB[1] & 0x1f)
1488 {
1489 case SCSI_SVC_ACTION_IN_READ_CAPACITY_16:
1490 {
1491 uint8_t aReply[32];
1492
1493 memset(aReply, 0, sizeof(aReply));
1494 scsiH2BE_U64(aReply, pVScsiLunMmc->cSectors - 1);
1495 scsiH2BE_U32(&aReply[8], _2K);
1496 /* Leave the rest 0 */
1497
1498 vscsiReqSetXferSize(pVScsiReq, sizeof(aReply));
1499 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply));
1500 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
1501 break;
1502 }
1503 default:
1504 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST,
1505 SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00); /* Don't know if this is correct */
1506 }
1507 break;
1508 }
1509 case SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL:
1510 {
1511 pVScsiLunMmc->fLocked = RT_BOOL(pVScsiReq->pbCDB[4] & 0x01);
1512 vscsiLunMediumSetLock(pVScsiLun, pVScsiLunMmc->fLocked);
1513 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
1514 break;
1515 }
1516 case SCSI_READ_TOC_PMA_ATIP:
1517 {
1518 uint8_t format;
1519 uint16_t cbMax;
1520 bool fMSF;
1521
1522 format = pVScsiReq->pbCDB[2] & 0x0f;
1523 cbMax = scsiBE2H_U16(&pVScsiReq->pbCDB[7]);
1524 fMSF = (pVScsiReq->pbCDB[1] >> 1) & 1;
1525
1526 vscsiReqSetXferSize(pVScsiReq, cbMax);
1527 switch (format)
1528 {
1529 case 0x00:
1530 rcReq = mmcReadTOCNormal(pVScsiLun, pVScsiReq, cbMax, fMSF);
1531 break;
1532 case 0x01:
1533 rcReq = mmcReadTOCMulti(pVScsiLun, pVScsiReq, cbMax, fMSF);
1534 break;
1535 case 0x02:
1536 rcReq = mmcReadTOCRaw(pVScsiLun, pVScsiReq, cbMax, fMSF);
1537 break;
1538 default:
1539 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
1540 }
1541 break;
1542 }
1543 case SCSI_GET_EVENT_STATUS_NOTIFICATION:
1544 {
1545 /* Only supporting polled mode at the moment. */
1546 size_t cbMax = scsiBE2H_U16(&pVScsiReq->pbCDB[7]);
1547
1548 vscsiReqSetXferSize(pVScsiReq, cbMax);
1549 if (pVScsiReq->pbCDB[1] & 0x1)
1550 rcReq = vscsiLunMmcGetEventStatusNotification(pVScsiLunMmc, pVScsiReq, cbMax);
1551 else
1552 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
1553 break;
1554 }
1555 case SCSI_MECHANISM_STATUS:
1556 {
1557 size_t cbMax = scsiBE2H_U16(&pVScsiReq->pbCDB[8]);
1558 uint8_t aReply[8];
1559
1560 vscsiReqSetXferSize(pVScsiReq, cbMax);
1561 scsiH2BE_U16(&aReply[0], 0);
1562 /* no current LBA */
1563 aReply[2] = 0;
1564 aReply[3] = 0;
1565 aReply[4] = 0;
1566 aReply[5] = 1;
1567 scsiH2BE_U16(&aReply[6], 0);
1568 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, RT_MIN(sizeof(aReply), cbMax));
1569 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
1570 break;
1571 }
1572 case SCSI_READ_DISC_INFORMATION:
1573 {
1574 uint8_t aReply[34];
1575 size_t cbMax = scsiBE2H_U16(&pVScsiReq->pbCDB[7]);
1576
1577 vscsiReqSetXferSize(pVScsiReq, cbMax);
1578 memset(aReply, '\0', sizeof(aReply));
1579 scsiH2BE_U16(&aReply[0], 32);
1580 aReply[2] = (0 << 4) | (3 << 2) | (2 << 0); /* not erasable, complete session, complete disc */
1581 aReply[3] = 1; /* number of first track */
1582 aReply[4] = 1; /* number of sessions (LSB) */
1583 aReply[5] = 1; /* first track number in last session (LSB) */
1584 aReply[6] = (uint8_t)vscsiLunMediumGetRegionCount(pVScsiLun); /* last track number in last session (LSB) */
1585 aReply[7] = (0 << 7) | (0 << 6) | (1 << 5) | (0 << 2) | (0 << 0); /* disc id not valid, disc bar code not valid, unrestricted use, not dirty, not RW medium */
1586 aReply[8] = 0; /* disc type = CD-ROM */
1587 aReply[9] = 0; /* number of sessions (MSB) */
1588 aReply[10] = 0; /* number of sessions (MSB) */
1589 aReply[11] = 0; /* number of sessions (MSB) */
1590 scsiH2BE_U32(&aReply[16], 0x00ffffff); /* last session lead-in start time is not available */
1591 scsiH2BE_U32(&aReply[20], 0x00ffffff); /* last possible start time for lead-out is not available */
1592 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, RT_MIN(sizeof(aReply), cbMax));
1593 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
1594 break;
1595 }
1596 case SCSI_READ_TRACK_INFORMATION:
1597 {
1598 size_t cbMax = scsiBE2H_U16(&pVScsiReq->pbCDB[7]);
1599 vscsiReqSetXferSize(pVScsiReq, cbMax);
1600 rcReq = vscsiLunMmcReadTrackInformation(pVScsiLunMmc, pVScsiReq, cbMax);
1601 break;
1602 }
1603 case SCSI_GET_CONFIGURATION:
1604 {
1605 size_t cbMax = scsiBE2H_U16(&pVScsiReq->pbCDB[7]);
1606 vscsiReqSetXferSize(pVScsiReq, cbMax);
1607 rcReq = vscsiLunMmcGetConfiguration(pVScsiLunMmc, pVScsiReq, cbMax);
1608 break;
1609 }
1610 case SCSI_READ_DVD_STRUCTURE:
1611 {
1612 size_t cbMax = scsiBE2H_U16(&pVScsiReq->pbCDB[8]);
1613 vscsiReqSetXferSize(pVScsiReq, cbMax);
1614 rcReq = vscsiLunMmcReadDvdStructure(pVScsiLunMmc, pVScsiReq, cbMax);
1615 break;
1616 }
1617 default:
1618 //AssertMsgFailed(("Command %#x [%s] not implemented\n", pVScsiReq->pbCDB[0], SCSICmdText(pVScsiReq->pbCDB[0])));
1619 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ILLEGAL_OPCODE, 0x00);
1620 }
1621 }
1622
1623 if (enmTxDir != VSCSIIOREQTXDIR_INVALID)
1624 {
1625 LogFlow(("%s: uLbaStart=%llu cSectorTransfer=%u\n",
1626 __FUNCTION__, uLbaStart, cSectorTransfer));
1627
1628 vscsiReqSetXferSize(pVScsiReq, cSectorTransfer * cbSector);
1629 if (RT_UNLIKELY(uLbaStart + cSectorTransfer > pVScsiLunMmc->cSectors))
1630 {
1631 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_LOGICAL_BLOCK_OOR, 0x00);
1632 vscsiDeviceReqComplete(pVScsiLun->pVScsiDevice, pVScsiReq, rcReq, false, VINF_SUCCESS);
1633 }
1634 else if (!cSectorTransfer)
1635 {
1636 /* A 0 transfer length is not an error. */
1637 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
1638 vscsiDeviceReqComplete(pVScsiLun->pVScsiDevice, pVScsiReq, rcReq, false, VINF_SUCCESS);
1639 }
1640 else
1641 {
1642 /* Check that the sector size is valid. */
1643 VDREGIONDATAFORM enmDataForm = VDREGIONDATAFORM_INVALID;
1644 rc = vscsiLunMediumQueryRegionPropertiesForLba(pVScsiLun, uLbaStart,
1645 NULL, NULL, NULL, &enmDataForm);
1646 if (RT_FAILURE(rc))
1647 {
1648 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_LOGICAL_BLOCK_OOR, 0x00);
1649 vscsiDeviceReqComplete(pVScsiLun->pVScsiDevice, pVScsiReq, rcReq, false, VINF_SUCCESS);
1650 }
1651 else if ( enmDataForm != VDREGIONDATAFORM_MODE1_2048
1652 && enmDataForm != VDREGIONDATAFORM_MODE1_2352
1653 && enmDataForm != VDREGIONDATAFORM_MODE2_2336
1654 && enmDataForm != VDREGIONDATAFORM_MODE2_2352
1655 && enmDataForm != VDREGIONDATAFORM_RAW
1656 && cbSector == _2K)
1657 {
1658 rcReq = vscsiLunReqSenseErrorInfoSet(pVScsiLun, pVScsiReq,
1659 SCSI_SENSE_ILLEGAL_REQUEST | SCSI_SENSE_FLAG_ILI,
1660 SCSI_ASC_ILLEGAL_MODE_FOR_THIS_TRACK, 0, uLbaStart);
1661 vscsiDeviceReqComplete(pVScsiLun->pVScsiDevice, pVScsiReq, rcReq, false, VINF_SUCCESS);
1662 }
1663 else
1664 {
1665 /* Enqueue new I/O request */
1666 rc = vscsiIoReqTransferEnqueueEx(pVScsiLun, pVScsiReq, enmTxDir,
1667 uLbaStart * cbSector,
1668 paSegs, cSegs, cSectorTransfer * cbSector);
1669 }
1670 }
1671 }
1672 else /* Request completed */
1673 vscsiDeviceReqComplete(pVScsiLun->pVScsiDevice, pVScsiReq, rcReq, false, VINF_SUCCESS);
1674
1675 return rc;
1676}
1677
1678/** @interface_method_impl{VSCSILUNDESC,pfnVScsiLunReqFree} */
1679static DECLCALLBACK(void) vscsiLunMmcReqFree(PVSCSILUNINT pVScsiLun, PVSCSIREQINT pVScsiReq,
1680 void *pvLun)
1681{
1682 RT_NOREF2(pVScsiLun, pVScsiReq);
1683 RTMemFree(pvLun);
1684}
1685
1686/** @interface_method_impl{VSCSILUNDESC,pfnVScsiLunMediumInserted} */
1687static DECLCALLBACK(int) vscsiLunMmcMediumInserted(PVSCSILUNINT pVScsiLun)
1688{
1689 int rc = VINF_SUCCESS;
1690 PVSCSILUNMMC pVScsiLunMmc = (PVSCSILUNMMC)pVScsiLun;
1691
1692 pVScsiLunMmc->cSectors = 0;
1693 uint32_t cTracks = vscsiLunMediumGetRegionCount(pVScsiLun);
1694 for (uint32_t i = 0; i < cTracks && RT_SUCCESS(rc); i++)
1695 {
1696 uint64_t cBlocks = 0;
1697 rc = vscsiLunMediumQueryRegionProperties(pVScsiLun, i, NULL, &cBlocks,
1698 NULL, NULL);
1699 if (RT_FAILURE(rc))
1700 break;
1701 pVScsiLunMmc->cSectors += cBlocks;
1702 }
1703
1704 if (RT_SUCCESS(rc))
1705 {
1706 uint32_t OldStatus, NewStatus;
1707 do
1708 {
1709 OldStatus = ASMAtomicReadU32((volatile uint32_t *)&pVScsiLunMmc->MediaEventStatus);
1710 switch (OldStatus)
1711 {
1712 case MMCEVENTSTATUSTYPE_MEDIA_CHANGED:
1713 case MMCEVENTSTATUSTYPE_MEDIA_REMOVED:
1714 /* no change, we will send "medium removed" + "medium inserted" */
1715 NewStatus = MMCEVENTSTATUSTYPE_MEDIA_CHANGED;
1716 break;
1717 default:
1718 NewStatus = MMCEVENTSTATUSTYPE_MEDIA_NEW;
1719 break;
1720 }
1721 } while (!ASMAtomicCmpXchgU32((volatile uint32_t *)&pVScsiLunMmc->MediaEventStatus,
1722 NewStatus, OldStatus));
1723
1724 ASMAtomicXchgU32(&pVScsiLunMmc->u32MediaTrackType, MMC_MEDIA_TYPE_UNKNOWN);
1725 }
1726
1727 return rc;
1728}
1729
1730/** @interface_method_impl{VSCSILUNDESC,pfnVScsiLunMediumRemoved} */
1731static DECLCALLBACK(int) vscsiLunMmcMediumRemoved(PVSCSILUNINT pVScsiLun)
1732{
1733 PVSCSILUNMMC pVScsiLunMmc = (PVSCSILUNMMC)pVScsiLun;
1734
1735 ASMAtomicWriteU32((volatile uint32_t *)&pVScsiLunMmc->MediaEventStatus, MMCEVENTSTATUSTYPE_MEDIA_REMOVED);
1736 ASMAtomicXchgU32(&pVScsiLunMmc->u32MediaTrackType, MMC_MEDIA_TYPE_NO_DISC);
1737 pVScsiLunMmc->cSectors = 0;
1738 return VINF_SUCCESS;
1739}
1740
1741
1742VSCSILUNDESC g_VScsiLunTypeMmc =
1743{
1744 /** enmLunType */
1745 VSCSILUNTYPE_MMC,
1746 /** pcszDescName */
1747 "MMC",
1748 /** cbLun */
1749 sizeof(VSCSILUNMMC),
1750 /** cSupOpcInfo */
1751 0,
1752 /** paSupOpcInfo */
1753 NULL,
1754 /** pfnVScsiLunInit */
1755 vscsiLunMmcInit,
1756 /** pfnVScsiLunDestroy */
1757 vscsiLunMmcDestroy,
1758 /** pfnVScsiLunReqProcess */
1759 vscsiLunMmcReqProcess,
1760 /** pfnVScsiLunReqFree */
1761 vscsiLunMmcReqFree,
1762 /** pfnVScsiLunMediumInserted */
1763 vscsiLunMmcMediumInserted,
1764 /** pfnVScsiLunMediumRemoved */
1765 vscsiLunMmcMediumRemoved
1766};
Note: See TracBrowser for help on using the repository browser.

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