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



The following reply was made to PR kern/59939; it has been noted by GNATS.

From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
To: netbsd-bugs%NetBSD.org@localhost, gnats-bugs%NetBSD.org@localhost
Cc: Jared McNeill <jmcneill%invisible.ca@localhost>,
	Martin Husemann <martin%duskware.de@localhost>,
	Nick Hudson <nick.hudson%gmx.co.uk@localhost>,
	matthew green <mrg%eterna23.net@localhost>
Subject: Re: kern/59939: usbnet(9): support multi-packet tx transfers
Date: Mon, 26 Jan 2026 04:39:05 +0000

 This is a multi-part message in MIME format.
 --=_su1OON3HGWIM8fZuEZsawKeY4iHDSE1Z
 
 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
 
 --=_su1OON3HGWIM8fZuEZsawKeY4iHDSE1Z
 Content-Type: text/plain; charset="ISO-8859-1"; name="pr59939-multipkttxxfer-v2"
 Content-Transfer-Encoding: quoted-printable
 Content-Disposition: attachment; filename="pr59939-multipkttxxfer-v2.patch"
 
 # 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 =3D 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 >=3D0), always followed by a sequence
    of calls to uno_tx_add and then either uno_tx_abort or
    uno_tx_commit.
 
 overhead =3D 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 =3D k + 1
         offset =3D 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);
  }
 =20
 +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 off=
 set)
 +{
 +	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 !=3D NULL &&
 +	    un->un_ops->uno_tx_add !=3D NULL &&
 +	    un->un_ops->uno_tx_commit !=3D 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 =3D cd->uncd_tx_prod;
  	count =3D 0;
  	while (cd->uncd_tx_cnt < un->un_tx_list_cnt) {
 +		struct mbuf *batch =3D NULL, **batch_tail =3D &batch;
 +		unsigned npkt =3D 0;
 +
  		IFQ_DEQUEUE(&ifp->if_snd, m);
  		if (m =3D=3D 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 <=3D un->un_tx_bufsz);
 =20
 -		struct usbnet_chain *c =3D &cd->uncd_tx_chain[idx];
 +		struct usbnet_chain *const c =3D &cd->uncd_tx_chain[idx];
  		KASSERT(c->unc_xfer !=3D NULL);
 =20
 +		if (usbnet_batch_tx(un)) {
 +			unsigned offset =3D 0;
 +			int overhead;
 +
 +			overhead =3D uno_tx_begin(un);
 +			KASSERT(overhead =3D=3D -1 ||
 +			    (unsigned)overhead <=3D un->un_tx_bufsz);
 +			if (overhead =3D=3D -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 =3D (unsigned)overhead;
 +
 +			overhead =3D uno_tx_add(un, m, 0, offset);
 +			if (overhead =3D=3D -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 =3D NULL;
 +				*batch_tail =3D m;
 +				batch_tail =3D &m->m_nextpkt;
 +				IFQ_POLL(&ifp->if_snd, m);
 +				if (m =3D=3D NULL)
 +					break;
 +				KASSERT(m->m_pkthdr.len <=3D un->un_tx_bufsz);
 +				if ((unsigned)m->m_pkthdr.len >
 +				    un->un_tx_bufsz - offset) {
 +					m =3D NULL; /* leave in ifp->if_snd q */
 +					break;
 +				}
 +				overhead =3D uno_tx_add(un, m, npkt, offset);
 +				if (overhead =3D=3D -1 ||
 +				    (unsigned)overhead > (un->un_tx_bufsz -
 +					offset - (unsigned)m->m_pkthdr.len)) {
 +					m =3D NULL; /* leave in ifp->if_snd q */
 +					break;
 +				}
 +				IFQ_DEQUEUE(&ifp->if_snd, m);
 +				npkt++;
 +				offset +=3D (unsigned)overhead;
 +				offset +=3D (unsigned)m->m_pkthdr.len;
 +			}
 +			uno_tx_commit(un, batch, c, npkt, offset);
 +			length =3D offset;
 +			goto xfer;
 +		}
 +
  		length =3D uno_tx_prepare(un, m, c);
  		if (length =3D=3D 0) {
  			DPRINTF("uno_tx_prepare gave zero length", 0, 0, 0, 0);
 @@ -546,6 +648,8 @@ usbnet_start_locked(struct ifnet *ifp)
  			continue;
  		}
 =20
 +xfer:		KASSERTMSG(length <=3D un->un_tx_bufsz,
 +		    "length=3D%u bufsz=3D%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);
 =20
 @@ -554,18 +658,35 @@ usbnet_start_locked(struct ifnet *ifp)
  		if (err !=3D 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 !=3D NULL) {
 +					m =3D batch;
 +					batch =3D m->m_nextpkt;
 +					m_freem(m);
 +				}
 +			} else {
 +				m_freem(m);
 +			}
  			continue;
  		}
  		done_transmit =3D true;
 =20
  		/*
 -		 * 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 !=3D NULL) {
 +				m =3D batch;
 +				batch =3D m->m_nextpkt;
 +				bpf_mtap(ifp, m, BPF_D_OUT);
 +				m_freem(m);
 +			}
 +		} else {
 +			bpf_mtap(ifp, m, BPF_D_OUT);
 +			m_freem(m);
 +		}
 =20
  		idx =3D (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 */
  };
 =20
  /*
 # 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=20
  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 =3D
  	.uno_write_reg =3D ure_uno_mii_write_reg,
  	.uno_statchg =3D ure_uno_miibus_statchg,
  	.uno_tx_prepare =3D ure_uno_tx_prepare,
 +	.uno_tx_begin =3D ure_uno_tx_begin,
 +	.uno_tx_add =3D ure_uno_tx_add,
 +	.uno_tx_commit =3D ure_uno_tx_commit,
  	.uno_rx_loop =3D ure_uno_rx_loop,
  	.uno_init =3D ure_uno_init,
  };
 @@ -1024,14 +1032,18 @@ ure_rxcsum(struct ifnet *ifp, struct ure
  }
 =20
  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 =3D 0;
 -	uint8_t *buf =3D c->unc_buf;
 +	uint8_t *buf =3D c->unc_buf + offset;
 =20
 -	if ((unsigned)m->m_pkthdr.len > un->un_tx_bufsz - sizeof(txhdr))
 -		return 0;
 +	__CTASSERT(sizeof(txhdr) <=3D URE_BUFSZ);
 +	KASSERT(sizeof(txhdr) <=3D un->un_tx_bufsz);
 +	KASSERT(offset <=3D un->un_tx_bufsz - sizeof(txhdr));
 +	KASSERT((unsigned)m->m_pkthdr.len <=3D
 +	    un->un_tx_bufsz - sizeof(txhdr) - offset);
 =20
  	/* header */
  	txhdr.ure_pktlen =3D htole32(m->m_pkthdr.len | URE_TXPKT_TX_FS |
 @@ -1050,6 +1062,51 @@ ure_uno_tx_prepare(struct usbnet *un, st
  	return frm_len;
  }
 =20
 +static unsigned
 +ure_uno_tx_prepare(struct usbnet *un, struct mbuf *m, struct usbnet_chain =
 *c)
 +{
 +
 +	__CTASSERT(sizeof(struct ure_txpkt) <=3D URE_BUFSZ);
 +	KASSERT(sizeof(struct ure_txpkt) <=3D 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 =3D 0;
 +
 +	for (m =3D m0; m !=3D NULL; m =3D m->m_nextpkt) {
 +		KASSERT(offset <=3D nbyte -
 +		    (sizeof(struct ure_txpkt) + (unsigned)m->m_pkthdr.len));
 +		offset +=3D ure_uno_tx_prepare_offset(un, m, c, offset);
 +		KASSERT(offset <=3D nbyte);
 +	}
 +	KASSERT(offset =3D=3D nbyte);
 +}
 +
  /*
   * We need to calculate L4 checksum in software, if the offset of
   * L4 header is larger than 0x7ff =3D 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);
 =20
  static const struct usbnet_ops ncm_ops =3D {
  	.uno_tx_prepare =3D ncm_uno_tx_prepare,
 +	.uno_tx_begin =3D ncm_uno_tx_begin,
 +	.uno_tx_add =3D ncm_uno_tx_add,
 +	.uno_tx_commit =3D ncm_uno_tx_commit,
  	.uno_rx_loop =3D ncm_uno_rx_loop,
  };
 =20
 @@ -328,7 +336,7 @@ ncm_uno_tx_prepare(struct usbnet *un, st
  	struct ncm_header16 *hdr;
  	unsigned hdr_len, len;
 =20
 -	hdr_len =3D sizeof(*hdr) + sizeof(*ptr);
 +	hdr_len =3D sizeof(*hdr) + offsetof(struct ncm_dptab16, datagram[2]);
  	len =3D m->m_pkthdr.len;
  	KASSERT(hdr_len <=3D 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));
 =20
  	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);
 =20
  	USETW(ptr->datagram[0].wDatagramIndex, hdr_len);
 @@ -362,6 +370,72 @@ ncm_uno_tx_prepare(struct usbnet *un, st
  	return len + hdr_len;
  }
 =20
 +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 =3D 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 =3D sizeof(*hdr) + sizeof(*ptr) + (npkt + 1)*sizeof(ptr->datagram);
 +
 +	hdr =3D (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 =3D (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 =3D 0, m =3D m0; m !=3D NULL; i++, m =3D m->m_nextpkt) {
 +		KASSERT(off < un->un_tx_bufsz);
 +		len =3D (unsigned)m->m_pkthdr.len;
 +		KASSERT(len <=3D 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 +=3D len;
 +	}
 +	USETW(ptr->datagram[i].wDatagramIndex, 0);
 +	USETW(ptr->datagram[i].wDatagramLength, 0);
 +
 +	KASSERT(off =3D=3D 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;
 =20
  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;
 =20
  #define NCM_GET_NTB_PARAMETERS 0x80
 
 --=_su1OON3HGWIM8fZuEZsawKeY4iHDSE1Z--
 


Home | Main Index | Thread Index | Old Index