VirtualBox

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

Last change on this file since 106061 was 106061, checked in by vboxsync, 5 months ago

Copyright year updates by scm.

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