if_patm_rx.c revision 117632
1/*
2 * Copyright (c) 2003
3 *	Fraunhofer Institute for Open Communication Systems (FhG Fokus).
4 * 	All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 * Author: Hartmut Brandt <harti@freebsd.org>
28 *
29 * Driver for IDT77252 based cards like ProSum's.
30 */
31#include <sys/cdefs.h>
32__FBSDID("$FreeBSD: head/sys/dev/patm/if_patm_rx.c 117632 2003-07-15 11:57:24Z harti $");
33
34#include "opt_inet.h"
35#include "opt_natm.h"
36
37#include <sys/types.h>
38#include <sys/param.h>
39#include <sys/systm.h>
40#include <sys/malloc.h>
41#include <sys/kernel.h>
42#include <sys/bus.h>
43#include <sys/errno.h>
44#include <sys/conf.h>
45#include <sys/module.h>
46#include <sys/lock.h>
47#include <sys/mutex.h>
48#include <sys/sysctl.h>
49#include <sys/queue.h>
50#include <sys/condvar.h>
51#include <sys/endian.h>
52#include <vm/uma.h>
53
54#include <sys/sockio.h>
55#include <sys/mbuf.h>
56#include <sys/socket.h>
57
58#include <net/if.h>
59#include <net/if_media.h>
60#include <net/if_atm.h>
61#include <net/route.h>
62#ifdef ENABLE_BPF
63#include <net/bpf.h>
64#endif
65#include <netinet/in.h>
66#include <netinet/if_atm.h>
67
68#include <machine/bus.h>
69#include <machine/resource.h>
70#include <sys/bus.h>
71#include <sys/rman.h>
72#include <sys/mbpool.h>
73
74#include <dev/utopia/utopia.h>
75#include <dev/patm/idt77252reg.h>
76#include <dev/patm/if_patmvar.h>
77
78static void *patm_rcv_handle(struct patm_softc *sc, u_int handle);
79static void patm_rcv_free(struct patm_softc *, void *, u_int handle);
80static struct mbuf *patm_rcv_mbuf(struct patm_softc *, void *, u_int, int);
81
82static __inline void
83rct_write(struct patm_softc *sc, u_int cid, u_int w, u_int val)
84{
85	patm_sram_write(sc, sc->mmap->rct + cid * IDT_RCT_ENTRY_SIZE + w, val);
86}
87static __inline void
88rct_init(struct patm_softc *sc, u_int cid, u_int w1)
89{
90	patm_sram_write4(sc, sc->mmap->rct + cid * IDT_RCT_ENTRY_SIZE,
91	    w1, 0, 0, 0xffffffff);
92}
93static __inline u_int
94rct_read(struct patm_softc *sc, u_int cid, u_int w)
95{
96	return (patm_sram_read(sc, sc->mmap->rct +
97	    cid * IDT_RCT_ENTRY_SIZE + w));
98}
99
100/* check if we can open this one */
101int
102patm_rx_vcc_can_open(struct patm_softc *sc, struct patm_vcc *vcc)
103{
104	return (0);
105}
106
107/*
108 * open the VCC
109 */
110void
111patm_rx_vcc_open(struct patm_softc *sc, struct patm_vcc *vcc)
112{
113	uint32_t w1 = IDT_RCT_OPEN;
114
115	patm_debug(sc, VCC, "%u.%u RX opening", vcc->vcc.vpi, vcc->vcc.vci);
116
117	switch (vcc->vcc.aal) {
118	  case ATMIO_AAL_0:
119		w1 |= IDT_RCT_AAL0 | IDT_RCT_FBP2 | IDT_RCT_RCI;
120		break;
121	  case ATMIO_AAL_34:
122		w1 |= IDT_RCT_AAL34;
123		break;
124	  case ATMIO_AAL_5:
125		w1 |= IDT_RCT_AAL5;
126		break;
127	  case ATMIO_AAL_RAW:
128		w1 |= IDT_RCT_AALRAW | IDT_RCT_RCI;
129		break;
130	}
131
132	if (vcc->cid != 0)
133		rct_init(sc, vcc->cid, w1);
134	else {
135		/* switch the interface into promiscuous mode */
136		patm_nor_write(sc, IDT_NOR_CFG, patm_nor_read(sc, IDT_NOR_CFG) |
137		    IDT_CFG_ICAPT | IDT_CFG_VPECA);
138	}
139
140	vcc->vflags |= PATM_VCC_RX_OPEN;
141}
142
143/* close the given vcc for transmission */
144void
145patm_rx_vcc_close(struct patm_softc *sc, struct patm_vcc *vcc)
146{
147	u_int w1;
148
149	patm_debug(sc, VCC, "%u.%u RX closing", vcc->vcc.vpi, vcc->vcc.vci);
150
151	if (vcc->cid == 0) {
152		/* switch off promiscuous mode */
153		patm_nor_write(sc, IDT_NOR_CFG, patm_nor_read(sc, IDT_NOR_CFG) &
154		    ~(IDT_CFG_ICAPT | IDT_CFG_VPECA));
155		vcc->vflags &= ~PATM_VCC_RX_OPEN;
156		return;
157	}
158
159	/* close the connection but keep state */
160	w1 = rct_read(sc, vcc->cid, 0);
161	w1 &= ~IDT_RCT_OPEN;
162	rct_write(sc, vcc->cid, 0, w1);
163
164	/* minimum idle count */
165	w1 = (w1 & ~IDT_RCT_IACT_CNT_MASK) | (1 << IDT_RCT_IACT_CNT_SHIFT);
166	rct_write(sc, vcc->cid, 0, w1);
167
168	/* initialize scan */
169	patm_nor_write(sc, IDT_NOR_IRCP, vcc->cid);
170
171	vcc->vflags &= ~PATM_VCC_RX_OPEN;
172	vcc->vflags |= PATM_VCC_RX_CLOSING;
173
174	/*
175	 * check the RSQ
176	 * This is a hack. The problem is, that although an entry is written
177	 * to the RSQ, no interrupt is generated. Also we must wait 1 cell
178	 * time for the SAR to process the scan of our connection.
179	 */
180	DELAY(1);
181	patm_intr_rsq(sc);
182}
183
184/* transmission side finally closed */
185void
186patm_rx_vcc_closed(struct patm_softc *sc, struct patm_vcc *vcc)
187{
188	patm_debug(sc, VCC, "%u.%u RX finally closed",
189	    vcc->vcc.vpi, vcc->vcc.vci);
190}
191
192/*
193 * Handle the given receive status queue entry
194 */
195void
196patm_rx(struct patm_softc *sc, struct idt_rsqe *rsqe)
197{
198	struct mbuf *m;
199	void *buf;
200	u_int stat, cid, w, cells, len, h;
201	struct patm_vcc *vcc;
202	struct atm_pseudohdr aph;
203	u_char *trail;
204
205	cid = le32toh(rsqe->cid);
206	stat = le32toh(rsqe->stat);
207	h = le32toh(rsqe->handle);
208
209	cid = PATM_CID(sc, IDT_RSQE_VPI(cid), IDT_RSQE_VCI(cid));
210	vcc = sc->vccs[cid];
211
212	if (IDT_RSQE_TYPE(stat) == IDT_RSQE_IDLE) {
213		/* connection has gone idle */
214		if (stat & IDT_RSQE_BUF)
215			patm_rcv_free(sc, patm_rcv_handle(sc, h), h);
216
217		w = rct_read(sc, cid, 0);
218		if (w != 0 && !(w & IDT_RCT_OPEN))
219			rct_write(sc, cid, 0, 0);
220		if (vcc != NULL && (vcc->vflags & PATM_VCC_RX_CLOSING)) {
221			patm_debug(sc, VCC, "%u.%u RX closed", vcc->vcc.vpi,
222			    vcc->vcc.vci);
223			vcc->vflags &= ~PATM_VCC_RX_CLOSING;
224			if (vcc->vflags & PATM_VCC_ASYNC) {
225				patm_rx_vcc_closed(sc, vcc);
226				if (!(vcc->vflags & PATM_VCC_OPEN))
227					patm_vcc_closed(sc, vcc);
228			} else
229				cv_signal(&sc->vcc_cv);
230		}
231		return;
232	}
233
234	buf = patm_rcv_handle(sc, h);
235
236	if (vcc == NULL || (vcc->vflags & PATM_VCC_RX_OPEN) == 0) {
237		patm_rcv_free(sc, buf, h);
238		return;
239	}
240
241	cells = IDT_RSQE_CNT(stat);
242	KASSERT(cells > 0, ("zero cell count"));
243
244	if (vcc->vcc.aal == ATMIO_AAL_0) {
245		/* deliver this packet as it is */
246		if ((m = patm_rcv_mbuf(sc, buf, h, 1)) == NULL)
247			return;
248
249		m->m_len = cells * 48;
250		m->m_pkthdr.len = m->m_len;
251		m->m_pkthdr.rcvif = &sc->ifatm.ifnet;
252
253	} else if (vcc->vcc.aal == ATMIO_AAL_34) {
254		/* XXX AAL3/4 */
255		patm_rcv_free(sc, buf, h);
256		return;
257
258	} else if (vcc->vcc.aal == ATMIO_AAL_5) {
259		if (stat & IDT_RSQE_CRC) {
260			sc->ifatm.ifnet.if_ierrors++;
261			if (vcc->chain != NULL) {
262				m_freem(vcc->chain);
263				vcc->chain = vcc->last = NULL;
264			}
265			return;
266		}
267
268		/* append to current chain */
269		if (vcc->chain == NULL) {
270			if ((m = patm_rcv_mbuf(sc, buf, h, 1)) == NULL)
271				return;
272			m->m_len = cells * 48;
273			m->m_pkthdr.len = m->m_len;
274			m->m_pkthdr.rcvif = &sc->ifatm.ifnet;
275			vcc->chain = vcc->last = m;
276		} else {
277			if ((m = patm_rcv_mbuf(sc, buf, h, 0)) == NULL)
278				return;
279			m->m_len = cells * 48;
280			vcc->last->m_next = m;
281			vcc->last = m;
282			vcc->chain->m_pkthdr.len += m->m_len;
283		}
284
285		if (!(stat & IDT_RSQE_EPDU))
286			return;
287
288		trail = mtod(m, u_char *) + m->m_len - 6;
289		len = (trail[0] << 8) + trail[1];
290
291		if ((u_int)vcc->chain->m_pkthdr.len < len + 8) {
292			patm_printf(sc, "%s: bad aal5 lengths %u %u\n",
293			    __func__, (u_int)m->m_pkthdr.len, len);
294			m_freem(vcc->chain);
295			vcc->chain = vcc->last = NULL;
296			return;
297		}
298		m->m_len -= vcc->chain->m_pkthdr.len - len;
299		KASSERT(m->m_len >= 0, ("bad last mbuf"));
300
301		m = vcc->chain;
302		vcc->chain = vcc->last = NULL;
303		m->m_pkthdr.len = len;
304	} else
305		panic("bad aal");
306
307#if 0
308	{
309		u_int i;
310
311		for (i = 0; i < m->m_len; i++) {
312			printf("%02x ", mtod(m, u_char *)[i]);
313		}
314		printf("\n");
315	}
316#endif
317
318	sc->ifatm.ifnet.if_ipackets++;
319	/* this is in if_atmsubr.c */
320	/* sc->ifatm.ifnet.if_ibytes += m->m_pkthdr.len; */
321
322	vcc->ibytes += m->m_pkthdr.len;
323	vcc->ipackets++;
324
325	ATM_PH_FLAGS(&aph) = vcc->vcc.flags & 0xff;
326	ATM_PH_VPI(&aph) = IDT_RSQE_VPI(cid);
327	ATM_PH_SETVCI(&aph, IDT_RSQE_VCI(cid));
328
329#ifdef ENABLE_BPF
330	if (!(vcc->vcc.flags & ATMIO_FLAG_NG) &&
331	    (vcc->vcc.flags & ATM_PH_AAL5) &&
332	    (vcc->vcc.flags & ATM_PH_LLCSNAP))
333		BPF_MTAP(&sc->ifatm.ifnet, m);
334#endif
335
336	atm_input(&sc->ifatm.ifnet, &aph, m, vcc->rxhand);
337}
338
339/*
340 * Get the buffer for a receive handle. This is either an mbuf for
341 * a large handle or a pool buffer for the others.
342 */
343static void *
344patm_rcv_handle(struct patm_softc *sc, u_int handle)
345{
346	void *buf;
347	u_int c;
348
349	if ((handle & ~MBUF_HMASK) == LMBUF_HANDLE) {
350		struct lmbuf *b;
351
352		c = handle & MBUF_HMASK;
353		b = &sc->lbufs[c];
354
355		buf = b->m;
356		b->m = NULL;
357
358		bus_dmamap_sync(sc->lbuf_tag, b->map, BUS_DMASYNC_POSTREAD);
359		patm_lbuf_free(sc, b);
360
361	} else if ((handle & ~MBUF_HMASK) == MBUF_VHANDLE) {
362		mbp_sync(sc->vbuf_pool, handle,
363		    0, VMBUF_SIZE, BUS_DMASYNC_POSTREAD);
364		buf = mbp_get(sc->vbuf_pool, handle);
365
366	} else {
367		mbp_sync(sc->sbuf_pool, handle,
368		    0, SMBUF_SIZE, BUS_DMASYNC_POSTREAD);
369		buf = mbp_get(sc->sbuf_pool, handle);
370	}
371
372	return (buf);
373}
374
375/*
376 * Free a buffer.
377 */
378static void
379patm_rcv_free(struct patm_softc *sc, void *p, u_int handle)
380{
381	if ((handle & ~MBUF_HMASK) == LMBUF_HANDLE)
382		m_free((struct mbuf *)p);
383
384	else if ((handle & ~MBUF_HMASK) == MBUF_VHANDLE)
385		mbp_free(sc->vbuf_pool, p);
386
387	else
388		mbp_free(sc->sbuf_pool, p);
389}
390
391/*
392 * Make an mbuf around the buffer
393 */
394static struct mbuf *
395patm_rcv_mbuf(struct patm_softc *sc, void *buf, u_int h, int hdr)
396{
397	struct mbuf *m;
398
399	if ((h & ~MBUF_HMASK) == MBUF_LHANDLE)
400		return ((struct mbuf *)buf);
401
402	if (hdr)
403		MGETHDR(m, M_DONTWAIT, MT_DATA);
404	else
405		MGET(m, M_DONTWAIT, MT_DATA);
406	if (m == NULL) {
407		patm_rcv_free(sc, buf, h);
408		return (NULL);
409	}
410
411	if ((h & ~MBUF_HMASK) == MBUF_VHANDLE) {
412		m_extadd(m, (caddr_t)buf, VMBUF_SIZE, mbp_ext_free,
413		    sc->vbuf_pool, M_PKTHDR, EXT_NET_DRV);
414		m->m_data += VMBUF_OFFSET;
415	} else {
416		m_extadd(m, (caddr_t)buf, SMBUF_SIZE, mbp_ext_free,
417		    sc->sbuf_pool, M_PKTHDR, EXT_NET_DRV);
418		m->m_data += SMBUF_OFFSET;
419	}
420
421	if (!(m->m_flags & M_EXT)) {
422		patm_rcv_free(sc, buf, h);
423		m_free(m);
424		return (NULL);
425	}
426	return (m);
427}
428
429/*
430 * Process the raw cell at the given address.
431 */
432void
433patm_rx_raw(struct patm_softc *sc, u_char *cell)
434{
435	u_int vpi, vci, cid;
436	struct patm_vcc *vcc;
437	struct mbuf *m;
438	u_char *dst;
439	struct timespec ts;
440	struct atm_pseudohdr aph;
441	uint64_t cts;
442
443	sc->stats.raw_cells++;
444
445	/*
446	 * For some non-appearant reason the cell header
447	 * is in the wrong endian.
448	 */
449	*(uint32_t *)cell = bswap32(*(uint32_t *)cell);
450
451	vpi = ((cell[0] & 0xf) << 4) | ((cell[1] & 0xf0) >> 4);
452	vci = ((cell[1] & 0xf) << 12) | (cell[2] << 4) | ((cell[3] & 0xf0) >> 4);
453	cid = PATM_CID(sc, vpi, vci);
454
455	vcc = sc->vccs[cid];
456	if (vcc == NULL || !(vcc->vflags & PATM_VCC_RX_OPEN) ||
457	    vcc->vcc.aal != ATMIO_AAL_RAW) {
458		vcc = sc->vccs[0];
459		if (vcc == NULL || !(vcc->vflags & PATM_VCC_RX_OPEN)) {
460			sc->stats.raw_no_vcc++;
461			return;
462		}
463	}
464
465	MGETHDR(m, M_DONTWAIT, MT_DATA);
466	if (m == NULL) {
467		sc->stats.raw_no_buf++;
468		return;
469	}
470	m->m_pkthdr.rcvif = &sc->ifatm.ifnet;
471
472	switch (vcc->vflags & PATM_RAW_FORMAT) {
473
474	  default:
475	  case PATM_RAW_CELL:
476		m->m_len = m->m_pkthdr.len = 53;
477		MH_ALIGN(m, 53);
478		dst = mtod(m, u_char *);
479		*dst++ = *cell++;
480		*dst++ = *cell++;
481		*dst++ = *cell++;
482		*dst++ = *cell++;
483		*dst++ = 0;		/* HEC */
484		bcopy(cell + 12, dst, 48);
485		break;
486
487	  case PATM_RAW_NOHEC:
488		m->m_len = m->m_pkthdr.len = 52;
489		MH_ALIGN(m, 52);
490		dst = mtod(m, u_char *);
491		*dst++ = *cell++;
492		*dst++ = *cell++;
493		*dst++ = *cell++;
494		*dst++ = *cell++;
495		bcopy(cell + 12, dst, 48);
496		break;
497
498	  case PATM_RAW_CS:
499		m->m_len = m->m_pkthdr.len = 64;
500		MH_ALIGN(m, 64);
501		dst = mtod(m, u_char *);
502		*dst++ = *cell++;
503		*dst++ = *cell++;
504		*dst++ = *cell++;
505		*dst++ = *cell++;
506		*dst++ = 0;		/* HEC */
507		*dst++ = 0;		/* flags */
508		*dst++ = 0;		/* reserved */
509		*dst++ = 0;		/* reserved */
510		nanotime(&ts);
511		cts = ts.tv_sec * 1000000000ULL + ts.tv_nsec;
512		bcopy(dst, &cts, 8);
513		bcopy(cell + 12, dst + 8, 48);
514		break;
515	}
516
517	sc->ifatm.ifnet.if_ipackets++;
518	/* this is in if_atmsubr.c */
519	/* sc->ifatm.ifnet.if_ibytes += m->m_pkthdr.len; */
520
521	vcc->ibytes += m->m_pkthdr.len;
522	vcc->ipackets++;
523
524	ATM_PH_FLAGS(&aph) = vcc->vcc.flags & 0xff;
525	ATM_PH_VPI(&aph) = vcc->vcc.vpi;
526	ATM_PH_SETVCI(&aph, vcc->vcc.vci);
527
528	atm_input(&sc->ifatm.ifnet, &aph, m, vcc->rxhand);
529}
530