]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/patm/if_patm_rx.c
Use __FBSDID().
[FreeBSD/FreeBSD.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 #include <sys/cdefs.h>
35 __FBSDID("$FreeBSD$");
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
81 static void *patm_rcv_handle(struct patm_softc *sc, u_int handle);
82 static void patm_rcv_free(struct patm_softc *, void *, u_int handle);
83 static struct mbuf *patm_rcv_mbuf(struct patm_softc *, void *, u_int, int);
84
85 static __inline void
86 rct_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 }
90 static __inline u_int
91 rct_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 */
98 int
99 patm_rx_vcc_can_open(struct patm_softc *sc, struct patm_vcc *vcc)
100 {
101         return (0);
102 }
103
104 /*
105  * open the VCC
106  */
107 void
108 patm_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 */
142 void
143 patm_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 */
183 void
184 patm_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  */
193 void
194 patm_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  */
341 static void *
342 patm_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  */
376 static void
377 patm_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  */
392 static struct mbuf *
393 patm_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  */
430 void
431 patm_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 }