VirtualBox

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

Last change on this file since 64132 was 64132, checked in by vboxsync, 8 years ago

VSCSI: Add method to eject media from the guest and make use of it in DrvSCSI, also add callbacks for attaching and detaching drivers below DrvSCSI which will be required to avoid destroying/recreating the driver every time the medium is changed

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 50.1 KB
Line 
1/* $Id: VSCSILunMmc.cpp 64132 2016-10-03 16:23:11Z vboxsync $ */
2/** @file
3 * Virtual SCSI driver: MMC LUN implementation (CD/DVD-ROM)
4 */
5
6/*
7 * Copyright (C) 2006-2016 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 * Different event status types.
36 */
37typedef enum MMCEVENTSTATUSTYPE
38{
39 /** Medium event status not changed. */
40 MMCEVENTSTATUSTYPE_UNCHANGED = 0,
41 /** New medium inserted. */
42 MMCEVENTSTATUSTYPE_MEDIA_NEW,
43 /** Medium removed. */
44 MMCEVENTSTATUSTYPE_MEDIA_REMOVED,
45 /** Medium was removed + new medium was inserted. */
46 MMCEVENTSTATUSTYPE_MEDIA_CHANGED,
47 /** Medium eject requested (eject button pressed). */
48 MMCEVENTSTATUSTYPE_MEDIA_EJECT_REQUESTED,
49 /** 32bit hack. */
50 MMCEVENTSTATUSTYPE_32BIT_HACK = 0x7fffffff
51} MMCEVENTSTATUSTYPE;
52
53/** @name Media track types.
54 * @{ */
55/** Unknown media type. */
56#define MMC_MEDIA_TYPE_UNKNOWN 0
57/** Door closed, no media. */
58#define MMC_MEDIA_TYPE_NO_DISC 0x70
59/** @} */
60
61
62/**
63 * MMC LUN instance
64 */
65typedef struct VSCSILUNMMC
66{
67 /** Core LUN structure */
68 VSCSILUNINT Core;
69 /** Size of the virtual disk. */
70 uint64_t cSectors;
71 /** Sector size. */
72 uint32_t cbSector;
73 /** Medium locked indicator. */
74 bool fLocked;
75 /** Media event status. */
76 volatile MMCEVENTSTATUSTYPE MediaEventStatus;
77 /** Media track type. */
78 volatile uint32_t u32MediaTrackType;
79} VSCSILUNMMC, *PVSCSILUNMMC;
80
81
82DECLINLINE(void) mmcLBA2MSF(uint8_t *pbBuf, uint32_t iLBA)
83{
84 iLBA += 150;
85 pbBuf[0] = (iLBA / 75) / 60;
86 pbBuf[1] = (iLBA / 75) % 60;
87 pbBuf[2] = iLBA % 75;
88}
89
90#if 0 /* unused */
91DECLINLINE(uint32_t) mmcMSF2LBA(const uint8_t *pbBuf)
92{
93 return (pbBuf[0] * 60 + pbBuf[1]) * 75 + pbBuf[2];
94}
95#endif
96
97
98/* Fabricate normal TOC information. */
99static int mmcReadTOCNormal(PVSCSILUNINT pVScsiLun, PVSCSIREQINT pVScsiReq, uint16_t cbMaxTransfer, bool fMSF)
100{
101 PVSCSILUNMMC pVScsiLunMmc = (PVSCSILUNMMC)pVScsiLun;
102 uint8_t aReply[32];
103 uint8_t *pbBuf = aReply;
104 uint8_t *q;
105 uint8_t iStartTrack;
106 uint32_t cbSize;
107
108 iStartTrack = pVScsiReq->pbCDB[6];
109 if (iStartTrack > 1 && iStartTrack != 0xaa)
110 {
111 return vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
112 }
113 q = pbBuf + 2;
114 *q++ = 1; /* first session */
115 *q++ = 1; /* last session */
116 if (iStartTrack <= 1)
117 {
118 *q++ = 0; /* reserved */
119 *q++ = 0x14; /* ADR, CONTROL */
120 *q++ = 1; /* track number */
121 *q++ = 0; /* reserved */
122 if (fMSF)
123 {
124 *q++ = 0; /* reserved */
125 mmcLBA2MSF(q, 0);
126 q += 3;
127 }
128 else
129 {
130 /* sector 0 */
131 vscsiH2BEU32(q, 0);
132 q += 4;
133 }
134 }
135 /* lead out track */
136 *q++ = 0; /* reserved */
137 *q++ = 0x14; /* ADR, CONTROL */
138 *q++ = 0xaa; /* track number */
139 *q++ = 0; /* reserved */
140 if (fMSF)
141 {
142 *q++ = 0; /* reserved */
143 mmcLBA2MSF(q, pVScsiLunMmc->cSectors);
144 q += 3;
145 }
146 else
147 {
148 vscsiH2BEU32(q, pVScsiLunMmc->cSectors);
149 q += 4;
150 }
151 cbSize = q - pbBuf;
152 Assert(cbSize <= sizeof(aReply));
153 vscsiH2BEU16(pbBuf, cbSize - 2);
154 if (cbSize < cbMaxTransfer)
155 cbMaxTransfer = cbSize;
156
157 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, cbMaxTransfer);
158
159 return vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
160}
161
162/* Fabricate session information. */
163static int mmcReadTOCMulti(PVSCSILUNINT pVScsiLun, PVSCSIREQINT pVScsiReq, uint16_t cbMaxTransfer, bool fMSF)
164{
165 RT_NOREF1(cbMaxTransfer);
166 uint8_t aReply[32];
167 uint8_t *pbBuf = aReply;
168
169 /* multi session: only a single session defined */
170 memset(pbBuf, 0, 12);
171 pbBuf[1] = 0x0a;
172 pbBuf[2] = 0x01; /* first complete session number */
173 pbBuf[3] = 0x01; /* last complete session number */
174 pbBuf[5] = 0x14; /* ADR, CONTROL */
175 pbBuf[6] = 1; /* first track in last complete session */
176
177 if (fMSF)
178 {
179 pbBuf[8] = 0; /* reserved */
180 mmcLBA2MSF(pbBuf + 8, 0);
181 }
182 else
183 {
184 /* sector 0 */
185 vscsiH2BEU32(pbBuf + 8, 0);
186 }
187
188 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, 12);
189
190 return vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
191}
192
193/**
194 * Create raw TOC data information.
195 *
196 * @returns SCSI status code.
197 * @param pVScsiLun The LUN instance.
198 * @param pVScsiReq The VSCSI request.
199 * @param cbMaxTransfer The maximum transfer size.
200 * @param fMSF Flag whether to use MSF format to encode sector numbers.
201 */
202static int mmcReadTOCRaw(PVSCSILUNINT pVScsiLun, PVSCSIREQINT pVScsiReq, uint16_t cbMaxTransfer, bool fMSF)
203{
204 PVSCSILUNMMC pVScsiLunMmc = (PVSCSILUNMMC)pVScsiLun;
205 uint8_t aReply[50]; /* Counted a maximum of 45 bytes but better be on the safe side. */
206 uint32_t cbSize;
207 uint8_t *pbBuf = &aReply[0] + 2;
208
209 *pbBuf++ = 1; /* first session */
210 *pbBuf++ = 1; /* last session */
211
212 *pbBuf++ = 1; /* session number */
213 *pbBuf++ = 0x14; /* data track */
214 *pbBuf++ = 0; /* track number */
215 *pbBuf++ = 0xa0; /* first track in program area */
216 *pbBuf++ = 0; /* min */
217 *pbBuf++ = 0; /* sec */
218 *pbBuf++ = 0; /* frame */
219 *pbBuf++ = 0;
220 *pbBuf++ = 1; /* first track */
221 *pbBuf++ = 0x00; /* disk type CD-DA or CD data */
222 *pbBuf++ = 0;
223
224 *pbBuf++ = 1; /* session number */
225 *pbBuf++ = 0x14; /* data track */
226 *pbBuf++ = 0; /* track number */
227 *pbBuf++ = 0xa1; /* last track in program area */
228 *pbBuf++ = 0; /* min */
229 *pbBuf++ = 0; /* sec */
230 *pbBuf++ = 0; /* frame */
231 *pbBuf++ = 0;
232 *pbBuf++ = 1; /* last track */
233 *pbBuf++ = 0;
234 *pbBuf++ = 0;
235
236 *pbBuf++ = 1; /* session number */
237 *pbBuf++ = 0x14; /* data track */
238 *pbBuf++ = 0; /* track number */
239 *pbBuf++ = 0xa2; /* lead-out */
240 *pbBuf++ = 0; /* min */
241 *pbBuf++ = 0; /* sec */
242 *pbBuf++ = 0; /* frame */
243 if (fMSF)
244 {
245 *pbBuf++ = 0; /* reserved */
246 mmcLBA2MSF(pbBuf, pVScsiLunMmc->cSectors);
247 pbBuf += 3;
248 }
249 else
250 {
251 vscsiH2BEU32(pbBuf, pVScsiLunMmc->cSectors);
252 pbBuf += 4;
253 }
254
255 *pbBuf++ = 1; /* session number */
256 *pbBuf++ = 0x14; /* ADR, control */
257 *pbBuf++ = 0; /* track number */
258 *pbBuf++ = 1; /* point */
259 *pbBuf++ = 0; /* min */
260 *pbBuf++ = 0; /* sec */
261 *pbBuf++ = 0; /* frame */
262 if (fMSF)
263 {
264 *pbBuf++ = 0; /* reserved */
265 mmcLBA2MSF(pbBuf, 0);
266 pbBuf += 3;
267 }
268 else
269 {
270 /* sector 0 */
271 vscsiH2BEU32(pbBuf, 0);
272 pbBuf += 4;
273 }
274
275 cbSize = pbBuf - aReply;
276 vscsiH2BEU16(&aReply[0], cbSize - 2);
277
278 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, RT_MIN(cbMaxTransfer, cbSize));
279 return vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
280}
281
282static size_t vscsiLunMmcGetConfigurationFillFeatureListProfiles(uint8_t *pbBuf, size_t cbBuf)
283{
284 if (cbBuf < 3*4)
285 return 0;
286
287 vscsiH2BEU16(pbBuf, 0x0); /* feature 0: list of profiles supported */
288 pbBuf[2] = (0 << 2) | (1 << 1) | (1 << 0); /* version 0, persistent, current */
289 pbBuf[3] = 8; /* additional bytes for profiles */
290 /* The MMC-3 spec says that DVD-ROM read capability should be reported
291 * before CD-ROM read capability. */
292 vscsiH2BEU16(pbBuf + 4, 0x10); /* profile: read-only DVD */
293 pbBuf[6] = (0 << 0); /* NOT current profile */
294 vscsiH2BEU16(pbBuf + 8, 0x08); /* profile: read only CD */
295 pbBuf[10] = (1 << 0); /* current profile */
296
297 return 3*4; /* Header + 2 profiles entries */
298}
299
300static size_t vscsiLunMmcGetConfigurationFillFeatureCore(uint8_t *pbBuf, size_t cbBuf)
301{
302 if (cbBuf < 12)
303 return 0;
304
305 vscsiH2BEU16(pbBuf, 0x1); /* feature 0001h: Core Feature */
306 pbBuf[2] = (0x2 << 2) | RT_BIT(1) | RT_BIT(0); /* Version | Persistent | Current */
307 pbBuf[3] = 8; /* Additional length */
308 vscsiH2BEU16(pbBuf + 4, 0x00000002); /* Physical interface ATAPI. */
309 pbBuf[8] = RT_BIT(0); /* DBE */
310 /* Rest is reserved. */
311
312 return 12;
313}
314
315static size_t vscsiLunMmcGetConfigurationFillFeatureMorphing(uint8_t *pbBuf, size_t cbBuf)
316{
317 if (cbBuf < 8)
318 return 0;
319
320 vscsiH2BEU16(pbBuf, 0x2); /* feature 0002h: Morphing Feature */
321 pbBuf[2] = (0x1 << 2) | RT_BIT(1) | RT_BIT(0); /* Version | Persistent | Current */
322 pbBuf[3] = 4; /* Additional length */
323 pbBuf[4] = RT_BIT(1) | 0x0; /* OCEvent | !ASYNC */
324 /* Rest is reserved. */
325
326 return 8;
327}
328
329static size_t vscsiLunMmcGetConfigurationFillFeatureRemovableMedium(uint8_t *pbBuf, size_t cbBuf)
330{
331 if (cbBuf < 8)
332 return 0;
333
334 vscsiH2BEU16(pbBuf, 0x3); /* feature 0003h: Removable Medium Feature */
335 pbBuf[2] = (0x2 << 2) | RT_BIT(1) | RT_BIT(0); /* Version | Persistent | Current */
336 pbBuf[3] = 4; /* Additional length */
337 /* Tray type loading | Load | Eject | !Pvnt Jmpr | !DBML | Lock */
338 pbBuf[4] = (0x2 << 5) | RT_BIT(4) | RT_BIT(3) | (0x0 << 2) | (0x0 << 1) | RT_BIT(0);
339 /* Rest is reserved. */
340
341 return 8;
342}
343
344static size_t vscsiLunMmcGetConfigurationFillFeatureRandomReadable(uint8_t *pbBuf, size_t cbBuf)
345{
346 if (cbBuf < 12)
347 return 0;
348
349 vscsiH2BEU16(pbBuf, 0x10); /* feature 0010h: Random Readable Feature */
350 pbBuf[2] = (0x0 << 2) | RT_BIT(1) | RT_BIT(0); /* Version | Persistent | Current */
351 pbBuf[3] = 8; /* Additional length */
352 vscsiH2BEU32(pbBuf + 4, 2048); /* Logical block size. */
353 vscsiH2BEU16(pbBuf + 8, 0x10); /* Blocking (0x10 for DVD, CD is not defined). */
354 pbBuf[10] = 0; /* PP not present */
355 /* Rest is reserved. */
356
357 return 12;
358}
359
360static size_t vscsiLunMmcGetConfigurationFillFeatureCDRead(uint8_t *pbBuf, size_t cbBuf)
361{
362 if (cbBuf < 8)
363 return 0;
364
365 vscsiH2BEU16(pbBuf, 0x1e); /* feature 001Eh: CD Read Feature */
366 pbBuf[2] = (0x2 << 2) | RT_BIT(1) | RT_BIT(0); /* Version | Persistent | Current */
367 pbBuf[3] = 0; /* Additional length */
368 pbBuf[4] = (0x0 << 7) | (0x0 << 1) | 0x0; /* !DAP | !C2-Flags | !CD-Text. */
369 /* Rest is reserved. */
370
371 return 8;
372}
373
374static size_t vscsiLunMmcGetConfigurationFillFeaturePowerManagement(uint8_t *pbBuf, size_t cbBuf)
375{
376 if (cbBuf < 4)
377 return 0;
378
379 vscsiH2BEU16(pbBuf, 0x100); /* feature 0100h: Power Management Feature */
380 pbBuf[2] = (0x0 << 2) | RT_BIT(1) | RT_BIT(0); /* Version | Persistent | Current */
381 pbBuf[3] = 0; /* Additional length */
382
383 return 4;
384}
385
386static size_t vscsiLunMmcGetConfigurationFillFeatureTimeout(uint8_t *pbBuf, size_t cbBuf)
387{
388 if (cbBuf < 8)
389 return 0;
390
391 vscsiH2BEU16(pbBuf, 0x105); /* feature 0105h: Timeout Feature */
392 pbBuf[2] = (0x0 << 2) | RT_BIT(1) | RT_BIT(0); /* Version | Persistent | Current */
393 pbBuf[3] = 4; /* Additional length */
394 pbBuf[4] = 0x0; /* !Group3 */
395
396 return 8;
397}
398
399/**
400 * Processes the GET CONFIGURATION SCSI request.
401 *
402 * @returns SCSI status code.
403 * @param pVScsiLunMmc The MMC LUN instance.
404 * @param pVScsiReq The VSCSI request.
405 * @param cbMaxTransfer The maximum transfer size.
406 */
407static int vscsiLunMmcGetConfiguration(PVSCSILUNMMC pVScsiLunMmc, PVSCSIREQINT pVScsiReq, size_t cbMaxTransfer)
408{
409 uint8_t aReply[80];
410 uint8_t *pbBuf = &aReply[0];
411 size_t cbBuf = sizeof(aReply);
412 size_t cbCopied = 0;
413
414 /* Accept valid request types only, and only starting feature 0. */
415 if ((pVScsiReq->pbCDB[1] & 0x03) == 3 || vscsiBE2HU16(&pVScsiReq->pbCDB[2]) != 0)
416 return vscsiLunReqSenseErrorSet(&pVScsiLunMmc->Core, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST,
417 SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
418
419 /** @todo implement switching between CD-ROM and DVD-ROM profile (the only
420 * way to differentiate them right now is based on the image size). */
421 if (pVScsiLunMmc->cSectors)
422 vscsiH2BEU16(pbBuf + 6, 0x08); /* current profile: read-only CD */
423 else
424 vscsiH2BEU16(pbBuf + 6, 0x00); /* current profile: none -> no media */
425 cbBuf -= 8;
426 pbBuf += 8;
427
428 cbCopied = vscsiLunMmcGetConfigurationFillFeatureListProfiles(pbBuf, cbBuf);
429 cbBuf -= cbCopied;
430 pbBuf += cbCopied;
431
432 cbCopied = vscsiLunMmcGetConfigurationFillFeatureCore(pbBuf, cbBuf);
433 cbBuf -= cbCopied;
434 pbBuf += cbCopied;
435
436 cbCopied = vscsiLunMmcGetConfigurationFillFeatureMorphing(pbBuf, cbBuf);
437 cbBuf -= cbCopied;
438 pbBuf += cbCopied;
439
440 cbCopied = vscsiLunMmcGetConfigurationFillFeatureRemovableMedium(pbBuf, cbBuf);
441 cbBuf -= cbCopied;
442 pbBuf += cbCopied;
443
444 cbCopied = vscsiLunMmcGetConfigurationFillFeatureRandomReadable(pbBuf, cbBuf);
445 cbBuf -= cbCopied;
446 pbBuf += cbCopied;
447
448 cbCopied = vscsiLunMmcGetConfigurationFillFeatureCDRead(pbBuf, cbBuf);
449 cbBuf -= cbCopied;
450 pbBuf += cbCopied;
451
452 cbCopied = vscsiLunMmcGetConfigurationFillFeaturePowerManagement(pbBuf, cbBuf);
453 cbBuf -= cbCopied;
454 pbBuf += cbCopied;
455
456 cbCopied = vscsiLunMmcGetConfigurationFillFeatureTimeout(pbBuf, cbBuf);
457 cbBuf -= cbCopied;
458 pbBuf += cbCopied;
459
460 /* Set data length now. */
461 vscsiH2BEU32(&aReply[0], (uint32_t)(sizeof(aReply) - cbBuf));
462
463 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, RT_MIN(cbMaxTransfer, sizeof(aReply) - cbBuf));
464 return vscsiLunReqSenseOkSet(&pVScsiLunMmc->Core, pVScsiReq);
465}
466
467/**
468 * Processes the READ DVD STRUCTURE SCSI request.
469 *
470 * @returns SCSI status code.
471 * @param pVScsiLunMmc The MMC LUN instance.
472 * @param pVScsiReq The VSCSI request.
473 * @param cbMaxTransfer The maximum transfer size.
474 */
475static int vscsiLunMmcReadDvdStructure(PVSCSILUNMMC pVScsiLunMmc, PVSCSIREQINT pVScsiReq, size_t cbMaxTransfer)
476{
477 uint8_t aReply[25]; /* Counted a maximum of 20 bytes but better be on the safe side. */
478
479 RT_ZERO(aReply);
480
481 /* Act according to the indicated format. */
482 switch (pVScsiReq->pbCDB[7])
483 {
484 case 0x00:
485 case 0x01:
486 case 0x02:
487 case 0x03:
488 case 0x04:
489 case 0x05:
490 case 0x06:
491 case 0x07:
492 case 0x08:
493 case 0x09:
494 case 0x0a:
495 case 0x0b:
496 case 0x0c:
497 case 0x0d:
498 case 0x0e:
499 case 0x0f:
500 case 0x10:
501 case 0x11:
502 case 0x30:
503 case 0x31:
504 case 0xff:
505 if (pVScsiReq->pbCDB[1] == 0)
506 {
507 int uASC = SCSI_ASC_NONE;
508
509 switch (pVScsiReq->pbCDB[7])
510 {
511 case 0x0: /* Physical format information */
512 {
513 uint8_t uLayer = pVScsiReq->pbCDB[6];
514 uint64_t cTotalSectors;
515
516 if (uLayer != 0)
517 {
518 uASC = -SCSI_ASC_INV_FIELD_IN_CMD_PACKET;
519 break;
520 }
521
522 cTotalSectors = pVScsiLunMmc->cSectors;
523 cTotalSectors >>= 2;
524 if (cTotalSectors == 0)
525 {
526 uASC = -SCSI_ASC_MEDIUM_NOT_PRESENT;
527 break;
528 }
529
530 aReply[4] = 1; /* DVD-ROM, part version 1 */
531 aReply[5] = 0xf; /* 120mm disc, minimum rate unspecified */
532 aReply[6] = 1; /* one layer, read-only (per MMC-2 spec) */
533 aReply[7] = 0; /* default densities */
534
535 /* FIXME: 0x30000 per spec? */
536 vscsiH2BEU32(&aReply[8], 0); /* start sector */
537 vscsiH2BEU32(&aReply[12], cTotalSectors - 1); /* end sector */
538 vscsiH2BEU32(&aReply[16], cTotalSectors - 1); /* l0 end sector */
539
540 /* Size of buffer, not including 2 byte size field */
541 vscsiH2BEU32(&aReply[0], 2048 + 2);
542
543 /* 2k data + 4 byte header */
544 uASC = (2048 + 4);
545 break;
546 }
547 case 0x01: /* DVD copyright information */
548 aReply[4] = 0; /* no copyright data */
549 aReply[5] = 0; /* no region restrictions */
550
551 /* Size of buffer, not including 2 byte size field */
552 vscsiH2BEU16(&aReply[0], 4 + 2);
553
554 /* 4 byte header + 4 byte data */
555 uASC = (4 + 4);
556 break;
557
558 case 0x03: /* BCA information - invalid field for no BCA info */
559 uASC = -SCSI_ASC_INV_FIELD_IN_CMD_PACKET;
560 break;
561
562 case 0x04: /* DVD disc manufacturing information */
563 /* Size of buffer, not including 2 byte size field */
564 vscsiH2BEU16(&aReply[0], 2048 + 2);
565
566 /* 2k data + 4 byte header */
567 uASC = (2048 + 4);
568 break;
569 case 0xff:
570 /*
571 * This lists all the command capabilities above. Add new ones
572 * in order and update the length and buffer return values.
573 */
574
575 aReply[4] = 0x00; /* Physical format */
576 aReply[5] = 0x40; /* Not writable, is readable */
577 vscsiH2BEU16(&aReply[6], 2048 + 4);
578
579 aReply[8] = 0x01; /* Copyright info */
580 aReply[9] = 0x40; /* Not writable, is readable */
581 vscsiH2BEU16(&aReply[10], 4 + 4);
582
583 aReply[12] = 0x03; /* BCA info */
584 aReply[13] = 0x40; /* Not writable, is readable */
585 vscsiH2BEU16(&aReply[14], 188 + 4);
586
587 aReply[16] = 0x04; /* Manufacturing info */
588 aReply[17] = 0x40; /* Not writable, is readable */
589 vscsiH2BEU16(&aReply[18], 2048 + 4);
590
591 /* Size of buffer, not including 2 byte size field */
592 vscsiH2BEU16(&aReply[0], 16 + 2);
593
594 /* data written + 4 byte header */
595 uASC = (16 + 4);
596 break;
597 default: /** @todo formats beyond DVD-ROM requires */
598 uASC = -SCSI_ASC_INV_FIELD_IN_CMD_PACKET;
599 }
600
601 if (uASC < 0)
602 return vscsiLunReqSenseErrorSet(&pVScsiLunMmc->Core, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST,
603 -uASC, 0x00);
604 break;
605 }
606 /** @todo BD support, fall through for now */
607
608 /* Generic disk structures */
609 case 0x80: /** @todo AACS volume identifier */
610 case 0x81: /** @todo AACS media serial number */
611 case 0x82: /** @todo AACS media identifier */
612 case 0x83: /** @todo AACS media key block */
613 case 0x90: /** @todo List of recognized format layers */
614 case 0xc0: /** @todo Write protection status */
615 default:
616 return vscsiLunReqSenseErrorSet(&pVScsiLunMmc->Core, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST,
617 SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
618 }
619
620 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, RT_MIN(cbMaxTransfer, sizeof(aReply)));
621 return vscsiLunReqSenseOkSet(&pVScsiLunMmc->Core, pVScsiReq);
622}
623
624/**
625 * Processes the MODE SENSE 10 SCSI request.
626 *
627 * @returns SCSI status code.
628 * @param pVScsiLunMmc The MMC LUN instance.
629 * @param pVScsiReq The VSCSI request.
630 * @param cbMaxTransfer The maximum transfer size.
631 */
632static int vscsiLunMmcModeSense10(PVSCSILUNMMC pVScsiLunMmc, PVSCSIREQINT pVScsiReq, size_t cbMaxTransfer)
633{
634 int rcReq;
635 uint8_t uPageControl = pVScsiReq->pbCDB[2] >> 6;
636 uint8_t uPageCode = pVScsiReq->pbCDB[2] & 0x3f;
637
638 switch (uPageControl)
639 {
640 case SCSI_PAGECONTROL_CURRENT:
641 switch (uPageCode)
642 {
643 case SCSI_MODEPAGE_ERROR_RECOVERY:
644 {
645 uint8_t aReply[16];
646
647 vscsiH2BEU16(&aReply[0], 16 + 6);
648 aReply[2] = (uint8_t)pVScsiLunMmc->u32MediaTrackType;
649 aReply[3] = 0;
650 aReply[4] = 0;
651 aReply[5] = 0;
652 aReply[6] = 0;
653 aReply[7] = 0;
654
655 aReply[8] = 0x01;
656 aReply[9] = 0x06;
657 aReply[10] = 0x00;
658 aReply[11] = 0x05;
659 aReply[12] = 0x00;
660 aReply[13] = 0x00;
661 aReply[14] = 0x00;
662 aReply[15] = 0x00;
663 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, RT_MIN(cbMaxTransfer, sizeof(aReply)));
664 rcReq = vscsiLunReqSenseOkSet(&pVScsiLunMmc->Core, pVScsiReq);
665 break;
666 }
667 case SCSI_MODEPAGE_CD_STATUS:
668 {
669 uint8_t aReply[40];
670
671 vscsiH2BEU16(&aReply[0], 38);
672 aReply[2] = (uint8_t)pVScsiLunMmc->u32MediaTrackType;
673 aReply[3] = 0;
674 aReply[4] = 0;
675 aReply[5] = 0;
676 aReply[6] = 0;
677 aReply[7] = 0;
678
679 aReply[8] = 0x2a;
680 aReply[9] = 30; /* page length */
681 aReply[10] = 0x08; /* DVD-ROM read support */
682 aReply[11] = 0x00; /* no write support */
683 /* The following claims we support audio play. This is obviously false,
684 * but the Linux generic CDROM support makes many features depend on this
685 * capability. If it's not set, this causes many things to be disabled. */
686 aReply[12] = 0x71; /* multisession support, mode 2 form 1/2 support, audio play */
687 aReply[13] = 0x00; /* no subchannel reads supported */
688 aReply[14] = (1 << 0) | (1 << 3) | (1 << 5); /* lock supported, eject supported, tray type loading mechanism */
689 if (pVScsiLunMmc->fLocked)
690 aReply[14] |= 1 << 1; /* report lock state */
691 aReply[15] = 0; /* no subchannel reads supported, no separate audio volume control, no changer etc. */
692 vscsiH2BEU16(&aReply[16], 5632); /* (obsolete) claim 32x speed support */
693 vscsiH2BEU16(&aReply[18], 2); /* number of audio volume levels */
694 vscsiH2BEU16(&aReply[20], 128); /* buffer size supported in Kbyte - We don't have a buffer because we write directly into guest memory.
695 Just write some dummy value. */
696 vscsiH2BEU16(&aReply[22], 5632); /* (obsolete) current read speed 32x */
697 aReply[24] = 0; /* reserved */
698 aReply[25] = 0; /* reserved for digital audio (see idx 15) */
699 vscsiH2BEU16(&aReply[26], 0); /* (obsolete) maximum write speed */
700 vscsiH2BEU16(&aReply[28], 0); /* (obsolete) current write speed */
701 vscsiH2BEU16(&aReply[30], 0); /* copy management revision supported 0=no CSS */
702 aReply[32] = 0; /* reserved */
703 aReply[33] = 0; /* reserved */
704 aReply[34] = 0; /* reserved */
705 aReply[35] = 1; /* rotation control CAV */
706 vscsiH2BEU16(&aReply[36], 0); /* current write speed */
707 vscsiH2BEU16(&aReply[38], 0); /* number of write speed performance descriptors */
708 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, RT_MIN(cbMaxTransfer, sizeof(aReply)));
709 rcReq = vscsiLunReqSenseOkSet(&pVScsiLunMmc->Core, pVScsiReq);
710 break;
711 }
712 default:
713 rcReq = vscsiLunReqSenseErrorSet(&pVScsiLunMmc->Core, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST,
714 SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
715 break;
716 }
717 break;
718 case SCSI_PAGECONTROL_CHANGEABLE:
719 case SCSI_PAGECONTROL_DEFAULT:
720 rcReq = vscsiLunReqSenseErrorSet(&pVScsiLunMmc->Core, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST,
721 SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
722 break;
723 default:
724 case SCSI_PAGECONTROL_SAVED:
725 rcReq = vscsiLunReqSenseErrorSet(&pVScsiLunMmc->Core, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST,
726 SCSI_ASC_SAVING_PARAMETERS_NOT_SUPPORTED, 0x00);
727 break;
728 }
729
730 return rcReq;
731}
732
733/**
734 * Processes the GET EVENT STATUS NOTIFICATION SCSI request.
735 *
736 * @returns SCSI status code.
737 * @param pVScsiLunMmc The MMC LUN instance.
738 * @param pVScsiReq The VSCSI request.
739 * @param cbMaxTransfer The maximum transfer size.
740 */
741static int vscsiLunMmcGetEventStatusNotification(PVSCSILUNMMC pVScsiLunMmc, PVSCSIREQINT pVScsiReq,
742 size_t cbMaxTransfer)
743{
744 uint32_t OldStatus;
745 uint32_t NewStatus;
746 uint8_t aReply[8];
747 RT_ZERO(aReply);
748
749 do
750 {
751 OldStatus = ASMAtomicReadU32((volatile uint32_t *)&pVScsiLunMmc->MediaEventStatus);
752 NewStatus = MMCEVENTSTATUSTYPE_UNCHANGED;
753
754 switch (OldStatus)
755 {
756 case MMCEVENTSTATUSTYPE_MEDIA_NEW:
757 /* mount */
758 vscsiH2BEU16(&aReply[0], 6);
759 aReply[2] = 0x04; /* media */
760 aReply[3] = 0x5e; /* supported = busy|media|external|power|operational */
761 aReply[4] = 0x02; /* new medium */
762 aReply[5] = 0x02; /* medium present / door closed */
763 aReply[6] = 0x00;
764 aReply[7] = 0x00;
765 break;
766
767 case MMCEVENTSTATUSTYPE_MEDIA_CHANGED:
768 case MMCEVENTSTATUSTYPE_MEDIA_REMOVED:
769 /* umount */
770 vscsiH2BEU16(&aReply[0], 6);
771 aReply[2] = 0x04; /* media */
772 aReply[3] = 0x5e; /* supported = busy|media|external|power|operational */
773 aReply[4] = 0x03; /* media removal */
774 aReply[5] = 0x00; /* medium absent / door closed */
775 aReply[6] = 0x00;
776 aReply[7] = 0x00;
777 if (OldStatus == MMCEVENTSTATUSTYPE_MEDIA_CHANGED)
778 NewStatus = MMCEVENTSTATUSTYPE_MEDIA_NEW;
779 break;
780
781 case MMCEVENTSTATUSTYPE_MEDIA_EJECT_REQUESTED: /* currently unused */
782 vscsiH2BEU16(&aReply[0], 6);
783 aReply[2] = 0x04; /* media */
784 aReply[3] = 0x5e; /* supported = busy|media|external|power|operational */
785 aReply[4] = 0x01; /* eject requested (eject button pressed) */
786 aReply[5] = 0x02; /* medium present / door closed */
787 aReply[6] = 0x00;
788 aReply[7] = 0x00;
789 break;
790
791 case MMCEVENTSTATUSTYPE_UNCHANGED:
792 default:
793 vscsiH2BEU16(&aReply[0], 6);
794 aReply[2] = 0x01; /* operational change request / notification */
795 aReply[3] = 0x5e; /* supported = busy|media|external|power|operational */
796 aReply[4] = 0x00;
797 aReply[5] = 0x00;
798 aReply[6] = 0x00;
799 aReply[7] = 0x00;
800 break;
801 }
802 } while (!ASMAtomicCmpXchgU32((volatile uint32_t *)&pVScsiLunMmc->MediaEventStatus, NewStatus, OldStatus));
803
804 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, RT_MIN(cbMaxTransfer, sizeof(aReply)));
805 return vscsiLunReqSenseOkSet(&pVScsiLunMmc->Core, pVScsiReq);
806}
807
808static DECLCALLBACK(int) vscsiLunMmcInit(PVSCSILUNINT pVScsiLun)
809{
810 PVSCSILUNMMC pVScsiLunMmc = (PVSCSILUNMMC)pVScsiLun;
811 uint64_t cbDisk = 0;
812 int rc = VINF_SUCCESS;
813
814 ASMAtomicWriteU32((volatile uint32_t *)&pVScsiLunMmc->MediaEventStatus, MMCEVENTSTATUSTYPE_UNCHANGED);
815 pVScsiLunMmc->u32MediaTrackType = MMC_MEDIA_TYPE_UNKNOWN;
816 pVScsiLunMmc->cbSector = 2048; /* Default to 2K sectors. */
817 rc = vscsiLunMediumGetSize(pVScsiLun, &cbDisk);
818 if (RT_SUCCESS(rc))
819 pVScsiLunMmc->cSectors = cbDisk / pVScsiLunMmc->cbSector;
820
821 return rc;
822}
823
824static DECLCALLBACK(int) vscsiLunMmcDestroy(PVSCSILUNINT pVScsiLun)
825{
826 RT_NOREF1(pVScsiLun);
827 return VINF_SUCCESS;
828}
829
830static DECLCALLBACK(int) vscsiLunMmcReqProcess(PVSCSILUNINT pVScsiLun, PVSCSIREQINT pVScsiReq)
831{
832 PVSCSILUNMMC pVScsiLunMmc = (PVSCSILUNMMC)pVScsiLun;
833 VSCSIIOREQTXDIR enmTxDir = VSCSIIOREQTXDIR_INVALID;
834 uint64_t uLbaStart = 0;
835 uint32_t cSectorTransfer = 0;
836 int rc = VINF_SUCCESS;
837 int rcReq = SCSI_STATUS_OK;
838 unsigned uCmd = pVScsiReq->pbCDB[0];
839
840 /*
841 * GET CONFIGURATION, GET EVENT/STATUS NOTIFICATION, INQUIRY, and REQUEST SENSE commands
842 * operate even when a unit attention condition exists for initiator; every other command
843 * needs to report CHECK CONDITION in that case.
844 */
845 if (!pVScsiLunMmc->Core.fReady && uCmd != SCSI_INQUIRY)
846 {
847 /*
848 * A note on media changes: As long as a medium is not present, the unit remains in
849 * the 'not ready' state. Technically the unit becomes 'ready' soon after a medium
850 * is inserted; however, we internally keep the 'not ready' state until we've had
851 * a chance to report the UNIT ATTENTION status indicating a media change.
852 */
853 if (pVScsiLunMmc->Core.fMediaPresent)
854 {
855 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_UNIT_ATTENTION,
856 SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED, 0x00);
857 pVScsiLunMmc->Core.fReady = true;
858 }
859 else
860 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_NOT_READY,
861 SCSI_ASC_MEDIUM_NOT_PRESENT, 0x00);
862 }
863 else
864 {
865 switch (uCmd)
866 {
867 case SCSI_TEST_UNIT_READY:
868 Assert(!pVScsiLunMmc->Core.fReady); /* Only should get here if LUN isn't ready. */
869 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT, 0x00);
870 break;
871
872 case SCSI_INQUIRY:
873 {
874 SCSIINQUIRYDATA ScsiInquiryReply;
875
876 memset(&ScsiInquiryReply, 0, sizeof(ScsiInquiryReply));
877
878 ScsiInquiryReply.cbAdditional = 31;
879 ScsiInquiryReply.fRMB = 1; /* Removable. */
880 ScsiInquiryReply.u5PeripheralDeviceType = SCSI_INQUIRY_DATA_PERIPHERAL_DEVICE_TYPE_CD_DVD;
881 ScsiInquiryReply.u3PeripheralQualifier = SCSI_INQUIRY_DATA_PERIPHERAL_QUALIFIER_CONNECTED;
882 ScsiInquiryReply.u3AnsiVersion = 0x05; /* MMC-?? compliant */
883 ScsiInquiryReply.fCmdQue = 1; /* Command queuing supported. */
884 ScsiInquiryReply.fWBus16 = 1;
885 vscsiPadStr(ScsiInquiryReply.achVendorId, "VBOX", 8);
886 vscsiPadStr(ScsiInquiryReply.achProductId, "CD-ROM", 16);
887 vscsiPadStr(ScsiInquiryReply.achProductLevel, "1.0", 4);
888
889 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, (uint8_t *)&ScsiInquiryReply, sizeof(SCSIINQUIRYDATA));
890 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
891 break;
892 }
893 case SCSI_READ_CAPACITY:
894 {
895 uint8_t aReply[8];
896 memset(aReply, 0, sizeof(aReply));
897
898 /*
899 * If sector size exceeds the maximum value that is
900 * able to be stored in 4 bytes return 0xffffffff in this field
901 */
902 if (pVScsiLunMmc->cSectors > UINT32_C(0xffffffff))
903 vscsiH2BEU32(aReply, UINT32_C(0xffffffff));
904 else
905 vscsiH2BEU32(aReply, pVScsiLunMmc->cSectors - 1);
906 vscsiH2BEU32(&aReply[4], pVScsiLunMmc->cbSector);
907 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply));
908 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
909 break;
910 }
911 case SCSI_MODE_SENSE_6:
912 {
913 uint8_t uModePage = pVScsiReq->pbCDB[2] & 0x3f;
914 uint8_t aReply[24];
915 uint8_t *pu8ReplyPos;
916 bool fValid = false;
917
918 memset(aReply, 0, sizeof(aReply));
919 aReply[0] = 4; /* Reply length 4. */
920 aReply[1] = 0; /* Default media type. */
921 aReply[2] = RT_BIT(4); /* Caching supported. */
922 aReply[3] = 0; /* Block descriptor length. */
923
924 pu8ReplyPos = aReply + 4;
925
926 if ((uModePage == 0x08) || (uModePage == 0x3f))
927 {
928 memset(pu8ReplyPos, 0, 20);
929 *pu8ReplyPos++ = 0x08; /* Page code. */
930 *pu8ReplyPos++ = 0x12; /* Size of the page. */
931 *pu8ReplyPos++ = 0x4; /* Write cache enabled. */
932 fValid = true;
933 } else if (uModePage == 0) {
934 fValid = true;
935 }
936
937 /* Querying unknown pages must fail. */
938 if (fValid) {
939 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply));
940 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
941 } else {
942 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
943 }
944 break;
945 }
946 case SCSI_MODE_SENSE_10:
947 {
948 size_t cbMax = vscsiBE2HU32(&pVScsiReq->pbCDB[7]);
949 rcReq = vscsiLunMmcModeSense10(pVScsiLunMmc, pVScsiReq, cbMax);
950 break;
951 }
952 case SCSI_SEEK_10:
953 {
954 uint32_t uLba = vscsiBE2HU32(&pVScsiReq->pbCDB[2]);
955 if (uLba > pVScsiLunMmc->cSectors)
956 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST,
957 SCSI_ASC_LOGICAL_BLOCK_OOR, 0x00);
958 else
959 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
960 break;
961 }
962 case SCSI_MODE_SELECT_6:
963 {
964 /** @todo implement!! */
965 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
966 break;
967 }
968 case SCSI_READ_6:
969 {
970 enmTxDir = VSCSIIOREQTXDIR_READ;
971 uLbaStart = ((uint64_t) pVScsiReq->pbCDB[3]
972 | (pVScsiReq->pbCDB[2] << 8)
973 | ((pVScsiReq->pbCDB[1] & 0x1f) << 16));
974 cSectorTransfer = pVScsiReq->pbCDB[4];
975 break;
976 }
977 case SCSI_READ_10:
978 {
979 enmTxDir = VSCSIIOREQTXDIR_READ;
980 uLbaStart = vscsiBE2HU32(&pVScsiReq->pbCDB[2]);
981 cSectorTransfer = vscsiBE2HU16(&pVScsiReq->pbCDB[7]);
982 break;
983 }
984 case SCSI_READ_12:
985 {
986 enmTxDir = VSCSIIOREQTXDIR_READ;
987 uLbaStart = vscsiBE2HU32(&pVScsiReq->pbCDB[2]);
988 cSectorTransfer = vscsiBE2HU32(&pVScsiReq->pbCDB[6]);
989 break;
990 }
991 case SCSI_READ_16:
992 {
993 enmTxDir = VSCSIIOREQTXDIR_READ;
994 uLbaStart = vscsiBE2HU64(&pVScsiReq->pbCDB[2]);
995 cSectorTransfer = vscsiBE2HU32(&pVScsiReq->pbCDB[10]);
996 break;
997 }
998 case SCSI_READ_BUFFER:
999 {
1000 uint8_t uDataMode = pVScsiReq->pbCDB[1] & 0x1f;
1001
1002 switch (uDataMode)
1003 {
1004 case 0x00:
1005 case 0x01:
1006 case 0x02:
1007 case 0x03:
1008 case 0x0a:
1009 break;
1010 case 0x0b:
1011 {
1012 uint8_t aReply[4];
1013 RT_ZERO(aReply);
1014
1015 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply));
1016 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
1017 break;
1018 }
1019 case 0x1a:
1020 case 0x1c:
1021 break;
1022 default:
1023 AssertMsgFailed(("Invalid data mode\n"));
1024 }
1025 break;
1026 }
1027 case SCSI_VERIFY_10:
1028 case SCSI_START_STOP_UNIT:
1029 {
1030 int rc2 = VINF_SUCCESS;
1031 switch (pVScsiReq->pbCDB[4] & 3)
1032 {
1033 case 0: /* 00 - Stop motor */
1034 case 1: /* 01 - Start motor */
1035 break;
1036 case 2: /* 10 - Eject media */
1037 rc2 = vscsiLunMediumEject(pVScsiLun);
1038 break;
1039 case 3: /* 11 - Load media */
1040 /** @todo */
1041 break;
1042 }
1043 if (RT_SUCCESS(rc2))
1044 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
1045 else
1046 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_MEDIA_LOAD_OR_EJECT_FAILED, 0x02);
1047 break;
1048 }
1049 case SCSI_LOG_SENSE:
1050 {
1051 uint8_t uPageCode = pVScsiReq->pbCDB[2] & 0x3f;
1052 uint8_t uSubPageCode = pVScsiReq->pbCDB[3];
1053
1054 switch (uPageCode)
1055 {
1056 case 0x00:
1057 {
1058 if (uSubPageCode == 0)
1059 {
1060 uint8_t aReply[4];
1061
1062 aReply[0] = 0;
1063 aReply[1] = 0;
1064 aReply[2] = 0;
1065 aReply[3] = 0;
1066
1067 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply));
1068 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
1069 break;
1070 }
1071 }
1072 default:
1073 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
1074 }
1075 break;
1076 }
1077 case SCSI_SERVICE_ACTION_IN_16:
1078 {
1079 switch (pVScsiReq->pbCDB[1] & 0x1f)
1080 {
1081 case SCSI_SVC_ACTION_IN_READ_CAPACITY_16:
1082 {
1083 uint8_t aReply[32];
1084
1085 memset(aReply, 0, sizeof(aReply));
1086 vscsiH2BEU64(aReply, pVScsiLunMmc->cSectors - 1);
1087 vscsiH2BEU32(&aReply[8], pVScsiLunMmc->cbSector);
1088 /* Leave the rest 0 */
1089 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply));
1090 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
1091 break;
1092 }
1093 default:
1094 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST,
1095 SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00); /* Don't know if this is correct */
1096 }
1097 break;
1098 }
1099 case SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL:
1100 {
1101 pVScsiLunMmc->fLocked = RT_BOOL(pVScsiReq->pbCDB[4] & 0x01);
1102 vscsiLunMediumSetLock(pVScsiLun, pVScsiLunMmc->fLocked);
1103 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
1104 break;
1105 }
1106 case SCSI_READ_TOC_PMA_ATIP:
1107 {
1108 uint8_t format;
1109 uint16_t cbMax;
1110 bool fMSF;
1111
1112 format = pVScsiReq->pbCDB[2] & 0x0f;
1113 cbMax = vscsiBE2HU16(&pVScsiReq->pbCDB[7]);
1114 fMSF = (pVScsiReq->pbCDB[1] >> 1) & 1;
1115 switch (format)
1116 {
1117 case 0x00:
1118 rcReq = mmcReadTOCNormal(pVScsiLun, pVScsiReq, cbMax, fMSF);
1119 break;
1120 case 0x01:
1121 rcReq = mmcReadTOCMulti(pVScsiLun, pVScsiReq, cbMax, fMSF);
1122 break;
1123 case 0x02:
1124 rcReq = mmcReadTOCRaw(pVScsiLun, pVScsiReq, cbMax, fMSF);
1125 break;
1126 default:
1127 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
1128 }
1129 break;
1130 }
1131 case SCSI_GET_EVENT_STATUS_NOTIFICATION:
1132 {
1133 /* Only supporting polled mode at the moment. */
1134 if (pVScsiReq->pbCDB[1] & 0x1)
1135 {
1136 size_t cbMax = vscsiBE2HU16(&pVScsiReq->pbCDB[7]);
1137 rcReq = vscsiLunMmcGetEventStatusNotification(pVScsiLunMmc, pVScsiReq, cbMax);
1138 }
1139 else
1140 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
1141 break;
1142 }
1143 case SCSI_MECHANISM_STATUS:
1144 {
1145 size_t cbMax = vscsiBE2HU16(&pVScsiReq->pbCDB[8]);
1146 uint8_t aReply[8];
1147
1148 vscsiH2BEU16(&aReply[0], 0);
1149 /* no current LBA */
1150 aReply[2] = 0;
1151 aReply[3] = 0;
1152 aReply[4] = 0;
1153 aReply[5] = 1;
1154 vscsiH2BEU16(&aReply[6], 0);
1155 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, RT_MIN(sizeof(aReply), cbMax));
1156 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
1157 break;
1158 }
1159 case SCSI_READ_DISC_INFORMATION:
1160 {
1161 uint8_t aReply[34];
1162 size_t cbMax = vscsiBE2HU16(&pVScsiReq->pbCDB[7]);
1163
1164 memset(aReply, '\0', sizeof(aReply));
1165 vscsiH2BEU16(&aReply[0], 32);
1166 aReply[2] = (0 << 4) | (3 << 2) | (2 << 0); /* not erasable, complete session, complete disc */
1167 aReply[3] = 1; /* number of first track */
1168 aReply[4] = 1; /* number of sessions (LSB) */
1169 aReply[5] = 1; /* first track number in last session (LSB) */
1170 aReply[6] = 1; /* last track number in last session (LSB) */
1171 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 */
1172 aReply[8] = 0; /* disc type = CD-ROM */
1173 aReply[9] = 0; /* number of sessions (MSB) */
1174 aReply[10] = 0; /* number of sessions (MSB) */
1175 aReply[11] = 0; /* number of sessions (MSB) */
1176 vscsiH2BEU32(&aReply[16], 0x00ffffff); /* last session lead-in start time is not available */
1177 vscsiH2BEU32(&aReply[20], 0x00ffffff); /* last possible start time for lead-out is not available */
1178 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, RT_MIN(sizeof(aReply), cbMax));
1179 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
1180 break;
1181 }
1182 case SCSI_READ_TRACK_INFORMATION:
1183 {
1184 /* Accept address/number type of 1 only, and only track 1 exists. */
1185 if ((pVScsiReq->pbCDB[1] & 0x03) != 1 || vscsiBE2HU32(&pVScsiReq->pbCDB[2]) != 1)
1186 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST,
1187 SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
1188 else
1189 {
1190 size_t cbMax = vscsiBE2HU16(&pVScsiReq->pbCDB[7]);
1191 uint8_t aReply[36];
1192 RT_ZERO(aReply);
1193
1194 vscsiH2BEU16(&aReply[0], 34);
1195 aReply[2] = 1; /* track number (LSB) */
1196 aReply[3] = 1; /* session number (LSB) */
1197 aReply[5] = (0 << 5) | (0 << 4) | (4 << 0); /* not damaged, primary copy, data track */
1198 aReply[6] = (0 << 7) | (0 << 6) | (0 << 5) | (0 << 6) | (1 << 0); /* not reserved track, not blank, not packet writing, not fixed packet, data mode 1 */
1199 aReply[7] = (0 << 1) | (0 << 0); /* last recorded address not valid, next recordable address not valid */
1200 vscsiH2BEU32(&aReply[8], 0); /* track start address is 0 */
1201 vscsiH2BEU32(&aReply[24], pVScsiLunMmc->cSectors); /* track size */
1202 aReply[32] = 0; /* track number (MSB) */
1203 aReply[33] = 0; /* session number (MSB) */
1204
1205 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, RT_MIN(sizeof(aReply), cbMax));
1206 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
1207 }
1208 break;
1209 }
1210 case SCSI_GET_CONFIGURATION:
1211 {
1212 size_t cbMax = vscsiBE2HU16(&pVScsiReq->pbCDB[7]);
1213 rcReq = vscsiLunMmcGetConfiguration(pVScsiLunMmc, pVScsiReq, cbMax);
1214 break;
1215 }
1216 case SCSI_READ_DVD_STRUCTURE:
1217 {
1218 size_t cbMax = vscsiBE2HU16(&pVScsiReq->pbCDB[8]);
1219 rcReq = vscsiLunMmcReadDvdStructure(pVScsiLunMmc, pVScsiReq, cbMax);
1220 break;
1221 }
1222 default:
1223 //AssertMsgFailed(("Command %#x [%s] not implemented\n", pVScsiReq->pbCDB[0], SCSICmdText(pVScsiReq->pbCDB[0])));
1224 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ILLEGAL_OPCODE, 0x00);
1225 }
1226 }
1227
1228 if (enmTxDir != VSCSIIOREQTXDIR_INVALID)
1229 {
1230 LogFlow(("%s: uLbaStart=%llu cSectorTransfer=%u\n",
1231 __FUNCTION__, uLbaStart, cSectorTransfer));
1232
1233 if (RT_UNLIKELY(uLbaStart + cSectorTransfer > pVScsiLunMmc->cSectors))
1234 {
1235 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_LOGICAL_BLOCK_OOR, 0x00);
1236 vscsiDeviceReqComplete(pVScsiLun->pVScsiDevice, pVScsiReq, rcReq, false, VINF_SUCCESS);
1237 }
1238 else if (!cSectorTransfer)
1239 {
1240 /* A 0 transfer length is not an error. */
1241 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
1242 vscsiDeviceReqComplete(pVScsiLun->pVScsiDevice, pVScsiReq, rcReq, false, VINF_SUCCESS);
1243 }
1244 else
1245 {
1246 /* Enqueue new I/O request */
1247 rc = vscsiIoReqTransferEnqueue(pVScsiLun, pVScsiReq, enmTxDir,
1248 uLbaStart * pVScsiLunMmc->cbSector,
1249 cSectorTransfer * pVScsiLunMmc->cbSector);
1250 }
1251 }
1252 else /* Request completed */
1253 vscsiDeviceReqComplete(pVScsiLun->pVScsiDevice, pVScsiReq, rcReq, false, VINF_SUCCESS);
1254
1255 return rc;
1256}
1257
1258/** @interface_method_impl{VSCSILUNDESC,pfnVScsiLunMediumInserted} */
1259static DECLCALLBACK(int) vscsiLunMmcMediumInserted(PVSCSILUNINT pVScsiLun)
1260{
1261 PVSCSILUNMMC pVScsiLunMmc = (PVSCSILUNMMC)pVScsiLun;
1262
1263 uint32_t OldStatus, NewStatus;
1264 do
1265 {
1266 OldStatus = ASMAtomicReadU32((volatile uint32_t *)&pVScsiLunMmc->MediaEventStatus);
1267 switch (OldStatus)
1268 {
1269 case MMCEVENTSTATUSTYPE_MEDIA_CHANGED:
1270 case MMCEVENTSTATUSTYPE_MEDIA_REMOVED:
1271 /* no change, we will send "medium removed" + "medium inserted" */
1272 NewStatus = MMCEVENTSTATUSTYPE_MEDIA_CHANGED;
1273 break;
1274 default:
1275 NewStatus = MMCEVENTSTATUSTYPE_MEDIA_NEW;
1276 break;
1277 }
1278 } while (!ASMAtomicCmpXchgU32((volatile uint32_t *)&pVScsiLunMmc->MediaEventStatus,
1279 NewStatus, OldStatus));
1280
1281 ASMAtomicXchgU32(&pVScsiLunMmc->u32MediaTrackType, MMC_MEDIA_TYPE_UNKNOWN);
1282 return VINF_SUCCESS;
1283}
1284
1285/** @interface_method_impl{VSCSILUNDESC,pfnVScsiLunMediumRemoved} */
1286static DECLCALLBACK(int) vscsiLunMmcMediumRemoved(PVSCSILUNINT pVScsiLun)
1287{
1288 PVSCSILUNMMC pVScsiLunMmc = (PVSCSILUNMMC)pVScsiLun;
1289
1290 ASMAtomicWriteU32((volatile uint32_t *)&pVScsiLunMmc->MediaEventStatus, MMCEVENTSTATUSTYPE_MEDIA_REMOVED);
1291 ASMAtomicXchgU32(&pVScsiLunMmc->u32MediaTrackType, MMC_MEDIA_TYPE_NO_DISC);
1292 return VINF_SUCCESS;
1293}
1294
1295
1296VSCSILUNDESC g_VScsiLunTypeMmc =
1297{
1298 /** enmLunType */
1299 VSCSILUNTYPE_MMC,
1300 /** pcszDescName */
1301 "MMC",
1302 /** cbLun */
1303 sizeof(VSCSILUNMMC),
1304 /** pfnVScsiLunInit */
1305 vscsiLunMmcInit,
1306 /** pfnVScsiLunDestroy */
1307 vscsiLunMmcDestroy,
1308 /** pfnVScsiLunReqProcess */
1309 vscsiLunMmcReqProcess,
1310 /** pfnVScsiLunMediumInserted */
1311 vscsiLunMmcMediumInserted,
1312 /** pfnVScsiLunMediumRemoved */
1313 vscsiLunMmcMediumRemoved
1314};
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