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