VirtualBox

source: vbox/trunk/src/VBox/Devices/Parallel/DrvHostParallel.cpp@ 39053

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

*: RTFILE becomes a pointer, RTFileOpen++ expands it's flags paramter from uint32_t to uint64_t.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 13.9 KB
Line 
1/* $Id: DrvHostParallel.cpp 37596 2011-06-22 19:30:06Z vboxsync $ */
2/** @file
3 * VirtualBox Host Parallel Port Driver.
4 *
5 * Contributed by: Alexander Eichner
6 */
7
8/*
9 * Copyright (C) 2006-2010 Oracle Corporation
10 *
11 * This file is part of VirtualBox Open Source Edition (OSE), as
12 * available from http://www.virtualbox.org. This file is free software;
13 * you can redistribute it and/or modify it under the terms of the GNU
14 * General Public License (GPL) as published by the Free Software
15 * Foundation, in version 2 as it comes in the "COPYING" file of the
16 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
17 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
18 */
19
20/*******************************************************************************
21* Header Files *
22*******************************************************************************/
23#define LOG_GROUP LOG_GROUP_DRV_HOST_PARALLEL
24#include <VBox/vmm/pdmdrv.h>
25#include <VBox/vmm/pdmthread.h>
26#include <iprt/asm.h>
27#include <iprt/assert.h>
28#include <iprt/file.h>
29#include <iprt/pipe.h>
30#include <iprt/semaphore.h>
31#include <iprt/stream.h>
32#include <iprt/uuid.h>
33
34#ifdef RT_OS_LINUX
35# include <sys/ioctl.h>
36# include <sys/types.h>
37# include <sys/stat.h>
38# include <sys/poll.h>
39# include <fcntl.h>
40# include <unistd.h>
41# include <linux/ppdev.h>
42# include <linux/parport.h>
43# include <errno.h>
44#endif
45
46#include "VBoxDD.h"
47
48
49/*******************************************************************************
50* Structures and Typedefs *
51*******************************************************************************/
52/**
53 * Host parallel port driver instance data.
54 * @implements PDMIHOSTPARALLELCONNECTOR
55 */
56typedef struct DRVHOSTPARALLEL
57{
58 /** Pointer to the driver instance structure. */
59 PPDMDRVINS pDrvIns;
60 /** Pointer to the char port interface of the driver/device above us. */
61 PPDMIHOSTPARALLELPORT pDrvHostParallelPort;
62 /** Our host device interface. */
63 PDMIHOSTPARALLELCONNECTOR IHostParallelConnector;
64 /** Device Path */
65 char *pszDevicePath;
66 /** Device Handle */
67 RTFILE hFileDevice;
68 /** Thread waiting for interrupts. */
69 PPDMTHREAD pMonitorThread;
70 /** Wakeup pipe read end. */
71 RTPIPE hWakeupPipeR;
72 /** Wakeup pipe write end. */
73 RTPIPE hWakeupPipeW;
74} DRVHOSTPARALLEL, *PDRVHOSTPARALLEL;
75
76/** Converts a pointer to DRVHOSTPARALLEL::IHostDeviceConnector to a PDRHOSTPARALLEL. */
77#define PDMIHOSTPARALLELCONNECTOR_2_DRVHOSTPARALLEL(pInterface) ( (PDRVHOSTPARALLEL)((uintptr_t)pInterface - RT_OFFSETOF(DRVHOSTPARALLEL, IHostParallelConnector)) )
78
79
80/* -=-=-=-=- IBase -=-=-=-=- */
81
82/**
83 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
84 */
85static DECLCALLBACK(void *) drvHostParallelQueryInterface(PPDMIBASE pInterface, const char *pszIID)
86{
87 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
88 PDRVHOSTPARALLEL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTPARALLEL);
89
90 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
91 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTPARALLELCONNECTOR, &pThis->IHostParallelConnector);
92 return NULL;
93}
94
95/* -=-=-=-=- IHostDeviceConnector -=-=-=-=- */
96
97/** @copydoc PDMICHARCONNECTOR::pfnWrite */
98static DECLCALLBACK(int) drvHostParallelWrite(PPDMIHOSTPARALLELCONNECTOR pInterface, const void *pvBuf, size_t *cbWrite)
99{
100 PDRVHOSTPARALLEL pThis = PDMIHOSTPARALLELCONNECTOR_2_DRVHOSTPARALLEL(pInterface);
101 const unsigned char *pBuffer = (const unsigned char *)pvBuf;
102
103 LogFlow(("%s: pvBuf=%#p cbWrite=%d\n", __FUNCTION__, pvBuf, *cbWrite));
104
105 ioctl(RTFileToNative(pThis->hFileDevice), PPWDATA, pBuffer);
106 *cbWrite = 1;
107
108 return VINF_SUCCESS;
109}
110
111static DECLCALLBACK(int) drvHostParallelRead(PPDMIHOSTPARALLELCONNECTOR pInterface, void *pvBuf, size_t *cbRead)
112{
113 PDRVHOSTPARALLEL pThis = PDMIHOSTPARALLELCONNECTOR_2_DRVHOSTPARALLEL(pInterface);
114 unsigned char *pBuffer = (unsigned char *)pvBuf;
115
116 LogFlow(("%s: pvBuf=%#p cbRead=%d\n", __FUNCTION__, pvBuf, cbRead));
117
118 ioctl(RTFileToNative(pThis->hFileDevice), PPRDATA, pBuffer);
119 *cbRead = 1;
120
121 return VINF_SUCCESS;
122}
123
124static DECLCALLBACK(int) drvHostParallelSetMode(PPDMIHOSTPARALLELCONNECTOR pInterface, PDMPARALLELPORTMODE enmMode)
125{
126 PDRVHOSTPARALLEL pThis = PDMIHOSTPARALLELCONNECTOR_2_DRVHOSTPARALLEL(pInterface);
127 int ppdev_mode;
128
129 LogFlow(("%s: mode=%d\n", __FUNCTION__, enmMode));
130
131 switch (enmMode) {
132 case PDM_PARALLEL_PORT_MODE_COMPAT:
133 ppdev_mode = IEEE1284_MODE_COMPAT;
134 break;
135 case PDM_PARALLEL_PORT_MODE_EPP:
136 ppdev_mode = IEEE1284_MODE_EPP;
137 break;
138 case PDM_PARALLEL_PORT_MODE_ECP:
139 //ppdev_mode = IEEE1284_MODE_ECP;
140 break;
141 }
142
143 ioctl(RTFileToNative(pThis->hFileDevice), PPSETMODE, &ppdev_mode);
144
145 return VINF_SUCCESS;
146}
147
148static DECLCALLBACK(int) drvHostParallelWriteControl(PPDMIHOSTPARALLELCONNECTOR pInterface, uint8_t fReg)
149{
150 PDRVHOSTPARALLEL pThis = PDMIHOSTPARALLELCONNECTOR_2_DRVHOSTPARALLEL(pInterface);
151
152 LogFlow(("%s: fReg=%d\n", __FUNCTION__, fReg));
153 ioctl(RTFileToNative(pThis->hFileDevice), PPWCONTROL, &fReg);
154
155 return VINF_SUCCESS;
156}
157
158static DECLCALLBACK(int) drvHostParallelReadControl(PPDMIHOSTPARALLELCONNECTOR pInterface, uint8_t *pfReg)
159{
160 PDRVHOSTPARALLEL pThis = PDMIHOSTPARALLELCONNECTOR_2_DRVHOSTPARALLEL(pInterface);
161
162 uint8_t fReg = 0;
163 ioctl(RTFileToNative(pThis->hFileDevice), PPRCONTROL, &fReg);
164 LogFlow(("%s: fReg=%d\n", __FUNCTION__, fReg));
165 *pfReg = fReg;
166
167 return VINF_SUCCESS;
168}
169
170static DECLCALLBACK(int) drvHostParallelReadStatus(PPDMIHOSTPARALLELCONNECTOR pInterface, uint8_t *pfReg)
171{
172 PDRVHOSTPARALLEL pThis = PDMIHOSTPARALLELCONNECTOR_2_DRVHOSTPARALLEL(pInterface);
173
174 uint8_t fReg = 0;
175 ioctl(RTFileToNative(pThis->hFileDevice), PPRSTATUS, &fReg);
176 LogFlow(("%s: fReg=%d\n", __FUNCTION__, fReg));
177 *pfReg = fReg;
178
179 return VINF_SUCCESS;
180}
181
182static DECLCALLBACK(int) drvHostParallelMonitorThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
183{
184 PDRVHOSTPARALLEL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTPARALLEL);
185 struct pollfd aFDs[2];
186
187 /*
188 * We can wait for interrupts using poll on linux hosts.
189 */
190 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
191 {
192 int rc;
193
194 aFDs[0].fd = RTFileToNative(pThis->hFileDevice);
195 aFDs[0].events = POLLIN;
196 aFDs[0].revents = 0;
197 aFDs[1].fd = RTPipeToNative(pThis->hWakeupPipeR);
198 aFDs[1].events = POLLIN | POLLERR | POLLHUP;
199 aFDs[1].revents = 0;
200 rc = poll(aFDs, RT_ELEMENTS(aFDs), -1);
201 if (rc < 0)
202 {
203 AssertMsgFailed(("poll failed with rc=%d\n", RTErrConvertFromErrno(errno)));
204 return RTErrConvertFromErrno(errno);
205 }
206
207 if (pThread->enmState != PDMTHREADSTATE_RUNNING)
208 break;
209 if (rc > 0 && aFDs[1].revents)
210 {
211 if (aFDs[1].revents & (POLLHUP | POLLERR | POLLNVAL))
212 break;
213 /* notification to terminate -- drain the pipe */
214 char ch;
215 size_t cbRead;
216 RTPipeRead(pThis->hWakeupPipeR, &ch, 1, &cbRead);
217 continue;
218 }
219
220 /* Interrupt occurred. */
221 rc = pThis->pDrvHostParallelPort->pfnNotifyInterrupt(pThis->pDrvHostParallelPort);
222 AssertRC(rc);
223 }
224
225 return VINF_SUCCESS;
226}
227
228/**
229 * Unblock the monitor thread so it can respond to a state change.
230 *
231 * @returns a VBox status code.
232 * @param pDrvIns The driver instance.
233 * @param pThread The send thread.
234 */
235static DECLCALLBACK(int) drvHostParallelWakeupMonitorThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
236{
237 PDRVHOSTPARALLEL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTPARALLEL);
238 size_t cbIgnored;
239 return RTPipeWrite(pThis->hWakeupPipeW, "", 1, &cbIgnored);
240}
241
242/**
243 * Destruct a host parallel driver instance.
244 *
245 * Most VM resources are freed by the VM. This callback is provided so that
246 * any non-VM resources can be freed correctly.
247 *
248 * @param pDrvIns The driver instance data.
249 */
250static DECLCALLBACK(void) drvHostParallelDestruct(PPDMDRVINS pDrvIns)
251{
252 PDRVHOSTPARALLEL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTPARALLEL);
253 LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance));
254 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
255 int rc;
256
257 if (pThis->hFileDevice != NIL_RTFILE)
258 ioctl(RTFileToNative(pThis->hFileDevice), PPRELEASE);
259
260 rc = RTPipeClose(pThis->hWakeupPipeW); AssertRC(rc);
261 pThis->hWakeupPipeW = NIL_RTPIPE;
262
263 rc = RTPipeClose(pThis->hWakeupPipeR); AssertRC(rc);
264 pThis->hWakeupPipeR = NIL_RTPIPE;
265
266 rc = RTFileClose(pThis->hFileDevice); AssertRC(rc);
267 pThis->hFileDevice = NIL_RTFILE;
268
269 if (pThis->pszDevicePath)
270 {
271 MMR3HeapFree(pThis->pszDevicePath);
272 pThis->pszDevicePath = NULL;
273 }
274}
275
276/**
277 * Construct a host parallel driver instance.
278 *
279 * @copydoc FNPDMDRVCONSTRUCT
280 */
281static DECLCALLBACK(int) drvHostParallelConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
282{
283 PDRVHOSTPARALLEL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTPARALLEL);
284 LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance));
285 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
286
287 /*
288 * Init basic data members and interfaces.
289 *
290 * Must be done before returning any failure because we've got a destructor.
291 */
292 pThis->hFileDevice = NIL_RTFILE;
293 pThis->hWakeupPipeR = NIL_RTPIPE;
294 pThis->hWakeupPipeW = NIL_RTPIPE;
295
296 /* IBase. */
297 pDrvIns->IBase.pfnQueryInterface = drvHostParallelQueryInterface;
298 /* IHostParallelConnector. */
299 pThis->IHostParallelConnector.pfnWrite = drvHostParallelWrite;
300 pThis->IHostParallelConnector.pfnRead = drvHostParallelRead;
301 pThis->IHostParallelConnector.pfnSetMode = drvHostParallelSetMode;
302 pThis->IHostParallelConnector.pfnWriteControl = drvHostParallelWriteControl;
303 pThis->IHostParallelConnector.pfnReadControl = drvHostParallelReadControl;
304 pThis->IHostParallelConnector.pfnReadStatus = drvHostParallelReadStatus;
305
306 /*
307 * Validate the config.
308 */
309 if (!CFGMR3AreValuesValid(pCfg, "DevicePath\0"))
310 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES,
311 N_("Unknown host parallel configuration option, only supports DevicePath"));
312
313 /*
314 * Query configuration.
315 */
316 /* Device */
317 int rc = CFGMR3QueryStringAlloc(pCfg, "DevicePath", &pThis->pszDevicePath);
318 if (RT_FAILURE(rc))
319 {
320 AssertMsgFailed(("Configuration error: query for \"DevicePath\" string returned %Rra.\n", rc));
321 return rc;
322 }
323
324 /*
325 * Open the device
326 */
327 rc = RTFileOpen(&pThis->hFileDevice, pThis->pszDevicePath, RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
328 if (RT_FAILURE(rc))
329 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("Parallel#%d could not open '%s'"),
330 pDrvIns->iInstance, pThis->pszDevicePath);
331
332 /*
333 * Try to get exclusive access to parallel port
334 */
335 rc = ioctl(RTFileToNative(pThis->hFileDevice), PPEXCL);
336 if (rc < 0)
337 return PDMDrvHlpVMSetError(pDrvIns, RTErrConvertFromErrno(errno), RT_SRC_POS,
338 N_("Parallel#%d could not get exclusive access for parallel port '%s'"
339 "Be sure that no other process or driver accesses this port"),
340 pDrvIns->iInstance, pThis->pszDevicePath);
341
342 /*
343 * Claim the parallel port
344 */
345 rc = ioctl(RTFileToNative(pThis->hFileDevice), PPCLAIM);
346 if (rc < 0)
347 return PDMDrvHlpVMSetError(pDrvIns, RTErrConvertFromErrno(errno), RT_SRC_POS,
348 N_("Parallel#%d could not claim parallel port '%s'"
349 "Be sure that no other process or driver accesses this port"),
350 pDrvIns->iInstance, pThis->pszDevicePath);
351
352 /*
353 * Get the IHostParallelPort interface of the above driver/device.
354 */
355 pThis->pDrvHostParallelPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIHOSTPARALLELPORT);
356 if (!pThis->pDrvHostParallelPort)
357 return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE, RT_SRC_POS, N_("Parallel#%d has no parallel port interface above"),
358 pDrvIns->iInstance);
359
360 /*
361 * Create wakeup pipe.
362 */
363 rc = RTPipeCreate(&pThis->hWakeupPipeR, &pThis->hWakeupPipeR, 0 /*fFlags*/);
364 AssertRCReturn(rc, rc);
365
366 /*
367 * Start waiting for interrupts.
368 */
369 rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pMonitorThread, pThis, drvHostParallelMonitorThread, drvHostParallelWakeupMonitorThread, 0,
370 RTTHREADTYPE_IO, "ParMon");
371 if (RT_FAILURE(rc))
372 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("HostParallel#%d cannot create monitor thread"), pDrvIns->iInstance);
373
374 return VINF_SUCCESS;
375}
376
377/**
378 * Char driver registration record.
379 */
380const PDMDRVREG g_DrvHostParallel =
381{
382 /* u32Version */
383 PDM_DRVREG_VERSION,
384 /* szName */
385 "HostParallel",
386 /* szRCMod */
387 "",
388 /* szR0Mod */
389 "",
390 /* pszDescription */
391 "Parallel host driver.",
392 /* fFlags */
393 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
394 /* fClass. */
395 PDM_DRVREG_CLASS_CHAR,
396 /* cMaxInstances */
397 ~0,
398 /* cbInstance */
399 sizeof(DRVHOSTPARALLEL),
400 /* pfnConstruct */
401 drvHostParallelConstruct,
402 /* pfnDestruct */
403 drvHostParallelDestruct,
404 /* pfnRelocate */
405 NULL,
406 /* pfnIOCtl */
407 NULL,
408 /* pfnPowerOn */
409 NULL,
410 /* pfnReset */
411 NULL,
412 /* pfnSuspend */
413 NULL,
414 /* pfnResume */
415 NULL,
416 /* pfnAttach */
417 NULL,
418 /* pfnDetach */
419 NULL,
420 /* pfnPowerOff */
421 NULL,
422 /* pfnSoftReset */
423 NULL,
424 /* u32EndVersion */
425 PDM_DRVREG_VERSION
426};
427
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