VirtualBox

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

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

DECLCALLBACK

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.2 KB
Line 
1/* $Id: VSCSILunMmc.cpp 57391 2015-08-17 14:26:29Z vboxsync $ */
2/** @file
3 * Virtual SCSI driver: MMC LUN implementation (CD/DVD-ROM)
4 */
5
6/*
7 * Copyright (C) 2006-2015 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 PVSCSILUNMMC pVScsiLunMmc = (PVSCSILUNMMC)pVScsiLun;
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 PVSCSILUNMMC pVScsiLunMmc = (PVSCSILUNMMC)pVScsiLun;
175
176 return VINF_SUCCESS;
177}
178
179static DECLCALLBACK(int) vscsiLunMmcReqProcess(PVSCSILUNINT pVScsiLun, PVSCSIREQINT pVScsiReq)
180{
181 PVSCSILUNMMC pVScsiLunMmc = (PVSCSILUNMMC)pVScsiLun;
182 VSCSIIOREQTXDIR enmTxDir = VSCSIIOREQTXDIR_INVALID;
183 uint64_t uLbaStart = 0;
184 uint32_t cSectorTransfer = 0;
185 int rc = VINF_SUCCESS;
186 int rcReq = SCSI_STATUS_OK;
187 unsigned uCmd = pVScsiReq->pbCDB[0];
188
189 /*
190 * GET CONFIGURATION, GET EVENT/STATUS NOTIFICATION, INQUIRY, and REQUEST SENSE commands
191 * operate even when a unit attention condition exists for initiator; every other command
192 * needs to report CHECK CONDITION in that case.
193 */
194 if (!pVScsiLunMmc->Core.fReady && uCmd != SCSI_INQUIRY)
195 {
196 /*
197 * A note on media changes: As long as a medium is not present, the unit remains in
198 * the 'not ready' state. Technically the unit becomes 'ready' soon after a medium
199 * is inserted; however, we internally keep the 'not ready' state until we've had
200 * a chance to report the UNIT ATTENTION status indicating a media change.
201 */
202 if (pVScsiLunMmc->Core.fMediaPresent)
203 {
204 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_UNIT_ATTENTION,
205 SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED, 0x00);
206 pVScsiLunMmc->Core.fReady = true;
207 }
208 else
209 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_NOT_READY,
210 SCSI_ASC_MEDIUM_NOT_PRESENT, 0x00);
211 }
212 else
213 {
214 switch (uCmd)
215 {
216 case SCSI_TEST_UNIT_READY:
217 Assert(!pVScsiLunMmc->Core.fReady); /* Only should get here if LUN isn't ready. */
218 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT, 0x00);
219 break;
220
221 case SCSI_INQUIRY:
222 {
223 SCSIINQUIRYDATA ScsiInquiryReply;
224
225 memset(&ScsiInquiryReply, 0, sizeof(ScsiInquiryReply));
226
227 ScsiInquiryReply.cbAdditional = 31;
228 ScsiInquiryReply.fRMB = 1; /* Removable. */
229 ScsiInquiryReply.u5PeripheralDeviceType = SCSI_INQUIRY_DATA_PERIPHERAL_DEVICE_TYPE_CD_DVD;
230 ScsiInquiryReply.u3PeripheralQualifier = SCSI_INQUIRY_DATA_PERIPHERAL_QUALIFIER_CONNECTED;
231 ScsiInquiryReply.u3AnsiVersion = 0x05; /* MMC-?? compliant */
232 ScsiInquiryReply.fCmdQue = 1; /* Command queuing supported. */
233 ScsiInquiryReply.fWBus16 = 1;
234 vscsiPadStr(ScsiInquiryReply.achVendorId, "VBOX", 8);
235 vscsiPadStr(ScsiInquiryReply.achProductId, "CD-ROM", 16);
236 vscsiPadStr(ScsiInquiryReply.achProductLevel, "1.0", 4);
237
238 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, (uint8_t *)&ScsiInquiryReply, sizeof(SCSIINQUIRYDATA));
239 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
240 break;
241 }
242 case SCSI_READ_CAPACITY:
243 {
244 uint8_t aReply[8];
245 memset(aReply, 0, sizeof(aReply));
246
247 /*
248 * If sector size exceeds the maximum value that is
249 * able to be stored in 4 bytes return 0xffffffff in this field
250 */
251 if (pVScsiLunMmc->cSectors > UINT32_C(0xffffffff))
252 vscsiH2BEU32(aReply, UINT32_C(0xffffffff));
253 else
254 vscsiH2BEU32(aReply, pVScsiLunMmc->cSectors - 1);
255 vscsiH2BEU32(&aReply[4], pVScsiLunMmc->cbSector);
256 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply));
257 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
258 break;
259 }
260 case SCSI_MODE_SENSE_6:
261 {
262 uint8_t uModePage = pVScsiReq->pbCDB[2] & 0x3f;
263 uint8_t aReply[24];
264 uint8_t *pu8ReplyPos;
265 bool fValid = false;
266
267 memset(aReply, 0, sizeof(aReply));
268 aReply[0] = 4; /* Reply length 4. */
269 aReply[1] = 0; /* Default media type. */
270 aReply[2] = RT_BIT(4); /* Caching supported. */
271 aReply[3] = 0; /* Block descriptor length. */
272
273 pu8ReplyPos = aReply + 4;
274
275 if ((uModePage == 0x08) || (uModePage == 0x3f))
276 {
277 memset(pu8ReplyPos, 0, 20);
278 *pu8ReplyPos++ = 0x08; /* Page code. */
279 *pu8ReplyPos++ = 0x12; /* Size of the page. */
280 *pu8ReplyPos++ = 0x4; /* Write cache enabled. */
281 fValid = true;
282 } else if (uModePage == 0) {
283 fValid = true;
284 }
285
286 /* Querying unknown pages must fail. */
287 if (fValid) {
288 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply));
289 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
290 } else {
291 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
292 }
293 break;
294 }
295 case SCSI_MODE_SELECT_6:
296 {
297 /* @todo: implement!! */
298 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
299 break;
300 }
301 case SCSI_READ_6:
302 {
303 enmTxDir = VSCSIIOREQTXDIR_READ;
304 uLbaStart = ((uint64_t) pVScsiReq->pbCDB[3]
305 | (pVScsiReq->pbCDB[2] << 8)
306 | ((pVScsiReq->pbCDB[1] & 0x1f) << 16));
307 cSectorTransfer = pVScsiReq->pbCDB[4];
308 break;
309 }
310 case SCSI_READ_10:
311 {
312 enmTxDir = VSCSIIOREQTXDIR_READ;
313 uLbaStart = vscsiBE2HU32(&pVScsiReq->pbCDB[2]);
314 cSectorTransfer = vscsiBE2HU16(&pVScsiReq->pbCDB[7]);
315 break;
316 }
317 case SCSI_READ_12:
318 {
319 enmTxDir = VSCSIIOREQTXDIR_READ;
320 uLbaStart = vscsiBE2HU32(&pVScsiReq->pbCDB[2]);
321 cSectorTransfer = vscsiBE2HU32(&pVScsiReq->pbCDB[6]);
322 break;
323 }
324 case SCSI_READ_16:
325 {
326 enmTxDir = VSCSIIOREQTXDIR_READ;
327 uLbaStart = vscsiBE2HU64(&pVScsiReq->pbCDB[2]);
328 cSectorTransfer = vscsiBE2HU32(&pVScsiReq->pbCDB[10]);
329 break;
330 }
331 case SCSI_READ_BUFFER:
332 {
333 uint8_t uDataMode = pVScsiReq->pbCDB[1] & 0x1f;
334
335 switch (uDataMode)
336 {
337 case 0x00:
338 case 0x01:
339 case 0x02:
340 case 0x03:
341 case 0x0a:
342 break;
343 case 0x0b:
344 {
345 uint8_t aReply[4];
346
347 /* We do not implement an echo buffer. */
348 memset(aReply, 0, sizeof(aReply));
349
350 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply));
351 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
352 break;
353 }
354 case 0x1a:
355 case 0x1c:
356 break;
357 default:
358 AssertMsgFailed(("Invalid data mode\n"));
359 }
360 break;
361 }
362 case SCSI_VERIFY_10:
363 case SCSI_START_STOP_UNIT:
364 {
365 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
366 break;
367 }
368 case SCSI_LOG_SENSE:
369 {
370 uint16_t cbMax = vscsiBE2HU16(&pVScsiReq->pbCDB[7]);
371 uint8_t uPageCode = pVScsiReq->pbCDB[2] & 0x3f;
372 uint8_t uSubPageCode = pVScsiReq->pbCDB[3];
373
374 switch (uPageCode)
375 {
376 case 0x00:
377 {
378 if (uSubPageCode == 0)
379 {
380 uint8_t aReply[4];
381
382 aReply[0] = 0;
383 aReply[1] = 0;
384 aReply[2] = 0;
385 aReply[3] = 0;
386
387 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply));
388 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
389 break;
390 }
391 }
392 default:
393 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
394 }
395 break;
396 }
397 case SCSI_SERVICE_ACTION_IN_16:
398 {
399 switch (pVScsiReq->pbCDB[1] & 0x1f)
400 {
401 case SCSI_SVC_ACTION_IN_READ_CAPACITY_16:
402 {
403 uint8_t aReply[32];
404
405 memset(aReply, 0, sizeof(aReply));
406 vscsiH2BEU64(aReply, pVScsiLunMmc->cSectors - 1);
407 vscsiH2BEU32(&aReply[8], pVScsiLunMmc->cbSector);
408 /* Leave the rest 0 */
409 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply));
410 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
411 break;
412 }
413 default:
414 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00); /* Don't know if this is correct */
415 }
416 break;
417 }
418 case SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL:
419 {
420 pVScsiLunMmc->fLocked = pVScsiReq->pbCDB[4] & 1;
421 vscsiLunMediumSetLock(pVScsiLun, pVScsiLunMmc->fLocked);
422 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
423 break;
424 }
425 case SCSI_READ_TOC_PMA_ATIP:
426 {
427 uint8_t format;
428 uint16_t cbMax;
429 bool fMSF;
430
431 format = pVScsiReq->pbCDB[2] & 0x0f;
432 cbMax = vscsiBE2HU16(&pVScsiReq->pbCDB[7]);
433 fMSF = (pVScsiReq->pbCDB[1] >> 1) & 1;
434 switch (format)
435 {
436 case 0x00:
437 mmcReadTOCNormal(pVScsiLun, pVScsiReq, cbMax, fMSF);
438 break;
439 case 0x01:
440 mmcReadTOCMulti(pVScsiLun, pVScsiReq, cbMax, fMSF);
441 break;
442 default:
443 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
444 }
445 break;
446 }
447
448 default:
449 //AssertMsgFailed(("Command %#x [%s] not implemented\n", pVScsiReq->pbCDB[0], SCSICmdText(pVScsiReq->pbCDB[0])));
450 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ILLEGAL_OPCODE, 0x00);
451 }
452 }
453
454 if (enmTxDir != VSCSIIOREQTXDIR_INVALID)
455 {
456 LogFlow(("%s: uLbaStart=%llu cSectorTransfer=%u\n",
457 __FUNCTION__, uLbaStart, cSectorTransfer));
458
459 if (RT_UNLIKELY(uLbaStart + cSectorTransfer > pVScsiLunMmc->cSectors))
460 {
461 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_LOGICAL_BLOCK_OOR, 0x00);
462 vscsiDeviceReqComplete(pVScsiLun->pVScsiDevice, pVScsiReq, rcReq, false, VINF_SUCCESS);
463 }
464 else if (!cSectorTransfer)
465 {
466 /* A 0 transfer length is not an error. */
467 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
468 vscsiDeviceReqComplete(pVScsiLun->pVScsiDevice, pVScsiReq, rcReq, false, VINF_SUCCESS);
469 }
470 else
471 {
472 /* Enqueue new I/O request */
473 rc = vscsiIoReqTransferEnqueue(pVScsiLun, pVScsiReq, enmTxDir,
474 uLbaStart * pVScsiLunMmc->cbSector,
475 cSectorTransfer * pVScsiLunMmc->cbSector);
476 }
477 }
478 else /* Request completed */
479 vscsiDeviceReqComplete(pVScsiLun->pVScsiDevice, pVScsiReq, rcReq, false, VINF_SUCCESS);
480
481 return rc;
482}
483
484VSCSILUNDESC g_VScsiLunTypeMmc =
485{
486 /** enmLunType */
487 VSCSILUNTYPE_MMC,
488 /** pcszDescName */
489 "MMC",
490 /** cbLun */
491 sizeof(VSCSILUNMMC),
492 /** pfnVScsiLunInit */
493 vscsiLunMmcInit,
494 /** pfnVScsiLunDestroy */
495 vscsiLunMmcDestroy,
496 /** pfnVScsiLunReqProcess */
497 vscsiLunMmcReqProcess
498};
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