VirtualBox

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

Last change on this file since 62956 was 62686, checked in by vboxsync, 8 years ago

Use the iprt/win/setupapi.h wrapper for setupapi.h to deal with preprocessor warnings.

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