VirtualBox

source: vbox/trunk/src/VBox/Devices/Storage/DrvHostDVD.cpp@ 64333

Last change on this file since 64333 was 64320, checked in by vboxsync, 9 years ago

Devices/Storage/DrvHost*: Start working on implementing PDMIMEDIAEX and SCSI command passthrough (wip)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.4 KB
Line 
1/* $Id: DrvHostDVD.cpp 64320 2016-10-19 14:55:59Z vboxsync $ */
2/** @file
3 * DrvHostDVD - Host DVD block driver.
4 */
5
6/*
7 * Copyright (C) 2006-2016 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_DRV_HOST_DVD
23#include <iprt/asm.h>
24#include <VBox/vmm/pdmdrv.h>
25#include <VBox/vmm/pdmstorageifs.h>
26#include <iprt/asm.h>
27#include <iprt/assert.h>
28#include <iprt/file.h>
29#include <iprt/string.h>
30#include <iprt/thread.h>
31#include <iprt/critsect.h>
32#include <VBox/scsi.h>
33
34#include "VBoxDD.h"
35#include "DrvHostBase.h"
36#include "ATAPIPassthrough.h"
37
38/**
39 * Host DVD driver instance data.
40 */
41typedef struct DRVHOSTDVD
42{
43 /** Base drivr data. */
44 DRVHOSTBASE Core;
45 /** The current tracklist of the loaded medium if passthrough is used. */
46 PTRACKLIST pTrackList;
47} DRVHOSTDVD;
48/** Pointer to the host DVD driver instance data. */
49typedef DRVHOSTDVD *PDRVHOSTDVD;
50
51/*********************************************************************************************************************************
52* Internal Functions *
53*********************************************************************************************************************************/
54
55
56static PDMMEDIATXDIR drvHostDvdGetTxDirFromCmd(uint8_t bCdb)
57{
58 PDMMEDIATXDIR enmTxDir = PDMMEDIATXDIR_NONE;
59
60 switch (bCdb)
61 {
62 case SCSI_BLANK:
63 case SCSI_CLOSE_TRACK_SESSION:
64 case SCSI_LOAD_UNLOAD_MEDIUM:
65 case SCSI_PAUSE_RESUME:
66 case SCSI_PLAY_AUDIO_10:
67 case SCSI_PLAY_AUDIO_12:
68 case SCSI_PLAY_AUDIO_MSF:
69 case SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL:
70 case SCSI_RESERVE_TRACK:
71 case SCSI_SCAN:
72 case SCSI_SEEK_10:
73 case SCSI_REPAIR_TRACK:
74 case SCSI_SET_CD_SPEED:
75 case SCSI_SET_READ_AHEAD:
76 case SCSI_START_STOP_UNIT:
77 case SCSI_STOP_PLAY_SCAN:
78 case SCSI_SYNCHRONIZE_CACHE:
79 case SCSI_TEST_UNIT_READY:
80 case SCSI_VERIFY_10:
81 enmTxDir = PDMMEDIATXDIR_NONE;
82 break;
83 case SCSI_SET_STREAMING:
84 case SCSI_ERASE_10:
85 case SCSI_FORMAT_UNIT:
86 case SCSI_MODE_SELECT_10:
87 case SCSI_SEND_CUE_SHEET:
88 case SCSI_SEND_DVD_STRUCTURE:
89 case SCSI_SEND_EVENT:
90 case SCSI_SEND_KEY:
91 case SCSI_SEND_OPC_INFORMATION:
92 case SCSI_WRITE_10:
93 case SCSI_WRITE_AND_VERIFY_10:
94 case SCSI_WRITE_12:
95 enmTxDir = PDMMEDIATXDIR_TO_DEVICE;
96 break;
97 case SCSI_GET_CONFIGURATION:
98 case SCSI_GET_EVENT_STATUS_NOTIFICATION:
99 case SCSI_GET_PERFORMANCE:
100 case SCSI_INQUIRY:
101 case SCSI_MECHANISM_STATUS:
102 case SCSI_MODE_SENSE_10:
103 case SCSI_READ_10:
104 case SCSI_READ_12:
105 case SCSI_READ_BUFFER:
106 case SCSI_READ_BUFFER_CAPACITY:
107 case SCSI_READ_CAPACITY:
108 case SCSI_READ_CD:
109 case SCSI_READ_CD_MSF:
110 case SCSI_READ_DISC_INFORMATION:
111 case SCSI_READ_DVD_STRUCTURE:
112 case SCSI_READ_FORMAT_CAPACITIES:
113 case SCSI_READ_SUBCHANNEL:
114 case SCSI_READ_TOC_PMA_ATIP:
115 case SCSI_READ_TRACK_INFORMATION:
116 case SCSI_REPORT_KEY:
117 case SCSI_REQUEST_SENSE:
118 case SCSI_REPORT_LUNS: /* Not part of MMC-3, but used by Windows. */
119 enmTxDir = PDMMEDIATXDIR_FROM_DEVICE;
120 break;
121
122 case SCSI_WRITE_BUFFER:
123#if 0
124 switch (pbPacket[1] & 0x1f)
125 {
126 case 0x04: /* download microcode */
127 case 0x05: /* download microcode and save */
128 case 0x06: /* download microcode with offsets */
129 case 0x07: /* download microcode with offsets and save */
130 case 0x0e: /* download microcode with offsets and defer activation */
131 case 0x0f: /* activate deferred microcode */
132 LogRel(("PIIX3 ATA: LUN#%d: CD-ROM passthrough command attempted to update firmware, blocked\n", s->iLUN));
133 atapiR3CmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET);
134 break;
135 default:
136 enmTxDir = PDMMEDIATXDIR_TO_DEVICE;
137 break;
138 }
139#endif
140 break;
141 case SCSI_REZERO_UNIT:
142 /* Obsolete command used by cdrecord. What else would one expect?
143 * This command is not sent to the drive, it is handled internally,
144 * as the Linux kernel doesn't like it (message "scsi: unknown
145 * opcode 0x01" in syslog) and replies with a sense code of 0,
146 * which sends cdrecord to an endless loop. */
147 //atapiR3CmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ILLEGAL_OPCODE);
148 break;
149 default:
150 LogRel(("HostDVD: passthrough unimplemented for command %#x\n", bCdb));
151 //atapiR3CmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ILLEGAL_OPCODE);
152 break;
153 }
154
155 return enmTxDir;
156}
157
158/**
159 * Locks or unlocks the drive.
160 *
161 * @returns VBox status code.
162 * @param pThis The instance data.
163 * @param fLock True if the request is to lock the drive, false if to unlock.
164 */
165static DECLCALLBACK(int) drvHostDvdDoLock(PDRVHOSTBASE pThis, bool fLock)
166{
167 int rc = drvHostBaseDoLockOs(pThis, fLock);
168
169 LogFlow(("drvHostDvdDoLock(, fLock=%RTbool): returns %Rrc\n", fLock, rc));
170 return rc;
171}
172
173
174/** @interface_method_impl{PDMIMEDIA,pfnSendCmd} */
175static DECLCALLBACK(int) drvHostDvdSendCmd(PPDMIMEDIA pInterface, const uint8_t *pbCmd,
176 PDMMEDIATXDIR enmTxDir, void *pvBuf, uint32_t *pcbBuf,
177 uint8_t *pabSense, size_t cbSense, uint32_t cTimeoutMillies)
178{
179 PDRVHOSTBASE pThis = RT_FROM_MEMBER(pInterface, DRVHOSTBASE, IMedia);
180 int rc;
181 LogFlow(("%s: cmd[0]=%#04x txdir=%d pcbBuf=%d timeout=%d\n", __FUNCTION__, pbCmd[0], enmTxDir, *pcbBuf, cTimeoutMillies));
182
183 RTCritSectEnter(&pThis->CritSect);
184 /*
185 * Pass the request on to the internal scsi command interface.
186 * The command seems to be 12 bytes long, the docs a bit copy&pasty on the command length point...
187 */
188 if (enmTxDir == PDMMEDIATXDIR_FROM_DEVICE)
189 memset(pvBuf, '\0', *pcbBuf); /* we got read size, but zero it anyway. */
190 rc = drvHostBaseScsiCmdOs(pThis, pbCmd, 12, enmTxDir, pvBuf, pcbBuf, pabSense, cbSense, cTimeoutMillies);
191 if (rc == VERR_UNRESOLVED_ERROR)
192 /* sense information set */
193 rc = VERR_DEV_IO_ERROR;
194
195 if (pbCmd[0] == SCSI_GET_EVENT_STATUS_NOTIFICATION)
196 {
197 uint8_t *pbBuf = (uint8_t*)pvBuf;
198 Log2(("Event Status Notification class=%#02x supported classes=%#02x\n", pbBuf[2], pbBuf[3]));
199 if (RT_BE2H_U16(*(uint16_t*)pbBuf) >= 6)
200 Log2((" event %#02x %#02x %#02x %#02x\n", pbBuf[4], pbBuf[5], pbBuf[6], pbBuf[7]));
201 }
202 RTCritSectLeave(&pThis->CritSect);
203
204 LogFlow(("%s: rc=%Rrc\n", __FUNCTION__, rc));
205 return rc;
206}
207
208
209/** @interface_method_impl{PDMIMEDIAEX,pfnIoReqSendScsiCmd} */
210static DECLCALLBACK(int) drvHostDvdIoReqSendScsiCmd(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq, uint32_t uLun,
211 const uint8_t *pbCdb, size_t cbCdb, PDMMEDIAEXIOREQSCSITXDIR enmTxDir,
212 size_t cbBuf, uint8_t *pabSense, size_t cbSense, uint8_t *pu8ScsiSts,
213 uint32_t cTimeoutMillies)
214{
215 RT_NOREF2(uLun, cTimeoutMillies);
216
217 PDRVHOSTBASE pThis = RT_FROM_MEMBER(pInterface, DRVHOSTBASE, IMediaEx);
218 PDRVHOSTBASEREQ pReq = (PDRVHOSTBASEREQ)hIoReq;
219 int rc = VINF_SUCCESS;
220 void *pvBuf = NULL;
221
222 LogFlow(("%s: pbCdb[0]=%#04x enmTxDir=%d cbBuf=%zu timeout=%u\n", __FUNCTION__, pbCdb[0], enmTxDir, cbBuf, cTimeoutMillies));
223
224 RTCritSectEnter(&pThis->CritSect);
225 if (cbBuf)
226 {
227 bool fWrite = (enmTxDir == PDMMEDIAEXIOREQSCSITXDIR_UNKNOWN || enmTxDir == PDMMEDIAEXIOREQSCSITXDIR_TO_DEVICE);
228 rc = drvHostBaseBufferRetain(pThis, pReq, cbBuf, fWrite, &pvBuf);
229 }
230
231 if (RT_SUCCESS(rc))
232 {
233 uint32_t cbBufTmp = (uint32_t)cbBuf;
234 PDMMEDIATXDIR enmMediaTxDir = PDMMEDIATXDIR_NONE;
235 /*
236 * Pass the request on to the internal scsi command interface.
237 * The command seems to be 12 bytes long, the docs a bit copy&pasty on the command length point...
238 */
239 if (enmTxDir == PDMMEDIAEXIOREQSCSITXDIR_UNKNOWN || enmTxDir == PDMMEDIAEXIOREQSCSITXDIR_FROM_DEVICE)
240 memset(pvBuf, '\0', cbBuf); /* we got read size, but zero it anyway. */
241
242 if (cbBuf)
243 {
244 switch (enmTxDir)
245 {
246 case PDMMEDIAEXIOREQSCSITXDIR_FROM_DEVICE:
247 enmMediaTxDir = PDMMEDIATXDIR_FROM_DEVICE;
248 break;
249 case PDMMEDIAEXIOREQSCSITXDIR_TO_DEVICE:
250 enmMediaTxDir = PDMMEDIATXDIR_TO_DEVICE;
251 break;
252 case PDMMEDIAEXIOREQSCSITXDIR_UNKNOWN:
253 enmMediaTxDir = drvHostDvdGetTxDirFromCmd(pbCdb[0]);
254 break;
255 case PDMMEDIAEXIOREQSCSITXDIR_NONE:
256 default:
257 enmMediaTxDir = PDMMEDIATXDIR_NONE;
258 break;
259 }
260 }
261
262 rc = drvHostBaseScsiCmdOs(pThis, pbCdb, cbCdb, enmMediaTxDir, pvBuf, &cbBufTmp, pabSense, cbSense, cTimeoutMillies);
263 if (rc == VERR_UNRESOLVED_ERROR)
264 {
265 /* sense information set */
266 rc = VERR_DEV_IO_ERROR;
267 *pu8ScsiSts = SCSI_STATUS_CHECK_CONDITION;
268 }
269
270 if (pbCdb[0] == SCSI_GET_EVENT_STATUS_NOTIFICATION)
271 {
272 uint8_t *pbBuf = (uint8_t*)pvBuf;
273 Log2(("Event Status Notification class=%#02x supported classes=%#02x\n", pbBuf[2], pbBuf[3]));
274 if (RT_BE2H_U16(*(uint16_t*)pbBuf) >= 6)
275 Log2((" event %#02x %#02x %#02x %#02x\n", pbBuf[4], pbBuf[5], pbBuf[6], pbBuf[7]));
276 }
277
278 if (cbBuf)
279 {
280 bool fWrite = (enmTxDir == PDMMEDIAEXIOREQSCSITXDIR_UNKNOWN || enmTxDir == PDMMEDIAEXIOREQSCSITXDIR_TO_DEVICE);
281 rc = drvHostBaseBufferRelease(pThis, pReq, cbBuf, fWrite, pvBuf);
282 }
283 }
284 RTCritSectLeave(&pThis->CritSect);
285
286 LogFlow(("%s: rc=%Rrc\n", __FUNCTION__, rc));
287 return rc;
288}
289
290
291/* -=-=-=-=- driver interface -=-=-=-=- */
292
293
294/** @interface_method_impl{PDMDRVREG,pfnDestruct} */
295static DECLCALLBACK(void) drvHostDvdDestruct(PPDMDRVINS pDrvIns)
296{
297 PDRVHOSTDVD pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTDVD);
298
299 if (pThis->pTrackList)
300 {
301 ATAPIPassthroughTrackListDestroy(pThis->pTrackList);
302 pThis->pTrackList = NULL;
303 }
304
305 DRVHostBaseDestruct(pDrvIns);
306}
307
308/**
309 * Construct a host dvd drive driver instance.
310 *
311 * @copydoc FNPDMDRVCONSTRUCT
312 */
313static DECLCALLBACK(int) drvHostDvdConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
314{
315 RT_NOREF(fFlags);
316 PDRVHOSTDVD pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTDVD);
317 LogFlow(("drvHostDvdConstruct: iInstance=%d\n", pDrvIns->iInstance));
318
319 bool fPassthrough;
320 int rc = CFGMR3QueryBool(pCfg, "Passthrough", &fPassthrough);
321 if (RT_SUCCESS(rc) && fPassthrough)
322 {
323 pThis->Core.IMedia.pfnSendCmd = drvHostDvdSendCmd;
324 pThis->Core.IMediaEx.pfnIoReqSendScsiCmd = drvHostDvdIoReqSendScsiCmd;
325 /* Passthrough requires opening the device in R/W mode. */
326 pThis->Core.fReadOnlyConfig = false;
327 }
328
329 pThis->Core.pfnDoLock = drvHostDvdDoLock;
330
331 /*
332 * Init instance data.
333 */
334 rc = DRVHostBaseInit(pDrvIns, pCfg, "Path\0Interval\0Locked\0BIOSVisible\0AttachFailError\0Passthrough\0",
335 PDMMEDIATYPE_DVD);
336 LogFlow(("drvHostDvdConstruct: returns %Rrc\n", rc));
337 return rc;
338}
339
340
341/**
342 * Block driver registration record.
343 */
344const PDMDRVREG g_DrvHostDVD =
345{
346 /* u32Version */
347 PDM_DRVREG_VERSION,
348 /* szName */
349 "HostDVD",
350 /* szRCMod */
351 "",
352 /* szR0Mod */
353 "",
354 /* pszDescription */
355 "Host DVD Block Driver.",
356 /* fFlags */
357 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
358 /* fClass. */
359 PDM_DRVREG_CLASS_BLOCK,
360 /* cMaxInstances */
361 ~0U,
362 /* cbInstance */
363 sizeof(DRVHOSTDVD),
364 /* pfnConstruct */
365 drvHostDvdConstruct,
366 /* pfnDestruct */
367 drvHostDvdDestruct,
368 /* pfnRelocate */
369 NULL,
370 /* pfnIOCtl */
371 NULL,
372 /* pfnPowerOn */
373 NULL,
374 /* pfnReset */
375 NULL,
376 /* pfnSuspend */
377 NULL,
378 /* pfnResume */
379 NULL,
380 /* pfnAttach */
381 NULL,
382 /* pfnDetach */
383 NULL,
384 /* pfnPowerOff */
385 NULL,
386 /* pfnSoftReset */
387 NULL,
388 /* u32EndVersion */
389 PDM_DRVREG_VERSION
390};
391
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette