VirtualBox

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

Last change on this file since 38844 was 38844, checked in by vboxsync, 13 years ago

Very rudimentary SCSI MMC implementation.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 11.1 KB
Line 
1/* $Id: VSCSILunMmc.cpp 38844 2011-09-23 12:25:21Z 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} VSCSILUNMMC, *PVSCSILUNMMC;
44
45static int vscsiLunMmcInit(PVSCSILUNINT pVScsiLun)
46{
47 PVSCSILUNMMC pVScsiLunMmc = (PVSCSILUNMMC)pVScsiLun;
48 uint64_t cbDisk = 0;
49 int rc = VINF_SUCCESS;
50
51 pVScsiLunMmc->cbSector = 2048; /* Default to 2K sectors. */
52 rc = vscsiLunMediumGetSize(pVScsiLun, &cbDisk);
53 if (RT_SUCCESS(rc))
54 pVScsiLunMmc->cSectors = cbDisk / pVScsiLunMmc->cbSector;
55
56 return rc;
57}
58
59static int vscsiLunMmcDestroy(PVSCSILUNINT pVScsiLun)
60{
61 PVSCSILUNMMC pVScsiLunMmc = (PVSCSILUNMMC)pVScsiLun;
62
63 return VINF_SUCCESS;
64}
65
66static int vscsiLunMmcReqProcess(PVSCSILUNINT pVScsiLun, PVSCSIREQINT pVScsiReq)
67{
68 PVSCSILUNMMC pVScsiLunMmc = (PVSCSILUNMMC)pVScsiLun;
69 VSCSIIOREQTXDIR enmTxDir = VSCSIIOREQTXDIR_INVALID;
70 uint64_t uLbaStart = 0;
71 uint32_t cSectorTransfer = 0;
72 int rc = VINF_SUCCESS;
73 int rcReq = SCSI_STATUS_OK;
74
75 switch(pVScsiReq->pbCDB[0])
76 {
77 case SCSI_INQUIRY:
78 {
79 SCSIINQUIRYDATA ScsiInquiryReply;
80
81 memset(&ScsiInquiryReply, 0, sizeof(ScsiInquiryReply));
82
83 ScsiInquiryReply.cbAdditional = 31;
84 ScsiInquiryReply.fRMB = 1; /* Removable. */
85 ScsiInquiryReply.u5PeripheralDeviceType = SCSI_INQUIRY_DATA_PERIPHERAL_DEVICE_TYPE_CD_DVD;
86 ScsiInquiryReply.u3PeripheralQualifier = SCSI_INQUIRY_DATA_PERIPHERAL_QUALIFIER_CONNECTED;
87 ScsiInquiryReply.u3AnsiVersion = 0x05; /* MMC-?? compliant */
88 ScsiInquiryReply.fCmdQue = 1; /* Command queuing supported. */
89 ScsiInquiryReply.fWBus16 = 1;
90 vscsiPadStr(ScsiInquiryReply.achVendorId, "VBOX", 8);
91 vscsiPadStr(ScsiInquiryReply.achProductId, "CD-ROM", 16);
92 vscsiPadStr(ScsiInquiryReply.achProductLevel, "1.0", 4);
93
94 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, (uint8_t *)&ScsiInquiryReply, sizeof(SCSIINQUIRYDATA));
95 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
96 break;
97 }
98 case SCSI_READ_CAPACITY:
99 {
100 uint8_t aReply[8];
101 memset(aReply, 0, sizeof(aReply));
102
103 /*
104 * If sector size exceeds the maximum value that is
105 * able to be stored in 4 bytes return 0xffffffff in this field
106 */
107 if (pVScsiLunMmc->cSectors > UINT32_C(0xffffffff))
108 vscsiH2BEU32(aReply, UINT32_C(0xffffffff));
109 else
110 vscsiH2BEU32(aReply, pVScsiLunMmc->cSectors - 1);
111 vscsiH2BEU32(&aReply[4], pVScsiLunMmc->cbSector);
112 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply));
113 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
114 break;
115 }
116 case SCSI_MODE_SENSE_6:
117 {
118 uint8_t uModePage = pVScsiReq->pbCDB[2] & 0x3f;
119 uint8_t aReply[24];
120 uint8_t *pu8ReplyPos;
121
122 memset(aReply, 0, sizeof(aReply));
123 aReply[0] = 4; /* Reply length 4. */
124 aReply[1] = 0; /* Default media type. */
125 aReply[2] = RT_BIT(4); /* Caching supported. */
126 aReply[3] = 0; /* Block descriptor length. */
127
128 pu8ReplyPos = aReply + 4;
129
130 if ((uModePage == 0x08) || (uModePage == 0x3f))
131 {
132 memset(pu8ReplyPos, 0, 20);
133 *pu8ReplyPos++ = 0x08; /* Page code. */
134 *pu8ReplyPos++ = 0x12; /* Size of the page. */
135 *pu8ReplyPos++ = 0x4; /* Write cache enabled. */
136 }
137
138 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply));
139 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
140 break;
141 }
142 case SCSI_MODE_SELECT_6:
143 {
144 /* @todo: implement!! */
145 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
146 break;
147 }
148 case SCSI_READ_6:
149 {
150 enmTxDir = VSCSIIOREQTXDIR_READ;
151 uLbaStart = ((uint64_t) pVScsiReq->pbCDB[3]
152 | (pVScsiReq->pbCDB[2] << 8)
153 | ((pVScsiReq->pbCDB[1] & 0x1f) << 16));
154 cSectorTransfer = pVScsiReq->pbCDB[4];
155 break;
156 }
157 case SCSI_READ_10:
158 {
159 enmTxDir = VSCSIIOREQTXDIR_READ;
160 uLbaStart = vscsiBE2HU32(&pVScsiReq->pbCDB[2]);
161 cSectorTransfer = vscsiBE2HU16(&pVScsiReq->pbCDB[7]);
162 break;
163 }
164 case SCSI_READ_12:
165 {
166 enmTxDir = VSCSIIOREQTXDIR_READ;
167 uLbaStart = vscsiBE2HU32(&pVScsiReq->pbCDB[2]);
168 cSectorTransfer = vscsiBE2HU32(&pVScsiReq->pbCDB[6]);
169 break;
170 }
171 case SCSI_READ_16:
172 {
173 enmTxDir = VSCSIIOREQTXDIR_READ;
174 uLbaStart = vscsiBE2HU64(&pVScsiReq->pbCDB[2]);
175 cSectorTransfer = vscsiBE2HU32(&pVScsiReq->pbCDB[10]);
176 break;
177 }
178 case SCSI_READ_BUFFER:
179 {
180 uint8_t uDataMode = pVScsiReq->pbCDB[1] & 0x1f;
181
182 switch (uDataMode)
183 {
184 case 0x00:
185 case 0x01:
186 case 0x02:
187 case 0x03:
188 case 0x0a:
189 break;
190 case 0x0b:
191 {
192 uint8_t aReply[4];
193
194 /* We do not implement an echo buffer. */
195 memset(aReply, 0, sizeof(aReply));
196
197 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply));
198 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
199 break;
200 }
201 case 0x1a:
202 case 0x1c:
203 break;
204 default:
205 AssertMsgFailed(("Invalid data mode\n"));
206 }
207 break;
208 }
209 case SCSI_VERIFY_10:
210 case SCSI_START_STOP_UNIT:
211 {
212 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
213 break;
214 }
215 case SCSI_LOG_SENSE:
216 {
217 uint16_t cbMax = vscsiBE2HU16(&pVScsiReq->pbCDB[7]);
218 uint8_t uPageCode = pVScsiReq->pbCDB[2] & 0x3f;
219 uint8_t uSubPageCode = pVScsiReq->pbCDB[3];
220
221 switch (uPageCode)
222 {
223 case 0x00:
224 {
225 if (uSubPageCode == 0)
226 {
227 uint8_t aReply[4];
228
229 aReply[0] = 0;
230 aReply[1] = 0;
231 aReply[2] = 0;
232 aReply[3] = 0;
233
234 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply));
235 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
236 break;
237 }
238 }
239 default:
240 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
241 }
242 break;
243 }
244 case SCSI_SERVICE_ACTION_IN_16:
245 {
246 switch (pVScsiReq->pbCDB[1] & 0x1f)
247 {
248 case SCSI_SVC_ACTION_IN_READ_CAPACITY_16:
249 {
250 uint8_t aReply[32];
251
252 memset(aReply, 0, sizeof(aReply));
253 vscsiH2BEU64(aReply, pVScsiLunMmc->cSectors - 1);
254 vscsiH2BEU32(&aReply[8], pVScsiLunMmc->cbSector);
255 /* Leave the rest 0 */
256 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply));
257 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
258 break;
259 }
260 default:
261 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00); /* Don't know if this is correct */
262 }
263 break;
264 }
265 default:
266 //AssertMsgFailed(("Command %#x [%s] not implemented\n", pRequest->pbCDB[0], SCSICmdText(pRequest->pbCDB[0])));
267 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ILLEGAL_OPCODE, 0x00);
268 }
269
270 if (enmTxDir != VSCSIIOREQTXDIR_INVALID)
271 {
272 LogFlow(("%s: uLbaStart=%llu cSectorTransfer=%u\n",
273 __FUNCTION__, uLbaStart, cSectorTransfer));
274
275 if (RT_UNLIKELY(uLbaStart + cSectorTransfer > pVScsiLunMmc->cSectors))
276 {
277 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_LOGICAL_BLOCK_OOR, 0x00);
278 vscsiDeviceReqComplete(pVScsiLun->pVScsiDevice, pVScsiReq, rcReq, false, VINF_SUCCESS);
279 }
280 else if (!cSectorTransfer)
281 {
282 /* A 0 transfer length is not an error. */
283 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
284 vscsiDeviceReqComplete(pVScsiLun->pVScsiDevice, pVScsiReq, rcReq, false, VINF_SUCCESS);
285 }
286 else
287 {
288 /* Enqueue new I/O request */
289 rc = vscsiIoReqTransferEnqueue(pVScsiLun, pVScsiReq, enmTxDir,
290 uLbaStart * pVScsiLunMmc->cbSector,
291 cSectorTransfer * pVScsiLunMmc->cbSector);
292 }
293 }
294 else /* Request completed */
295 vscsiDeviceReqComplete(pVScsiLun->pVScsiDevice, pVScsiReq, rcReq, false, VINF_SUCCESS);
296
297 return rc;
298}
299
300VSCSILUNDESC g_VScsiLunTypeMmc =
301{
302 /** enmLunType */
303 VSCSILUNTYPE_MMC,
304 /** pcszDescName */
305 "MMC",
306 /** cbLun */
307 sizeof(VSCSILUNMMC),
308 /** pfnVScsiLunInit */
309 vscsiLunMmcInit,
310 /** pfnVScsiLunDestroy */
311 vscsiLunMmcDestroy,
312 /** pfnVScsiLunReqProcess */
313 vscsiLunMmcReqProcess
314};
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