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