VirtualBox

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

Last change on this file since 43659 was 43640, checked in by vboxsync, 12 years ago

VSCSI: Added basic media change support.

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