/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" #include "rge.h" #define RGE_DBG RGE_DBG_STATS /* debug flag for this code */ /* * Local datatype for defining tables of (Offset, Name) pairs */ typedef struct { offset_t index; char *name; } rge_ksindex_t; static int rge_params_update(kstat_t *ksp, int flag) { rge_t *rgep; kstat_named_t *knp; int i; if (flag != KSTAT_READ) return (EACCES); rgep = ksp->ks_private; for (knp = ksp->ks_data, i = 0; i < PARAM_COUNT; ++knp, ++i) knp->value.ui64 = rgep->nd_params[i].ndp_val; return (0); } static const rge_ksindex_t rge_driverinfo[] = { { 0, "rx_ring_addr" }, { 1, "rx_next" }, { 2, "rx_free" }, { 3, "rx_bcopy" }, { 4, "tx_ring_addr" }, { 5, "tx_next" }, { 6, "tx_free" }, { 7, "tx_flow" }, { 8, "resched_needed" }, { 9, "watchdog" }, { 10, "rx_config" }, { 11, "tx_config" }, { 12, "mac_ver" }, { 13, "phy_ver" }, { 14, "chip_reset" }, { 15, "phy_reset" }, { -1, NULL } }; static int rge_driverinfo_update(kstat_t *ksp, int flag) { rge_t *rgep; kstat_named_t *knp; if (flag != KSTAT_READ) return (EACCES); rgep = ksp->ks_private; knp = ksp->ks_data; (knp++)->value.ui64 = rgep->dma_area_rxdesc.cookie.dmac_laddress; (knp++)->value.ui64 = rgep->rx_next; (knp++)->value.ui64 = rgep->rx_free; (knp++)->value.ui64 = rgep->rx_bcopy; (knp++)->value.ui64 = rgep->dma_area_txdesc.cookie.dmac_laddress; (knp++)->value.ui64 = rgep->tx_next; (knp++)->value.ui64 = rgep->tx_free; (knp++)->value.ui64 = rgep->tx_flow; (knp++)->value.ui64 = rgep->resched_needed; (knp++)->value.ui64 = rgep->watchdog; (knp++)->value.ui64 = rgep->chipid.rxconfig; (knp++)->value.ui64 = rgep->chipid.txconfig; (knp++)->value.ui64 = rgep->chipid.mac_ver; (knp++)->value.ui64 = rgep->chipid.phy_ver; (knp++)->value.ui64 = rgep->stats.chip_reset; (knp++)->value.ui64 = rgep->stats.phy_reset; return (0); } static kstat_t * rge_setup_named_kstat(rge_t *rgep, int instance, char *name, const rge_ksindex_t *ksip, size_t size, int (*update)(kstat_t *, int)) { kstat_t *ksp; kstat_named_t *knp; char *np; int type; size /= sizeof (rge_ksindex_t); ksp = kstat_create(RGE_DRIVER_NAME, instance, name, "net", KSTAT_TYPE_NAMED, size-1, KSTAT_FLAG_PERSISTENT); if (ksp == NULL) return (NULL); ksp->ks_private = rgep; ksp->ks_update = update; for (knp = ksp->ks_data; (np = ksip->name) != NULL; ++knp, ++ksip) { switch (*np) { default: type = KSTAT_DATA_UINT64; break; case '%': np += 1; type = KSTAT_DATA_UINT32; break; case '$': np += 1; type = KSTAT_DATA_STRING; break; case '&': np += 1; type = KSTAT_DATA_CHAR; break; } kstat_named_init(knp, np, type); } kstat_install(ksp); return (ksp); } /* * Create kstats corresponding to NDD parameters */ static kstat_t * rge_setup_params_kstat(rge_t *rgep, int instance, char *name, int (*update)(kstat_t *, int)) { kstat_t *ksp; kstat_named_t *knp; int i; ksp = kstat_create(RGE_DRIVER_NAME, instance, name, "net", KSTAT_TYPE_NAMED, PARAM_COUNT, KSTAT_FLAG_PERSISTENT); if (ksp != NULL) { ksp->ks_private = rgep; ksp->ks_update = update; for (knp = ksp->ks_data, i = 0; i < PARAM_COUNT; ++knp, ++i) kstat_named_init(knp, rgep->nd_params[i].ndp_name+1, KSTAT_DATA_UINT64); kstat_install(ksp); } return (ksp); } void rge_init_kstats(rge_t *rgep, int instance) { rgep->rge_kstats[RGE_KSTAT_PARAMS] = rge_setup_params_kstat(rgep, instance, "parameters", rge_params_update); rgep->rge_kstats[RGE_KSTAT_DRIVER] = rge_setup_named_kstat(rgep, instance, "driverinfo", rge_driverinfo, sizeof (rge_driverinfo), rge_driverinfo_update); } void rge_fini_kstats(rge_t *rgep) { int i; for (i = RGE_KSTAT_COUNT; --i >= 0; ) if (rgep->rge_kstats[i] != NULL) { kstat_delete(rgep->rge_kstats[i]); } } int rge_m_stat(void *arg, uint_t stat, uint64_t *val) { rge_t *rgep = arg; rge_hw_stats_t *bstp; mutex_enter(rgep->genlock); rge_hw_stats_dump(rgep); mutex_exit(rgep->genlock); bstp = rgep->hw_stats; switch (stat) { case MAC_STAT_IFSPEED: *val = rgep->param_link_speed * 1000000ull; break; case MAC_STAT_MULTIRCV: *val = RGE_BSWAP_32(bstp->multi_rcv); break; case MAC_STAT_BRDCSTRCV: *val = RGE_BSWAP_64(bstp->brdcst_rcv); break; case MAC_STAT_NORCVBUF: *val = RGE_BSWAP_16(bstp->in_discards); break; case MAC_STAT_IERRORS: *val = RGE_BSWAP_32(bstp->rcv_err); break; case MAC_STAT_OERRORS: *val = RGE_BSWAP_64(bstp->xmt_err); break; case MAC_STAT_COLLISIONS: *val = RGE_BSWAP_32(bstp->xmt_1col + bstp->xmt_mcol); break; case MAC_STAT_RBYTES: *val = rgep->stats.rbytes; break; case MAC_STAT_IPACKETS: *val = RGE_BSWAP_64(bstp->rcv_ok); break; case MAC_STAT_OBYTES: *val = rgep->stats.obytes; break; case MAC_STAT_OPACKETS: *val = RGE_BSWAP_64(bstp->xmt_ok); break; case ETHER_STAT_ALIGN_ERRORS: *val = RGE_BSWAP_16(bstp->frame_err); break; case ETHER_STAT_FIRST_COLLISIONS: *val = RGE_BSWAP_32(bstp->xmt_1col); break; case ETHER_STAT_MULTI_COLLISIONS: *val = RGE_BSWAP_32(bstp->xmt_mcol); break; case ETHER_STAT_DEFER_XMTS: *val = rgep->stats.defer; break; case ETHER_STAT_XCVR_ADDR: *val = rgep->phy_mii_addr; break; case ETHER_STAT_XCVR_ID: mutex_enter(rgep->genlock); *val = rge_mii_get16(rgep, MII_PHYIDH); *val <<= 16; *val |= rge_mii_get16(rgep, MII_PHYIDL); mutex_exit(rgep->genlock); break; case ETHER_STAT_XCVR_INUSE: *val = XCVR_1000T; break; case ETHER_STAT_CAP_1000FDX: *val = 1; break; case ETHER_STAT_CAP_1000HDX: *val = 0; break; case ETHER_STAT_CAP_100FDX: *val = 1; break; case ETHER_STAT_CAP_100HDX: *val = 1; break; case ETHER_STAT_CAP_10FDX: *val = 1; break; case ETHER_STAT_CAP_10HDX: *val = 1; break; case ETHER_STAT_CAP_ASMPAUSE: *val = 1; break; case ETHER_STAT_CAP_PAUSE: *val = 1; break; case ETHER_STAT_CAP_AUTONEG: *val = 1; break; case ETHER_STAT_ADV_CAP_1000FDX: *val = rgep->param_adv_1000fdx; break; case ETHER_STAT_ADV_CAP_1000HDX: *val = rgep->param_adv_1000hdx; break; case ETHER_STAT_ADV_CAP_100FDX: *val = rgep->param_adv_100fdx; break; case ETHER_STAT_ADV_CAP_100HDX: *val = rgep->param_adv_100hdx; break; case ETHER_STAT_ADV_CAP_10FDX: *val = rgep->param_adv_10fdx; break; case ETHER_STAT_ADV_CAP_10HDX: *val = rgep->param_adv_10hdx; break; case ETHER_STAT_ADV_CAP_ASMPAUSE: *val = rgep->param_adv_asym_pause; break; case ETHER_STAT_ADV_CAP_PAUSE: *val = rgep->param_adv_pause; break; case ETHER_STAT_ADV_CAP_AUTONEG: *val = rgep->param_adv_autoneg; break; case ETHER_STAT_LINK_DUPLEX: *val = rgep->param_link_duplex; break; default: return (ENOTSUP); } return (0); }