1235117Sjmallett/*- 2235117Sjmallett * Copyright (c) 2008-2012 Juli Mallett <jmallett@FreeBSD.org> 3235117Sjmallett * All rights reserved. 4235117Sjmallett * 5235117Sjmallett * Redistribution and use in source and binary forms, with or without 6235117Sjmallett * modification, are permitted provided that the following conditions 7235117Sjmallett * are met: 8235117Sjmallett * 1. Redistributions of source code must retain the above copyright 9235117Sjmallett * notice, this list of conditions and the following disclaimer. 10235117Sjmallett * 2. Redistributions in binary form must reproduce the above copyright 11235117Sjmallett * notice, this list of conditions and the following disclaimer in the 12235117Sjmallett * documentation and/or other materials provided with the distribution. 13235117Sjmallett * 14235117Sjmallett * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15235117Sjmallett * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16235117Sjmallett * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17235117Sjmallett * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18235117Sjmallett * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19235117Sjmallett * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20235117Sjmallett * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21235117Sjmallett * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22235117Sjmallett * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23235117Sjmallett * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24235117Sjmallett * SUCH DAMAGE. 25235117Sjmallett * 26235117Sjmallett * $FreeBSD$ 27235117Sjmallett */ 28235117Sjmallett 29235117Sjmallett#include "opt_inet.h" 30235117Sjmallett 31235117Sjmallett#include <sys/param.h> 32235117Sjmallett#include <sys/systm.h> 33235117Sjmallett#include <sys/bus.h> 34235117Sjmallett#include <sys/endian.h> 35235117Sjmallett#include <sys/kernel.h> 36235117Sjmallett#include <sys/mbuf.h> 37235117Sjmallett#include <sys/lock.h> 38235117Sjmallett#include <sys/module.h> 39235117Sjmallett#include <sys/mutex.h> 40235117Sjmallett#include <sys/rman.h> 41235117Sjmallett#include <sys/socket.h> 42235117Sjmallett#include <sys/sockio.h> 43235117Sjmallett#include <sys/sysctl.h> 44235117Sjmallett 45235117Sjmallett#include <net/bpf.h> 46235117Sjmallett#include <net/ethernet.h> 47235117Sjmallett#include <net/if.h> 48235117Sjmallett#include <net/if_dl.h> 49235117Sjmallett#include <net/if_media.h> 50235117Sjmallett#include <net/if_types.h> 51235117Sjmallett#include <net/if_var.h> 52235117Sjmallett#include <net/if_vlan_var.h> 53235117Sjmallett 54235117Sjmallett#ifdef INET 55235117Sjmallett#include <netinet/in.h> 56235117Sjmallett#include <netinet/if_ether.h> 57235117Sjmallett#endif 58235117Sjmallett 59235117Sjmallett#include <machine/cpuregs.h> 60235117Sjmallett 61235117Sjmallett#include <dev/gxemul/ether/gxreg.h> 62235117Sjmallett 63235117Sjmallettstruct gx_softc { 64235117Sjmallett struct ifnet *sc_ifp; 65235117Sjmallett device_t sc_dev; 66235117Sjmallett unsigned sc_port; 67235117Sjmallett int sc_flags; 68235117Sjmallett struct ifmedia sc_ifmedia; 69235117Sjmallett struct resource *sc_intr; 70235117Sjmallett void *sc_intr_cookie; 71235117Sjmallett struct mtx sc_mtx; 72235117Sjmallett}; 73235117Sjmallett 74235117Sjmallett#define GXEMUL_ETHER_LOCK(sc) mtx_lock(&(sc)->sc_mtx) 75235117Sjmallett#define GXEMUL_ETHER_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx) 76235117Sjmallett 77235117Sjmallettstatic void gx_identify(driver_t *, device_t); 78235117Sjmallettstatic int gx_probe(device_t); 79235117Sjmallettstatic int gx_attach(device_t); 80235117Sjmallettstatic int gx_detach(device_t); 81235117Sjmallettstatic int gx_shutdown(device_t); 82235117Sjmallett 83235117Sjmallettstatic void gx_init(void *); 84235117Sjmallettstatic int gx_transmit(struct ifnet *, struct mbuf *); 85235117Sjmallett 86235117Sjmallettstatic int gx_medchange(struct ifnet *); 87235117Sjmallettstatic void gx_medstat(struct ifnet *, struct ifmediareq *); 88235117Sjmallett 89235117Sjmallettstatic int gx_ioctl(struct ifnet *, u_long, caddr_t); 90235117Sjmallett 91235117Sjmallettstatic void gx_rx_intr(void *); 92235117Sjmallett 93235117Sjmallettstatic device_method_t gx_methods[] = { 94235117Sjmallett /* Device interface */ 95235117Sjmallett DEVMETHOD(device_identify, gx_identify), 96235117Sjmallett DEVMETHOD(device_probe, gx_probe), 97235117Sjmallett DEVMETHOD(device_attach, gx_attach), 98235117Sjmallett DEVMETHOD(device_detach, gx_detach), 99235117Sjmallett DEVMETHOD(device_shutdown, gx_shutdown), 100235117Sjmallett 101235117Sjmallett { 0, 0 } 102235117Sjmallett}; 103235117Sjmallett 104235117Sjmallettstatic driver_t gx_driver = { 105235117Sjmallett "gx", 106235117Sjmallett gx_methods, 107235117Sjmallett sizeof (struct gx_softc), 108235117Sjmallett}; 109235117Sjmallett 110235117Sjmallettstatic devclass_t gx_devclass; 111235117Sjmallett 112235117SjmallettDRIVER_MODULE(gx, nexus, gx_driver, gx_devclass, 0, 0); 113235117Sjmallett 114235117Sjmallettstatic void 115235117Sjmallettgx_identify(driver_t *drv, device_t parent) 116235117Sjmallett{ 117235117Sjmallett BUS_ADD_CHILD(parent, 0, "gx", 0); 118235117Sjmallett} 119235117Sjmallett 120235117Sjmallettstatic int 121235117Sjmallettgx_probe(device_t dev) 122235117Sjmallett{ 123235117Sjmallett if (device_get_unit(dev) != 0) 124235117Sjmallett return (ENXIO); 125235117Sjmallett 126235117Sjmallett device_set_desc(dev, "GXemul test Ethernet"); 127235117Sjmallett 128265999Sian return (BUS_PROBE_NOWILDCARD); 129235117Sjmallett} 130235117Sjmallett 131235117Sjmallettstatic int 132235117Sjmallettgx_attach(device_t dev) 133235117Sjmallett{ 134235117Sjmallett struct ifnet *ifp; 135235117Sjmallett struct gx_softc *sc; 136235117Sjmallett uint8_t mac[6]; 137235117Sjmallett int error; 138235117Sjmallett int rid; 139235117Sjmallett 140235117Sjmallett sc = device_get_softc(dev); 141235117Sjmallett sc->sc_dev = dev; 142235117Sjmallett sc->sc_port = device_get_unit(dev); 143235117Sjmallett 144235117Sjmallett /* Read MAC address. */ 145235117Sjmallett GXEMUL_ETHER_DEV_WRITE(GXEMUL_ETHER_DEV_MAC, (uintptr_t)mac); 146235117Sjmallett 147235117Sjmallett /* Allocate and establish interrupt. */ 148235117Sjmallett rid = 0; 149235117Sjmallett sc->sc_intr = bus_alloc_resource(sc->sc_dev, SYS_RES_IRQ, &rid, 150235117Sjmallett GXEMUL_ETHER_DEV_IRQ - 2, GXEMUL_ETHER_DEV_IRQ - 2, 1, RF_ACTIVE); 151235117Sjmallett if (sc->sc_intr == NULL) { 152235117Sjmallett device_printf(dev, "unable to allocate IRQ.\n"); 153235117Sjmallett return (ENXIO); 154235117Sjmallett } 155235117Sjmallett 156235117Sjmallett error = bus_setup_intr(sc->sc_dev, sc->sc_intr, INTR_TYPE_NET, NULL, 157235117Sjmallett gx_rx_intr, sc, &sc->sc_intr_cookie); 158235117Sjmallett if (error != 0) { 159235117Sjmallett device_printf(dev, "unable to setup interrupt.\n"); 160235117Sjmallett bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_intr); 161235117Sjmallett return (ENXIO); 162235117Sjmallett } 163235117Sjmallett 164235117Sjmallett bus_describe_intr(sc->sc_dev, sc->sc_intr, sc->sc_intr_cookie, "rx"); 165235117Sjmallett 166235117Sjmallett ifp = if_alloc(IFT_ETHER); 167235117Sjmallett if (ifp == NULL) { 168235117Sjmallett device_printf(dev, "cannot allocate ifnet.\n"); 169235117Sjmallett bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_intr); 170235117Sjmallett return (ENOMEM); 171235117Sjmallett } 172235117Sjmallett 173235117Sjmallett if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 174235117Sjmallett ifp->if_mtu = ETHERMTU; 175235117Sjmallett ifp->if_init = gx_init; 176235117Sjmallett ifp->if_softc = sc; 177235117Sjmallett ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST | IFF_ALLMULTI; 178235117Sjmallett ifp->if_ioctl = gx_ioctl; 179235117Sjmallett 180235117Sjmallett sc->sc_ifp = ifp; 181235117Sjmallett sc->sc_flags = ifp->if_flags; 182235117Sjmallett 183235117Sjmallett ifmedia_init(&sc->sc_ifmedia, 0, gx_medchange, gx_medstat); 184235117Sjmallett 185235117Sjmallett ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_AUTO, 0, NULL); 186235117Sjmallett ifmedia_set(&sc->sc_ifmedia, IFM_ETHER | IFM_AUTO); 187235117Sjmallett 188235117Sjmallett mtx_init(&sc->sc_mtx, "GXemul Ethernet", NULL, MTX_DEF); 189235117Sjmallett 190235117Sjmallett ether_ifattach(ifp, mac); 191235117Sjmallett 192235117Sjmallett ifp->if_transmit = gx_transmit; 193235117Sjmallett 194235117Sjmallett return (bus_generic_attach(dev)); 195235117Sjmallett} 196235117Sjmallett 197235117Sjmallettstatic int 198235117Sjmallettgx_detach(device_t dev) 199235117Sjmallett{ 200235117Sjmallett struct gx_softc *sc; 201235117Sjmallett 202235117Sjmallett sc = device_get_softc(dev); 203235117Sjmallett 204235117Sjmallett bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_intr); 205235117Sjmallett /* XXX Incomplete. */ 206235117Sjmallett 207235117Sjmallett return (0); 208235117Sjmallett} 209235117Sjmallett 210235117Sjmallettstatic int 211235117Sjmallettgx_shutdown(device_t dev) 212235117Sjmallett{ 213235117Sjmallett return (gx_detach(dev)); 214235117Sjmallett} 215235117Sjmallett 216235117Sjmallettstatic void 217235117Sjmallettgx_init(void *arg) 218235117Sjmallett{ 219235117Sjmallett struct ifnet *ifp; 220235117Sjmallett struct gx_softc *sc; 221235117Sjmallett 222235117Sjmallett sc = arg; 223235117Sjmallett ifp = sc->sc_ifp; 224235117Sjmallett 225235117Sjmallett if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) 226235117Sjmallett ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 227235117Sjmallett 228235117Sjmallett ifp->if_drv_flags |= IFF_DRV_RUNNING; 229235117Sjmallett} 230235117Sjmallett 231235117Sjmallettstatic int 232235117Sjmallettgx_transmit(struct ifnet *ifp, struct mbuf *m) 233235117Sjmallett{ 234235117Sjmallett struct gx_softc *sc; 235235117Sjmallett 236235117Sjmallett sc = ifp->if_softc; 237235117Sjmallett 238235117Sjmallett if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != IFF_DRV_RUNNING) { 239235117Sjmallett m_freem(m); 240235117Sjmallett return (0); 241235117Sjmallett } 242235117Sjmallett 243235117Sjmallett GXEMUL_ETHER_LOCK(sc); 244235117Sjmallett GXEMUL_ETHER_DEV_WRITE(GXEMUL_ETHER_DEV_LENGTH, m->m_pkthdr.len); 245235117Sjmallett m_copydata(m, 0, m->m_pkthdr.len, (void *)(uintptr_t)GXEMUL_ETHER_DEV_FUNCTION(GXEMUL_ETHER_DEV_BUFFER)); 246235117Sjmallett GXEMUL_ETHER_DEV_WRITE(GXEMUL_ETHER_DEV_COMMAND, GXEMUL_ETHER_DEV_COMMAND_TX); 247235117Sjmallett GXEMUL_ETHER_UNLOCK(sc); 248235117Sjmallett 249235117Sjmallett ETHER_BPF_MTAP(ifp, m); 250235117Sjmallett 251235117Sjmallett ifp->if_opackets++; 252235117Sjmallett ifp->if_obytes += m->m_pkthdr.len; 253235117Sjmallett 254235117Sjmallett m_freem(m); 255235117Sjmallett 256235117Sjmallett return (0); 257235117Sjmallett} 258235117Sjmallett 259235117Sjmallettstatic int 260235117Sjmallettgx_medchange(struct ifnet *ifp) 261235117Sjmallett{ 262235117Sjmallett return (ENOTSUP); 263235117Sjmallett} 264235117Sjmallett 265235117Sjmallettstatic void 266235117Sjmallettgx_medstat(struct ifnet *ifp, struct ifmediareq *ifm) 267235117Sjmallett{ 268235117Sjmallett struct gx_softc *sc; 269235117Sjmallett 270235117Sjmallett sc = ifp->if_softc; 271235117Sjmallett 272235117Sjmallett /* Lie amazingly. */ 273235117Sjmallett ifm->ifm_status = IFM_AVALID | IFM_ACTIVE; 274235117Sjmallett ifm->ifm_active = IFT_ETHER | IFM_1000_T | IFM_FDX; 275235117Sjmallett} 276235117Sjmallett 277235117Sjmallettstatic int 278235117Sjmallettgx_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 279235117Sjmallett{ 280235117Sjmallett struct gx_softc *sc; 281235117Sjmallett struct ifreq *ifr; 282235117Sjmallett#ifdef INET 283235117Sjmallett struct ifaddr *ifa; 284235117Sjmallett#endif 285235117Sjmallett int error; 286235117Sjmallett 287235117Sjmallett sc = ifp->if_softc; 288235117Sjmallett ifr = (struct ifreq *)data; 289235117Sjmallett#ifdef INET 290235117Sjmallett ifa = (struct ifaddr *)data; 291235117Sjmallett#endif 292235117Sjmallett 293235117Sjmallett switch (cmd) { 294235117Sjmallett case SIOCSIFADDR: 295235117Sjmallett#ifdef INET 296235117Sjmallett /* 297235117Sjmallett * Avoid reinitialization unless it's necessary. 298235117Sjmallett */ 299235117Sjmallett if (ifa->ifa_addr->sa_family == AF_INET) { 300235117Sjmallett ifp->if_flags |= IFF_UP; 301235117Sjmallett if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) 302235117Sjmallett gx_init(sc); 303235117Sjmallett arp_ifinit(ifp, ifa); 304235117Sjmallett 305235117Sjmallett return (0); 306235117Sjmallett } 307235117Sjmallett#endif 308235117Sjmallett error = ether_ioctl(ifp, cmd, data); 309235117Sjmallett if (error != 0) 310235117Sjmallett return (error); 311235117Sjmallett return (0); 312235117Sjmallett 313235117Sjmallett case SIOCSIFFLAGS: 314235117Sjmallett if (ifp->if_flags == sc->sc_flags) 315235117Sjmallett return (0); 316235117Sjmallett if ((ifp->if_flags & IFF_UP) != 0) { 317235117Sjmallett gx_init(sc); 318235117Sjmallett } else { 319235117Sjmallett if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) { 320235117Sjmallett ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 321235117Sjmallett } 322235117Sjmallett } 323235117Sjmallett sc->sc_flags = ifp->if_flags; 324235117Sjmallett return (0); 325235117Sjmallett 326235117Sjmallett case SIOCSIFMTU: 327235117Sjmallett if (ifr->ifr_mtu + ifp->if_data.ifi_hdrlen > GXEMUL_ETHER_DEV_MTU) 328235117Sjmallett return (ENOTSUP); 329235117Sjmallett return (0); 330235117Sjmallett 331235117Sjmallett case SIOCSIFMEDIA: 332235117Sjmallett case SIOCGIFMEDIA: 333235117Sjmallett error = ifmedia_ioctl(ifp, ifr, &sc->sc_ifmedia, cmd); 334235117Sjmallett if (error != 0) 335235117Sjmallett return (error); 336235117Sjmallett return (0); 337235117Sjmallett 338235117Sjmallett default: 339235117Sjmallett error = ether_ioctl(ifp, cmd, data); 340235117Sjmallett if (error != 0) 341235117Sjmallett return (error); 342235117Sjmallett return (0); 343235117Sjmallett } 344235117Sjmallett} 345235117Sjmallett 346235117Sjmallettstatic void 347235117Sjmallettgx_rx_intr(void *arg) 348235117Sjmallett{ 349235117Sjmallett struct gx_softc *sc = arg; 350235117Sjmallett 351235117Sjmallett GXEMUL_ETHER_LOCK(sc); 352235117Sjmallett for (;;) { 353235117Sjmallett uint64_t status, length; 354235117Sjmallett struct mbuf *m; 355235117Sjmallett 356235117Sjmallett /* 357235117Sjmallett * XXX 358235117Sjmallett * Limit number of packets received at once? 359235117Sjmallett */ 360235117Sjmallett status = GXEMUL_ETHER_DEV_READ(GXEMUL_ETHER_DEV_STATUS); 361235117Sjmallett if (status == GXEMUL_ETHER_DEV_STATUS_RX_MORE) { 362235117Sjmallett GXEMUL_ETHER_DEV_WRITE(GXEMUL_ETHER_DEV_COMMAND, GXEMUL_ETHER_DEV_COMMAND_RX); 363235117Sjmallett continue; 364235117Sjmallett } 365235117Sjmallett if (status != GXEMUL_ETHER_DEV_STATUS_RX_OK) 366235117Sjmallett break; 367235117Sjmallett length = GXEMUL_ETHER_DEV_READ(GXEMUL_ETHER_DEV_LENGTH); 368235117Sjmallett if (length > MCLBYTES - ETHER_ALIGN) { 369235117Sjmallett sc->sc_ifp->if_ierrors++; 370235117Sjmallett continue; 371235117Sjmallett } 372235117Sjmallett 373243857Sglebius m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); 374235117Sjmallett if (m == NULL) { 375235117Sjmallett device_printf(sc->sc_dev, "no memory for receive mbuf.\n"); 376235117Sjmallett sc->sc_ifp->if_iqdrops++; 377235117Sjmallett GXEMUL_ETHER_UNLOCK(sc); 378235117Sjmallett return; 379235117Sjmallett } 380235117Sjmallett 381235117Sjmallett /* Align incoming frame so IP headers are aligned. */ 382235117Sjmallett m->m_data += ETHER_ALIGN; 383235117Sjmallett 384235117Sjmallett memcpy(m->m_data, (const void *)(uintptr_t)GXEMUL_ETHER_DEV_FUNCTION(GXEMUL_ETHER_DEV_BUFFER), length); 385235117Sjmallett 386235117Sjmallett m->m_pkthdr.rcvif = sc->sc_ifp; 387235117Sjmallett m->m_pkthdr.len = m->m_len = length; 388235117Sjmallett 389235117Sjmallett sc->sc_ifp->if_ipackets++; 390235117Sjmallett 391235117Sjmallett GXEMUL_ETHER_UNLOCK(sc); 392235117Sjmallett 393235117Sjmallett (*sc->sc_ifp->if_input)(sc->sc_ifp, m); 394235117Sjmallett 395235117Sjmallett GXEMUL_ETHER_LOCK(sc); 396235117Sjmallett } 397235117Sjmallett GXEMUL_ETHER_UNLOCK(sc); 398235117Sjmallett} 399