if_patm_rx.c revision 119418
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: head/sys/dev/patm/if_patm_rx.c 119418 2003-08-24 17:55:58Z obrien $"); 34#include <sys/cdefs.h> 35__FBSDID("$FreeBSD: head/sys/dev/patm/if_patm_rx.c 119418 2003-08-24 17:55:58Z obrien $"); 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 81static void *patm_rcv_handle(struct patm_softc *sc, u_int handle); 82static void patm_rcv_free(struct patm_softc *, void *, u_int handle); 83static struct mbuf *patm_rcv_mbuf(struct patm_softc *, void *, u_int, int); 84 85static __inline void 86rct_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} 90static __inline u_int 91rct_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 */ 98int 99patm_rx_vcc_can_open(struct patm_softc *sc, struct patm_vcc *vcc) 100{ 101 return (0); 102} 103 104/* 105 * open the VCC 106 */ 107void 108patm_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 */ 142void 143patm_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 */ 183void 184patm_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 */ 193void 194patm_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 */ 341static void * 342patm_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 */ 376static void 377patm_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 */ 392static struct mbuf * 393patm_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 */ 430void 431patm_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} 528