utopia.c revision 302408
1/*- 2 * Copyright (c) 2003 3 * Fraunhofer Institute for Open Communication Systems (FhG Fokus). 4 * All rights reserved. 5 * 6 * Author: Hartmut Brandt <harti@freebsd.org> 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30#include <sys/cdefs.h> 31__FBSDID("$FreeBSD: stable/11/sys/dev/utopia/utopia.c 241394 2012-10-10 08:36:38Z kevlo $"); 32 33#include <sys/param.h> 34#include <sys/systm.h> 35#include <sys/unistd.h> 36#include <sys/kernel.h> 37#include <sys/kthread.h> 38#include <sys/proc.h> 39#include <sys/bus.h> 40#include <sys/malloc.h> 41#include <sys/module.h> 42#include <sys/sysctl.h> 43#include <sys/lock.h> 44#include <sys/mutex.h> 45#include <sys/socket.h> 46 47#include <net/if.h> 48#include <net/if_var.h> 49#include <net/if_media.h> 50#include <net/if_atm.h> 51 52#include <dev/utopia/suni.h> 53#include <dev/utopia/idtphy.h> 54#include <dev/utopia/utopia.h> 55#include <dev/utopia/utopia_priv.h> 56 57/* known chips */ 58extern const struct utopia_chip utopia_chip_idt77155; 59extern const struct utopia_chip utopia_chip_idt77105; 60extern const struct utopia_chip utopia_chip_lite; 61extern const struct utopia_chip utopia_chip_ultra; 62extern const struct utopia_chip utopia_chip_622; 63 64/* 65 * Global list of all registered interfaces 66 */ 67static struct mtx utopia_list_mtx; 68static LIST_HEAD(, utopia) utopia_list = LIST_HEAD_INITIALIZER(utopia_list); 69 70#define UTP_RLOCK_LIST() mtx_lock(&utopia_list_mtx) 71#define UTP_RUNLOCK_LIST() mtx_unlock(&utopia_list_mtx) 72#define UTP_WLOCK_LIST() mtx_lock(&utopia_list_mtx) 73#define UTP_WUNLOCK_LIST() mtx_unlock(&utopia_list_mtx) 74 75#define UTP_LOCK(UTP) mtx_lock((UTP)->lock) 76#define UTP_UNLOCK(UTP) mtx_unlock((UTP)->lock) 77#define UTP_LOCK_ASSERT(UTP) mtx_assert((UTP)->lock, MA_OWNED) 78 79static struct proc *utopia_kproc; 80 81static void utopia_dump(struct utopia *) __unused; 82 83/* 84 * Read a multi-register value. 85 */ 86uint32_t 87utopia_update(struct utopia *utp, u_int reg, u_int nreg, uint32_t mask) 88{ 89 int err; 90 u_int n; 91 uint8_t regs[4]; 92 uint32_t val; 93 94 n = nreg; 95 if ((err = UTP_READREGS(utp, reg, regs, &n)) != 0) { 96#ifdef DIAGNOSTIC 97 printf("%s: register read error %s(%u,%u): %d\n", __func__, 98 utp->chip->name, reg, nreg, err); 99#endif 100 return (0); 101 } 102 if (n < nreg) { 103#ifdef DIAGNOSTIC 104 printf("%s: got only %u regs %s(%u,%u): %d\n", __func__, n, 105 utp->chip->name, reg, nreg, err); 106#endif 107 return (0); 108 } 109 val = 0; 110 for (n = nreg; n > 0; n--) { 111 val <<= 8; 112 val |= regs[n - 1]; 113 } 114 return (val & mask); 115} 116 117/* 118 * Debugging - dump all registers. 119 */ 120static void 121utopia_dump(struct utopia *utp) 122{ 123 uint8_t regs[256]; 124 u_int n = 256, i; 125 int err; 126 127 if ((err = UTP_READREGS(utp, 0, regs, &n)) != 0) { 128 printf("UTOPIA reg read error %d\n", err); 129 return; 130 } 131 for (i = 0; i < n; i++) { 132 if (i % 16 == 0) 133 printf("%02x:", i); 134 if (i % 16 == 8) 135 printf(" "); 136 printf(" %02x", regs[i]); 137 if (i % 16 == 15) 138 printf("\n"); 139 } 140 if (i % 16 != 0) 141 printf("\n"); 142} 143 144/* 145 * Update the carrier status 146 */ 147void 148utopia_check_carrier(struct utopia *utp, u_int carr_ok) 149{ 150 int old; 151 152 old = utp->carrier; 153 if (carr_ok) { 154 /* carrier */ 155 utp->carrier = UTP_CARR_OK; 156 if (old != UTP_CARR_OK) { 157 if_printf(utp->ifatm->ifp, "carrier detected\n"); 158 ATMEV_SEND_IFSTATE_CHANGED(utp->ifatm, 1); 159 } 160 } else { 161 /* no carrier */ 162 utp->carrier = UTP_CARR_LOST; 163 if (old == UTP_CARR_OK) { 164 if_printf(utp->ifatm->ifp, "carrier lost\n"); 165 ATMEV_SEND_IFSTATE_CHANGED(utp->ifatm, 0); 166 } 167 } 168} 169 170static int 171unknown_inval(struct utopia *utp, int what __unused) 172{ 173 174 return (EINVAL); 175} 176 177static int 178unknown_reset(struct utopia *utp __unused) 179{ 180 return (EIO); 181} 182 183static int 184unknown_update_carrier(struct utopia *utp) 185{ 186 utp->carrier = UTP_CARR_UNKNOWN; 187 return (0); 188} 189 190static int 191unknown_set_loopback(struct utopia *utp __unused, u_int mode __unused) 192{ 193 return (EINVAL); 194} 195 196static void 197unknown_intr(struct utopia *utp __unused) 198{ 199} 200 201static void 202unknown_update_stats(struct utopia *utp __unused) 203{ 204} 205 206static const struct utopia_chip utopia_chip_unknown = { 207 UTP_TYPE_UNKNOWN, 208 "unknown", 209 0, 210 unknown_reset, 211 unknown_inval, 212 unknown_inval, 213 unknown_inval, 214 unknown_update_carrier, 215 unknown_set_loopback, 216 unknown_intr, 217 unknown_update_stats, 218}; 219 220/* 221 * Callbacks for the ifmedia infrastructure. 222 */ 223static int 224utopia_media_change(struct ifnet *ifp) 225{ 226 struct ifatm *ifatm = IFP2IFATM(ifp); 227 struct utopia *utp = ifatm->phy; 228 int error = 0; 229 230 UTP_LOCK(utp); 231 if (utp->chip->type != UTP_TYPE_UNKNOWN && utp->state & UTP_ST_ACTIVE) { 232 if (utp->media->ifm_media & IFM_ATM_SDH) { 233 if (!(utp->state & UTP_ST_SDH)) 234 error = utopia_set_sdh(utp, 1); 235 } else { 236 if (utp->state & UTP_ST_SDH) 237 error = utopia_set_sdh(utp, 0); 238 } 239 if (utp->media->ifm_media & IFM_ATM_UNASSIGNED) { 240 if (!(utp->state & UTP_ST_UNASS)) 241 error = utopia_set_unass(utp, 1); 242 } else { 243 if (utp->state & UTP_ST_UNASS) 244 error = utopia_set_unass(utp, 0); 245 } 246 if (utp->media->ifm_media & IFM_ATM_NOSCRAMB) { 247 if (!(utp->state & UTP_ST_NOSCRAMB)) 248 error = utopia_set_noscramb(utp, 1); 249 } else { 250 if (utp->state & UTP_ST_NOSCRAMB) 251 error = utopia_set_noscramb(utp, 0); 252 } 253 } else 254 error = EIO; 255 UTP_UNLOCK(utp); 256 return (error); 257} 258 259/* 260 * Look at the carrier status. 261 */ 262static void 263utopia_media_status(struct ifnet *ifp, struct ifmediareq *ifmr) 264{ 265 struct utopia *utp = IFP2IFATM(ifp)->phy; 266 267 UTP_LOCK(utp); 268 if (utp->chip->type != UTP_TYPE_UNKNOWN && utp->state & UTP_ST_ACTIVE) { 269 ifmr->ifm_active = IFM_ATM | utp->ifatm->mib.media; 270 271 switch (utp->carrier) { 272 273 case UTP_CARR_OK: 274 ifmr->ifm_status = IFM_AVALID | IFM_ACTIVE; 275 break; 276 277 case UTP_CARR_LOST: 278 ifmr->ifm_status = IFM_AVALID; 279 break; 280 281 default: 282 ifmr->ifm_status = 0; 283 break; 284 } 285 if (utp->state & UTP_ST_SDH) { 286 ifmr->ifm_active |= IFM_ATM_SDH; 287 ifmr->ifm_current |= IFM_ATM_SDH; 288 } 289 if (utp->state & UTP_ST_UNASS) { 290 ifmr->ifm_active |= IFM_ATM_UNASSIGNED; 291 ifmr->ifm_current |= IFM_ATM_UNASSIGNED; 292 } 293 if (utp->state & UTP_ST_NOSCRAMB) { 294 ifmr->ifm_active |= IFM_ATM_NOSCRAMB; 295 ifmr->ifm_current |= IFM_ATM_NOSCRAMB; 296 } 297 } else { 298 ifmr->ifm_active = 0; 299 ifmr->ifm_status = 0; 300 } 301 UTP_UNLOCK(utp); 302} 303 304/* 305 * Initialize media from the mib 306 */ 307void 308utopia_init_media(struct utopia *utp) 309{ 310 311 ifmedia_removeall(utp->media); 312 ifmedia_add(utp->media, IFM_ATM | utp->ifatm->mib.media, 0, NULL); 313 ifmedia_set(utp->media, IFM_ATM | utp->ifatm->mib.media); 314} 315 316/* 317 * Reset all media 318 */ 319void 320utopia_reset_media(struct utopia *utp) 321{ 322 323 ifmedia_removeall(utp->media); 324} 325 326/* 327 * This is called by the driver as soon as the SUNI registers are accessible. 328 * This may be either in the attach routine or the init routine of the driver. 329 */ 330int 331utopia_start(struct utopia *utp) 332{ 333 uint8_t reg; 334 int err; 335 u_int n = 1; 336 337 /* 338 * Try to find out what chip we have 339 */ 340 if ((err = UTP_READREGS(utp, SUNI_REGO_MRESET, ®, &n)) != 0) 341 return (err); 342 343 switch (reg & SUNI_REGM_MRESET_TYPE) { 344 345 case SUNI_REGM_MRESET_TYPE_622: 346 utp->chip = &utopia_chip_622; 347 break; 348 349 case SUNI_REGM_MRESET_TYPE_LITE: 350 /* this may be either a SUNI LITE or a IDT77155 * 351 * Read register 0x70. The SUNI doesn't have it */ 352 n = 1; 353 if ((err = UTP_READREGS(utp, IDTPHY_REGO_RBER, ®, &n)) != 0) 354 return (err); 355 if ((reg & ~IDTPHY_REGM_RBER_RESV) == 356 (IDTPHY_REGM_RBER_FAIL | IDTPHY_REGM_RBER_WARN)) 357 utp->chip = &utopia_chip_idt77155; 358 else 359 utp->chip = &utopia_chip_lite; 360 break; 361 362 case SUNI_REGM_MRESET_TYPE_ULTRA: 363 utp->chip = &utopia_chip_ultra; 364 break; 365 366 default: 367 if (reg == (IDTPHY_REGM_MCR_DRIC | IDTPHY_REGM_MCR_EI)) 368 utp->chip = &utopia_chip_idt77105; 369 else { 370 if_printf(utp->ifatm->ifp, 371 "unknown ATM-PHY chip %#x\n", reg); 372 utp->chip = &utopia_chip_unknown; 373 } 374 break; 375 } 376 utp->state |= UTP_ST_ACTIVE; 377 return (0); 378} 379 380/* 381 * Stop the chip 382 */ 383void 384utopia_stop(struct utopia *utp) 385{ 386 utp->state &= ~UTP_ST_ACTIVE; 387} 388 389/* 390 * Handle the sysctls 391 */ 392static int 393utopia_sysctl_regs(SYSCTL_HANDLER_ARGS) 394{ 395 struct utopia *utp = (struct utopia *)arg1; 396 int error; 397 u_int n; 398 uint8_t *val; 399 uint8_t new[3]; 400 401 if ((n = utp->chip->nregs) == 0) 402 return (EIO); 403 val = malloc(sizeof(uint8_t) * n, M_TEMP, M_WAITOK); 404 405 UTP_LOCK(utp); 406 error = UTP_READREGS(utp, 0, val, &n); 407 UTP_UNLOCK(utp); 408 409 if (error) { 410 free(val, M_TEMP); 411 return (error); 412 } 413 414 error = SYSCTL_OUT(req, val, sizeof(uint8_t) * n); 415 free(val, M_TEMP); 416 if (error != 0 || req->newptr == NULL) 417 return (error); 418 419 error = SYSCTL_IN(req, new, sizeof(new)); 420 if (error) 421 return (error); 422 423 UTP_LOCK(utp); 424 error = UTP_WRITEREG(utp, new[0], new[1], new[2]); 425 UTP_UNLOCK(utp); 426 427 return (error); 428} 429 430static int 431utopia_sysctl_stats(SYSCTL_HANDLER_ARGS) 432{ 433 struct utopia *utp = (struct utopia *)arg1; 434 void *val; 435 int error; 436 437 val = malloc(sizeof(utp->stats), M_TEMP, M_WAITOK); 438 439 UTP_LOCK(utp); 440 bcopy(&utp->stats, val, sizeof(utp->stats)); 441 if (req->newptr != NULL) 442 bzero((char *)&utp->stats + sizeof(utp->stats.version), 443 sizeof(utp->stats) - sizeof(utp->stats.version)); 444 UTP_UNLOCK(utp); 445 446 error = SYSCTL_OUT(req, val, sizeof(utp->stats)); 447 if (error && req->newptr != NULL) 448 bcopy(val, &utp->stats, sizeof(utp->stats)); 449 free(val, M_TEMP); 450 451 /* ignore actual new value */ 452 453 return (error); 454} 455 456/* 457 * Handle the loopback sysctl 458 */ 459static int 460utopia_sysctl_loopback(SYSCTL_HANDLER_ARGS) 461{ 462 struct utopia *utp = (struct utopia *)arg1; 463 int error; 464 u_int loopback; 465 466 error = SYSCTL_OUT(req, &utp->loopback, sizeof(u_int)); 467 if (error != 0 || req->newptr == NULL) 468 return (error); 469 470 error = SYSCTL_IN(req, &loopback, sizeof(u_int)); 471 if (error) 472 return (error); 473 474 UTP_LOCK(utp); 475 error = utopia_set_loopback(utp, loopback); 476 UTP_UNLOCK(utp); 477 478 return (error); 479} 480 481/* 482 * Handle the type sysctl 483 */ 484static int 485utopia_sysctl_type(SYSCTL_HANDLER_ARGS) 486{ 487 struct utopia *utp = (struct utopia *)arg1; 488 489 return (SYSCTL_OUT(req, &utp->chip->type, sizeof(utp->chip->type))); 490} 491 492/* 493 * Handle the name sysctl 494 */ 495static int 496utopia_sysctl_name(SYSCTL_HANDLER_ARGS) 497{ 498 struct utopia *utp = (struct utopia *)arg1; 499 500 return (SYSCTL_OUT(req, utp->chip->name, strlen(utp->chip->name) + 1)); 501} 502 503/* 504 * Initialize the state. This is called from the drivers attach 505 * function. The mutex must be already initialized. 506 */ 507int 508utopia_attach(struct utopia *utp, struct ifatm *ifatm, struct ifmedia *media, 509 struct mtx *lock, struct sysctl_ctx_list *ctx, 510 struct sysctl_oid_list *children, const struct utopia_methods *m) 511{ 512 513 bzero(utp, sizeof(*utp)); 514 utp->ifatm = ifatm; 515 utp->methods = m; 516 utp->media = media; 517 utp->lock = lock; 518 utp->chip = &utopia_chip_unknown; 519 utp->stats.version = 1; 520 521 ifmedia_init(media, 522 IFM_ATM_SDH | IFM_ATM_UNASSIGNED | IFM_ATM_NOSCRAMB, 523 utopia_media_change, utopia_media_status); 524 525 if (SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "phy_regs", 526 CTLFLAG_RW | CTLTYPE_OPAQUE, utp, 0, utopia_sysctl_regs, "S", 527 "phy registers") == NULL) 528 return (-1); 529 530 if (SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "phy_loopback", 531 CTLFLAG_RW | CTLTYPE_UINT, utp, 0, utopia_sysctl_loopback, "IU", 532 "phy loopback mode") == NULL) 533 return (-1); 534 535 if (SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "phy_type", 536 CTLFLAG_RD | CTLTYPE_UINT, utp, 0, utopia_sysctl_type, "IU", 537 "phy type") == NULL) 538 return (-1); 539 540 if (SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "phy_name", 541 CTLFLAG_RD | CTLTYPE_STRING, utp, 0, utopia_sysctl_name, "A", 542 "phy name") == NULL) 543 return (-1); 544 545 if (SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "phy_stats", 546 CTLFLAG_RW | CTLTYPE_OPAQUE, utp, 0, utopia_sysctl_stats, "S", 547 "phy statistics") == NULL) 548 return (-1); 549 550 if (SYSCTL_ADD_UINT(ctx, children, OID_AUTO, "phy_state", 551 CTLFLAG_RD, &utp->state, 0, "phy state") == NULL) 552 return (-1); 553 554 if (SYSCTL_ADD_UINT(ctx, children, OID_AUTO, "phy_carrier", 555 CTLFLAG_RD, &utp->carrier, 0, "phy carrier") == NULL) 556 return (-1); 557 558 UTP_WLOCK_LIST(); 559 LIST_INSERT_HEAD(&utopia_list, utp, link); 560 UTP_WUNLOCK_LIST(); 561 562 utp->state |= UTP_ST_ATTACHED; 563 return (0); 564} 565 566/* 567 * Detach. We set a flag here, wakeup the daemon and let him do it. 568 * Here we need the lock for synchronisation with the daemon. 569 */ 570void 571utopia_detach(struct utopia *utp) 572{ 573 574 UTP_LOCK_ASSERT(utp); 575 if (utp->state & UTP_ST_ATTACHED) { 576 utp->state |= UTP_ST_DETACH; 577 while (utp->state & UTP_ST_DETACH) { 578 wakeup(&utopia_list); 579 msleep(utp, utp->lock, PZERO, "utopia_detach", hz); 580 } 581 } 582} 583 584/* 585 * The carrier state kernel proc for those adapters that do not interrupt. 586 * 587 * We assume, that utopia_attach can safely add a new utopia while we are going 588 * through the list without disturbing us (we lock the list while getting 589 * the address of the first element, adding is always done at the head). 590 * Removing is entirely handled here. 591 */ 592static void 593utopia_daemon(void *arg __unused) 594{ 595 struct utopia *utp, *next; 596 597 UTP_RLOCK_LIST(); 598 while (utopia_kproc != NULL) { 599 utp = LIST_FIRST(&utopia_list); 600 UTP_RUNLOCK_LIST(); 601 602 while (utp != NULL) { 603 mtx_lock(&Giant); /* XXX depend on MPSAFE */ 604 UTP_LOCK(utp); 605 next = LIST_NEXT(utp, link); 606 if (utp->state & UTP_ST_DETACH) { 607 LIST_REMOVE(utp, link); 608 utp->state &= ~UTP_ST_DETACH; 609 wakeup_one(utp); 610 } else if (utp->state & UTP_ST_ACTIVE) { 611 if (utp->flags & UTP_FL_POLL_CARRIER) 612 utopia_update_carrier(utp); 613 utopia_update_stats(utp); 614 } 615 UTP_UNLOCK(utp); 616 mtx_unlock(&Giant); /* XXX depend on MPSAFE */ 617 utp = next; 618 } 619 620 UTP_RLOCK_LIST(); 621 msleep(&utopia_list, &utopia_list_mtx, PZERO, "*idle*", hz); 622 } 623 wakeup_one(&utopia_list); 624 UTP_RUNLOCK_LIST(); 625 kproc_exit(0); 626} 627 628/* 629 * Module initialisation 630 */ 631static int 632utopia_mod_init(module_t mod, int what, void *arg) 633{ 634 int err; 635 struct proc *kp; 636 637 switch (what) { 638 639 case MOD_LOAD: 640 mtx_init(&utopia_list_mtx, "utopia list mutex", NULL, MTX_DEF); 641 err = kproc_create(utopia_daemon, NULL, &utopia_kproc, 642 RFHIGHPID, 0, "utopia"); 643 if (err != 0) { 644 printf("cannot created utopia thread %d\n", err); 645 return (err); 646 } 647 break; 648 649 case MOD_UNLOAD: 650 UTP_WLOCK_LIST(); 651 if ((kp = utopia_kproc) != NULL) { 652 utopia_kproc = NULL; 653 wakeup_one(&utopia_list); 654 PROC_LOCK(kp); 655 UTP_WUNLOCK_LIST(); 656 msleep(kp, &kp->p_mtx, PWAIT, "utopia_destroy", 0); 657 PROC_UNLOCK(kp); 658 } else 659 UTP_WUNLOCK_LIST(); 660 mtx_destroy(&utopia_list_mtx); 661 break; 662 default: 663 return (EOPNOTSUPP); 664 } 665 return (0); 666} 667 668static moduledata_t utopia_mod = { 669 "utopia", 670 utopia_mod_init, 671 0 672}; 673 674DECLARE_MODULE(utopia, utopia_mod, SI_SUB_INIT_IF, SI_ORDER_ANY); 675MODULE_VERSION(utopia, 1); 676