xref: /NextBSD/sys/dev/patm/if_patm_rx.c (revision 287e3b14e9552995def1802ec9c5034f4adf28ec)
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 
80 static void *patm_rcv_handle(struct patm_softc *sc, u_int handle);
81 static void patm_rcv_free(struct patm_softc *, void *, u_int handle);
82 static struct mbuf *patm_rcv_mbuf(struct patm_softc *, void *, u_int, int);
83 
84 static __inline void
rct_write(struct patm_softc * sc,u_int cid,u_int w,u_int val)85 rct_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 }
89 static __inline u_int
rct_read(struct patm_softc * sc,u_int cid,u_int w)90 rct_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 */
97 int
patm_rx_vcc_can_open(struct patm_softc * sc,struct patm_vcc * vcc)98 patm_rx_vcc_can_open(struct patm_softc *sc, struct patm_vcc *vcc)
99 {
100 	return (0);
101 }
102 
103 /*
104  * open the VCC
105  */
106 void
patm_rx_vcc_open(struct patm_softc * sc,struct patm_vcc * vcc)107 patm_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 */
141 void
patm_rx_vcc_close(struct patm_softc * sc,struct patm_vcc * vcc)142 patm_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 */
182 void
patm_rx_vcc_closed(struct patm_softc * sc,struct patm_vcc * vcc)183 patm_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  */
192 void
patm_rx(struct patm_softc * sc,struct idt_rsqe * rsqe)193 patm_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  */
340 static void *
patm_rcv_handle(struct patm_softc * sc,u_int handle)341 patm_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  */
375 static void
patm_rcv_free(struct patm_softc * sc,void * p,u_int handle)376 patm_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  */
391 static struct mbuf *
patm_rcv_mbuf(struct patm_softc * sc,void * buf,u_int h,int hdr)392 patm_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  */
429 void
patm_rx_raw(struct patm_softc * sc,u_char * cell)430 patm_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