]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - sys/dev/patm/if_patm_rx.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / sys / dev / patm / if_patm_rx.c
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_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
79 static void *patm_rcv_handle(struct patm_softc *sc, u_int handle);
80 static void patm_rcv_free(struct patm_softc *, void *, u_int handle);
81 static struct mbuf *patm_rcv_mbuf(struct patm_softc *, void *, u_int, int);
82
83 static __inline void
84 rct_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 }
88 static __inline u_int
89 rct_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 */
96 int
97 patm_rx_vcc_can_open(struct patm_softc *sc, struct patm_vcc *vcc)
98 {
99         return (0);
100 }
101
102 /*
103  * open the VCC
104  */
105 void
106 patm_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 */
140 void
141 patm_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 */
181 void
182 patm_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  */
191 void
192 patm_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->ifp;
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->ifp->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->ifp;
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->ifp->if_ipackets++;
315         /* this is in if_atmsubr.c */
316         /* sc->ifp->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->ifp, m);
330 #endif
331
332         atm_input(sc->ifp, &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  */
339 static void *
340 patm_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  */
374 static void
375 patm_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  */
390 static struct mbuf *
391 patm_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_NOWAIT, MT_DATA);
400         else
401                 MGET(m, M_NOWAIT, 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                 MEXTADD(m, (caddr_t)buf, VMBUF_SIZE, mbp_ext_free,
409                     buf, sc->vbuf_pool, M_PKTHDR, EXT_NET_DRV);
410                 m->m_data += VMBUF_OFFSET;
411         } else {
412                 MEXTADD(m, (caddr_t)buf, SMBUF_SIZE, mbp_ext_free,
413                     buf, 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  */
428 void
429 patm_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_NOWAIT, MT_DATA);
462         if (m == NULL) {
463                 sc->stats.raw_no_buf++;
464                 return;
465         }
466         m->m_pkthdr.rcvif = sc->ifp;
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->ifp->if_ipackets++;
514         /* this is in if_atmsubr.c */
515         /* sc->ifp->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->ifp, &aph, m, vcc->rxhand);
525 }