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