VirtualBox

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

Last change on this file since 13384 was 11286, checked in by vboxsync, 16 years ago

Devices: %Vra -> %Rra (just preferred, not mandatory (yet))

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 14.5 KB
Line 
1/* $Id: DrvHostParallel.cpp 11286 2008-08-08 22:33:43Z vboxsync $ */
2/** @file
3 * VirtualBox Host Parallel Port Driver.
4 *
5 * Contributed by: Alexander Eichner
6 */
7
8/*
9 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
20 * Clara, CA 95054 USA or visit http://www.sun.com if you need
21 * additional information or have any questions.
22 */
23
24/*******************************************************************************
25* Header Files *
26*******************************************************************************/
27#define LOG_GROUP LOG_GROUP_DRV_HOST_PARALLEL
28#include <VBox/pdmdrv.h>
29#include <VBox/pdmthread.h>
30#include <iprt/asm.h>
31#include <iprt/assert.h>
32#include <iprt/stream.h>
33#include <iprt/semaphore.h>
34#include <iprt/file.h>
35
36#ifdef RT_OS_LINUX
37# include <sys/ioctl.h>
38# include <sys/types.h>
39# include <sys/stat.h>
40# include <sys/poll.h>
41# include <fcntl.h>
42# include <unistd.h>
43# include <linux/ppdev.h>
44# include <linux/parport.h>
45# include <errno.h>
46#endif
47
48#include "Builtins.h"
49
50/*******************************************************************************
51* Structures and Typedefs *
52*******************************************************************************/
53/**
54 * Host parallel port driver instance data.
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 /** Our host device port interface. */
65 PDMIHOSTPARALLELPORT IHostParallelPort;
66 /** Device Path */
67 char *pszDevicePath;
68 /** Device Handle */
69 RTFILE FileDevice;
70 /** Thread waiting for interrupts. */
71 PPDMTHREAD pMonitorThread;
72 /** Wakeup pipe read end. */
73 RTFILE WakeupPipeR;
74 /** Wakeup pipe write end. */
75 RTFILE WakeupPipeW;
76} DRVHOSTPARALLEL, *PDRVHOSTPARALLEL;
77
78/** Converts a pointer to DRVHOSTPARALLEL::IHostDeviceConnector to a PDRHOSTPARALLEL. */
79#define PDMIHOSTPARALLELCONNECTOR_2_DRVHOSTPARALLEL(pInterface) ( (PDRVHOSTPARALLEL)((uintptr_t)pInterface - RT_OFFSETOF(DRVHOSTPARALLEL, IHostParallelConnector)) )
80/** Converts a pointer to DRVHOSTPARALLEL::IHostDevicePort to a PDRHOSTPARALLEL. */
81#define PDMIHOSTPARALLELPORT_2_DRVHOSTPARALLEL(pInterface) ( (PDRVHOSTPARALLEL)((uintptr_t)pInterface - RT_OFFSETOF(DRVHOSTPARALLEL, IHostParallelPort)) )
82
83/* -=-=-=-=- IBase -=-=-=-=- */
84
85/**
86 * Queries an interface to the driver.
87 *
88 * @returns Pointer to interface.
89 * @returns NULL if the interface was not supported by the driver.
90 * @param pInterface Pointer to this interface structure.
91 * @param enmInterface The requested interface identification.
92 */
93static DECLCALLBACK(void *) drvHostParallelQueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface)
94{
95 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
96 PDRVHOSTPARALLEL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTPARALLEL);
97 switch (enmInterface)
98 {
99 case PDMINTERFACE_BASE:
100 return &pDrvIns->IBase;
101 case PDMINTERFACE_HOST_PARALLEL_CONNECTOR:
102 return &pThis->IHostParallelConnector;
103 default:
104 return NULL;
105 }
106}
107
108/* -=-=-=-=- IHostDeviceConnector -=-=-=-=- */
109
110/** @copydoc PDMICHAR::pfnWrite */
111static DECLCALLBACK(int) drvHostParallelWrite(PPDMIHOSTPARALLELCONNECTOR pInterface, const void *pvBuf, size_t *cbWrite)
112{
113 PDRVHOSTPARALLEL pThis = PDMIHOSTPARALLELCONNECTOR_2_DRVHOSTPARALLEL(pInterface);
114 const unsigned char *pBuffer = (const unsigned char *)pvBuf;
115
116 LogFlow(("%s: pvBuf=%#p cbWrite=%d\n", __FUNCTION__, pvBuf, *cbWrite));
117
118 ioctl(pThis->FileDevice, PPWDATA, pBuffer);
119 *cbWrite = 1;
120
121 return VINF_SUCCESS;
122}
123
124static DECLCALLBACK(int) drvHostParallelRead(PPDMIHOSTPARALLELCONNECTOR pInterface, void *pvBuf, size_t *cbRead)
125{
126 PDRVHOSTPARALLEL pThis = PDMIHOSTPARALLELCONNECTOR_2_DRVHOSTPARALLEL(pInterface);
127 unsigned char *pBuffer = (unsigned char *)pvBuf;
128
129 LogFlow(("%s: pvBuf=%#p cbRead=%d\n", __FUNCTION__, pvBuf, cbRead));
130
131 ioctl(pThis->FileDevice, PPRDATA, pBuffer);
132 *cbRead = 1;
133
134 return VINF_SUCCESS;
135}
136
137static DECLCALLBACK(int) drvHostParallelSetMode(PPDMIHOSTPARALLELCONNECTOR pInterface, PDMPARALLELPORTMODE enmMode)
138{
139 PDRVHOSTPARALLEL pThis = PDMIHOSTPARALLELCONNECTOR_2_DRVHOSTPARALLEL(pInterface);
140 int ppdev_mode;
141
142 LogFlow(("%s: mode=%d\n", __FUNCTION__, enmMode));
143
144 switch (enmMode) {
145 case PDM_PARALLEL_PORT_MODE_COMPAT:
146 ppdev_mode = IEEE1284_MODE_COMPAT;
147 break;
148 case PDM_PARALLEL_PORT_MODE_EPP:
149 ppdev_mode = IEEE1284_MODE_EPP;
150 break;
151 case PDM_PARALLEL_PORT_MODE_ECP:
152 //ppdev_mode = IEEE1284_MODE_ECP;
153 break;
154 }
155
156 ioctl(pThis->FileDevice, PPSETMODE, &ppdev_mode);
157
158 return VINF_SUCCESS;
159}
160
161static DECLCALLBACK(int) drvHostParallelWriteControl(PPDMIHOSTPARALLELCONNECTOR pInterface, uint8_t fReg)
162{
163 PDRVHOSTPARALLEL pThis = PDMIHOSTPARALLELCONNECTOR_2_DRVHOSTPARALLEL(pInterface);
164
165 LogFlow(("%s: fReg=%d\n", __FUNCTION__, fReg));
166
167 ioctl(pThis->FileDevice, PPWCONTROL, &fReg);
168
169 return VINF_SUCCESS;
170}
171
172static DECLCALLBACK(int) drvHostParallelReadControl(PPDMIHOSTPARALLELCONNECTOR pInterface, uint8_t *pfReg)
173{
174 PDRVHOSTPARALLEL pThis = PDMIHOSTPARALLELCONNECTOR_2_DRVHOSTPARALLEL(pInterface);
175 uint8_t fReg;
176
177 ioctl(pThis->FileDevice, PPRCONTROL, &fReg);
178
179 LogFlow(("%s: fReg=%d\n", __FUNCTION__, fReg));
180
181 *pfReg = fReg;
182
183 return VINF_SUCCESS;
184}
185
186static DECLCALLBACK(int) drvHostParallelReadStatus(PPDMIHOSTPARALLELCONNECTOR pInterface, uint8_t *pfReg)
187{
188 PDRVHOSTPARALLEL pThis = PDMIHOSTPARALLELCONNECTOR_2_DRVHOSTPARALLEL(pInterface);
189 uint8_t fReg;
190
191 ioctl(pThis->FileDevice, PPRSTATUS, &fReg);
192
193 LogFlow(("%s: fReg=%d\n", __FUNCTION__, fReg));
194
195 *pfReg = fReg;
196
197 return VINF_SUCCESS;
198}
199
200static DECLCALLBACK(int) drvHostParallelMonitorThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
201{
202 PDRVHOSTPARALLEL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTPARALLEL);
203 struct pollfd aFDs[2];
204
205 /*
206 * We can wait for interrupts using poll on linux hosts.
207 */
208 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
209 {
210 int rc;
211
212 aFDs[0].fd = pThis->FileDevice;
213 aFDs[0].events = POLLIN;
214 aFDs[0].revents = 0;
215 aFDs[1].fd = pThis->WakeupPipeR;
216 aFDs[1].events = POLLIN | POLLERR | POLLHUP;
217 aFDs[1].revents = 0;
218 rc = poll(aFDs, RT_ELEMENTS(aFDs), -1);
219 if (rc < 0)
220 {
221 AssertMsgFailed(("poll failed with rc=%d\n", RTErrConvertFromErrno(errno)));
222 return RTErrConvertFromErrno(errno);
223 }
224
225 if (pThread->enmState != PDMTHREADSTATE_RUNNING)
226 break;
227 if (rc > 0 && aFDs[1].revents)
228 {
229 if (aFDs[1].revents & (POLLHUP | POLLERR | POLLNVAL))
230 break;
231 /* notification to terminate -- drain the pipe */
232 char ch;
233 size_t cbRead;
234 RTFileRead(pThis->WakeupPipeR, &ch, 1, &cbRead);
235 continue;
236 }
237
238 /* Interrupt occured. */
239 rc = pThis->pDrvHostParallelPort->pfnNotifyInterrupt(pThis->pDrvHostParallelPort);
240 AssertRC(rc);
241 }
242
243 return VINF_SUCCESS;
244}
245
246/**
247 * Unblock the monitor thread so it can respond to a state change.
248 *
249 * @returns a VBox status code.
250 * @param pDrvIns The driver instance.
251 * @param pThread The send thread.
252 */
253static DECLCALLBACK(int) drvHostParallelWakeupMonitorThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
254{
255 PDRVHOSTPARALLEL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTPARALLEL);
256
257 return RTFileWrite(pThis->WakeupPipeW, "", 1, NULL);
258}
259
260/**
261 * Construct a host parallel driver instance.
262 *
263 * @returns VBox status.
264 * @param pDrvIns The driver instance data.
265 * If the registration structure is needed,
266 * pDrvIns->pDrvReg points to it.
267 * @param pCfgHandle Configuration node handle for the driver. Use this to
268 * obtain the configuration of the driver instance. It's
269 * also found in pDrvIns->pCfgHandle as it's expected to
270 * be used frequently in this function.
271 */
272static DECLCALLBACK(int) drvHostParallelConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle)
273{
274 PDRVHOSTPARALLEL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTPARALLEL);
275 LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance));
276
277 /*
278 * Validate the config.
279 */
280 if (!CFGMR3AreValuesValid(pCfgHandle, "DevicePath\0"))
281 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES,
282 N_("Unknown host parallel configuration option, only supports DevicePath"));
283
284 /*
285 * Init basic data members and interfaces.
286 */
287
288 /* IBase. */
289 pDrvIns->IBase.pfnQueryInterface = drvHostParallelQueryInterface;
290 /* IHostParallelConnector. */
291 pThis->IHostParallelConnector.pfnWrite = drvHostParallelWrite;
292 pThis->IHostParallelConnector.pfnRead = drvHostParallelRead;
293 pThis->IHostParallelConnector.pfnSetMode = drvHostParallelSetMode;
294 pThis->IHostParallelConnector.pfnWriteControl = drvHostParallelWriteControl;
295 pThis->IHostParallelConnector.pfnReadControl = drvHostParallelReadControl;
296 pThis->IHostParallelConnector.pfnReadStatus = drvHostParallelReadStatus;
297
298 /*
299 * Query configuration.
300 */
301 /* Device */
302 int rc = CFGMR3QueryStringAlloc(pCfgHandle, "DevicePath", &pThis->pszDevicePath);
303 if (RT_FAILURE(rc))
304 {
305 AssertMsgFailed(("Configuration error: query for \"DevicePath\" string returned %Rra.\n", rc));
306 return rc;
307 }
308
309 /*
310 * Open the device
311 */
312 rc = RTFileOpen(&pThis->FileDevice, pThis->pszDevicePath, RTFILE_O_OPEN | RTFILE_O_READWRITE);
313 if (RT_FAILURE(rc))
314 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("Parallel#%d could not open '%s'"),
315 pDrvIns->iInstance, pThis->pszDevicePath);
316
317 /*
318 * Try to get exclusive access to parallel port
319 */
320 rc = ioctl(pThis->FileDevice, PPEXCL);
321 if (rc < 0)
322 return PDMDrvHlpVMSetError(pDrvIns, RTErrConvertFromErrno(errno), RT_SRC_POS,
323 N_("Parallel#%d could not get exclusive access for parallel port '%s'"
324 "Be sure that no other process or driver accesses this port"),
325 pDrvIns->iInstance, pThis->pszDevicePath);
326
327 /*
328 * Claim the parallel port
329 */
330 rc = ioctl(pThis->FileDevice, PPCLAIM);
331 if (rc < 0)
332 return PDMDrvHlpVMSetError(pDrvIns, RTErrConvertFromErrno(errno), RT_SRC_POS,
333 N_("Parallel#%d could not claim parallel port '%s'"
334 "Be sure that no other process or driver accesses this port"),
335 pDrvIns->iInstance, pThis->pszDevicePath);
336
337 /*
338 * Get the IHostParallelPort interface of the above driver/device.
339 */
340 pThis->pDrvHostParallelPort = (PPDMIHOSTPARALLELPORT)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_HOST_PARALLEL_PORT);
341 if (!pThis->pDrvHostParallelPort)
342 return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE, RT_SRC_POS, N_("Parallel#%d has no parallel port interface above"),
343 pDrvIns->iInstance);
344
345 /*
346 * Create wakeup pipe.
347 */
348 int aFDs[2];
349 if (pipe(aFDs) != 0)
350 {
351 int rc = RTErrConvertFromErrno(errno);
352 AssertRC(rc);
353 return rc;
354 }
355 pThis->WakeupPipeR = aFDs[0];
356 pThis->WakeupPipeW = aFDs[1];
357
358 /*
359 * Start waiting for interrupts.
360 */
361 rc = PDMDrvHlpPDMThreadCreate(pDrvIns, &pThis->pMonitorThread, pThis, drvHostParallelMonitorThread, drvHostParallelWakeupMonitorThread, 0,
362 RTTHREADTYPE_IO, "ParMon");
363 if (RT_FAILURE(rc))
364 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("HostParallel#%d cannot create monitor thread"), pDrvIns->iInstance);
365
366 return VINF_SUCCESS;
367}
368
369
370/**
371 * Destruct a host parallel driver instance.
372 *
373 * Most VM resources are freed by the VM. This callback is provided so that
374 * any non-VM resources can be freed correctly.
375 *
376 * @param pDrvIns The driver instance data.
377 */
378static DECLCALLBACK(void) drvHostParallelDestruct(PPDMDRVINS pDrvIns)
379{
380 PDRVHOSTPARALLEL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTPARALLEL);
381
382 LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance));
383
384 ioctl(pThis->FileDevice, PPRELEASE);
385
386 if (pThis->WakeupPipeW != NIL_RTFILE)
387 {
388 int rc = RTFileClose(pThis->WakeupPipeW);
389 AssertRC(rc);
390 pThis->WakeupPipeW = NIL_RTFILE;
391 }
392 if (pThis->WakeupPipeR != NIL_RTFILE)
393 {
394 int rc = RTFileClose(pThis->WakeupPipeR);
395 AssertRC(rc);
396 pThis->WakeupPipeR = NIL_RTFILE;
397 }
398 if (pThis->FileDevice != NIL_RTFILE)
399 {
400 int rc = RTFileClose(pThis->FileDevice);
401 AssertRC(rc);
402 pThis->FileDevice = NIL_RTFILE;
403 }
404}
405
406/**
407 * Char driver registration record.
408 */
409const PDMDRVREG g_DrvHostParallel =
410{
411 /* u32Version */
412 PDM_DRVREG_VERSION,
413 /* szDriverName */
414 "HostParallel",
415 /* pszDescription */
416 "Parallel host driver.",
417 /* fFlags */
418 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
419 /* fClass. */
420 PDM_DRVREG_CLASS_CHAR,
421 /* cMaxInstances */
422 ~0,
423 /* cbInstance */
424 sizeof(DRVHOSTPARALLEL),
425 /* pfnConstruct */
426 drvHostParallelConstruct,
427 /* pfnDestruct */
428 drvHostParallelDestruct,
429 /* pfnIOCtl */
430 NULL,
431 /* pfnPowerOn */
432 NULL,
433 /* pfnReset */
434 NULL,
435 /* pfnSuspend */
436 NULL,
437 /* pfnResume */
438 NULL,
439 /* pfnDetach */
440 NULL,
441 /** pfnPowerOff */
442 NULL
443};
444
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