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