1139749Simp/*-
2116491Sharti * Copyright (c) 2001-2003
3116491Sharti *	Fraunhofer Institute for Open Communication Systems (FhG Fokus).
4116491Sharti * 	All rights reserved.
5116491Sharti *
6116491Sharti * Redistribution and use in source and binary forms, with or without
7116491Sharti * modification, are permitted provided that the following conditions
8116491Sharti * are met:
9116491Sharti * 1. Redistributions of source code must retain the above copyright
10116491Sharti *    notice, this list of conditions and the following disclaimer.
11116491Sharti * 2. Redistributions in binary form must reproduce the above copyright
12116491Sharti *    notice, this list of conditions and the following disclaimer in the
13116491Sharti *    documentation and/or other materials provided with the distribution.
14116491Sharti *
15116491Sharti * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16116491Sharti * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17116491Sharti * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18116491Sharti * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19116491Sharti * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20116491Sharti * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21116491Sharti * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22116491Sharti * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23116491Sharti * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24116491Sharti * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25116491Sharti * SUCH DAMAGE.
26116491Sharti *
27116491Sharti * Author: Hartmut Brandt <harti@freebsd.org>
28116491Sharti *
29116491Sharti * ForeHE driver.
30116491Sharti *
31116491Sharti * Transmission.
32116491Sharti */
33116491Sharti
34116519Sharti#include <sys/cdefs.h>
35116519Sharti__FBSDID("$FreeBSD: releng/11.0/sys/dev/hatm/if_hatm_tx.c 298955 2016-05-03 03:41:25Z pfg $");
36116519Sharti
37116491Sharti#include "opt_inet.h"
38116491Sharti#include "opt_natm.h"
39116491Sharti
40116491Sharti#include <sys/types.h>
41116491Sharti#include <sys/param.h>
42116491Sharti#include <sys/systm.h>
43116491Sharti#include <sys/kernel.h>
44116491Sharti#include <sys/malloc.h>
45116491Sharti#include <sys/bus.h>
46116491Sharti#include <sys/errno.h>
47116491Sharti#include <sys/conf.h>
48116491Sharti#include <sys/module.h>
49116491Sharti#include <sys/queue.h>
50116491Sharti#include <sys/syslog.h>
51116491Sharti#include <sys/condvar.h>
52116491Sharti#include <sys/sysctl.h>
53116491Sharti#include <vm/uma.h>
54116491Sharti
55116491Sharti#include <sys/sockio.h>
56116491Sharti#include <sys/mbuf.h>
57116491Sharti#include <sys/socket.h>
58116491Sharti
59116491Sharti#include <net/if.h>
60257176Sglebius#include <net/if_var.h>
61116491Sharti#include <net/if_media.h>
62116491Sharti#include <net/if_atm.h>
63116491Sharti#include <net/route.h>
64116491Sharti#ifdef ENABLE_BPF
65116491Sharti#include <net/bpf.h>
66116491Sharti#endif
67116491Sharti#include <netinet/in.h>
68116491Sharti#include <netinet/if_atm.h>
69116491Sharti
70116491Sharti#include <machine/bus.h>
71116491Sharti#include <machine/resource.h>
72116491Sharti#include <sys/bus.h>
73116491Sharti#include <sys/rman.h>
74119280Simp#include <dev/pci/pcireg.h>
75119280Simp#include <dev/pci/pcivar.h>
76116491Sharti
77116491Sharti#include <dev/utopia/utopia.h>
78116491Sharti#include <dev/hatm/if_hatmconf.h>
79116491Sharti#include <dev/hatm/if_hatmreg.h>
80116491Sharti#include <dev/hatm/if_hatmvar.h>
81116491Sharti
82122112Sharti
83116491Sharti/*
84122112Sharti * These macros are used to trace the flow of transmit mbufs and to
85122112Sharti * detect transmit mbuf leaks in the driver.
86122112Sharti */
87122112Sharti#ifdef HATM_DEBUG
88122112Sharti#define	hatm_free_txmbuf(SC)						\
89122112Sharti	do {								\
90122112Sharti		if (--sc->txmbuf < 0)					\
91122112Sharti			DBG(sc, TX, ("txmbuf below 0!"));		\
92122112Sharti		else if (sc->txmbuf == 0)				\
93122112Sharti			DBG(sc, TX, ("txmbuf now 0"));			\
94122112Sharti	} while (0)
95122112Sharti#define	hatm_get_txmbuf(SC)						\
96122112Sharti	do {								\
97122112Sharti		if (++sc->txmbuf > 20000)				\
98146617Sharti			DBG(sc,	TX, ("txmbuf %u", sc->txmbuf));		\
99122112Sharti		else if (sc->txmbuf == 1)				\
100122112Sharti			DBG(sc, TX, ("txmbuf leaves 0"));		\
101122112Sharti	} while (0)
102122112Sharti#else
103122112Sharti#define	hatm_free_txmbuf(SC)	do { } while (0)
104122112Sharti#define	hatm_get_txmbuf(SC)	do { } while (0)
105122112Sharti#endif
106122112Sharti
107122112Sharti/*
108116491Sharti * Allocate a new TPD, zero the TPD part. Cannot return NULL if
109116491Sharti * flag is 0. The TPD is removed from the free list and its used
110116491Sharti * bit is set.
111116491Sharti */
112116491Shartistatic struct tpd *
113116491Shartihatm_alloc_tpd(struct hatm_softc *sc, u_int flags)
114116491Sharti{
115116491Sharti	struct tpd *t;
116116491Sharti
117116491Sharti	/* if we allocate a transmit TPD check for the reserve */
118116491Sharti	if (flags & M_NOWAIT) {
119116491Sharti		if (sc->tpd_nfree <= HE_CONFIG_TPD_RESERVE)
120116491Sharti			return (NULL);
121116491Sharti	} else {
122116491Sharti		if (sc->tpd_nfree == 0)
123116491Sharti			return (NULL);
124116491Sharti	}
125116491Sharti
126298955Spfg	/* make it being used */
127116491Sharti	t = SLIST_FIRST(&sc->tpd_free);
128116491Sharti	KASSERT(t != NULL, ("tpd botch"));
129116491Sharti	SLIST_REMOVE_HEAD(&sc->tpd_free, link);
130116491Sharti	TPD_SET_USED(sc, t->no);
131116491Sharti	sc->tpd_nfree--;
132116491Sharti
133116491Sharti	/* initialize */
134116491Sharti	t->mbuf = NULL;
135116491Sharti	t->cid = 0;
136116491Sharti	bzero(&t->tpd, sizeof(t->tpd));
137116491Sharti	t->tpd.addr = t->no << HE_REGS_TPD_ADDR;
138116491Sharti
139116491Sharti	return (t);
140116491Sharti}
141116491Sharti
142116491Sharti/*
143116491Sharti * Free a TPD. If the mbuf pointer in that TPD is not zero, it is assumed, that
144116491Sharti * the DMA map of this TPD was used to load this mbuf. The map is unloaded
145116491Sharti * and the mbuf is freed. The TPD is put back onto the free list and
146116491Sharti * its used bit is cleared.
147116491Sharti */
148116491Shartistatic void
149116491Shartihatm_free_tpd(struct hatm_softc *sc, struct tpd *tpd)
150116491Sharti{
151116491Sharti	if (tpd->mbuf != NULL) {
152116491Sharti		bus_dmamap_unload(sc->tx_tag, tpd->map);
153122112Sharti		hatm_free_txmbuf(sc);
154116491Sharti		m_freem(tpd->mbuf);
155116491Sharti		tpd->mbuf = NULL;
156116491Sharti	}
157116491Sharti
158116491Sharti	/* insert TPD into free list */
159116491Sharti	SLIST_INSERT_HEAD(&sc->tpd_free, tpd, link);
160116491Sharti	TPD_CLR_USED(sc, tpd->no);
161116491Sharti	sc->tpd_nfree++;
162116491Sharti}
163116491Sharti
164116491Sharti/*
165116491Sharti * Queue a number of TPD. If there is not enough space none of the TPDs
166116491Sharti * is queued and an error code is returned.
167116491Sharti */
168116491Shartistatic int
169116491Shartihatm_queue_tpds(struct hatm_softc *sc, u_int count, struct tpd **list,
170116491Sharti    u_int cid)
171116491Sharti{
172116491Sharti	u_int space;
173116491Sharti	u_int i;
174116491Sharti
175116491Sharti	if (count >= sc->tpdrq.size) {
176116491Sharti		sc->istats.tdprq_full++;
177116491Sharti		return (EBUSY);
178116491Sharti	}
179116491Sharti
180116491Sharti	if (sc->tpdrq.tail < sc->tpdrq.head)
181116491Sharti		space = sc->tpdrq.head - sc->tpdrq.tail;
182116491Sharti	else
183116491Sharti		space = sc->tpdrq.head - sc->tpdrq.tail +  sc->tpdrq.size;
184116491Sharti
185116491Sharti	if (space <= count) {
186116491Sharti		sc->tpdrq.head =
187116491Sharti		    (READ4(sc, HE_REGO_TPDRQ_H) >> HE_REGS_TPDRQ_H_H) &
188116491Sharti		    (sc->tpdrq.size - 1);
189116491Sharti
190116491Sharti		if (sc->tpdrq.tail < sc->tpdrq.head)
191116491Sharti			space = sc->tpdrq.head - sc->tpdrq.tail;
192116491Sharti		else
193116491Sharti			space = sc->tpdrq.head - sc->tpdrq.tail +
194116491Sharti			    sc->tpdrq.size;
195116491Sharti
196116491Sharti		if (space <= count) {
197147256Sbrooks			if_printf(sc->ifp, "TPDRQ full\n");
198116491Sharti			sc->istats.tdprq_full++;
199116491Sharti			return (EBUSY);
200116491Sharti		}
201116491Sharti	}
202116491Sharti
203116491Sharti	/* we are going to write to the TPD queue space */
204116491Sharti	bus_dmamap_sync(sc->tpdrq.mem.tag, sc->tpdrq.mem.map,
205116491Sharti	    BUS_DMASYNC_PREWRITE);
206116491Sharti
207116491Sharti	/* put the entries into the TPD space */
208116491Sharti	for (i = 0; i < count; i++) {
209116491Sharti		/* we are going to 'write' the TPD to the device */
210116491Sharti		bus_dmamap_sync(sc->tpds.tag, sc->tpds.map,
211116491Sharti		    BUS_DMASYNC_PREWRITE);
212116491Sharti
213116491Sharti		sc->tpdrq.tpdrq[sc->tpdrq.tail].tpd =
214116491Sharti		    sc->tpds.paddr + HE_TPD_SIZE * list[i]->no;
215116491Sharti		sc->tpdrq.tpdrq[sc->tpdrq.tail].cid = cid;
216116491Sharti
217116491Sharti		if (++sc->tpdrq.tail == sc->tpdrq.size)
218116491Sharti			sc->tpdrq.tail = 0;
219116491Sharti	}
220116491Sharti
221116491Sharti	/* update tail pointer */
222116491Sharti	WRITE4(sc, HE_REGO_TPDRQ_T, (sc->tpdrq.tail << HE_REGS_TPDRQ_T_T));
223116491Sharti
224116491Sharti	return (0);
225116491Sharti}
226116491Sharti
227116491Sharti/*
228116491Sharti * Helper struct for communication with the DMA load helper.
229116491Sharti */
230116491Shartistruct load_txbuf_arg {
231116491Sharti	struct hatm_softc *sc;
232116491Sharti	struct tpd *first;
233116491Sharti	struct mbuf *mbuf;
234116491Sharti	struct hevcc *vcc;
235116491Sharti	int error;
236116491Sharti	u_int pti;
237116491Sharti	u_int vpi, vci;
238116491Sharti};
239116491Sharti
240116491Sharti/*
241116491Sharti * Loader callback for the mbuf. This function allocates the TPDs and
242116491Sharti * fills them. It puts the dmamap and and the mbuf pointer into the last
243116491Sharti * TPD and then tries to queue all the TPDs. If anything fails, all TPDs
244116491Sharti * allocated by this function are freed and the error flag is set in the
245116491Sharti * argument structure. The first TPD must then be freed by the caller.
246116491Sharti */
247116491Shartistatic void
248116491Shartihatm_load_txbuf(void *uarg, bus_dma_segment_t *segs, int nseg,
249116491Sharti    bus_size_t mapsize, int error)
250116491Sharti{
251116491Sharti	struct load_txbuf_arg *arg = uarg;
252116491Sharti	u_int tpds_needed, i, n, tpd_cnt;
253116491Sharti	int need_intr;
254116491Sharti	struct tpd *tpd;
255116491Sharti	struct tpd *tpd_list[HE_CONFIG_MAX_TPD_PER_PACKET];
256116491Sharti
257116491Sharti	if (error != 0) {
258116491Sharti		DBG(arg->sc, DMA, ("%s -- error=%d plen=%d\n",
259116491Sharti		    __func__, error, arg->mbuf->m_pkthdr.len));
260116491Sharti		return;
261116491Sharti	}
262116491Sharti
263116491Sharti	/* ensure, we have enough TPDs (remember, we already have one) */
264116491Sharti	tpds_needed = (nseg + 2) / 3;
265116491Sharti	if (HE_CONFIG_TPD_RESERVE + tpds_needed - 1 > arg->sc->tpd_nfree) {
266147256Sbrooks		if_printf(arg->sc->ifp, "%s -- out of TPDs (need %d, "
267116491Sharti		    "have %u)\n", __func__, tpds_needed - 1,
268116491Sharti		    arg->sc->tpd_nfree + 1);
269116491Sharti		arg->error = 1;
270116491Sharti		return;
271116491Sharti	}
272116491Sharti
273116491Sharti	/*
274116491Sharti	 * Check for the maximum number of TPDs on the connection.
275116491Sharti	 */
276116491Sharti	need_intr = 0;
277116491Sharti	if (arg->sc->max_tpd > 0) {
278116491Sharti		if (arg->vcc->ntpds + tpds_needed > arg->sc->max_tpd) {
279116491Sharti			arg->sc->istats.flow_closed++;
280116491Sharti			arg->vcc->vflags |= HE_VCC_FLOW_CTRL;
281147256Sbrooks			ATMEV_SEND_FLOW_CONTROL(IFP2IFATM(arg->sc->ifp),
282118170Sharti			    arg->vpi, arg->vci, 1);
283116491Sharti			arg->error = 1;
284116491Sharti			return;
285116491Sharti		}
286116491Sharti		if (arg->vcc->ntpds + tpds_needed >
287116491Sharti		    (9 * arg->sc->max_tpd) / 10)
288116491Sharti			need_intr = 1;
289116491Sharti	}
290116491Sharti
291116491Sharti	tpd = arg->first;
292116491Sharti	tpd_cnt = 0;
293116491Sharti	tpd_list[tpd_cnt++] = tpd;
294116491Sharti	for (i = n = 0; i < nseg; i++, n++) {
295116491Sharti		if (n == 3) {
296116491Sharti			if ((tpd = hatm_alloc_tpd(arg->sc, M_NOWAIT)) == NULL)
297116491Sharti				/* may not fail (see check above) */
298116491Sharti				panic("%s: out of TPDs", __func__);
299116491Sharti			tpd->cid = arg->first->cid;
300116491Sharti			tpd->tpd.addr |= arg->pti;
301116491Sharti			tpd_list[tpd_cnt++] = tpd;
302116491Sharti			n = 0;
303116491Sharti		}
304116491Sharti		KASSERT(segs[i].ds_addr <= 0xffffffffLU,
305116491Sharti		    ("phys addr too large %lx", (u_long)segs[i].ds_addr));
306116491Sharti
307116491Sharti		DBG(arg->sc, DMA, ("DMA loaded: %lx/%lu",
308116491Sharti		    (u_long)segs[i].ds_addr, (u_long)segs[i].ds_len));
309116491Sharti
310116491Sharti		tpd->tpd.bufs[n].addr = segs[i].ds_addr;
311116491Sharti		tpd->tpd.bufs[n].len = segs[i].ds_len;
312116491Sharti
313116491Sharti		DBG(arg->sc, TX, ("seg[%u]=tpd[%u,%u]=%x/%u", i,
314116491Sharti		    tpd_cnt, n, tpd->tpd.bufs[n].addr, tpd->tpd.bufs[n].len));
315116491Sharti
316116491Sharti		if (i == nseg - 1)
317116491Sharti			tpd->tpd.bufs[n].len |= HE_REGM_TPD_LST;
318116491Sharti	}
319116491Sharti
320116491Sharti	/*
321116491Sharti	 * Swap the MAP in the first and the last TPD and set the mbuf
322116491Sharti	 * pointer into the last TPD. We use the map in the last TPD, because
323116491Sharti	 * the map must stay valid until the last TPD is processed by the card.
324116491Sharti	 */
325116491Sharti	if (tpd_cnt > 1) {
326116491Sharti		bus_dmamap_t tmp;
327116491Sharti
328116491Sharti		tmp = arg->first->map;
329116491Sharti		arg->first->map = tpd_list[tpd_cnt - 1]->map;
330116491Sharti		tpd_list[tpd_cnt - 1]->map = tmp;
331116491Sharti	}
332116491Sharti	tpd_list[tpd_cnt - 1]->mbuf = arg->mbuf;
333116491Sharti
334116491Sharti	if (need_intr)
335116491Sharti		tpd_list[tpd_cnt - 1]->tpd.addr |= HE_REGM_TPD_INTR;
336116491Sharti
337116491Sharti	/* queue the TPDs */
338116491Sharti	if (hatm_queue_tpds(arg->sc, tpd_cnt, tpd_list, arg->first->cid)) {
339116491Sharti		/* free all, except the first TPD */
340116491Sharti		for (i = 1; i < tpd_cnt; i++)
341116491Sharti			hatm_free_tpd(arg->sc, tpd_list[i]);
342116491Sharti		arg->error = 1;
343116491Sharti		return;
344116491Sharti	}
345116491Sharti	arg->vcc->ntpds += tpd_cnt;
346116491Sharti}
347116491Sharti
348116491Sharti
349116491Sharti/*
350116491Sharti * Start output on the interface
351116491Sharti */
352116491Shartivoid
353116491Shartihatm_start(struct ifnet *ifp)
354116491Sharti{
355147721Sharti	struct hatm_softc *sc = ifp->if_softc;
356116491Sharti	struct mbuf *m;
357116491Sharti	struct atm_pseudohdr *aph;
358116491Sharti	u_int cid;
359116491Sharti	struct tpd *tpd;
360116491Sharti	struct load_txbuf_arg arg;
361116491Sharti	u_int len;
362116491Sharti	int error;
363116491Sharti
364148887Srwatson	if (!(ifp->if_drv_flags & IFF_DRV_RUNNING))
365116491Sharti		return;
366116491Sharti	mtx_lock(&sc->mtx);
367116491Sharti	arg.sc = sc;
368116491Sharti
369116491Sharti	while (1) {
370116491Sharti		IF_DEQUEUE(&ifp->if_snd, m);
371116491Sharti		if (m == NULL)
372116491Sharti			break;
373116491Sharti
374122112Sharti		hatm_get_txmbuf(sc);
375122112Sharti
376116491Sharti		if (m->m_len < sizeof(*aph))
377122112Sharti			if ((m = m_pullup(m, sizeof(*aph))) == NULL) {
378122112Sharti				hatm_free_txmbuf(sc);
379116491Sharti				continue;
380122112Sharti			}
381116491Sharti
382116491Sharti		aph = mtod(m, struct atm_pseudohdr *);
383116491Sharti		arg.vci = ATM_PH_VCI(aph);
384116491Sharti		arg.vpi = ATM_PH_VPI(aph);
385116491Sharti		m_adj(m, sizeof(*aph));
386116491Sharti
387116491Sharti		if ((len = m->m_pkthdr.len) == 0) {
388122112Sharti			hatm_free_txmbuf(sc);
389116491Sharti			m_freem(m);
390116491Sharti			continue;
391116491Sharti		}
392116491Sharti
393116491Sharti		if ((arg.vpi & ~HE_VPI_MASK) || (arg.vci & ~HE_VCI_MASK) ||
394116491Sharti		    (arg.vci == 0)) {
395122112Sharti			hatm_free_txmbuf(sc);
396116491Sharti			m_freem(m);
397116491Sharti			continue;
398116491Sharti		}
399116491Sharti		cid = HE_CID(arg.vpi, arg.vci);
400116491Sharti		arg.vcc = sc->vccs[cid];
401116491Sharti
402116491Sharti		if (arg.vcc == NULL || !(arg.vcc->vflags & HE_VCC_OPEN)) {
403122112Sharti			hatm_free_txmbuf(sc);
404116491Sharti			m_freem(m);
405116491Sharti			continue;
406116491Sharti		}
407116491Sharti		if (arg.vcc->vflags & HE_VCC_FLOW_CTRL) {
408122112Sharti			hatm_free_txmbuf(sc);
409116491Sharti			m_freem(m);
410116491Sharti			sc->istats.flow_drop++;
411116491Sharti			continue;
412116491Sharti		}
413116491Sharti
414116491Sharti		arg.pti = 0;
415116491Sharti		if (arg.vcc->param.aal == ATMIO_AAL_RAW) {
416116491Sharti			if (len < 52) {
417121687Sharti				/* too short */
418122112Sharti				hatm_free_txmbuf(sc);
419116491Sharti				m_freem(m);
420116491Sharti				continue;
421116491Sharti			}
422121687Sharti
423121687Sharti			/*
424121687Sharti			 * Get the header and ignore except
425121687Sharti			 * payload type and CLP.
426121687Sharti			 */
427122112Sharti			if (m->m_len < 4 && (m = m_pullup(m, 4)) == NULL) {
428122112Sharti				hatm_free_txmbuf(sc);
429116491Sharti				continue;
430122112Sharti			}
431116491Sharti			arg.pti = mtod(m, u_char *)[3] & 0xf;
432116491Sharti			arg.pti = ((arg.pti & 0xe) << 2) | ((arg.pti & 1) << 1);
433116491Sharti			m_adj(m, 4);
434116491Sharti			len -= 4;
435121687Sharti
436121687Sharti			if (len % 48 != 0) {
437121687Sharti				m_adj(m, -((int)(len % 48)));
438121687Sharti				len -= len % 48;
439121687Sharti			}
440116491Sharti		}
441116491Sharti
442116491Sharti#ifdef ENABLE_BPF
443116491Sharti		if (!(arg.vcc->param.flags & ATMIO_FLAG_NG) &&
444118540Sharti		    (arg.vcc->param.aal == ATMIO_AAL_5) &&
445116491Sharti		    (arg.vcc->param.flags & ATM_PH_LLCSNAP))
446116491Sharti		 	BPF_MTAP(ifp, m);
447116491Sharti#endif
448116491Sharti
449116491Sharti		/* Now load a DMA map with the packet. Allocate the first
450116491Sharti		 * TPD to get a map. Additional TPDs may be allocated by the
451116491Sharti		 * callback. */
452116491Sharti		if ((tpd = hatm_alloc_tpd(sc, M_NOWAIT)) == NULL) {
453122112Sharti			hatm_free_txmbuf(sc);
454116491Sharti			m_freem(m);
455271849Sglebius			if_inc_counter(sc->ifp, IFCOUNTER_OERRORS, 1);
456116491Sharti			continue;
457116491Sharti		}
458116491Sharti		tpd->cid = cid;
459116491Sharti		tpd->tpd.addr |= arg.pti;
460116491Sharti		arg.first = tpd;
461116491Sharti		arg.error = 0;
462116491Sharti		arg.mbuf = m;
463116491Sharti
464116491Sharti		error = bus_dmamap_load_mbuf(sc->tx_tag, tpd->map, m,
465117382Sharti		    hatm_load_txbuf, &arg, BUS_DMA_NOWAIT);
466116491Sharti
467116491Sharti		if (error == EFBIG) {
468116491Sharti			/* try to defragment the packet */
469116491Sharti			sc->istats.defrag++;
470243857Sglebius			m = m_defrag(m, M_NOWAIT);
471116491Sharti			if (m == NULL) {
472122112Sharti				tpd->mbuf = NULL;
473122112Sharti				hatm_free_txmbuf(sc);
474122112Sharti				hatm_free_tpd(sc, tpd);
475271849Sglebius				if_inc_counter(sc->ifp, IFCOUNTER_OERRORS, 1);
476116491Sharti				continue;
477116491Sharti			}
478116491Sharti			arg.mbuf = m;
479116491Sharti			error = bus_dmamap_load_mbuf(sc->tx_tag, tpd->map, m,
480117382Sharti			    hatm_load_txbuf, &arg, BUS_DMA_NOWAIT);
481116491Sharti		}
482116491Sharti
483116491Sharti		if (error != 0) {
484147256Sbrooks			if_printf(sc->ifp, "mbuf loaded error=%d\n",
485116491Sharti			    error);
486116491Sharti			hatm_free_tpd(sc, tpd);
487271849Sglebius			if_inc_counter(sc->ifp, IFCOUNTER_OERRORS, 1);
488116491Sharti			continue;
489116491Sharti		}
490116491Sharti		if (arg.error) {
491116491Sharti			hatm_free_tpd(sc, tpd);
492271849Sglebius			if_inc_counter(sc->ifp, IFCOUNTER_OERRORS, 1);
493116491Sharti			continue;
494116491Sharti		}
495116491Sharti		arg.vcc->opackets++;
496116491Sharti		arg.vcc->obytes += len;
497271849Sglebius		if_inc_counter(sc->ifp, IFCOUNTER_OPACKETS, 1);
498116491Sharti	}
499116491Sharti	mtx_unlock(&sc->mtx);
500116491Sharti}
501116491Sharti
502116491Shartivoid
503116491Shartihatm_tx_complete(struct hatm_softc *sc, struct tpd *tpd, uint32_t flags)
504116491Sharti{
505116491Sharti	struct hevcc *vcc = sc->vccs[tpd->cid];
506116491Sharti
507116491Sharti	DBG(sc, TX, ("tx_complete cid=%#x flags=%#x", tpd->cid, flags));
508116491Sharti
509116491Sharti	if (vcc == NULL)
510116491Sharti		return;
511116491Sharti	if ((flags & HE_REGM_TBRQ_EOS) && (vcc->vflags & HE_VCC_TX_CLOSING)) {
512116491Sharti		vcc->vflags &= ~HE_VCC_TX_CLOSING;
513118540Sharti		if (vcc->param.flags & ATMIO_FLAG_ASYNC) {
514116491Sharti			hatm_tx_vcc_closed(sc, tpd->cid);
515116491Sharti			if (!(vcc->vflags & HE_VCC_OPEN)) {
516116491Sharti				hatm_vcc_closed(sc, tpd->cid);
517116491Sharti				vcc = NULL;
518116491Sharti			}
519116491Sharti		} else
520116491Sharti			cv_signal(&sc->vcc_cv);
521116491Sharti	}
522116491Sharti	hatm_free_tpd(sc, tpd);
523116491Sharti
524116491Sharti	if (vcc == NULL)
525116491Sharti		return;
526116491Sharti
527116491Sharti	vcc->ntpds--;
528116491Sharti
529116491Sharti	if ((vcc->vflags & HE_VCC_FLOW_CTRL) &&
530116491Sharti	    vcc->ntpds <= HE_CONFIG_TPD_FLOW_ENB) {
531116491Sharti		vcc->vflags &= ~HE_VCC_FLOW_CTRL;
532147256Sbrooks		ATMEV_SEND_FLOW_CONTROL(IFP2IFATM(sc->ifp),
533118170Sharti		    HE_VPI(tpd->cid), HE_VCI(tpd->cid), 0);
534116491Sharti	}
535116491Sharti}
536116491Sharti
537116491Sharti/*
538116491Sharti * Convert CPS to Rate for a rate group
539116491Sharti */
540116491Shartistatic u_int
541116491Sharticps_to_rate(struct hatm_softc *sc, uint32_t cps)
542116491Sharti{
543116491Sharti	u_int clk = sc->he622 ? HE_622_CLOCK : HE_155_CLOCK;
544116491Sharti	u_int period, rate;
545116491Sharti
546116491Sharti	/* how many double ticks between two cells */
547116491Sharti	period = (clk + 2 * cps - 1) / (2 * cps);
548116491Sharti	rate = hatm_cps2atmf(period);
549116491Sharti	if (hatm_atmf2cps(rate) < period)
550116491Sharti		rate++;
551116491Sharti
552116491Sharti	return (rate);
553116491Sharti}
554116491Sharti
555116491Sharti/*
556116491Sharti * Check whether the VCC is really closed on the hardware and available for
557116491Sharti * open. Check that we have enough resources. If this function returns ok,
558116491Sharti * a later actual open must succeed. Assume, that we are locked between this
559116491Sharti * function and the next one, so that nothing does change. For CBR this
560116491Sharti * assigns the rate group and set the rate group's parameter.
561116491Sharti */
562116491Shartiint
563116491Shartihatm_tx_vcc_can_open(struct hatm_softc *sc, u_int cid, struct hevcc *vcc)
564116491Sharti{
565116491Sharti	uint32_t v, line_rate;
566116491Sharti	u_int rc, idx, free_idx;
567116491Sharti	struct atmio_tparam *t = &vcc->param.tparam;
568116491Sharti
569116491Sharti	/* verify that connection is closed */
570116491Sharti#if 0
571116491Sharti	v = READ_TSR(sc, cid, 4);
572116491Sharti	if(!(v & HE_REGM_TSR4_SESS_END)) {
573147256Sbrooks		if_printf(sc->ifp, "cid=%#x not closed (TSR4)\n", cid);
574116491Sharti		return (EBUSY);
575116491Sharti	}
576116491Sharti#endif
577116491Sharti	v = READ_TSR(sc, cid, 0);
578116491Sharti	if((v & HE_REGM_TSR0_CONN_STATE) != 0) {
579147256Sbrooks		if_printf(sc->ifp, "cid=%#x not closed (TSR0=%#x)\n",
580116491Sharti		    cid, v);
581116491Sharti		return (EBUSY);
582116491Sharti	}
583116491Sharti
584116491Sharti	/* check traffic parameters */
585116491Sharti	line_rate = sc->he622 ? ATM_RATE_622M : ATM_RATE_155M;
586116491Sharti	switch (vcc->param.traffic) {
587116491Sharti
588116491Sharti	  case ATMIO_TRAFFIC_UBR:
589116491Sharti		if (t->pcr == 0 || t->pcr > line_rate)
590116491Sharti			t->pcr = line_rate;
591116491Sharti		if (t->mcr != 0 || t->icr != 0 || t->tbe != 0 || t->nrm != 0 ||
592116491Sharti		    t->trm != 0 || t->adtf != 0 || t->rif != 0 || t->rdf != 0 ||
593116491Sharti		    t->cdf != 0)
594116491Sharti			return (EINVAL);
595116491Sharti		break;
596116491Sharti
597116491Sharti	  case ATMIO_TRAFFIC_CBR:
598116491Sharti		/*
599116491Sharti		 * Compute rate group index
600116491Sharti		 */
601116491Sharti		if (t->pcr < 10)
602116491Sharti			t->pcr = 10;
603116491Sharti		if (sc->cbr_bw + t->pcr > line_rate)
604116491Sharti			return (EINVAL);
605116491Sharti		if (t->mcr != 0 || t->icr != 0 || t->tbe != 0 || t->nrm != 0 ||
606116491Sharti		    t->trm != 0 || t->adtf != 0 || t->rif != 0 || t->rdf != 0 ||
607116491Sharti		    t->cdf != 0)
608116491Sharti			return (EINVAL);
609116491Sharti
610116491Sharti		rc = cps_to_rate(sc, t->pcr);
611116491Sharti		free_idx = HE_REGN_CS_STPER;
612116491Sharti		for (idx = 0; idx < HE_REGN_CS_STPER; idx++) {
613116491Sharti			if (sc->rate_ctrl[idx].refcnt == 0) {
614116491Sharti				if (free_idx == HE_REGN_CS_STPER)
615116491Sharti					free_idx = idx;
616116491Sharti			} else {
617116491Sharti				if (sc->rate_ctrl[idx].rate == rc)
618116491Sharti					break;
619116491Sharti			}
620116491Sharti		}
621116491Sharti		if (idx == HE_REGN_CS_STPER) {
622116491Sharti			if ((idx = free_idx) == HE_REGN_CS_STPER)
623116491Sharti				return (EBUSY);
624116491Sharti			sc->rate_ctrl[idx].rate = rc;
625116491Sharti		}
626116491Sharti		vcc->rc = idx;
627118598Sharti
628118598Sharti		/* commit */
629118598Sharti		sc->rate_ctrl[idx].refcnt++;
630118598Sharti		sc->cbr_bw += t->pcr;
631116491Sharti		break;
632116491Sharti
633116491Sharti	  case ATMIO_TRAFFIC_ABR:
634116491Sharti		if (t->pcr > line_rate)
635116491Sharti			t->pcr = line_rate;
636116491Sharti		if (t->mcr > line_rate)
637116491Sharti			t->mcr = line_rate;
638116491Sharti		if (t->icr > line_rate)
639116491Sharti			t->icr = line_rate;
640116491Sharti		if (t->tbe == 0 || t->tbe >= 1 << 24 || t->nrm > 7 ||
641116491Sharti		    t->trm > 7 || t->adtf >= 1 << 10 || t->rif > 15 ||
642116491Sharti		    t->rdf > 15 || t->cdf > 7)
643116491Sharti			return (EINVAL);
644116491Sharti		break;
645116491Sharti
646116491Sharti	  default:
647116491Sharti		return (EINVAL);
648116491Sharti	}
649116491Sharti	return (0);
650116491Sharti}
651116491Sharti
652116491Sharti#define NRM_CODE2VAL(CODE) (2 * (1 << (CODE)))
653116491Sharti
654116491Sharti/*
655116491Sharti * Actually open the transmit VCC
656116491Sharti */
657116491Shartivoid
658116491Shartihatm_tx_vcc_open(struct hatm_softc *sc, u_int cid)
659116491Sharti{
660116491Sharti	struct hevcc *vcc = sc->vccs[cid];
661116491Sharti	uint32_t tsr0, tsr4, atmf, crm;
662116491Sharti	const struct atmio_tparam *t = &vcc->param.tparam;
663116491Sharti
664116491Sharti	if (vcc->param.aal == ATMIO_AAL_5) {
665116491Sharti		tsr0 = HE_REGM_TSR0_AAL_5 << HE_REGS_TSR0_AAL;
666116491Sharti		tsr4 = HE_REGM_TSR4_AAL_5 << HE_REGS_TSR4_AAL;
667116491Sharti	} else {
668116491Sharti		tsr0 = HE_REGM_TSR0_AAL_0 << HE_REGS_TSR0_AAL;
669116491Sharti		tsr4 = HE_REGM_TSR4_AAL_0 << HE_REGS_TSR4_AAL;
670116491Sharti	}
671116491Sharti	tsr4 |= 1;
672116491Sharti
673116491Sharti	switch (vcc->param.traffic) {
674116491Sharti
675116491Sharti	  case ATMIO_TRAFFIC_UBR:
676116491Sharti		atmf = hatm_cps2atmf(t->pcr);
677116491Sharti
678116491Sharti		tsr0 |= HE_REGM_TSR0_TRAFFIC_UBR << HE_REGS_TSR0_TRAFFIC;
679116491Sharti		tsr0 |= HE_REGM_TSR0_USE_WMIN | HE_REGM_TSR0_UPDATE_GER;
680116491Sharti
681116491Sharti		WRITE_TSR(sc, cid, 0, 0xf, tsr0);
682116491Sharti		WRITE_TSR(sc, cid, 4, 0xf, tsr4);
683116491Sharti		WRITE_TSR(sc, cid, 1, 0xf, (atmf << HE_REGS_TSR1_PCR));
684116491Sharti		WRITE_TSR(sc, cid, 2, 0xf, (atmf << HE_REGS_TSR2_ACR));
685116491Sharti		WRITE_TSR(sc, cid, 9, 0xf, HE_REGM_TSR9_INIT);
686116491Sharti		WRITE_TSR(sc, cid, 3, 0xf, 0);
687116491Sharti		WRITE_TSR(sc, cid, 5, 0xf, 0);
688116491Sharti		WRITE_TSR(sc, cid, 6, 0xf, 0);
689116491Sharti		WRITE_TSR(sc, cid, 7, 0xf, 0);
690116491Sharti		WRITE_TSR(sc, cid, 8, 0xf, 0);
691116491Sharti		WRITE_TSR(sc, cid, 10, 0xf, 0);
692116491Sharti		WRITE_TSR(sc, cid, 11, 0xf, 0);
693116491Sharti		WRITE_TSR(sc, cid, 12, 0xf, 0);
694116491Sharti		WRITE_TSR(sc, cid, 13, 0xf, 0);
695116491Sharti		WRITE_TSR(sc, cid, 14, 0xf, 0);
696116491Sharti		break;
697116491Sharti
698116491Sharti	  case ATMIO_TRAFFIC_CBR:
699116491Sharti		atmf = hatm_cps2atmf(t->pcr);
700116491Sharti
701118598Sharti		if (sc->rate_ctrl[vcc->rc].refcnt == 1)
702118598Sharti			WRITE_MBOX4(sc, HE_REGO_CS_STPER(vcc->rc),
703118598Sharti			    sc->rate_ctrl[vcc->rc].rate);
704118598Sharti
705116491Sharti		tsr0 |= HE_REGM_TSR0_TRAFFIC_CBR << HE_REGS_TSR0_TRAFFIC;
706116491Sharti		tsr0 |= vcc->rc;
707116491Sharti
708116491Sharti		WRITE_TSR(sc, cid, 1, 0xf, (atmf << HE_REGS_TSR1_PCR));
709116491Sharti		WRITE_TSR(sc, cid, 2, 0xf, (atmf << HE_REGS_TSR2_ACR));
710116491Sharti		WRITE_TSR(sc, cid, 3, 0xf, 0);
711116491Sharti		WRITE_TSR(sc, cid, 5, 0xf, 0);
712116491Sharti		WRITE_TSR(sc, cid, 6, 0xf, 0);
713116491Sharti		WRITE_TSR(sc, cid, 7, 0xf, 0);
714116491Sharti		WRITE_TSR(sc, cid, 8, 0xf, 0);
715116491Sharti		WRITE_TSR(sc, cid, 10, 0xf, 0);
716116491Sharti		WRITE_TSR(sc, cid, 11, 0xf, 0);
717116491Sharti		WRITE_TSR(sc, cid, 12, 0xf, 0);
718116491Sharti		WRITE_TSR(sc, cid, 13, 0xf, 0);
719116491Sharti		WRITE_TSR(sc, cid, 14, 0xf, 0);
720116491Sharti		WRITE_TSR(sc, cid, 4, 0xf, tsr4);
721116491Sharti		WRITE_TSR(sc, cid, 9, 0xf, HE_REGM_TSR9_INIT);
722116491Sharti		WRITE_TSR(sc, cid, 0, 0xf, tsr0);
723116491Sharti
724116491Sharti		break;
725116491Sharti
726116491Sharti	  case ATMIO_TRAFFIC_ABR:
727116491Sharti		if ((crm = t->tbe / NRM_CODE2VAL(t->nrm)) > 0xffff)
728116491Sharti			crm = 0xffff;
729116491Sharti
730116491Sharti		tsr0 |= HE_REGM_TSR0_TRAFFIC_ABR << HE_REGS_TSR0_TRAFFIC;
731116491Sharti		tsr0 |= HE_REGM_TSR0_USE_WMIN | HE_REGM_TSR0_UPDATE_GER;
732116491Sharti
733116491Sharti		WRITE_TSR(sc, cid, 0, 0xf, tsr0);
734116491Sharti		WRITE_TSR(sc, cid, 4, 0xf, tsr4);
735116491Sharti
736116491Sharti		WRITE_TSR(sc, cid, 1, 0xf,
737116491Sharti		    ((hatm_cps2atmf(t->pcr) << HE_REGS_TSR1_PCR) |
738116491Sharti		     (hatm_cps2atmf(t->mcr) << HE_REGS_TSR1_MCR)));
739116491Sharti		WRITE_TSR(sc, cid, 2, 0xf,
740116491Sharti		    (hatm_cps2atmf(t->icr) << HE_REGS_TSR2_ACR));
741116491Sharti		WRITE_TSR(sc, cid, 3, 0xf,
742116491Sharti		    ((NRM_CODE2VAL(t->nrm) - 1) << HE_REGS_TSR3_NRM) |
743116491Sharti		    (crm << HE_REGS_TSR3_CRM));
744116491Sharti
745116491Sharti		WRITE_TSR(sc, cid, 5, 0xf, 0);
746116491Sharti		WRITE_TSR(sc, cid, 6, 0xf, 0);
747116491Sharti		WRITE_TSR(sc, cid, 7, 0xf, 0);
748116491Sharti		WRITE_TSR(sc, cid, 8, 0xf, 0);
749116491Sharti		WRITE_TSR(sc, cid, 10, 0xf, 0);
750116491Sharti		WRITE_TSR(sc, cid, 12, 0xf, 0);
751116491Sharti		WRITE_TSR(sc, cid, 14, 0xf, 0);
752116491Sharti		WRITE_TSR(sc, cid, 9, 0xf, HE_REGM_TSR9_INIT);
753116491Sharti
754116491Sharti		WRITE_TSR(sc, cid, 11, 0xf,
755116491Sharti		    (hatm_cps2atmf(t->icr) << HE_REGS_TSR11_ICR) |
756116491Sharti		    (t->trm << HE_REGS_TSR11_TRM) |
757116491Sharti		    (t->nrm << HE_REGS_TSR11_NRM) |
758116491Sharti		    (t->adtf << HE_REGS_TSR11_ADTF));
759116491Sharti
760116491Sharti		WRITE_TSR(sc, cid, 13, 0xf,
761116491Sharti		    (t->rdf << HE_REGS_TSR13_RDF) |
762116491Sharti		    (t->rif << HE_REGS_TSR13_RIF) |
763116491Sharti		    (t->cdf << HE_REGS_TSR13_CDF) |
764116491Sharti		    (crm << HE_REGS_TSR13_CRM));
765116491Sharti
766116491Sharti		break;
767116491Sharti
768116491Sharti	  default:
769116491Sharti		return;
770116491Sharti	}
771116491Sharti
772116491Sharti	vcc->vflags |= HE_VCC_TX_OPEN;
773116491Sharti}
774116491Sharti
775116491Sharti/*
776116491Sharti * Close the TX side of a VCC. Set the CLOSING flag.
777116491Sharti */
778116491Shartivoid
779116491Shartihatm_tx_vcc_close(struct hatm_softc *sc, u_int cid)
780116491Sharti{
781116491Sharti	struct hevcc *vcc = sc->vccs[cid];
782116491Sharti	struct tpd *tpd_list[1];
783116491Sharti	u_int i, pcr = 0;
784116491Sharti
785116491Sharti	WRITE_TSR(sc, cid, 4, 0x8, HE_REGM_TSR4_FLUSH);
786116491Sharti
787116491Sharti	switch (vcc->param.traffic) {
788116491Sharti
789116491Sharti	  case ATMIO_TRAFFIC_CBR:
790116491Sharti		WRITE_TSR(sc, cid, 14, 0x8, HE_REGM_TSR14_CBR_DELETE);
791116491Sharti		break;
792116491Sharti
793116491Sharti	  case ATMIO_TRAFFIC_ABR:
794116491Sharti		WRITE_TSR(sc, cid, 14, 0x4, HE_REGM_TSR14_ABR_CLOSE);
795116491Sharti		pcr = vcc->param.tparam.pcr;
796116491Sharti		/* FALL THROUGH */
797116491Sharti
798116491Sharti	  case ATMIO_TRAFFIC_UBR:
799116491Sharti		WRITE_TSR(sc, cid, 1, 0xf,
800116491Sharti		    hatm_cps2atmf(HE_CONFIG_FLUSH_RATE) << HE_REGS_TSR1_MCR |
801116491Sharti		    hatm_cps2atmf(pcr) << HE_REGS_TSR1_PCR);
802116491Sharti		break;
803116491Sharti	}
804116491Sharti
805116491Sharti	tpd_list[0] = hatm_alloc_tpd(sc, 0);
806116491Sharti	tpd_list[0]->tpd.addr |= HE_REGM_TPD_EOS | HE_REGM_TPD_INTR;
807116491Sharti	tpd_list[0]->cid = cid;
808116491Sharti
809116491Sharti	vcc->vflags |= HE_VCC_TX_CLOSING;
810116491Sharti	vcc->vflags &= ~HE_VCC_TX_OPEN;
811116491Sharti
812116491Sharti	i = 0;
813116491Sharti	while (hatm_queue_tpds(sc, 1, tpd_list, cid) != 0) {
814116491Sharti		if (++i == 1000)
815116491Sharti			panic("TPDRQ permanently full");
816116491Sharti		DELAY(1000);
817116491Sharti	}
818116491Sharti}
819116491Sharti
820116491Shartivoid
821116491Shartihatm_tx_vcc_closed(struct hatm_softc *sc, u_int cid)
822116491Sharti{
823116491Sharti	if (sc->vccs[cid]->param.traffic == ATMIO_TRAFFIC_CBR) {
824116491Sharti		sc->cbr_bw -= sc->vccs[cid]->param.tparam.pcr;
825116491Sharti		sc->rate_ctrl[sc->vccs[cid]->rc].refcnt--;
826116491Sharti	}
827116491Sharti}
828