VirtualBox

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

Last change on this file since 6223 was 6223, checked in by vboxsync, 17 years ago

We can use poll instead of select and cleanups

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