lance.c revision 155093
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 155093 2006-01-31 14:48:58Z marius $"); 76155093Smarius 77155093Smarius#include <sys/param.h> 78155093Smarius#include <sys/bus.h> 79155093Smarius#include <sys/endian.h> 80155093Smarius#include <sys/lock.h> 81155093Smarius#include <sys/mbuf.h> 82155093Smarius#include <sys/mutex.h> 83155093Smarius#include <sys/socket.h> 84155093Smarius#include <sys/sockio.h> 85155093Smarius 86155093Smarius#include <net/ethernet.h> 87155093Smarius#include <net/if.h> 88155093Smarius#include <net/if_arp.h> 89155093Smarius#include <net/if_dl.h> 90155093Smarius#include <net/if_media.h> 91155093Smarius#include <net/if_types.h> 92155093Smarius#include <net/if_vlan_var.h> 93155093Smarius 94155093Smarius#include <dev/le/lancereg.h> 95155093Smarius#include <dev/le/lancevar.h> 96155093Smarius 97155093Smariusdevclass_t le_devclass; 98155093Smarius 99155093Smariusstatic inline uint16_t ether_cmp(void *, void *); 100155093Smarius 101155093Smariusstatic void lance_start(struct ifnet *); 102155093Smariusstatic void lance_stop(struct lance_softc *); 103155093Smariusstatic void lance_init(void *); 104155093Smariusstatic struct mbuf *lance_get(struct lance_softc *, int, int); 105155093Smariusstatic void lance_watchdog(struct ifnet *); 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 110155093Smarius/* 111155093Smarius * Compare two Ether/802 addresses for equality, inlined and 112155093Smarius * unrolled for speed. Use this like memcmp(). 113155093Smarius * 114155093Smarius * XXX: Add <machine/inlines.h> for stuff like this? 115155093Smarius * XXX: or maybe add it to libkern.h instead? 116155093Smarius * 117155093Smarius * "I'd love to have an inline assembler version of this." 118155093Smarius * XXX: Who wanted that? mycroft? I wrote one, but this 119155093Smarius * version in C is as good as hand-coded assembly. -gwr 120155093Smarius * 121155093Smarius * Please do NOT tweak this without looking at the actual 122155093Smarius * assembly code generated before and after your tweaks! 123155093Smarius */ 124155093Smariusstatic inline uint16_t 125155093Smariusether_cmp(void *one, void *two) 126155093Smarius{ 127155093Smarius uint16_t *a = (u_short *)one; 128155093Smarius uint16_t *b = (u_short *)two; 129155093Smarius uint16_t diff; 130155093Smarius 131155093Smarius#ifdef m68k 132155093Smarius /* 133155093Smarius * The post-increment-pointer form produces the best 134155093Smarius * machine code for m68k. This was carefully tuned 135155093Smarius * so it compiles to just 8 short (2-byte) op-codes! 136155093Smarius */ 137155093Smarius diff = *a++ - *b++; 138155093Smarius diff |= *a++ - *b++; 139155093Smarius diff |= *a++ - *b++; 140155093Smarius#else 141155093Smarius /* 142155093Smarius * Most modern CPUs do better with a single expresion. 143155093Smarius * Note that short-cut evaluation is NOT helpful here, 144155093Smarius * because it just makes the code longer, not faster! 145155093Smarius */ 146155093Smarius diff = (a[0] - b[0]) | (a[1] - b[1]) | (a[2] - b[2]); 147155093Smarius#endif 148155093Smarius 149155093Smarius return (diff); 150155093Smarius} 151155093Smarius 152155093Smarius#define ETHER_CMP ether_cmp 153155093Smarius 154155093Smarius#ifdef LANCE_REVC_BUG 155155093Smarius/* Make sure this is short-aligned, for ether_cmp(). */ 156155093Smariusstatic uint16_t bcast_enaddr[3] = { ~0, ~0, ~0 }; 157155093Smarius#endif 158155093Smarius 159155093Smariusint 160155093Smariuslance_config(struct lance_softc *sc, const char* name, int unit) 161155093Smarius{ 162155093Smarius struct ifnet *ifp; 163155093Smarius int i, nbuf; 164155093Smarius 165155093Smarius if (LE_LOCK_INITIALIZED(sc) == 0) 166155093Smarius return (ENXIO); 167155093Smarius 168155093Smarius ifp = sc->sc_ifp = if_alloc(IFT_ETHER); 169155093Smarius if (ifp == NULL) 170155093Smarius return (ENOSPC); 171155093Smarius 172155093Smarius /* Initialize ifnet structure. */ 173155093Smarius ifp->if_softc = sc; 174155093Smarius if_initname(ifp, name, unit); 175155093Smarius ifp->if_start = lance_start; 176155093Smarius ifp->if_ioctl = lance_ioctl; 177155093Smarius ifp->if_watchdog = lance_watchdog; 178155093Smarius ifp->if_init = lance_init; 179155093Smarius ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 180155093Smarius#ifdef LANCE_REVC_BUG 181155093Smarius ifp->if_flags &= ~IFF_MULTICAST; 182155093Smarius#endif 183155093Smarius ifp->if_baudrate = IF_Mbps(10); 184155093Smarius IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); 185155093Smarius ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; 186155093Smarius IFQ_SET_READY(&ifp->if_snd); 187155093Smarius 188155093Smarius /* Initialize ifmedia structures. */ 189155093Smarius ifmedia_init(&sc->sc_media, 0, lance_mediachange, lance_mediastatus); 190155093Smarius if (sc->sc_supmedia != NULL) { 191155093Smarius for (i = 0; i < sc->sc_nsupmedia; i++) 192155093Smarius ifmedia_add(&sc->sc_media, sc->sc_supmedia[i], 0, NULL); 193155093Smarius ifmedia_set(&sc->sc_media, sc->sc_defaultmedia); 194155093Smarius } else { 195155093Smarius ifmedia_add(&sc->sc_media, 196155093Smarius IFM_MAKEWORD(IFM_ETHER, IFM_MANUAL, 0, 0), 0, NULL); 197155093Smarius ifmedia_set(&sc->sc_media, 198155093Smarius IFM_MAKEWORD(IFM_ETHER, IFM_MANUAL, 0, 0)); 199155093Smarius } 200155093Smarius 201155093Smarius switch (sc->sc_memsize) { 202155093Smarius case 8192: 203155093Smarius sc->sc_nrbuf = 4; 204155093Smarius sc->sc_ntbuf = 1; 205155093Smarius break; 206155093Smarius case 16384: 207155093Smarius sc->sc_nrbuf = 8; 208155093Smarius sc->sc_ntbuf = 2; 209155093Smarius break; 210155093Smarius case 32768: 211155093Smarius sc->sc_nrbuf = 16; 212155093Smarius sc->sc_ntbuf = 4; 213155093Smarius break; 214155093Smarius case 65536: 215155093Smarius sc->sc_nrbuf = 32; 216155093Smarius sc->sc_ntbuf = 8; 217155093Smarius break; 218155093Smarius case 131072: 219155093Smarius sc->sc_nrbuf = 64; 220155093Smarius sc->sc_ntbuf = 16; 221155093Smarius break; 222155093Smarius case 262144: 223155093Smarius sc->sc_nrbuf = 128; 224155093Smarius sc->sc_ntbuf = 32; 225155093Smarius break; 226155093Smarius default: 227155093Smarius /* weird memory size; cope with it */ 228155093Smarius nbuf = sc->sc_memsize / LEBLEN; 229155093Smarius sc->sc_ntbuf = nbuf / 5; 230155093Smarius sc->sc_nrbuf = nbuf - sc->sc_ntbuf; 231155093Smarius } 232155093Smarius 233155093Smarius if_printf(ifp, "%d receive buffers, %d transmit buffers\n", 234155093Smarius sc->sc_nrbuf, sc->sc_ntbuf); 235155093Smarius 236155093Smarius /* Make sure the chip is stopped. */ 237155093Smarius LE_LOCK(sc); 238155093Smarius lance_stop(sc); 239155093Smarius LE_UNLOCK(sc); 240155093Smarius 241155093Smarius return (0); 242155093Smarius} 243155093Smarius 244155093Smariusvoid 245155093Smariuslance_attach(struct lance_softc *sc) 246155093Smarius{ 247155093Smarius struct ifnet *ifp = sc->sc_ifp; 248155093Smarius 249155093Smarius /* Attach the interface. */ 250155093Smarius ether_ifattach(ifp, sc->sc_enaddr); 251155093Smarius 252155093Smarius /* Claim 802.1q capability. */ 253155093Smarius ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header); 254155093Smarius ifp->if_capabilities |= IFCAP_VLAN_MTU; 255155093Smarius ifp->if_capenable |= IFCAP_VLAN_MTU; 256155093Smarius} 257155093Smarius 258155093Smariusvoid 259155093Smariuslance_detach(struct lance_softc *sc) 260155093Smarius{ 261155093Smarius struct ifnet *ifp = sc->sc_ifp; 262155093Smarius 263155093Smarius LE_LOCK(sc); 264155093Smarius lance_stop(sc); 265155093Smarius LE_UNLOCK(sc); 266155093Smarius ether_ifdetach(ifp); 267155093Smarius if_free(ifp); 268155093Smarius} 269155093Smarius 270155093Smariusvoid 271155093Smariuslance_suspend(struct lance_softc *sc) 272155093Smarius{ 273155093Smarius 274155093Smarius LE_LOCK(sc); 275155093Smarius lance_stop(sc); 276155093Smarius LE_UNLOCK(sc); 277155093Smarius} 278155093Smarius 279155093Smariusvoid 280155093Smariuslance_resume(struct lance_softc *sc) 281155093Smarius{ 282155093Smarius 283155093Smarius LE_LOCK(sc); 284155093Smarius if (sc->sc_ifp->if_flags & IFF_UP) 285155093Smarius lance_init_locked(sc); 286155093Smarius LE_UNLOCK(sc); 287155093Smarius} 288155093Smarius 289155093Smariusstatic void 290155093Smariuslance_start(struct ifnet *ifp) 291155093Smarius{ 292155093Smarius struct lance_softc *sc = ifp->if_softc; 293155093Smarius 294155093Smarius LE_LOCK(sc); 295155093Smarius (*sc->sc_start_locked)(sc); 296155093Smarius LE_UNLOCK(sc); 297155093Smarius} 298155093Smarius 299155093Smariusstatic void 300155093Smariuslance_stop(struct lance_softc *sc) 301155093Smarius{ 302155093Smarius struct ifnet *ifp = sc->sc_ifp; 303155093Smarius 304155093Smarius LE_LOCK_ASSERT(sc, MA_OWNED); 305155093Smarius 306155093Smarius /* 307155093Smarius * Mark the interface down and cancel the watchdog timer. 308155093Smarius */ 309155093Smarius ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); 310155093Smarius ifp->if_timer = 0; 311155093Smarius 312155093Smarius (*sc->sc_wrcsr)(sc, LE_CSR0, LE_C0_STOP); 313155093Smarius} 314155093Smarius 315155093Smariusstatic void 316155093Smariuslance_init(void *xsc) 317155093Smarius{ 318155093Smarius struct lance_softc *sc = (struct lance_softc *)xsc; 319155093Smarius 320155093Smarius LE_LOCK(sc); 321155093Smarius lance_init_locked(sc); 322155093Smarius LE_UNLOCK(sc); 323155093Smarius} 324155093Smarius 325155093Smarius/* 326155093Smarius * Initialization of interface; set up initialization block 327155093Smarius * and transmit/receive descriptor rings. 328155093Smarius */ 329155093Smariusvoid 330155093Smariuslance_init_locked(struct lance_softc *sc) 331155093Smarius{ 332155093Smarius struct ifnet *ifp = sc->sc_ifp; 333155093Smarius u_long a; 334155093Smarius int timo; 335155093Smarius 336155093Smarius LE_LOCK_ASSERT(sc, MA_OWNED); 337155093Smarius 338155093Smarius (*sc->sc_wrcsr)(sc, LE_CSR0, LE_C0_STOP); 339155093Smarius DELAY(100); 340155093Smarius 341155093Smarius /* Newer LANCE chips have a reset register. */ 342155093Smarius if (sc->sc_hwreset) 343155093Smarius (*sc->sc_hwreset)(sc); 344155093Smarius 345155093Smarius /* Set the correct byte swapping mode, etc. */ 346155093Smarius (*sc->sc_wrcsr)(sc, LE_CSR3, sc->sc_conf3); 347155093Smarius 348155093Smarius /* 349155093Smarius * Update our private copy of the Ethernet address. 350155093Smarius * We NEED the copy so we can ensure its alignment! 351155093Smarius */ 352155093Smarius memcpy(sc->sc_enaddr, IF_LLADDR(ifp), ETHER_ADDR_LEN); 353155093Smarius 354155093Smarius /* Set up LANCE init block. */ 355155093Smarius (*sc->sc_meminit)(sc); 356155093Smarius 357155093Smarius /* Give LANCE the physical address of its init block. */ 358155093Smarius a = sc->sc_addr + LE_INITADDR(sc); 359155093Smarius (*sc->sc_wrcsr)(sc, LE_CSR1, a & 0xffff); 360155093Smarius (*sc->sc_wrcsr)(sc, LE_CSR2, a >> 16); 361155093Smarius 362155093Smarius /* Try to initialize the LANCE. */ 363155093Smarius DELAY(100); 364155093Smarius (*sc->sc_wrcsr)(sc, LE_CSR0, LE_C0_INIT); 365155093Smarius 366155093Smarius /* Wait for initialization to finish. */ 367155093Smarius for (timo = 100000; timo; timo--) 368155093Smarius if ((*sc->sc_rdcsr)(sc, LE_CSR0) & LE_C0_IDON) 369155093Smarius break; 370155093Smarius 371155093Smarius /* Set the current media. */ 372155093Smarius if (sc->sc_mediachange) 373155093Smarius (void)(*sc->sc_mediachange)(sc); 374155093Smarius 375155093Smarius if ((*sc->sc_rdcsr)(sc, LE_CSR0) & LE_C0_IDON) { 376155093Smarius /* Start the LANCE. */ 377155093Smarius (*sc->sc_wrcsr)(sc, LE_CSR0, LE_C0_INEA | LE_C0_STRT); 378155093Smarius ifp->if_drv_flags |= IFF_DRV_RUNNING; 379155093Smarius ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 380155093Smarius ifp->if_timer = 0; 381155093Smarius (*sc->sc_start_locked)(sc); 382155093Smarius } else 383155093Smarius if_printf(ifp, "controller failed to initialize\n"); 384155093Smarius 385155093Smarius if (sc->sc_hwinit) 386155093Smarius (*sc->sc_hwinit)(sc); 387155093Smarius} 388155093Smarius 389155093Smarius/* 390155093Smarius * Routine to copy from mbuf chain to transmit buffer in 391155093Smarius * network buffer memory. 392155093Smarius */ 393155093Smariusint 394155093Smariuslance_put(struct lance_softc *sc, int boff, struct mbuf *m) 395155093Smarius{ 396155093Smarius struct mbuf *n; 397155093Smarius int len, tlen = 0; 398155093Smarius 399155093Smarius LE_LOCK_ASSERT(sc, MA_OWNED); 400155093Smarius 401155093Smarius for (; m; m = n) { 402155093Smarius len = m->m_len; 403155093Smarius if (len == 0) { 404155093Smarius n = m_free(m); 405155093Smarius m = NULL; 406155093Smarius continue; 407155093Smarius } 408155093Smarius (*sc->sc_copytobuf)(sc, mtod(m, caddr_t), boff, len); 409155093Smarius boff += len; 410155093Smarius tlen += len; 411155093Smarius n = m_free(m); 412155093Smarius m = NULL; 413155093Smarius } 414155093Smarius if (tlen < LEMINSIZE) { 415155093Smarius (*sc->sc_zerobuf)(sc, boff, LEMINSIZE - tlen); 416155093Smarius tlen = LEMINSIZE; 417155093Smarius } 418155093Smarius return (tlen); 419155093Smarius} 420155093Smarius 421155093Smarius/* 422155093Smarius * Pull data off an interface. 423155093Smarius * Len is length of data, with local net header stripped. 424155093Smarius * We copy the data into mbufs. When full cluster sized units are present 425155093Smarius * we copy into clusters. 426155093Smarius */ 427155093Smariusstatic struct mbuf * 428155093Smariuslance_get(struct lance_softc *sc, int boff, int totlen) 429155093Smarius{ 430155093Smarius struct ifnet *ifp = sc->sc_ifp; 431155093Smarius struct mbuf *m, *m0, *newm; 432155093Smarius caddr_t newdata; 433155093Smarius int len; 434155093Smarius 435155093Smarius MGETHDR(m0, M_DONTWAIT, MT_DATA); 436155093Smarius if (m0 == 0) 437155093Smarius return (0); 438155093Smarius m0->m_pkthdr.rcvif = ifp; 439155093Smarius m0->m_pkthdr.len = totlen; 440155093Smarius len = MHLEN; 441155093Smarius m = m0; 442155093Smarius 443155093Smarius while (totlen > 0) { 444155093Smarius if (totlen >= MINCLSIZE) { 445155093Smarius MCLGET(m, M_DONTWAIT); 446155093Smarius if ((m->m_flags & M_EXT) == 0) 447155093Smarius goto bad; 448155093Smarius len = MCLBYTES; 449155093Smarius } 450155093Smarius 451155093Smarius if (m == m0) { 452155093Smarius newdata = (caddr_t) 453155093Smarius ALIGN(m->m_data + ETHER_HDR_LEN) - ETHER_HDR_LEN; 454155093Smarius len -= newdata - m->m_data; 455155093Smarius m->m_data = newdata; 456155093Smarius } 457155093Smarius 458155093Smarius m->m_len = len = min(totlen, len); 459155093Smarius (*sc->sc_copyfrombuf)(sc, mtod(m, caddr_t), boff, len); 460155093Smarius boff += len; 461155093Smarius 462155093Smarius totlen -= len; 463155093Smarius if (totlen > 0) { 464155093Smarius MGET(newm, M_DONTWAIT, MT_DATA); 465155093Smarius if (newm == 0) 466155093Smarius goto bad; 467155093Smarius len = MLEN; 468155093Smarius m = m->m_next = newm; 469155093Smarius } 470155093Smarius } 471155093Smarius 472155093Smarius return (m0); 473155093Smarius 474155093Smarius bad: 475155093Smarius m_freem(m0); 476155093Smarius return (0); 477155093Smarius} 478155093Smarius 479155093Smarius/* 480155093Smarius * Pass a packet to the higher levels. 481155093Smarius */ 482155093Smariusvoid 483155093Smariuslance_read(struct lance_softc *sc, int boff, int len) 484155093Smarius{ 485155093Smarius struct ifnet *ifp = sc->sc_ifp; 486155093Smarius struct ether_header *eh; 487155093Smarius struct mbuf *m; 488155093Smarius 489155093Smarius LE_LOCK_ASSERT(sc, MA_OWNED); 490155093Smarius 491155093Smarius if (len <= ETHER_HDR_LEN || len > LEBLEN - ETHER_CRC_LEN) { 492155093Smarius#ifdef LEDEBUG 493155093Smarius if_printf(ifp, "invalid packet size %d; dropping\n", len); 494155093Smarius#endif 495155093Smarius ifp->if_ierrors++; 496155093Smarius return; 497155093Smarius } 498155093Smarius 499155093Smarius /* Pull packet off interface. */ 500155093Smarius m = lance_get(sc, boff, len); 501155093Smarius if (m == 0) { 502155093Smarius ifp->if_ierrors++; 503155093Smarius return; 504155093Smarius } 505155093Smarius 506155093Smarius ifp->if_ipackets++; 507155093Smarius 508155093Smarius eh = mtod(m, struct ether_header *); 509155093Smarius 510155093Smarius#ifdef LANCE_REVC_BUG 511155093Smarius /* 512155093Smarius * The old LANCE (Rev. C) chips have a bug which causes 513155093Smarius * garbage to be inserted in front of the received packet. 514155093Smarius * The work-around is to ignore packets with an invalid 515155093Smarius * destination address (garbage will usually not match). 516155093Smarius * Of course, this precludes multicast support... 517155093Smarius */ 518155093Smarius if (ETHER_CMP(eh->ether_dhost, sc->sc_enaddr) && 519155093Smarius ETHER_CMP(eh->ether_dhost, bcast_enaddr)) { 520155093Smarius m_freem(m); 521155093Smarius return; 522155093Smarius } 523155093Smarius#endif 524155093Smarius 525155093Smarius /* 526155093Smarius * Some lance device does not present IFF_SIMPLEX behavior on multicast 527155093Smarius * packets. Make sure to drop it if it is from ourselves. 528155093Smarius */ 529155093Smarius if (!ETHER_CMP(eh->ether_shost, sc->sc_enaddr)) { 530155093Smarius m_freem(m); 531155093Smarius return; 532155093Smarius } 533155093Smarius 534155093Smarius /* Pass the packet up. */ 535155093Smarius LE_UNLOCK(sc); 536155093Smarius (*ifp->if_input)(ifp, m); 537155093Smarius LE_LOCK(sc); 538155093Smarius} 539155093Smarius 540155093Smariusstatic void 541155093Smariuslance_watchdog(struct ifnet *ifp) 542155093Smarius{ 543155093Smarius struct lance_softc *sc = ifp->if_softc; 544155093Smarius 545155093Smarius LE_LOCK(sc); 546155093Smarius if_printf(ifp, "device timeout\n"); 547155093Smarius ++ifp->if_oerrors; 548155093Smarius lance_init_locked(sc); 549155093Smarius LE_UNLOCK(sc); 550155093Smarius} 551155093Smarius 552155093Smariusstatic int 553155093Smariuslance_mediachange(struct ifnet *ifp) 554155093Smarius{ 555155093Smarius struct lance_softc *sc = ifp->if_softc; 556155093Smarius int error; 557155093Smarius 558155093Smarius if (sc->sc_mediachange) { 559155093Smarius LE_LOCK(sc); 560155093Smarius error = (*sc->sc_mediachange)(sc); 561155093Smarius LE_UNLOCK(sc); 562155093Smarius return (error); 563155093Smarius } 564155093Smarius return (0); 565155093Smarius} 566155093Smarius 567155093Smariusstatic void 568155093Smariuslance_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr) 569155093Smarius{ 570155093Smarius struct lance_softc *sc = ifp->if_softc; 571155093Smarius 572155093Smarius LE_LOCK(sc); 573155093Smarius if (!(ifp->if_flags & IFF_UP)) { 574155093Smarius LE_UNLOCK(sc); 575155093Smarius return; 576155093Smarius } 577155093Smarius 578155093Smarius ifmr->ifm_status = IFM_AVALID; 579155093Smarius if (sc->sc_flags & LE_CARRIER) 580155093Smarius ifmr->ifm_status |= IFM_ACTIVE; 581155093Smarius 582155093Smarius if (sc->sc_mediastatus) 583155093Smarius (*sc->sc_mediastatus)(sc, ifmr); 584155093Smarius LE_UNLOCK(sc); 585155093Smarius} 586155093Smarius 587155093Smarius/* 588155093Smarius * Process an ioctl request. 589155093Smarius */ 590155093Smariusstatic int 591155093Smariuslance_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 592155093Smarius{ 593155093Smarius struct lance_softc *sc = ifp->if_softc; 594155093Smarius struct ifreq *ifr = (struct ifreq *)data; 595155093Smarius int error = 0; 596155093Smarius 597155093Smarius switch (cmd) { 598155093Smarius case SIOCSIFFLAGS: 599155093Smarius LE_LOCK(sc); 600155093Smarius if (ifp->if_flags & IFF_PROMISC) { 601155093Smarius if (!(sc->sc_flags & LE_PROMISC)) { 602155093Smarius sc->sc_flags |= LE_PROMISC; 603155093Smarius lance_init_locked(sc); 604155093Smarius } 605155093Smarius } else if (sc->sc_flags & LE_PROMISC) { 606155093Smarius sc->sc_flags &= ~LE_PROMISC; 607155093Smarius lance_init_locked(sc); 608155093Smarius } 609155093Smarius 610155093Smarius if ((ifp->if_flags & IFF_ALLMULTI) && 611155093Smarius !(sc->sc_flags & LE_ALLMULTI)) { 612155093Smarius sc->sc_flags |= LE_ALLMULTI; 613155093Smarius lance_init_locked(sc); 614155093Smarius } else if (!(ifp->if_flags & IFF_ALLMULTI) && 615155093Smarius (sc->sc_flags & LE_ALLMULTI)) { 616155093Smarius sc->sc_flags &= ~LE_ALLMULTI; 617155093Smarius lance_init_locked(sc); 618155093Smarius } 619155093Smarius 620155093Smarius if (!(ifp->if_flags & IFF_UP) && 621155093Smarius ifp->if_drv_flags & IFF_DRV_RUNNING) { 622155093Smarius /* 623155093Smarius * If interface is marked down and it is running, then 624155093Smarius * stop it. 625155093Smarius */ 626155093Smarius lance_stop(sc); 627155093Smarius } else if (ifp->if_flags & IFF_UP && 628155093Smarius !(ifp->if_drv_flags & IFF_DRV_RUNNING)) { 629155093Smarius /* 630155093Smarius * If interface is marked up and it is stopped, then 631155093Smarius * start it. 632155093Smarius */ 633155093Smarius lance_init_locked(sc); 634155093Smarius } 635155093Smarius#ifdef LEDEBUG 636155093Smarius if (ifp->if_flags & IFF_DEBUG) 637155093Smarius sc->sc_flags |= LE_DEBUG; 638155093Smarius else 639155093Smarius sc->sc_flags &= ~LE_DEBUG; 640155093Smarius#endif 641155093Smarius LE_UNLOCK(sc); 642155093Smarius break; 643155093Smarius 644155093Smarius case SIOCADDMULTI: 645155093Smarius case SIOCDELMULTI: 646155093Smarius /* 647155093Smarius * Multicast list has changed; set the hardware filter 648155093Smarius * accordingly. 649155093Smarius */ 650155093Smarius LE_LOCK(sc); 651155093Smarius if (ifp->if_drv_flags & IFF_DRV_RUNNING) 652155093Smarius lance_init_locked(sc); 653155093Smarius LE_UNLOCK(sc); 654155093Smarius break; 655155093Smarius 656155093Smarius case SIOCGIFMEDIA: 657155093Smarius case SIOCSIFMEDIA: 658155093Smarius error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd); 659155093Smarius break; 660155093Smarius 661155093Smarius default: 662155093Smarius error = ether_ioctl(ifp, cmd, data); 663155093Smarius break; 664155093Smarius } 665155093Smarius 666155093Smarius return (error); 667155093Smarius} 668155093Smarius 669155093Smarius/* 670155093Smarius * Set up the logical address filter. 671155093Smarius */ 672155093Smariusvoid 673155093Smariuslance_setladrf(struct lance_softc *sc, uint16_t *af) 674155093Smarius{ 675155093Smarius struct ifnet *ifp = sc->sc_ifp; 676155093Smarius struct ifmultiaddr *ifma; 677155093Smarius uint32_t crc; 678155093Smarius 679155093Smarius /* 680155093Smarius * Set up multicast address filter by passing all multicast addresses 681155093Smarius * through a crc generator, and then using the high order 6 bits as an 682155093Smarius * index into the 64 bit logical address filter. The high order bit 683155093Smarius * selects the word, while the rest of the bits select the bit within 684155093Smarius * the word. 685155093Smarius */ 686155093Smarius 687155093Smarius if (ifp->if_flags & IFF_PROMISC || sc->sc_flags & LE_ALLMULTI) { 688155093Smarius af[0] = af[1] = af[2] = af[3] = 0xffff; 689155093Smarius return; 690155093Smarius } 691155093Smarius 692155093Smarius af[0] = af[1] = af[2] = af[3] = 0x0000; 693155093Smarius IF_ADDR_LOCK(ifp); 694155093Smarius TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { 695155093Smarius if (ifma->ifma_addr->sa_family != AF_LINK) 696155093Smarius continue; 697155093Smarius 698155093Smarius crc = ether_crc32_le(LLADDR((struct sockaddr_dl *) 699155093Smarius ifma->ifma_addr), ETHER_ADDR_LEN); 700155093Smarius 701155093Smarius /* Just want the 6 most significant bits. */ 702155093Smarius crc >>= 26; 703155093Smarius 704155093Smarius /* Set the corresponding bit in the filter. */ 705155093Smarius af[crc >> 4] |= LE_HTOLE16(1U << (crc & 0xf)); 706155093Smarius } 707155093Smarius IF_ADDR_UNLOCK(ifp); 708155093Smarius} 709155093Smarius 710155093Smarius/* 711155093Smarius * Routines for accessing the transmit and receive buffers. 712155093Smarius * The various CPU and adapter configurations supported by this 713155093Smarius * driver require three different access methods for buffers 714155093Smarius * and descriptors: 715155093Smarius * (1) contig (contiguous data; no padding), 716155093Smarius * (2) gap2 (two bytes of data followed by two bytes of padding), 717155093Smarius * (3) gap16 (16 bytes of data followed by 16 bytes of padding). 718155093Smarius */ 719155093Smarius 720155093Smarius/* 721155093Smarius * contig: contiguous data with no padding. 722155093Smarius * 723155093Smarius * Buffers may have any alignment. 724155093Smarius */ 725155093Smarius 726155093Smariusvoid 727155093Smariuslance_copytobuf_contig(struct lance_softc *sc, void *from, int boff, int len) 728155093Smarius{ 729155093Smarius volatile caddr_t buf = sc->sc_mem; 730155093Smarius 731155093Smarius /* 732155093Smarius * Just call memcpy() to do the work. 733155093Smarius */ 734155093Smarius memcpy(buf + boff, from, len); 735155093Smarius} 736155093Smarius 737155093Smariusvoid 738155093Smariuslance_copyfrombuf_contig(struct lance_softc *sc, void *to, int boff, int len) 739155093Smarius{ 740155093Smarius volatile caddr_t buf = sc->sc_mem; 741155093Smarius 742155093Smarius /* 743155093Smarius * Just call memcpy() to do the work. 744155093Smarius */ 745155093Smarius memcpy(to, buf + boff, len); 746155093Smarius} 747155093Smarius 748155093Smariusvoid 749155093Smariuslance_zerobuf_contig(struct lance_softc *sc, int boff, int len) 750155093Smarius{ 751155093Smarius volatile caddr_t buf = sc->sc_mem; 752155093Smarius 753155093Smarius /* 754155093Smarius * Just let memset() do the work 755155093Smarius */ 756155093Smarius memset(buf + boff, 0, len); 757155093Smarius} 758155093Smarius 759155093Smarius#if 0 760155093Smarius/* 761155093Smarius * Examples only; duplicate these and tweak (if necessary) in 762155093Smarius * machine-specific front-ends. 763155093Smarius */ 764155093Smarius 765155093Smarius/* 766155093Smarius * gap2: two bytes of data followed by two bytes of pad. 767155093Smarius * 768155093Smarius * Buffers must be 4-byte aligned. The code doesn't worry about 769155093Smarius * doing an extra byte. 770155093Smarius */ 771155093Smarius 772155093Smariusstatic void 773155093Smariuslance_copytobuf_gap2(struct lance_softc *sc, void *fromv, int boff, int len) 774155093Smarius{ 775155093Smarius volatile caddr_t buf = sc->sc_mem; 776155093Smarius caddr_t from = fromv; 777155093Smarius volatile uint16_t *bptr; 778155093Smarius 779155093Smarius if (boff & 0x1) { 780155093Smarius /* Handle unaligned first byte. */ 781155093Smarius bptr = ((volatile uint16_t *)buf) + (boff - 1); 782155093Smarius *bptr = (*from++ << 8) | (*bptr & 0xff); 783155093Smarius bptr += 2; 784155093Smarius len--; 785155093Smarius } else 786155093Smarius bptr = ((volatile uint16_t *)buf) + boff; 787155093Smarius while (len > 1) { 788155093Smarius *bptr = (from[1] << 8) | (from[0] & 0xff); 789155093Smarius bptr += 2; 790155093Smarius from += 2; 791155093Smarius len -= 2; 792155093Smarius } 793155093Smarius if (len == 1) 794155093Smarius *bptr = (uint16_t)*from; 795155093Smarius} 796155093Smarius 797155093Smariusstatic void 798155093Smariuslance_copyfrombuf_gap2(struct lance_softc *sc, void *tov, int boff, int len) 799155093Smarius{ 800155093Smarius volatile caddr_t buf = sc->sc_mem; 801155093Smarius caddr_t to = tov; 802155093Smarius volatile uint16_t *bptr; 803155093Smarius uint16_t tmp; 804155093Smarius 805155093Smarius if (boff & 0x1) { 806155093Smarius /* Handle unaligned first byte. */ 807155093Smarius bptr = ((volatile uint16_t *)buf) + (boff - 1); 808155093Smarius *to++ = (*bptr >> 8) & 0xff; 809155093Smarius bptr += 2; 810155093Smarius len--; 811155093Smarius } else 812155093Smarius bptr = ((volatile uint16_t *)buf) + boff; 813155093Smarius while (len > 1) { 814155093Smarius tmp = *bptr; 815155093Smarius *to++ = tmp & 0xff; 816155093Smarius *to++ = (tmp >> 8) & 0xff; 817155093Smarius bptr += 2; 818155093Smarius len -= 2; 819155093Smarius } 820155093Smarius if (len == 1) 821155093Smarius *to = *bptr & 0xff; 822155093Smarius} 823155093Smarius 824155093Smariusstatic void 825155093Smariuslance_zerobuf_gap2(struct lance_softc *sc, int boff, int len) 826155093Smarius{ 827155093Smarius volatile caddr_t buf = sc->sc_mem; 828155093Smarius volatile uint16_t *bptr; 829155093Smarius 830155093Smarius if ((unsigned)boff & 0x1) { 831155093Smarius bptr = ((volatile uint16_t *)buf) + (boff - 1); 832155093Smarius *bptr &= 0xff; 833155093Smarius bptr += 2; 834155093Smarius len--; 835155093Smarius } else 836155093Smarius bptr = ((volatile uint16_t *)buf) + boff; 837155093Smarius while (len > 0) { 838155093Smarius *bptr = 0; 839155093Smarius bptr += 2; 840155093Smarius len -= 2; 841155093Smarius } 842155093Smarius} 843155093Smarius 844155093Smarius/* 845155093Smarius * gap16: 16 bytes of data followed by 16 bytes of pad. 846155093Smarius * 847155093Smarius * Buffers must be 32-byte aligned. 848155093Smarius */ 849155093Smarius 850155093Smariusstatic void 851155093Smariuslance_copytobuf_gap16(struct lance_softc *sc, void *fromv, int boff, int len) 852155093Smarius{ 853155093Smarius volatile caddr_t buf = sc->sc_mem; 854155093Smarius caddr_t bptr, from = fromv; 855155093Smarius int xfer; 856155093Smarius 857155093Smarius bptr = buf + ((boff << 1) & ~0x1f); 858155093Smarius boff &= 0xf; 859155093Smarius xfer = min(len, 16 - boff); 860155093Smarius while (len > 0) { 861155093Smarius memcpy(bptr + boff, from, xfer); 862155093Smarius from += xfer; 863155093Smarius bptr += 32; 864155093Smarius boff = 0; 865155093Smarius len -= xfer; 866155093Smarius xfer = min(len, 16); 867155093Smarius } 868155093Smarius} 869155093Smarius 870155093Smariusstatic void 871155093Smariuslance_copyfrombuf_gap16(struct lance_softc *sc, void *tov, int boff, int len) 872155093Smarius{ 873155093Smarius volatile caddr_t buf = sc->sc_mem; 874155093Smarius caddr_t bptr, to = tov; 875155093Smarius int xfer; 876155093Smarius 877155093Smarius bptr = buf + ((boff << 1) & ~0x1f); 878155093Smarius boff &= 0xf; 879155093Smarius xfer = min(len, 16 - boff); 880155093Smarius while (len > 0) { 881155093Smarius memcpy(to, bptr + boff, xfer); 882155093Smarius to += xfer; 883155093Smarius bptr += 32; 884155093Smarius boff = 0; 885155093Smarius len -= xfer; 886155093Smarius xfer = min(len, 16); 887155093Smarius } 888155093Smarius} 889155093Smarius 890155093Smariusstatic void 891155093Smariuslance_zerobuf_gap16(struct lance_softc *sc, int boff, int len) 892155093Smarius{ 893155093Smarius volatile caddr_t buf = sc->sc_mem; 894155093Smarius caddr_t bptr; 895155093Smarius int xfer; 896155093Smarius 897155093Smarius bptr = buf + ((boff << 1) & ~0x1f); 898155093Smarius boff &= 0xf; 899155093Smarius xfer = min(len, 16 - boff); 900155093Smarius while (len > 0) { 901155093Smarius memset(bptr + boff, 0, xfer); 902155093Smarius bptr += 32; 903155093Smarius boff = 0; 904155093Smarius len -= xfer; 905155093Smarius xfer = min(len, 16); 906155093Smarius } 907155093Smarius} 908155093Smarius#endif /* Example only */ 909