VirtualBox

source: vbox/trunk/src/VBox/Devices/Storage/VSCSI/VSCSILunSsc.cpp@ 98651

Last change on this file since 98651 was 98103, checked in by vboxsync, 22 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.1 KB
Line 
1/* $Id: VSCSILunSsc.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * Virtual SCSI driver: SSC LUN implementation (Streaming tape)
4 */
5
6/*
7 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#define LOG_GROUP LOG_GROUP_VSCSI
33#include <VBox/log.h>
34#include <iprt/errcore.h>
35#include <VBox/types.h>
36#include <VBox/vscsi.h>
37#include <iprt/assert.h>
38#include <iprt/mem.h>
39#include <iprt/string.h>
40
41#include "VSCSIInternal.h"
42
43/**
44 * SSC LUN instance
45 */
46typedef struct VSCSILUNSSC
47{
48 /** Core LUN structure */
49 VSCSILUNINT Core;
50 /** Size of the virtual tape. */
51 uint64_t cbTape;
52 /** Current position. */
53 uint64_t uCurPos;
54 /** Number of blocks. */
55 uint64_t cBlocks;
56 /** Block size. */
57 uint64_t cbBlock;
58 /** Medium locked indicator. */
59 bool fLocked;
60} VSCSILUNSSC, *PVSCSILUNSSC;
61
62
63static int vscsiLUNSSCInit(PVSCSILUNINT pVScsiLun)
64{
65 PVSCSILUNSSC pVScsiLunSsc = (PVSCSILUNSSC)pVScsiLun;
66 int rc = VINF_SUCCESS;
67
68 pVScsiLunSsc->cbBlock = 512; /* Default to 512-byte blocks. */
69 pVScsiLunSsc->uCurPos = 0; /* Start at beginning of tape. */
70 pVScsiLunSsc->cbTape = 0;
71
72 uint32_t cRegions = vscsiLunMediumGetRegionCount(pVScsiLun);
73 if (cRegions != 1)
74 rc = VERR_INVALID_PARAMETER;
75
76 if (RT_SUCCESS(rc))
77 rc = vscsiLunMediumQueryRegionProperties(pVScsiLun, 0, NULL, &pVScsiLunSsc->cBlocks,
78 &pVScsiLunSsc->cbBlock, NULL);
79
80 if (RT_SUCCESS(rc))
81 pVScsiLunSsc->cbTape = pVScsiLunSsc->cBlocks * pVScsiLunSsc->cbBlock;
82
83 return rc;
84}
85
86static int vscsiLUNSSCDestroy(PVSCSILUNINT pVScsiLun)
87{
88 PVSCSILUNSSC pVScsiLUNSSC = (PVSCSILUNSSC)pVScsiLun;
89
90 pVScsiLUNSSC->uCurPos = 0; // shut compiler up
91
92 return VINF_SUCCESS;
93}
94
95static int vscsiLUNSSCReqProcess(PVSCSILUNINT pVScsiLun, PVSCSIREQINT pVScsiReq)
96{
97 PVSCSILUNSSC pVScsiLUNSSC = (PVSCSILUNSSC)pVScsiLun;
98 VSCSIIOREQTXDIR enmTxDir = VSCSIIOREQTXDIR_INVALID;
99 uint64_t uTransferStart = 0;
100 uint32_t cBlocksTransfer = 0;
101 uint32_t cbTransfer = 0;
102 int rc = VINF_SUCCESS;
103 int rcReq = SCSI_STATUS_OK;
104 unsigned uCmd = pVScsiReq->pbCDB[0];
105
106 /*
107 * GET CONFIGURATION, GET EVENT/STATUS NOTIFICATION, INQUIRY, and REQUEST SENSE commands
108 * operate even when a unit attention condition exists for initiator; every other command
109 * needs to report CHECK CONDITION in that case.
110 */
111 if (!pVScsiLUNSSC->Core.fReady && uCmd != SCSI_INQUIRY)
112 {
113 /*
114 * A note on media changes: As long as a medium is not present, the unit remains in
115 * the 'not ready' state. Technically the unit becomes 'ready' soon after a medium
116 * is inserted; however, we internally keep the 'not ready' state until we've had
117 * a chance to report the UNIT ATTENTION status indicating a media change.
118 */
119 if (pVScsiLUNSSC->Core.fMediaPresent)
120 {
121 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_UNIT_ATTENTION,
122 SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED, 0x00);
123 pVScsiLUNSSC->Core.fReady = true;
124 }
125 else
126 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_NOT_READY,
127 SCSI_ASC_MEDIUM_NOT_PRESENT, 0x00);
128 }
129 else
130 {
131 switch (uCmd)
132 {
133 case SCSI_TEST_UNIT_READY:
134 Assert(!pVScsiLUNSSC->Core.fReady); /* Only should get here if LUN isn't ready. */
135 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT, 0x00);
136 break;
137
138 case SCSI_INQUIRY:
139 {
140 SCSIINQUIRYDATA ScsiInquiryReply;
141
142 memset(&ScsiInquiryReply, 0, sizeof(ScsiInquiryReply));
143
144 ScsiInquiryReply.cbAdditional = 31;
145 ScsiInquiryReply.fRMB = 1; /* Removable. */
146 ScsiInquiryReply.u5PeripheralDeviceType = SCSI_INQUIRY_DATA_PERIPHERAL_DEVICE_TYPE_SEQUENTIAL_ACCESS;
147 ScsiInquiryReply.u3PeripheralQualifier = SCSI_INQUIRY_DATA_PERIPHERAL_QUALIFIER_CONNECTED;
148 ScsiInquiryReply.u3AnsiVersion = 0x05; /* SSC-?? compliant */
149 ScsiInquiryReply.fCmdQue = 1; /* Command queuing supported. */
150 ScsiInquiryReply.fWBus16 = 1;
151 scsiPadStrS(ScsiInquiryReply.achVendorId, "VBOX", 8);
152 scsiPadStrS(ScsiInquiryReply.achProductId, "TAPE DRIVE", 16);
153 scsiPadStrS(ScsiInquiryReply.achProductLevel, "1.0", 4);
154
155 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, (uint8_t *)&ScsiInquiryReply, sizeof(SCSIINQUIRYDATA));
156 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
157 break;
158 }
159 case SCSI_MODE_SENSE_6:
160 {
161// uint8_t uModePage = pVScsiReq->pbCDB[2] & 0x3f;
162 uint8_t aReply[24];
163 uint8_t *pu8ReplyPos;
164 uint8_t uReplyLen;
165 bool fWantBlkDesc = !!(pVScsiReq->pbCDB[1] & RT_BIT(3)); /* DBD bit. */
166
167 memset(aReply, 0, sizeof(aReply));
168 if (fWantBlkDesc)
169 uReplyLen = 4 + 8;
170 else
171 uReplyLen = 4;
172
173 aReply[0] = uReplyLen - 1; /* Reply length. */
174 aReply[1] = 0xB6; /* Travan TR-4 medium (whatever). */
175 aReply[2] = 0; //RT_BIT(7); /* Write Protected. */ //@todo!
176 aReply[3] = uReplyLen - 4; /* Block descriptor length. */
177
178 pu8ReplyPos = aReply + 4;
179
180#if 0
181 if ((uModePage == 0x08) || (uModePage == 0x3f))
182 {
183 memset(pu8ReplyPos, 0, 20);
184 *pu8ReplyPos++ = 0x08; /* Page code. */
185 *pu8ReplyPos++ = 0x12; /* Size of the page. */
186 *pu8ReplyPos++ = 0x4; /* Write cache enabled. */
187 }
188#endif
189
190 /* Fill out the Block Descriptor. */
191 if (fWantBlkDesc)
192 {
193 *pu8ReplyPos++ = 0x45; /* Travan TR-4 density. */
194 *pu8ReplyPos++ = 0; /* All blocks are the same. */
195 *pu8ReplyPos++ = 0; /// @todo this calls for some macros!
196 *pu8ReplyPos++ = 0;
197 *pu8ReplyPos++ = 0; /* Reserved. */
198 *pu8ReplyPos++ = 0x00; /* Block length (512). */
199 *pu8ReplyPos++ = 0x02;
200 *pu8ReplyPos++ = 0x00;
201 }
202
203 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply));
204 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
205 break;
206 }
207 case SCSI_MODE_SELECT_6:
208 {
209 /** @todo implement!! */
210 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
211 break;
212 }
213 case SCSI_READ_6:
214 {
215 enmTxDir = VSCSIIOREQTXDIR_READ;
216 cbTransfer = ((uint32_t) pVScsiReq->pbCDB[4]
217 | (pVScsiReq->pbCDB[3] << 8)
218 | (pVScsiReq->pbCDB[2] << 16));
219 cBlocksTransfer = pVScsiReq->pbCDB[4];
220 uTransferStart = pVScsiLUNSSC->uCurPos;
221 pVScsiLUNSSC->uCurPos += cbTransfer;
222 break;
223 }
224 case SCSI_WRITE_6:
225 {
226 enmTxDir = VSCSIIOREQTXDIR_WRITE;
227 cbTransfer = ((uint32_t) pVScsiReq->pbCDB[4]
228 | (pVScsiReq->pbCDB[3] << 8)
229 | (pVScsiReq->pbCDB[2] << 16));
230 cBlocksTransfer = pVScsiReq->pbCDB[4];
231 uTransferStart = pVScsiLUNSSC->uCurPos;
232 pVScsiLUNSSC->uCurPos += cbTransfer;
233 break;
234 }
235 case SCSI_READ_BUFFER:
236 {
237 uint8_t uDataMode = pVScsiReq->pbCDB[1] & 0x1f;
238
239 switch (uDataMode)
240 {
241 case 0x00:
242 case 0x01:
243 case 0x02:
244 case 0x03:
245 case 0x0a:
246 break;
247 case 0x0b:
248 {
249 uint8_t aReply[4];
250
251 /* We do not implement an echo buffer. */
252 memset(aReply, 0, sizeof(aReply));
253
254 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply));
255 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
256 break;
257 }
258 case 0x1a:
259 case 0x1c:
260 break;
261 default:
262 AssertMsgFailed(("Invalid data mode\n"));
263 }
264 break;
265 }
266 case SCSI_VERIFY_10:
267 case SCSI_LOAD_UNLOAD:
268 {
269 /// @todo should load/unload do anyhting? is verify even supported?
270 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
271 break;
272 }
273 case SCSI_LOG_SENSE:
274 {
275 uint8_t uPageCode = pVScsiReq->pbCDB[2] & 0x3f;
276 uint8_t uSubPageCode = pVScsiReq->pbCDB[3];
277
278 switch (uPageCode)
279 {
280 case 0x00:
281 {
282 if (uSubPageCode == 0)
283 {
284 uint8_t aReply[4];
285
286 aReply[0] = 0;
287 aReply[1] = 0;
288 aReply[2] = 0;
289 aReply[3] = 0;
290
291 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply));
292 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
293 break;
294 }
295 }
296 default:
297 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
298 }
299 break;
300 }
301 case SCSI_SERVICE_ACTION_IN_16:
302 {
303 switch (pVScsiReq->pbCDB[1] & 0x1f)
304 {
305 default:
306 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00); /* Don't know if this is correct */
307 }
308 break;
309 }
310 case SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL:
311 {
312 pVScsiLUNSSC->fLocked = pVScsiReq->pbCDB[4] & 1;
313 vscsiLunMediumSetLock(pVScsiLun, pVScsiLUNSSC->fLocked);
314 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
315 break;
316 }
317 case SCSI_REWIND:
318 /// @todo flush data + write EOD? immed bit? partitions?
319 pVScsiLUNSSC->uCurPos = 0;
320 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
321 break;
322 case SCSI_RESERVE_6:
323 /// @todo perform actual reservation
324 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
325 break;
326 case SCSI_RELEASE_6:
327 /// @todo perform actual release
328 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
329 break;
330 case SCSI_READ_BLOCK_LIMITS:
331 {
332 uint8_t aReply[6];
333
334 /* Report unrestricted block sizes (1-FFFFFFh). */
335 memset(aReply, 0, sizeof(aReply));
336 /// @todo Helpers for big-endian 16-bit/24-bit/32-bit constants?
337 aReply[1] = aReply[2] = aReply[3] = 0xff;
338 aReply[5] = 0x01;
339 RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply));
340 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
341 break;
342 }
343 default:
344 //AssertMsgFailed(("Command %#x [%s] not implemented\n", pVScsiReq->pbCDB[0], SCSICmdText(pVScsiReq->pbCDB[0])));
345 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ILLEGAL_OPCODE, 0x00);
346 }
347 }
348
349 if (enmTxDir != VSCSIIOREQTXDIR_INVALID)
350 {
351 LogFlow(("%s: uTransferStart=%llu cbTransfer=%u\n",
352 __FUNCTION__, uTransferStart, cbTransfer));
353
354 if (RT_UNLIKELY(uTransferStart + cbTransfer > pVScsiLUNSSC->cbTape))
355 {
356 uint64_t cbResidue = uTransferStart + cbTransfer - pVScsiLUNSSC->cbTape;
357
358 if (enmTxDir == VSCSIIOREQTXDIR_READ && cbResidue < cbTransfer)
359 {
360 /* If it's a read and some data is still available, read what we can. */
361 rc = vscsiIoReqTransferEnqueue(pVScsiLun, pVScsiReq, enmTxDir,
362 uTransferStart, cbTransfer - cbResidue);
363 rcReq = vscsiLunReqSenseErrorInfoSet(pVScsiLun, pVScsiReq, SCSI_SENSE_NONE | SCSI_SENSE_FLAG_FILEMARK, SCSI_ASC_NONE, SCSI_ASCQ_FILEMARK_DETECTED, cbResidue);
364 }
365 else
366 {
367// rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_, SCSI_ASC_NONE, SCSI_ASCQ_END_OF_DATA_DETECTED);
368 /* Report a file mark. */
369 rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_NONE | SCSI_SENSE_FLAG_FILEMARK, SCSI_ASC_NONE, SCSI_ASCQ_FILEMARK_DETECTED);
370 vscsiDeviceReqComplete(pVScsiLun->pVScsiDevice, pVScsiReq, rcReq, false, VINF_SUCCESS);
371 }
372 }
373 else if (!cbTransfer)
374 {
375 /* A 0 transfer length is not an error. */
376 rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
377 vscsiDeviceReqComplete(pVScsiLun->pVScsiDevice, pVScsiReq, rcReq, false, VINF_SUCCESS);
378 }
379 else
380 {
381 /* Enqueue new I/O request */
382 rc = vscsiIoReqTransferEnqueue(pVScsiLun, pVScsiReq, enmTxDir,
383 uTransferStart, cbTransfer);
384 }
385 }
386 else /* Request completed */
387 vscsiDeviceReqComplete(pVScsiLun->pVScsiDevice, pVScsiReq, rcReq, false, VINF_SUCCESS);
388
389 return rc;
390}
391
392/** @interface_method_impl{VSCSILUNDESC,pfnVScsiLunMediumInserted} */
393static DECLCALLBACK(int) vscsiLunSSCMediumInserted(PVSCSILUNINT pVScsiLun)
394{
395 int rc = VINF_SUCCESS;
396 uint32_t cRegions = vscsiLunMediumGetRegionCount(pVScsiLun);
397 if (cRegions != 1)
398 rc = VERR_INVALID_PARAMETER;
399
400 if (RT_SUCCESS(rc))
401 {
402#if 0
403 PVSCSILUNSSC pVScsiLUNSSC = (PVSCSILUNSSC)pVScsiLun;
404
405 pVScsiLUNSSC->cSectors = cbDisk / pVScsiLUNSSC->cbSector;
406
407 uint32_t OldStatus, NewStatus;
408 do
409 {
410 OldStatus = ASMAtomicReadU32((volatile uint32_t *)&pVScsiLUNSSC->MediaEventStatus);
411 switch (OldStatus)
412 {
413 case MMCEVENTSTATUSTYPE_MEDIA_CHANGED:
414 case MMCEVENTSTATUSTYPE_MEDIA_REMOVED:
415 /* no change, we will send "medium removed" + "medium inserted" */
416 NewStatus = MMCEVENTSTATUSTYPE_MEDIA_CHANGED;
417 break;
418 default:
419 NewStatus = MMCEVENTSTATUSTYPE_MEDIA_NEW;
420 break;
421 }
422 } while (!ASMAtomicCmpXchgU32((volatile uint32_t *)&pVScsiLUNSSC->MediaEventStatus,
423 NewStatus, OldStatus));
424
425 ASMAtomicXchgU32(&pVScsiLUNSSC->u32MediaTrackType, MMC_MEDIA_TYPE_UNKNOWN);
426#endif
427 }
428
429 return rc;
430}
431
432/** @interface_method_impl{VSCSILUNDESC,pfnVScsiLunMediumRemoved} */
433static DECLCALLBACK(int) vscsiLunSSCMediumRemoved(PVSCSILUNINT pVScsiLun)
434{
435 NOREF(pVScsiLun);
436#if 0
437 PVSCSILUNSSC pVScsiLUNSSC = (PVSCSILUNSSC)pVScsiLun;
438
439 ASMAtomicWriteU32((volatile uint32_t *)&pVScsiLUNSSC->MediaEventStatus, MMCEVENTSTATUSTYPE_MEDIA_REMOVED);
440 ASMAtomicXchgU32(&pVScsiLUNSSC->u32MediaTrackType, MMC_MEDIA_TYPE_NO_DISC);
441#endif
442 return VINF_SUCCESS;
443}
444
445VSCSILUNDESC g_VScsiLunTypeSsc =
446{
447 /** enmLunType */
448 VSCSILUNTYPE_SSC,
449 /** pcszDescName */
450 "SSC",
451 /** cbLun */
452 sizeof(VSCSILUNSSC),
453 /** cSupOpcInfo */
454 0,
455 /** paSupOpcInfo */
456 NULL,
457 /** pfnVScsiLunInit */
458 vscsiLUNSSCInit,
459 /** pfnVScsiLunDestroy */
460 vscsiLUNSSCDestroy,
461 /** pfnVScsiLunReqProcess */
462 vscsiLUNSSCReqProcess,
463 /** pfnVScsiLunReqFree */
464 NULL,
465 /** pfnVScsiLunMediumInserted */
466 vscsiLunSSCMediumInserted,
467 /** pfnVScsiLunMediumRemoved */
468 vscsiLunSSCMediumRemoved
469};
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