VirtualBox

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

Last change on this file since 94368 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

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