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