1139749Simp/*-
2117632Sharti * Copyright (c) 2003
3117632Sharti *	Fraunhofer Institute for Open Communication Systems (FhG Fokus).
4117632Sharti * 	All rights reserved.
5117632Sharti *
6117632Sharti * Redistribution and use in source and binary forms, with or without
7117632Sharti * modification, are permitted provided that the following conditions
8117632Sharti * are met:
9117632Sharti * 1. Redistributions of source code must retain the above copyright
10117632Sharti *    notice, this list of conditions and the following disclaimer.
11117632Sharti * 2. Redistributions in binary form must reproduce the above copyright
12117632Sharti *    notice, this list of conditions and the following disclaimer in the
13117632Sharti *    documentation and/or other materials provided with the distribution.
14117632Sharti *
15117632Sharti * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16117632Sharti * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17117632Sharti * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18117632Sharti * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19117632Sharti * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20117632Sharti * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21117632Sharti * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22117632Sharti * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23117632Sharti * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24117632Sharti * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25117632Sharti * SUCH DAMAGE.
26117632Sharti *
27117632Sharti * The TST allocation algorithm is from the IDT driver which is:
28117632Sharti *
29117632Sharti *	Copyright (c) 2000, 2001 Richard Hodges and Matriplex, inc.
30117632Sharti *	All rights reserved.
31117632Sharti *
32117632Sharti *	Copyright (c) 1996, 1997, 1998, 1999 Mark Tinguely
33117632Sharti *	All rights reserved.
34117632Sharti *
35117632Sharti * Author: Hartmut Brandt <harti@freebsd.org>
36117632Sharti *
37117632Sharti * Driver for IDT77252 based cards like ProSum's.
38117632Sharti */
39119418Sobrien
40117632Sharti#include <sys/cdefs.h>
41117632Sharti__FBSDID("$FreeBSD: stable/11/sys/dev/patm/if_patm_tx.c 315221 2017-03-14 02:06:03Z pfg $");
42117632Sharti
43117632Sharti#include "opt_inet.h"
44117632Sharti#include "opt_natm.h"
45117632Sharti
46117632Sharti#include <sys/types.h>
47117632Sharti#include <sys/param.h>
48117632Sharti#include <sys/systm.h>
49117632Sharti#include <sys/malloc.h>
50117632Sharti#include <sys/kernel.h>
51117632Sharti#include <sys/bus.h>
52117632Sharti#include <sys/errno.h>
53117632Sharti#include <sys/conf.h>
54117632Sharti#include <sys/module.h>
55117632Sharti#include <sys/lock.h>
56117632Sharti#include <sys/mutex.h>
57117632Sharti#include <sys/sysctl.h>
58117632Sharti#include <sys/queue.h>
59117632Sharti#include <sys/condvar.h>
60117632Sharti#include <sys/endian.h>
61117632Sharti#include <vm/uma.h>
62117632Sharti
63117632Sharti#include <sys/sockio.h>
64117632Sharti#include <sys/mbuf.h>
65117632Sharti#include <sys/socket.h>
66117632Sharti
67117632Sharti#include <net/if.h>
68257176Sglebius#include <net/if_var.h>
69117632Sharti#include <net/if_media.h>
70117632Sharti#include <net/if_atm.h>
71117632Sharti#include <net/route.h>
72117632Sharti#ifdef ENABLE_BPF
73117632Sharti#include <net/bpf.h>
74117632Sharti#endif
75117632Sharti#include <netinet/in.h>
76117632Sharti#include <netinet/if_atm.h>
77117632Sharti
78117632Sharti#include <machine/bus.h>
79117632Sharti#include <machine/resource.h>
80117632Sharti#include <sys/bus.h>
81117632Sharti#include <sys/rman.h>
82117632Sharti#include <sys/mbpool.h>
83117632Sharti
84117632Sharti#include <dev/utopia/utopia.h>
85117632Sharti#include <dev/patm/idt77252reg.h>
86117632Sharti#include <dev/patm/if_patmvar.h>
87117632Sharti
88117632Shartistatic struct mbuf *patm_tx_pad(struct patm_softc *sc, struct mbuf *m0);
89117632Shartistatic void patm_launch(struct patm_softc *sc, struct patm_scd *scd);
90117632Sharti
91117632Shartistatic struct patm_txmap *patm_txmap_get(struct patm_softc *);
92117632Shartistatic void patm_load_txbuf(void *, bus_dma_segment_t *, int,
93117632Sharti    bus_size_t, int);
94117632Sharti
95117632Shartistatic void patm_tst_alloc(struct patm_softc *sc, struct patm_vcc *vcc);
96117632Shartistatic void patm_tst_free(struct patm_softc *sc, struct patm_vcc *vcc);
97117632Shartistatic void patm_tst_timer(void *p);
98117632Shartistatic void patm_tst_update(struct patm_softc *);
99117632Sharti
100117632Shartistatic void patm_tct_start(struct patm_softc *sc, struct patm_vcc *);
101117632Sharti
102117632Shartistatic const char *dump_scd(struct patm_softc *sc, struct patm_scd *scd)
103117632Sharti    __unused;
104117632Shartistatic void patm_tct_print(struct patm_softc *sc, u_int cid) __unused;
105117632Sharti
106117632Sharti/*
107117632Sharti * Structure for communication with the loader function for transmission
108117632Sharti */
109117632Shartistruct txarg {
110117632Sharti	struct patm_softc *sc;
111117632Sharti	struct patm_scd	*scd;		/* scheduling channel */
112117632Sharti	struct patm_vcc	*vcc;		/* the VCC of this PDU */
113117632Sharti	struct mbuf	*mbuf;
114117632Sharti	u_int		hdr;		/* cell header */
115117632Sharti};
116117632Sharti
117117632Shartistatic __inline u_int
118117632Sharticbr2slots(struct patm_softc *sc, struct patm_vcc *vcc)
119117632Sharti{
120117632Sharti	/* compute the number of slots we need, make sure to get at least
121117632Sharti	 * the specified PCR */
122298646Spfg	return ((u_int)howmany((uint64_t)(sc->mmap->tst_size - 1) *
123298646Spfg	    vcc->vcc.tparam.pcr, IFP2IFATM(sc->ifp)->mib.pcr));
124117632Sharti}
125117632Sharti
126117632Shartistatic __inline u_int
127117632Shartislots2cr(struct patm_softc *sc, u_int slots)
128117632Sharti{
129147256Sbrooks	return ((slots * IFP2IFATM(sc->ifp)->mib.pcr + sc->mmap->tst_size - 2) /
130117632Sharti	    (sc->mmap->tst_size - 1));
131117632Sharti}
132117632Sharti
133117632Sharti/* check if we can open this one */
134117632Shartiint
135117632Shartipatm_tx_vcc_can_open(struct patm_softc *sc, struct patm_vcc *vcc)
136117632Sharti{
137117632Sharti
138117632Sharti	/* check resources */
139117632Sharti	switch (vcc->vcc.traffic) {
140117632Sharti
141117632Sharti	  case ATMIO_TRAFFIC_CBR:
142117632Sharti	    {
143117632Sharti		u_int slots = cbr2slots(sc, vcc);
144117632Sharti
145117632Sharti		if (slots > sc->tst_free + sc->tst_reserve)
146117632Sharti			return (EINVAL);
147117632Sharti		break;
148117632Sharti	    }
149117632Sharti
150117632Sharti	  case ATMIO_TRAFFIC_VBR:
151117632Sharti		if (vcc->vcc.tparam.scr > sc->bwrem)
152117632Sharti			return (EINVAL);
153147256Sbrooks		if (vcc->vcc.tparam.pcr > IFP2IFATM(sc->ifp)->mib.pcr)
154117632Sharti			return (EINVAL);
155117632Sharti		if (vcc->vcc.tparam.scr > vcc->vcc.tparam.pcr ||
156117632Sharti		    vcc->vcc.tparam.mbs == 0)
157117632Sharti			return (EINVAL);
158117632Sharti		break;
159117632Sharti
160117632Sharti	  case ATMIO_TRAFFIC_ABR:
161117632Sharti		if (vcc->vcc.tparam.tbe == 0 ||
162117632Sharti		    vcc->vcc.tparam.nrm == 0)
163117632Sharti			/* needed to compute CRM */
164117632Sharti			return (EINVAL);
165147256Sbrooks		if (vcc->vcc.tparam.pcr > IFP2IFATM(sc->ifp)->mib.pcr ||
166117632Sharti		    vcc->vcc.tparam.icr > vcc->vcc.tparam.pcr ||
167117632Sharti		    vcc->vcc.tparam.mcr > vcc->vcc.tparam.icr)
168117632Sharti			return (EINVAL);
169117632Sharti		if (vcc->vcc.tparam.mcr > sc->bwrem ||
170117632Sharti		    vcc->vcc.tparam.icr > sc->bwrem)
171117632Sharti			return (EINVAL);
172117632Sharti		break;
173117632Sharti	}
174117632Sharti
175117632Sharti	return (0);
176117632Sharti}
177117632Sharti
178117632Sharti#define	NEXT_TAG(T) do {				\
179117632Sharti	(T) = ((T) + 1) % IDT_TSQE_TAG_SPACE;		\
180117632Sharti    } while (0)
181117632Sharti
182117632Sharti/*
183117632Sharti * open it
184117632Sharti */
185117632Shartivoid
186117632Shartipatm_tx_vcc_open(struct patm_softc *sc, struct patm_vcc *vcc)
187117632Sharti{
188117632Sharti	struct patm_scd *scd;
189117632Sharti
190117632Sharti	if (vcc->vcc.traffic == ATMIO_TRAFFIC_UBR) {
191117632Sharti		/* we use UBR0 */
192117632Sharti		vcc->scd = sc->scd0;
193117632Sharti		vcc->vflags |= PATM_VCC_TX_OPEN;
194117632Sharti		return;
195117632Sharti	}
196117632Sharti
197117632Sharti	/* get an SCD */
198117632Sharti	scd = patm_scd_alloc(sc);
199117632Sharti	if (scd == NULL) {
200117632Sharti		/* should not happen */
201117632Sharti		patm_printf(sc, "out of SCDs\n");
202117632Sharti		return;
203117632Sharti	}
204117632Sharti	vcc->scd = scd;
205117632Sharti	patm_scd_setup(sc, scd);
206117632Sharti	patm_tct_setup(sc, scd, vcc);
207117632Sharti
208117632Sharti	if (vcc->vcc.traffic != ATMIO_TRAFFIC_CBR)
209117632Sharti		patm_tct_start(sc, vcc);
210117632Sharti
211117632Sharti	vcc->vflags |= PATM_VCC_TX_OPEN;
212117632Sharti}
213117632Sharti
214117632Sharti/*
215117632Sharti * close the given vcc for transmission
216117632Sharti */
217117632Shartivoid
218117632Shartipatm_tx_vcc_close(struct patm_softc *sc, struct patm_vcc *vcc)
219117632Sharti{
220117632Sharti	struct patm_scd *scd;
221117632Sharti	struct mbuf *m;
222117632Sharti
223117632Sharti	vcc->vflags |= PATM_VCC_TX_CLOSING;
224117632Sharti
225117632Sharti	if (vcc->vcc.traffic == ATMIO_TRAFFIC_UBR) {
226117632Sharti		/* let the queue PDUs go out */
227117632Sharti		vcc->scd = NULL;
228117632Sharti		vcc->vflags &= ~(PATM_VCC_TX_OPEN | PATM_VCC_TX_CLOSING);
229117632Sharti		return;
230117632Sharti	}
231117632Sharti	scd = vcc->scd;
232117632Sharti
233117632Sharti	/* empty the waitq */
234117632Sharti	for (;;) {
235117632Sharti		_IF_DEQUEUE(&scd->q, m);
236117632Sharti		if (m == NULL)
237117632Sharti			break;
238117632Sharti		m_freem(m);
239117632Sharti	}
240117632Sharti
241117632Sharti	if (scd->num_on_card == 0) {
242117632Sharti		/* we are idle */
243117632Sharti		vcc->vflags &= ~PATM_VCC_TX_OPEN;
244117632Sharti
245117632Sharti		if (vcc->vcc.traffic == ATMIO_TRAFFIC_CBR)
246117632Sharti			patm_tst_free(sc, vcc);
247117632Sharti
248117632Sharti		patm_sram_write4(sc, scd->sram + 0, 0, 0, 0, 0);
249117632Sharti		patm_sram_write4(sc, scd->sram + 4, 0, 0, 0, 0);
250117632Sharti		patm_scd_free(sc, scd);
251117632Sharti
252117632Sharti		vcc->scd = NULL;
253117632Sharti		vcc->vflags &= ~PATM_VCC_TX_CLOSING;
254117632Sharti
255117632Sharti		return;
256117632Sharti	}
257117632Sharti
258117632Sharti	/* speed up transmission */
259117632Sharti	patm_nor_write(sc, IDT_NOR_TCMDQ, IDT_TCMDQ_UIER(vcc->cid, 0xff));
260117632Sharti	patm_nor_write(sc, IDT_NOR_TCMDQ, IDT_TCMDQ_ULACR(vcc->cid, 0xff));
261117632Sharti
262117632Sharti	/* wait for the interrupt to drop the number to 0 */
263117632Sharti	patm_debug(sc, VCC, "%u buffers still on card", scd->num_on_card);
264117632Sharti}
265117632Sharti
266117632Sharti/* transmission side finally closed */
267117632Shartivoid
268117632Shartipatm_tx_vcc_closed(struct patm_softc *sc, struct patm_vcc *vcc)
269117632Sharti{
270117632Sharti
271117632Sharti	patm_debug(sc, VCC, "%u.%u TX closed", vcc->vcc.vpi, vcc->vcc.vci);
272117632Sharti
273117632Sharti	if (vcc->vcc.traffic == ATMIO_TRAFFIC_VBR)
274117632Sharti		sc->bwrem += vcc->vcc.tparam.scr;
275117632Sharti}
276117632Sharti
277117632Sharti/*
278117632Sharti * Pull off packets from the interface queue and try to transmit them.
279117632Sharti * If the transmission fails because of a full transmit channel, we drop
280117632Sharti * packets for CBR and queue them for other channels up to limit.
281117632Sharti * This limit should depend on the CDVT for VBR and ABR, but it doesn't.
282117632Sharti */
283117632Shartivoid
284117632Shartipatm_start(struct ifnet *ifp)
285117632Sharti{
286147721Sharti	struct patm_softc *sc = ifp->if_softc;
287117632Sharti	struct mbuf *m;
288117632Sharti	struct atm_pseudohdr *aph;
289117632Sharti	u_int vpi, vci, cid;
290117632Sharti	struct patm_vcc *vcc;
291117632Sharti
292117632Sharti	mtx_lock(&sc->mtx);
293148887Srwatson	if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
294117632Sharti		mtx_unlock(&sc->mtx);
295117632Sharti		return;
296117632Sharti	}
297117632Sharti
298117632Sharti	while (1) {
299117632Sharti		/* get a new mbuf */
300117632Sharti		IF_DEQUEUE(&ifp->if_snd, m);
301117632Sharti		if (m == NULL)
302117632Sharti			break;
303117632Sharti
304117632Sharti		/* split of pseudo header */
305117632Sharti		if (m->m_len < sizeof(*aph) &&
306117632Sharti		    (m = m_pullup(m, sizeof(*aph))) == NULL) {
307271849Sglebius			if_inc_counter(sc->ifp, IFCOUNTER_OERRORS, 1);
308117632Sharti			continue;
309117632Sharti		}
310117632Sharti
311117632Sharti		aph = mtod(m, struct atm_pseudohdr *);
312117632Sharti		vci = ATM_PH_VCI(aph);
313117632Sharti		vpi = ATM_PH_VPI(aph);
314117632Sharti		m_adj(m, sizeof(*aph));
315117632Sharti
316117632Sharti		/* reject empty packets */
317117632Sharti		if (m->m_pkthdr.len == 0) {
318117632Sharti			m_freem(m);
319271849Sglebius			if_inc_counter(sc->ifp, IFCOUNTER_OERRORS, 1);
320117632Sharti			continue;
321117632Sharti		}
322117632Sharti
323117632Sharti		/* check whether this is a legal vcc */
324117632Sharti		if (!LEGAL_VPI(sc, vpi) || !LEGAL_VCI(sc, vci) || vci == 0) {
325117632Sharti			m_freem(m);
326271849Sglebius			if_inc_counter(sc->ifp, IFCOUNTER_OERRORS, 1);
327117632Sharti			continue;
328117632Sharti		}
329117632Sharti		cid = PATM_CID(sc, vpi, vci);
330117632Sharti		vcc = sc->vccs[cid];
331117632Sharti		if (vcc == NULL) {
332117632Sharti			m_freem(m);
333271849Sglebius			if_inc_counter(sc->ifp, IFCOUNTER_OERRORS, 1);
334117632Sharti			continue;
335117632Sharti		}
336117632Sharti
337117632Sharti		/* must be multiple of 48 if not AAL5 */
338117632Sharti		if (vcc->vcc.aal == ATMIO_AAL_0 ||
339117632Sharti		    vcc->vcc.aal == ATMIO_AAL_34) {
340117632Sharti			/* XXX AAL3/4 format? */
341117632Sharti			if (m->m_pkthdr.len % 48 != 0 &&
342117632Sharti			    (m = patm_tx_pad(sc, m)) == NULL) {
343271849Sglebius				if_inc_counter(sc->ifp, IFCOUNTER_OERRORS, 1);
344117632Sharti				continue;
345117632Sharti			}
346117632Sharti		} else if (vcc->vcc.aal == ATMIO_AAL_RAW) {
347117632Sharti			switch (vcc->vflags & PATM_RAW_FORMAT) {
348117632Sharti
349117632Sharti			  default:
350117632Sharti			  case PATM_RAW_CELL:
351117632Sharti				if (m->m_pkthdr.len != 53) {
352271849Sglebius					if_inc_counter(sc->ifp, IFCOUNTER_OERRORS, 1);
353117632Sharti					m_freem(m);
354117632Sharti					continue;
355117632Sharti				}
356117632Sharti				break;
357117632Sharti
358117632Sharti			  case PATM_RAW_NOHEC:
359117632Sharti				if (m->m_pkthdr.len != 52) {
360271849Sglebius					if_inc_counter(sc->ifp, IFCOUNTER_OERRORS, 1);
361117632Sharti					m_freem(m);
362117632Sharti					continue;
363117632Sharti				}
364117632Sharti				break;
365117632Sharti
366117632Sharti			  case PATM_RAW_CS:
367117632Sharti				if (m->m_pkthdr.len != 64) {
368271849Sglebius					if_inc_counter(sc->ifp, IFCOUNTER_OERRORS, 1);
369117632Sharti					m_freem(m);
370117632Sharti					continue;
371117632Sharti				}
372117632Sharti				break;
373117632Sharti			}
374117632Sharti		}
375117632Sharti
376117632Sharti		/* save data */
377254804Sandre		m->m_pkthdr.PH_loc.ptr = vcc;
378117632Sharti
379117632Sharti		/* try to put it on the channels queue */
380117632Sharti		if (_IF_QFULL(&vcc->scd->q)) {
381271849Sglebius			if_inc_counter(sc->ifp, IFCOUNTER_OERRORS, 1);
382117632Sharti			sc->stats.tx_qfull++;
383117632Sharti			m_freem(m);
384117632Sharti			continue;
385117632Sharti		}
386117632Sharti		_IF_ENQUEUE(&vcc->scd->q, m);
387117632Sharti
388117632Sharti#ifdef ENABLE_BPF
389117632Sharti		if (!(vcc->vcc.flags & ATMIO_FLAG_NG) &&
390118539Sharti		    (vcc->vcc.aal == ATMIO_AAL_5) &&
391117632Sharti		    (vcc->vcc.flags & ATM_PH_LLCSNAP))
392117632Sharti		 	BPF_MTAP(ifp, m);
393117632Sharti#endif
394117632Sharti
395117632Sharti		/* kick the channel to life */
396117632Sharti		patm_launch(sc, vcc->scd);
397117632Sharti
398117632Sharti	}
399117632Sharti	mtx_unlock(&sc->mtx);
400117632Sharti}
401117632Sharti
402117632Sharti/*
403117632Sharti * Pad non-AAL5 packet to a multiple of 48-byte.
404117632Sharti * We assume AAL0 only. We have still to decide on the format of AAL3/4.
405117632Sharti */
406117632Shartistatic struct mbuf *
407117632Shartipatm_tx_pad(struct patm_softc *sc, struct mbuf *m0)
408117632Sharti{
409117632Sharti	struct mbuf *last, *m;
410117632Sharti	u_int plen, pad, space;
411117632Sharti
412117632Sharti	plen = m_length(m0, &last);
413117632Sharti	if (plen != m0->m_pkthdr.len) {
414117632Sharti		patm_printf(sc, "%s: mbuf length mismatch %d %u\n", __func__,
415117632Sharti		    m0->m_pkthdr.len, plen);
416117632Sharti		m0->m_pkthdr.len = plen;
417117632Sharti		if (plen == 0) {
418117632Sharti			m_freem(m0);
419271849Sglebius			if_inc_counter(sc->ifp, IFCOUNTER_OERRORS, 1);
420117632Sharti			return (NULL);
421117632Sharti		}
422117632Sharti		if (plen % 48 == 0)
423117632Sharti			return (m0);
424117632Sharti	}
425117632Sharti	pad = 48 - plen % 48;
426117872Sharti	m0->m_pkthdr.len += pad;
427117632Sharti	if (M_WRITABLE(last)) {
428117632Sharti		if (M_TRAILINGSPACE(last) >= pad) {
429117632Sharti			bzero(last->m_data + last->m_len, pad);
430117632Sharti			last->m_len += pad;
431117632Sharti			return (m0);
432117632Sharti		}
433117632Sharti		space = M_LEADINGSPACE(last);
434117632Sharti		if (space + M_TRAILINGSPACE(last) >= pad) {
435117632Sharti			bcopy(last->m_data, last->m_data + space, last->m_len);
436117632Sharti			last->m_data -= space;
437117632Sharti			bzero(last->m_data + last->m_len, pad);
438117632Sharti			last->m_len += pad;
439117632Sharti			return (m0);
440117632Sharti		}
441117632Sharti	}
442243857Sglebius	MGET(m, M_NOWAIT, MT_DATA);
443315221Spfg	if (m == NULL) {
444117632Sharti		m_freem(m0);
445271849Sglebius		if_inc_counter(sc->ifp, IFCOUNTER_OERRORS, 1);
446117632Sharti		return (NULL);
447117632Sharti	}
448117632Sharti	bzero(mtod(m, u_char *), pad);
449117632Sharti	m->m_len = pad;
450117632Sharti	last->m_next = m;
451117632Sharti
452117632Sharti	return (m0);
453117632Sharti}
454117632Sharti
455117632Sharti/*
456117632Sharti * Try to put as many packets from the channels queue onto the channel
457117632Sharti */
458117632Shartistatic void
459117632Shartipatm_launch(struct patm_softc *sc, struct patm_scd *scd)
460117632Sharti{
461117632Sharti	struct txarg a;
462117632Sharti	struct mbuf *m, *tmp;
463117632Sharti	u_int segs;
464117632Sharti	struct patm_txmap *map;
465117632Sharti	int error;
466117632Sharti
467117632Sharti	a.sc = sc;
468117632Sharti	a.scd = scd;
469117632Sharti
470117632Sharti	/* limit the number of outstanding packets to the tag space */
471117632Sharti	while (scd->num_on_card < IDT_TSQE_TAG_SPACE) {
472117632Sharti		/* get the next packet */
473117632Sharti		_IF_DEQUEUE(&scd->q, m);
474117632Sharti		if (m == NULL)
475117632Sharti			break;
476117632Sharti
477254804Sandre		a.vcc = m->m_pkthdr.PH_loc.ptr;
478117632Sharti
479117632Sharti		/* we must know the number of segments beforehand - count
480117632Sharti		 * this may actually give a wrong number of segments for
481117632Sharti		 * AAL_RAW where we still need to remove the cell header */
482117632Sharti		segs = 0;
483117632Sharti		for (tmp = m; tmp != NULL; tmp = tmp->m_next)
484117632Sharti			if (tmp->m_len != 0)
485117632Sharti				segs++;
486117632Sharti
487117632Sharti		/* check whether there is space in the queue */
488117632Sharti		if (segs >= scd->space) {
489117632Sharti			/* put back */
490117632Sharti			_IF_PREPEND(&scd->q, m);
491117632Sharti			sc->stats.tx_out_of_tbds++;
492117632Sharti			break;
493117632Sharti		}
494117632Sharti
495117632Sharti		/* get a DMA map */
496117632Sharti		if ((map = patm_txmap_get(sc)) == NULL) {
497117632Sharti			_IF_PREPEND(&scd->q, m);
498117632Sharti			sc->stats.tx_out_of_maps++;
499117632Sharti			break;
500117632Sharti		}
501117632Sharti
502117632Sharti		/* load the map */
503254804Sandre		m->m_pkthdr.PH_loc.ptr = map;
504117632Sharti		a.mbuf = m;
505117632Sharti
506117632Sharti		/* handle AAL_RAW */
507117632Sharti		if (a.vcc->vcc.aal == ATMIO_AAL_RAW) {
508117632Sharti			u_char hdr[4];
509117632Sharti
510117632Sharti			m_copydata(m, 0, 4, hdr);
511117632Sharti			a.hdr = (hdr[0] << 24) | (hdr[1] << 16) |
512117632Sharti			    (hdr[2] << 8) | hdr[3];
513117632Sharti
514117632Sharti			switch (a.vcc->vflags & PATM_RAW_FORMAT) {
515117632Sharti
516117632Sharti			  default:
517117632Sharti			  case PATM_RAW_CELL:
518117632Sharti				m_adj(m, 5);
519117632Sharti				break;
520117632Sharti
521117632Sharti			  case PATM_RAW_NOHEC:
522117632Sharti				m_adj(m, 4);
523117632Sharti				break;
524117632Sharti
525117632Sharti			  case PATM_RAW_CS:
526117632Sharti				m_adj(m, 16);
527117632Sharti				break;
528117632Sharti			}
529117632Sharti		} else
530117632Sharti			a.hdr = IDT_TBD_HDR(a.vcc->vcc.vpi, a.vcc->vcc.vci,
531117632Sharti			    0, 0);
532117632Sharti
533117632Sharti		error = bus_dmamap_load_mbuf(sc->tx_tag, map->map, m,
534117632Sharti		    patm_load_txbuf, &a, BUS_DMA_NOWAIT);
535117632Sharti		if (error == EFBIG) {
536243857Sglebius			if ((m = m_defrag(m, M_NOWAIT)) == NULL) {
537271849Sglebius				if_inc_counter(sc->ifp, IFCOUNTER_OERRORS, 1);
538117632Sharti				continue;
539117632Sharti			}
540117632Sharti			error = bus_dmamap_load_mbuf(sc->tx_tag, map->map, m,
541117632Sharti			    patm_load_txbuf, &a, BUS_DMA_NOWAIT);
542117632Sharti		}
543117632Sharti		if (error != 0) {
544117632Sharti			sc->stats.tx_load_err++;
545271849Sglebius			if_inc_counter(sc->ifp, IFCOUNTER_OERRORS, 1);
546117632Sharti			SLIST_INSERT_HEAD(&sc->tx_maps_free, map, link);
547117632Sharti			m_freem(m);
548117632Sharti			continue;
549117632Sharti		}
550117632Sharti
551271849Sglebius		if_inc_counter(sc->ifp, IFCOUNTER_OPACKETS, 1);
552117632Sharti	}
553117632Sharti}
554117632Sharti
555117632Sharti/*
556117632Sharti * Load the DMA segments into the scheduling channel
557117632Sharti */
558117632Shartistatic void
559117632Shartipatm_load_txbuf(void *uarg, bus_dma_segment_t *segs, int nseg,
560117632Sharti    bus_size_t mapsize, int error)
561117632Sharti{
562117632Sharti	struct txarg *a= uarg;
563117632Sharti	struct patm_scd *scd = a->scd;
564117632Sharti	u_int w1, w3, cnt;
565117632Sharti	struct idt_tbd *tbd = NULL;
566117632Sharti	u_int rest = mapsize;
567117632Sharti
568117632Sharti	if (error != 0)
569117632Sharti		return;
570117632Sharti
571117632Sharti	cnt = 0;
572117632Sharti	while (nseg > 0) {
573117632Sharti		if (segs->ds_len == 0) {
574117632Sharti			/* transmit buffer length must be > 0 */
575117632Sharti			nseg--;
576117632Sharti			segs++;
577117632Sharti			continue;
578117632Sharti		}
579117632Sharti		/* rest after this buffer */
580117632Sharti		rest -= segs->ds_len;
581117632Sharti
582117632Sharti		/* put together status word */
583117632Sharti		w1 = 0;
584117632Sharti		if (rest < 48 /* && a->vcc->vcc.aal != ATMIO_AAL_5 */)
585117632Sharti			/* last cell is in this buffer */
586117632Sharti			w1 |= IDT_TBD_EPDU;
587117632Sharti
588117632Sharti		if (a->vcc->vcc.aal == ATMIO_AAL_5)
589117632Sharti			w1 |= IDT_TBD_AAL5;
590117632Sharti		else if (a->vcc->vcc.aal == ATMIO_AAL_34)
591117632Sharti			w1 |= IDT_TBD_AAL34;
592117632Sharti		else
593117632Sharti			w1 |= IDT_TBD_AAL0;
594117632Sharti
595117632Sharti		w1 |= segs->ds_len;
596117632Sharti
597117632Sharti		/* AAL5 PDU length (unpadded) */
598117632Sharti		if (a->vcc->vcc.aal == ATMIO_AAL_5)
599117632Sharti			w3 = mapsize;
600117632Sharti		else
601117632Sharti			w3 = 0;
602117632Sharti
603117632Sharti		if (rest == 0)
604117632Sharti			w1 |= IDT_TBD_TSIF | IDT_TBD_GTSI |
605117632Sharti			    (scd->tag << IDT_TBD_TAG_SHIFT);
606117632Sharti
607117632Sharti		tbd = &scd->scq[scd->tail];
608117632Sharti
609117632Sharti		tbd->flags = htole32(w1);
610117632Sharti		tbd->addr = htole32(segs->ds_addr);
611117632Sharti		tbd->aal5 = htole32(w3);
612117632Sharti		tbd->hdr = htole32(a->hdr);
613117632Sharti
614117632Sharti		patm_debug(a->sc, TX, "TBD(%u): %08x %08x %08x %08x",
615117632Sharti		    scd->tail, w1, segs->ds_addr, w3, a->hdr);
616117632Sharti
617117632Sharti		/* got to next entry */
618117632Sharti		if (++scd->tail == IDT_SCQ_SIZE)
619117632Sharti			scd->tail = 0;
620117632Sharti		cnt++;
621117632Sharti		nseg--;
622117632Sharti		segs++;
623117632Sharti	}
624117632Sharti	scd->space -= cnt;
625117632Sharti	scd->num_on_card++;
626117632Sharti
627117632Sharti	KASSERT(rest == 0, ("bad mbuf"));
628117632Sharti	KASSERT(cnt > 0, ("no segs"));
629117632Sharti	KASSERT(scd->space > 0, ("scq full"));
630117632Sharti
631117632Sharti	KASSERT(scd->on_card[scd->tag] == NULL,
632117632Sharti	    ("scd on_card wedged %u%s", scd->tag, dump_scd(a->sc, scd)));
633117632Sharti	scd->on_card[scd->tag] = a->mbuf;
634117632Sharti	a->mbuf->m_pkthdr.csum_data = cnt;
635117632Sharti
636117632Sharti	NEXT_TAG(scd->tag);
637117632Sharti
638117632Sharti	patm_debug(a->sc, TX, "SCD tail %u (%lx:%lx)", scd->tail,
639117632Sharti	    (u_long)scd->phy, (u_long)scd->phy + (scd->tail << IDT_TBD_SHIFT));
640117632Sharti	patm_sram_write(a->sc, scd->sram,
641117632Sharti	    scd->phy + (scd->tail << IDT_TBD_SHIFT));
642117632Sharti
643117632Sharti	if (patm_sram_read(a->sc, a->vcc->cid * 8 + 3) & IDT_TCT_IDLE) {
644117632Sharti		/*
645117632Sharti		 * if the connection is idle start it. We cannot rely
646117632Sharti		 * on a flag set by patm_tx_idle() here, because sometimes
647117632Sharti		 * the card seems to place an idle TSI into the TSQ but
648117632Sharti		 * forgets to raise an interrupt.
649117632Sharti		 */
650117632Sharti		patm_nor_write(a->sc, IDT_NOR_TCMDQ,
651117632Sharti		    IDT_TCMDQ_START(a->vcc->cid));
652117632Sharti	}
653117632Sharti}
654117632Sharti
655117632Sharti/*
656117632Sharti * packet transmitted
657117632Sharti */
658117632Shartivoid
659117632Shartipatm_tx(struct patm_softc *sc, u_int stamp, u_int status)
660117632Sharti{
661117632Sharti	u_int cid, tag, last;
662117632Sharti	struct mbuf *m;
663117632Sharti	struct patm_vcc *vcc;
664117632Sharti	struct patm_scd *scd;
665117632Sharti	struct patm_txmap *map;
666117632Sharti
667117632Sharti	/* get the connection */
668117632Sharti	cid = PATM_CID(sc, IDT_TBD_VPI(status), IDT_TBD_VCI(status));
669117632Sharti	if ((vcc = sc->vccs[cid]) == NULL) {
670117632Sharti		/* closed UBR connection */
671117632Sharti		return;
672117632Sharti	}
673117632Sharti	scd = vcc->scd;
674117632Sharti
675117632Sharti	tag = IDT_TSQE_TAG(stamp);
676117632Sharti
677117632Sharti	last = scd->last_tag;
678117632Sharti	if (tag == last) {
679117632Sharti		patm_printf(sc, "same tag %u\n", tag);
680117632Sharti		return;
681117632Sharti	}
682117632Sharti
683117632Sharti	/* Errata 12 requests us to free all entries up to the one
684117632Sharti	 * with the given tag. */
685117632Sharti	do {
686117632Sharti		/* next tag to try */
687117632Sharti		NEXT_TAG(last);
688117632Sharti
689117632Sharti		m = scd->on_card[last];
690117632Sharti		KASSERT(m != NULL, ("%stag=%u", dump_scd(sc, scd), tag));
691117632Sharti		scd->on_card[last] = NULL;
692117632Sharti		patm_debug(sc, TX, "ok tag=%x", last);
693117632Sharti
694254804Sandre		map = m->m_pkthdr.PH_loc.ptr;
695117632Sharti		scd->space += m->m_pkthdr.csum_data;
696117632Sharti
697117632Sharti		bus_dmamap_sync(sc->tx_tag, map->map,
698117632Sharti		    BUS_DMASYNC_POSTWRITE);
699117632Sharti		bus_dmamap_unload(sc->tx_tag, map->map);
700117632Sharti		m_freem(m);
701117632Sharti		SLIST_INSERT_HEAD(&sc->tx_maps_free, map, link);
702117632Sharti		scd->num_on_card--;
703117632Sharti
704117632Sharti		if (vcc->vflags & PATM_VCC_TX_CLOSING) {
705117632Sharti			if (scd->num_on_card == 0) {
706117632Sharti				/* done with this VCC */
707117632Sharti				if (vcc->vcc.traffic == ATMIO_TRAFFIC_CBR)
708117632Sharti					patm_tst_free(sc, vcc);
709117632Sharti
710117632Sharti				patm_sram_write4(sc, scd->sram + 0, 0, 0, 0, 0);
711117632Sharti				patm_sram_write4(sc, scd->sram + 4, 0, 0, 0, 0);
712117632Sharti				patm_scd_free(sc, scd);
713117632Sharti
714117632Sharti				vcc->scd = NULL;
715117632Sharti				vcc->vflags &= ~PATM_VCC_TX_CLOSING;
716117632Sharti
717118539Sharti				if (vcc->vcc.flags & ATMIO_FLAG_ASYNC) {
718117632Sharti					patm_tx_vcc_closed(sc, vcc);
719117632Sharti					if (!(vcc->vflags & PATM_VCC_OPEN))
720117632Sharti						patm_vcc_closed(sc, vcc);
721117632Sharti				} else
722117632Sharti					cv_signal(&sc->vcc_cv);
723117632Sharti				return;
724117632Sharti			}
725117632Sharti			patm_debug(sc, VCC, "%u buffers still on card",
726117632Sharti			    scd->num_on_card);
727117632Sharti
728117632Sharti			if (vcc->vcc.traffic == ATMIO_TRAFFIC_ABR) {
729117632Sharti				/* insist on speeding up transmission for ABR */
730117632Sharti				patm_nor_write(sc, IDT_NOR_TCMDQ,
731117632Sharti				    IDT_TCMDQ_UIER(vcc->cid, 0xff));
732117632Sharti				patm_nor_write(sc, IDT_NOR_TCMDQ,
733117632Sharti				    IDT_TCMDQ_ULACR(vcc->cid, 0xff));
734117632Sharti			}
735117632Sharti		}
736117632Sharti
737117632Sharti	} while (last != tag);
738117632Sharti	scd->last_tag = tag;
739117632Sharti
740117632Sharti	if (vcc->vcc.traffic == ATMIO_TRAFFIC_ABR) {
741117632Sharti		u_int acri, cps;
742117632Sharti
743117632Sharti		acri = (patm_sram_read(sc, 8 * cid + 2) >> IDT_TCT_ACRI_SHIFT)
744117632Sharti		    & 0x3fff;
745147256Sbrooks		cps = IFP2IFATM(sc->ifp)->mib.pcr * 32 /
746117632Sharti		    ((1 << (acri >> 10)) * (acri & 0x3ff));
747117632Sharti
748117632Sharti		if (cps != vcc->cps) {
749117632Sharti			patm_debug(sc, VCC, "ACRI=%04x CPS=%u", acri, cps);
750147256Sbrooks			ATMEV_SEND_ACR_CHANGED(IFP2IFATM(sc->ifp), vcc->vcc.vpi,
751118158Sharti			    vcc->vcc.vci, cps);
752117632Sharti			vcc->cps = cps;
753117632Sharti		}
754117632Sharti	}
755117632Sharti
756117632Sharti	patm_launch(sc, scd);
757117632Sharti}
758117632Sharti
759117632Sharti/*
760117632Sharti * VBR/ABR connection went idle
761117632Sharti * Either restart it or set the idle flag.
762117632Sharti */
763117632Shartivoid
764117632Shartipatm_tx_idle(struct patm_softc *sc, u_int cid)
765117632Sharti{
766117632Sharti	struct patm_vcc *vcc;
767117632Sharti
768117632Sharti	patm_debug(sc, VCC, "idle %u", cid);
769117632Sharti
770117632Sharti	if ((vcc = sc->vccs[cid]) != NULL &&
771117632Sharti	    (vcc->vflags & (PATM_VCC_TX_OPEN | PATM_VCC_TX_CLOSING)) != 0 &&
772117632Sharti	    vcc->scd != NULL && (vcc->scd->num_on_card != 0 ||
773117632Sharti	    _IF_QLEN(&vcc->scd->q) != 0)) {
774117632Sharti		/*
775117632Sharti		 * If there is any packet outstanding in the SCD re-activate
776117632Sharti		 * the channel and kick it.
777117632Sharti		 */
778117632Sharti		patm_nor_write(sc, IDT_NOR_TCMDQ,
779117632Sharti		    IDT_TCMDQ_START(vcc->cid));
780117632Sharti
781117632Sharti		patm_launch(sc, vcc->scd);
782117632Sharti	}
783117632Sharti}
784117632Sharti
785117632Sharti/*
786117632Sharti * Convert a (24bit) rate to the atm-forum form
787117632Sharti * Our rate is never larger than 19 bit.
788117632Sharti */
789117632Shartistatic u_int
790117632Sharticps2atmf(u_int cps)
791117632Sharti{
792117632Sharti	u_int e;
793117632Sharti
794117632Sharti	if (cps == 0)
795117632Sharti		return (0);
796117632Sharti	cps <<= 9;
797117632Sharti	e = 0;
798117632Sharti	while (cps > (1024 - 1)) {
799117632Sharti		e++;
800117632Sharti		cps >>= 1;
801117632Sharti	}
802117632Sharti	return ((1 << 14) | (e << 9) | (cps & 0x1ff));
803117632Sharti}
804117632Sharti
805117632Sharti/*
806117632Sharti * Do a binary search on the log2rate table to convert the rate
807117632Sharti * to its log form. This assumes that the ATM-Forum form is monotonically
808117632Sharti * increasing with the plain cell rate.
809117632Sharti */
810117632Shartistatic u_int
811117632Shartirate2log(struct patm_softc *sc, u_int rate)
812117632Sharti{
813117632Sharti	const uint32_t *tbl;
814117632Sharti	u_int lower, upper, mid, done, val, afr;
815117632Sharti
816117632Sharti	afr = cps2atmf(rate);
817117632Sharti
818117632Sharti	if (sc->flags & PATM_25M)
819117632Sharti		tbl = patm_rtables25;
820117632Sharti	else
821117632Sharti		tbl = patm_rtables155;
822117632Sharti
823117632Sharti	lower = 0;
824117632Sharti	upper = 255;
825117632Sharti	done = 0;
826117632Sharti	while (!done) {
827117632Sharti		mid = (lower + upper) / 2;
828117632Sharti		val = tbl[mid] >> 17;
829117632Sharti		if (val == afr || upper == lower)
830117632Sharti			break;
831117632Sharti		if (afr > val)
832117632Sharti			lower = mid + 1;
833117632Sharti		else
834117632Sharti			upper = mid - 1;
835117632Sharti	}
836117632Sharti	if (val > afr && mid > 0)
837117632Sharti		mid--;
838117632Sharti	return (mid);
839117632Sharti}
840117632Sharti
841117632Sharti/*
842117632Sharti * Return the table index for an increase table. The increase table
843117632Sharti * must be selected not by the RIF itself, but by PCR/2^RIF. Each table
844117632Sharti * represents an additive increase of a cell rate that can be computed
845117632Sharti * from the first table entry (the value in this entry will not be clamped
846117632Sharti * by the link rate).
847117632Sharti */
848117632Shartistatic u_int
849117632Shartiget_air_table(struct patm_softc *sc, u_int rif, u_int pcr)
850117632Sharti{
851117632Sharti	const uint32_t *tbl;
852117632Sharti	u_int increase, base, lair0, ret, t, cps;
853117632Sharti
854117632Sharti#define	GET_ENTRY(TAB, IDX) (0xffff & ((IDX & 1) ?			\
855117632Sharti	(tbl[512 + (IDX / 2) + 128 * (TAB)] >> 16) :			\
856117632Sharti	(tbl[512 + (IDX / 2) + 128 * (TAB)])))
857117632Sharti
858117632Sharti#define	MANT_BITS	10
859117632Sharti#define	FRAC_BITS	16
860117632Sharti
861117632Sharti#define	DIFF_TO_FP(D)	(((D) & ((1 << MANT_BITS) - 1)) << ((D) >> MANT_BITS))
862117632Sharti#define	AFR_TO_INT(A)	((1 << (((A) >> 9) & 0x1f)) * \
863117632Sharti			    (512 + ((A) & 0x1ff)) / 512 * ((A) >> 14))
864117632Sharti
865117632Sharti	if (sc->flags & PATM_25M)
866117632Sharti		tbl = patm_rtables25;
867117632Sharti	else
868117632Sharti		tbl = patm_rtables155;
869117632Sharti	if (rif >= patm_rtables_ntab)
870117632Sharti		rif = patm_rtables_ntab - 1;
871117632Sharti	increase = pcr >> rif;
872117632Sharti
873117632Sharti	ret = 0;
874117632Sharti	for (t = 0; t < patm_rtables_ntab; t++) {
875117632Sharti		/* get base rate of this table */
876117632Sharti		base = GET_ENTRY(t, 0);
877117632Sharti		/* convert this to fixed point */
878117632Sharti		lair0 = DIFF_TO_FP(base) >> FRAC_BITS;
879117632Sharti
880117632Sharti		/* get the CPS from the log2rate table */
881117632Sharti		cps = AFR_TO_INT(tbl[lair0] >> 17) - 10;
882117632Sharti
883117632Sharti		if (increase >= cps)
884117632Sharti			break;
885117632Sharti
886117632Sharti		ret = t;
887117632Sharti	}
888117632Sharti	return (ret + 4);
889117632Sharti}
890117632Sharti
891117632Sharti/*
892117632Sharti * Setup the TCT
893117632Sharti */
894117632Shartivoid
895117632Shartipatm_tct_setup(struct patm_softc *sc, struct patm_scd *scd,
896117632Sharti    struct patm_vcc *vcc)
897117632Sharti{
898117632Sharti	uint32_t tct[8];
899117632Sharti	u_int sram;
900117632Sharti	u_int mbs, token;
901117632Sharti	u_int tmp, crm, rdf, cdf, air, mcr;
902117632Sharti
903117632Sharti	bzero(tct, sizeof(tct));
904117632Sharti	if (vcc == NULL) {
905117632Sharti		/* special case for UBR0 */
906117632Sharti		sram = 0;
907117632Sharti		tct[0] = IDT_TCT_UBR | scd->sram;
908117632Sharti		tct[7] = IDT_TCT_UBR_FLG;
909117632Sharti
910117632Sharti	} else {
911117632Sharti		sram = vcc->cid * 8;
912117632Sharti		switch (vcc->vcc.traffic) {
913117632Sharti
914117632Sharti		  case ATMIO_TRAFFIC_CBR:
915117632Sharti			patm_tst_alloc(sc, vcc);
916117632Sharti			tct[0] = IDT_TCT_CBR | scd->sram;
917117632Sharti			/* must account for what was really allocated */
918117632Sharti			break;
919117632Sharti
920117632Sharti		  case ATMIO_TRAFFIC_VBR:
921117632Sharti			/* compute parameters for the TCT */
922117632Sharti			scd->init_er = rate2log(sc, vcc->vcc.tparam.pcr);
923117632Sharti			scd->lacr = rate2log(sc, vcc->vcc.tparam.scr);
924117632Sharti
925117632Sharti			/* get the 16-bit fraction of SCR/PCR
926117632Sharti			 * both a 24 bit. Do it the simple way. */
927117632Sharti			token = (uint64_t)(vcc->vcc.tparam.scr << 16) /
928117632Sharti			    vcc->vcc.tparam.pcr;
929117632Sharti
930117632Sharti			patm_debug(sc, VCC, "VBR: init_er=%u lacr=%u "
931117632Sharti			    "token=0x%04x\n", scd->init_er, scd->lacr, token);
932117632Sharti
933117632Sharti			tct[0] = IDT_TCT_VBR | scd->sram;
934117632Sharti			tct[2] = IDT_TCT_TSIF;
935117632Sharti			tct[3] = IDT_TCT_IDLE | IDT_TCT_HALT;
936117632Sharti			tct[4] = IDT_TCT_MAXIDLE;
937117632Sharti			tct[5] = 0x01000000;
938117632Sharti			if ((mbs = vcc->vcc.tparam.mbs) > 0xff)
939117632Sharti				mbs = 0xff;
940117632Sharti			tct[6] = (mbs << 16) | token;
941117632Sharti			sc->bwrem -= vcc->vcc.tparam.scr;
942117632Sharti			break;
943117632Sharti
944117632Sharti		  case ATMIO_TRAFFIC_ABR:
945117632Sharti			scd->init_er = rate2log(sc, vcc->vcc.tparam.pcr);
946117632Sharti			scd->lacr = rate2log(sc, vcc->vcc.tparam.icr);
947117632Sharti			mcr = rate2log(sc, vcc->vcc.tparam.mcr);
948117632Sharti
949117632Sharti			/* compute CRM */
950117632Sharti			tmp = vcc->vcc.tparam.tbe / vcc->vcc.tparam.nrm;
951117632Sharti			if (tmp * vcc->vcc.tparam.nrm < vcc->vcc.tparam.tbe)
952117632Sharti				tmp++;
953117632Sharti			for (crm = 1; tmp > (1 << crm); crm++)
954117632Sharti				;
955117632Sharti			if (crm > 0x7)
956117632Sharti				crm = 7;
957117632Sharti
958117632Sharti			air = get_air_table(sc, vcc->vcc.tparam.rif,
959117632Sharti			    vcc->vcc.tparam.pcr);
960117632Sharti
961117632Sharti			if ((rdf = vcc->vcc.tparam.rdf) >= patm_rtables_ntab)
962117632Sharti				rdf = patm_rtables_ntab - 1;
963117632Sharti			rdf += patm_rtables_ntab + 4;
964117632Sharti
965117632Sharti			if ((cdf = vcc->vcc.tparam.cdf) >= patm_rtables_ntab)
966117632Sharti				cdf = patm_rtables_ntab - 1;
967117632Sharti			cdf += patm_rtables_ntab + 4;
968117632Sharti
969117632Sharti			patm_debug(sc, VCC, "ABR: init_er=%u lacr=%u mcr=%u "
970117632Sharti			    "crm=%u air=%u rdf=%u cdf=%u\n", scd->init_er,
971117632Sharti			    scd->lacr, mcr, crm, air, rdf, cdf);
972117632Sharti
973117632Sharti			tct[0] = IDT_TCT_ABR | scd->sram;
974117632Sharti			tct[1] = crm << IDT_TCT_CRM_SHIFT;
975117632Sharti			tct[3] = IDT_TCT_HALT | IDT_TCT_IDLE |
976117632Sharti			    (4 << IDT_TCT_NAGE_SHIFT);
977117632Sharti			tct[4] = mcr << IDT_TCT_LMCR_SHIFT;
978117632Sharti			tct[5] = (cdf << IDT_TCT_CDF_SHIFT) |
979117632Sharti			    (rdf << IDT_TCT_RDF_SHIFT) |
980117632Sharti			    (air << IDT_TCT_AIR_SHIFT);
981117632Sharti
982117632Sharti			sc->bwrem -= vcc->vcc.tparam.mcr;
983117632Sharti			break;
984117632Sharti		}
985117632Sharti	}
986117632Sharti
987117632Sharti	patm_sram_write4(sc, sram + 0, tct[0], tct[1], tct[2], tct[3]);
988117632Sharti	patm_sram_write4(sc, sram + 4, tct[4], tct[5], tct[6], tct[7]);
989117632Sharti
990117632Sharti	patm_debug(sc, VCC, "TCT[%u]: %08x %08x %08x %08x  %08x %08x %08x %08x",
991117632Sharti	    sram / 8, patm_sram_read(sc, sram + 0),
992117632Sharti	    patm_sram_read(sc, sram + 1), patm_sram_read(sc, sram + 2),
993117632Sharti	    patm_sram_read(sc, sram + 3), patm_sram_read(sc, sram + 4),
994117632Sharti	    patm_sram_read(sc, sram + 5), patm_sram_read(sc, sram + 6),
995117632Sharti	    patm_sram_read(sc, sram + 7));
996117632Sharti}
997117632Sharti
998117632Sharti/*
999117632Sharti * Start a channel
1000117632Sharti */
1001117632Shartistatic void
1002117632Shartipatm_tct_start(struct patm_softc *sc, struct patm_vcc *vcc)
1003117632Sharti{
1004117632Sharti
1005117632Sharti	patm_nor_write(sc, IDT_NOR_TCMDQ, IDT_TCMDQ_UIER(vcc->cid,
1006117632Sharti	    vcc->scd->init_er));
1007117632Sharti	patm_nor_write(sc, IDT_NOR_TCMDQ, IDT_TCMDQ_SLACR(vcc->cid,
1008117632Sharti	    vcc->scd->lacr));
1009117632Sharti}
1010117632Sharti
1011117632Shartistatic void
1012117632Shartipatm_tct_print(struct patm_softc *sc, u_int cid)
1013117632Sharti{
1014117632Sharti#ifdef PATM_DEBUG
1015117632Sharti	u_int sram = cid * 8;
1016117632Sharti#endif
1017117632Sharti
1018117632Sharti	patm_debug(sc, VCC, "TCT[%u]: %08x %08x %08x %08x  %08x %08x %08x %08x",
1019117632Sharti	    sram / 8, patm_sram_read(sc, sram + 0),
1020117632Sharti	    patm_sram_read(sc, sram + 1), patm_sram_read(sc, sram + 2),
1021117632Sharti	    patm_sram_read(sc, sram + 3), patm_sram_read(sc, sram + 4),
1022117632Sharti	    patm_sram_read(sc, sram + 5), patm_sram_read(sc, sram + 6),
1023117632Sharti	    patm_sram_read(sc, sram + 7));
1024117632Sharti}
1025117632Sharti
1026117632Sharti/*
1027117632Sharti * Setup the SCD
1028117632Sharti */
1029117632Shartivoid
1030117632Shartipatm_scd_setup(struct patm_softc *sc, struct patm_scd *scd)
1031117632Sharti{
1032117632Sharti	patm_sram_write4(sc, scd->sram + 0,
1033117632Sharti	    scd->phy, 0, 0xffffffff, 0);
1034117632Sharti	patm_sram_write4(sc, scd->sram + 4,
1035117632Sharti	    0, 0, 0, 0);
1036117632Sharti
1037117632Sharti	patm_debug(sc, VCC, "SCD(%x): %08x %08x %08x %08x %08x %08x %08x %08x",
1038117632Sharti	    scd->sram,
1039117632Sharti	    patm_sram_read(sc, scd->sram + 0),
1040117632Sharti	    patm_sram_read(sc, scd->sram + 1),
1041117632Sharti	    patm_sram_read(sc, scd->sram + 2),
1042117632Sharti	    patm_sram_read(sc, scd->sram + 3),
1043117632Sharti	    patm_sram_read(sc, scd->sram + 4),
1044117632Sharti	    patm_sram_read(sc, scd->sram + 5),
1045117632Sharti	    patm_sram_read(sc, scd->sram + 6),
1046117632Sharti	    patm_sram_read(sc, scd->sram + 7));
1047117632Sharti}
1048117632Sharti
1049117632Sharti/*
1050117632Sharti * Grow the TX map table if possible
1051117632Sharti */
1052117632Shartistatic void
1053117632Shartipatm_txmaps_grow(struct patm_softc *sc)
1054117632Sharti{
1055117632Sharti	u_int i;
1056117632Sharti	struct patm_txmap *map;
1057117632Sharti	int err;
1058117632Sharti
1059117632Sharti	if (sc->tx_nmaps >= sc->tx_maxmaps)
1060117632Sharti		return;
1061117632Sharti
1062117632Sharti	for (i = sc->tx_nmaps; i < sc->tx_nmaps + PATM_CFG_TXMAPS_STEP; i++) {
1063117632Sharti		map = uma_zalloc(sc->tx_mapzone, M_NOWAIT);
1064117632Sharti		err = bus_dmamap_create(sc->tx_tag, 0, &map->map);
1065117632Sharti		if (err) {
1066117632Sharti			uma_zfree(sc->tx_mapzone, map);
1067117632Sharti			break;
1068117632Sharti		}
1069117632Sharti		SLIST_INSERT_HEAD(&sc->tx_maps_free, map, link);
1070117632Sharti	}
1071117632Sharti
1072117632Sharti	sc->tx_nmaps = i;
1073117632Sharti}
1074117632Sharti
1075117632Sharti/*
1076117632Sharti * Allocate a transmission map
1077117632Sharti */
1078117632Shartistatic struct patm_txmap *
1079117632Shartipatm_txmap_get(struct patm_softc *sc)
1080117632Sharti{
1081117632Sharti	struct patm_txmap *map;
1082117632Sharti
1083117632Sharti	if ((map = SLIST_FIRST(&sc->tx_maps_free)) == NULL) {
1084117632Sharti		patm_txmaps_grow(sc);
1085117632Sharti		if ((map = SLIST_FIRST(&sc->tx_maps_free)) == NULL)
1086117632Sharti			return (NULL);
1087117632Sharti	}
1088117632Sharti	SLIST_REMOVE_HEAD(&sc->tx_maps_free, link);
1089117632Sharti	return (map);
1090117632Sharti}
1091117632Sharti
1092117632Sharti/*
1093117632Sharti * Look whether we are in the process of updating the TST on the chip.
1094117632Sharti * If we are set the flag that we need another update.
1095117632Sharti * If we are not start the update.
1096117632Sharti */
1097117632Shartistatic __inline void
1098117632Shartipatm_tst_start(struct patm_softc *sc)
1099117632Sharti{
1100117632Sharti
1101117632Sharti	if (!(sc->tst_state & TST_PENDING)) {
1102117632Sharti		sc->tst_state |= TST_PENDING;
1103117632Sharti		if (!(sc->tst_state & TST_WAIT)) {
1104117632Sharti			/* timer not running */
1105117632Sharti			patm_tst_update(sc);
1106117632Sharti		}
1107117632Sharti	}
1108117632Sharti}
1109117632Sharti
1110117632Sharti/*
1111117632Sharti * Allocate TST entries to a CBR connection
1112117632Sharti */
1113117632Shartistatic void
1114117632Shartipatm_tst_alloc(struct patm_softc *sc, struct patm_vcc *vcc)
1115117632Sharti{
1116117632Sharti	u_int slots;
1117117632Sharti	u_int qptr, pptr;
1118117632Sharti	u_int qmax, pmax;
1119117632Sharti	u_int pspc, last;
1120117632Sharti
1121117632Sharti	mtx_lock(&sc->tst_lock);
1122117632Sharti
1123117632Sharti	/* compute the number of slots we need, make sure to get at least
1124117632Sharti	 * the specified PCR */
1125117632Sharti	slots = cbr2slots(sc, vcc);
1126117632Sharti	vcc->scd->slots = slots;
1127117632Sharti	sc->bwrem -= slots2cr(sc, slots);
1128117632Sharti
1129117632Sharti	patm_debug(sc, TST, "tst_alloc: cbr=%u link=%u tst=%u slots=%u",
1130147256Sbrooks	    vcc->vcc.tparam.pcr, IFP2IFATM(sc->ifp)->mib.pcr, sc->mmap->tst_size, slots);
1131117632Sharti
1132117632Sharti	qmax = sc->mmap->tst_size - 1;
1133117632Sharti	pmax = qmax << 8;
1134117632Sharti
1135117632Sharti	pspc = pmax / slots;
1136117632Sharti
1137117632Sharti	pptr = pspc >> 1;	/* starting point */
1138117632Sharti	qptr = pptr >> 8;
1139117632Sharti
1140117632Sharti	last = qptr;
1141117632Sharti
1142117632Sharti	while (slots > 0) {
1143117632Sharti		if (qptr >= qmax)
1144117632Sharti			qptr -= qmax;
1145117632Sharti		if (sc->tst_soft[qptr] != IDT_TST_VBR) {
1146117632Sharti			/* used - try next */
1147117632Sharti			qptr++;
1148117632Sharti			continue;
1149117632Sharti		}
1150117632Sharti		patm_debug(sc, TST, "slot[%u] = %u.%u diff=%d", qptr,
1151117632Sharti		    vcc->vcc.vpi, vcc->vcc.vci, (int)qptr - (int)last);
1152117632Sharti		last = qptr;
1153117632Sharti
1154117632Sharti		sc->tst_soft[qptr] = IDT_TST_CBR | vcc->cid | TST_BOTH;
1155117632Sharti		sc->tst_free--;
1156117632Sharti
1157117632Sharti		if ((pptr += pspc) >= pmax)
1158117632Sharti			pptr -= pmax;
1159117632Sharti		qptr = pptr >> 8;
1160117632Sharti
1161117632Sharti		slots--;
1162117632Sharti	}
1163117632Sharti	patm_tst_start(sc);
1164117632Sharti	mtx_unlock(&sc->tst_lock);
1165117632Sharti}
1166117632Sharti
1167117632Sharti/*
1168117632Sharti * Free a CBR connection's TST entries
1169117632Sharti */
1170117632Shartistatic void
1171117632Shartipatm_tst_free(struct patm_softc *sc, struct patm_vcc *vcc)
1172117632Sharti{
1173117632Sharti	u_int i;
1174117632Sharti
1175117632Sharti	mtx_lock(&sc->tst_lock);
1176117632Sharti	for (i = 0; i < sc->mmap->tst_size - 1; i++) {
1177117632Sharti		if ((sc->tst_soft[i] & IDT_TST_MASK) == vcc->cid) {
1178117632Sharti			sc->tst_soft[i] = IDT_TST_VBR | TST_BOTH;
1179117632Sharti			sc->tst_free++;
1180117632Sharti		}
1181117632Sharti	}
1182117632Sharti	sc->bwrem += slots2cr(sc, vcc->scd->slots);
1183117632Sharti	patm_tst_start(sc);
1184117632Sharti	mtx_unlock(&sc->tst_lock);
1185117632Sharti}
1186117632Sharti
1187117632Sharti/*
1188117632Sharti * Write the soft TST into the idle incore TST and start the wait timer.
1189117632Sharti * We assume that we hold the tst lock.
1190117632Sharti */
1191117632Shartistatic void
1192117632Shartipatm_tst_update(struct patm_softc *sc)
1193117632Sharti{
1194117632Sharti	u_int flag;		/* flag to clear from soft TST */
1195117632Sharti	u_int idle;		/* the idle TST */
1196117632Sharti	u_int act;		/* the active TST */
1197117632Sharti	u_int i;
1198117632Sharti
1199117632Sharti	if (sc->tst_state & TST_ACT1) {
1200117632Sharti		act = 1;
1201117632Sharti		idle = 0;
1202117632Sharti		flag = TST_CH0;
1203117632Sharti	} else {
1204117632Sharti		act = 0;
1205117632Sharti		idle = 1;
1206117632Sharti		flag = TST_CH1;
1207117632Sharti	}
1208117632Sharti	/* update the idle one */
1209117632Sharti	for (i = 0; i < sc->mmap->tst_size - 1; i++)
1210117632Sharti		if (sc->tst_soft[i] & flag) {
1211117632Sharti			patm_sram_write(sc, sc->tst_base[idle] + i,
1212117632Sharti			    sc->tst_soft[i] & ~TST_BOTH);
1213117632Sharti			sc->tst_soft[i] &= ~flag;
1214117632Sharti		}
1215117632Sharti	/* the used one jump to the idle one */
1216117632Sharti	patm_sram_write(sc, sc->tst_jump[act],
1217117632Sharti	    IDT_TST_BR | (sc->tst_base[idle] << 2));
1218117632Sharti
1219117632Sharti	/* wait for the chip to jump */
1220117632Sharti	sc->tst_state &= ~TST_PENDING;
1221117632Sharti	sc->tst_state |= TST_WAIT;
1222117632Sharti
1223117632Sharti	callout_reset(&sc->tst_callout, 1, patm_tst_timer, sc);
1224117632Sharti}
1225117632Sharti
1226117632Sharti/*
1227117632Sharti * Timer for TST updates
1228117632Sharti */
1229117632Shartistatic void
1230117632Shartipatm_tst_timer(void *p)
1231117632Sharti{
1232117632Sharti	struct patm_softc *sc = p;
1233117632Sharti	u_int act;	/* active TST */
1234117632Sharti	u_int now;	/* current place in TST */
1235117632Sharti
1236117632Sharti	mtx_lock(&sc->tst_lock);
1237117632Sharti
1238117632Sharti	if (sc->tst_state & TST_WAIT) {
1239117632Sharti		/* ignore the PENDING state while we are waiting for
1240117632Sharti		 * the chip to switch tables. Once the switch is done,
1241117632Sharti		 * we will again lock at PENDING */
1242117632Sharti		act = (sc->tst_state & TST_ACT1) ? 1 : 0;
1243117632Sharti		now = patm_nor_read(sc, IDT_NOR_NOW) >> 2;
1244117632Sharti		if (now >= sc->tst_base[act] && now <= sc->tst_jump[act]) {
1245117632Sharti			/* not yet */
1246117632Sharti			callout_reset(&sc->tst_callout, 1, patm_tst_timer, sc);
1247117632Sharti			goto done;
1248117632Sharti		}
1249117632Sharti		sc->tst_state &= ~TST_WAIT;
1250117632Sharti		/* change back jump */
1251117632Sharti		patm_sram_write(sc, sc->tst_jump[act],
1252117632Sharti		    IDT_TST_BR | (sc->tst_base[act] << 2));
1253117632Sharti
1254117632Sharti		/* switch */
1255117632Sharti		sc->tst_state ^= TST_ACT1;
1256117632Sharti	}
1257117632Sharti
1258117632Sharti	if (sc->tst_state & TST_PENDING)
1259117632Sharti		/* we got another update request while the timer was running. */
1260117632Sharti		patm_tst_update(sc);
1261117632Sharti
1262117632Sharti  done:
1263117632Sharti	mtx_unlock(&sc->tst_lock);
1264117632Sharti}
1265117632Sharti
1266117632Shartistatic const char *
1267117632Shartidump_scd(struct patm_softc *sc, struct patm_scd *scd)
1268117632Sharti{
1269117632Sharti	u_int i;
1270117632Sharti
1271117632Sharti	for (i = 0; i < IDT_TSQE_TAG_SPACE; i++)
1272117632Sharti		printf("on_card[%u] = %p\n", i, scd->on_card[i]);
1273117632Sharti	printf("space=%u tag=%u num_on_card=%u last_tag=%u\n",
1274117632Sharti	    scd->space, scd->tag, scd->num_on_card, scd->last_tag);
1275117632Sharti
1276117632Sharti	return ("");
1277117632Sharti}
1278