summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwpaul <wpaul@FreeBSD.org>2005-05-15 04:27:59 +0000
committerwpaul <wpaul@FreeBSD.org>2005-05-15 04:27:59 +0000
commit09647ee931869435d500b0f24b91d25af5145f6f (patch)
tree52249e86ab10b745ca626f1239199f119e89a939
parent2e784092b665281139996ed328c8655147601212 (diff)
downloadFreeBSD-src-09647ee931869435d500b0f24b91d25af5145f6f.zip
FreeBSD-src-09647ee931869435d500b0f24b91d25af5145f6f.tar.gz
Add support for NdisMEthIndicateReceive() and MiniportTransferData().
The Ralink RT2500 driver uses this API instead of NdisMIndicateReceivePacket(). Drivers use NdisMEthIndicateReceive() when they know they support 802.3 media and expect to hand their packets only protocols that want to deal with that particular media type. With this API, the driver does not manage its own NDIS_PACKET/NDIS_BUFFER structures. Instead, it lets bound protocols have a peek at the data, and then they supply an NDIS_PACKET/NDIS_BUFFER combo to the miniport driver, into which it copies the packet data. Drivers use NdisMIndicateReceivePacket() to allow their packets to be read by any protocol, not just those bound to 802.3 media devices. To make this work, we need an internal pool of NDIS_PACKETS for receives. Currently, we check to see if the driver exports a MiniportTransferData() method in its characteristics structure, and only allocate the pool for drivers that have this method. This should allow the RT2500 driver to work correctly, though I still have to fix ndiscvt(8) to parse its .inf file properly. Also, change kern_ndis.c:ndis_halt_nic() to reap timers before acquiring NDIS_LOCK(), since the reaping process might entail sleeping briefly (and we can't sleep with a lock held).
-rw-r--r--sys/compat/ndis/kern_ndis.c38
-rw-r--r--sys/compat/ndis/ndis_var.h19
-rw-r--r--sys/dev/if_ndis/if_ndis.c225
-rw-r--r--sys/dev/if_ndis/if_ndisvar.h1
4 files changed, 273 insertions, 10 deletions
diff --git a/sys/compat/ndis/kern_ndis.c b/sys/compat/ndis/kern_ndis.c
index f45f314..6e6fd60 100644
--- a/sys/compat/ndis/kern_ndis.c
+++ b/sys/compat/ndis/kern_ndis.c
@@ -1095,15 +1095,6 @@ ndis_halt_nic(arg)
sc = arg;
- NDIS_LOCK(sc);
- adapter = sc->ndis_block->nmb_miniportadapterctx;
- if (adapter == NULL) {
- NDIS_UNLOCK(sc);
- return(EIO);
- }
-
- sc->ndis_block->nmb_devicectx = NULL;
-
#ifdef NDIS_REAP_TIMERS
/*
* Drivers are sometimes very lax about cancelling all
@@ -1124,6 +1115,15 @@ ndis_halt_nic(arg)
KeFlushQueuedDpcs();
#endif
+ NDIS_LOCK(sc);
+ adapter = sc->ndis_block->nmb_miniportadapterctx;
+ if (adapter == NULL) {
+ NDIS_UNLOCK(sc);
+ return(EIO);
+ }
+
+ sc->ndis_block->nmb_devicectx = NULL;
+
/*
* The adapter context is only valid after the init
* handler has been called, and is invalid once the
@@ -1420,6 +1420,8 @@ NdisAddDevice(drv, pdo)
return(status);
block = fdo->do_devext;
+
+ block->nmb_filterdbs.nf_ethdb = block;
block->nmb_deviceobj = fdo;
block->nmb_physdeviceobj = pdo;
block->nmb_nextdeviceobj = IoAttachDeviceToDeviceStack(fdo, pdo);
@@ -1434,6 +1436,22 @@ NdisAddDevice(drv, pdo)
sc->ndis_block = block;
sc->ndis_chars = IoGetDriverObjectExtension(drv, (void *)1);
+ /*
+ * If the driver has a MiniportTransferData() function,
+ * we should allocate a private RX packet pool.
+ */
+
+ if (sc->ndis_chars->nmc_transferdata_func != NULL) {
+ NdisAllocatePacketPool(&status, &block->nmb_rxpool,
+ 32, PROTOCOL_RESERVED_SIZE_IN_PACKET);
+ if (status != NDIS_STATUS_SUCCESS) {
+ IoDetachDevice(block->nmb_nextdeviceobj);
+ IoDeleteDevice(fdo);
+ return(status);
+ }
+ INIT_LIST_HEAD((&block->nmb_packetlist));
+ }
+
/* Give interrupt handling priority over timers. */
IoInitializeDpcRequest(fdo, kernndis_functbl[6].ipt_wrap);
KeSetImportanceDpc(&fdo->do_dpc, KDPC_IMPORTANCE_HIGH);
@@ -1470,6 +1488,8 @@ ndis_unload_driver(arg)
TAILQ_REMOVE(&ndis_devhead, sc->ndis_block, link);
+ if (sc->ndis_chars->nmc_transferdata_func != NULL)
+ NdisFreePacketPool(sc->ndis_block->nmb_rxpool);
fdo = sc->ndis_block->nmb_deviceobj;
IoDetachDevice(sc->ndis_block->nmb_nextdeviceobj);
IoDeleteDevice(fdo);
diff --git a/sys/compat/ndis/ndis_var.h b/sys/compat/ndis/ndis_var.h
index 6e81b99..fc577a1 100644
--- a/sys/compat/ndis/ndis_var.h
+++ b/sys/compat/ndis/ndis_var.h
@@ -1144,6 +1144,20 @@ struct ndis_packet_oob {
typedef struct ndis_packet_oob ndis_packet_oob;
+/*
+ * Our protocol private region for handling ethernet.
+ * We need this to stash some of the things returned
+ * by NdisMEthIndicateReceive().
+ */
+
+struct ndis_ethpriv {
+ void *nep_ctx; /* packet context */
+ long nep_offset; /* residual data to transfer */
+ void *nep_pad[2];
+};
+
+typedef struct ndis_ethpriv ndis_ethpriv;
+
#define PROTOCOL_RESERVED_SIZE_IN_PACKET (4 * sizeof(void *))
struct ndis_packet {
@@ -1164,7 +1178,7 @@ struct ndis_packet {
} np_macrsvd;
} u;
uint32_t *np_rsvd[2];
- uint8_t nm_protocolreserved[PROTOCOL_RESERVED_SIZE_IN_PACKET];
+ uint8_t np_protocolreserved[PROTOCOL_RESERVED_SIZE_IN_PACKET];
/*
* This next part is probably wrong, but we need some place
@@ -1204,6 +1218,8 @@ struct ndis_filterdbs {
typedef struct ndis_filterdbs ndis_filterdbs;
+#define nf_ethdb u.nf_ethdb
+
enum ndis_medium {
NdisMedium802_3,
NdisMedium802_5,
@@ -1482,6 +1498,7 @@ struct ndis_miniport_block {
ndis_status nmb_getstat;
ndis_status nmb_setstat;
ndis_miniport_timer *nmb_timerlist;
+ ndis_handle nmb_rxpool;
TAILQ_ENTRY(ndis_miniport_block) link;
};
diff --git a/sys/dev/if_ndis/if_ndis.c b/sys/dev/if_ndis/if_ndis.c
index ce2b15c..efcada3 100644
--- a/sys/dev/if_ndis/if_ndis.c
+++ b/sys/dev/if_ndis/if_ndis.c
@@ -99,6 +99,12 @@ int ndisdrv_modevent (module_t, int, void *);
static void ndis_txeof (ndis_handle, ndis_packet *, ndis_status);
static void ndis_rxeof (ndis_handle, ndis_packet **, uint32_t);
+static void ndis_rxeof_eth (ndis_handle, ndis_handle, char *, void *,
+ uint32_t, void *, uint32_t, uint32_t);
+static void ndis_rxeof_done (ndis_handle);
+static void ndis_rxeof_xfr (kdpc *, ndis_handle, void *, void *);
+static void ndis_rxeof_xfr_done (ndis_handle, ndis_packet *,
+ uint32_t, uint32_t);
static void ndis_linksts (ndis_handle, ndis_status, void *, uint32_t);
static void ndis_linksts_done (ndis_handle);
@@ -106,6 +112,10 @@ static void ndis_linksts_done (ndis_handle);
static funcptr ndis_txeof_wrap;
static funcptr ndis_rxeof_wrap;
+static funcptr ndis_rxeof_eth_wrap;
+static funcptr ndis_rxeof_done_wrap;
+static funcptr ndis_rxeof_xfr_wrap;
+static funcptr ndis_rxeof_xfr_done_wrap;
static funcptr ndis_linksts_wrap;
static funcptr ndis_linksts_done_wrap;
static funcptr ndis_ticktask_wrap;
@@ -165,6 +175,14 @@ ndisdrv_modevent(mod, cmd, arg)
break;
windrv_wrap((funcptr)ndis_rxeof, &ndis_rxeof_wrap,
3, WINDRV_WRAP_STDCALL);
+ windrv_wrap((funcptr)ndis_rxeof_eth, &ndis_rxeof_eth_wrap,
+ 8, WINDRV_WRAP_STDCALL);
+ windrv_wrap((funcptr)ndis_rxeof_done, &ndis_rxeof_done_wrap,
+ 1, WINDRV_WRAP_STDCALL);
+ windrv_wrap((funcptr)ndis_rxeof_xfr, &ndis_rxeof_xfr_wrap,
+ 4, WINDRV_WRAP_STDCALL);
+ windrv_wrap((funcptr)ndis_rxeof_xfr_done,
+ &ndis_rxeof_xfr_done_wrap, 4, WINDRV_WRAP_STDCALL);
windrv_wrap((funcptr)ndis_txeof, &ndis_txeof_wrap,
3, WINDRV_WRAP_STDCALL);
windrv_wrap((funcptr)ndis_linksts, &ndis_linksts_wrap,
@@ -185,6 +203,10 @@ ndisdrv_modevent(mod, cmd, arg)
/* fallthrough */
case MOD_SHUTDOWN:
windrv_unwrap(ndis_rxeof_wrap);
+ windrv_unwrap(ndis_rxeof_eth_wrap);
+ windrv_unwrap(ndis_rxeof_done_wrap);
+ windrv_unwrap(ndis_rxeof_xfr_wrap);
+ windrv_unwrap(ndis_rxeof_xfr_done_wrap);
windrv_unwrap(ndis_txeof_wrap);
windrv_unwrap(ndis_linksts_wrap);
windrv_unwrap(ndis_linksts_done_wrap);
@@ -520,6 +542,9 @@ ndis_attach(dev)
/* Install our RX and TX interrupt handlers. */
sc->ndis_block->nmb_senddone_func = ndis_txeof_wrap;
sc->ndis_block->nmb_pktind_func = ndis_rxeof_wrap;
+ sc->ndis_block->nmb_ethrxindicate_func = ndis_rxeof_eth_wrap;
+ sc->ndis_block->nmb_ethrxdone_func = ndis_rxeof_done_wrap;
+ sc->ndis_block->nmb_tdcond_func = ndis_rxeof_xfr_done_wrap;
/* Call driver's init routine. */
if (ndis_init_nic(sc)) {
@@ -812,6 +837,8 @@ nonettypes:
(work_item_func)ndis_starttask_wrap, ifp);
NdisInitializeWorkItem(&sc->ndis_resetitem,
(work_item_func)ndis_resettask_wrap, sc);
+ KeInitializeDpc(&sc->ndis_rxdpc, ndis_rxeof_xfr_wrap, sc->ndis_block);
+
fail:
if (error)
@@ -947,6 +974,204 @@ ndis_resume(dev)
}
/*
+ * The following bunch of routines are here to support drivers that
+ * use the NdisMEthIndicateReceive()/MiniportTransferData() mechanism.
+ */
+
+static void
+ndis_rxeof_eth(adapter, ctx, addr, hdr, hdrlen, lookahead, lookaheadlen, pktlen)
+ ndis_handle adapter;
+ ndis_handle ctx;
+ char *addr;
+ void *hdr;
+ uint32_t hdrlen;
+ void *lookahead;
+ uint32_t lookaheadlen;
+ uint32_t pktlen;
+{
+ ndis_miniport_block *block;
+ uint8_t irql;
+ uint32_t status;
+ ndis_buffer *b;
+ ndis_packet *p;
+ struct mbuf *m;
+ ndis_ethpriv *priv;
+
+ block = adapter;
+
+ m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
+
+ if (m == NULL) {
+ NdisFreePacket(p);
+ return;
+ }
+
+ /* Save the data provided to us so far. */
+
+ m->m_len = lookaheadlen + hdrlen;
+ m->m_pkthdr.len = pktlen + hdrlen;
+ m->m_next = NULL;
+ m_copyback(m, 0, hdrlen, hdr);
+ m_copyback(m, hdrlen, lookaheadlen, lookahead);
+
+ /* Now create a fake NDIS_PACKET to hold the data */
+
+ NdisAllocatePacket(&status, &p, block->nmb_rxpool);
+
+ if (status != NDIS_STATUS_SUCCESS) {
+ m_freem(m);
+ return;
+ }
+
+ p->np_m0 = m;
+
+ b = IoAllocateMdl(m->m_data, m->m_pkthdr.len, FALSE, FALSE, NULL);
+
+ if (b == NULL) {
+ NdisFreePacket(p);
+ m_freem(m);
+ return;
+ }
+
+ p->np_private.npp_head = p->np_private.npp_tail = b;
+ p->np_private.npp_totlen = m->m_pkthdr.len;
+
+ /* Save the packet RX context somewhere. */
+ priv = (ndis_ethpriv *)&p->np_protocolreserved;
+ priv->nep_ctx = ctx;
+
+ KeAcquireSpinLock(&block->nmb_lock, &irql);
+
+ INSERT_LIST_TAIL((&block->nmb_packetlist),
+ ((list_entry *)&p->u.np_clrsvd.np_miniport_rsvd));
+
+ KeReleaseSpinLock(&block->nmb_lock, irql);
+
+ return;
+}
+
+static void
+ndis_rxeof_done(adapter)
+ ndis_handle adapter;
+{
+ struct ndis_softc *sc;
+ ndis_miniport_block *block;
+
+ block = adapter;
+
+ /* Schedule transfer/RX of queued packets. */
+
+ sc = device_get_softc(block->nmb_physdeviceobj->do_devext);
+
+ KeInsertQueueDpc(&sc->ndis_rxdpc, NULL, NULL);
+
+ return;
+}
+
+/*
+ * Runs at DISPATCH_LEVEL.
+ */
+static void
+ndis_rxeof_xfr(dpc, adapter, sysarg1, sysarg2)
+ kdpc *dpc;
+ ndis_handle adapter;
+ void *sysarg1;
+ void *sysarg2;
+{
+ ndis_miniport_block *block;
+ struct ndis_softc *sc;
+ ndis_packet *p;
+ list_entry *l;
+ uint32_t status;
+ ndis_ethpriv *priv;
+ struct ifnet *ifp;
+ struct mbuf *m;
+
+ block = adapter;
+ sc = device_get_softc(block->nmb_physdeviceobj->do_devext);
+ ifp = &sc->arpcom.ac_if;
+
+ KeAcquireSpinLockAtDpcLevel(&block->nmb_lock);
+
+ l = block->nmb_packetlist.nle_flink;
+ while(l != &block->nmb_packetlist) {
+ REMOVE_LIST_HEAD((&block->nmb_packetlist));
+ p = CONTAINING_RECORD(l, ndis_packet,
+ u.np_clrsvd.np_miniport_rsvd);
+
+ priv = (ndis_ethpriv *)&p->np_protocolreserved;
+ m = p->np_m0;
+ p->np_softc = sc;
+ p->np_m0 = NULL;
+
+ KeReleaseSpinLockFromDpcLevel(&block->nmb_lock);
+
+ status = MSCALL6(sc->ndis_chars->nmc_transferdata_func,
+ p, &p->np_private.npp_totlen, block, priv->nep_ctx,
+ m->m_len, m->m_pkthdr.len - m->m_len);
+
+ KeAcquireSpinLockAtDpcLevel(&block->nmb_lock);
+
+ /*
+ * If status is NDIS_STATUS_PENDING, do nothing and
+ * wait for a callback to the ndis_rxeof_xfr_done()
+ * handler.
+ */
+
+ m->m_len = m->m_pkthdr.len;
+ m->m_pkthdr.rcvif = ifp;
+
+ if (status == NDIS_STATUS_SUCCESS) {
+ IoFreeMdl(p->np_private.npp_head);
+ NdisFreePacket(p);
+ ifp->if_ipackets++;
+ (*ifp->if_input)(ifp, m);
+ }
+
+ if (status == NDIS_STATUS_FAILURE)
+ m_freem(m);
+
+ /* Advance to next packet */
+ l = block->nmb_packetlist.nle_flink;
+ }
+
+ KeReleaseSpinLockFromDpcLevel(&block->nmb_lock);
+
+ return;
+}
+
+static void
+ndis_rxeof_xfr_done(adapter, packet, status, len)
+ ndis_handle adapter;
+ ndis_packet *packet;
+ uint32_t status;
+ uint32_t len;
+{
+ ndis_miniport_block *block;
+ struct ndis_softc *sc;
+ struct ifnet *ifp;
+ struct mbuf *m;
+
+ block = adapter;
+ sc = device_get_softc(block->nmb_physdeviceobj->do_devext);
+ ifp = &sc->arpcom.ac_if;
+
+ m = packet->np_m0;
+ IoFreeMdl(packet->np_private.npp_head);
+ NdisFreePacket(packet);
+
+ if (status != NDIS_STATUS_SUCCESS) {
+ m_freem(m);
+ return;
+ }
+
+ m->m_len = m->m_pkthdr.len;
+ m->m_pkthdr.rcvif = ifp;
+ ifp->if_ipackets++;
+ (*ifp->if_input)(ifp, m);
+ return;
+}
+/*
* A frame has been uploaded: pass the resulting mbuf chain up to
* the higher level protocols.
*
diff --git a/sys/dev/if_ndis/if_ndisvar.h b/sys/dev/if_ndis/if_ndisvar.h
index 06f394e..bcab175 100644
--- a/sys/dev/if_ndis/if_ndisvar.h
+++ b/sys/dev/if_ndis/if_ndisvar.h
@@ -127,6 +127,7 @@ struct ndis_softc {
ndis_work_item ndis_tickitem;
ndis_work_item ndis_startitem;
ndis_work_item ndis_resetitem;
+ kdpc ndis_rxdpc;
bus_dma_tag_t ndis_parent_tag;
struct ndis_shmem *ndis_shlist;
bus_dma_tag_t ndis_mtag;
OpenPOWER on IntegriCloud