mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-24 07:46:42 +00:00
vbox: rework network backend
* Handle signals and thereby work asynchronously * Forward link-state changes to the attached AboveNetwork Fixes #1465.
This commit is contained in:
parent
19f8666170
commit
e53e6b0c5c
@ -4,7 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2014 Genode Labs GmbH
|
* Copyright (C) 2014-2015 Genode Labs GmbH
|
||||||
*
|
*
|
||||||
* This file is distributed under the terms of the GNU General Public License
|
* This file is distributed under the terms of the GNU General Public License
|
||||||
* version 2.
|
* version 2.
|
||||||
@ -42,243 +42,338 @@
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* Structures and Typedefs *
|
* Structures and Typedefs *
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
|
||||||
|
struct Nic_client;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TAP driver instance data.
|
* Nic driver instance data.
|
||||||
*
|
*
|
||||||
* @implements PDMINETWORKUP
|
* @implements PDMINETWORKUP
|
||||||
*/
|
*/
|
||||||
typedef struct DRVTAP
|
typedef struct DRVNIC
|
||||||
{
|
{
|
||||||
/** The network interface. */
|
/** The network interface to Nic session. */
|
||||||
PDMINETWORKUP INetworkUp;
|
PDMINETWORKUP INetworkUp;
|
||||||
/** The network interface. */
|
/** The config port interface we're representing. */
|
||||||
PPDMINETWORKDOWN pIAboveNet;
|
PDMINETWORKCONFIG INetworkConfig;
|
||||||
/** Pointer to the driver instance. */
|
/** The network interface to VBox driver. */
|
||||||
PPDMDRVINS pDrvIns;
|
PPDMINETWORKDOWN pIAboveNet;
|
||||||
/** Reader thread. */
|
/** The config port interface we're attached to. */
|
||||||
PPDMTHREAD pThread;
|
PPDMINETWORKCONFIG pIAboveConfig;
|
||||||
|
/** Pointer to the driver instance. */
|
||||||
/** @todo The transmit thread. */
|
PPDMDRVINS pDrvIns;
|
||||||
/** Transmit lock used by drvTAPNetworkUp_BeginXmit. */
|
/** Receiver thread that handles all signals. */
|
||||||
RTCRITSECT XmitLock;
|
PPDMTHREAD pThread;
|
||||||
|
/** Nic::Session client wrapper. */
|
||||||
Nic::Session *nic_session;
|
Nic_client *nic_client;
|
||||||
|
} DRVNIC, *PDRVNIC;
|
||||||
PDMINETWORKCONFIG INetworkConfig;
|
|
||||||
|
|
||||||
} DRVTAP, *PDRVTAP;
|
|
||||||
|
|
||||||
|
|
||||||
/** Converts a pointer to TAP::INetworkUp to a PRDVTAP. */
|
class Nic_client
|
||||||
#define PDMINETWORKUP_2_DRVTAP(pInterface) ( (PDRVTAP)((uintptr_t)pInterface - RT_OFFSETOF(DRVTAP, INetworkUp)) )
|
{
|
||||||
#define PDMINETWORKCONFIG_2_DRVTAP(pInterface) ( (PDRVTAP)((uintptr_t)pInterface - RT_OFFSETOF(DRVTAP, INetworkConfig)) )
|
private:
|
||||||
|
|
||||||
|
enum {
|
||||||
|
PACKET_SIZE = Nic::Packet_allocator::DEFAULT_PACKET_SIZE,
|
||||||
|
BUF_SIZE = Nic::Session::QUEUE_SIZE * PACKET_SIZE,
|
||||||
|
};
|
||||||
|
|
||||||
|
Nic::Packet_allocator *_tx_block_alloc;
|
||||||
|
Nic::Connection _nic;
|
||||||
|
Genode::Signal_receiver _sig_rec;
|
||||||
|
|
||||||
|
Genode::Signal_dispatcher<Nic_client> _link_state_dispatcher;
|
||||||
|
Genode::Signal_dispatcher<Nic_client> _rx_packet_avail_dispatcher;
|
||||||
|
Genode::Signal_dispatcher<Nic_client> _rx_ready_to_ack_dispatcher;
|
||||||
|
Genode::Signal_dispatcher<Nic_client> _destruct_dispatcher;
|
||||||
|
|
||||||
|
/* VM <-> device driver (down) <-> nic_client (up) <-> nic session */
|
||||||
|
PPDMINETWORKDOWN _down_rx;
|
||||||
|
PPDMINETWORKCONFIG _down_rx_config;
|
||||||
|
|
||||||
|
void _handle_rx_packet_avail(unsigned)
|
||||||
|
{
|
||||||
|
while (_nic.rx()->packet_avail() && _nic.rx()->ready_to_ack()) {
|
||||||
|
Nic::Packet_descriptor rx_packet = _nic.rx()->get_packet();
|
||||||
|
|
||||||
|
char *rx_content = _nic.rx()->packet_content(rx_packet);
|
||||||
|
|
||||||
|
int rc = _down_rx->pfnWaitReceiveAvail(_down_rx, RT_INDEFINITE_WAIT);
|
||||||
|
if (RT_FAILURE(rc))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
rc = _down_rx->pfnReceive(_down_rx, rx_content, rx_packet.size());
|
||||||
|
AssertRC(rc);
|
||||||
|
|
||||||
|
_nic.rx()->acknowledge_packet(rx_packet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handle_rx_ready_to_ack(unsigned) { _handle_rx_packet_avail(0); }
|
||||||
|
|
||||||
|
void _handle_link_state(unsigned)
|
||||||
|
{
|
||||||
|
_down_rx_config->pfnSetLinkState(_down_rx_config,
|
||||||
|
_nic.link_state() ? PDMNETWORKLINKSTATE_UP
|
||||||
|
: PDMNETWORKLINKSTATE_DOWN);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* By handling this signal the I/O thread gets unblocked
|
||||||
|
* and will leave its loop when the DRVNIC instance is
|
||||||
|
* being destructed.
|
||||||
|
*/
|
||||||
|
void _handle_destruct(unsigned) { }
|
||||||
|
|
||||||
|
void _tx_ack(bool block = false)
|
||||||
|
{
|
||||||
|
/* check for acknowledgements */
|
||||||
|
while (_nic.tx()->ack_avail() || block) {
|
||||||
|
Nic::Packet_descriptor acked_packet = _nic.tx()->get_acked_packet();
|
||||||
|
_nic.tx()->release_packet(acked_packet);
|
||||||
|
block = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Nic::Packet_descriptor _alloc_tx_packet(Genode::size_t len)
|
||||||
|
{
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
Nic::Packet_descriptor packet = _nic.tx()->alloc_packet(len);
|
||||||
|
return packet;
|
||||||
|
} catch (Nic::Session::Tx::Source::Packet_alloc_failed) {
|
||||||
|
_tx_ack(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Nic::Packet_allocator* _packet_allocator()
|
||||||
|
{
|
||||||
|
using namespace Genode;
|
||||||
|
return new (env()->heap())Nic::Packet_allocator(env()->heap());
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Nic_client(PDRVNIC drvtap)
|
||||||
|
:
|
||||||
|
_tx_block_alloc(_packet_allocator()),
|
||||||
|
_nic(_tx_block_alloc, BUF_SIZE, BUF_SIZE),
|
||||||
|
_link_state_dispatcher(_sig_rec, *this, &Nic_client::_handle_link_state),
|
||||||
|
_rx_packet_avail_dispatcher(_sig_rec, *this, &Nic_client::_handle_rx_packet_avail),
|
||||||
|
_rx_ready_to_ack_dispatcher(_sig_rec, *this, &Nic_client::_handle_rx_ready_to_ack),
|
||||||
|
_destruct_dispatcher(_sig_rec, *this, &Nic_client::_handle_destruct),
|
||||||
|
_down_rx(drvtap->pIAboveNet),
|
||||||
|
_down_rx_config(drvtap->pIAboveConfig)
|
||||||
|
{
|
||||||
|
_nic.link_state_sigh(_link_state_dispatcher);
|
||||||
|
_nic.rx_channel()->sigh_packet_avail(_rx_packet_avail_dispatcher);
|
||||||
|
_nic.rx_channel()->sigh_ready_to_ack(_rx_ready_to_ack_dispatcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
~Nic_client()
|
||||||
|
{
|
||||||
|
using namespace Genode;
|
||||||
|
destroy(env()->heap(), _tx_block_alloc);
|
||||||
|
}
|
||||||
|
|
||||||
|
Genode::Signal_context_capability &dispatcher() { return _destruct_dispatcher; }
|
||||||
|
Genode::Signal_receiver &sig_rec() { return _sig_rec; }
|
||||||
|
Nic::Mac_address mac_address() { return _nic.mac_address(); }
|
||||||
|
|
||||||
|
int send_packet(void *packet, uint32_t packet_len)
|
||||||
|
{
|
||||||
|
Nic::Packet_descriptor tx_packet = _alloc_tx_packet(packet_len);
|
||||||
|
|
||||||
|
char *tx_content = _nic.tx()->packet_content(tx_packet);
|
||||||
|
Genode::memcpy(tx_content, packet, packet_len);
|
||||||
|
|
||||||
|
_nic.tx()->submit_packet(tx_packet);
|
||||||
|
_tx_ack();
|
||||||
|
|
||||||
|
return VINF_SUCCESS;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return lock to synchronize the destruction of the
|
||||||
|
* PDRVNIC, i.e., the Nic_client.
|
||||||
|
*/
|
||||||
|
static Genode::Lock *destruct_lock()
|
||||||
|
{
|
||||||
|
static Genode::Lock lock(Genode::Lock::LOCKED);
|
||||||
|
return &lock;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Converts a pointer to Nic::INetworkUp to a PRDVNic. */
|
||||||
|
#define PDMINETWORKUP_2_DRVNIC(pInterface) ( (PDRVNIC)((uintptr_t)pInterface - RT_OFFSETOF(DRVNIC, INetworkUp)) )
|
||||||
|
#define PDMINETWORKCONFIG_2_DRVNIC(pInterface) ( (PDRVNIC)((uintptr_t)pInterface - RT_OFFSETOF(DRVNIC, INetworkConfig)) )
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* Internal Functions *
|
* Internal Functions *
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
|
||||||
static int net_send_packet(void * packet, uint32_t packet_len, Nic::Session * nic) {
|
|
||||||
|
|
||||||
/* allocate transmit packet */
|
|
||||||
Nic::Packet_descriptor tx_packet;
|
|
||||||
try {
|
|
||||||
tx_packet = nic->tx()->alloc_packet(packet_len);
|
|
||||||
} catch (Nic::Session::Tx::Source::Packet_alloc_failed) {
|
|
||||||
PERR("tx packet alloc failed");
|
|
||||||
return VERR_NO_MEMORY;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* fill packet with content */
|
|
||||||
char * stream_buffer = nic->tx()->packet_content(tx_packet);
|
|
||||||
memcpy(stream_buffer, packet, packet_len);
|
|
||||||
|
|
||||||
nic->tx()->submit_packet(tx_packet);
|
|
||||||
|
|
||||||
/* wait for acknowledgement */
|
|
||||||
Nic::Packet_descriptor ack_tx_packet = nic->tx()->get_acked_packet();
|
|
||||||
|
|
||||||
if (ack_tx_packet.size() != tx_packet.size() ||
|
|
||||||
ack_tx_packet.offset() != tx_packet.offset())
|
|
||||||
PERR("unexpected acked packet");
|
|
||||||
|
|
||||||
nic->tx()->release_packet(tx_packet);
|
|
||||||
|
|
||||||
return VINF_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @interface_method_impl{PDMINETWORKUP,pfnBeginXmit}
|
* @interface_method_impl{PDMINETWORKUP,pfnBeginXmit}
|
||||||
*/
|
*/
|
||||||
static DECLCALLBACK(int) drvTAPNetworkUp_BeginXmit(PPDMINETWORKUP pInterface, bool fOnWorkerThread)
|
static DECLCALLBACK(int) drvNicNetworkUp_BeginXmit(PPDMINETWORKUP pInterface, bool fOnWorkerThread)
|
||||||
{
|
{
|
||||||
PDRVTAP pThis = PDMINETWORKUP_2_DRVTAP(pInterface);
|
return VINF_SUCCESS;
|
||||||
int rc = RTCritSectTryEnter(&pThis->XmitLock);
|
|
||||||
if (RT_FAILURE(rc))
|
|
||||||
{
|
|
||||||
/** @todo XMIT thread */
|
|
||||||
rc = VERR_TRY_AGAIN;
|
|
||||||
}
|
|
||||||
return rc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @interface_method_impl{PDMINETWORKUP,pfnAllocBuf}
|
* @interface_method_impl{PDMINETWORKUP,pfnAllocBuf}
|
||||||
*/
|
*/
|
||||||
static DECLCALLBACK(int) drvTAPNetworkUp_AllocBuf(PPDMINETWORKUP pInterface, size_t cbMin,
|
static DECLCALLBACK(int) drvNicNetworkUp_AllocBuf(PPDMINETWORKUP pInterface, size_t cbMin,
|
||||||
PCPDMNETWORKGSO pGso, PPPDMSCATTERGATHER ppSgBuf)
|
PCPDMNETWORKGSO pGso, PPPDMSCATTERGATHER ppSgBuf)
|
||||||
{
|
{
|
||||||
PDRVTAP pThis = PDMINETWORKUP_2_DRVTAP(pInterface);
|
PDRVNIC pThis = PDMINETWORKUP_2_DRVNIC(pInterface);
|
||||||
Assert(RTCritSectIsOwner(&pThis->XmitLock));
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Allocate a scatter / gather buffer descriptor that is immediately
|
* Allocate a scatter / gather buffer descriptor that is immediately
|
||||||
* followed by the buffer space of its single segment. The GSO context
|
* followed by the buffer space of its single segment. The GSO context
|
||||||
* comes after that again.
|
* comes after that again.
|
||||||
*/
|
*/
|
||||||
PPDMSCATTERGATHER pSgBuf = (PPDMSCATTERGATHER)RTMemAlloc( RT_ALIGN_Z(sizeof(*pSgBuf), 16)
|
PPDMSCATTERGATHER pSgBuf = (PPDMSCATTERGATHER)RTMemAlloc(RT_ALIGN_Z(sizeof(*pSgBuf), 16)
|
||||||
+ RT_ALIGN_Z(cbMin, 16)
|
+ RT_ALIGN_Z(cbMin, 16)
|
||||||
+ (pGso ? RT_ALIGN_Z(sizeof(*pGso), 16) : 0));
|
+ (pGso ? RT_ALIGN_Z(sizeof(*pGso), 16) : 0));
|
||||||
if (!pSgBuf)
|
if (!pSgBuf)
|
||||||
return VERR_NO_MEMORY;
|
return VERR_NO_MEMORY;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize the S/G buffer and return.
|
* Initialize the S/G buffer and return.
|
||||||
*/
|
*/
|
||||||
pSgBuf->fFlags = PDMSCATTERGATHER_FLAGS_MAGIC | PDMSCATTERGATHER_FLAGS_OWNER_1;
|
pSgBuf->fFlags = PDMSCATTERGATHER_FLAGS_MAGIC | PDMSCATTERGATHER_FLAGS_OWNER_1;
|
||||||
pSgBuf->cbUsed = 0;
|
pSgBuf->cbUsed = 0;
|
||||||
pSgBuf->cbAvailable = RT_ALIGN_Z(cbMin, 16);
|
pSgBuf->cbAvailable = RT_ALIGN_Z(cbMin, 16);
|
||||||
pSgBuf->pvAllocator = NULL;
|
pSgBuf->pvAllocator = NULL;
|
||||||
if (!pGso)
|
if (!pGso)
|
||||||
pSgBuf->pvUser = NULL;
|
pSgBuf->pvUser = NULL;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
pSgBuf->pvUser = (uint8_t *)(pSgBuf + 1) + pSgBuf->cbAvailable;
|
pSgBuf->pvUser = (uint8_t *)(pSgBuf + 1) + pSgBuf->cbAvailable;
|
||||||
*(PPDMNETWORKGSO)pSgBuf->pvUser = *pGso;
|
*(PPDMNETWORKGSO)pSgBuf->pvUser = *pGso;
|
||||||
}
|
}
|
||||||
pSgBuf->cSegs = 1;
|
pSgBuf->cSegs = 1;
|
||||||
pSgBuf->aSegs[0].cbSeg = pSgBuf->cbAvailable;
|
pSgBuf->aSegs[0].cbSeg = pSgBuf->cbAvailable;
|
||||||
pSgBuf->aSegs[0].pvSeg = pSgBuf + 1;
|
pSgBuf->aSegs[0].pvSeg = pSgBuf + 1;
|
||||||
|
|
||||||
#if 0 /* poison */
|
*ppSgBuf = pSgBuf;
|
||||||
memset(pSgBuf->aSegs[0].pvSeg, 'F', pSgBuf->aSegs[0].cbSeg);
|
return VINF_SUCCESS;
|
||||||
#endif
|
|
||||||
*ppSgBuf = pSgBuf;
|
|
||||||
return VINF_SUCCESS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @interface_method_impl{PDMINETWORKUP,pfnFreeBuf}
|
* @interface_method_impl{PDMINETWORKUP,pfnFreeBuf}
|
||||||
*/
|
*/
|
||||||
static DECLCALLBACK(int) drvTAPNetworkUp_FreeBuf(PPDMINETWORKUP pInterface, PPDMSCATTERGATHER pSgBuf)
|
static DECLCALLBACK(int) drvNicNetworkUp_FreeBuf(PPDMINETWORKUP pInterface, PPDMSCATTERGATHER pSgBuf)
|
||||||
{
|
{
|
||||||
PDRVTAP pThis = PDMINETWORKUP_2_DRVTAP(pInterface);
|
PDRVNIC pThis = PDMINETWORKUP_2_DRVNIC(pInterface);
|
||||||
Assert(RTCritSectIsOwner(&pThis->XmitLock));
|
if (pSgBuf)
|
||||||
if (pSgBuf)
|
{
|
||||||
{
|
Assert((pSgBuf->fFlags & PDMSCATTERGATHER_FLAGS_MAGIC_MASK) == PDMSCATTERGATHER_FLAGS_MAGIC);
|
||||||
Assert((pSgBuf->fFlags & PDMSCATTERGATHER_FLAGS_MAGIC_MASK) == PDMSCATTERGATHER_FLAGS_MAGIC);
|
pSgBuf->fFlags = 0;
|
||||||
pSgBuf->fFlags = 0;
|
RTMemFree(pSgBuf);
|
||||||
RTMemFree(pSgBuf);
|
}
|
||||||
}
|
return VINF_SUCCESS;
|
||||||
return VINF_SUCCESS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @interface_method_impl{PDMINETWORKUP,pfnSendBuf}
|
* @interface_method_impl{PDMINETWORKUP,pfnSendBuf}
|
||||||
*/
|
*/
|
||||||
static DECLCALLBACK(int) drvTAPNetworkUp_SendBuf(PPDMINETWORKUP pInterface, PPDMSCATTERGATHER pSgBuf, bool fOnWorkerThread)
|
static DECLCALLBACK(int) drvNicNetworkUp_SendBuf(PPDMINETWORKUP pInterface, PPDMSCATTERGATHER pSgBuf, bool fOnWorkerThread)
|
||||||
{
|
{
|
||||||
PDRVTAP pThis = PDMINETWORKUP_2_DRVTAP(pInterface);
|
PDRVNIC pThis = PDMINETWORKUP_2_DRVNIC(pInterface);
|
||||||
|
Nic_client *nic_client = pThis->nic_client;
|
||||||
|
|
||||||
AssertPtr(pSgBuf);
|
AssertPtr(pSgBuf);
|
||||||
Assert((pSgBuf->fFlags & PDMSCATTERGATHER_FLAGS_MAGIC_MASK) == PDMSCATTERGATHER_FLAGS_MAGIC);
|
Assert((pSgBuf->fFlags & PDMSCATTERGATHER_FLAGS_MAGIC_MASK) == PDMSCATTERGATHER_FLAGS_MAGIC);
|
||||||
Assert(RTCritSectIsOwner(&pThis->XmitLock));
|
|
||||||
|
|
||||||
/* Set an FTM checkpoint as this operation changes the state permanently. */
|
/* Set an FTM checkpoint as this operation changes the state permanently. */
|
||||||
PDMDrvHlpFTSetCheckpoint(pThis->pDrvIns, FTMCHECKPOINTTYPE_NETWORK);
|
PDMDrvHlpFTSetCheckpoint(pThis->pDrvIns, FTMCHECKPOINTTYPE_NETWORK);
|
||||||
|
|
||||||
int rc;
|
int rc;
|
||||||
if (!pSgBuf->pvUser)
|
if (!pSgBuf->pvUser)
|
||||||
{
|
{
|
||||||
Log2(("drvTAPSend: pSgBuf->aSegs[0].pvSeg=%p pSgBuf->cbUsed=%#x\n"
|
Log2(("drvNicSend: pSgBuf->aSegs[0].pvSeg=%p pSgBuf->cbUsed=%#x\n"
|
||||||
"%.*Rhxd\n",
|
"%.*Rhxd\n", pSgBuf->aSegs[0].pvSeg, pSgBuf->cbUsed, pSgBuf->cbUsed,
|
||||||
pSgBuf->aSegs[0].pvSeg, pSgBuf->cbUsed, pSgBuf->cbUsed, pSgBuf->aSegs[0].pvSeg));
|
pSgBuf->aSegs[0].pvSeg));
|
||||||
|
|
||||||
rc = net_send_packet(pSgBuf->aSegs[0].pvSeg, pSgBuf->cbUsed, pThis->nic_session);
|
rc = nic_client->send_packet(pSgBuf->aSegs[0].pvSeg, pSgBuf->cbUsed);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
uint8_t abHdrScratch[256];
|
uint8_t abHdrScratch[256];
|
||||||
uint8_t const *pbFrame = (uint8_t const *)pSgBuf->aSegs[0].pvSeg;
|
uint8_t const *pbFrame = (uint8_t const *)pSgBuf->aSegs[0].pvSeg;
|
||||||
PCPDMNETWORKGSO pGso = (PCPDMNETWORKGSO)pSgBuf->pvUser;
|
PCPDMNETWORKGSO pGso = (PCPDMNETWORKGSO)pSgBuf->pvUser;
|
||||||
uint32_t const cSegs = PDMNetGsoCalcSegmentCount(pGso, pSgBuf->cbUsed); Assert(cSegs > 1);
|
uint32_t const cSegs = PDMNetGsoCalcSegmentCount(pGso, pSgBuf->cbUsed);
|
||||||
rc = VINF_SUCCESS;
|
Assert(cSegs > 1);
|
||||||
for (size_t iSeg = 0; iSeg < cSegs; iSeg++)
|
rc = VINF_SUCCESS;
|
||||||
{
|
for (size_t iSeg = 0; iSeg < cSegs; iSeg++)
|
||||||
uint32_t cbSegFrame;
|
{
|
||||||
void *pvSegFrame = PDMNetGsoCarveSegmentQD(pGso, (uint8_t *)pbFrame, pSgBuf->cbUsed, abHdrScratch,
|
uint32_t cbSegFrame;
|
||||||
iSeg, cSegs, &cbSegFrame);
|
void *pvSegFrame = PDMNetGsoCarveSegmentQD(pGso, (uint8_t *)pbFrame, pSgBuf->cbUsed,
|
||||||
rc = net_send_packet(pvSegFrame, cbSegFrame, pThis->nic_session);
|
abHdrScratch, iSeg, cSegs, &cbSegFrame);
|
||||||
if (RT_FAILURE(rc))
|
rc = nic_client->send_packet(pvSegFrame, cbSegFrame);
|
||||||
break;
|
if (RT_FAILURE(rc))
|
||||||
}
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pSgBuf->fFlags = 0;
|
pSgBuf->fFlags = 0;
|
||||||
RTMemFree(pSgBuf);
|
RTMemFree(pSgBuf);
|
||||||
|
|
||||||
AssertRC(rc);
|
AssertRC(rc);
|
||||||
if (RT_FAILURE(rc))
|
if (RT_FAILURE(rc))
|
||||||
rc = rc == VERR_NO_MEMORY ? VERR_NET_NO_BUFFER_SPACE : VERR_NET_DOWN;
|
rc = rc == VERR_NO_MEMORY ? VERR_NET_NO_BUFFER_SPACE : VERR_NET_DOWN;
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @interface_method_impl{PDMINETWORKUP,pfnEndXmit}
|
* @interface_method_impl{PDMINETWORKUP,pfnEndXmit}
|
||||||
*/
|
*/
|
||||||
static DECLCALLBACK(void) drvTAPNetworkUp_EndXmit(PPDMINETWORKUP pInterface)
|
static DECLCALLBACK(void) drvNicNetworkUp_EndXmit(PPDMINETWORKUP pInterface)
|
||||||
{
|
{
|
||||||
PDRVTAP pThis = PDMINETWORKUP_2_DRVTAP(pInterface);
|
PDRVNIC pThis = PDMINETWORKUP_2_DRVNIC(pInterface);
|
||||||
RTCritSectLeave(&pThis->XmitLock);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @interface_method_impl{PDMINETWORKUP,pfnSetPromiscuousMode}
|
* @interface_method_impl{PDMINETWORKUP,pfnSetPromiscuousMode}
|
||||||
*/
|
*/
|
||||||
static DECLCALLBACK(void) drvTAPNetworkUp_SetPromiscuousMode(PPDMINETWORKUP pInterface, bool fPromiscuous)
|
static DECLCALLBACK(void) drvNicNetworkUp_SetPromiscuousMode(PPDMINETWORKUP pInterface, bool fPromiscuous)
|
||||||
{
|
{
|
||||||
LogFlow(("drvTAPNetworkUp_SetPromiscuousMode: fPromiscuous=%d\n", fPromiscuous));
|
LogFlow(("drvNicNetworkUp_SetPromiscuousMode: fPromiscuous=%d\n", fPromiscuous));
|
||||||
/* nothing to do */
|
/* nothing to do */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notification on link status changes.
|
* Notification on link status changes.
|
||||||
*
|
|
||||||
* @param pInterface Pointer to the interface structure containing the called function pointer.
|
|
||||||
* @param enmLinkState The new link state.
|
|
||||||
* @thread EMT
|
|
||||||
*/
|
*/
|
||||||
static DECLCALLBACK(void) drvTAPNetworkUp_NotifyLinkChanged(PPDMINETWORKUP pInterface, PDMNETWORKLINKSTATE enmLinkState)
|
static DECLCALLBACK(void) drvNicNetworkUp_NotifyLinkChanged(PPDMINETWORKUP pInterface, PDMNETWORKLINKSTATE enmLinkState)
|
||||||
{
|
{
|
||||||
LogFlow(("drvTAPNetworkUp_NotifyLinkChanged: enmLinkState=%d\n", enmLinkState));
|
LogFlow(("drvNicNetworkUp_NotifyLinkChanged: enmLinkState=%d\n", enmLinkState));
|
||||||
/** @todo take action on link down and up. Stop the polling and such like. */
|
/*
|
||||||
|
* At this point we could stop waiting for signals etc. but for now we just do nothing.
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static DECLCALLBACK(int) drvGetMac(PPDMINETWORKCONFIG pInterface, PRTMAC pMac)
|
static DECLCALLBACK(int) drvGetMac(PPDMINETWORKCONFIG pInterface, PRTMAC pMac)
|
||||||
{
|
{
|
||||||
PDRVTAP pThis = PDMINETWORKCONFIG_2_DRVTAP(pInterface);
|
PDRVNIC pThis = PDMINETWORKCONFIG_2_DRVNIC(pInterface);
|
||||||
|
Nic_client *nic_client = pThis->nic_client;
|
||||||
|
|
||||||
static_assert (sizeof(*pMac) == sizeof(pThis->nic_session->mac_address()),
|
static_assert (sizeof(*pMac) == sizeof(nic_client->mac_address()),
|
||||||
"should be equal");
|
"should be equal");
|
||||||
memcpy(pMac, pThis->nic_session->mac_address().addr, sizeof(*pMac));
|
memcpy(pMac, nic_client->mac_address().addr, sizeof(*pMac));
|
||||||
return VINF_SUCCESS;
|
return VINF_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -288,53 +383,47 @@ static DECLCALLBACK(int) drvGetMac(PPDMINETWORKCONFIG pInterface, PRTMAC pMac)
|
|||||||
*
|
*
|
||||||
* @returns VINF_SUCCESS (ignored).
|
* @returns VINF_SUCCESS (ignored).
|
||||||
* @param Thread Thread handle.
|
* @param Thread Thread handle.
|
||||||
* @param pvUser Pointer to a DRVTAP structure.
|
* @param pvUser Pointer to a DRVNIC structure.
|
||||||
*/
|
*/
|
||||||
static DECLCALLBACK(int) drvTAPAsyncIoThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
|
static DECLCALLBACK(int) drvNicAsyncIoThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
|
||||||
{
|
{
|
||||||
PDRVTAP pThis = PDMINS_2_DATA(pDrvIns, PDRVTAP);
|
PDRVNIC pThis = PDMINS_2_DATA(pDrvIns, PDRVNIC);
|
||||||
LogFlow(("drvTAPAsyncIoThread: pThis=%p\n", pThis));
|
LogFlow(("drvNicAsyncIoThread: pThis=%p\n", pThis));
|
||||||
|
|
||||||
if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
|
if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
|
||||||
return VINF_SUCCESS;
|
return VINF_SUCCESS;
|
||||||
|
|
||||||
Nic::Session * nic = pThis->nic_session;
|
Genode::Signal_receiver &sig_rec = pThis->nic_client->sig_rec();
|
||||||
|
|
||||||
while (pThread->enmState == PDMTHREADSTATE_RUNNING)
|
while (pThread->enmState == PDMTHREADSTATE_RUNNING)
|
||||||
{
|
{
|
||||||
Nic::Packet_descriptor rx_packet = nic->rx()->get_packet();
|
Genode::Signal sig = sig_rec.wait_for_signal();
|
||||||
|
int num = sig.num();
|
||||||
|
|
||||||
/* send it to the network bus */
|
Genode::Signal_dispatcher_base *dispatcher;
|
||||||
char * rx_content = nic->rx()->packet_content(rx_packet);
|
dispatcher = dynamic_cast<Genode::Signal_dispatcher_base *>(sig.context());
|
||||||
|
dispatcher->dispatch(num);
|
||||||
int rc1 = pThis->pIAboveNet->pfnWaitReceiveAvail(pThis->pIAboveNet,
|
|
||||||
RT_INDEFINITE_WAIT);
|
|
||||||
if (RT_FAILURE(rc1))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
rc1 = pThis->pIAboveNet->pfnReceive(pThis->pIAboveNet, rx_content,
|
|
||||||
rx_packet.size());
|
|
||||||
AssertRC(rc1);
|
|
||||||
|
|
||||||
/* acknowledge received packet */
|
|
||||||
nic->rx()->acknowledge_packet(rx_packet);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return VINF_SUCCESS;
|
destruct_lock()->unlock();
|
||||||
|
|
||||||
|
return VINF_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unblock the send thread so it can respond to a state change.
|
* Unblock the asynchronous I/O thread.
|
||||||
*
|
*
|
||||||
* @returns VBox status code.
|
* @returns VBox status code.
|
||||||
* @param pDevIns The pcnet device instance.
|
* @param pDevIns The pcnet device instance.
|
||||||
* @param pThread The send thread.
|
* @param pThread The asynchronous I/O thread.
|
||||||
*/
|
*/
|
||||||
static DECLCALLBACK(int) drvTapAsyncIoWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
|
static DECLCALLBACK(int) drvNicAsyncIoWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
|
||||||
{
|
{
|
||||||
PDRVTAP pThis = PDMINS_2_DATA(pDrvIns, PDRVTAP);
|
/*
|
||||||
Assert(!"Not implemented");
|
* Since we already wake up on singals in the I/O thread function
|
||||||
|
* we just return success at this point.
|
||||||
|
*/
|
||||||
return VINF_SUCCESS;
|
return VINF_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -344,149 +433,149 @@ static DECLCALLBACK(int) drvTapAsyncIoWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThr
|
|||||||
/**
|
/**
|
||||||
* @interface_method_impl{PDMIBASE,pfnQueryInterface}
|
* @interface_method_impl{PDMIBASE,pfnQueryInterface}
|
||||||
*/
|
*/
|
||||||
static DECLCALLBACK(void *) drvTAPQueryInterface(PPDMIBASE pInterface, const char *pszIID)
|
static DECLCALLBACK(void *) drvNicQueryInterface(PPDMIBASE pInterface, const char *pszIID)
|
||||||
{
|
{
|
||||||
PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
|
PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
|
||||||
PDRVTAP pThis = PDMINS_2_DATA(pDrvIns, PDRVTAP);
|
PDRVNIC pThis = PDMINS_2_DATA(pDrvIns, PDRVNIC);
|
||||||
|
|
||||||
PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
|
PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
|
||||||
PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKUP, &pThis->INetworkUp);
|
PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKUP, &pThis->INetworkUp);
|
||||||
PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKCONFIG, &pThis->INetworkConfig);
|
PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKCONFIG, &pThis->INetworkConfig);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* -=-=-=-=- PDMDRVREG -=-=-=-=- */
|
/* -=-=-=-=- PDMDRVREG -=-=-=-=- */
|
||||||
|
|
||||||
static DECLCALLBACK(void) drvTAPDestruct(PPDMDRVINS pDrvIns)
|
static DECLCALLBACK(void) drvNicDestruct(PPDMDRVINS pDrvIns)
|
||||||
{
|
{
|
||||||
Assert(!"Not implemented");
|
PDRVNIC pThis = PDMINS_2_DATA(pDrvIns, PDRVNIC);
|
||||||
|
Nic_client *nic_client = pThis->nic_client;
|
||||||
|
|
||||||
|
Genode::Signal_transmitter(nic_client->dispatcher()).submit();
|
||||||
|
|
||||||
|
/* wait until the recv thread exits */
|
||||||
|
destruct_lock()->lock();
|
||||||
|
destroy(Genode::env()->heap(), nic_client);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a TAP network transport driver instance.
|
* Construct a Nic network transport driver instance.
|
||||||
*
|
*
|
||||||
* @copydoc FNPDMDRVCONSTRUCT
|
* @copydoc FNPDMDRVCONSTRUCT
|
||||||
*/
|
*/
|
||||||
static DECLCALLBACK(int) drvTAPConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
|
static DECLCALLBACK(int) drvNicConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
|
||||||
{
|
{
|
||||||
PDRVTAP pThis = PDMINS_2_DATA(pDrvIns, PDRVTAP);
|
PDRVNIC pThis = PDMINS_2_DATA(pDrvIns, PDRVNIC);
|
||||||
PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
|
PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
|
||||||
|
|
||||||
/*
|
|
||||||
* Init the static parts.
|
|
||||||
*/
|
|
||||||
pThis->pDrvIns = pDrvIns;
|
|
||||||
|
|
||||||
/* IBase */
|
|
||||||
pDrvIns->IBase.pfnQueryInterface = drvTAPQueryInterface;
|
|
||||||
/* INetwork */
|
|
||||||
pThis->INetworkUp.pfnBeginXmit = drvTAPNetworkUp_BeginXmit;
|
|
||||||
pThis->INetworkUp.pfnAllocBuf = drvTAPNetworkUp_AllocBuf;
|
|
||||||
pThis->INetworkUp.pfnFreeBuf = drvTAPNetworkUp_FreeBuf;
|
|
||||||
pThis->INetworkUp.pfnSendBuf = drvTAPNetworkUp_SendBuf;
|
|
||||||
pThis->INetworkUp.pfnEndXmit = drvTAPNetworkUp_EndXmit;
|
|
||||||
pThis->INetworkUp.pfnSetPromiscuousMode = drvTAPNetworkUp_SetPromiscuousMode;
|
|
||||||
pThis->INetworkUp.pfnNotifyLinkChanged = drvTAPNetworkUp_NotifyLinkChanged;
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Init the static parts.
|
||||||
|
*/
|
||||||
|
pThis->pDrvIns = pDrvIns;
|
||||||
|
/* IBase */
|
||||||
|
pDrvIns->IBase.pfnQueryInterface = drvNicQueryInterface;
|
||||||
|
/* INetwork */
|
||||||
|
pThis->INetworkUp.pfnBeginXmit = drvNicNetworkUp_BeginXmit;
|
||||||
|
pThis->INetworkUp.pfnAllocBuf = drvNicNetworkUp_AllocBuf;
|
||||||
|
pThis->INetworkUp.pfnFreeBuf = drvNicNetworkUp_FreeBuf;
|
||||||
|
pThis->INetworkUp.pfnSendBuf = drvNicNetworkUp_SendBuf;
|
||||||
|
pThis->INetworkUp.pfnEndXmit = drvNicNetworkUp_EndXmit;
|
||||||
|
pThis->INetworkUp.pfnSetPromiscuousMode = drvNicNetworkUp_SetPromiscuousMode;
|
||||||
|
pThis->INetworkUp.pfnNotifyLinkChanged = drvNicNetworkUp_NotifyLinkChanged;
|
||||||
/* INetworkConfig - used on Genode to request Mac address of nic_session */
|
/* INetworkConfig - used on Genode to request Mac address of nic_session */
|
||||||
pThis->INetworkConfig.pfnGetMac = drvGetMac;
|
pThis->INetworkConfig.pfnGetMac = drvGetMac;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check that no-one is attached to us.
|
||||||
|
*/
|
||||||
|
AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
|
||||||
|
("Configuration error: Not possible to attach anything to"
|
||||||
|
" this driver!\n"), VERR_PDM_DRVINS_NO_ATTACH);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Query the above network port interface.
|
||||||
|
*/
|
||||||
|
pThis->pIAboveNet = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMINETWORKDOWN);
|
||||||
|
if (!pThis->pIAboveNet)
|
||||||
|
return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE,
|
||||||
|
N_("Configuration error: The above device/driver"
|
||||||
|
" didn't export the network port interface"));
|
||||||
|
pThis->pIAboveConfig = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMINETWORKCONFIG);
|
||||||
|
if (!pThis->pIAboveConfig)
|
||||||
|
return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE,
|
||||||
|
N_("Configuration error: the above device/driver"
|
||||||
|
" didn't export the network config interface!\n"));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Setup Genode nic_session connection
|
* Setup Genode nic_session connection
|
||||||
*/
|
*/
|
||||||
Nic::Packet_allocator *tx_block_alloc =
|
|
||||||
new (Genode::env()->heap()) Nic::Packet_allocator(Genode::env()->heap());
|
|
||||||
|
|
||||||
enum {
|
|
||||||
PACKET_SIZE = Nic::Packet_allocator::DEFAULT_PACKET_SIZE,
|
|
||||||
BUF_SIZE = Nic::Session::QUEUE_SIZE * PACKET_SIZE,
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
pThis->nic_session = new Nic::Connection(tx_block_alloc, BUF_SIZE,
|
pThis->nic_client = new (Genode::env()->heap()) Nic_client(pThis);
|
||||||
BUF_SIZE);
|
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
return VERR_HOSTIF_INIT_FAILED;
|
return VERR_HOSTIF_INIT_FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check that no-one is attached to us.
|
* Create the asynchronous I/O thread.
|
||||||
*/
|
*/
|
||||||
AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
|
int rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pThread, pThis,
|
||||||
("Configuration error: Not possible to attach anything to this driver!\n"),
|
drvNicAsyncIoThread, drvNicAsyncIoWakeup,
|
||||||
VERR_PDM_DRVINS_NO_ATTACH);
|
128 * _1K, RTTHREADTYPE_IO, "nic_thread");
|
||||||
|
AssertRCReturn(rc, rc);
|
||||||
/*
|
|
||||||
* Query the network port interface.
|
|
||||||
*/
|
|
||||||
pThis->pIAboveNet = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMINETWORKDOWN);
|
|
||||||
if (!pThis->pIAboveNet)
|
|
||||||
return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE,
|
|
||||||
N_("Configuration error: The above device/driver didn't export the network port interface"));
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Create the transmit lock.
|
|
||||||
*/
|
|
||||||
int rc = RTCritSectInit(&pThis->XmitLock);
|
|
||||||
AssertRCReturn(rc, rc);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Create the async I/O thread.
|
|
||||||
*/
|
|
||||||
rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pThread, pThis, drvTAPAsyncIoThread, drvTapAsyncIoWakeup, 128 * _1K, RTTHREADTYPE_IO, "TAP");
|
|
||||||
AssertRCReturn(rc, rc);
|
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TAP network transport driver registration record.
|
* Nic network transport driver registration record.
|
||||||
*/
|
*/
|
||||||
const PDMDRVREG g_DrvHostInterface =
|
const PDMDRVREG g_DrvHostInterface =
|
||||||
{
|
{
|
||||||
/* u32Version */
|
/* u32Version */
|
||||||
PDM_DRVREG_VERSION,
|
PDM_DRVREG_VERSION,
|
||||||
/* szName */
|
/* szName */
|
||||||
"HostInterface",
|
"HostInterface",
|
||||||
/* szRCMod */
|
/* szRCMod */
|
||||||
"",
|
"",
|
||||||
/* szR0Mod */
|
/* szR0Mod */
|
||||||
"",
|
"",
|
||||||
/* pszDescription */
|
/* pszDescription */
|
||||||
"Genode Network Session Driver",
|
"Genode Network Session Driver",
|
||||||
/* fFlags */
|
/* fFlags */
|
||||||
PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
|
PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
|
||||||
/* fClass. */
|
/* fClass. */
|
||||||
PDM_DRVREG_CLASS_NETWORK,
|
PDM_DRVREG_CLASS_NETWORK,
|
||||||
/* cMaxInstances */
|
/* cMaxInstances */
|
||||||
~0U,
|
~0U,
|
||||||
/* cbInstance */
|
/* cbInstance */
|
||||||
sizeof(DRVTAP),
|
sizeof(DRVNIC),
|
||||||
/* pfnConstruct */
|
/* pfnConstruct */
|
||||||
drvTAPConstruct,
|
drvNicConstruct,
|
||||||
/* pfnDestruct */
|
/* pfnDestruct */
|
||||||
drvTAPDestruct,
|
drvNicDestruct,
|
||||||
/* pfnRelocate */
|
/* pfnRelocate */
|
||||||
NULL,
|
NULL,
|
||||||
/* pfnIOCtl */
|
/* pfnIOCtl */
|
||||||
NULL,
|
NULL,
|
||||||
/* pfnPowerOn */
|
/* pfnPowerOn */
|
||||||
NULL,
|
NULL,
|
||||||
/* pfnReset */
|
/* pfnReset */
|
||||||
NULL,
|
NULL,
|
||||||
/* pfnSuspend */
|
/* pfnSuspend */
|
||||||
NULL, /** @todo Do power on, suspend and resume handlers! */
|
NULL,
|
||||||
/* pfnResume */
|
/* pfnResume */
|
||||||
NULL,
|
NULL,
|
||||||
/* pfnAttach */
|
/* pfnAttach */
|
||||||
NULL,
|
NULL,
|
||||||
/* pfnDetach */
|
/* pfnDetach */
|
||||||
NULL,
|
NULL,
|
||||||
/* pfnPowerOff */
|
/* pfnPowerOff */
|
||||||
NULL,
|
NULL,
|
||||||
/* pfnSoftReset */
|
/* pfnSoftReset */
|
||||||
NULL,
|
NULL,
|
||||||
/* u32EndVersion */
|
/* u32EndVersion */
|
||||||
PDM_DRVREG_VERSION
|
PDM_DRVREG_VERSION
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user