VirtualBox

source: vbox/trunk/src/VBox/Devices/Network/DrvTAPOs2.cpp@ 4624

Last change on this file since 4624 was 4372, checked in by vboxsync, 18 years ago

Finally corrected the RTFileRead, RTFileReadAt, RTFileWrite and RTFileWriteAt APIs to size_t. This was long overdue.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.0 KB
Line 
1/** $Id: DrvTAPOs2.cpp 4372 2007-08-24 21:21:51Z vboxsync $ */
2/** @file
3 * VBox network devices: OS/2 TAP network transport driver.
4 */
5
6/*
7 *
8 * Copyright (C) 2006-2007 innotek GmbH
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License as published by the Free Software Foundation,
14 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
15 * distribution. VirtualBox OSE is distributed in the hope that it will
16 * be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19/*******************************************************************************
20* Header Files *
21*******************************************************************************/
22#define LOG_GROUP LOG_GROUP_DRV_TUN
23#include <VBox/pdmdrv.h>
24
25#include <iprt/assert.h>
26#include <iprt/file.h>
27#include <iprt/string.h>
28#include <iprt/thread.h>
29#include <iprt/asm.h>
30#include <iprt/semaphore.h>
31
32#include "Builtins.h"
33
34
35
36/*******************************************************************************
37* Structures and Typedefs *
38*******************************************************************************/
39
40/**
41 * Block driver instance data.
42 */
43typedef struct DRVTAPOS2
44{
45 /** The network interface. */
46 PDMINETWORKCONNECTOR INetworkConnector;
47 /** The network interface. */
48 PPDMINETWORKPORT pPort;
49 /** Pointer to the driver instance. */
50 PPDMDRVINS pDrvIns;
51 /** TAP device file handle. */
52 RTFILE FileDevice;
53 /** Receiver thread. */
54 PPDMTHREAD pThread;
55 /** We are waiting for more receive buffers. */
56 uint32_t volatile fOutOfSpace;
57 /** Event semaphore for blocking on receive. */
58 RTSEMEVENT EventOutOfSpace;
59
60#ifdef VBOX_WITH_STATISTICS
61 /** Number of sent packets. */
62 STAMCOUNTER StatPktSent;
63 /** Number of sent bytes. */
64 STAMCOUNTER StatPktSentBytes;
65 /** Number of received packets. */
66 STAMCOUNTER StatPktRecv;
67 /** Number of received bytes. */
68 STAMCOUNTER StatPktRecvBytes;
69 /** Profiling packet transmit runs. */
70 STAMPROFILE StatTransmit;
71 /** Profiling packet receive runs. */
72 STAMPROFILEADV StatReceive;
73 STAMPROFILE StatRecvOverflows;
74#endif /* VBOX_WITH_STATISTICS */
75
76#ifdef LOG_ENABLED
77 /** The nano ts of the last transfer. */
78 uint64_t u64LastTransferTS;
79 /** The nano ts of the last receive. */
80 uint64_t u64LastReceiveTS;
81#endif
82} DRVTAPOS2, *PDRVTAPOS2;
83
84
85/** Converts a pointer to TAP::INetworkConnector to a PRDVTAP. */
86#define PDMINETWORKCONNECTOR_2_DRVTAPOS2(pInterface) ( (PDRVTAPOS2)((uintptr_t)pInterface - RT_OFFSETOF(DRVTAPOS2, INetworkConnector)) )
87
88
89/**
90 * Send data to the network.
91 *
92 * @returns VBox status code.
93 * @param pInterface Pointer to the interface structure containing the called function pointer.
94 * @param pvBuf Data to send.
95 * @param cb Number of bytes to send.
96 * @thread EMT
97 */
98static DECLCALLBACK(int) drvTAPOs2Send(PPDMINETWORKCONNECTOR pInterface, const void *pvBuf, size_t cb)
99{
100 PDRVTAPOS2 pData = PDMINETWORKCONNECTOR_2_DRVTAPOS2(pInterface);
101 STAM_COUNTER_INC(&pData->StatPktSent);
102 STAM_COUNTER_ADD(&pData->StatPktSentBytes, cb);
103 STAM_PROFILE_START(&pData->StatTransmit, a);
104
105#ifdef LOG_ENABLED
106 uint64_t u64Now = RTTimeProgramNanoTS();
107 LogFlow(("drvTAPOs2Send: %-4d bytes at %llu ns deltas: r=%llu t=%llu\n",
108 cb, u64Now, u64Now - pData->u64LastReceiveTS, u64Now - pData->u64LastTransferTS));
109 pData->u64LastTransferTS = u64Now;
110#endif
111 Log2(("drvTAPOs2Send: pvBuf=%p cb=%#x\n"
112 "%.*Vhxd\n",
113 pvBuf, cb, cb, pvBuf));
114
115 ULONG UnusedParms[10] = { 0,0,0,0, 0,0,0,0, 0,0 };
116 ULONG cbParms = sizeof(UnusedParms);
117 ULONG cbData = cb;
118 int rc = DosDevIOCtl(pData->FileDevice, PROT_CATEGORY, TAP_WRITE_PACKET,
119 &UnusedParms[0], cbParms, &cbParms,
120 pvBuf, cbData, &cbData);
121 if (rc)
122 rc = RTErrConvertFromOS2(rc);
123
124 STAM_PROFILE_STOP(&pData->StatTransmit, a);
125 AssertRC(rc);
126 return rc;
127}
128
129
130/**
131 * Set promiscuous mode.
132 *
133 * This is called when the promiscuous mode is set. This means that there doesn't have
134 * to be a mode change when it's called.
135 *
136 * @param pInterface Pointer to the interface structure containing the called function pointer.
137 * @param fPromiscuous Set if the adaptor is now in promiscuous mode. Clear if it is not.
138 * @thread EMT
139 */
140static DECLCALLBACK(void) drvTAPOs2SetPromiscuousMode(PPDMINETWORKCONNECTOR pInterface, bool fPromiscuous)
141{
142 LogFlow(("drvTAPOs2SetPromiscuousMode: fPromiscuous=%d\n", fPromiscuous));
143 /* nothing to do */
144}
145
146
147/**
148 * Notification on link status changes.
149 *
150 * @param pInterface Pointer to the interface structure containing the called function pointer.
151 * @param enmLinkState The new link state.
152 * @thread EMT
153 */
154static DECLCALLBACK(void) drvTAPOs2NotifyLinkChanged(PPDMINETWORKCONNECTOR pInterface, PDMNETWORKLINKSTATE enmLinkState)
155{
156 LogFlow(("drvNATNotifyLinkChanged: enmLinkState=%d\n", enmLinkState));
157 /** @todo take action on link down and up. Stop the polling and such like. */
158}
159
160
161/**
162 * More receive buffer has become available.
163 *
164 * This is called when the NIC frees up receive buffers.
165 *
166 * @param pInterface Pointer to the interface structure containing the called function pointer.
167 * @thread EMT
168 */
169static DECLCALLBACK(void) drvTAPOs2NotifyCanReceive(PPDMINETWORKCONNECTOR pInterface)
170{
171 PDRVTAPOS2 pData = PDMINETWORKCONNECTOR_2_DRVTAPOS2(pInterface);
172
173 LogFlow(("drvTAPOs2NotifyCanReceive:\n"));
174 /* ensure we wake up only once */
175 if (ASMAtomicXchgU32(&pData->fOutOfSpace, false))
176 RTSemEventSignal(pData->EventOutOfSpace);
177}
178
179
180/**
181 * Asynchronous I/O thread for handling receive.
182 *
183 * @returns VINF_SUCCESS (ignored).
184 * @param pDrvIns The driver instance.
185 * @param pThread The PDM thread structure.
186 */
187static DECLCALLBACK(int) drvTAPOs2AsyncIoThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
188{
189 PDRVTAPOS2 pData = PDMINS2DATA(pDrvIns, PDRVTAPOS2);
190 LogFlow(("drvTAPOs2AsyncIoThread: pData=%p\n", pData));
191 Assert(pThread->enmState == PDMTHREADSTATE_INITIALIZING);
192
193
194 /*
195 * Outer loop.
196 */
197 for (;;)
198 {
199 /*
200 *
201 */
202 PDMR3ThreadSuspend(pThread);
203 if (pThread->enmState != PDMTHREADSTATE_RESUMING)
204 break;
205
206 {
207 }
208 }
209
210
211
212
213 STAM_PROFILE_ADV_START(&pData->StatReceive, a);
214
215 /*
216 * Polling loop.
217 */
218 for (;;)
219 {
220 /*
221 * Read/wait the frame.
222 */
223 char achBuf[4096];
224 ULONG cbParm = ;
225 ULONG cbRead = 0;
226 int LanNumber;
227
228 int rc = DosDevIOCtl(pData->FileDevice, PROT_CATEGORY, TAP_CANCEL_READ,
229 &UnusedParms[0], cbParm, &cbParm,
230 &achBuf[0], cbRead, &cbRead);
231 if (rc == NO_ERROR)
232 {
233 AssertMsg(cbRead <= 1536, ("cbRead=%d\n", cbRead));
234
235 /*
236 * Wait for the device to have space for this frame.
237 */
238 size_t cbMax = pData->pPort->pfnCanReceive(pData->pPort);
239 if (cbMax < cbRead)
240 {
241 /** @todo receive overflow handling needs serious improving! */
242 STAM_PROFILE_ADV_STOP(&pData->StatReceive, a);
243 STAM_PROFILE_START(&pData->StatRecvOverflows, b);
244 while ( cbMax < cbRead
245 && pData->enmState != ASYNCSTATE_TERMINATE)
246 {
247 LogFlow(("drvTAPOs2AsyncIoThread: cbMax=%d cbRead=%d waiting...\n", cbMax, cbRead));
248#if 1
249 /* We get signalled by the network driver. 50ms is just for sanity */
250 ASMAtomicXchgU32(&pData->fOutOfSpace, true);
251 RTSemEventWait(pData->EventOutOfSpace, 50);
252#else
253 RTThreadSleep(1);
254#endif
255 cbMax = pData->pPort->pfnCanReceive(pData->pPort);
256 }
257 ASMAtomicXchgU32(&pData->fOutOfSpace, false);
258 STAM_PROFILE_STOP(&pData->StatRecvOverflows, b);
259 STAM_PROFILE_ADV_START(&pData->StatReceive, a);
260 if (pData->enmState == ASYNCSTATE_TERMINATE)
261 break;
262 }
263
264 /*
265 * Pass the data up.
266 */
267#ifdef LOG_ENABLED
268 uint64_t u64Now = RTTimeProgramNanoTS();
269 LogFlow(("drvTAPOs2AsyncIoThread: %-4d bytes at %llu ns deltas: r=%llu t=%llu\n",
270 cbRead, u64Now, u64Now - pData->u64LastReceiveTS, u64Now - pData->u64LastTransferTS));
271 pData->u64LastReceiveTS = u64Now;
272#endif
273 Log2(("drvTAPOs2AsyncIoThread: cbRead=%#x\n"
274 "%.*Vhxd\n",
275 cbRead, cbRead, achBuf));
276 STAM_COUNTER_INC(&pData->StatPktRecv);
277 STAM_COUNTER_ADD(&pData->StatPktRecvBytes, cbRead);
278 rc = pData->pPort->pfnReceive(pData->pPort, achBuf, cbRead);
279 AssertRC(rc);
280 }
281 else
282 {
283 LogFlow(("drvTAPOs2AsyncIoThread: DoDevIOCtl -> %Vrc\n", rc));
284 if (rc == VERR_INVALID_HANDLE)
285 break;
286 RTThreadYield();
287 }
288 }
289
290 LogFlow(("drvTAPOs2AsyncIoThread: returns %Vrc\n", VINF_SUCCESS));
291 STAM_PROFILE_ADV_STOP(&pData->StatReceive, a);
292 return VINF_SUCCESS;
293}
294
295
296/**
297 * Queries an interface to the driver.
298 *
299 * @returns Pointer to interface.
300 * @returns NULL if the interface was not supported by the driver.
301 * @param pInterface Pointer to this interface structure.
302 * @param enmInterface The requested interface identification.
303 * @thread Any thread.
304 */
305static DECLCALLBACK(void *) drvTAPOs2QueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface)
306{
307 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
308 PDRVTAPOS2 pData = PDMINS2DATA(pDrvIns, PDRVTAPOS2);
309 switch (enmInterface)
310 {
311 case PDMINTERFACE_BASE:
312 return &pDrvIns->IBase;
313 case PDMINTERFACE_NETWORK_CONNECTOR:
314 return &pData->INetworkConnector;
315 default:
316 return NULL;
317 }
318}
319
320
321/**
322 * Destruct a driver instance.
323 *
324 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
325 * resources can be freed correctly.
326 *
327 * @param pDrvIns The driver instance data.
328 */
329static DECLCALLBACK(void) drvTAPOs2Destruct(PPDMDRVINS pDrvIns)
330{
331 LogFlow(("drvTAPOs2Destruct\n"));
332 PDRVTAPOS2 pData = PDMINS2DATA(pDrvIns, PDRVTAPOS2);
333
334 /*
335 * Destroy the event semaphore.
336 */
337 if (pData->EventOutOfSpace != NIL_RTSEMEVENTMULTI)
338 {
339 rc = RTSemEventDestroy(pData->EventOutOfSpace);
340 AssertRC(rc);
341 pData->EventOutOfSpace = NIL_RTSEMEVENTMULTI;
342 }
343}
344
345
346/**
347 * Construct a TAP network transport driver instance.
348 *
349 * @returns VBox status.
350 * @param pDrvIns The driver instance data.
351 * If the registration structure is needed, pDrvIns->pDrvReg points to it.
352 * @param pCfgHandle Configuration node handle for the driver. Use this to obtain the configuration
353 * of the driver instance. It's also found in pDrvIns->pCfgHandle, but like
354 * iInstance it's expected to be used a bit in this function.
355 */
356static DECLCALLBACK(int) drvTAPOs2Construct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle)
357{
358 PDRVTAPOS2 pData = PDMINS2DATA(pDrvIns, PDRVTAPOS2);
359
360 /*
361 * Init the static parts.
362 */
363 pData->pDrvIns = pDrvIns;
364 pData->FileDevice = NIL_RTFILE;
365 pData->Thread = NIL_RTTHREAD;
366 pData->enmState = ASYNCSTATE_RUNNING;
367 /* IBase */
368 pDrvIns->IBase.pfnQueryInterface = drvTAPOs2QueryInterface;
369 /* INetwork */
370 pData->INetworkConnector.pfnSend = drvTAPOs2Send;
371 pData->INetworkConnector.pfnSetPromiscuousMode = drvTAPOs2SetPromiscuousMode;
372 pData->INetworkConnector.pfnNotifyLinkChanged = drvTAPOs2NotifyLinkChanged;
373 pData->INetworkConnector.pfnNotifyCanReceive = drvTAPOs2NotifyCanReceive;
374
375 /*
376 * Validate the config.
377 */
378 if (!CFGMR3AreValuesValid(pCfgHandle, "Device\0InitProg\0TermProg\0FileHandle\0"))
379 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES, "");
380
381 /*
382 * Check that no-one is attached to us.
383 */
384 int rc = pDrvIns->pDrvHlp->pfnAttach(pDrvIns, NULL);
385 if (rc != VERR_PDM_NO_ATTACHED_DRIVER)
386 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRVINS_NO_ATTACH,
387 N_("Configuration error: Cannot attach drivers to the TAP driver!"));
388
389 /*
390 * Query the network port interface.
391 */
392 pData->pPort = (PPDMINETWORKPORT)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_NETWORK_PORT);
393 if (!pData->pPort)
394 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE,
395 N_("Configuration error: The above device/driver didn't export the network port interface!"));
396
397 /*
398 * Read the configuration.
399 */
400 int32_t iFile;
401 rc = CFGMR3QueryS32(pCfgHandle, "FileHandle", &iFile);
402 if (VBOX_FAILURE(rc))
403 return PDMDRV_SET_ERROR(pDrvIns, rc,
404 N_("Configuration error: Query for \"FileHandle\" 32-bit signed integer failed!"));
405 pData->FileDevice = (RTFILE)iFile;
406 if (!RTFileIsValid(pData->FileDevice))
407 return PDMDrvHlpVMSetError(pDrvIns, VERR_INVALID_HANDLE, RT_SRC_POS,
408 N_("The TAP file handle %RTfile is not valid!"), pData->FileDevice);
409
410 /*
411 * Make sure the descriptor is non-blocking and valid.
412 *
413 * We should actually query if it's a TAP device, but I haven't
414 * found any way to do that.
415 */
416 if (fcntl(pData->FileDevice, F_SETFL, O_NONBLOCK) == -1)
417 return PDMDrvHlpVMSetError(pDrvIns, VERR_HOSTIF_IOCTL, RT_SRC_POS,
418 N_("Configuration error: Failed to configure /dev/net/tun. errno=%d"), errno);
419 Log(("drvTAPOs2Contruct: %d (from fd)\n", pData->FileDevice));
420 rc = VINF_SUCCESS;
421
422 /*
423 * Create the out-of-space semaphore and the async receiver thread.
424 */
425 rc = RTSemEventCreate(&pData->EventOutOfSpace);
426 AssertRCReturn(rc, rc);
427
428 rc = PDMDrvHlpThreadCreate(pDrvIns, &pData->pThread, pData, drvTAPOs2AsyncIoThread, drvTAPOs2WakeupThread,
429 0, RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "TAP");
430 AssertRCReturn(rc, rc);
431
432#ifdef VBOX_WITH_STATISTICS
433 /*
434 * Statistics.
435 */
436 PDMDrvHlpSTAMRegisterF(pDrvIns, &pData->StatPktSent, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Number of sent packets.", "/Drivers/TAP%d/Packets/Sent", pDrvIns->iInstance);
437 PDMDrvHlpSTAMRegisterF(pDrvIns, &pData->StatPktSentBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Number of sent bytes.", "/Drivers/TAP%d/Bytes/Sent", pDrvIns->iInstance);
438 PDMDrvHlpSTAMRegisterF(pDrvIns, &pData->StatPktRecv, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Number of received packets.", "/Drivers/TAP%d/Packets/Received", pDrvIns->iInstance);
439 PDMDrvHlpSTAMRegisterF(pDrvIns, &pData->StatPktRecvBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Number of received bytes.", "/Drivers/TAP%d/Bytes/Received", pDrvIns->iInstance);
440 PDMDrvHlpSTAMRegisterF(pDrvIns, &pData->StatTransmit, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling packet transmit runs.", "/Drivers/TAP%d/Transmit", pDrvIns->iInstance);
441 PDMDrvHlpSTAMRegisterF(pDrvIns, &pData->StatReceive, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling packet receive runs.", "/Drivers/TAP%d/Receive", pDrvIns->iInstance);
442 PDMDrvHlpSTAMRegisterF(pDrvIns, &pData->StatRecvOverflows, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_OCCURENCE, "Profiling packet receive overflows.", "/Drivers/TAP%d/RecvOverflows", pDrvIns->iInstance);
443#endif /* VBOX_WITH_STATISTICS */
444
445 return rc;
446}
447
448
449/**
450 * TAP network transport driver registration record.
451 */
452const PDMDRVREG g_DrvHostInterface =
453{
454 /* u32Version */
455 PDM_DRVREG_VERSION,
456 /* szDriverName */
457 "HostInterface",
458 /* pszDescription */
459 "TAP Network Transport Driver",
460 /* fFlags */
461 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
462 /* fClass. */
463 PDM_DRVREG_CLASS_NETWORK,
464 /* cMaxInstances */
465 ~0,
466 /* cbInstance */
467 sizeof(DRVTAPOS2),
468 /* pfnConstruct */
469 drvTAPOs2Construct,
470 /* pfnDestruct */
471 drvTAPOs2Destruct,
472 /* pfnIOCtl */
473 NULL,
474 /* pfnPowerOn */
475 NULL,
476 /* pfnReset */
477 NULL,
478 /* pfnSuspend */
479 NULL,
480 /* pfnResume */
481 NULL,
482 /* pfnDetach */
483 NULL,
484 /* pfnPowerOff */
485 NULL
486};
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