VirtualBox

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

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

Devices/Storage/VSCSI: Make sure the additional allocated segments are freed when the request completes

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

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