if_patm_rx.c revision 139749
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 139749 2005-01-06 01:43:34Z imp $"); 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 79static void *patm_rcv_handle(struct patm_softc *sc, u_int handle); 80static void patm_rcv_free(struct patm_softc *, void *, u_int handle); 81static struct mbuf *patm_rcv_mbuf(struct patm_softc *, void *, u_int, int); 82 83static __inline void 84rct_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} 88static __inline u_int 89rct_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 */ 96int 97patm_rx_vcc_can_open(struct patm_softc *sc, struct patm_vcc *vcc) 98{ 99 return (0); 100} 101 102/* 103 * open the VCC 104 */ 105void 106patm_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 */ 140void 141patm_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 */ 181void 182patm_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 */ 191void 192patm_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->ifatm.ifnet; 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->ifatm.ifnet.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->ifatm.ifnet; 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->ifatm.ifnet.if_ipackets++; 315 /* this is in if_atmsubr.c */ 316 /* sc->ifatm.ifnet.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->ifatm.ifnet, m); 330#endif 331 332 atm_input(&sc->ifatm.ifnet, &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 */ 339static void * 340patm_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 */ 374static void 375patm_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 */ 390static struct mbuf * 391patm_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_DONTWAIT, MT_DATA); 400 else 401 MGET(m, M_DONTWAIT, 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 m_extadd(m, (caddr_t)buf, VMBUF_SIZE, mbp_ext_free, 409 sc->vbuf_pool, M_PKTHDR, EXT_NET_DRV); 410 m->m_data += VMBUF_OFFSET; 411 } else { 412 m_extadd(m, (caddr_t)buf, SMBUF_SIZE, mbp_ext_free, 413 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 */ 428void 429patm_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_DONTWAIT, MT_DATA); 462 if (m == NULL) { 463 sc->stats.raw_no_buf++; 464 return; 465 } 466 m->m_pkthdr.rcvif = &sc->ifatm.ifnet; 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->ifatm.ifnet.if_ipackets++; 514 /* this is in if_atmsubr.c */ 515 /* sc->ifatm.ifnet.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->ifatm.ifnet, &aph, m, vcc->rxhand); 525} 526