1155093Smarius/* $NetBSD: lance.c,v 1.34 2005/12/24 20:27:30 perry Exp $ */ 2155093Smarius 3155093Smarius/*- 4155093Smarius * Copyright (c) 1997, 1998 The NetBSD Foundation, Inc. 5155093Smarius * All rights reserved. 6155093Smarius * 7155093Smarius * This code is derived from software contributed to The NetBSD Foundation 8155093Smarius * by Charles M. Hannum and by Jason R. Thorpe of the Numerical Aerospace 9155093Smarius * Simulation Facility, NASA Ames Research Center. 10155093Smarius * 11155093Smarius * Redistribution and use in source and binary forms, with or without 12155093Smarius * modification, are permitted provided that the following conditions 13155093Smarius * are met: 14155093Smarius * 1. Redistributions of source code must retain the above copyright 15155093Smarius * notice, this list of conditions and the following disclaimer. 16155093Smarius * 2. Redistributions in binary form must reproduce the above copyright 17155093Smarius * notice, this list of conditions and the following disclaimer in the 18155093Smarius * documentation and/or other materials provided with the distribution. 19155093Smarius * 20155093Smarius * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 21155093Smarius * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22155093Smarius * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23155093Smarius * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 24155093Smarius * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25155093Smarius * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26155093Smarius * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27155093Smarius * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28155093Smarius * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29155093Smarius * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30155093Smarius * POSSIBILITY OF SUCH DAMAGE. 31155093Smarius */ 32155093Smarius 33155093Smarius/*- 34155093Smarius * Copyright (c) 1992, 1993 35155093Smarius * The Regents of the University of California. All rights reserved. 36155093Smarius * 37155093Smarius * This code is derived from software contributed to Berkeley by 38155093Smarius * Ralph Campbell and Rick Macklem. 39155093Smarius * 40155093Smarius * Redistribution and use in source and binary forms, with or without 41155093Smarius * modification, are permitted provided that the following conditions 42155093Smarius * are met: 43155093Smarius * 1. Redistributions of source code must retain the above copyright 44155093Smarius * notice, this list of conditions and the following disclaimer. 45155093Smarius * 2. Redistributions in binary form must reproduce the above copyright 46155093Smarius * notice, this list of conditions and the following disclaimer in the 47155093Smarius * documentation and/or other materials provided with the distribution. 48155093Smarius * 3. Neither the name of the University nor the names of its contributors 49155093Smarius * may be used to endorse or promote products derived from this software 50155093Smarius * without specific prior written permission. 51155093Smarius * 52155093Smarius * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 53155093Smarius * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 54155093Smarius * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 55155093Smarius * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 56155093Smarius * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 57155093Smarius * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 58155093Smarius * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 59155093Smarius * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 60155093Smarius * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 61155093Smarius * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 62155093Smarius * SUCH DAMAGE. 63155093Smarius * 64155093Smarius * @(#)if_le.c 8.2 (Berkeley) 11/16/93 65155093Smarius */ 66155093Smarius 67155093Smarius#include <sys/cdefs.h> 68155093Smarius__FBSDID("$FreeBSD$"); 69155093Smarius 70155093Smarius#include <sys/param.h> 71155093Smarius#include <sys/bus.h> 72155093Smarius#include <sys/endian.h> 73155093Smarius#include <sys/lock.h> 74164933Smarius#include <sys/kernel.h> 75155093Smarius#include <sys/mbuf.h> 76155093Smarius#include <sys/mutex.h> 77155093Smarius#include <sys/socket.h> 78155093Smarius#include <sys/sockio.h> 79155093Smarius 80155093Smarius#include <net/ethernet.h> 81155093Smarius#include <net/if.h> 82155093Smarius#include <net/if_arp.h> 83155093Smarius#include <net/if_dl.h> 84155093Smarius#include <net/if_media.h> 85155093Smarius#include <net/if_types.h> 86155093Smarius#include <net/if_vlan_var.h> 87155093Smarius 88158663Smarius#include <machine/bus.h> 89158663Smarius 90155093Smarius#include <dev/le/lancereg.h> 91155093Smarius#include <dev/le/lancevar.h> 92155093Smarius 93155093Smariusdevclass_t le_devclass; 94155093Smarius 95155093Smariusstatic void lance_start(struct ifnet *); 96155093Smariusstatic void lance_stop(struct lance_softc *); 97155093Smariusstatic void lance_init(void *); 98164933Smariusstatic void lance_watchdog(void *s); 99155093Smariusstatic int lance_mediachange(struct ifnet *); 100155093Smariusstatic void lance_mediastatus(struct ifnet *, struct ifmediareq *); 101155093Smariusstatic int lance_ioctl(struct ifnet *, u_long, caddr_t); 102155093Smarius 103155093Smariusint 104155093Smariuslance_config(struct lance_softc *sc, const char* name, int unit) 105155093Smarius{ 106155093Smarius struct ifnet *ifp; 107155093Smarius int i, nbuf; 108155093Smarius 109155093Smarius if (LE_LOCK_INITIALIZED(sc) == 0) 110155093Smarius return (ENXIO); 111155093Smarius 112155093Smarius ifp = sc->sc_ifp = if_alloc(IFT_ETHER); 113155093Smarius if (ifp == NULL) 114155093Smarius return (ENOSPC); 115155093Smarius 116164933Smarius callout_init_mtx(&sc->sc_wdog_ch, &sc->sc_mtx, 0); 117164933Smarius 118155093Smarius /* Initialize ifnet structure. */ 119155093Smarius ifp->if_softc = sc; 120155093Smarius if_initname(ifp, name, unit); 121155093Smarius ifp->if_start = lance_start; 122155093Smarius ifp->if_ioctl = lance_ioctl; 123155093Smarius ifp->if_init = lance_init; 124155093Smarius ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 125155093Smarius#ifdef LANCE_REVC_BUG 126155093Smarius ifp->if_flags &= ~IFF_MULTICAST; 127155093Smarius#endif 128155093Smarius ifp->if_baudrate = IF_Mbps(10); 129207554Ssobomax IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); 130207554Ssobomax ifp->if_snd.ifq_drv_maxlen = ifqmaxlen; 131155093Smarius IFQ_SET_READY(&ifp->if_snd); 132155093Smarius 133155093Smarius /* Initialize ifmedia structures. */ 134155093Smarius ifmedia_init(&sc->sc_media, 0, lance_mediachange, lance_mediastatus); 135155093Smarius if (sc->sc_supmedia != NULL) { 136155093Smarius for (i = 0; i < sc->sc_nsupmedia; i++) 137155093Smarius ifmedia_add(&sc->sc_media, sc->sc_supmedia[i], 0, NULL); 138155093Smarius ifmedia_set(&sc->sc_media, sc->sc_defaultmedia); 139155093Smarius } else { 140155093Smarius ifmedia_add(&sc->sc_media, 141155093Smarius IFM_MAKEWORD(IFM_ETHER, IFM_MANUAL, 0, 0), 0, NULL); 142155093Smarius ifmedia_set(&sc->sc_media, 143155093Smarius IFM_MAKEWORD(IFM_ETHER, IFM_MANUAL, 0, 0)); 144155093Smarius } 145155093Smarius 146155093Smarius switch (sc->sc_memsize) { 147155093Smarius case 8192: 148155093Smarius sc->sc_nrbuf = 4; 149155093Smarius sc->sc_ntbuf = 1; 150155093Smarius break; 151155093Smarius case 16384: 152155093Smarius sc->sc_nrbuf = 8; 153155093Smarius sc->sc_ntbuf = 2; 154155093Smarius break; 155155093Smarius case 32768: 156155093Smarius sc->sc_nrbuf = 16; 157155093Smarius sc->sc_ntbuf = 4; 158155093Smarius break; 159155093Smarius case 65536: 160155093Smarius sc->sc_nrbuf = 32; 161155093Smarius sc->sc_ntbuf = 8; 162155093Smarius break; 163155093Smarius case 131072: 164155093Smarius sc->sc_nrbuf = 64; 165155093Smarius sc->sc_ntbuf = 16; 166155093Smarius break; 167155093Smarius case 262144: 168155093Smarius sc->sc_nrbuf = 128; 169155093Smarius sc->sc_ntbuf = 32; 170155093Smarius break; 171155093Smarius default: 172155093Smarius /* weird memory size; cope with it */ 173155093Smarius nbuf = sc->sc_memsize / LEBLEN; 174155093Smarius sc->sc_ntbuf = nbuf / 5; 175155093Smarius sc->sc_nrbuf = nbuf - sc->sc_ntbuf; 176155093Smarius } 177155093Smarius 178155093Smarius if_printf(ifp, "%d receive buffers, %d transmit buffers\n", 179155093Smarius sc->sc_nrbuf, sc->sc_ntbuf); 180155093Smarius 181155093Smarius /* Make sure the chip is stopped. */ 182155093Smarius LE_LOCK(sc); 183155093Smarius lance_stop(sc); 184155093Smarius LE_UNLOCK(sc); 185155093Smarius 186155093Smarius return (0); 187155093Smarius} 188155093Smarius 189155093Smariusvoid 190155093Smariuslance_attach(struct lance_softc *sc) 191155093Smarius{ 192155093Smarius struct ifnet *ifp = sc->sc_ifp; 193155093Smarius 194155093Smarius /* Attach the interface. */ 195155093Smarius ether_ifattach(ifp, sc->sc_enaddr); 196155093Smarius 197155093Smarius /* Claim 802.1q capability. */ 198155093Smarius ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header); 199155093Smarius ifp->if_capabilities |= IFCAP_VLAN_MTU; 200155093Smarius ifp->if_capenable |= IFCAP_VLAN_MTU; 201155093Smarius} 202155093Smarius 203155093Smariusvoid 204155093Smariuslance_detach(struct lance_softc *sc) 205155093Smarius{ 206155093Smarius struct ifnet *ifp = sc->sc_ifp; 207155093Smarius 208155093Smarius LE_LOCK(sc); 209155093Smarius lance_stop(sc); 210155093Smarius LE_UNLOCK(sc); 211164933Smarius callout_drain(&sc->sc_wdog_ch); 212155093Smarius ether_ifdetach(ifp); 213155093Smarius if_free(ifp); 214155093Smarius} 215155093Smarius 216155093Smariusvoid 217155093Smariuslance_suspend(struct lance_softc *sc) 218155093Smarius{ 219155093Smarius 220155093Smarius LE_LOCK(sc); 221155093Smarius lance_stop(sc); 222155093Smarius LE_UNLOCK(sc); 223155093Smarius} 224155093Smarius 225155093Smariusvoid 226155093Smariuslance_resume(struct lance_softc *sc) 227155093Smarius{ 228155093Smarius 229155093Smarius LE_LOCK(sc); 230155093Smarius if (sc->sc_ifp->if_flags & IFF_UP) 231155093Smarius lance_init_locked(sc); 232155093Smarius LE_UNLOCK(sc); 233155093Smarius} 234155093Smarius 235155093Smariusstatic void 236155093Smariuslance_start(struct ifnet *ifp) 237155093Smarius{ 238155093Smarius struct lance_softc *sc = ifp->if_softc; 239155093Smarius 240155093Smarius LE_LOCK(sc); 241155093Smarius (*sc->sc_start_locked)(sc); 242155093Smarius LE_UNLOCK(sc); 243155093Smarius} 244155093Smarius 245155093Smariusstatic void 246155093Smariuslance_stop(struct lance_softc *sc) 247155093Smarius{ 248155093Smarius struct ifnet *ifp = sc->sc_ifp; 249155093Smarius 250155093Smarius LE_LOCK_ASSERT(sc, MA_OWNED); 251155093Smarius 252155093Smarius /* 253155093Smarius * Mark the interface down and cancel the watchdog timer. 254155093Smarius */ 255155093Smarius ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); 256164933Smarius callout_stop(&sc->sc_wdog_ch); 257164933Smarius sc->sc_wdog_timer = 0; 258155093Smarius 259155093Smarius (*sc->sc_wrcsr)(sc, LE_CSR0, LE_C0_STOP); 260155093Smarius} 261155093Smarius 262155093Smariusstatic void 263155093Smariuslance_init(void *xsc) 264155093Smarius{ 265155093Smarius struct lance_softc *sc = (struct lance_softc *)xsc; 266155093Smarius 267155093Smarius LE_LOCK(sc); 268155093Smarius lance_init_locked(sc); 269155093Smarius LE_UNLOCK(sc); 270155093Smarius} 271155093Smarius 272155093Smarius/* 273155093Smarius * Initialization of interface; set up initialization block 274155093Smarius * and transmit/receive descriptor rings. 275155093Smarius */ 276155093Smariusvoid 277155093Smariuslance_init_locked(struct lance_softc *sc) 278155093Smarius{ 279155093Smarius struct ifnet *ifp = sc->sc_ifp; 280155093Smarius u_long a; 281155093Smarius int timo; 282155093Smarius 283155093Smarius LE_LOCK_ASSERT(sc, MA_OWNED); 284155093Smarius 285155093Smarius (*sc->sc_wrcsr)(sc, LE_CSR0, LE_C0_STOP); 286155093Smarius DELAY(100); 287155093Smarius 288155093Smarius /* Newer LANCE chips have a reset register. */ 289155093Smarius if (sc->sc_hwreset) 290155093Smarius (*sc->sc_hwreset)(sc); 291155093Smarius 292155093Smarius /* Set the correct byte swapping mode, etc. */ 293155093Smarius (*sc->sc_wrcsr)(sc, LE_CSR3, sc->sc_conf3); 294155093Smarius 295166139Smarius /* Set the current media. This may require the chip to be stopped. */ 296166139Smarius if (sc->sc_mediachange) 297166139Smarius (void)(*sc->sc_mediachange)(sc); 298166139Smarius 299155093Smarius /* 300155093Smarius * Update our private copy of the Ethernet address. 301155093Smarius * We NEED the copy so we can ensure its alignment! 302155093Smarius */ 303155093Smarius memcpy(sc->sc_enaddr, IF_LLADDR(ifp), ETHER_ADDR_LEN); 304155093Smarius 305155093Smarius /* Set up LANCE init block. */ 306155093Smarius (*sc->sc_meminit)(sc); 307155093Smarius 308155093Smarius /* Give LANCE the physical address of its init block. */ 309155093Smarius a = sc->sc_addr + LE_INITADDR(sc); 310155093Smarius (*sc->sc_wrcsr)(sc, LE_CSR1, a & 0xffff); 311155093Smarius (*sc->sc_wrcsr)(sc, LE_CSR2, a >> 16); 312155093Smarius 313155093Smarius /* Try to initialize the LANCE. */ 314155093Smarius DELAY(100); 315155093Smarius (*sc->sc_wrcsr)(sc, LE_CSR0, LE_C0_INIT); 316155093Smarius 317155093Smarius /* Wait for initialization to finish. */ 318155093Smarius for (timo = 100000; timo; timo--) 319155093Smarius if ((*sc->sc_rdcsr)(sc, LE_CSR0) & LE_C0_IDON) 320155093Smarius break; 321155093Smarius 322155093Smarius if ((*sc->sc_rdcsr)(sc, LE_CSR0) & LE_C0_IDON) { 323155093Smarius /* Start the LANCE. */ 324155093Smarius (*sc->sc_wrcsr)(sc, LE_CSR0, LE_C0_INEA | LE_C0_STRT); 325155093Smarius ifp->if_drv_flags |= IFF_DRV_RUNNING; 326155093Smarius ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 327164933Smarius sc->sc_wdog_timer = 0; 328164933Smarius callout_reset(&sc->sc_wdog_ch, hz, lance_watchdog, sc); 329155093Smarius (*sc->sc_start_locked)(sc); 330155093Smarius } else 331155093Smarius if_printf(ifp, "controller failed to initialize\n"); 332155093Smarius 333155093Smarius if (sc->sc_hwinit) 334155093Smarius (*sc->sc_hwinit)(sc); 335155093Smarius} 336155093Smarius 337155093Smarius/* 338155093Smarius * Routine to copy from mbuf chain to transmit buffer in 339155093Smarius * network buffer memory. 340155093Smarius */ 341155093Smariusint 342155093Smariuslance_put(struct lance_softc *sc, int boff, struct mbuf *m) 343155093Smarius{ 344155093Smarius struct mbuf *n; 345155093Smarius int len, tlen = 0; 346155093Smarius 347155093Smarius LE_LOCK_ASSERT(sc, MA_OWNED); 348155093Smarius 349155093Smarius for (; m; m = n) { 350155093Smarius len = m->m_len; 351155093Smarius if (len == 0) { 352155093Smarius n = m_free(m); 353155093Smarius m = NULL; 354155093Smarius continue; 355155093Smarius } 356155093Smarius (*sc->sc_copytobuf)(sc, mtod(m, caddr_t), boff, len); 357155093Smarius boff += len; 358155093Smarius tlen += len; 359155093Smarius n = m_free(m); 360155093Smarius m = NULL; 361155093Smarius } 362155093Smarius if (tlen < LEMINSIZE) { 363155093Smarius (*sc->sc_zerobuf)(sc, boff, LEMINSIZE - tlen); 364155093Smarius tlen = LEMINSIZE; 365155093Smarius } 366155093Smarius return (tlen); 367155093Smarius} 368155093Smarius 369155093Smarius/* 370155093Smarius * Pull data off an interface. 371155093Smarius * Len is length of data, with local net header stripped. 372155093Smarius * We copy the data into mbufs. When full cluster sized units are present 373155093Smarius * we copy into clusters. 374155093Smarius */ 375158663Smariusstruct mbuf * 376155093Smariuslance_get(struct lance_softc *sc, int boff, int totlen) 377155093Smarius{ 378155093Smarius struct ifnet *ifp = sc->sc_ifp; 379155093Smarius struct mbuf *m, *m0, *newm; 380155093Smarius caddr_t newdata; 381155093Smarius int len; 382155093Smarius 383158663Smarius if (totlen <= ETHER_HDR_LEN || totlen > LEBLEN - ETHER_CRC_LEN) { 384158663Smarius#ifdef LEDEBUG 385158663Smarius if_printf(ifp, "invalid packet size %d; dropping\n", totlen); 386158663Smarius#endif 387158663Smarius return (NULL); 388158663Smarius } 389158663Smarius 390248078Smarius MGETHDR(m0, M_NOWAIT, MT_DATA); 391158663Smarius if (m0 == NULL) 392158663Smarius return (NULL); 393155093Smarius m0->m_pkthdr.rcvif = ifp; 394155093Smarius m0->m_pkthdr.len = totlen; 395155093Smarius len = MHLEN; 396155093Smarius m = m0; 397155093Smarius 398155093Smarius while (totlen > 0) { 399155093Smarius if (totlen >= MINCLSIZE) { 400248078Smarius MCLGET(m, M_NOWAIT); 401155093Smarius if ((m->m_flags & M_EXT) == 0) 402155093Smarius goto bad; 403155093Smarius len = MCLBYTES; 404155093Smarius } 405155093Smarius 406155093Smarius if (m == m0) { 407155093Smarius newdata = (caddr_t) 408155093Smarius ALIGN(m->m_data + ETHER_HDR_LEN) - ETHER_HDR_LEN; 409155093Smarius len -= newdata - m->m_data; 410155093Smarius m->m_data = newdata; 411155093Smarius } 412155093Smarius 413155093Smarius m->m_len = len = min(totlen, len); 414155093Smarius (*sc->sc_copyfrombuf)(sc, mtod(m, caddr_t), boff, len); 415155093Smarius boff += len; 416155093Smarius 417155093Smarius totlen -= len; 418155093Smarius if (totlen > 0) { 419248078Smarius MGET(newm, M_NOWAIT, MT_DATA); 420155093Smarius if (newm == 0) 421155093Smarius goto bad; 422155093Smarius len = MLEN; 423155093Smarius m = m->m_next = newm; 424155093Smarius } 425155093Smarius } 426155093Smarius 427155093Smarius return (m0); 428155093Smarius 429155093Smarius bad: 430155093Smarius m_freem(m0); 431158663Smarius return (NULL); 432155093Smarius} 433155093Smarius 434155093Smariusstatic void 435164933Smariuslance_watchdog(void *xsc) 436155093Smarius{ 437164933Smarius struct lance_softc *sc = (struct lance_softc *)xsc; 438164933Smarius struct ifnet *ifp = sc->sc_ifp; 439155093Smarius 440164933Smarius LE_LOCK_ASSERT(sc, MA_OWNED); 441164933Smarius 442164933Smarius if (sc->sc_wdog_timer == 0 || --sc->sc_wdog_timer != 0) { 443164933Smarius callout_reset(&sc->sc_wdog_ch, hz, lance_watchdog, sc); 444164933Smarius return; 445164933Smarius } 446164933Smarius 447155093Smarius if_printf(ifp, "device timeout\n"); 448155093Smarius ++ifp->if_oerrors; 449155093Smarius lance_init_locked(sc); 450155093Smarius} 451155093Smarius 452155093Smariusstatic int 453155093Smariuslance_mediachange(struct ifnet *ifp) 454155093Smarius{ 455155093Smarius struct lance_softc *sc = ifp->if_softc; 456155093Smarius 457155093Smarius if (sc->sc_mediachange) { 458166139Smarius /* 459166139Smarius * For setting the port in LE_CSR15 the PCnet chips must 460166139Smarius * be powered down or stopped and unlike documented may 461166139Smarius * not take effect without an initialization. So don't 462166139Smarius * invoke (*sc_mediachange) directly here but go through 463166139Smarius * lance_init_locked(). 464166139Smarius */ 465155093Smarius LE_LOCK(sc); 466166139Smarius lance_stop(sc); 467166139Smarius lance_init_locked(sc); 468166139Smarius if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) 469166139Smarius (*sc->sc_start_locked)(sc); 470155093Smarius LE_UNLOCK(sc); 471155093Smarius } 472155093Smarius return (0); 473155093Smarius} 474155093Smarius 475155093Smariusstatic void 476155093Smariuslance_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr) 477155093Smarius{ 478155093Smarius struct lance_softc *sc = ifp->if_softc; 479155093Smarius 480155093Smarius LE_LOCK(sc); 481155093Smarius if (!(ifp->if_flags & IFF_UP)) { 482155093Smarius LE_UNLOCK(sc); 483155093Smarius return; 484155093Smarius } 485155093Smarius 486155093Smarius ifmr->ifm_status = IFM_AVALID; 487155093Smarius if (sc->sc_flags & LE_CARRIER) 488155093Smarius ifmr->ifm_status |= IFM_ACTIVE; 489155093Smarius 490155093Smarius if (sc->sc_mediastatus) 491155093Smarius (*sc->sc_mediastatus)(sc, ifmr); 492155093Smarius LE_UNLOCK(sc); 493155093Smarius} 494155093Smarius 495155093Smarius/* 496155093Smarius * Process an ioctl request. 497155093Smarius */ 498155093Smariusstatic int 499155093Smariuslance_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 500155093Smarius{ 501155093Smarius struct lance_softc *sc = ifp->if_softc; 502155093Smarius struct ifreq *ifr = (struct ifreq *)data; 503155093Smarius int error = 0; 504155093Smarius 505155093Smarius switch (cmd) { 506155093Smarius case SIOCSIFFLAGS: 507155093Smarius LE_LOCK(sc); 508155093Smarius if (ifp->if_flags & IFF_PROMISC) { 509155093Smarius if (!(sc->sc_flags & LE_PROMISC)) { 510155093Smarius sc->sc_flags |= LE_PROMISC; 511155093Smarius lance_init_locked(sc); 512155093Smarius } 513155093Smarius } else if (sc->sc_flags & LE_PROMISC) { 514155093Smarius sc->sc_flags &= ~LE_PROMISC; 515155093Smarius lance_init_locked(sc); 516155093Smarius } 517155093Smarius 518155093Smarius if ((ifp->if_flags & IFF_ALLMULTI) && 519155093Smarius !(sc->sc_flags & LE_ALLMULTI)) { 520155093Smarius sc->sc_flags |= LE_ALLMULTI; 521155093Smarius lance_init_locked(sc); 522155093Smarius } else if (!(ifp->if_flags & IFF_ALLMULTI) && 523155093Smarius (sc->sc_flags & LE_ALLMULTI)) { 524155093Smarius sc->sc_flags &= ~LE_ALLMULTI; 525155093Smarius lance_init_locked(sc); 526155093Smarius } 527155093Smarius 528155093Smarius if (!(ifp->if_flags & IFF_UP) && 529155093Smarius ifp->if_drv_flags & IFF_DRV_RUNNING) { 530155093Smarius /* 531155093Smarius * If interface is marked down and it is running, then 532155093Smarius * stop it. 533155093Smarius */ 534155093Smarius lance_stop(sc); 535155093Smarius } else if (ifp->if_flags & IFF_UP && 536155093Smarius !(ifp->if_drv_flags & IFF_DRV_RUNNING)) { 537155093Smarius /* 538155093Smarius * If interface is marked up and it is stopped, then 539155093Smarius * start it. 540155093Smarius */ 541155093Smarius lance_init_locked(sc); 542155093Smarius } 543155093Smarius#ifdef LEDEBUG 544155093Smarius if (ifp->if_flags & IFF_DEBUG) 545155093Smarius sc->sc_flags |= LE_DEBUG; 546155093Smarius else 547155093Smarius sc->sc_flags &= ~LE_DEBUG; 548155093Smarius#endif 549155093Smarius LE_UNLOCK(sc); 550155093Smarius break; 551155093Smarius 552155093Smarius case SIOCADDMULTI: 553155093Smarius case SIOCDELMULTI: 554155093Smarius /* 555155093Smarius * Multicast list has changed; set the hardware filter 556155093Smarius * accordingly. 557155093Smarius */ 558155093Smarius LE_LOCK(sc); 559155093Smarius if (ifp->if_drv_flags & IFF_DRV_RUNNING) 560155093Smarius lance_init_locked(sc); 561155093Smarius LE_UNLOCK(sc); 562155093Smarius break; 563155093Smarius 564155093Smarius case SIOCGIFMEDIA: 565155093Smarius case SIOCSIFMEDIA: 566155093Smarius error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd); 567155093Smarius break; 568155093Smarius 569155093Smarius default: 570155093Smarius error = ether_ioctl(ifp, cmd, data); 571155093Smarius break; 572155093Smarius } 573155093Smarius 574155093Smarius return (error); 575155093Smarius} 576155093Smarius 577155093Smarius/* 578155093Smarius * Set up the logical address filter. 579155093Smarius */ 580155093Smariusvoid 581155093Smariuslance_setladrf(struct lance_softc *sc, uint16_t *af) 582155093Smarius{ 583155093Smarius struct ifnet *ifp = sc->sc_ifp; 584155093Smarius struct ifmultiaddr *ifma; 585155093Smarius uint32_t crc; 586155093Smarius 587155093Smarius /* 588155093Smarius * Set up multicast address filter by passing all multicast addresses 589155093Smarius * through a crc generator, and then using the high order 6 bits as an 590155093Smarius * index into the 64 bit logical address filter. The high order bit 591155093Smarius * selects the word, while the rest of the bits select the bit within 592155093Smarius * the word. 593155093Smarius */ 594155093Smarius 595155093Smarius if (ifp->if_flags & IFF_PROMISC || sc->sc_flags & LE_ALLMULTI) { 596155093Smarius af[0] = af[1] = af[2] = af[3] = 0xffff; 597155093Smarius return; 598155093Smarius } 599155093Smarius 600155093Smarius af[0] = af[1] = af[2] = af[3] = 0x0000; 601195049Srwatson if_maddr_rlock(ifp); 602155093Smarius TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { 603155093Smarius if (ifma->ifma_addr->sa_family != AF_LINK) 604155093Smarius continue; 605155093Smarius 606155093Smarius crc = ether_crc32_le(LLADDR((struct sockaddr_dl *) 607155093Smarius ifma->ifma_addr), ETHER_ADDR_LEN); 608155093Smarius 609155093Smarius /* Just want the 6 most significant bits. */ 610155093Smarius crc >>= 26; 611155093Smarius 612155093Smarius /* Set the corresponding bit in the filter. */ 613158663Smarius af[crc >> 4] |= LE_HTOLE16(1 << (crc & 0xf)); 614155093Smarius } 615195049Srwatson if_maddr_runlock(ifp); 616155093Smarius} 617155093Smarius 618155093Smarius/* 619155093Smarius * Routines for accessing the transmit and receive buffers. 620155093Smarius * The various CPU and adapter configurations supported by this 621155093Smarius * driver require three different access methods for buffers 622155093Smarius * and descriptors: 623155093Smarius * (1) contig (contiguous data; no padding), 624155093Smarius * (2) gap2 (two bytes of data followed by two bytes of padding), 625155093Smarius * (3) gap16 (16 bytes of data followed by 16 bytes of padding). 626155093Smarius */ 627155093Smarius 628155093Smarius/* 629155093Smarius * contig: contiguous data with no padding. 630155093Smarius * 631155093Smarius * Buffers may have any alignment. 632155093Smarius */ 633155093Smarius 634155093Smariusvoid 635155093Smariuslance_copytobuf_contig(struct lance_softc *sc, void *from, int boff, int len) 636155093Smarius{ 637155093Smarius volatile caddr_t buf = sc->sc_mem; 638155093Smarius 639155093Smarius /* 640155093Smarius * Just call memcpy() to do the work. 641155093Smarius */ 642155093Smarius memcpy(buf + boff, from, len); 643155093Smarius} 644155093Smarius 645155093Smariusvoid 646155093Smariuslance_copyfrombuf_contig(struct lance_softc *sc, void *to, int boff, int len) 647155093Smarius{ 648155093Smarius volatile caddr_t buf = sc->sc_mem; 649155093Smarius 650155093Smarius /* 651155093Smarius * Just call memcpy() to do the work. 652155093Smarius */ 653155093Smarius memcpy(to, buf + boff, len); 654155093Smarius} 655155093Smarius 656155093Smariusvoid 657155093Smariuslance_zerobuf_contig(struct lance_softc *sc, int boff, int len) 658155093Smarius{ 659155093Smarius volatile caddr_t buf = sc->sc_mem; 660155093Smarius 661155093Smarius /* 662155093Smarius * Just let memset() do the work 663155093Smarius */ 664155093Smarius memset(buf + boff, 0, len); 665155093Smarius} 666155093Smarius 667155093Smarius#if 0 668155093Smarius/* 669155093Smarius * Examples only; duplicate these and tweak (if necessary) in 670155093Smarius * machine-specific front-ends. 671155093Smarius */ 672155093Smarius 673155093Smarius/* 674155093Smarius * gap2: two bytes of data followed by two bytes of pad. 675155093Smarius * 676155093Smarius * Buffers must be 4-byte aligned. The code doesn't worry about 677155093Smarius * doing an extra byte. 678155093Smarius */ 679155093Smarius 680155093Smariusstatic void 681155093Smariuslance_copytobuf_gap2(struct lance_softc *sc, void *fromv, int boff, int len) 682155093Smarius{ 683155093Smarius volatile caddr_t buf = sc->sc_mem; 684155093Smarius caddr_t from = fromv; 685155093Smarius volatile uint16_t *bptr; 686155093Smarius 687155093Smarius if (boff & 0x1) { 688155093Smarius /* Handle unaligned first byte. */ 689155093Smarius bptr = ((volatile uint16_t *)buf) + (boff - 1); 690155093Smarius *bptr = (*from++ << 8) | (*bptr & 0xff); 691155093Smarius bptr += 2; 692155093Smarius len--; 693155093Smarius } else 694155093Smarius bptr = ((volatile uint16_t *)buf) + boff; 695155093Smarius while (len > 1) { 696155093Smarius *bptr = (from[1] << 8) | (from[0] & 0xff); 697155093Smarius bptr += 2; 698155093Smarius from += 2; 699155093Smarius len -= 2; 700155093Smarius } 701155093Smarius if (len == 1) 702155093Smarius *bptr = (uint16_t)*from; 703155093Smarius} 704155093Smarius 705155093Smariusstatic void 706155093Smariuslance_copyfrombuf_gap2(struct lance_softc *sc, void *tov, int boff, int len) 707155093Smarius{ 708155093Smarius volatile caddr_t buf = sc->sc_mem; 709155093Smarius caddr_t to = tov; 710155093Smarius volatile uint16_t *bptr; 711155093Smarius uint16_t tmp; 712155093Smarius 713155093Smarius if (boff & 0x1) { 714155093Smarius /* Handle unaligned first byte. */ 715155093Smarius bptr = ((volatile uint16_t *)buf) + (boff - 1); 716155093Smarius *to++ = (*bptr >> 8) & 0xff; 717155093Smarius bptr += 2; 718155093Smarius len--; 719155093Smarius } else 720155093Smarius bptr = ((volatile uint16_t *)buf) + boff; 721155093Smarius while (len > 1) { 722155093Smarius tmp = *bptr; 723155093Smarius *to++ = tmp & 0xff; 724155093Smarius *to++ = (tmp >> 8) & 0xff; 725155093Smarius bptr += 2; 726155093Smarius len -= 2; 727155093Smarius } 728155093Smarius if (len == 1) 729155093Smarius *to = *bptr & 0xff; 730155093Smarius} 731155093Smarius 732155093Smariusstatic void 733155093Smariuslance_zerobuf_gap2(struct lance_softc *sc, int boff, int len) 734155093Smarius{ 735155093Smarius volatile caddr_t buf = sc->sc_mem; 736155093Smarius volatile uint16_t *bptr; 737155093Smarius 738155093Smarius if ((unsigned)boff & 0x1) { 739155093Smarius bptr = ((volatile uint16_t *)buf) + (boff - 1); 740155093Smarius *bptr &= 0xff; 741155093Smarius bptr += 2; 742155093Smarius len--; 743155093Smarius } else 744155093Smarius bptr = ((volatile uint16_t *)buf) + boff; 745155093Smarius while (len > 0) { 746155093Smarius *bptr = 0; 747155093Smarius bptr += 2; 748155093Smarius len -= 2; 749155093Smarius } 750155093Smarius} 751155093Smarius 752155093Smarius/* 753155093Smarius * gap16: 16 bytes of data followed by 16 bytes of pad. 754155093Smarius * 755155093Smarius * Buffers must be 32-byte aligned. 756155093Smarius */ 757155093Smarius 758155093Smariusstatic void 759155093Smariuslance_copytobuf_gap16(struct lance_softc *sc, void *fromv, int boff, int len) 760155093Smarius{ 761155093Smarius volatile caddr_t buf = sc->sc_mem; 762155093Smarius caddr_t bptr, from = fromv; 763155093Smarius int xfer; 764155093Smarius 765155093Smarius bptr = buf + ((boff << 1) & ~0x1f); 766155093Smarius boff &= 0xf; 767155093Smarius xfer = min(len, 16 - boff); 768155093Smarius while (len > 0) { 769155093Smarius memcpy(bptr + boff, from, xfer); 770155093Smarius from += xfer; 771155093Smarius bptr += 32; 772155093Smarius boff = 0; 773155093Smarius len -= xfer; 774155093Smarius xfer = min(len, 16); 775155093Smarius } 776155093Smarius} 777155093Smarius 778155093Smariusstatic void 779155093Smariuslance_copyfrombuf_gap16(struct lance_softc *sc, void *tov, int boff, int len) 780155093Smarius{ 781155093Smarius volatile caddr_t buf = sc->sc_mem; 782155093Smarius caddr_t bptr, to = tov; 783155093Smarius int xfer; 784155093Smarius 785155093Smarius bptr = buf + ((boff << 1) & ~0x1f); 786155093Smarius boff &= 0xf; 787155093Smarius xfer = min(len, 16 - boff); 788155093Smarius while (len > 0) { 789155093Smarius memcpy(to, bptr + boff, xfer); 790155093Smarius to += xfer; 791155093Smarius bptr += 32; 792155093Smarius boff = 0; 793155093Smarius len -= xfer; 794155093Smarius xfer = min(len, 16); 795155093Smarius } 796155093Smarius} 797155093Smarius 798155093Smariusstatic void 799155093Smariuslance_zerobuf_gap16(struct lance_softc *sc, int boff, int len) 800155093Smarius{ 801155093Smarius volatile caddr_t buf = sc->sc_mem; 802155093Smarius caddr_t bptr; 803155093Smarius int xfer; 804155093Smarius 805155093Smarius bptr = buf + ((boff << 1) & ~0x1f); 806155093Smarius boff &= 0xf; 807155093Smarius xfer = min(len, 16 - boff); 808155093Smarius while (len > 0) { 809155093Smarius memset(bptr + boff, 0, xfer); 810155093Smarius bptr += 32; 811155093Smarius boff = 0; 812155093Smarius len -= xfer; 813155093Smarius xfer = min(len, 16); 814155093Smarius } 815155093Smarius} 816155093Smarius#endif /* Example only */ 817