VirtualBox

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

Last change on this file since 62619 was 62619, checked in by vboxsync, 9 years ago

DrvHostParallel: Cleaned up some toally bonkers windows code, untested and might not work (probably never did).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 36.3 KB
Line 
1/* $Id: DrvHostParallel.cpp 62619 2016-07-28 14:18:00Z vboxsync $ */
2/** @file
3 * VirtualBox Host Parallel Port Driver.
4 *
5 * Initial Linux-only code contributed by: Alexander Eichner
6 */
7
8/*
9 * Copyright (C) 2006-2016 Oracle Corporation
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
20
21/*********************************************************************************************************************************
22* Header Files *
23*********************************************************************************************************************************/
24#define LOG_GROUP LOG_GROUP_DRV_HOST_PARALLEL
25#include <VBox/vmm/pdmdrv.h>
26#include <VBox/vmm/pdmthread.h>
27#include <iprt/asm.h>
28#include <iprt/assert.h>
29#include <iprt/file.h>
30#include <iprt/pipe.h>
31#include <iprt/semaphore.h>
32#include <iprt/stream.h>
33#include <iprt/uuid.h>
34#include <iprt/cdefs.h>
35#include <iprt/ctype.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/** @def VBOX_WITH_WIN_PARPORT_SUP *
50 * Indicates whether to use the generic direct hardware access or host specific
51 * code to access the parallel port.
52 */
53#if defined(RT_OS_LINUX)
54# undef VBOX_WITH_WIN_PARPORT_SUP
55#elif defined(RT_OS_WINDOWS)
56#else
57# error "Not ported"
58#endif
59
60#if defined(VBOX_WITH_WIN_PARPORT_SUP) && defined(IN_RING0)
61# include <iprt/asm-amd64-x86.h>
62#endif
63
64#if defined(VBOX_WITH_WIN_PARPORT_SUP) && defined(IN_RING3)
65# include <Windows.h>
66# include <setupapi.h>
67# include <cfgmgr32.h>
68# include <iprt/mem.h>
69# include <iprt/ctype.h>
70# include <iprt/path.h>
71# include <iprt/string.h>
72#endif
73
74#include "VBoxDD.h"
75
76
77/*********************************************************************************************************************************
78* Structures and Typedefs *
79*********************************************************************************************************************************/
80/**
81 * Host parallel port driver instance data.
82 * @implements PDMIHOSTPARALLELCONNECTOR
83 */
84typedef struct DRVHOSTPARALLEL
85{
86 /** Pointer to the driver instance structure. */
87 PPDMDRVINS pDrvIns;
88 /** Pointer to the driver instance. */
89 PPDMDRVINSR3 pDrvInsR3;
90 PPDMDRVINSR0 pDrvInsR0;
91 /** Pointer to the char port interface of the driver/device above us. */
92 PPDMIHOSTPARALLELPORT pDrvHostParallelPort;
93 /** Our host device interface. */
94 PDMIHOSTPARALLELCONNECTOR IHostParallelConnector;
95 /** Our host device interface. */
96 PDMIHOSTPARALLELCONNECTOR IHostParallelConnectorR3;
97 /** Device Path */
98 char *pszDevicePath;
99 /** Device Handle */
100 RTFILE hFileDevice;
101#ifndef VBOX_WITH_WIN_PARPORT_SUP
102 /** Thread waiting for interrupts. */
103 PPDMTHREAD pMonitorThread;
104 /** Wakeup pipe read end. */
105 RTPIPE hWakeupPipeR;
106 /** Wakeup pipe write end. */
107 RTPIPE hWakeupPipeW;
108 /** Current mode the parallel port is in. */
109 PDMPARALLELPORTMODE enmModeCur;
110#endif
111
112#ifdef VBOX_WITH_WIN_PARPORT_SUP
113 /** Data register. */
114 RTIOPORT PortDirectData;
115 /** Status register. */
116 RTIOPORT PortDirectStatus;
117 /** Control register. */
118 RTIOPORT PortDirectControl;
119 /** Data read result buffer. */
120 uint8_t bReadIn;
121 /** Control read result buffer. */
122 uint8_t bReadInControl;
123 /** Status read result buffer. */
124 uint8_t bReadInStatus;
125#endif /* VBOX_WITH_WIN_PARPORT_SUP */
126} DRVHOSTPARALLEL, *PDRVHOSTPARALLEL;
127
128
129/**
130 * Ring-0 operations.
131 */
132typedef enum DRVHOSTPARALLELR0OP
133{
134 /** Invalid zero value. */
135 DRVHOSTPARALLELR0OP_INVALID = 0,
136 /** Perform R0 initialization. */
137 DRVHOSTPARALLELR0OP_INITR0STUFF,
138 /** Read data. */
139 DRVHOSTPARALLELR0OP_READ,
140 /** Read status register. */
141 DRVHOSTPARALLELR0OP_READSTATUS,
142 /** Read control register. */
143 DRVHOSTPARALLELR0OP_READCONTROL,
144 /** Write data. */
145 DRVHOSTPARALLELR0OP_WRITE,
146 /** Write control register. */
147 DRVHOSTPARALLELR0OP_WRITECONTROL,
148 /** Set port direction. */
149 DRVHOSTPARALLELR0OP_SETPORTDIRECTION
150} DRVHOSTPARALLELR0OP;
151
152/** Converts a pointer to DRVHOSTPARALLEL::IHostDeviceConnector to a PDRHOSTPARALLEL. */
153#define PDMIHOSTPARALLELCONNECTOR_2_DRVHOSTPARALLEL(pInterface) ( (PDRVHOSTPARALLEL)((uintptr_t)pInterface - RT_OFFSETOF(DRVHOSTPARALLEL, CTX_SUFF(IHostParallelConnector))) )
154
155
156/*********************************************************************************************************************************
157* Defined Constants And Macros *
158*********************************************************************************************************************************/
159#define CTRL_REG_OFFSET 2
160#define STATUS_REG_OFFSET 1
161#define LPT_CONTROL_ENABLE_BIDIRECT 0x20
162
163
164
165#ifdef VBOX_WITH_WIN_PARPORT_SUP
166# ifdef IN_RING0
167
168/**
169 * R0 mode function to write byte value to data port.
170 *
171 * @returns VBox status code.
172 * @param pDrvIns Driver instance.
173 * @param u64Arg Data to be written to data register.
174 *
175 */
176static int drvR0HostParallelReqWrite(PPDMDRVINS pDrvIns, uint64_t u64Arg)
177{
178 PDRVHOSTPARALLEL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTPARALLEL);
179 LogFlowFunc(("write to data port=%#x val=%#x\n", pThis->PortDirectData, u64Arg));
180 ASMOutU8(pThis->PortDirectData, (uint8_t)(u64Arg));
181 return VINF_SUCCESS;
182}
183
184/**
185 * R0 mode function to write byte value to parallel port control register.
186 *
187 * @returns VBox status code.
188 * @param pDrvIns Driver instance.
189 * @param u64Arg Data to be written to control register.
190 */
191static int drvR0HostParallelReqWriteControl(PPDMDRVINS pDrvIns, uint64_t u64Arg)
192{
193 PDRVHOSTPARALLEL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTPARALLEL);
194 LogFlowFunc(("write to ctrl port=%#x val=%#x\n", pThis->PortDirectControl, u64Arg));
195 ASMOutU8(pThis->PortDirectControl, (uint8_t)(u64Arg));
196 return VINF_SUCCESS;
197}
198
199/**
200 * R0 mode function to ready byte value from the parallel port data register.
201 *
202 * @returns VBox status code.
203 * @param pDrvIns Driver instance.
204 */
205static int drvR0HostParallelReqRead(PPDMDRVINS pDrvIns)
206{
207 PDRVHOSTPARALLEL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTPARALLEL);
208 uint8_t u8Data = ASMInU8(pThis->PortDirectData);
209 LogFlowFunc(("read from data port=%#x val=%#x\n", pThis->PortDirectData, u8Data));
210 pThis->bReadIn = u8Data;
211 return VINF_SUCCESS;
212}
213
214/**
215 * R0 mode function to ready byte value from the parallel port control register.
216 *
217 * @returns VBox status code.
218 * @param pDrvIns Driver instance.
219 */
220static int drvR0HostParallelReqReadControl(PPDMDRVINS pDrvIns)
221{
222 PDRVHOSTPARALLEL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTPARALLEL);
223 uint8_t u8Data = ASMInU8(pThis->PortDirectControl);
224 LogFlowFunc(("read from ctrl port=%#x val=%#x\n", pThis->PortDirectControl, u8Data));
225 pThis->bReadInControl = u8Data;
226 return VINF_SUCCESS;
227}
228
229/**
230 * R0 mode function to ready byte value from the parallel port status register.
231 *
232 * @returns VBox status code.
233 * @param pDrvIns Driver instance.
234 */
235static int drvR0HostParallelReqReadStatus(PPDMDRVINS pDrvIns)
236{
237 PDRVHOSTPARALLEL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTPARALLEL);
238 uint8_t u8Data = ASMInU8(pThis->PortDirectStatus);
239 LogFlowFunc(("read from status port=%#x val=%#x\n", pThis->PortDirectStatus, u8Data));
240 pThis->bReadInStatus = u8Data;
241 return VINF_SUCCESS;
242}
243
244/**
245 * R0 mode function to set the direction of parallel port -
246 * operate in bidirectional mode or single direction.
247 *
248 * @returns VBox status code.
249 * @param pDrvIns Driver instance.
250 * @param u64Arg Mode.
251 */
252static int drvR0HostParallelReqSetPortDir(PPDMDRVINS pDrvIns, uint64_t u64Arg)
253{
254 PDRVHOSTPARALLEL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTPARALLEL);
255
256 uint8_t bCtl = ASMInU8(pThis->PortDirectControl);
257 if (u64Arg)
258 bCtl |= LPT_CONTROL_ENABLE_BIDIRECT; /* enable input direction */
259 else
260 bCtl &= ~LPT_CONTROL_ENABLE_BIDIRECT; /* disable input direction */
261 ASMOutU8(pThis->PortDirectControl, bCtl);
262
263 return VINF_SUCCESS;
264}
265
266/**
267 * @interface_method_impl{FNPDMDRVREQHANDLERR0}
268 */
269PDMBOTHCBDECL(int) drvR0HostParallelReqHandler(PPDMDRVINS pDrvIns, uint32_t uOperation, uint64_t u64Arg)
270{
271 int rc;
272
273 LogFlowFuncEnter();
274 switch ((DRVHOSTPARALLELR0OP)uOperation)
275 {
276 case DRVHOSTPARALLELR0OP_READ:
277 rc = drvR0HostParallelReqRead(pDrvIns);
278 break;
279 case DRVHOSTPARALLELR0OP_READSTATUS:
280 rc = drvR0HostParallelReqReadStatus(pDrvIns);
281 break;
282 case DRVHOSTPARALLELR0OP_READCONTROL:
283 rc = drvR0HostParallelReqReadControl(pDrvIns);
284 break;
285 case DRVHOSTPARALLELR0OP_WRITE:
286 rc = drvR0HostParallelReqWrite(pDrvIns, u64Arg);
287 break;
288 case DRVHOSTPARALLELR0OP_WRITECONTROL:
289 rc = drvR0HostParallelReqWriteControl(pDrvIns, u64Arg);
290 break;
291 case DRVHOSTPARALLELR0OP_SETPORTDIRECTION:
292 rc = drvR0HostParallelReqSetPortDir(pDrvIns, u64Arg);
293 break;
294 default: /* not supported */
295 rc = VERR_NOT_SUPPORTED;
296 }
297 LogFlowFuncLeave();
298 return rc;
299}
300
301# endif /* IN_RING0 */
302#endif /* VBOX_WITH_WIN_PARPORT_SUP */
303
304#ifdef IN_RING3
305# ifdef VBOX_WITH_WIN_PARPORT_SUP
306
307/**
308 * Find IO port range for the parallel port and return the lower address.
309 *
310 * @returns Parallel base I/O port.
311 * @param DevInst Device instance (dword/handle) for the parallel port.
312 */
313static RTIOPORT drvHostParallelGetWinHostIoPortsSub(const DEVINST DevInst)
314{
315 RTIOPORT PortBase = 0;
316
317 /* Get handle of the first logical configuration. */
318 LOG_CONF hFirstLogConf;
319 short wHeaderSize = sizeof(IO_DES);
320 CONFIGRET rcCm = CM_Get_First_Log_Conf(&hFirstLogConf, DevInst, ALLOC_LOG_CONF);
321 if (rcCm != CR_SUCCESS)
322 rcCm = CM_Get_First_Log_Conf(&hFirstLogConf, DevInst, BOOT_LOG_CONF);
323 if (rcCm == CR_SUCCESS)
324 {
325 /*
326 * This loop is based on the "fact" that only one I/O resource is assigned
327 * to the LPT port. Should there ever be multiple resources, we'll pick
328 * the last one for some silly reason.
329 */
330
331 /* Get the first resource descriptor handle. */
332 LOG_CONF hCurLogConf = 0;
333 rcCm = CM_Get_Next_Res_Des(&hCurLogConf, hFirstLogConf, ResType_IO, 0, 0);
334 if (rcCm == CR_SUCCESS)
335 {
336 for (;;)
337 {
338 ULONG cbData;
339 rcCm = CM_Get_Res_Des_Data_Size(&cbData, hCurLogConf, 0);
340 if (rcCm != CR_SUCCESS)
341 cbData = 0;
342 cbData = RT_MAX(cbData, sizeof(IO_DES));
343 IO_DES *pIoDesc = (IO_DES *)RTMemAllocZ(cbData);
344 if (pIoDesc)
345 {
346 rcCm = CM_Get_Res_Des_Data(hCurLogConf, pIoDesc, cbData, 0L);
347 if (rcCm == CR_SUCCESS)
348 {
349 LogRel(("drvHostParallelGetWinHostIoPortsSub: Count=%#u Type=%#x Base=%#RX64 End=%#RX64 Flags=%#x\n",
350 pIoDesc->IOD_Count, pIoDesc->IOD_Type, (uint64_t)pIoDesc->IOD_Alloc_Base,
351 (uint64_t)pIoDesc->IOD_Alloc_End, pIoDesc->IOD_DesFlags));
352 PortBase = (RTIOPORT)pIoDesc->IOD_Alloc_Base;
353 }
354 else
355 LogRel(("drvHostParallelGetWinHostIoPortsSub: CM_Get_Res_Des_Data(,,%u,0) failed: %u\n", cbData, rcCm));
356 RTMemFree(pIoDesc);
357 }
358 else
359 LogRel(("drvHostParallelGetWinHostIoPortsSub: failed to allocate %#x bytes\n", cbData));
360
361 /* Next */
362 RES_DES hFreeResDesc = hCurLogConf;
363 rcCm = CM_Get_Next_Res_Des(&hCurLogConf, hCurLogConf, ResType_IO, 0, 0);
364 CM_Free_Res_Des_Handle(hFreeResDesc);
365 if (rcCm != CR_SUCCESS)
366 {
367 if (rcCm != CR_NO_MORE_RES_DES)
368 LogRel(("drvHostParallelGetWinHostIoPortsSub: CM_Get_Next_Res_Des failed: %u\n", rcCm));
369 break;
370 }
371 }
372 }
373 else
374 LogRel(("drvHostParallelGetWinHostIoPortsSub: Initial CM_Get_Next_Res_Des failed: %u\n", rcCm));
375 CM_Free_Log_Conf_Handle(hFirstLogConf);
376 }
377 LogFlowFunc(("return PortBase=%#x", PortBase));
378 return PortBase;
379}
380
381/**
382 * Get Parallel port address and update the shared data structure.
383 *
384 * @returns VBox status code.
385 * @param pThis The host parallel port instance data.
386 */
387static int drvHostParallelGetWinHostIoPorts(PDRVHOSTPARALLEL pThis)
388{
389 /*
390 * Assume the host device path is on the form "\\.\PIPE\LPT1", then get the "LPT1" part.
391 */
392 const char * const pszCfgPortName = RTPathFilename(pThis->pszDevicePath);
393 AssertReturn(pszCfgPortName, VERR_INTERNAL_ERROR_3);
394 size_t const cchCfgPortName = strlen(pszCfgPortName);
395 if ( cchCfgPortName != 4
396 || RTStrNICmp(pszCfgPortName, "LPT", 3) != 0
397 || !RT_C_IS_DIGIT(pszCfgPortName[3]) )
398 {
399 LogRel(("drvHostParallelGetWinHostIoPorts: The configured device name '%s' is not on the expected 'LPTx' form!\n",
400 pszCfgPortName));
401 return VERR_INVALID_NAME;
402 }
403
404 /*
405 * Get a list of devices then enumerate it looking for the LPT port we're using.
406 */
407 HDEVINFO hDevInfo = SetupDiGetClassDevs(NULL, 0, 0, DIGCF_PRESENT | DIGCF_ALLCLASSES);
408 if (hDevInfo == INVALID_HANDLE_VALUE)
409 {
410 DWORD dwErr = GetLastError();
411 LogRel(("drvHostParallelGetWinHostIoPorts: SetupDiGetClassDevs failed: %u\n", dwErr));
412 return RTErrConvertFromWin32(dwErr);
413 }
414
415 int rc = VINF_SUCCESS;
416 char *pszBuf = NULL;
417 DWORD cbBuf = 0;
418 for (int32_t idxDevInfo = 0;; idxDevInfo++)
419 {
420 /*
421 * Query the next device info.
422 */
423 SP_DEVINFO_DATA DeviceInfoData;
424 DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
425 if (!SetupDiEnumDeviceInfo(hDevInfo, idxDevInfo, &DeviceInfoData))
426 {
427 DWORD dwErr = GetLastError();
428 if (dwErr != ERROR_NO_MORE_ITEMS && dwErr != NO_ERROR)
429 {
430 LogRel(("drvHostParallelGetWinHostIoPorts: SetupDiEnumDeviceInfo failed: %u\n", dwErr));
431 rc = RTErrConvertFromWin32(dwErr);
432 }
433 break;
434 }
435
436 /* Get the friendly name of the device. */
437 DWORD dwDataType;
438 DWORD cbBufActual;
439 BOOL fOk;
440 while (!(fOk = SetupDiGetDeviceRegistryProperty(hDevInfo, &DeviceInfoData, SPDRP_FRIENDLYNAME,
441 &dwDataType, (PBYTE)pszBuf, cbBuf, &cbBufActual)))
442 {
443 DWORD dwErr = GetLastError();
444 if (dwErr == ERROR_INSUFFICIENT_BUFFER)
445 {
446 LogFlow(("ERROR_INSUFF_BUFF = %d. dwBufSz = %d\n", dwErr, cbBuf));
447 void *pvNew = RTMemRealloc(pszBuf, RT_MAX(RT_ALIGN_Z(cbBufActual + 16, 64), 256));
448 if (pvNew)
449 pszBuf = (char *)pvNew;
450 else
451 {
452 LogFlow(("GetDevProp Error = %d & cbBufActual = %d\n", dwErr, cbBufActual));
453 break;
454 }
455 }
456 else
457 {
458 /* No need to bother about this error (in most cases its errno=13,
459 * INVALID_DATA . Just break from here and proceed to next device
460 * enumerated item
461 */
462 LogFlow(("GetDevProp Error = %d & cbBufActual = %d\n", dwErr, cbBufActual));
463 break;
464 }
465 }
466 if ( fOk
467 && pszBuf)
468 {
469 pszBuf[cbBuf - 1] = '\0';
470
471 /*
472 * Does this look like the port name we're looking for.
473 *
474 * We're expecting either "Parallel Port (LPT1)" or just "LPT1", though we'll make do
475 * with anything that includes the name we're looking for as a separate word.
476 */
477 char *pszMatch;
478 do
479 pszMatch = RTStrIStr(pszBuf, pszCfgPortName);
480 while ( pszMatch != NULL
481 && !( ( pszMatch == pszBuf
482 || pszMatch[-1] == '('
483 || RT_C_IS_BLANK(pszMatch[-1]))
484 && ( pszMatch[cchCfgPortName] == '\0'
485 || pszMatch[cchCfgPortName] == ')'
486 || RT_C_IS_BLANK(pszMatch[cchCfgPortName])) ) );
487 if (pszMatch != NULL)
488 {
489 RTIOPORT Port = drvHostParallelGetWinHostIoPortsSub(DeviceInfoData.DevInst);
490 if (Port != 0)
491 {
492 pThis->PortDirectData = Port;
493 pThis->PortDirectControl = Port + CTRL_REG_OFFSET;
494 pThis->PortDirectStatus = Port + STATUS_REG_OFFSET;
495 break;
496 }
497 LogRel(("drvHostParallelGetWinHostIoPorts: Addr not found for '%s'\n", pszBuf));
498 }
499 }
500 }
501
502 /* Cleanup. */
503 RTMemFree(pszBuf);
504 SetupDiDestroyDeviceInfoList(hDevInfo);
505 return rc;
506
507}
508# endif /* VBOX_WITH_WIN_PARPORT_SUP */
509
510/**
511 * Changes the current mode of the host parallel port.
512 *
513 * @returns VBox status code.
514 * @param pThis The host parallel port instance data.
515 * @param enmMode The mode to change the port to.
516 */
517static int drvHostParallelSetMode(PDRVHOSTPARALLEL pThis, PDMPARALLELPORTMODE enmMode)
518{
519 int iMode = 0;
520 int rc = VINF_SUCCESS;
521 LogFlowFunc(("mode=%d\n", enmMode));
522
523# ifndef VBOX_WITH_WIN_PARPORT_SUP
524 int rcLnx;
525 if (pThis->enmModeCur != enmMode)
526 {
527 switch (enmMode)
528 {
529 case PDM_PARALLEL_PORT_MODE_SPP:
530 iMode = IEEE1284_MODE_COMPAT;
531 break;
532 case PDM_PARALLEL_PORT_MODE_EPP_DATA:
533 iMode = IEEE1284_MODE_EPP | IEEE1284_DATA;
534 break;
535 case PDM_PARALLEL_PORT_MODE_EPP_ADDR:
536 iMode = IEEE1284_MODE_EPP | IEEE1284_ADDR;
537 break;
538 case PDM_PARALLEL_PORT_MODE_ECP:
539 case PDM_PARALLEL_PORT_MODE_INVALID:
540 default:
541 return VERR_NOT_SUPPORTED;
542 }
543
544 rcLnx = ioctl(RTFileToNative(pThis->hFileDevice), PPSETMODE, &iMode);
545 if (RT_UNLIKELY(rcLnx < 0))
546 rc = RTErrConvertFromErrno(errno);
547 else
548 pThis->enmModeCur = enmMode;
549 }
550
551 return rc;
552# else /* VBOX_WITH_WIN_PARPORT_SUP */
553 return VINF_SUCCESS;
554# endif /* VBOX_WITH_WIN_PARPORT_SUP */
555}
556
557/* -=-=-=-=- IBase -=-=-=-=- */
558
559/**
560 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
561 */
562static DECLCALLBACK(void *) drvHostParallelQueryInterface(PPDMIBASE pInterface, const char *pszIID)
563{
564 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
565 PDRVHOSTPARALLEL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTPARALLEL);
566
567 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
568 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTPARALLELCONNECTOR, &pThis->CTX_SUFF(IHostParallelConnector));
569 return NULL;
570}
571
572
573/* -=-=-=-=- IHostDeviceConnector -=-=-=-=- */
574
575/**
576 * @interface_method_impl{PDMIHOSTPARALLELCONNECTOR,pfnWrite}
577 */
578static DECLCALLBACK(int) drvHostParallelWrite(PPDMIHOSTPARALLELCONNECTOR pInterface, const void *pvBuf, size_t cbWrite, PDMPARALLELPORTMODE enmMode)
579{
580 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
581 //PDRVHOSTPARALLEL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTPARALLEL);
582 PDRVHOSTPARALLEL pThis = RT_FROM_MEMBER(pInterface, DRVHOSTPARALLEL, CTX_SUFF(IHostParallelConnector));
583 int rc = VINF_SUCCESS;
584 int rcLnx = 0;
585
586 LogFlowFunc(("pvBuf=%#p cbWrite=%d\n", pvBuf, cbWrite));
587
588 rc = drvHostParallelSetMode(pThis, enmMode);
589 if (RT_FAILURE(rc))
590 return rc;
591# ifndef VBOX_WITH_WIN_PARPORT_SUP
592 if (enmMode == PDM_PARALLEL_PORT_MODE_SPP)
593 {
594 /* Set the data lines directly. */
595 rcLnx = ioctl(RTFileToNative(pThis->hFileDevice), PPWDATA, pvBuf);
596 }
597 else
598 {
599 /* Use write interface. */
600 rcLnx = write(RTFileToNative(pThis->hFileDevice), pvBuf, cbWrite);
601 }
602 if (RT_UNLIKELY(rcLnx < 0))
603 rc = RTErrConvertFromErrno(errno);
604# else /* VBOX_WITH_WIN_PARPORT_SUP */
605 if (pThis->PortDirectData != 0)
606 {
607 for (size_t i = 0; i < cbWrite; i++)
608 {
609 uint64_t u64Data = (uint8_t) *((uint8_t *)(pvBuf) + i);
610 LogFlowFunc(("calling R0 to write to parallel port, data=%#x\n", u64Data));
611 rc = PDMDrvHlpCallR0(pThis->CTX_SUFF(pDrvIns), DRVHOSTPARALLELR0OP_WRITE, u64Data);
612 AssertRC(rc);
613 }
614 }
615# endif /* VBOX_WITH_WIN_PARPORT_SUP */
616 return rc;
617}
618
619/**
620 * @interface_method_impl{PDMIHOSTPARALLELCONNECTOR,pfnRead}
621 */
622static DECLCALLBACK(int) drvHostParallelRead(PPDMIHOSTPARALLELCONNECTOR pInterface, void *pvBuf, size_t cbRead, PDMPARALLELPORTMODE enmMode)
623{
624 PDRVHOSTPARALLEL pThis = RT_FROM_MEMBER(pInterface, DRVHOSTPARALLEL, CTX_SUFF(IHostParallelConnector));
625 int rc = VINF_SUCCESS;
626
627# ifndef VBOX_WITH_WIN_PARPORT_SUP
628 int rcLnx = 0;
629 LogFlowFunc(("pvBuf=%#p cbRead=%d\n", pvBuf, cbRead));
630
631 rc = drvHostParallelSetMode(pThis, enmMode);
632 if (RT_FAILURE(rc))
633 return rc;
634
635 if (enmMode == PDM_PARALLEL_PORT_MODE_SPP)
636 {
637 /* Set the data lines directly. */
638 rcLnx = ioctl(RTFileToNative(pThis->hFileDevice), PPWDATA, pvBuf);
639 }
640 else
641 {
642 /* Use write interface. */
643 rcLnx = read(RTFileToNative(pThis->hFileDevice), pvBuf, cbRead);
644 }
645 if (RT_UNLIKELY(rcLnx < 0))
646 rc = RTErrConvertFromErrno(errno);
647# else /* VBOX_WITH_WIN_PARPORT_SUP */
648 if (pThis->PortDirectData != 0)
649 {
650 uint8_t *pabBuf = (uint8_t *)pvBuf;
651 memset(pabBuf, 0, cbRead);
652 for (size_t i = 0; i < cbRead; i++)
653 {
654 LogFlowFunc(("calling R0 to read from parallel port\n"));
655 int rc = PDMDrvHlpCallR0(pThis->CTX_SUFF(pDrvIns), DRVHOSTPARALLELR0OP_READ, 0);
656 AssertRC(rc);
657 pabBuf[i] = (uint8_t)pThis->bReadIn;
658 }
659 }
660# endif /* VBOX_WITH_WIN_PARPORT_SUP */
661 return rc;
662}
663
664/**
665 * @interface_method_impl{PDMIHOSTPARALLELCONNECTOR,pfnSetPortDirection}
666 */
667static DECLCALLBACK(int) drvHostParallelSetPortDirection(PPDMIHOSTPARALLELCONNECTOR pInterface, bool fForward)
668{
669 PDRVHOSTPARALLEL pThis = RT_FROM_MEMBER(pInterface, DRVHOSTPARALLEL, CTX_SUFF(IHostParallelConnector));
670 int rc = VINF_SUCCESS;
671 int iMode = 0;
672 if (!fForward)
673 iMode = 1;
674# ifndef VBOX_WITH_WIN_PARPORT_SUP
675 int rcLnx = ioctl(RTFileToNative(pThis->hFileDevice), PPDATADIR, &iMode);
676 if (RT_UNLIKELY(rcLnx < 0))
677 rc = RTErrConvertFromErrno(errno);
678
679# else /* VBOX_WITH_WIN_PARPORT_SUP */
680 if (pThis->PortDirectData != 0)
681 {
682 LogFlowFunc(("calling R0 to write CTRL, data=%#x\n", iMode));
683 rc = PDMDrvHlpCallR0(pThis->CTX_SUFF(pDrvIns), DRVHOSTPARALLELR0OP_SETPORTDIRECTION, iMode);
684 AssertRC(rc);
685 }
686# endif /* VBOX_WITH_WIN_PARPORT_SUP */
687 return rc;
688}
689
690/**
691 * @interface_method_impl{PDMIHOSTPARALLELCONNECTOR,pfnWriteControl}
692 */
693static DECLCALLBACK(int) drvHostParallelWriteControl(PPDMIHOSTPARALLELCONNECTOR pInterface, uint8_t fReg)
694{
695 PDRVHOSTPARALLEL pThis = RT_FROM_MEMBER(pInterface, DRVHOSTPARALLEL, CTX_SUFF(IHostParallelConnector));
696 int rc = VINF_SUCCESS;
697
698 LogFlowFunc(("fReg=%#x\n", fReg));
699# ifndef VBOX_WITH_WIN_PARPORT_SUP
700 int rcLnx = ioctl(RTFileToNative(pThis->hFileDevice), PPWCONTROL, &fReg);
701 if (RT_UNLIKELY(rcLnx < 0))
702 rc = RTErrConvertFromErrno(errno);
703# else /* VBOX_WITH_WIN_PARPORT_SUP */
704 if (pThis->PortDirectData != 0)
705 {
706 LogFlowFunc(("calling R0 to write CTRL, data=%#x\n", fReg));
707 rc = PDMDrvHlpCallR0(pThis->CTX_SUFF(pDrvIns), DRVHOSTPARALLELR0OP_WRITECONTROL, fReg);
708 AssertRC(rc);
709 }
710# endif /* VBOX_WITH_WIN_PARPORT_SUP */
711 return rc;
712}
713
714
715/**
716 * @interface_method_impl{PDMIHOSTPARALLELCONNECTOR,pfnReadControl}
717 */
718static DECLCALLBACK(int) drvHostParallelReadControl(PPDMIHOSTPARALLELCONNECTOR pInterface, uint8_t *pfReg)
719{
720 PDRVHOSTPARALLEL pThis = RT_FROM_MEMBER(pInterface, DRVHOSTPARALLEL, CTX_SUFF(IHostParallelConnector));
721 int rc = VINF_SUCCESS;
722
723# ifndef VBOX_WITH_WIN_PARPORT_SUP
724 uint8_t fReg = 0;
725 int rcLnx = ioctl(RTFileToNative(pThis->hFileDevice), PPRCONTROL, &fReg);
726 if (RT_UNLIKELY(rcLnx < 0))
727 rc = RTErrConvertFromErrno(errno);
728 else
729 {
730 LogFlowFunc(("fReg=%#x\n", fReg));
731 *pfReg = fReg;
732 }
733# else /* VBOX_WITH_WIN_PARPORT_SUP */
734 *pfReg = 0; /* Initialize the buffer*/
735 if (pThis->PortDirectData != 0)
736 {
737 LogFlowFunc(("calling R0 to read control from parallel port\n"));
738 rc = PDMDrvHlpCallR0(pThis-> CTX_SUFF(pDrvIns), DRVHOSTPARALLELR0OP_READCONTROL, 0);
739 AssertRC(rc);
740 *pfReg = pThis->bReadInControl;
741 }
742# endif /* VBOX_WITH_WIN_PARPORT_SUP */
743 return rc;
744}
745
746/**
747 * @interface_method_impl{PDMIHOSTPARALLELCONNECTOR,pfnReadStatus}
748 */
749static DECLCALLBACK(int) drvHostParallelReadStatus(PPDMIHOSTPARALLELCONNECTOR pInterface, uint8_t *pfReg)
750{
751 PDRVHOSTPARALLEL pThis = RT_FROM_MEMBER(pInterface, DRVHOSTPARALLEL, CTX_SUFF(IHostParallelConnector));
752 int rc = VINF_SUCCESS;
753# ifndef VBOX_WITH_WIN_PARPORT_SUP
754 uint8_t fReg = 0;
755 int rcLnx = ioctl(RTFileToNative(pThis->hFileDevice), PPRSTATUS, &fReg);
756 if (RT_UNLIKELY(rcLnx < 0))
757 rc = RTErrConvertFromErrno(errno);
758 else
759 {
760 LogFlowFunc(("fReg=%#x\n", fReg));
761 *pfReg = fReg;
762 }
763# else /* VBOX_WITH_WIN_PARPORT_SUP */
764 *pfReg = 0; /* Intialize the buffer. */
765 if (pThis->PortDirectData != 0)
766 {
767 LogFlowFunc(("calling R0 to read status from parallel port\n"));
768 rc = PDMDrvHlpCallR0(pThis->CTX_SUFF(pDrvIns), DRVHOSTPARALLELR0OP_READSTATUS, 0);
769 AssertRC(rc);
770 *pfReg = pThis->bReadInStatus;
771 }
772# endif /* VBOX_WITH_WIN_PARPORT_SUP */
773 return rc;
774}
775
776# ifndef VBOX_WITH_WIN_PARPORT_SUP
777
778static DECLCALLBACK(int) drvHostParallelMonitorThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
779{
780 PDRVHOSTPARALLEL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTPARALLEL);
781 struct pollfd aFDs[2];
782
783 /*
784 * We can wait for interrupts using poll on linux hosts.
785 */
786 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
787 {
788 int rc;
789
790 aFDs[0].fd = RTFileToNative(pThis->hFileDevice);
791 aFDs[0].events = POLLIN;
792 aFDs[0].revents = 0;
793 aFDs[1].fd = RTPipeToNative(pThis->hWakeupPipeR);
794 aFDs[1].events = POLLIN | POLLERR | POLLHUP;
795 aFDs[1].revents = 0;
796 rc = poll(aFDs, RT_ELEMENTS(aFDs), -1);
797 if (rc < 0)
798 {
799 AssertMsgFailed(("poll failed with rc=%d\n", RTErrConvertFromErrno(errno)));
800 return RTErrConvertFromErrno(errno);
801 }
802
803 if (pThread->enmState != PDMTHREADSTATE_RUNNING)
804 break;
805 if (rc > 0 && aFDs[1].revents)
806 {
807 if (aFDs[1].revents & (POLLHUP | POLLERR | POLLNVAL))
808 break;
809 /* notification to terminate -- drain the pipe */
810 char ch;
811 size_t cbRead;
812 RTPipeRead(pThis->hWakeupPipeR, &ch, 1, &cbRead);
813 continue;
814 }
815
816 /* Interrupt occurred. */
817 rc = pThis->pDrvHostParallelPort->pfnNotifyInterrupt(pThis->pDrvHostParallelPort);
818 AssertRC(rc);
819 }
820
821 return VINF_SUCCESS;
822}
823
824/**
825 * Unblock the monitor thread so it can respond to a state change.
826 *
827 * @returns a VBox status code.
828 * @param pDrvIns The driver instance.
829 * @param pThread The send thread.
830 */
831static DECLCALLBACK(int) drvHostParallelWakeupMonitorThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
832{
833 PDRVHOSTPARALLEL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTPARALLEL);
834 size_t cbIgnored;
835 return RTPipeWrite(pThis->hWakeupPipeW, "", 1, &cbIgnored);
836}
837
838# endif /* VBOX_WITH_WIN_PARPORT_SUP */
839
840/**
841 * Destruct a host parallel driver instance.
842 *
843 * Most VM resources are freed by the VM. This callback is provided so that
844 * any non-VM resources can be freed correctly.
845 *
846 * @param pDrvIns The driver instance data.
847 */
848static DECLCALLBACK(void) drvHostParallelDestruct(PPDMDRVINS pDrvIns)
849{
850 PDRVHOSTPARALLEL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTPARALLEL);
851 LogFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
852 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
853
854#ifndef VBOX_WITH_WIN_PARPORT_SUP
855 if (pThis->hFileDevice != NIL_RTFILE)
856 ioctl(RTFileToNative(pThis->hFileDevice), PPRELEASE);
857
858 if (pThis->hWakeupPipeW != NIL_RTPIPE)
859 {
860 int rc = RTPipeClose(pThis->hWakeupPipeW); AssertRC(rc);
861 pThis->hWakeupPipeW = NIL_RTPIPE;
862 }
863
864 if (pThis->hWakeupPipeR != NIL_RTPIPE)
865 {
866 int rc = RTPipeClose(pThis->hWakeupPipeR); AssertRC(rc);
867 pThis->hWakeupPipeR = NIL_RTPIPE;
868 }
869
870 if (pThis->hFileDevice != NIL_RTFILE)
871 {
872 int rc = RTFileClose(pThis->hFileDevice); AssertRC(rc);
873 pThis->hFileDevice = NIL_RTFILE;
874 }
875
876 if (pThis->pszDevicePath)
877 {
878 MMR3HeapFree(pThis->pszDevicePath);
879 pThis->pszDevicePath = NULL;
880 }
881#endif /* !VBOX_WITH_WIN_PARPORT_SUP */
882}
883
884/**
885 * Construct a host parallel driver instance.
886 *
887 * @copydoc FNPDMDRVCONSTRUCT
888 */
889static DECLCALLBACK(int) drvHostParallelConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
890{
891 PDRVHOSTPARALLEL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTPARALLEL);
892 LogFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
893
894 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
895
896 /*
897 * Init basic data members and interfaces.
898 *
899 * Must be done before returning any failure because we've got a destructor.
900 */
901 pThis->hFileDevice = NIL_RTFILE;
902#ifndef VBOX_WITH_WIN_PARPORT_SUP
903 pThis->hWakeupPipeR = NIL_RTPIPE;
904 pThis->hWakeupPipeW = NIL_RTPIPE;
905#endif
906
907 pThis->pDrvInsR3 = pDrvIns;
908#ifdef VBOX_WITH_DRVINTNET_IN_R0
909 pThis->pDrvInsR0 = PDMDRVINS_2_R0PTR(pDrvIns);
910#endif
911
912 /* IBase. */
913 pDrvIns->IBase.pfnQueryInterface = drvHostParallelQueryInterface;
914 /* IHostParallelConnector. */
915 pThis->IHostParallelConnectorR3.pfnWrite = drvHostParallelWrite;
916 pThis->IHostParallelConnectorR3.pfnRead = drvHostParallelRead;
917 pThis->IHostParallelConnectorR3.pfnSetPortDirection = drvHostParallelSetPortDirection;
918 pThis->IHostParallelConnectorR3.pfnWriteControl = drvHostParallelWriteControl;
919 pThis->IHostParallelConnectorR3.pfnReadControl = drvHostParallelReadControl;
920 pThis->IHostParallelConnectorR3.pfnReadStatus = drvHostParallelReadStatus;
921
922 /*
923 * Validate the config.
924 */
925 if (!CFGMR3AreValuesValid(pCfg, "DevicePath\0"))
926 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES,
927 N_("Unknown host parallel configuration option, only supports DevicePath"));
928
929 /*
930 * Query configuration.
931 */
932 /* Device */
933 int rc = CFGMR3QueryStringAlloc(pCfg, "DevicePath", &pThis->pszDevicePath);
934 if (RT_FAILURE(rc))
935 {
936 AssertMsgFailed(("Configuration error: query for \"DevicePath\" string returned %Rra.\n", rc));
937 return rc;
938 }
939
940 /*
941 * Open the device
942 */
943 /** @todo exclusive access on windows? */
944 rc = RTFileOpen(&pThis->hFileDevice, pThis->pszDevicePath, RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
945 if (RT_FAILURE(rc))
946 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("Parallel#%d could not open '%s'"),
947 pDrvIns->iInstance, pThis->pszDevicePath);
948
949#ifndef VBOX_WITH_WIN_PARPORT_SUP
950 /*
951 * Try to get exclusive access to parallel port
952 */
953 rc = ioctl(RTFileToNative(pThis->hFileDevice), PPEXCL);
954 if (rc < 0)
955 return PDMDrvHlpVMSetError(pDrvIns, RTErrConvertFromErrno(errno), RT_SRC_POS,
956 N_("Parallel#%d could not get exclusive access for parallel port '%s'"
957 "Be sure that no other process or driver accesses this port"),
958 pDrvIns->iInstance, pThis->pszDevicePath);
959
960 /*
961 * Claim the parallel port
962 */
963 rc = ioctl(RTFileToNative(pThis->hFileDevice), PPCLAIM);
964 if (rc < 0)
965 return PDMDrvHlpVMSetError(pDrvIns, RTErrConvertFromErrno(errno), RT_SRC_POS,
966 N_("Parallel#%d could not claim parallel port '%s'"
967 "Be sure that no other process or driver accesses this port"),
968 pDrvIns->iInstance, pThis->pszDevicePath);
969
970 /*
971 * Get the IHostParallelPort interface of the above driver/device.
972 */
973 pThis->pDrvHostParallelPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIHOSTPARALLELPORT);
974 if (!pThis->pDrvHostParallelPort)
975 return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE, RT_SRC_POS, N_("Parallel#%d has no parallel port interface above"),
976 pDrvIns->iInstance);
977
978 /*
979 * Create wakeup pipe.
980 */
981 rc = RTPipeCreate(&pThis->hWakeupPipeR, &pThis->hWakeupPipeW, 0 /*fFlags*/);
982 AssertRCReturn(rc, rc);
983
984 /*
985 * Start in SPP mode.
986 */
987 pThis->enmModeCur = PDM_PARALLEL_PORT_MODE_INVALID;
988 rc = drvHostParallelSetMode(pThis, PDM_PARALLEL_PORT_MODE_SPP);
989 if (RT_FAILURE(rc))
990 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("HostParallel#%d cannot change mode of parallel mode to SPP"), pDrvIns->iInstance);
991
992 /*
993 * Start waiting for interrupts.
994 */
995 rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pMonitorThread, pThis, drvHostParallelMonitorThread, drvHostParallelWakeupMonitorThread, 0,
996 RTTHREADTYPE_IO, "ParMon");
997 if (RT_FAILURE(rc))
998 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("HostParallel#%d cannot create monitor thread"), pDrvIns->iInstance);
999
1000#else /* VBOX_WITH_WIN_PARPORT_SUP */
1001
1002 pThis->PortDirectData = 0;
1003 pThis->PortDirectControl = 0;
1004 pThis->PortDirectStatus = 0;
1005 rc = drvHostParallelGetWinHostIoPorts(pThis);
1006 if (RT_FAILURE(rc))
1007 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
1008 N_("HostParallel#%d: Could not get direct access to the host parallel port!! (rc=%Rrc)"),
1009 pDrvIns->iInstance, rc);
1010
1011#endif /* VBOX_WITH_WIN_PARPORT_SUP */
1012 return VINF_SUCCESS;
1013}
1014
1015
1016/**
1017 * Char driver registration record.
1018 */
1019const PDMDRVREG g_DrvHostParallel =
1020{
1021 /* u32Version */
1022 PDM_DRVREG_VERSION,
1023 /* szName */
1024 "HostParallel",
1025 /* szRCMod */
1026 "",
1027 /* szR0Mod */
1028 "VBoxDDR0.r0",
1029 /* pszDescription */
1030 "Parallel host driver.",
1031 /* fFlags */
1032# if defined(VBOX_WITH_WIN_PARPORT_SUP)
1033 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT | PDM_DRVREG_FLAGS_R0,
1034# else
1035 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1036# endif
1037 /* fClass. */
1038 PDM_DRVREG_CLASS_CHAR,
1039 /* cMaxInstances */
1040 ~0U,
1041 /* cbInstance */
1042 sizeof(DRVHOSTPARALLEL),
1043 /* pfnConstruct */
1044 drvHostParallelConstruct,
1045 /* pfnDestruct */
1046 drvHostParallelDestruct,
1047 /* pfnRelocate */
1048 NULL,
1049 /* pfnIOCtl */
1050 NULL,
1051 /* pfnPowerOn */
1052 NULL,
1053 /* pfnReset */
1054 NULL,
1055 /* pfnSuspend */
1056 NULL,
1057 /* pfnResume */
1058 NULL,
1059 /* pfnAttach */
1060 NULL,
1061 /* pfnDetach */
1062 NULL,
1063 /* pfnPowerOff */
1064 NULL,
1065 /* pfnSoftReset */
1066 NULL,
1067 /* u32EndVersion */
1068 PDM_DRVREG_VERSION
1069};
1070#endif /*IN_RING3*/
1071
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