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