diff options
Diffstat (limited to 'sys/dev/utopia/utopia.c')
-rw-r--r-- | sys/dev/utopia/utopia.c | 1082 |
1 files changed, 1082 insertions, 0 deletions
diff --git a/sys/dev/utopia/utopia.c b/sys/dev/utopia/utopia.c new file mode 100644 index 0000000..ce63186 --- /dev/null +++ b/sys/dev/utopia/utopia.c @@ -0,0 +1,1082 @@ +/* + * Copyright (c) 2003 + * Fraunhofer Institute for Open Communication Systems (FhG Fokus). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Author: Hartmut Brandt <harti@freebsd.org> + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/unistd.h> +#include <sys/kernel.h> +#include <sys/kthread.h> +#include <sys/proc.h> +#include <sys/bus.h> +#include <sys/malloc.h> +#include <sys/sysctl.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/socket.h> + +#include <net/if.h> +#include <net/if_var.h> +#include <net/if_media.h> +#include <net/if_atm.h> + +#include <dev/utopia/suni.h> +#include <dev/utopia/idtphy.h> +#include <dev/utopia/utopia.h> + +#define READREGS(UTOPIA, REG, VALP, NP) \ + (UTOPIA)->methods->readregs((UTOPIA)->ifatm, REG, VALP, NP) +#define WRITEREG(UTOPIA, REG, MASK, VAL) \ + (UTOPIA)->methods->writereg((UTOPIA)->ifatm, REG, MASK, VAL) + +/* + * Global list of all registered interfaces + */ +static struct mtx utopia_list_mtx; +static LIST_HEAD(, utopia) utopia_list = LIST_HEAD_INITIALIZER(utopia_list); + +#define UTP_RLOCK_LIST() mtx_lock(&utopia_list_mtx) +#define UTP_RUNLOCK_LIST() mtx_unlock(&utopia_list_mtx) +#define UTP_WLOCK_LIST() mtx_lock(&utopia_list_mtx) +#define UTP_WUNLOCK_LIST() mtx_unlock(&utopia_list_mtx) + +#define UTP_LOCK(UTP) mtx_lock((UTP)->lock) +#define UTP_UNLOCK(UTP) mtx_unlock((UTP)->lock) +#define UTP_LOCK_ASSERT(UTP) mtx_assert((UTP)->lock, MA_OWNED) + +static struct proc *utopia_kproc; + +static void utopia_dump(struct utopia *) __unused; + +/* + * Debugging - dump all registers. + */ +static void +utopia_dump(struct utopia *utp) +{ + uint8_t regs[256]; + u_int n = 256, i; + int err; + + if ((err = READREGS(utp, SUNI_REGO_MRESET, regs, &n)) != 0) { + printf("SUNI read error %d\n", err); + return; + } + for (i = 0; i < n; i++) { + if (i % 16 == 0) + printf("%02x:", i); + if (i % 16 == 8) + printf(" "); + printf(" %02x", regs[i]); + if (i % 16 == 15) + printf("\n"); + } + if (i % 16 != 0) + printf("\n"); +} + +/* + * Update the carrier status + */ +static void +utopia_check_carrier(struct utopia *utp, u_int carr_ok) +{ + int old; + + old = utp->carrier; + if (carr_ok) { + /* carrier */ + utp->carrier = UTP_CARR_OK; + if (old != UTP_CARR_OK) { + if_printf(&utp->ifatm->ifnet, "carrier detected\n"); + } + } else { + /* no carrier */ + utp->carrier = UTP_CARR_LOST; + if (old == UTP_CARR_OK) { + if_printf(&utp->ifatm->ifnet, "carrier lost\n"); + } + } +} + +static int +utopia_update_carrier_default(struct utopia *utp) +{ + int err; + uint8_t reg; + u_int n = 1; + + if ((err = READREGS(utp, SUNI_REGO_RSOPSIS, ®, &n)) != 0) { + utp->carrier = UTP_CARR_UNKNOWN; + return (err); + } + utopia_check_carrier(utp, !(reg & SUNI_REGM_RSOPSIS_LOSV)); + return (0); +} + +/* + * enable/disable scrambling + */ +static int +utopia_set_noscramb_default(struct utopia *utp, int noscramb) +{ + int err; + + if (noscramb) { + err = WRITEREG(utp, SUNI_REGO_TACPCTRL, + SUNI_REGM_TACPCTRL_DSCR, SUNI_REGM_TACPCTRL_DSCR); + if (err) + return (err); + err = WRITEREG(utp, SUNI_REGO_RACPCTRL, + SUNI_REGM_RACPCTRL_DDSCR, SUNI_REGM_RACPCTRL_DDSCR); + if (err) + return (err); + utp->state |= UTP_ST_NOSCRAMB; + } else { + err = WRITEREG(utp, SUNI_REGO_TACPCTRL, + SUNI_REGM_TACPCTRL_DSCR, 0); + if (err) + return (err); + err = WRITEREG(utp, SUNI_REGO_RACPCTRL, + SUNI_REGM_RACPCTRL_DDSCR, 0); + if (err) + return (err); + utp->state &= ~UTP_ST_NOSCRAMB; + } + return (0); +} + +/* + * set SONET/SDH mode + */ +static int +utopia_set_sdh_default(struct utopia *utp, int sdh) +{ + int err; + + if (sdh) + err = WRITEREG(utp, SUNI_REGO_TPOPAPTR + 1, + SUNI_REGM_TPOPAPTR_S, + SUNI_REGM_SDH << SUNI_REGS_TPOPAPTR_S); + else + err = WRITEREG(utp, SUNI_REGO_TPOPAPTR + 1, + SUNI_REGM_TPOPAPTR_S, + SUNI_REGM_SONET << SUNI_REGS_TPOPAPTR_S); + if (err != 0) + return (err); + + utp->state &= ~UTP_ST_SDH; + if (sdh) + utp->state |= UTP_ST_SDH; + + return (0); +} + +/* + * set idle/unassigned cells + */ +static int +utopia_set_unass_default(struct utopia *utp, int unass) +{ + int err; + + if (unass) + err = WRITEREG(utp, SUNI_REGO_TACPIDLEH, + 0xff, (0 << SUNI_REGS_TACPIDLEH_CLP)); + else + err = WRITEREG(utp, SUNI_REGO_TACPIDLEH, + 0xff, (1 << SUNI_REGS_TACPIDLEH_CLP)); + if (err != 0) + return (err); + + utp->state &= ~UTP_ST_UNASS; + if (unass) + utp->state |= UTP_ST_UNASS; + + return (0); +} + +/* + * Set loopback mode for the Lite + */ +static int +utopia_set_loopback_lite(struct utopia *utp, u_int mode) +{ + int err; + uint32_t val; + u_int nmode; + + val = 0; + nmode = mode; + if (mode & UTP_LOOP_TIME) { + nmode &= ~UTP_LOOP_TIME; + val |= SUNI_REGM_MCTRL_LOOPT; + } + if (mode & UTP_LOOP_DIAG) { + nmode &= ~UTP_LOOP_DIAG; + val |= SUNI_REGM_MCTRL_DLE; + } + if (mode & UTP_LOOP_LINE) { + nmode &= ~UTP_LOOP_LINE; + if (val & SUNI_REGM_MCTRL_DLE) + return (EINVAL); + val |= SUNI_REGM_MCTRL_LLE; + } + if (nmode != 0) + return (EINVAL); + + err = WRITEREG(utp, SUNI_REGO_MCTRL, + SUNI_REGM_MCTRL_LLE | SUNI_REGM_MCTRL_DLE | SUNI_REGM_MCTRL_LOOPT, + val); + if (err) + return (err); + utp->loopback = mode; + + return (0); +} + +/* + * Set loopback mode for the Ultra + */ +static int +utopia_set_loopback_ultra(struct utopia *utp, u_int mode) +{ + int err; + uint32_t val; + u_int nmode; + + val = 0; + nmode = mode; + if (mode & UTP_LOOP_TIME) { + nmode &= ~UTP_LOOP_TIME; + val |= SUNI_REGM_MCTRL_LOOPT; + } + if (mode & UTP_LOOP_DIAG) { + nmode &= ~UTP_LOOP_DIAG; + if (val & SUNI_REGM_MCTRL_LOOPT) + return (EINVAL); + val |= SUNI_REGM_MCTRL_SDLE; + } + if (mode & UTP_LOOP_LINE) { + nmode &= ~UTP_LOOP_LINE; + if (val & (SUNI_REGM_MCTRL_LOOPT | SUNI_REGM_MCTRL_SDLE)) + return (EINVAL); + val |= SUNI_REGM_MCTRL_LLE; + } + if (mode & UTP_LOOP_PARAL) { + nmode &= ~UTP_LOOP_PARAL; + val |= SUNI_REGM_MCTRL_PDLE; + } + if (mode & UTP_LOOP_TWIST) { + nmode &= ~UTP_LOOP_TWIST; + val |= SUNI_REGM_MCTRL_TPLE; + } + if (nmode != 0) + return (EINVAL); + + err = WRITEREG(utp, SUNI_REGO_MCTRL, + SUNI_REGM_MCTRL_LLE | SUNI_REGM_MCTRL_SDLE | SUNI_REGM_MCTRL_LOOPT | + SUNI_REGM_MCTRL_PDLE | SUNI_REGM_MCTRL_TPLE, val); + if (err) + return (err); + utp->loopback = mode; + + return (0); +} + +/* + * Set loopback mode for the Ultra + */ +static int +utopia_set_loopback_622(struct utopia *utp, u_int mode) +{ + int err; + uint32_t val; + uint8_t config; + int smode; + u_int nmode; + u_int n = 1; + + val = 0; + nmode = mode; + if (mode & UTP_LOOP_PATH) { + nmode &= ~UTP_LOOP_PATH; + val |= SUNI_REGM_MCTRLM_DPLE; + } + + err = READREGS(utp, SUNI_REGO_MCONFIG, &config, &n); + if (err != 0) + return (err); + smode = ((config & SUNI_REGM_MCONFIG_TMODE_622) == + SUNI_REGM_MCONFIG_TMODE_STS1_BIT && + (config & SUNI_REGM_MCONFIG_RMODE_622) == + SUNI_REGM_MCONFIG_RMODE_STS1_BIT); + + if (mode & UTP_LOOP_TIME) { + if (!smode) + return (EINVAL); + nmode &= ~UTP_LOOP_TIME; + val |= SUNI_REGM_MCTRLM_LOOPT; + } + if (mode & UTP_LOOP_DIAG) { + nmode &= ~UTP_LOOP_DIAG; + if (val & SUNI_REGM_MCTRLM_LOOPT) + return (EINVAL); + val |= SUNI_REGM_MCTRLM_DLE; + } + if (mode & UTP_LOOP_LINE) { + nmode &= ~UTP_LOOP_LINE; + if (val & (SUNI_REGM_MCTRLM_LOOPT | SUNI_REGM_MCTRLM_DLE)) + return (EINVAL); + val |= SUNI_REGM_MCTRLM_LLE; + } + if (nmode != 0) + return (EINVAL); + + err = WRITEREG(utp, SUNI_REGO_MCTRLM, + SUNI_REGM_MCTRLM_LLE | SUNI_REGM_MCTRLM_DLE | + SUNI_REGM_MCTRLM_DPLE | SUNI_REGM_MCTRL_LOOPT, val); + if (err) + return (err); + utp->loopback = mode; + + return (0); +} + +/* + * Set the SUNI chip to reflect the current state in utopia. + * Assume, that the chip has been reset. + */ +static int +utopia_set_chip(struct utopia *utp) +{ + int err = 0; + + /* set sonet/sdh */ + err |= utopia_set_sdh(utp, utp->state & UTP_ST_SDH); + + /* unassigned or idle cells */ + err |= utopia_set_unass(utp, utp->state & UTP_ST_UNASS); + err |= WRITEREG(utp, SUNI_REGO_TACPIDLEP, 0xff, 0x6a); + + /* loopback */ + err |= utopia_set_loopback(utp, utp->loopback); + + /* update carrier state */ + err |= utopia_update_carrier(utp); + + /* enable interrupts on LOS */ + err |= WRITEREG(utp, SUNI_REGO_RSOPCIE, + SUNI_REGM_RSOPCIE_LOSE, SUNI_REGM_RSOPCIE_LOSE); + + return (err ? EIO : 0); +} + +/* + * Reset the SUNI chip to reflect the current state of utopia. + */ +static int +utopia_reset_default(struct utopia *utp) +{ + int err = 0; + + if (!(utp->flags & UTP_FL_NORESET)) { + err |= WRITEREG(utp, SUNI_REGO_MRESET, SUNI_REGM_MRESET_RESET, + SUNI_REGM_MRESET_RESET); + err |= WRITEREG(utp, SUNI_REGO_MRESET, SUNI_REGM_MRESET_RESET, + 0); + } + + /* disable test mode */ + err |= WRITEREG(utp, SUNI_REGO_MTEST, 0xff, 0x00); + + err |= utopia_set_chip(utp); + + return (err ? EIO : 0); +} + +/* + * Reset the SUNI chip to reflect the current state of utopia. + */ +static int +utopia_reset_622(struct utopia *utp) +{ + int err = 0; + + if (!(utp->flags & UTP_FL_NORESET)) { + err |= WRITEREG(utp, SUNI_REGO_MRESET, SUNI_REGM_MRESET_RESET, + SUNI_REGM_MRESET_RESET); + err |= WRITEREG(utp, SUNI_REGO_MRESET, SUNI_REGM_MRESET_RESET, + 0); + } + + /* disable test mode */ + err |= WRITEREG(utp, SUNI_REGO_MTEST, 0xff, + SUNI_REGM_MTEST_DS27_53_622); + + err |= utopia_set_chip(utp); + + return (err ? EIO : 0); +} + +/* + * Handle interrupt on lite chip + */ +static void +utopia_intr_default(struct utopia *utp) +{ + uint8_t regs[SUNI_REGO_MTEST]; + u_int n = SUNI_REGO_MTEST; + int err; + + /* Read all registers. This acks the interrupts */ + if ((err = READREGS(utp, SUNI_REGO_MRESET, regs, &n)) != 0) { + printf("SUNI read error %d\n", err); + return; + } + if (n <= SUNI_REGO_RSOPSIS) { + printf("%s: could not read RSOPSIS", __func__); + return; + } + /* check for LOSI (loss of signal) */ + if ((regs[SUNI_REGO_MISTATUS] & SUNI_REGM_MISTATUS_RSOPI) && + (regs[SUNI_REGO_RSOPSIS] & SUNI_REGM_RSOPSIS_LOSI)) + utopia_check_carrier(utp, !(regs[SUNI_REGO_RSOPSIS] + & SUNI_REGM_RSOPSIS_LOSV)); +} + +static const struct utopia_chip chip_622 = { + UTP_TYPE_SUNI_622, + "Suni/622 (PMC-5355)", + 256, + utopia_reset_622, + utopia_set_sdh_default, + utopia_set_unass_default, + utopia_set_noscramb_default, + utopia_update_carrier_default, + utopia_set_loopback_622, + utopia_intr_default, +}; +static const struct utopia_chip chip_lite = { + UTP_TYPE_SUNI_LITE, + "Suni/Lite (PMC-5346)", + 256, + utopia_reset_default, + utopia_set_sdh_default, + utopia_set_unass_default, + utopia_set_noscramb_default, + utopia_update_carrier_default, + utopia_set_loopback_lite, + utopia_intr_default, +}; +static const struct utopia_chip chip_ultra = { + UTP_TYPE_SUNI_ULTRA, + "Suni/Ultra (PMC-5350)", + 256, + utopia_reset_default, + utopia_set_sdh_default, + utopia_set_unass_default, + utopia_set_noscramb_default, + utopia_update_carrier_default, + utopia_set_loopback_ultra, + utopia_intr_default, +}; + +/* + * Reset IDT77105. There is really no way to reset this thing by acessing + * the registers. Load the registers with default values. + */ +static int +idt77105_reset(struct utopia *utp) +{ + int err = 0; + u_int n; + uint8_t val[2]; + + err |= WRITEREG(utp, IDTPHY_REGO_MCR, 0xff, + IDTPHY_REGM_MCR_DRIC | IDTPHY_REGM_MCR_EI); + n = 1; + err |= READREGS(utp, IDTPHY_REGO_ISTAT, val, &n); + err |= WRITEREG(utp, IDTPHY_REGO_DIAG, 0xff, 0); + err |= WRITEREG(utp, IDTPHY_REGO_LHEC, 0xff, 0); + + err |= WRITEREG(utp, IDTPHY_REGO_CNTS, 0xff, IDTPHY_REGM_CNTS_SEC); + n = 2; + err |= READREGS(utp, IDTPHY_REGO_CNT, val, &n); + + err |= WRITEREG(utp, IDTPHY_REGO_CNTS, 0xff, IDTPHY_REGM_CNTS_TX); + n = 2; + err |= READREGS(utp, IDTPHY_REGO_CNT, val, &n); + + err |= WRITEREG(utp, IDTPHY_REGO_CNTS, 0xff, IDTPHY_REGM_CNTS_RX); + n = 2; + err |= READREGS(utp, IDTPHY_REGO_CNT, val, &n); + + err |= WRITEREG(utp, IDTPHY_REGO_CNTS, 0xff, IDTPHY_REGM_CNTS_HECE); + n = 2; + err |= READREGS(utp, IDTPHY_REGO_CNT, val, &n); + + err |= WRITEREG(utp, IDTPHY_REGO_MCR, IDTPHY_REGM_MCR_DREC, + IDTPHY_REGM_MCR_DREC); + err |= WRITEREG(utp, IDTPHY_REGO_DIAG, IDTPHY_REGM_DIAG_RFLUSH, + IDTPHY_REGM_DIAG_RFLUSH); + + /* loopback */ + err |= utopia_set_loopback(utp, utp->loopback); + + /* update carrier state */ + err |= utopia_update_carrier(utp); + + return (err ? EIO : 0); +} + +static int +unknown_inval(struct utopia *utp, int what __unused) +{ + return (EINVAL); +} + +static int +idt77105_update_carrier(struct utopia *utp) +{ + int err; + uint8_t reg; + u_int n = 1; + + if ((err = READREGS(utp, IDTPHY_REGO_ISTAT, ®, &n)) != 0) { + utp->carrier = UTP_CARR_UNKNOWN; + return (err); + } + utopia_check_carrier(utp, reg & IDTPHY_REGM_ISTAT_GOOD); + return (0); +} + +static int +idt77105_set_loopback(struct utopia *utp, u_int mode) +{ + int err; + + switch (mode) { + case UTP_LOOP_NONE: + err = WRITEREG(utp, IDTPHY_REGO_DIAG, + IDTPHY_REGM_DIAG_LOOP, IDTPHY_REGM_DIAG_LOOP_NONE); + break; + + case UTP_LOOP_DIAG: + err = WRITEREG(utp, IDTPHY_REGO_DIAG, + IDTPHY_REGM_DIAG_LOOP, IDTPHY_REGM_DIAG_LOOP_PHY); + break; + + case UTP_LOOP_LINE: + err = WRITEREG(utp, IDTPHY_REGO_DIAG, + IDTPHY_REGM_DIAG_LOOP, IDTPHY_REGM_DIAG_LOOP_LINE); + break; + + default: + return (EINVAL); + } + if (err) + return (err); + utp->loopback = mode; + return (0); +} + +/* + * Handle interrupt on IDT77105 chip + */ +static void +idt77105_intr(struct utopia *utp) +{ + uint8_t reg; + u_int n = 1; + int err; + + /* Interrupt status and ack the interrupt */ + if ((err = READREGS(utp, IDTPHY_REGO_ISTAT, ®, &n)) != 0) { + printf("IDT77105 read error %d\n", err); + return; + } + /* check for signal condition */ + utopia_check_carrier(utp, reg & IDTPHY_REGM_ISTAT_GOOD); +} + +static const struct utopia_chip chip_idt77105 = { + UTP_TYPE_IDT77105, + "IDT77105", + 7, + idt77105_reset, + unknown_inval, + unknown_inval, + unknown_inval, + idt77105_update_carrier, + idt77105_set_loopback, + idt77105_intr, +}; + +static int +unknown_reset(struct utopia *utp __unused) +{ + return (EIO); +} + +static int +unknown_update_carrier(struct utopia *utp) +{ + utp->carrier = UTP_CARR_UNKNOWN; + return (0); +} + +static int +unknown_set_loopback(struct utopia *utp __unused, u_int mode __unused) +{ + return (EINVAL); +} + +static void +unknown_intr(struct utopia *utp __unused) +{ +} + +static const struct utopia_chip chip_unknown = { + UTP_TYPE_UNKNOWN, + "unknown", + 0, + unknown_reset, + unknown_inval, + unknown_inval, + unknown_inval, + unknown_update_carrier, + unknown_set_loopback, + unknown_intr, +}; + +/* + * Callbacks for the ifmedia infrastructure. + */ +static int +utopia_media_change(struct ifnet *ifp) +{ + struct ifatm *ifatm = (struct ifatm *)ifp->if_softc; + struct utopia *utp = ifatm->phy; + int error = 0; + + UTP_LOCK(utp); + if (utp->chip->type != UTP_TYPE_UNKNOWN && utp->state & UTP_ST_ACTIVE) { + if (utp->media->ifm_media & IFM_ATM_SDH) { + if (!(utp->state & UTP_ST_SDH)) + error = utopia_set_sdh(utp, 1); + } else { + if (utp->state & UTP_ST_SDH) + error = utopia_set_sdh(utp, 0); + } + if (utp->media->ifm_media & IFM_ATM_UNASSIGNED) { + if (!(utp->state & UTP_ST_UNASS)) + error = utopia_set_unass(utp, 1); + } else { + if (utp->state & UTP_ST_UNASS) + error = utopia_set_unass(utp, 0); + } + if (utp->media->ifm_media & IFM_ATM_NOSCRAMB) { + if (!(utp->state & UTP_ST_NOSCRAMB)) + error = utopia_set_noscramb(utp, 1); + } else { + if (utp->state & UTP_ST_NOSCRAMB) + error = utopia_set_noscramb(utp, 0); + } + } else + error = EIO; + UTP_UNLOCK(utp); + return (error); +} + +/* + * Look at the carrier status. + */ +static void +utopia_media_status(struct ifnet *ifp, struct ifmediareq *ifmr) +{ + struct utopia *utp = ((struct ifatm *)ifp->if_softc)->phy; + + UTP_LOCK(utp); + if (utp->chip->type != UTP_TYPE_UNKNOWN && utp->state & UTP_ST_ACTIVE) { + ifmr->ifm_active = IFM_ATM | utp->ifatm->mib.media; + + switch (utp->carrier) { + + case UTP_CARR_OK: + ifmr->ifm_status = IFM_AVALID | IFM_ACTIVE; + break; + + case UTP_CARR_LOST: + ifmr->ifm_status = IFM_AVALID; + break; + + default: + ifmr->ifm_status = 0; + break; + } + if (utp->state & UTP_ST_SDH) { + ifmr->ifm_active |= IFM_ATM_SDH; + ifmr->ifm_current |= IFM_ATM_SDH; + } + if (utp->state & UTP_ST_UNASS) { + ifmr->ifm_active |= IFM_ATM_UNASSIGNED; + ifmr->ifm_current |= IFM_ATM_UNASSIGNED; + } + if (utp->state & UTP_ST_NOSCRAMB) { + ifmr->ifm_active |= IFM_ATM_NOSCRAMB; + ifmr->ifm_current |= IFM_ATM_NOSCRAMB; + } + } else { + ifmr->ifm_active = 0; + ifmr->ifm_status = 0; + } + UTP_UNLOCK(utp); +} + +/* + * Initialize media from the mib + */ +void +utopia_init_media(struct utopia *utp) +{ + + ifmedia_removeall(utp->media); + ifmedia_add(utp->media, IFM_ATM | utp->ifatm->mib.media, 0, NULL); + ifmedia_set(utp->media, IFM_ATM | utp->ifatm->mib.media); +} + +/* + * Reset all media + */ +void +utopia_reset_media(struct utopia *utp) +{ + + ifmedia_removeall(utp->media); +} + +/* + * This is called by the driver as soon as the SUNI registers are accessible. + * This may be either in the attach routine or the init routine of the driver. + */ +int +utopia_start(struct utopia *utp) +{ + uint8_t reg; + int err; + u_int n = 1; + + if ((err = READREGS(utp, SUNI_REGO_MRESET, ®, &n)) != 0) + return (err); + + switch (reg & SUNI_REGM_MRESET_TYPE) { + + case SUNI_REGM_MRESET_TYPE_622: + utp->chip = &chip_622; + break; + + case SUNI_REGM_MRESET_TYPE_LITE: + utp->chip = &chip_lite; + break; + + case SUNI_REGM_MRESET_TYPE_ULTRA: + utp->chip = &chip_ultra; + break; + + default: + if (reg == (IDTPHY_REGM_MCR_DRIC | IDTPHY_REGM_MCR_EI)) + utp->chip = &chip_idt77105; + else { + if_printf(&utp->ifatm->ifnet, + "unknown ATM-PHY chip %#x\n", reg); + utp->chip = &chip_unknown; + } + break; + } + utp->state |= UTP_ST_ACTIVE; + return (0); +} + +/* + * Stop the chip + */ +void +utopia_stop(struct utopia *utp) +{ + utp->state &= ~UTP_ST_ACTIVE; +} + +/* + * Handle the sysctls + */ +static int +utopia_sysctl_regs(SYSCTL_HANDLER_ARGS) +{ + struct utopia *utp = (struct utopia *)arg1; + int error; + u_int n; + uint8_t *val; + uint8_t new[3]; + + if ((n = utp->chip->nregs) == 0) + return (EIO); + val = malloc(sizeof(uint8_t) * n, M_TEMP, M_WAITOK); + + UTP_LOCK(utp); + error = READREGS(utp, 0, val, &n); + UTP_UNLOCK(utp); + + if (error) { + free(val, M_TEMP); + return (error); + } + + error = SYSCTL_OUT(req, val, sizeof(uint8_t) * n); + free(val, M_TEMP); + if (error != 0 || req->newptr == NULL) + return (error); + + error = SYSCTL_IN(req, new, sizeof(new)); + if (error) + return (error); + + UTP_LOCK(utp); + error = WRITEREG(utp, new[0], new[1], new[2]); + UTP_UNLOCK(utp); + + return (error); +} + +/* + * Handle the loopback sysctl + */ +static int +utopia_sysctl_loopback(SYSCTL_HANDLER_ARGS) +{ + struct utopia *utp = (struct utopia *)arg1; + int error; + u_int loopback; + + error = SYSCTL_OUT(req, &utp->loopback, sizeof(u_int)); + if (error != 0 || req->newptr == NULL) + return (error); + + error = SYSCTL_IN(req, &loopback, sizeof(u_int)); + if (error) + return (error); + + UTP_LOCK(utp); + error = utopia_set_loopback(utp, loopback); + UTP_UNLOCK(utp); + + return (error); +} + +/* + * Handle the type sysctl + */ +static int +utopia_sysctl_type(SYSCTL_HANDLER_ARGS) +{ + struct utopia *utp = (struct utopia *)arg1; + + return (SYSCTL_OUT(req, &utp->chip->type, sizeof(utp->chip->type))); +} + +/* + * Handle the name sysctl + */ +static int +utopia_sysctl_name(SYSCTL_HANDLER_ARGS) +{ + struct utopia *utp = (struct utopia *)arg1; + + return (SYSCTL_OUT(req, utp->chip->name, strlen(utp->chip->name) + 1)); +} + +/* + * Initialize the state. This is called from the drivers attach + * function. The mutex must be already initialized. + */ +int +utopia_attach(struct utopia *utp, struct ifatm *ifatm, struct ifmedia *media, + struct mtx *lock, struct sysctl_ctx_list *ctx, + struct sysctl_oid_list *children, const struct utopia_methods *m) +{ + + bzero(utp, sizeof(*utp)); + utp->ifatm = ifatm; + utp->methods = m; + utp->media = media; + utp->lock = lock; + utp->chip = &chip_unknown; + + ifmedia_init(media, + IFM_ATM_SDH | IFM_ATM_UNASSIGNED | IFM_ATM_NOSCRAMB, + utopia_media_change, utopia_media_status); + + if (SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "phy_regs", + CTLFLAG_RW | CTLTYPE_OPAQUE, utp, 0, utopia_sysctl_regs, "S", + "phy registers") == NULL) + return (-1); + + if (SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "phy_loopback", + CTLFLAG_RW | CTLTYPE_UINT, utp, 0, utopia_sysctl_loopback, "IU", + "phy loopback mode") == NULL) + return (-1); + + if (SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "phy_type", + CTLFLAG_RD | CTLTYPE_UINT, utp, 0, utopia_sysctl_type, "IU", + "phy type") == NULL) + return (-1); + + if (SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "phy_name", + CTLFLAG_RD | CTLTYPE_STRING, utp, 0, utopia_sysctl_name, "A", + "phy name") == NULL) + return (-1); + + UTP_WLOCK_LIST(); + LIST_INSERT_HEAD(&utopia_list, utp, link); + UTP_WUNLOCK_LIST(); + + utp->state |= UTP_ST_ATTACHED; + return (0); +} + +/* + * Detach. We set a flag here, wakeup the daemon and let him do it. + * Here we need the lock for synchronisation with the daemon. + */ +void +utopia_detach(struct utopia *utp) +{ + + UTP_LOCK_ASSERT(utp); + if (utp->state & UTP_ST_ATTACHED) { + utp->state |= UTP_ST_DETACH; + while (utp->state & UTP_ST_DETACH) { + wakeup(&utopia_list); + msleep(utp, utp->lock, PZERO, "utopia_detach", hz); + } + } +} + +/* + * The carrier state kernel proc for those adapters that do not interrupt. + * + * We assume, that utopia_attach can safely add a new utopia while we are going + * through the list without disturbing us (we lock the list while getting + * the address of the first element, adding is always done at the head). + * Removing is entirely handled here. + */ +static void +utopia_daemon(void *arg __unused) +{ + struct utopia *utp, *next; + + UTP_RLOCK_LIST(); + while (utopia_kproc != NULL) { + utp = LIST_FIRST(&utopia_list); + UTP_RUNLOCK_LIST(); + + while (utp != NULL) { + mtx_lock(&Giant); /* XXX depend on MPSAFE */ + UTP_LOCK(utp); + next = LIST_NEXT(utp, link); + if (utp->state & UTP_ST_DETACH) { + LIST_REMOVE(utp, link); + utp->state &= ~UTP_ST_DETACH; + wakeup_one(utp); + } else if ((utp->state & UTP_ST_ACTIVE) && + (utp->flags & UTP_FL_POLL_CARRIER)) { + utopia_update_carrier(utp); + } + UTP_UNLOCK(utp); + mtx_unlock(&Giant); /* XXX depend on MPSAFE */ + utp = next; + } + + UTP_RLOCK_LIST(); + msleep(&utopia_list, &utopia_list_mtx, PZERO, "utopia", hz); + } + wakeup_one(&utopia_list); + UTP_RUNLOCK_LIST(); + mtx_lock(&Giant); + kthread_exit(0); +} + +/* + * Module initialisation + */ +static int +utopia_mod_init(module_t mod, int what, void *arg) +{ + int err; + struct proc *kp; + + switch (what) { + + case MOD_LOAD: + mtx_init(&utopia_list_mtx, "utopia list mutex", NULL, MTX_DEF); + err = kthread_create(utopia_daemon, NULL, &utopia_kproc, + RFHIGHPID, 0, "utopia"); + if (err != 0) { + printf("cannot created utopia thread %d\n", err); + return (err); + } + break; + + case MOD_UNLOAD: + UTP_WLOCK_LIST(); + if ((kp = utopia_kproc) != NULL) { + utopia_kproc = NULL; + wakeup_one(&utopia_list); + PROC_LOCK(kp); + UTP_WUNLOCK_LIST(); + msleep(kp, &kp->p_mtx, PWAIT, "utopia_destroy", 0); + PROC_UNLOCK(kp); + } else + UTP_WUNLOCK_LIST(); + mtx_destroy(&utopia_list_mtx); + break; + } + return (0); +} + +static moduledata_t utopia_mod = { + "utopia", + utopia_mod_init, + 0 +}; + +DECLARE_MODULE(utopia, utopia_mod, SI_SUB_INIT_IF, SI_ORDER_ANY); +MODULE_VERSION(utopia, 1); |