VirtualBox

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

Last change on this file since 33440 was 32678, checked in by vboxsync, 14 years ago

Devices/Serial, Devices/Parallel: minor fix (free alloc'd string)

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette