NetBSD-Bugs archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: kern/59939: usbnet(9): support multi-packet tx transfers



Reworked and adapted to ncm(4) which needs a little more structure
than ure(4) -- while ure(4) just concatenates (header, payload)
sequences in a transfer, ncm(4) uses a transfer header and a table of
packet headers.  (Would like to see a third example of a device that
can handle multiple packets per xfer -- suggestions with documentation
references welcome!)

So I split it into

uno_tx_begin
uno_tx_add(m0)
uno_tx_add(m1)
uno_tx_add(m2)
uno_tx_commit(m0)

with m0->m_next = m1, m1->m_next = m2, m2->m_next = NULL in
uno_tx_commit.

This way, the driver has a chance to vet each packet first (so we can
stop and leave a packet in the queue if it would be too long), and
then can format the chosen batch into a transfer all at once.

Drivers can keep state for the tx batch but don't need to;
uno_tx_begin and uno_tx_add say how much overhead is needed and
usbnet(9) will ensure there's space for the overhead so far plus
mi->m_pkthdr.len before even trying uno_tx_add.  So most drivers will
just need to return the number of bytes of per-transfer overhead in
uno_tx_begin and per-packet overhead in uno_tx_add, and usbnet(9) will
take care of the rest of bounds checking.

Still no ABI compat to allow us to pull this up later.  Thinking of
something like:

#define	USBNET_API_VERSION	1
#define	usbnet_attach(...)	usbnet_attach_real(USBNET_API_VERSION, ...)

but hoping to find a way to avoid copying & pasting the content of
struct usbnet_ops across struct usbnet_ops_v1, struct usbnet_ops_v2,
&c., without making the internal API or the module API a mess.

Also rebased on top of a couple other patches pending for:

kern/59940: usbnet(9): uno_tx_prepare buffer overrun audit
kern/59943: usbnet(9) keeps failed packet in queue to retry indefinitely
# HG changeset patch
# User Taylor R Campbell <riastradh%NetBSD.org@localhost>
# Date 1769290484 0
#      Sat Jan 24 21:34:44 2026 +0000
# Branch trunk
# Node ID 187d0d4c9c2915c349bd7475c40fb49b152c0492
# Parent  f2a961ab939686586e96a4a42110282d83dcad30
# EXP-Topic riastradh-pr59939-usbnetmultipkttxxfer
WIP: usbnet(9): Support multi-packet tx transfers.

New usbnet_ops:

overhead = uno_tx_begin(un)

   Returns the number of bytes of overhead in a USB transfer,
   independent of the packets being transmitted, or -1 if tx is blocked
   for any reason.

   If successful (i.e., if returns >=0), always followed by a sequence
   of calls to uno_tx_add and then either uno_tx_abort or
   uno_tx_commit.

overhead = uno_tx_add(un, m, pktno, offset)

   Returns the number of bytes of overhead in a USB transfer to
   transmit the packet m _beyond_ m->m_pkthdr.len, or -1 if tx is
   blocked for any reason.

   pktno is the zero-based sequence number of m in the transfer, and
   offset is the number of bytes reserved so far by uno_tx_begin and
   uno_tx_add and all the packet lengths.  In other words, if packets
   m0, m1, ..., mk have already passed through uno_tx_add, then

        pktno = k + 1
        offset = uno_tx_begin(un)
          + uno_tx_add(un, m0, ...) + m0->m_pkthdr.len
          + uno_tx_add(un, m1, ...) + m1->m_pkthdr.len
          ...
          + uno_tx_add(un, mk, ...) + mk->m_pkthdr.len

   Caller has guaranteed the transfer buffer already has space for
   offset + m->m_pkthdr.len bytes.

uno_tx_abort(un)

   Called if the caller has decided not to transmit the packets it
   previously passed to uno_tx_begin/add.

   Optional; may be omitted if it has nothing to do.

uno_tx_commit(un, m0, c, npkt, nbyte)

   Fill usbnet transfer chain c's buffer with any device-specific
   headers and payloads of the linked list of npkt packets headed by m0
   linked through m_nexptkt previously passed in order to uno_tx_add,
   with a total of nbyte bytes reserved in the transfer, guaranteed to
   be at most un->un_tx_bufsz.

XXX kernel revbump -- breaks usbnet(9) module ABI by expanding struct
usbnet_ops with no version or feature flag on which to gate access

kern/59939: usbnet(9): support multi-packet tx transfers

diff -r f2a961ab9396 -r 187d0d4c9c29 sys/dev/usb/usbnet.c
--- a/sys/dev/usb/usbnet.c	Mon Jan 26 01:55:52 2026 +0000
+++ b/sys/dev/usb/usbnet.c	Sat Jan 24 21:34:44 2026 +0000
@@ -229,6 +229,45 @@ uno_tx_prepare(struct usbnet *un, struct
 	return (*un->un_ops->uno_tx_prepare)(un, m, c);
 }
 
+static int
+uno_tx_begin(struct usbnet *un)
+{
+	usbnet_isowned_tx(un);
+	return (*un->un_ops->uno_tx_begin)(un);
+}
+
+static int
+uno_tx_add(struct usbnet *un, struct mbuf *m, unsigned pktno, unsigned offset)
+{
+	usbnet_isowned_tx(un);
+	return (*un->un_ops->uno_tx_add)(un, m, pktno, offset);
+}
+
+static void
+uno_tx_abort(struct usbnet *un)
+{
+	usbnet_isowned_tx(un);
+	if (un->un_ops->uno_tx_abort)
+		return (*un->un_ops->uno_tx_abort)(un);
+}
+
+static void
+uno_tx_commit(struct usbnet *un, struct mbuf *m, struct usbnet_chain *c,
+    unsigned npkt, unsigned nbyte)
+{
+	usbnet_isowned_tx(un);
+	return (*un->un_ops->uno_tx_commit)(un, m, c, npkt, nbyte);
+}
+
+static bool
+usbnet_batch_tx(struct usbnet *un)
+{
+
+	return un->un_ops->uno_tx_begin != NULL &&
+	    un->un_ops->uno_tx_add != NULL &&
+	    un->un_ops->uno_tx_commit != NULL;
+}
+
 static void
 uno_rx_loop(struct usbnet *un, struct usbnet_chain *c, uint32_t total_len)
 {
@@ -521,11 +560,16 @@ usbnet_start_locked(struct ifnet *ifp)
 	idx = cd->uncd_tx_prod;
 	count = 0;
 	while (cd->uncd_tx_cnt < un->un_tx_list_cnt) {
+		struct mbuf *batch = NULL, **batch_tail = &batch;
+		unsigned npkt = 0;
+
 		IFQ_DEQUEUE(&ifp->if_snd, m);
 		if (m == NULL) {
 			DPRINTF("start called, queue empty", 0, 0, 0, 0);
 			break;
 		}
+		npkt++;
+
 		if (m->m_pkthdr.len > un->un_tx_bufsz) {
 			DPRINTF("oversize packet, %ju > %ju",
 			    m->m_pkthdr.len, un->un_tx_bufsz, 0, 0);
@@ -535,9 +579,67 @@ usbnet_start_locked(struct ifnet *ifp)
 		}
 		KASSERT(m->m_pkthdr.len <= un->un_tx_bufsz);
 
-		struct usbnet_chain *c = &cd->uncd_tx_chain[idx];
+		struct usbnet_chain *const c = &cd->uncd_tx_chain[idx];
 		KASSERT(c->unc_xfer != NULL);
 
+		if (usbnet_batch_tx(un)) {
+			unsigned offset = 0;
+			int overhead;
+
+			overhead = uno_tx_begin(un);
+			KASSERT(overhead == -1 ||
+			    (unsigned)overhead <= un->un_tx_bufsz);
+			if (overhead == -1 ||
+			    (unsigned)overhead > un->un_tx_bufsz) {
+				DPRINTF("uno_tx_begin failed", 0, 0, 0, 0);
+				if_statinc(ifp, if_oerrors);
+				m_freem(m);
+				continue;
+			}
+			offset = (unsigned)overhead;
+
+			overhead = uno_tx_add(un, m, 0, offset);
+			if (overhead == -1 ||
+			    (unsigned)overhead > un->un_tx_bufsz - offset ||
+			    ((unsigned)m->m_pkthdr.len >
+				(un->un_tx_bufsz - offset -
+				    (unsigned)overhead))) {
+				DPRINTF("uno_tx_add failed", 0, 0, 0, 0);
+				uno_tx_abort(un);
+				if_statinc(ifp, if_oerrors);
+				m_freem(m);
+				continue;
+			}
+			for (;;) {
+				m->m_nextpkt = NULL;
+				*batch_tail = m;
+				batch_tail = &m->m_nextpkt;
+				IFQ_POLL(&ifp->if_snd, m);
+				if (m == NULL)
+					break;
+				KASSERT(m->m_pkthdr.len <= un->un_tx_bufsz);
+				if ((unsigned)m->m_pkthdr.len >
+				    un->un_tx_bufsz - offset) {
+					m = NULL; /* leave in ifp->if_snd q */
+					break;
+				}
+				overhead = uno_tx_add(un, m, npkt, offset);
+				if (overhead == -1 ||
+				    (unsigned)overhead > (un->un_tx_bufsz -
+					offset - (unsigned)m->m_pkthdr.len)) {
+					m = NULL; /* leave in ifp->if_snd q */
+					break;
+				}
+				IFQ_DEQUEUE(&ifp->if_snd, m);
+				npkt++;
+				offset += (unsigned)overhead;
+				offset += (unsigned)m->m_pkthdr.len;
+			}
+			uno_tx_commit(un, batch, c, npkt, offset);
+			length = offset;
+			goto xfer;
+		}
+
 		length = uno_tx_prepare(un, m, c);
 		if (length == 0) {
 			DPRINTF("uno_tx_prepare gave zero length", 0, 0, 0, 0);
@@ -546,6 +648,8 @@ usbnet_start_locked(struct ifnet *ifp)
 			continue;
 		}
 
+xfer:		KASSERTMSG(length <= un->un_tx_bufsz,
+		    "length=%u bufsz=%u", length, un->un_tx_bufsz);
 		usbd_setup_xfer(c->unc_xfer, c, c->unc_buf, length,
 		    un->un_tx_xfer_flags, 10000, usbnet_txeof);
 
@@ -554,18 +658,35 @@ usbnet_start_locked(struct ifnet *ifp)
 		if (err != USBD_IN_PROGRESS) {
 			DPRINTF("usbd_transfer on %#jx for %ju bytes: %jd",
 			    (uintptr_t)c->unc_buf, length, err, 0);
-			if_statinc(ifp, if_oerrors);
-			m_freem(m);
+			if_statadd(ifp, if_oerrors, npkt);
+			if (batch) {
+				while (batch != NULL) {
+					m = batch;
+					batch = m->m_nextpkt;
+					m_freem(m);
+				}
+			} else {
+				m_freem(m);
+			}
 			continue;
 		}
 		done_transmit = true;
 
 		/*
-		 * If there's a BPF listener, bounce a copy of this frame
-		 * to him.
+		 * If there's a BPF listener, bounce a copy of these
+		 * frames to them, and free them all.
 		 */
-		bpf_mtap(ifp, m, BPF_D_OUT);
-		m_freem(m);
+		if (batch) {
+			while (batch != NULL) {
+				m = batch;
+				batch = m->m_nextpkt;
+				bpf_mtap(ifp, m, BPF_D_OUT);
+				m_freem(m);
+			}
+		} else {
+			bpf_mtap(ifp, m, BPF_D_OUT);
+			m_freem(m);
+		}
 
 		idx = (idx + 1) % un->un_tx_list_cnt;
 		cd->uncd_tx_cnt++;
diff -r f2a961ab9396 -r 187d0d4c9c29 sys/dev/usb/usbnet.h
--- a/sys/dev/usb/usbnet.h	Mon Jan 26 01:55:52 2026 +0000
+++ b/sys/dev/usb/usbnet.h	Sat Jan 24 21:34:44 2026 +0000
@@ -141,6 +141,12 @@ typedef void (*usbnet_mii_statchg_cb)(st
 /* Prepare packet to send callback, returns length. */
 typedef unsigned (*usbnet_tx_prepare_cb)(struct usbnet *, struct mbuf *,
 					 struct usbnet_chain *);
+typedef int (*usbnet_tx_begin_cb)(struct usbnet *);
+typedef int (*usbnet_tx_add_cb)(struct usbnet *, struct mbuf *,
+				unsigned, unsigned);
+typedef void (*usbnet_tx_abort_cb)(struct usbnet *);
+typedef void (*usbnet_tx_commit_cb)(struct usbnet *, struct mbuf *,
+				    struct usbnet_chain *, unsigned, unsigned);
 /* Receive some packets callback. */
 typedef void (*usbnet_rx_loop_cb)(struct usbnet *, struct usbnet_chain *,
 				  uint32_t);
@@ -181,6 +187,10 @@ struct usbnet_ops {
 	usbnet_rx_loop_cb	uno_rx_loop;		/* R */
 	usbnet_tick_cb		uno_tick;		/* n */
 	usbnet_intr_cb		uno_intr;		/* n */
+	usbnet_tx_begin_cb	uno_tx_begin;		/* T */
+	usbnet_tx_add_cb	uno_tx_add;		/* T */
+	usbnet_tx_abort_cb	uno_tx_abort;		/* T */
+	usbnet_tx_commit_cb	uno_tx_commit;		/* T */
 };
 
 /*
# HG changeset patch
# User Taylor R Campbell <riastradh%NetBSD.org@localhost>
# Date 1769290712 0
#      Sat Jan 24 21:38:32 2026 +0000
# Branch trunk
# Node ID 747aaf9e3a41dde20b9e50749762a8ab5329e16e
# Parent  187d0d4c9c2915c349bd7475c40fb49b152c0492
# EXP-Topic riastradh-pr59939-usbnetmultipkttxxfer
WIP: ure(4): Support multi-packet tx transfers.

PR kern/59939: usbnet(9): support multi-packet tx transfers

diff -r 187d0d4c9c29 -r 747aaf9e3a41 sys/dev/usb/if_ure.c
--- a/sys/dev/usb/if_ure.c	Sat Jan 24 21:34:44 2026 +0000
+++ b/sys/dev/usb/if_ure.c	Sat Jan 24 21:38:32 2026 +0000
@@ -93,6 +93,11 @@ static int	ure_uno_mii_write_reg(struct 
 static void	ure_uno_miibus_statchg(struct ifnet *);
 static unsigned ure_uno_tx_prepare(struct usbnet *, struct mbuf *,
 				   struct usbnet_chain *);
+static int	ure_uno_tx_begin(struct usbnet *);
+static int	ure_uno_tx_add(struct usbnet *, struct mbuf *,
+			       unsigned, unsigned);
+static void	ure_uno_tx_commit(struct usbnet *, struct mbuf *,
+				  struct usbnet_chain *, unsigned, unsigned);
 static void	ure_uno_rx_loop(struct usbnet *, struct usbnet_chain *,
 				uint32_t);
 static int	ure_uno_init(struct ifnet *);
@@ -110,6 +115,9 @@ static const struct usbnet_ops ure_ops =
 	.uno_write_reg = ure_uno_mii_write_reg,
 	.uno_statchg = ure_uno_miibus_statchg,
 	.uno_tx_prepare = ure_uno_tx_prepare,
+	.uno_tx_begin = ure_uno_tx_begin,
+	.uno_tx_add = ure_uno_tx_add,
+	.uno_tx_commit = ure_uno_tx_commit,
 	.uno_rx_loop = ure_uno_rx_loop,
 	.uno_init = ure_uno_init,
 };
@@ -1024,14 +1032,18 @@ ure_rxcsum(struct ifnet *ifp, struct ure
 }
 
 static unsigned
-ure_uno_tx_prepare(struct usbnet *un, struct mbuf *m, struct usbnet_chain *c)
+ure_uno_tx_prepare_offset(struct usbnet *un, struct mbuf *m,
+    struct usbnet_chain *c, unsigned offset)
 {
 	struct ure_txpkt txhdr;
 	uint32_t frm_len = 0;
-	uint8_t *buf = c->unc_buf;
+	uint8_t *buf = c->unc_buf + offset;
 
-	if ((unsigned)m->m_pkthdr.len > un->un_tx_bufsz - sizeof(txhdr))
-		return 0;
+	__CTASSERT(sizeof(txhdr) <= URE_BUFSZ);
+	KASSERT(sizeof(txhdr) <= un->un_tx_bufsz);
+	KASSERT(offset <= un->un_tx_bufsz - sizeof(txhdr));
+	KASSERT((unsigned)m->m_pkthdr.len <=
+	    un->un_tx_bufsz - sizeof(txhdr) - offset);
 
 	/* header */
 	txhdr.ure_pktlen = htole32(m->m_pkthdr.len | URE_TXPKT_TX_FS |
@@ -1050,6 +1062,51 @@ ure_uno_tx_prepare(struct usbnet *un, st
 	return frm_len;
 }
 
+static unsigned
+ure_uno_tx_prepare(struct usbnet *un, struct mbuf *m, struct usbnet_chain *c)
+{
+
+	__CTASSERT(sizeof(struct ure_txpkt) <= URE_BUFSZ);
+	KASSERT(sizeof(struct ure_txpkt) <= un->un_tx_bufsz);
+	if ((unsigned)m->m_pkthdr.len >
+	    un->un_tx_bufsz - sizeof(struct ure_txpkt))
+		return 0;
+
+	return ure_uno_tx_prepare_offset(un, m, c, 0);
+}
+
+static int
+ure_uno_tx_begin(struct usbnet *un)
+{
+
+	/* zero per-packet overhead */
+	return 0;
+}
+
+static int
+ure_uno_tx_add(struct usbnet *un, struct mbuf *m,
+    unsigned pktno, unsigned offset)
+{
+
+	return sizeof(struct ure_txpkt);
+}
+
+static void
+ure_uno_tx_commit(struct usbnet *un, struct mbuf *m0, struct usbnet_chain *c,
+    unsigned npkt, unsigned nbyte)
+{
+	struct mbuf *m;
+	unsigned offset = 0;
+
+	for (m = m0; m != NULL; m = m->m_nextpkt) {
+		KASSERT(offset <= nbyte -
+		    (sizeof(struct ure_txpkt) + (unsigned)m->m_pkthdr.len));
+		offset += ure_uno_tx_prepare_offset(un, m, c, offset);
+		KASSERT(offset <= nbyte);
+	}
+	KASSERT(offset == nbyte);
+}
+
 /*
  * We need to calculate L4 checksum in software, if the offset of
  * L4 header is larger than 0x7ff = 2047.
# HG changeset patch
# User Taylor R Campbell <riastradh%NetBSD.org@localhost>
# Date 1769297008 0
#      Sat Jan 24 23:23:28 2026 +0000
# Branch trunk
# Node ID d390134355e39af36750bd2a3046f28380706b53
# Parent  747aaf9e3a41dde20b9e50749762a8ab5329e16e
# EXP-Topic riastradh-pr59939-usbnetmultipkttxxfer
WIP: ncm(4): Support multi-packet tx transfers.

PR kern/59939: usbnet(9): support multi-packet tx transfers

diff -r 747aaf9e3a41 -r d390134355e3 sys/dev/usb/if_ncm.c
--- a/sys/dev/usb/if_ncm.c	Sat Jan 24 21:38:32 2026 +0000
+++ b/sys/dev/usb/if_ncm.c	Sat Jan 24 23:23:28 2026 +0000
@@ -67,9 +67,17 @@ static void	ncm_uno_rx_loop(struct usbne
 		    uint32_t);
 static unsigned	ncm_uno_tx_prepare(struct usbnet *, struct mbuf *,
 		    struct usbnet_chain *);
+static int	ncm_uno_tx_begin(struct usbnet *);
+static int	ncm_uno_tx_add(struct usbnet *, struct mbuf *,
+		    unsigned, unsigned);
+static void	ncm_uno_tx_commit(struct usbnet *, struct mbuf *,
+		    struct usbnet_chain *, unsigned, unsigned);
 
 static const struct usbnet_ops ncm_ops = {
 	.uno_tx_prepare = ncm_uno_tx_prepare,
+	.uno_tx_begin = ncm_uno_tx_begin,
+	.uno_tx_add = ncm_uno_tx_add,
+	.uno_tx_commit = ncm_uno_tx_commit,
 	.uno_rx_loop = ncm_uno_rx_loop,
 };
 
@@ -328,7 +336,7 @@ ncm_uno_tx_prepare(struct usbnet *un, st
 	struct ncm_header16 *hdr;
 	unsigned hdr_len, len;
 
-	hdr_len = sizeof(*hdr) + sizeof(*ptr);
+	hdr_len = sizeof(*hdr) + offsetof(struct ncm_dptab16, datagram[2]);
 	len = m->m_pkthdr.len;
 	KASSERT(hdr_len <= un->un_tx_bufsz);
 	if (len > un->un_tx_bufsz - hdr_len)
@@ -348,7 +356,7 @@ ncm_uno_tx_prepare(struct usbnet *un, st
 	USETW(hdr->wNdpIndex, sizeof(*hdr));
 
 	USETDW(ptr->dwSignature, NCM_PTR16_SIG_NO_CRC);
-	USETW(ptr->wLength, sizeof(*ptr));
+	USETW(ptr->wLength, offsetof(struct ncm_dptab16, datagram[2]));
 	USETW(ptr->wNextNdpIndex, 0);
 
 	USETW(ptr->datagram[0].wDatagramIndex, hdr_len);
@@ -362,6 +370,72 @@ ncm_uno_tx_prepare(struct usbnet *un, st
 	return len + hdr_len;
 }
 
+static int
+ncm_uno_tx_begin(struct usbnet *un)
+{
+
+	return sizeof(struct ncm_header16) +
+	    sizeof(struct ncm_dptab16) +
+	    sizeof(struct ncm_pointer16);
+}
+
+static int
+ncm_uno_tx_add(struct usbnet *un, struct mbuf *m,
+    unsigned pktno, unsigned offset)
+{
+
+	return sizeof(struct ncm_pointer16);
+}
+
+static void
+ncm_uno_tx_commit(struct usbnet *un, struct mbuf *m0, struct usbnet_chain *c,
+    unsigned npkt, unsigned nbyte)
+{
+	struct ncm_softc *sc = un->un_sc;
+	struct ncm_header16 *hdr;
+	struct ncm_dptab16 *ptr;
+	unsigned i, off, len;
+	struct mbuf *m;
+
+	/*
+	 * Where in the buffer does the payload start?  We have to pass
+	 * the header, and then the pointer table, which contains as
+	 * many entries as there are packets plus one more all-zero
+	 * pointer to terminate the chain.
+	 */
+	off = sizeof(*hdr) + sizeof(*ptr) + (npkt + 1)*sizeof(ptr->datagram);
+
+	hdr = (void *)c->unc_buf;
+	memset(hdr, 0, sizeof(*hdr));
+	USETDW(hdr->dwSignature, NCM_HDR16_SIG);
+	USETW(hdr->wHeaderLength, sizeof(*hdr));
+	USETW(hdr->wSequence, sc->sc_tx_seq);
+	sc->sc_tx_seq++;
+	USETW(hdr->wBlockLength, nbyte);
+	USETW(hdr->wNdpIndex, sizeof(*hdr));
+
+	ptr = (void *)(hdr + 1);
+	memset(ptr, 0, sizeof(*ptr));
+	USETDW(ptr->dwSignature, NCM_PTR16_SIG_NO_CRC);
+	USETW(ptr->wLength, off);
+	USETW(ptr->wNextNdpIndex, 0);
+
+	for (i = 0, m = m0; m != NULL; i++, m = m->m_nextpkt) {
+		KASSERT(off < un->un_tx_bufsz);
+		len = (unsigned)m->m_pkthdr.len;
+		KASSERT(len <= un->un_tx_bufsz - off);
+		memset(&ptr->datagram[i], 0, sizeof(ptr->datagram[0]));
+		USETW(ptr->datagram[i].wDatagramIndex, off);
+		USETW(ptr->datagram[i].wDatagramLength, len);
+		m_copydata(m, 0, len, &c->unc_buf[off]);
+		off += len;
+	}
+	USETW(ptr->datagram[i].wDatagramIndex, 0);
+	USETW(ptr->datagram[i].wDatagramLength, 0);
+
+	KASSERT(off == nbyte);
+}
+
 #ifdef _MODULE
 #include "ioconf.c"
 #endif
diff -r 747aaf9e3a41 -r d390134355e3 sys/dev/usb/if_ncmreg.h
--- a/sys/dev/usb/if_ncmreg.h	Sat Jan 24 21:38:32 2026 +0000
+++ b/sys/dev/usb/if_ncmreg.h	Sat Jan 24 23:23:28 2026 +0000
@@ -73,7 +73,7 @@ struct ncm_dptab16 {
 	uDWord			dwSignature;
 	uWord			wLength;
 	uWord			wNextNdpIndex;
-	struct ncm_pointer16	datagram[2];
+	struct ncm_pointer16	datagram[0];
 } UPACKED;
 
 struct ncm_pointer32 {
@@ -89,7 +89,7 @@ struct ncm_dptab32 {
 	uWord			wReserved6;
 	uWord			wNextNdpIndex;
 	uDWord			dwReserved12;
-	struct ncm_pointer32	datagram[2];
+	struct ncm_pointer32	datagram[0];
 } UPACKED;
 
 #define NCM_GET_NTB_PARAMETERS 0x80


Home | Main Index | Thread Index | Old Index