VirtualBox

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

Last change on this file since 25984 was 25984, checked in by vboxsync, 15 years ago

pdmifs.h: the penultimate batch of refactored interface ID code.

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