if_patm_rx.c revision 175872
1139735Simp/*-
2244471Scognet * Copyright (c) 2003
3129198Scognet *	Fraunhofer Institute for Open Communication Systems (FhG Fokus).
4129198Scognet * 	All rights reserved.
5129198Scognet *
6129198Scognet * Redistribution and use in source and binary forms, with or without
7129198Scognet * modification, are permitted provided that the following conditions
8129198Scognet * are met:
9129198Scognet * 1. Redistributions of source code must retain the above copyright
10129198Scognet *    notice, this list of conditions and the following disclaimer.
11129198Scognet * 2. Redistributions in binary form must reproduce the above copyright
12129198Scognet *    notice, this list of conditions and the following disclaimer in the
13129198Scognet *    documentation and/or other materials provided with the distribution.
14129198Scognet *
15129198Scognet * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16129198Scognet * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17129198Scognet * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18129198Scognet * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19129198Scognet * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20129198Scognet * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21129198Scognet * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22129198Scognet * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23129198Scognet * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24129198Scognet * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25129198Scognet * SUCH DAMAGE.
26129198Scognet *
27129198Scognet * Author: Hartmut Brandt <harti@freebsd.org>
28129198Scognet *
29129198Scognet * Driver for IDT77252 based cards like ProSum's.
30129198Scognet */
31129198Scognet
32129198Scognet#include <sys/cdefs.h>
33129198Scognet__FBSDID("$FreeBSD: head/sys/dev/patm/if_patm_rx.c 175872 2008-02-01 19:36:27Z phk $");
34129198Scognet
35129198Scognet#include "opt_inet.h"
36244471Scognet#include "opt_natm.h"
37244471Scognet
38244471Scognet#include <sys/types.h>
39244471Scognet#include <sys/param.h>
40244471Scognet#include <sys/systm.h>
41244471Scognet#include <sys/malloc.h>
42244471Scognet#include <sys/kernel.h>
43244471Scognet#include <sys/bus.h>
44244471Scognet#include <sys/errno.h>
45244471Scognet#include <sys/conf.h>
46244471Scognet#include <sys/module.h>
47244471Scognet#include <sys/lock.h>
48244471Scognet#include <sys/mutex.h>
49244471Scognet#include <sys/sysctl.h>
50244471Scognet#include <sys/queue.h>
51244471Scognet#include <sys/condvar.h>
52244471Scognet#include <sys/endian.h>
53129198Scognet#include <vm/uma.h>
54129198Scognet
55129198Scognet#include <sys/sockio.h>
56129198Scognet#include <sys/mbuf.h>
57129198Scognet#include <sys/socket.h>
58129198Scognet
59129198Scognet#include <net/if.h>
60244471Scognet#include <net/if_media.h>
61129198Scognet#include <net/if_atm.h>
62129198Scognet#include <net/route.h>
63129198Scognet#ifdef ENABLE_BPF
64246713Skib#include <net/bpf.h>
65129198Scognet#endif
66140310Scognet#include <netinet/in.h>
67146597Scognet#include <netinet/if_atm.h>
68166063Scognet
69246713Skib#include <machine/bus.h>
70129198Scognet#include <machine/resource.h>
71244471Scognet#include <sys/bus.h>
72129198Scognet#include <sys/rman.h>
73244471Scognet#include <sys/mbpool.h>
74244471Scognet
75129198Scognet#include <dev/utopia/utopia.h>
76129198Scognet#include <dev/patm/idt77252reg.h>
77129198Scognet#include <dev/patm/if_patmvar.h>
78129198Scognet
79129198Scognetstatic void *patm_rcv_handle(struct patm_softc *sc, u_int handle);
80129198Scognetstatic void patm_rcv_free(struct patm_softc *, void *, u_int handle);
81166063Scognetstatic struct mbuf *patm_rcv_mbuf(struct patm_softc *, void *, u_int, int);
82129198Scognet
83166063Scognetstatic __inline void
84166063Scognetrct_write(struct patm_softc *sc, u_int cid, u_int w, u_int val)
85166063Scognet{
86166063Scognet	patm_sram_write(sc, sc->mmap->rct + cid * IDT_RCT_ENTRY_SIZE + w, val);
87166063Scognet}
88166063Scognetstatic __inline u_int
89129198Scognetrct_read(struct patm_softc *sc, u_int cid, u_int w)
90129198Scognet{
91129198Scognet	return (patm_sram_read(sc, sc->mmap->rct +
92232356Sjhb	    cid * IDT_RCT_ENTRY_SIZE + w));
93129198Scognet}
94129198Scognet
95129198Scognet/* check if we can open this one */
96129198Scognetint
97129198Scognetpatm_rx_vcc_can_open(struct patm_softc *sc, struct patm_vcc *vcc)
98129198Scognet{
99129198Scognet	return (0);
100129198Scognet}
101129198Scognet
102129198Scognet/*
103129198Scognet * open the VCC
104129198Scognet */
105129198Scognetvoid
106129198Scognetpatm_rx_vcc_open(struct patm_softc *sc, struct patm_vcc *vcc)
107129198Scognet{
108129198Scognet	uint32_t w1 = IDT_RCT_OPEN;
109129198Scognet
110129198Scognet	patm_debug(sc, VCC, "%u.%u RX opening", vcc->vcc.vpi, vcc->vcc.vci);
111129198Scognet
112129198Scognet	switch (vcc->vcc.aal) {
113166063Scognet	  case ATMIO_AAL_0:
114244471Scognet		w1 |= IDT_RCT_AAL0 | IDT_RCT_FBP2 | IDT_RCT_RCI;
115244471Scognet		break;
116244471Scognet	  case ATMIO_AAL_34:
117244471Scognet		w1 |= IDT_RCT_AAL34;
118244471Scognet		break;
119244471Scognet	  case ATMIO_AAL_5:
120244471Scognet		w1 |= IDT_RCT_AAL5;
121129198Scognet		break;
122129198Scognet	  case ATMIO_AAL_RAW:
123166063Scognet		w1 |= IDT_RCT_AALRAW | IDT_RCT_RCI;
124166063Scognet		break;
125166063Scognet	}
126166063Scognet
127166063Scognet	if (vcc->cid != 0)
128246713Skib		patm_sram_write4(sc, sc->mmap->rct + vcc->cid *
129166063Scognet		    IDT_RCT_ENTRY_SIZE, w1, 0, 0, 0xffffffff);
130166063Scognet	else {
131166063Scognet		/* switch the interface into promiscuous mode */
132166063Scognet		patm_nor_write(sc, IDT_NOR_CFG, patm_nor_read(sc, IDT_NOR_CFG) |
133246713Skib		    IDT_CFG_ICAPT | IDT_CFG_VPECA);
134246713Skib	}
135246713Skib
136246713Skib	vcc->vflags |= PATM_VCC_RX_OPEN;
137246713Skib}
138246713Skib
139166063Scognet/* close the given vcc for transmission */
140166063Scognetvoid
141166063Scognetpatm_rx_vcc_close(struct patm_softc *sc, struct patm_vcc *vcc)
142166063Scognet{
143166063Scognet	u_int w1;
144166063Scognet
145166063Scognet	patm_debug(sc, VCC, "%u.%u RX closing", vcc->vcc.vpi, vcc->vcc.vci);
146166063Scognet
147166063Scognet	if (vcc->cid == 0) {
148166063Scognet		/* switch off promiscuous mode */
149166063Scognet		patm_nor_write(sc, IDT_NOR_CFG, patm_nor_read(sc, IDT_NOR_CFG) &
150188403Scognet		    ~(IDT_CFG_ICAPT | IDT_CFG_VPECA));
151166063Scognet		vcc->vflags &= ~PATM_VCC_RX_OPEN;
152166063Scognet		return;
153166063Scognet	}
154166063Scognet
155166063Scognet	/* close the connection but keep state */
156166063Scognet	w1 = rct_read(sc, vcc->cid, 0);
157166063Scognet	w1 &= ~IDT_RCT_OPEN;
158166063Scognet	rct_write(sc, vcc->cid, 0, w1);
159166063Scognet
160166063Scognet	/* minimum idle count */
161166063Scognet	w1 = (w1 & ~IDT_RCT_IACT_CNT_MASK) | (1 << IDT_RCT_IACT_CNT_SHIFT);
162166063Scognet	rct_write(sc, vcc->cid, 0, w1);
163166063Scognet
164227309Sed	/* initialize scan */
165166063Scognet	patm_nor_write(sc, IDT_NOR_IRCP, vcc->cid);
166166063Scognet
167166063Scognet	vcc->vflags &= ~PATM_VCC_RX_OPEN;
168246713Skib	vcc->vflags |= PATM_VCC_RX_CLOSING;
169244471Scognet
170246713Skib	/*
171129198Scognet	 * check the RSQ
172166063Scognet	 * This is a hack. The problem is, that although an entry is written
173166063Scognet	 * to the RSQ, no interrupt is generated. Also we must wait 1 cell
174166063Scognet	 * time for the SAR to process the scan of our connection.
175135644Scognet	 */
176246713Skib	DELAY(1);
177135644Scognet	patm_intr_rsq(sc);
178166063Scognet}
179166063Scognet
180166063Scognet/* transmission side finally closed */
181246713Skibvoid
182246713Skibpatm_rx_vcc_closed(struct patm_softc *sc, struct patm_vcc *vcc)
183129198Scognet{
184129198Scognet	patm_debug(sc, VCC, "%u.%u RX finally closed",
185166063Scognet	    vcc->vcc.vpi, vcc->vcc.vci);
186166063Scognet}
187166063Scognet
188146597Scognet/*
189146597Scognet * Handle the given receive status queue entry
190146597Scognet */
191146597Scognetvoid
192166063Scognetpatm_rx(struct patm_softc *sc, struct idt_rsqe *rsqe)
193166063Scognet{
194166063Scognet	struct mbuf *m;
195166063Scognet	void *buf;
196166063Scognet	u_int stat, cid, w, cells, len, h;
197166063Scognet	struct patm_vcc *vcc;
198246713Skib	struct atm_pseudohdr aph;
199246713Skib	u_char *trail;
200166063Scognet
201166063Scognet	cid = le32toh(rsqe->cid);
202166063Scognet	stat = le32toh(rsqe->stat);
203166063Scognet	h = le32toh(rsqe->handle);
204166063Scognet
205244473Scognet	cid = PATM_CID(sc, IDT_RSQE_VPI(cid), IDT_RSQE_VCI(cid));
206244473Scognet	vcc = sc->vccs[cid];
207244473Scognet
208244473Scognet	if (IDT_RSQE_TYPE(stat) == IDT_RSQE_IDLE) {
209244471Scognet		/* connection has gone idle */
210244471Scognet		if (stat & IDT_RSQE_BUF)
211244471Scognet			patm_rcv_free(sc, patm_rcv_handle(sc, h), h);
212244471Scognet
213244471Scognet		w = rct_read(sc, cid, 0);
214244471Scognet		if (w != 0 && !(w & IDT_RCT_OPEN))
215166063Scognet			rct_write(sc, cid, 0, 0);
216244471Scognet		if (vcc != NULL && (vcc->vflags & PATM_VCC_RX_CLOSING)) {
217244471Scognet			patm_debug(sc, VCC, "%u.%u RX closed", vcc->vcc.vpi,
218244471Scognet			    vcc->vcc.vci);
219244471Scognet			vcc->vflags &= ~PATM_VCC_RX_CLOSING;
220244471Scognet			if (vcc->vcc.flags & ATMIO_FLAG_ASYNC) {
221244471Scognet				patm_rx_vcc_closed(sc, vcc);
222244471Scognet				if (!(vcc->vflags & PATM_VCC_OPEN))
223244471Scognet					patm_vcc_closed(sc, vcc);
224244471Scognet			} else
225244471Scognet				cv_signal(&sc->vcc_cv);
226244471Scognet		}
227244471Scognet		return;
228244471Scognet	}
229244471Scognet
230244471Scognet	buf = patm_rcv_handle(sc, h);
231244471Scognet
232244471Scognet	if (vcc == NULL || (vcc->vflags & PATM_VCC_RX_OPEN) == 0) {
233244471Scognet		patm_rcv_free(sc, buf, h);
234244471Scognet		return;
235244471Scognet	}
236244471Scognet
237244471Scognet	cells = IDT_RSQE_CNT(stat);
238244471Scognet	KASSERT(cells > 0, ("zero cell count"));
239244471Scognet
240244471Scognet	if (vcc->vcc.aal == ATMIO_AAL_0) {
241244471Scognet		/* deliver this packet as it is */
242244471Scognet		if ((m = patm_rcv_mbuf(sc, buf, h, 1)) == NULL)
243244471Scognet			return;
244244471Scognet
245244471Scognet		m->m_len = cells * 48;
246244471Scognet		m->m_pkthdr.len = m->m_len;
247244471Scognet		m->m_pkthdr.rcvif = sc->ifp;
248244471Scognet
249244471Scognet	} else if (vcc->vcc.aal == ATMIO_AAL_34) {
250244471Scognet		/* XXX AAL3/4 */
251244471Scognet		patm_rcv_free(sc, buf, h);
252244471Scognet		return;
253244471Scognet
254244471Scognet	} else if (vcc->vcc.aal == ATMIO_AAL_5) {
255244471Scognet		if (stat & IDT_RSQE_CRC) {
256244471Scognet			sc->ifp->if_ierrors++;
257244471Scognet			if (vcc->chain != NULL) {
258244471Scognet				m_freem(vcc->chain);
259244471Scognet				vcc->chain = vcc->last = NULL;
260244471Scognet			}
261244471Scognet			return;
262244471Scognet		}
263244471Scognet
264244471Scognet		/* append to current chain */
265244471Scognet		if (vcc->chain == NULL) {
266244471Scognet			if ((m = patm_rcv_mbuf(sc, buf, h, 1)) == NULL)
267244471Scognet				return;
268244471Scognet			m->m_len = cells * 48;
269244471Scognet			m->m_pkthdr.len = m->m_len;
270244471Scognet			m->m_pkthdr.rcvif = sc->ifp;
271244471Scognet			vcc->chain = vcc->last = m;
272244471Scognet		} else {
273244471Scognet			if ((m = patm_rcv_mbuf(sc, buf, h, 0)) == NULL)
274244471Scognet				return;
275244471Scognet			m->m_len = cells * 48;
276244471Scognet			vcc->last->m_next = m;
277244471Scognet			vcc->last = m;
278244471Scognet			vcc->chain->m_pkthdr.len += m->m_len;
279244471Scognet		}
280244471Scognet
281244471Scognet		if (!(stat & IDT_RSQE_EPDU))
282244471Scognet			return;
283244471Scognet
284244471Scognet		trail = mtod(m, u_char *) + m->m_len - 6;
285244473Scognet		len = (trail[0] << 8) + trail[1];
286244473Scognet
287244473Scognet		if ((u_int)vcc->chain->m_pkthdr.len < len + 8) {
288244473Scognet			patm_printf(sc, "%s: bad aal5 lengths %u %u\n",
289244471Scognet			    __func__, (u_int)m->m_pkthdr.len, len);
290244471Scognet			m_freem(vcc->chain);
291166063Scognet			vcc->chain = vcc->last = NULL;
292166063Scognet			return;
293166063Scognet		}
294166063Scognet		m->m_len -= vcc->chain->m_pkthdr.len - len;
295166063Scognet		KASSERT(m->m_len >= 0, ("bad last mbuf"));
296166063Scognet
297166063Scognet		m = vcc->chain;
298166063Scognet		vcc->chain = vcc->last = NULL;
299166063Scognet		m->m_pkthdr.len = len;
300166063Scognet	} else
301166063Scognet		panic("bad aal");
302166063Scognet
303166063Scognet#if 0
304166063Scognet	{
305166063Scognet		u_int i;
306166063Scognet
307166063Scognet		for (i = 0; i < m->m_len; i++) {
308166063Scognet			printf("%02x ", mtod(m, u_char *)[i]);
309166063Scognet		}
310166063Scognet		printf("\n");
311166063Scognet	}
312166063Scognet#endif
313166063Scognet
314166063Scognet	sc->ifp->if_ipackets++;
315166063Scognet	/* this is in if_atmsubr.c */
316166063Scognet	/* sc->ifp->if_ibytes += m->m_pkthdr.len; */
317129198Scognet
318244471Scognet	vcc->ibytes += m->m_pkthdr.len;
319244471Scognet	vcc->ipackets++;
320244471Scognet
321244471Scognet	ATM_PH_FLAGS(&aph) = vcc->vcc.flags & 0xff;
322244471Scognet	ATM_PH_VPI(&aph) = IDT_RSQE_VPI(cid);
323244471Scognet	ATM_PH_SETVCI(&aph, IDT_RSQE_VCI(cid));
324244471Scognet
325244471Scognet#ifdef ENABLE_BPF
326244471Scognet	if (!(vcc->vcc.flags & ATMIO_FLAG_NG) &&
327244471Scognet	    (vcc->vcc.aal == ATMIO_AAL_5) &&
328129198Scognet	    (vcc->vcc.flags & ATM_PH_LLCSNAP))
329137758Scognet		BPF_MTAP(sc->ifp, m);
330166063Scognet#endif
331166063Scognet
332166063Scognet	atm_input(sc->ifp, &aph, m, vcc->rxhand);
333244471Scognet}
334244471Scognet
335244471Scognet/*
336244471Scognet * Get the buffer for a receive handle. This is either an mbuf for
337166063Scognet * a large handle or a pool buffer for the others.
338166063Scognet */
339236991Simpstatic void *
340166063Scognetpatm_rcv_handle(struct patm_softc *sc, u_int handle)
341166063Scognet{
342166063Scognet	void *buf;
343166063Scognet	u_int c;
344166063Scognet
345166063Scognet	if ((handle & ~MBUF_HMASK) == LMBUF_HANDLE) {
346129198Scognet		struct lmbuf *b;
347129198Scognet
348129198Scognet		c = handle & MBUF_HMASK;
349129198Scognet		b = &sc->lbufs[c];
350129198Scognet
351129198Scognet		buf = b->m;
352129198Scognet		b->m = NULL;
353129198Scognet
354129198Scognet		bus_dmamap_sync(sc->lbuf_tag, b->map, BUS_DMASYNC_POSTREAD);
355129198Scognet		patm_lbuf_free(sc, b);
356129198Scognet
357129198Scognet	} else if ((handle & ~MBUF_HMASK) == MBUF_VHANDLE) {
358129198Scognet		mbp_sync(sc->vbuf_pool, handle,
359129198Scognet		    0, VMBUF_SIZE, BUS_DMASYNC_POSTREAD);
360129198Scognet		buf = mbp_get(sc->vbuf_pool, handle);
361129198Scognet
362129198Scognet	} else {
363129198Scognet		mbp_sync(sc->sbuf_pool, handle,
364129198Scognet		    0, SMBUF_SIZE, BUS_DMASYNC_POSTREAD);
365129198Scognet		buf = mbp_get(sc->sbuf_pool, handle);
366129198Scognet	}
367129198Scognet
368129198Scognet	return (buf);
369129198Scognet}
370129198Scognet
371129198Scognet/*
372129198Scognet * Free a buffer.
373129198Scognet */
374129198Scognetstatic void
375129198Scognetpatm_rcv_free(struct patm_softc *sc, void *p, u_int handle)
376129198Scognet{
377129198Scognet	if ((handle & ~MBUF_HMASK) == LMBUF_HANDLE)
378129198Scognet		m_free((struct mbuf *)p);
379129198Scognet
380129198Scognet	else if ((handle & ~MBUF_HMASK) == MBUF_VHANDLE)
381129198Scognet		mbp_free(sc->vbuf_pool, p);
382129198Scognet
383129198Scognet	else
384129198Scognet		mbp_free(sc->sbuf_pool, p);
385129198Scognet}
386129198Scognet
387129198Scognet/*
388129198Scognet * Make an mbuf around the buffer
389129198Scognet */
390129198Scognetstatic struct mbuf *
391129198Scognetpatm_rcv_mbuf(struct patm_softc *sc, void *buf, u_int h, int hdr)
392129198Scognet{
393129198Scognet	struct mbuf *m;
394129198Scognet
395129198Scognet	if ((h & ~MBUF_HMASK) == MBUF_LHANDLE)
396129198Scognet		return ((struct mbuf *)buf);
397129198Scognet
398129198Scognet	if (hdr)
399129198Scognet		MGETHDR(m, M_DONTWAIT, MT_DATA);
400129198Scognet	else
401129198Scognet		MGET(m, M_DONTWAIT, MT_DATA);
402129198Scognet	if (m == NULL) {
403129198Scognet		patm_rcv_free(sc, buf, h);
404135644Scognet		return (NULL);
405135644Scognet	}
406129198Scognet
407129198Scognet	if ((h & ~MBUF_HMASK) == MBUF_VHANDLE) {
408232356Sjhb		MEXTADD(m, (caddr_t)buf, VMBUF_SIZE, mbp_ext_free,
409129198Scognet		    buf, sc->vbuf_pool, M_PKTHDR, EXT_NET_DRV);
410129198Scognet		m->m_data += VMBUF_OFFSET;
411129198Scognet	} else {
412129198Scognet		MEXTADD(m, (caddr_t)buf, SMBUF_SIZE, mbp_ext_free,
413129198Scognet		    buf, sc->sbuf_pool, M_PKTHDR, EXT_NET_DRV);
414129198Scognet		m->m_data += SMBUF_OFFSET;
415129198Scognet	}
416129198Scognet
417129198Scognet	if (!(m->m_flags & M_EXT)) {
418166063Scognet		patm_rcv_free(sc, buf, h);
419166063Scognet		m_free(m);
420129198Scognet		return (NULL);
421129198Scognet	}
422140313Scognet	return (m);
423143294Smux}
424143284Smux
425129198Scognet/*
426140313Scognet * Process the raw cell at the given address.
427129198Scognet */
428129198Scognetvoid
429244471Scognetpatm_rx_raw(struct patm_softc *sc, u_char *cell)
430129198Scognet{
431129198Scognet	u_int vpi, vci, cid;
432129198Scognet	struct patm_vcc *vcc;
433129198Scognet	struct mbuf *m;
434129198Scognet	u_char *dst;
435129198Scognet	struct timespec ts;
436129198Scognet	struct atm_pseudohdr aph;
437129198Scognet	uint64_t cts;
438129198Scognet
439129198Scognet	sc->stats.raw_cells++;
440129198Scognet
441129198Scognet	/*
442135644Scognet	 * For some non-appearant reason the cell header
443129198Scognet	 * is in the wrong endian.
444129198Scognet	 */
445129198Scognet	*(uint32_t *)cell = bswap32(*(uint32_t *)cell);
446129198Scognet
447129198Scognet	vpi = ((cell[0] & 0xf) << 4) | ((cell[1] & 0xf0) >> 4);
448129198Scognet	vci = ((cell[1] & 0xf) << 12) | (cell[2] << 4) | ((cell[3] & 0xf0) >> 4);
449129198Scognet	cid = PATM_CID(sc, vpi, vci);
450244471Scognet
451244471Scognet	vcc = sc->vccs[cid];
452244471Scognet	if (vcc == NULL || !(vcc->vflags & PATM_VCC_RX_OPEN) ||
453244471Scognet	    vcc->vcc.aal != ATMIO_AAL_RAW) {
454244471Scognet		vcc = sc->vccs[0];
455244471Scognet		if (vcc == NULL || !(vcc->vflags & PATM_VCC_RX_OPEN)) {
456244471Scognet			sc->stats.raw_no_vcc++;
457244471Scognet			return;
458244471Scognet		}
459244471Scognet	}
460244471Scognet
461244471Scognet	MGETHDR(m, M_DONTWAIT, MT_DATA);
462244471Scognet	if (m == NULL) {
463129198Scognet		sc->stats.raw_no_buf++;
464129198Scognet		return;
465129198Scognet	}
466232356Sjhb	m->m_pkthdr.rcvif = sc->ifp;
467232356Sjhb
468134934Sscottl	switch (vcc->vflags & PATM_RAW_FORMAT) {
469134934Sscottl
470134934Sscottl	  default:
471232356Sjhb	  case PATM_RAW_CELL:
472134934Sscottl		m->m_len = m->m_pkthdr.len = 53;
473166063Scognet		MH_ALIGN(m, 53);
474166063Scognet		dst = mtod(m, u_char *);
475166063Scognet		*dst++ = *cell++;
476129198Scognet		*dst++ = *cell++;
477129198Scognet		*dst++ = *cell++;
478129198Scognet		*dst++ = *cell++;
479129198Scognet		*dst++ = 0;		/* HEC */
480129198Scognet		bcopy(cell + 12, dst, 48);
481129198Scognet		break;
482129198Scognet
483129198Scognet	  case PATM_RAW_NOHEC:
484129198Scognet		m->m_len = m->m_pkthdr.len = 52;
485129198Scognet		MH_ALIGN(m, 52);
486129198Scognet		dst = mtod(m, u_char *);
487129198Scognet		*dst++ = *cell++;
488166063Scognet		*dst++ = *cell++;
489166063Scognet		*dst++ = *cell++;
490166063Scognet		*dst++ = *cell++;
491129198Scognet		bcopy(cell + 12, dst, 48);
492166063Scognet		break;
493166063Scognet
494166063Scognet	  case PATM_RAW_CS:
495166063Scognet		m->m_len = m->m_pkthdr.len = 64;
496166063Scognet		MH_ALIGN(m, 64);
497166063Scognet		dst = mtod(m, u_char *);
498166063Scognet		*dst++ = *cell++;
499166063Scognet		*dst++ = *cell++;
500166063Scognet		*dst++ = *cell++;
501166063Scognet		*dst++ = *cell++;
502166063Scognet		*dst++ = 0;		/* HEC */
503166063Scognet		*dst++ = 0;		/* flags */
504166063Scognet		*dst++ = 0;		/* reserved */
505166063Scognet		*dst++ = 0;		/* reserved */
506166063Scognet		nanotime(&ts);
507166063Scognet		cts = ts.tv_sec * 1000000000ULL + ts.tv_nsec;
508166063Scognet		bcopy(dst, &cts, 8);
509166063Scognet		bcopy(cell + 12, dst + 8, 48);
510166063Scognet		break;
511166063Scognet	}
512166063Scognet
513166063Scognet	sc->ifp->if_ipackets++;
514166063Scognet	/* this is in if_atmsubr.c */
515170502Scognet	/* sc->ifp->if_ibytes += m->m_pkthdr.len; */
516170502Scognet
517166063Scognet	vcc->ibytes += m->m_pkthdr.len;
518166063Scognet	vcc->ipackets++;
519166063Scognet
520166063Scognet	ATM_PH_FLAGS(&aph) = vcc->vcc.flags & 0xff;
521143294Smux	ATM_PH_VPI(&aph) = vcc->vcc.vpi;
522143284Smux	ATM_PH_SETVCI(&aph, vcc->vcc.vci);
523140313Scognet
524129198Scognet	atm_input(sc->ifp, &aph, m, vcc->rxhand);
525129198Scognet}
526129198Scognet