VirtualBox

source: vbox/trunk/src/VBox/Devices/Network/DrvIntNet.cpp@ 11444

Last change on this file since 11444 was 11284, checked in by vboxsync, 16 years ago

Devices: %Vrc -> %Rrc (just preferred, not mandatory (yet))

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 38.1 KB
Line 
1/* $Id: DrvIntNet.cpp 11284 2008-08-08 22:32:08Z vboxsync $ */
2/** @file
3 * DrvIntNet - Internal network transport driver.
4 */
5
6/*
7 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22
23/*******************************************************************************
24* Header Files *
25*******************************************************************************/
26#define LOG_GROUP LOG_GROUP_DRV_INTNET
27#include <VBox/pdmdrv.h>
28#include <VBox/cfgm.h>
29#include <VBox/intnet.h>
30#include <VBox/vmm.h>
31#include <VBox/err.h>
32
33#include <VBox/log.h>
34#include <iprt/asm.h>
35#include <iprt/assert.h>
36#include <iprt/thread.h>
37#include <iprt/semaphore.h>
38#include <iprt/string.h>
39#include <iprt/time.h>
40#ifdef RT_OS_DARWIN
41# include <iprt/ctype.h>
42#endif
43
44#include "Builtins.h"
45
46
47/*******************************************************************************
48* Structures and Typedefs *
49*******************************************************************************/
50/**
51 * The state of the asynchronous thread.
52 */
53typedef enum ASYNCSTATE
54{
55 /** The thread is suspended. */
56 ASYNCSTATE_SUSPENDED = 1,
57 /** The thread is running. */
58 ASYNCSTATE_RUNNING,
59 /** The thread must (/has) terminate. */
60 ASYNCSTATE_TERMINATE,
61 /** The usual 32-bit type blowup. */
62 ASYNCSTATE_32BIT_HACK = 0x7fffffff
63} ASYNCSTATE;
64
65/**
66 * Block driver instance data.
67 */
68typedef struct DRVINTNET
69{
70 /** The network interface. */
71 PDMINETWORKCONNECTOR INetworkConnector;
72 /** The network interface. */
73 PPDMINETWORKPORT pPort;
74 /** The network config interface.
75 * Can (in theory at least) be NULL. */
76 PPDMINETWORKCONFIG pConfigIf;
77 /** Pointer to the driver instance. */
78 PPDMDRVINS pDrvIns;
79 /** Interface handle. */
80 INTNETIFHANDLE hIf;
81 /** Pointer to the communication buffer. */
82 PINTNETBUF pBuf;
83 /** The thread state. */
84 ASYNCSTATE volatile enmState;
85 /** Reader thread. */
86 RTTHREAD Thread;
87 /** Event semaphore the Thread waits on while the VM is suspended. */
88 RTSEMEVENT EventSuspended;
89 /** Set if the link is down.
90 * When the link is down all incoming packets will be dropped. */
91 bool volatile fLinkDown;
92 /** Set if data transmission should start immediately and deactivate
93 * as late as possible. */
94 bool fActivateEarlyDeactivateLate;
95
96#ifdef VBOX_WITH_STATISTICS
97 /** Profiling packet transmit runs. */
98 STAMPROFILE StatTransmit;
99 /** Profiling packet receive runs. */
100 STAMPROFILEADV StatReceive;
101#endif /* VBOX_WITH_STATISTICS */
102
103#ifdef LOG_ENABLED
104 /** The nano ts of the last transfer. */
105 uint64_t u64LastTransferTS;
106 /** The nano ts of the last receive. */
107 uint64_t u64LastReceiveTS;
108#endif
109 /** The network name. */
110 char szNetwork[INTNET_MAX_NETWORK_NAME];
111} DRVINTNET, *PDRVINTNET;
112
113
114/** Converts a pointer to DRVINTNET::INetworkConnector to a PDRVINTNET. */
115#define PDMINETWORKCONNECTOR_2_DRVINTNET(pInterface) ( (PDRVINTNET)((uintptr_t)pInterface - RT_OFFSETOF(DRVINTNET, INetworkConnector)) )
116
117
118/**
119 * Updates the MAC address on the kernel side.
120 *
121 * @returns VBox status code.
122 * @param pThis The driver instance.
123 */
124static int drvIntNetUpdateMacAddress(PDRVINTNET pThis)
125{
126 if (!pThis->pConfigIf)
127 return VINF_SUCCESS;
128
129 INTNETIFSETMACADDRESSREQ SetMacAddressReq;
130 SetMacAddressReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
131 SetMacAddressReq.Hdr.cbReq = sizeof(SetMacAddressReq);
132 SetMacAddressReq.pSession = NIL_RTR0PTR;
133 SetMacAddressReq.hIf = pThis->hIf;
134 int rc = pThis->pConfigIf->pfnGetMac(pThis->pConfigIf, &SetMacAddressReq.Mac);
135 if (RT_SUCCESS(rc))
136 rc = pThis->pDrvIns->pDrvHlp->pfnSUPCallVMMR0Ex(pThis->pDrvIns, VMMR0_DO_INTNET_IF_SET_MAC_ADDRESS,
137 &SetMacAddressReq, sizeof(SetMacAddressReq));
138
139 Log(("drvIntNetUpdateMacAddress: %.*Rhxs rc=%Rrc\n", sizeof(SetMacAddressReq.Mac), &SetMacAddressReq.Mac, rc));
140 return rc;
141}
142
143
144/**
145 * Sets the kernel interface active or inactive.
146 *
147 * Worker for poweron, poweroff, suspend and resume.
148 *
149 * @returns VBox status code.
150 * @param pThis The driver instance.
151 * @param fActive The new state.
152 */
153static int drvIntNetSetActive(PDRVINTNET pThis, bool fActive)
154{
155 if (!pThis->pConfigIf)
156 return VINF_SUCCESS;
157
158 INTNETIFSETACTIVEREQ SetActiveReq;
159 SetActiveReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
160 SetActiveReq.Hdr.cbReq = sizeof(SetActiveReq);
161 SetActiveReq.pSession = NIL_RTR0PTR;
162 SetActiveReq.hIf = pThis->hIf;
163 SetActiveReq.fActive = fActive;
164 int rc = pThis->pDrvIns->pDrvHlp->pfnSUPCallVMMR0Ex(pThis->pDrvIns, VMMR0_DO_INTNET_IF_SET_ACTIVE,
165 &SetActiveReq, sizeof(SetActiveReq));
166
167 Log(("drvIntNetUpdateMacAddress: fActive=%d rc=%Rrc\n", fActive, rc));
168 AssertRC(rc);
169 return rc;
170}
171
172
173/**
174 * Writes a frame packet to the buffer.
175 *
176 * @returns VBox status code.
177 * @param pBuf The buffer.
178 * @param pRingBuf The ring buffer to read from.
179 * @param pvFrame The frame to write.
180 * @param cbFrame The size of the frame.
181 * @remark This is the same as INTNETRingWriteFrame
182 */
183static int drvIntNetRingWriteFrame(PINTNETBUF pBuf, PINTNETRINGBUF pRingBuf, const void *pvFrame, uint32_t cbFrame)
184{
185 /*
186 * Validate input.
187 */
188 Assert(pBuf);
189 Assert(pRingBuf);
190 Assert(pvFrame);
191 Assert(cbFrame >= sizeof(RTMAC) * 2);
192 uint32_t offWrite = pRingBuf->offWrite;
193 Assert(offWrite == RT_ALIGN_32(offWrite, sizeof(INTNETHDR)));
194 uint32_t offRead = pRingBuf->offRead;
195 Assert(offRead == RT_ALIGN_32(offRead, sizeof(INTNETHDR)));
196
197 const uint32_t cb = RT_ALIGN_32(cbFrame, sizeof(INTNETHDR));
198 if (offRead <= offWrite)
199 {
200 /*
201 * Try fit it all before the end of the buffer.
202 */
203 if (pRingBuf->offEnd - offWrite >= cb + sizeof(INTNETHDR))
204 {
205 PINTNETHDR pHdr = (PINTNETHDR)((uint8_t *)pBuf + offWrite);
206 pHdr->u16Type = INTNETHDR_TYPE_FRAME;
207 pHdr->cbFrame = cbFrame;
208 pHdr->offFrame = sizeof(INTNETHDR);
209
210 memcpy(pHdr + 1, pvFrame, cbFrame);
211
212 offWrite += cb + sizeof(INTNETHDR);
213 Assert(offWrite <= pRingBuf->offEnd && offWrite >= pRingBuf->offStart);
214 if (offWrite >= pRingBuf->offEnd)
215 offWrite = pRingBuf->offStart;
216 Log2(("WriteFrame: offWrite: %#x -> %#x (1)\n", pRingBuf->offWrite, offWrite));
217 ASMAtomicXchgU32(&pRingBuf->offWrite, offWrite);
218 return VINF_SUCCESS;
219 }
220
221 /*
222 * Try fit the frame at the start of the buffer.
223 * (The header fits before the end of the buffer because of alignment.)
224 */
225 AssertMsg(pRingBuf->offEnd - offWrite >= sizeof(INTNETHDR), ("offEnd=%x offWrite=%x\n", pRingBuf->offEnd, offWrite));
226 if (offRead - pRingBuf->offStart > cb) /* not >= ! */
227 {
228 PINTNETHDR pHdr = (PINTNETHDR)((uint8_t *)pBuf + offWrite);
229 void *pvFrameOut = (PINTNETHDR)((uint8_t *)pBuf + pRingBuf->offStart);
230 pHdr->u16Type = INTNETHDR_TYPE_FRAME;
231 pHdr->cbFrame = cbFrame;
232 pHdr->offFrame = (intptr_t)pvFrameOut - (intptr_t)pHdr;
233
234 memcpy(pvFrameOut, pvFrame, cbFrame);
235
236 offWrite = pRingBuf->offStart + cb;
237 ASMAtomicXchgU32(&pRingBuf->offWrite, offWrite);
238 Log2(("WriteFrame: offWrite: %#x -> %#x (2)\n", pRingBuf->offWrite, offWrite));
239 return VINF_SUCCESS;
240 }
241 }
242 /*
243 * The reader is ahead of the writer, try fit it into that space.
244 */
245 else if (offRead - offWrite > cb + sizeof(INTNETHDR)) /* not >= ! */
246 {
247 PINTNETHDR pHdr = (PINTNETHDR)((uint8_t *)pBuf + offWrite);
248 pHdr->u16Type = INTNETHDR_TYPE_FRAME;
249 pHdr->cbFrame = cbFrame;
250 pHdr->offFrame = sizeof(INTNETHDR);
251
252 memcpy(pHdr + 1, pvFrame, cbFrame);
253
254 offWrite += cb + sizeof(INTNETHDR);
255 ASMAtomicXchgU32(&pRingBuf->offWrite, offWrite);
256 Log2(("WriteFrame: offWrite: %#x -> %#x (3)\n", pRingBuf->offWrite, offWrite));
257 return VINF_SUCCESS;
258 }
259
260 /* (it didn't fit) */
261 /** @todo stats */
262 return VERR_BUFFER_OVERFLOW;
263}
264
265
266/**
267 * Send data to the network.
268 *
269 * @returns VBox status code.
270 * @param pInterface Pointer to the interface structure containing the called function pointer.
271 * @param pvBuf Data to send.
272 * @param cb Number of bytes to send.
273 * @thread EMT
274 */
275static DECLCALLBACK(int) drvIntNetSend(PPDMINETWORKCONNECTOR pInterface, const void *pvBuf, size_t cb)
276{
277 PDRVINTNET pThis = PDMINETWORKCONNECTOR_2_DRVINTNET(pInterface);
278 STAM_PROFILE_START(&pThis->StatTransmit, a);
279
280#ifdef LOG_ENABLED
281 uint64_t u64Now = RTTimeProgramNanoTS();
282 LogFlow(("drvIntNetSend: %-4d bytes at %llu ns deltas: r=%llu t=%llu\n",
283 cb, u64Now, u64Now - pThis->u64LastReceiveTS, u64Now - pThis->u64LastTransferTS));
284 pThis->u64LastTransferTS = u64Now;
285 Log2(("drvIntNetSend: pvBuf=%p cb=%#x\n"
286 "%.*Vhxd\n",
287 pvBuf, cb, cb, pvBuf));
288#endif
289
290 /*
291 * Add the frame to the send buffer and push it onto the network.
292 */
293 int rc = drvIntNetRingWriteFrame(pThis->pBuf, &pThis->pBuf->Send, pvBuf, cb);
294 if ( rc == VERR_BUFFER_OVERFLOW
295 && pThis->pBuf->cbSend < cb)
296 {
297 INTNETIFSENDREQ SendReq;
298 SendReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
299 SendReq.Hdr.cbReq = sizeof(SendReq);
300 SendReq.pSession = NIL_RTR0PTR;
301 SendReq.hIf = pThis->hIf;
302 pThis->pDrvIns->pDrvHlp->pfnSUPCallVMMR0Ex(pThis->pDrvIns, VMMR0_DO_INTNET_IF_SEND, &SendReq, sizeof(SendReq));
303
304 rc = drvIntNetRingWriteFrame(pThis->pBuf, &pThis->pBuf->Send, pvBuf, cb);
305 }
306
307 if (RT_SUCCESS(rc))
308 {
309 INTNETIFSENDREQ SendReq;
310 SendReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
311 SendReq.Hdr.cbReq = sizeof(SendReq);
312 SendReq.pSession = NIL_RTR0PTR;
313 SendReq.hIf = pThis->hIf;
314 rc = pThis->pDrvIns->pDrvHlp->pfnSUPCallVMMR0Ex(pThis->pDrvIns, VMMR0_DO_INTNET_IF_SEND, &SendReq, sizeof(SendReq));
315 }
316
317 STAM_PROFILE_STOP(&pThis->StatTransmit, a);
318 AssertRC(rc);
319 return rc;
320}
321
322
323/**
324 * Set promiscuous mode.
325 *
326 * This is called when the promiscuous mode is set. This means that there doesn't have
327 * to be a mode change when it's called.
328 *
329 * @param pInterface Pointer to the interface structure containing the called function pointer.
330 * @param fPromiscuous Set if the adaptor is now in promiscuous mode. Clear if it is not.
331 * @thread EMT
332 */
333static DECLCALLBACK(void) drvIntNetSetPromiscuousMode(PPDMINETWORKCONNECTOR pInterface, bool fPromiscuous)
334{
335 PDRVINTNET pThis = PDMINETWORKCONNECTOR_2_DRVINTNET(pInterface);
336 INTNETIFSETPROMISCUOUSMODEREQ Req;
337 Req.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
338 Req.Hdr.cbReq = sizeof(Req);
339 Req.pSession = NIL_RTR0PTR;
340 Req.hIf = pThis->hIf;
341 Req.fPromiscuous = fPromiscuous;
342 int rc = pThis->pDrvIns->pDrvHlp->pfnSUPCallVMMR0Ex(pThis->pDrvIns, VMMR0_DO_INTNET_IF_SET_PROMISCUOUS_MODE, &Req, sizeof(Req));
343 LogFlow(("drvIntNetSetPromiscuousMode: fPromiscuous=%RTbool\n", fPromiscuous));
344 AssertRC(rc);
345}
346
347
348/**
349 * Notification on link status changes.
350 *
351 * @param pInterface Pointer to the interface structure containing the called function pointer.
352 * @param enmLinkState The new link state.
353 * @thread EMT
354 */
355static DECLCALLBACK(void) drvIntNetNotifyLinkChanged(PPDMINETWORKCONNECTOR pInterface, PDMNETWORKLINKSTATE enmLinkState)
356{
357 PDRVINTNET pThis = PDMINETWORKCONNECTOR_2_DRVINTNET(pInterface);
358 bool fLinkDown;
359 switch (enmLinkState)
360 {
361 case PDMNETWORKLINKSTATE_DOWN:
362 case PDMNETWORKLINKSTATE_DOWN_RESUME:
363 fLinkDown = true;
364 break;
365 default:
366 AssertMsgFailed(("enmLinkState=%d\n", enmLinkState));
367 case PDMNETWORKLINKSTATE_UP:
368 fLinkDown = false;
369 break;
370 }
371 LogFlow(("drvIntNetNotifyLinkChanged: enmLinkState=%d %d->%d\n", enmLinkState, pThis->fLinkDown, fLinkDown));
372 ASMAtomicXchgSize(&pThis->fLinkDown, fLinkDown);
373}
374
375
376/**
377 * Wait for space to become available up the driver/device chain.
378 *
379 * @returns VINF_SUCCESS if space is available.
380 * @returns VERR_STATE_CHANGED if the state changed.
381 * @returns VBox status code on other errors.
382 * @param pThis Pointer to the instance data.
383 */
384static int drvIntNetAsyncIoWaitForSpace(PDRVINTNET pThis)
385{
386 LogFlow(("drvIntNetAsyncIoWaitForSpace:\n"));
387 STAM_PROFILE_ADV_STOP(&pThis->StatReceive, a);
388 int rc = pThis->pPort->pfnWaitReceiveAvail(pThis->pPort, RT_INDEFINITE_WAIT);
389 STAM_PROFILE_ADV_START(&pThis->StatReceive, a);
390 LogFlow(("drvIntNetAsyncIoWaitForSpace: returns %Rrc\n", rc));
391 return rc;
392}
393
394
395/**
396 * Executes async I/O (RUNNING mode).
397 *
398 * @returns VERR_STATE_CHANGED if the state changed.
399 * @returns Appropriate VBox status code (error) on fatal error.
400 * @param pThis The driver instance data.
401 */
402static int drvIntNetAsyncIoRun(PDRVINTNET pThis)
403{
404 PPDMDRVINS pDrvIns = pThis->pDrvIns;
405 LogFlow(("drvIntNetAsyncIoRun: pThis=%p\n", pThis));
406
407 /*
408 * The running loop - processing received data and waiting for more to arrive.
409 */
410 STAM_PROFILE_ADV_START(&pThis->StatReceive, a);
411 PINTNETBUF pBuf = pThis->pBuf;
412 PINTNETRINGBUF pRingBuf = &pThis->pBuf->Recv;
413 for (;;)
414 {
415 /*
416 * Process the receive buffer.
417 */
418 while (INTNETRingGetReadable(pRingBuf) > 0)
419 {
420 /*
421 * Check the state and then inspect the packet.
422 */
423 if (pThis->enmState != ASYNCSTATE_RUNNING)
424 {
425 STAM_PROFILE_ADV_STOP(&pThis->StatReceive, a);
426 LogFlow(("drvIntNetAsyncIoRun: returns VERR_STATE_CHANGED (state changed - #0)\n"));
427 return VERR_STATE_CHANGED;
428 }
429
430 PINTNETHDR pHdr = (PINTNETHDR)((uintptr_t)pBuf + pRingBuf->offRead);
431 Log2(("pHdr=%p offRead=%#x: %.8Rhxs\n", pHdr, pRingBuf->offRead, pHdr));
432 if ( pHdr->u16Type == INTNETHDR_TYPE_FRAME
433 && !pThis->fLinkDown)
434 {
435 /*
436 * Check if there is room for the frame and pass it up.
437 */
438 size_t cbFrame = pHdr->cbFrame;
439 int rc = pThis->pPort->pfnWaitReceiveAvail(pThis->pPort, 0);
440 if (rc == VINF_SUCCESS)
441 {
442#ifdef LOG_ENABLED
443 uint64_t u64Now = RTTimeProgramNanoTS();
444 LogFlow(("drvIntNetAsyncIoRun: %-4d bytes at %llu ns deltas: r=%llu t=%llu\n",
445 cbFrame, u64Now, u64Now - pThis->u64LastReceiveTS, u64Now - pThis->u64LastTransferTS));
446 pThis->u64LastReceiveTS = u64Now;
447 Log2(("drvIntNetAsyncIoRun: cbFrame=%#x\n"
448 "%.*Vhxd\n",
449 cbFrame, cbFrame, INTNETHdrGetFramePtr(pHdr, pBuf)));
450#endif
451 int rc = pThis->pPort->pfnReceive(pThis->pPort, INTNETHdrGetFramePtr(pHdr, pBuf), cbFrame);
452 AssertRC(rc);
453
454 /* skip to the next frame. */
455 INTNETRingSkipFrame(pBuf, pRingBuf);
456 }
457 else
458 {
459 /*
460 * Wait for sufficient space to become available and then retry.
461 */
462 rc = drvIntNetAsyncIoWaitForSpace(pThis);
463 if (RT_FAILURE(rc))
464 {
465 STAM_PROFILE_ADV_STOP(&pThis->StatReceive, a);
466 LogFlow(("drvIntNetAsyncIoRun: returns %Rrc (wait-for-space)\n", rc));
467 return rc;
468 }
469 }
470 }
471 else
472 {
473 /*
474 * Link down or unknown frame - skip to the next frame.
475 */
476 AssertMsg(pHdr->u16Type == INTNETHDR_TYPE_FRAME, ("Unknown frame type %RX16! offRead=%#x\n",
477 pHdr->u16Type, pRingBuf->offRead));
478 INTNETRingSkipFrame(pBuf, pRingBuf);
479 }
480 } /* while more received data */
481
482 /*
483 * Wait for data, checking the state before we block.
484 */
485 if (pThis->enmState != ASYNCSTATE_RUNNING)
486 {
487 STAM_PROFILE_ADV_STOP(&pThis->StatReceive, a);
488 LogFlow(("drvIntNetAsyncIoRun: returns VINF_SUCCESS (state changed - #1)\n"));
489 return VERR_STATE_CHANGED;
490 }
491 INTNETIFWAITREQ WaitReq;
492 WaitReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
493 WaitReq.Hdr.cbReq = sizeof(WaitReq);
494 WaitReq.pSession = NIL_RTR0PTR;
495 WaitReq.hIf = pThis->hIf;
496 WaitReq.cMillies = 30000; /* 30s - don't wait forever, timeout now and then. */
497 STAM_PROFILE_ADV_STOP(&pThis->StatReceive, a);
498 int rc = pDrvIns->pDrvHlp->pfnSUPCallVMMR0Ex(pDrvIns, VMMR0_DO_INTNET_IF_WAIT, &WaitReq, sizeof(WaitReq));
499 if ( RT_FAILURE(rc)
500 && rc != VERR_TIMEOUT
501 && rc != VERR_INTERRUPTED)
502 {
503 LogFlow(("drvIntNetAsyncIoRun: returns %Rrc\n", rc));
504 return rc;
505 }
506 STAM_PROFILE_ADV_START(&pThis->StatReceive, a);
507 }
508}
509
510
511/**
512 * Asynchronous I/O thread for handling receive.
513 *
514 * @returns VINF_SUCCESS (ignored).
515 * @param ThreadSelf Thread handle.
516 * @param pvUser Pointer to a DRVINTNET structure.
517 */
518static DECLCALLBACK(int) drvIntNetAsyncIoThread(RTTHREAD ThreadSelf, void *pvUser)
519{
520 PDRVINTNET pThis = (PDRVINTNET)pvUser;
521 LogFlow(("drvIntNetAsyncIoThread: pThis=%p\n", pThis));
522 STAM_PROFILE_ADV_START(&pThis->StatReceive, a);
523
524 /*
525 * The main loop - acting on state.
526 */
527 for (;;)
528 {
529 ASYNCSTATE enmState = pThis->enmState;
530 switch (enmState)
531 {
532 case ASYNCSTATE_SUSPENDED:
533 {
534 int rc = RTSemEventWait(pThis->EventSuspended, 30000);
535 if ( RT_FAILURE(rc)
536 && rc != VERR_TIMEOUT)
537 {
538 LogFlow(("drvIntNetAsyncIoThread: returns %Rrc\n", rc));
539 return rc;
540 }
541 break;
542 }
543
544 case ASYNCSTATE_RUNNING:
545 {
546 int rc = drvIntNetAsyncIoRun(pThis);
547 if ( rc != VERR_STATE_CHANGED
548 && RT_FAILURE(rc))
549 {
550 LogFlow(("drvIntNetAsyncIoThread: returns %Rrc\n", rc));
551 return rc;
552 }
553 break;
554 }
555
556 default:
557 AssertMsgFailed(("Invalid state %d\n", enmState));
558 case ASYNCSTATE_TERMINATE:
559 LogFlow(("drvIntNetAsyncIoThread: returns VINF_SUCCESS\n"));
560 return VINF_SUCCESS;
561 }
562 }
563}
564
565
566/**
567 * Queries an interface to the driver.
568 *
569 * @returns Pointer to interface.
570 * @returns NULL if the interface was not supported by the driver.
571 * @param pInterface Pointer to this interface structure.
572 * @param enmInterface The requested interface identification.
573 * @thread Any thread.
574 */
575static DECLCALLBACK(void *) drvIntNetQueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface)
576{
577 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
578 PDRVINTNET pThis = PDMINS_2_DATA(pDrvIns, PDRVINTNET);
579 switch (enmInterface)
580 {
581 case PDMINTERFACE_BASE:
582 return &pDrvIns->IBase;
583 case PDMINTERFACE_NETWORK_CONNECTOR:
584 return &pThis->INetworkConnector;
585 default:
586 return NULL;
587 }
588}
589
590
591/**
592 * Power Off notification.
593 *
594 * @param pDrvIns The driver instance.
595 */
596static DECLCALLBACK(void) drvIntNetPowerOff(PPDMDRVINS pDrvIns)
597{
598 LogFlow(("drvIntNetPowerOff\n"));
599 PDRVINTNET pThis = PDMINS_2_DATA(pDrvIns, PDRVINTNET);
600 if (!pThis->fActivateEarlyDeactivateLate)
601 {
602 ASMAtomicXchgSize(&pThis->enmState, ASYNCSTATE_SUSPENDED);
603 drvIntNetSetActive(pThis, false /* fActive */);
604 }
605}
606
607
608/**
609 * Resume notification.
610 *
611 * @param pDrvIns The driver instance.
612 */
613static DECLCALLBACK(void) drvIntNetResume(PPDMDRVINS pDrvIns)
614{
615 LogFlow(("drvIntNetPowerResume\n"));
616 PDRVINTNET pThis = PDMINS_2_DATA(pDrvIns, PDRVINTNET);
617 if (!pThis->fActivateEarlyDeactivateLate)
618 {
619 ASMAtomicXchgSize(&pThis->enmState, ASYNCSTATE_RUNNING);
620 RTSemEventSignal(pThis->EventSuspended);
621 drvIntNetUpdateMacAddress(pThis); /* (could be a state restore) */
622 drvIntNetSetActive(pThis, true /* fActive */);
623 }
624}
625
626
627/**
628 * Suspend notification.
629 *
630 * @param pDrvIns The driver instance.
631 */
632static DECLCALLBACK(void) drvIntNetSuspend(PPDMDRVINS pDrvIns)
633{
634 LogFlow(("drvIntNetPowerSuspend\n"));
635 PDRVINTNET pThis = PDMINS_2_DATA(pDrvIns, PDRVINTNET);
636 if (!pThis->fActivateEarlyDeactivateLate)
637 {
638 ASMAtomicXchgSize(&pThis->enmState, ASYNCSTATE_SUSPENDED);
639 drvIntNetSetActive(pThis, false /* fActive */);
640 }
641}
642
643
644/**
645 * Power On notification.
646 *
647 * @param pDrvIns The driver instance.
648 */
649static DECLCALLBACK(void) drvIntNetPowerOn(PPDMDRVINS pDrvIns)
650{
651 LogFlow(("drvIntNetPowerOn\n"));
652 PDRVINTNET pThis = PDMINS_2_DATA(pDrvIns, PDRVINTNET);
653 if (!pThis->fActivateEarlyDeactivateLate)
654 {
655 ASMAtomicXchgSize(&pThis->enmState, ASYNCSTATE_RUNNING);
656 RTSemEventSignal(pThis->EventSuspended);
657 drvIntNetUpdateMacAddress(pThis);
658 drvIntNetSetActive(pThis, true /* fActive */);
659 }
660}
661
662
663/**
664 * Destruct a driver instance.
665 *
666 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
667 * resources can be freed correctly.
668 *
669 * @param pDrvIns The driver instance data.
670 */
671static DECLCALLBACK(void) drvIntNetDestruct(PPDMDRVINS pDrvIns)
672{
673 LogFlow(("drvIntNetDestruct\n"));
674 PDRVINTNET pThis = PDMINS_2_DATA(pDrvIns, PDRVINTNET);
675
676 /*
677 * Indicate to the thread that it's time to quit.
678 */
679 ASMAtomicXchgSize(&pThis->enmState, ASYNCSTATE_TERMINATE);
680 ASMAtomicXchgSize(&pThis->fLinkDown, true);
681 RTSEMEVENT EventSuspended = pThis->EventSuspended;
682 pThis->EventSuspended = NIL_RTSEMEVENT;
683
684 /*
685 * Close the interface
686 */
687 if (pThis->hIf != INTNET_HANDLE_INVALID)
688 {
689 INTNETIFCLOSEREQ CloseReq;
690 CloseReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
691 CloseReq.Hdr.cbReq = sizeof(CloseReq);
692 CloseReq.pSession = NIL_RTR0PTR;
693 CloseReq.hIf = pThis->hIf;
694 pThis->hIf = INTNET_HANDLE_INVALID;
695 int rc = pDrvIns->pDrvHlp->pfnSUPCallVMMR0Ex(pDrvIns, VMMR0_DO_INTNET_IF_CLOSE, &CloseReq, sizeof(CloseReq));
696 AssertRC(rc);
697 }
698
699 /*
700 * Wait for the thread to terminate.
701 */
702 if (pThis->Thread != NIL_RTTHREAD)
703 {
704 if (EventSuspended != NIL_RTSEMEVENT)
705 RTSemEventSignal(EventSuspended);
706 int rc = RTThreadWait(pThis->Thread, 5000, NULL);
707 AssertRC(rc);
708 pThis->Thread = NIL_RTTHREAD;
709 }
710
711 /*
712 * Destroy the semaphores.
713 */
714 if (EventSuspended != NIL_RTSEMEVENT)
715 RTSemEventDestroy(EventSuspended);
716}
717
718
719/**
720 * Construct a TAP network transport driver instance.
721 *
722 * @returns VBox status.
723 * @param pDrvIns The driver instance data.
724 * If the registration structure is needed, pDrvIns->pDrvReg points to it.
725 * @param pCfgHandle Configuration node handle for the driver. Use this to obtain the configuration
726 * of the driver instance. It's also found in pDrvIns->pCfgHandle, but like
727 * iInstance it's expected to be used a bit in this function.
728 */
729static DECLCALLBACK(int) drvIntNetConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle)
730{
731 PDRVINTNET pThis = PDMINS_2_DATA(pDrvIns, PDRVINTNET);
732
733 /*
734 * Init the static parts.
735 */
736 pThis->pDrvIns = pDrvIns;
737 pThis->hIf = INTNET_HANDLE_INVALID;
738 pThis->Thread = NIL_RTTHREAD;
739 pThis->EventSuspended = NIL_RTSEMEVENT;
740 pThis->enmState = ASYNCSTATE_SUSPENDED;
741 pThis->fActivateEarlyDeactivateLate = false;
742 /* IBase */
743 pDrvIns->IBase.pfnQueryInterface = drvIntNetQueryInterface;
744 /* INetwork */
745 pThis->INetworkConnector.pfnSend = drvIntNetSend;
746 pThis->INetworkConnector.pfnSetPromiscuousMode = drvIntNetSetPromiscuousMode;
747 pThis->INetworkConnector.pfnNotifyLinkChanged = drvIntNetNotifyLinkChanged;
748
749 /*
750 * Validate the config.
751 */
752 if (!CFGMR3AreValuesValid(pCfgHandle,
753 "Network\0"
754 "Trunk\0"
755 "TrunkType\0"
756 "ReceiveBufferSize\0"
757 "SendBufferSize\0"
758 "RestrictAccess\0"
759 "SharedMacOnWire\0"
760 "IsService\0"))
761 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
762
763 /*
764 * Check that no-one is attached to us.
765 */
766 int rc = pDrvIns->pDrvHlp->pfnAttach(pDrvIns, NULL);
767 if (rc != VERR_PDM_NO_ATTACHED_DRIVER)
768 {
769 AssertMsgFailed(("Configuration error: Cannot attach drivers to the TAP driver!\n"));
770 return VERR_PDM_DRVINS_NO_ATTACH;
771 }
772
773 /*
774 * Query the network port interface.
775 */
776 pThis->pPort = (PPDMINETWORKPORT)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_NETWORK_PORT);
777 if (!pThis->pPort)
778 {
779 AssertMsgFailed(("Configuration error: the above device/driver didn't export the network port interface!\n"));
780 return VERR_PDM_MISSING_INTERFACE_ABOVE;
781 }
782 pThis->pConfigIf = (PPDMINETWORKCONFIG)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_NETWORK_CONFIG);
783
784 /*
785 * Read the configuration.
786 */
787 INTNETOPENREQ OpenReq;
788 memset(&OpenReq, 0, sizeof(OpenReq));
789 OpenReq.Hdr.cbReq = sizeof(OpenReq);
790 OpenReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
791 OpenReq.pSession = NIL_RTR0PTR;
792
793 /** @cfgm{Network, string}
794 * The name of the internal network to connect to.
795 */
796 rc = CFGMR3QueryString(pCfgHandle, "Network", OpenReq.szNetwork, sizeof(OpenReq.szNetwork));
797 if (RT_FAILURE(rc))
798 return PDMDRV_SET_ERROR(pDrvIns, rc,
799 N_("Configuration error: Failed to get the \"Network\" value"));
800 strcpy(pThis->szNetwork, OpenReq.szNetwork);
801
802 /** @cfgm{TrunkType, uint32_t, kIntNetTrunkType_None}
803 * The trunk connection type see INTNETTRUNKTYPE.
804 */
805 uint32_t u32TrunkType;
806 rc = CFGMR3QueryU32(pCfgHandle, "TrunkType", &u32TrunkType);
807 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
808 u32TrunkType = kIntNetTrunkType_None;
809 else if (RT_FAILURE(rc))
810 return PDMDRV_SET_ERROR(pDrvIns, rc,
811 N_("Configuration error: Failed to get the \"TrunkType\" value"));
812 OpenReq.enmTrunkType = (INTNETTRUNKTYPE)u32TrunkType;
813
814 /** @cfgm{Trunk, string, ""}
815 * The name of the trunk connection.
816 */
817 rc = CFGMR3QueryString(pCfgHandle, "Trunk", OpenReq.szTrunk, sizeof(OpenReq.szTrunk));
818 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
819 OpenReq.szTrunk[0] = '\0';
820 else if (RT_FAILURE(rc))
821 return PDMDRV_SET_ERROR(pDrvIns, rc,
822 N_("Configuration error: Failed to get the \"Trunk\" value"));
823
824 /** @cfgm{RestrictAccess, boolean, true}
825 * Whether to restrict the access to the network or if it should be public. Everyone on
826 * the computer can connect to a public network. Don't change this.
827 */
828 bool fRestrictAccess;
829 rc = CFGMR3QueryBool(pCfgHandle, "RestrictAccess", &fRestrictAccess);
830 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
831 fRestrictAccess = true;
832 else if (RT_FAILURE(rc))
833 return PDMDRV_SET_ERROR(pDrvIns, rc,
834 N_("Configuration error: Failed to get the \"RestrictAccess\" value"));
835 OpenReq.fFlags = fRestrictAccess ? 0 : INTNET_OPEN_FLAGS_PUBLIC;
836
837 /** @cfgm{SharedMacOnWire, boolean, false}
838 * Whether to shared the MAC address of the host interface when using the wire. When
839 * attaching to a wireless NIC this option is usally a requirement.
840 */
841 bool fSharedMacOnWire;
842 rc = CFGMR3QueryBoolDef(pCfgHandle, "SharedMacOnWire", &fSharedMacOnWire, false);
843 if (RT_FAILURE(rc))
844 return PDMDRV_SET_ERROR(pDrvIns, rc,
845 N_("Configuration error: Failed to get the \"SharedMacOnWire\" value"));
846 if (fSharedMacOnWire)
847 OpenReq.fFlags |= INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE;
848
849 /** @cfgm{ReceiveBufferSize, uint32_t, 218 KB}
850 * The size of the receive buffer.
851 */
852 rc = CFGMR3QueryU32(pCfgHandle, "ReceiveBufferSize", &OpenReq.cbRecv);
853 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
854 OpenReq.cbRecv = 218 * _1K ;
855 else if (RT_FAILURE(rc))
856 return PDMDRV_SET_ERROR(pDrvIns, rc,
857 N_("Configuration error: Failed to get the \"ReceiveBufferSize\" value"));
858
859 /** @cfgm{SendBufferSize, uint32_t, 36 KB}
860 * The size of the send (transmit) buffer.
861 * This should be more than twice the size of the larges frame size because
862 * the ring buffer is very simple and doesn't support splitting up frames
863 * nor inserting padding. So, if this is too close to the frame size the
864 * header will fragment the buffer such that the frame won't fit on either
865 * side of it and the code will get very upset about it all.
866 */
867 rc = CFGMR3QueryU32(pCfgHandle, "SendBufferSize", &OpenReq.cbSend);
868 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
869 OpenReq.cbSend = 36*_1K;
870 else if (RT_FAILURE(rc))
871 return PDMDRV_SET_ERROR(pDrvIns, rc,
872 N_("Configuration error: Failed to get the \"SendBufferSize\" value"));
873 if (OpenReq.cbSend < 32)
874 return PDMDRV_SET_ERROR(pDrvIns, rc,
875 N_("Configuration error: The \"SendBufferSize\" value is too small"));
876 if (OpenReq.cbSend < 16384*2 + 64)
877 LogRel(("DrvIntNet: Warning! SendBufferSize=%u, Recommended minimum size %u butes.\n", OpenReq.cbSend, 16384*2 + 64));
878
879 /** @cfgm{IsService, boolean, true}
880 * This alterns the way the thread is suspended and resumed. When it's being used by
881 * a service such as LWIP/iSCSI it shouldn't suspend immediately like for a NIC.
882 */
883 rc = CFGMR3QueryBool(pCfgHandle, "IsService", &pThis->fActivateEarlyDeactivateLate);
884 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
885 pThis->fActivateEarlyDeactivateLate = false;
886 else if (RT_FAILURE(rc))
887 return PDMDRV_SET_ERROR(pDrvIns, rc,
888 N_("Configuration error: Failed to get the \"IsService\" value"));
889
890 LogRel(("IntNet#%u: szNetwork={%s} enmTrunkType=%d szTrunk={%s} fFlags=%#x cbRecv=%u cbSend=%u\n",
891 pDrvIns->iInstance, OpenReq.szNetwork, OpenReq.enmTrunkType, OpenReq.szTrunk, OpenReq.fFlags,
892 OpenReq.cbRecv, OpenReq.cbSend));
893
894#ifdef RT_OS_DARWIN
895 /* Temporary hack: attach to a network with the name 'if=en0' and you're hitting the wire. */
896 if ( !OpenReq.szTrunk[0]
897 && OpenReq.enmTrunkType == kIntNetTrunkType_None
898 && !strncmp(pThis->szNetwork, "if=en", sizeof("if=en") - 1)
899 && RT_C_IS_DIGIT(pThis->szNetwork[sizeof("if=en") - 1])
900 && !pThis->szNetwork[sizeof("if=en")])
901 {
902 OpenReq.enmTrunkType = kIntNetTrunkType_NetFlt;
903 strcpy(OpenReq.szTrunk, &pThis->szNetwork[sizeof("if=") - 1]);
904 }
905 /* Temporary hack: attach to a network with the name 'wif=en0' and you're on the air. */
906 if ( !OpenReq.szTrunk[0]
907 && OpenReq.enmTrunkType == kIntNetTrunkType_None
908 && !strncmp(pThis->szNetwork, "wif=en", sizeof("wif=en") - 1)
909 && RT_C_IS_DIGIT(pThis->szNetwork[sizeof("wif=en") - 1])
910 && !pThis->szNetwork[sizeof("wif=en")])
911 {
912 OpenReq.enmTrunkType = kIntNetTrunkType_NetFlt;
913 OpenReq.fFlags |= INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE;
914 strcpy(OpenReq.szTrunk, &pThis->szNetwork[sizeof("wif=") - 1]);
915 }
916#endif
917
918 /*
919 * Create the event semaphores
920 */
921 rc = RTSemEventCreate(&pThis->EventSuspended);
922 if (RT_FAILURE(rc))
923 return rc;
924
925 /*
926 * Create the interface.
927 */
928 OpenReq.hIf = INTNET_HANDLE_INVALID;
929 rc = pDrvIns->pDrvHlp->pfnSUPCallVMMR0Ex(pDrvIns, VMMR0_DO_INTNET_OPEN, &OpenReq, sizeof(OpenReq));
930 if (RT_FAILURE(rc))
931 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
932 N_("Failed to open/create the internal network '%s'"), pThis->szNetwork);
933 AssertRelease(OpenReq.hIf != INTNET_HANDLE_INVALID);
934 pThis->hIf = OpenReq.hIf;
935 Log(("IntNet%d: hIf=%RX32 '%s'\n", pDrvIns->iInstance, pThis->hIf, pThis->szNetwork));
936
937 /*
938 * Get default buffer.
939 */
940 INTNETIFGETRING3BUFFERREQ GetRing3BufferReq;
941 GetRing3BufferReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
942 GetRing3BufferReq.Hdr.cbReq = sizeof(GetRing3BufferReq);
943 GetRing3BufferReq.pSession = NIL_RTR0PTR;
944 GetRing3BufferReq.hIf = pThis->hIf;
945 GetRing3BufferReq.pRing3Buf = NULL;
946 rc = pDrvIns->pDrvHlp->pfnSUPCallVMMR0Ex(pDrvIns, VMMR0_DO_INTNET_IF_GET_RING3_BUFFER, &GetRing3BufferReq, sizeof(GetRing3BufferReq));
947 if (RT_FAILURE(rc))
948 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
949 N_("Failed to get ring-3 buffer for the newly created interface to '%s'"), pThis->szNetwork);
950 AssertRelease(VALID_PTR(GetRing3BufferReq.pRing3Buf));
951 pThis->pBuf = GetRing3BufferReq.pRing3Buf;
952
953 /*
954 * Create the async I/O thread.
955 * Note! Using a PDM thread here doesn't fit with the IsService=true operation.
956 */
957 rc = RTThreadCreate(&pThis->Thread, drvIntNetAsyncIoThread, pThis, _128K, RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "INTNET");
958 if (RT_FAILURE(rc))
959 {
960 AssertRC(rc);
961 return rc;
962 }
963
964 char szStatName[64];
965 RTStrPrintf(szStatName, sizeof(szStatName), "/Net/IntNet%d/Bytes/Received", pDrvIns->iInstance);
966 pDrvIns->pDrvHlp->pfnSTAMRegister(pDrvIns, &pThis->pBuf->cbStatRecv, STAMTYPE_COUNTER, szStatName, STAMUNIT_BYTES, "Number of received bytes.");
967 RTStrPrintf(szStatName, sizeof(szStatName), "/Net/IntNet%d/Bytes/Sent", pDrvIns->iInstance);
968 pDrvIns->pDrvHlp->pfnSTAMRegister(pDrvIns, &pThis->pBuf->cbStatSend, STAMTYPE_COUNTER, szStatName, STAMUNIT_BYTES, "Number of sent bytes.");
969 RTStrPrintf(szStatName, sizeof(szStatName), "/Net/IntNet%d/Packets/Received", pDrvIns->iInstance);
970 pDrvIns->pDrvHlp->pfnSTAMRegister(pDrvIns, &pThis->pBuf->cStatRecvs, STAMTYPE_COUNTER, szStatName, STAMUNIT_OCCURENCES, "Number of received packets.");
971 RTStrPrintf(szStatName, sizeof(szStatName), "/Net/IntNet%d/Packets/Sent", pDrvIns->iInstance);
972 pDrvIns->pDrvHlp->pfnSTAMRegister(pDrvIns, &pThis->pBuf->cStatSends, STAMTYPE_COUNTER, szStatName, STAMUNIT_OCCURENCES, "Number of sent packets.");
973 RTStrPrintf(szStatName, sizeof(szStatName), "/Net/IntNet%d/Packets/Lost", pDrvIns->iInstance);
974 pDrvIns->pDrvHlp->pfnSTAMRegister(pDrvIns, &pThis->pBuf->cStatLost, STAMTYPE_COUNTER, szStatName, STAMUNIT_OCCURENCES, "Number of lost packets.");
975 RTStrPrintf(szStatName, sizeof(szStatName), "/Net/IntNet%d/YieldOk", pDrvIns->iInstance);
976 pDrvIns->pDrvHlp->pfnSTAMRegister(pDrvIns, &pThis->pBuf->cStatYieldsOk, STAMTYPE_COUNTER, szStatName, STAMUNIT_OCCURENCES, "Number of times yielding fixed an overflow.");
977 RTStrPrintf(szStatName, sizeof(szStatName), "/Net/IntNet%d/YieldNok", pDrvIns->iInstance);
978 pDrvIns->pDrvHlp->pfnSTAMRegister(pDrvIns, &pThis->pBuf->cStatYieldsNok, STAMTYPE_COUNTER, szStatName, STAMUNIT_OCCURENCES, "Number of times yielding didn't help fix an overflow.");
979
980#ifdef VBOX_WITH_STATISTICS
981 RTStrPrintf(szStatName, sizeof(szStatName), "/Net/IntNet%d/Receive", pDrvIns->iInstance);
982 pDrvIns->pDrvHlp->pfnSTAMRegister(pDrvIns, &pThis->StatReceive, STAMTYPE_PROFILE, szStatName, STAMUNIT_TICKS_PER_CALL, "Profiling packet receive runs.");
983 RTStrPrintf(szStatName, sizeof(szStatName), "/Net/IntNet%d/Transmit", pDrvIns->iInstance);
984 pDrvIns->pDrvHlp->pfnSTAMRegister(pDrvIns, &pThis->StatTransmit, STAMTYPE_PROFILE, szStatName, STAMUNIT_TICKS_PER_CALL, "Profiling packet transmit runs.");
985#endif
986
987 /*
988 * Activate data transmission as early as possible
989 */
990 if (pThis->fActivateEarlyDeactivateLate)
991 {
992 ASMAtomicXchgSize(&pThis->enmState, ASYNCSTATE_RUNNING);
993 RTSemEventSignal(pThis->EventSuspended);
994 drvIntNetUpdateMacAddress(pThis);
995 drvIntNetSetActive(pThis, true /* fActive */);
996 }
997
998 return rc;
999}
1000
1001
1002/**
1003 * Internal networking transport driver registration record.
1004 */
1005const PDMDRVREG g_DrvIntNet =
1006{
1007 /* u32Version */
1008 PDM_DRVREG_VERSION,
1009 /* szDriverName */
1010 "IntNet",
1011 /* pszDescription */
1012 "Internal Networking Transport Driver",
1013 /* fFlags */
1014 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1015 /* fClass. */
1016 PDM_DRVREG_CLASS_NETWORK,
1017 /* cMaxInstances */
1018 ~0,
1019 /* cbInstance */
1020 sizeof(DRVINTNET),
1021 /* pfnConstruct */
1022 drvIntNetConstruct,
1023 /* pfnDestruct */
1024 drvIntNetDestruct,
1025 /* pfnIOCtl */
1026 NULL,
1027 /* pfnPowerOn */
1028 drvIntNetPowerOn,
1029 /* pfnReset */
1030 NULL,
1031 /* pfnSuspend */
1032 drvIntNetSuspend,
1033 /* pfnResume */
1034 drvIntNetResume,
1035 /* pfnDetach */
1036 NULL,
1037 /* pfnPowerOff */
1038 drvIntNetPowerOff
1039};
1040
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