VirtualBox

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

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

pdmifs.h: another batch of _IID changes.

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