VirtualBox

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

Last change on this file since 94368 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

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