VirtualBox

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

Last change on this file since 62885 was 62885, checked in by vboxsync, 9 years ago

Devices: gcc warnings

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.1 KB
Line 
1/* $Id: VSCSILunMmc.cpp 62885 2016-08-02 16:21:40Z 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/assert.h>
28#include <iprt/mem.h>
29#include <iprt/string.h>
30
31#include "VSCSIInternal.h"
32
33/**
34 * MMC LUN instance
35 */
36typedef struct VSCSILUNMMC
37{
38 /** Core LUN structure */
39 VSCSILUNINT Core;
40 /** Size of the virtual disk. */
41 uint64_t cSectors;
42 /** Sector size. */
43 uint32_t cbSector;
44 /** Medium locked indicator. */
45 bool fLocked;
46} VSCSILUNMMC, *PVSCSILUNMMC;
47
48
49DECLINLINE(void) mmcLBA2MSF(uint8_t *pbBuf, uint32_t iLBA)
50{
51 iLBA += 150;
52 pbBuf[0] = (iLBA / 75) / 60;
53 pbBuf[1] = (iLBA / 75) % 60;
54 pbBuf[2] = iLBA % 75;
55}
56
57DECLINLINE(uint32_t) mmcMSF2LBA(const uint8_t *pbBuf)
58{
59 return (pbBuf[0] * 60 + pbBuf[1]) * 75 + pbBuf[2];
60}
61
62
63/* Fabricate normal TOC information. */
64static int mmcReadTOCNormal(PVSCSILUNINT pVScsiLun, PVSCSIREQINT pVScsiReq, uint16_t cbMaxTransfer, bool fMSF)
65{
66 PVSCSILUNMMC pVScsiLunMmc = (PVSCSILUNMMC)pVScsiLun;
67 uint8_t aReply[32];
68 uint8_t *pbBuf = aReply;
69 uint8_t *q;
70 uint8_t iStartTrack;
71 uint32_t cbSize;
72
73 iStartTrack = pVScsiReq->pbCDB[6];
74 if (iStartTrack > 1 && iStartTrack != 0xaa)
75 {
76 return vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
77 }
78 q = pbBuf + 2;
79 *q++ = 1; /* first session */
80 *q++ = 1; /* last session */
81 if (iStartTrack <= 1)
82 {
83 *q++ = 0; /* reserved */
84 *q++ = 0x14; /* ADR, CONTROL */
85 *q++ = 1; /* track number */
86 *q++ = 0; /* reserved */
87 if (fMSF)
88 {
89 *q++ = 0; /* reserved */
90 mmcLBA2MSF(q, 0);
91 q += 3;
92 }
93 else
94 {
95 /* sector 0 */
96 vscsiH2BEU32(q, 0);
97 q += 4;
98 }
99 }
100 /* lead out track */
101 *q++ = 0; /* reserved */
102 *q++ = 0x14; /* ADR, CONTROL */
103 *q++ = 0xaa; /* track number */
104 *q++ = 0; /* reserved */
105 if (fMSF)
106 {
107 *q++ = 0; /* reserved */
108 mmcLBA2MSF(q, pVScsiLunMmc->cSectors);
109 q += 3;
110 }
111 else
112 {
113 vscsiH2BEU32(q, pVScsiLunMmc->cSectors);
114 q += 4;
115 }
116 cbSize = q - pbBuf;
117 Assert(cbSize <= sizeof(aReply));
118 vscsiH2BEU16(pbBuf, cbSize - 2);
119 if (cbSize < cbMaxTransfer)
120 cbMaxTransfer = cbSize;
121
122 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, cbMaxTransfer);
123
124 return vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
125}
126
127/* Fabricate session information. */
128static int mmcReadTOCMulti(PVSCSILUNINT pVScsiLun, PVSCSIREQINT pVScsiReq, uint16_t cbMaxTransfer, bool fMSF)
129{
130 RT_NOREF1(cbMaxTransfer);
131 uint8_t aReply[32];
132 uint8_t *pbBuf = aReply;
133
134 /* multi session: only a single session defined */
135 memset(pbBuf, 0, 12);
136 pbBuf[1] = 0x0a;
137 pbBuf[2] = 0x01; /* first complete session number */
138 pbBuf[3] = 0x01; /* last complete session number */
139 pbBuf[5] = 0x14; /* ADR, CONTROL */
140 pbBuf[6] = 1; /* first track in last complete session */
141
142 if (fMSF)
143 {
144 pbBuf[8] = 0; /* reserved */
145 mmcLBA2MSF(pbBuf + 8, 0);
146 }
147 else
148 {
149 /* sector 0 */
150 vscsiH2BEU32(pbBuf + 8, 0);
151 }
152
153 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, 12);
154
155 return vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
156}
157
158static DECLCALLBACK(int) vscsiLunMmcInit(PVSCSILUNINT pVScsiLun)
159{
160 PVSCSILUNMMC pVScsiLunMmc = (PVSCSILUNMMC)pVScsiLun;
161 uint64_t cbDisk = 0;
162 int rc = VINF_SUCCESS;
163
164 pVScsiLunMmc->cbSector = 2048; /* Default to 2K sectors. */
165 rc = vscsiLunMediumGetSize(pVScsiLun, &cbDisk);
166 if (RT_SUCCESS(rc))
167 pVScsiLunMmc->cSectors = cbDisk / pVScsiLunMmc->cbSector;
168
169 return rc;
170}
171
172static DECLCALLBACK(int) vscsiLunMmcDestroy(PVSCSILUNINT pVScsiLun)
173{
174 RT_NOREF1(pVScsiLun);
175 return VINF_SUCCESS;
176}
177
178static DECLCALLBACK(int) vscsiLunMmcReqProcess(PVSCSILUNINT pVScsiLun, PVSCSIREQINT pVScsiReq)
179{
180 PVSCSILUNMMC pVScsiLunMmc = (PVSCSILUNMMC)pVScsiLun;
181 VSCSIIOREQTXDIR enmTxDir = VSCSIIOREQTXDIR_INVALID;
182 uint64_t uLbaStart = 0;
183 uint32_t cSectorTransfer = 0;
184 int rc = VINF_SUCCESS;
185 int rcReq = SCSI_STATUS_OK;
186 unsigned uCmd = pVScsiReq->pbCDB[0];
187
188 /*
189 * GET CONFIGURATION, GET EVENT/STATUS NOTIFICATION, INQUIRY, and REQUEST SENSE commands
190 * operate even when a unit attention condition exists for initiator; every other command
191 * needs to report CHECK CONDITION in that case.
192 */
193 if (!pVScsiLunMmc->Core.fReady && uCmd != SCSI_INQUIRY)
194 {
195 /*
196 * A note on media changes: As long as a medium is not present, the unit remains in
197 * the 'not ready' state. Technically the unit becomes 'ready' soon after a medium
198 * is inserted; however, we internally keep the 'not ready' state until we've had
199 * a chance to report the UNIT ATTENTION status indicating a media change.
200 */
201 if (pVScsiLunMmc->Core.fMediaPresent)
202 {
203 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_UNIT_ATTENTION,
204 SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED, 0x00);
205 pVScsiLunMmc->Core.fReady = true;
206 }
207 else
208 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_NOT_READY,
209 SCSI_ASC_MEDIUM_NOT_PRESENT, 0x00);
210 }
211 else
212 {
213 switch (uCmd)
214 {
215 case SCSI_TEST_UNIT_READY:
216 Assert(!pVScsiLunMmc->Core.fReady); /* Only should get here if LUN isn't ready. */
217 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT, 0x00);
218 break;
219
220 case SCSI_INQUIRY:
221 {
222 SCSIINQUIRYDATA ScsiInquiryReply;
223
224 memset(&ScsiInquiryReply, 0, sizeof(ScsiInquiryReply));
225
226 ScsiInquiryReply.cbAdditional = 31;
227 ScsiInquiryReply.fRMB = 1; /* Removable. */
228 ScsiInquiryReply.u5PeripheralDeviceType = SCSI_INQUIRY_DATA_PERIPHERAL_DEVICE_TYPE_CD_DVD;
229 ScsiInquiryReply.u3PeripheralQualifier = SCSI_INQUIRY_DATA_PERIPHERAL_QUALIFIER_CONNECTED;
230 ScsiInquiryReply.u3AnsiVersion = 0x05; /* MMC-?? compliant */
231 ScsiInquiryReply.fCmdQue = 1; /* Command queuing supported. */
232 ScsiInquiryReply.fWBus16 = 1;
233 vscsiPadStr(ScsiInquiryReply.achVendorId, "VBOX", 8);
234 vscsiPadStr(ScsiInquiryReply.achProductId, "CD-ROM", 16);
235 vscsiPadStr(ScsiInquiryReply.achProductLevel, "1.0", 4);
236
237 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, (uint8_t *)&ScsiInquiryReply, sizeof(SCSIINQUIRYDATA));
238 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
239 break;
240 }
241 case SCSI_READ_CAPACITY:
242 {
243 uint8_t aReply[8];
244 memset(aReply, 0, sizeof(aReply));
245
246 /*
247 * If sector size exceeds the maximum value that is
248 * able to be stored in 4 bytes return 0xffffffff in this field
249 */
250 if (pVScsiLunMmc->cSectors > UINT32_C(0xffffffff))
251 vscsiH2BEU32(aReply, UINT32_C(0xffffffff));
252 else
253 vscsiH2BEU32(aReply, pVScsiLunMmc->cSectors - 1);
254 vscsiH2BEU32(&aReply[4], pVScsiLunMmc->cbSector);
255 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply));
256 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
257 break;
258 }
259 case SCSI_MODE_SENSE_6:
260 {
261 uint8_t uModePage = pVScsiReq->pbCDB[2] & 0x3f;
262 uint8_t aReply[24];
263 uint8_t *pu8ReplyPos;
264 bool fValid = false;
265
266 memset(aReply, 0, sizeof(aReply));
267 aReply[0] = 4; /* Reply length 4. */
268 aReply[1] = 0; /* Default media type. */
269 aReply[2] = RT_BIT(4); /* Caching supported. */
270 aReply[3] = 0; /* Block descriptor length. */
271
272 pu8ReplyPos = aReply + 4;
273
274 if ((uModePage == 0x08) || (uModePage == 0x3f))
275 {
276 memset(pu8ReplyPos, 0, 20);
277 *pu8ReplyPos++ = 0x08; /* Page code. */
278 *pu8ReplyPos++ = 0x12; /* Size of the page. */
279 *pu8ReplyPos++ = 0x4; /* Write cache enabled. */
280 fValid = true;
281 } else if (uModePage == 0) {
282 fValid = true;
283 }
284
285 /* Querying unknown pages must fail. */
286 if (fValid) {
287 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply));
288 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
289 } else {
290 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
291 }
292 break;
293 }
294 case SCSI_MODE_SELECT_6:
295 {
296 /* @todo: implement!! */
297 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
298 break;
299 }
300 case SCSI_READ_6:
301 {
302 enmTxDir = VSCSIIOREQTXDIR_READ;
303 uLbaStart = ((uint64_t) pVScsiReq->pbCDB[3]
304 | (pVScsiReq->pbCDB[2] << 8)
305 | ((pVScsiReq->pbCDB[1] & 0x1f) << 16));
306 cSectorTransfer = pVScsiReq->pbCDB[4];
307 break;
308 }
309 case SCSI_READ_10:
310 {
311 enmTxDir = VSCSIIOREQTXDIR_READ;
312 uLbaStart = vscsiBE2HU32(&pVScsiReq->pbCDB[2]);
313 cSectorTransfer = vscsiBE2HU16(&pVScsiReq->pbCDB[7]);
314 break;
315 }
316 case SCSI_READ_12:
317 {
318 enmTxDir = VSCSIIOREQTXDIR_READ;
319 uLbaStart = vscsiBE2HU32(&pVScsiReq->pbCDB[2]);
320 cSectorTransfer = vscsiBE2HU32(&pVScsiReq->pbCDB[6]);
321 break;
322 }
323 case SCSI_READ_16:
324 {
325 enmTxDir = VSCSIIOREQTXDIR_READ;
326 uLbaStart = vscsiBE2HU64(&pVScsiReq->pbCDB[2]);
327 cSectorTransfer = vscsiBE2HU32(&pVScsiReq->pbCDB[10]);
328 break;
329 }
330 case SCSI_READ_BUFFER:
331 {
332 uint8_t uDataMode = pVScsiReq->pbCDB[1] & 0x1f;
333
334 switch (uDataMode)
335 {
336 case 0x00:
337 case 0x01:
338 case 0x02:
339 case 0x03:
340 case 0x0a:
341 break;
342 case 0x0b:
343 {
344 uint8_t aReply[4];
345
346 /* We do not implement an echo buffer. */
347 memset(aReply, 0, sizeof(aReply));
348
349 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply));
350 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
351 break;
352 }
353 case 0x1a:
354 case 0x1c:
355 break;
356 default:
357 AssertMsgFailed(("Invalid data mode\n"));
358 }
359 break;
360 }
361 case SCSI_VERIFY_10:
362 case SCSI_START_STOP_UNIT:
363 {
364 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
365 break;
366 }
367 case SCSI_LOG_SENSE:
368 {
369 uint8_t uPageCode = pVScsiReq->pbCDB[2] & 0x3f;
370 uint8_t uSubPageCode = pVScsiReq->pbCDB[3];
371
372 switch (uPageCode)
373 {
374 case 0x00:
375 {
376 if (uSubPageCode == 0)
377 {
378 uint8_t aReply[4];
379
380 aReply[0] = 0;
381 aReply[1] = 0;
382 aReply[2] = 0;
383 aReply[3] = 0;
384
385 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply));
386 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
387 break;
388 }
389 }
390 default:
391 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
392 }
393 break;
394 }
395 case SCSI_SERVICE_ACTION_IN_16:
396 {
397 switch (pVScsiReq->pbCDB[1] & 0x1f)
398 {
399 case SCSI_SVC_ACTION_IN_READ_CAPACITY_16:
400 {
401 uint8_t aReply[32];
402
403 memset(aReply, 0, sizeof(aReply));
404 vscsiH2BEU64(aReply, pVScsiLunMmc->cSectors - 1);
405 vscsiH2BEU32(&aReply[8], pVScsiLunMmc->cbSector);
406 /* Leave the rest 0 */
407 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply));
408 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
409 break;
410 }
411 default:
412 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00); /* Don't know if this is correct */
413 }
414 break;
415 }
416 case SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL:
417 {
418 pVScsiLunMmc->fLocked = pVScsiReq->pbCDB[4] & 1;
419 vscsiLunMediumSetLock(pVScsiLun, pVScsiLunMmc->fLocked);
420 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
421 break;
422 }
423 case SCSI_READ_TOC_PMA_ATIP:
424 {
425 uint8_t format;
426 uint16_t cbMax;
427 bool fMSF;
428
429 format = pVScsiReq->pbCDB[2] & 0x0f;
430 cbMax = vscsiBE2HU16(&pVScsiReq->pbCDB[7]);
431 fMSF = (pVScsiReq->pbCDB[1] >> 1) & 1;
432 switch (format)
433 {
434 case 0x00:
435 mmcReadTOCNormal(pVScsiLun, pVScsiReq, cbMax, fMSF);
436 break;
437 case 0x01:
438 mmcReadTOCMulti(pVScsiLun, pVScsiReq, cbMax, fMSF);
439 break;
440 default:
441 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
442 }
443 break;
444 }
445
446 default:
447 //AssertMsgFailed(("Command %#x [%s] not implemented\n", pVScsiReq->pbCDB[0], SCSICmdText(pVScsiReq->pbCDB[0])));
448 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ILLEGAL_OPCODE, 0x00);
449 }
450 }
451
452 if (enmTxDir != VSCSIIOREQTXDIR_INVALID)
453 {
454 LogFlow(("%s: uLbaStart=%llu cSectorTransfer=%u\n",
455 __FUNCTION__, uLbaStart, cSectorTransfer));
456
457 if (RT_UNLIKELY(uLbaStart + cSectorTransfer > pVScsiLunMmc->cSectors))
458 {
459 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_LOGICAL_BLOCK_OOR, 0x00);
460 vscsiDeviceReqComplete(pVScsiLun->pVScsiDevice, pVScsiReq, rcReq, false, VINF_SUCCESS);
461 }
462 else if (!cSectorTransfer)
463 {
464 /* A 0 transfer length is not an error. */
465 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
466 vscsiDeviceReqComplete(pVScsiLun->pVScsiDevice, pVScsiReq, rcReq, false, VINF_SUCCESS);
467 }
468 else
469 {
470 /* Enqueue new I/O request */
471 rc = vscsiIoReqTransferEnqueue(pVScsiLun, pVScsiReq, enmTxDir,
472 uLbaStart * pVScsiLunMmc->cbSector,
473 cSectorTransfer * pVScsiLunMmc->cbSector);
474 }
475 }
476 else /* Request completed */
477 vscsiDeviceReqComplete(pVScsiLun->pVScsiDevice, pVScsiReq, rcReq, false, VINF_SUCCESS);
478
479 return rc;
480}
481
482VSCSILUNDESC g_VScsiLunTypeMmc =
483{
484 /** enmLunType */
485 VSCSILUNTYPE_MMC,
486 /** pcszDescName */
487 "MMC",
488 /** cbLun */
489 sizeof(VSCSILUNMMC),
490 /** pfnVScsiLunInit */
491 vscsiLunMmcInit,
492 /** pfnVScsiLunDestroy */
493 vscsiLunMmcDestroy,
494 /** pfnVScsiLunReqProcess */
495 vscsiLunMmcReqProcess
496};
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