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