if_gx.c revision 265999
1/*- 2 * Copyright (c) 2008-2012 Juli Mallett <jmallett@FreeBSD.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD: stable/10/sys/dev/gxemul/ether/if_gx.c 265999 2014-05-14 01:35:43Z ian $ 27 */ 28 29#include "opt_inet.h" 30 31#include <sys/param.h> 32#include <sys/systm.h> 33#include <sys/bus.h> 34#include <sys/endian.h> 35#include <sys/kernel.h> 36#include <sys/mbuf.h> 37#include <sys/lock.h> 38#include <sys/module.h> 39#include <sys/mutex.h> 40#include <sys/rman.h> 41#include <sys/socket.h> 42#include <sys/sockio.h> 43#include <sys/sysctl.h> 44 45#include <net/bpf.h> 46#include <net/ethernet.h> 47#include <net/if.h> 48#include <net/if_dl.h> 49#include <net/if_media.h> 50#include <net/if_types.h> 51#include <net/if_var.h> 52#include <net/if_vlan_var.h> 53 54#ifdef INET 55#include <netinet/in.h> 56#include <netinet/if_ether.h> 57#endif 58 59#include <machine/cpuregs.h> 60 61#include <dev/gxemul/ether/gxreg.h> 62 63struct gx_softc { 64 struct ifnet *sc_ifp; 65 device_t sc_dev; 66 unsigned sc_port; 67 int sc_flags; 68 struct ifmedia sc_ifmedia; 69 struct resource *sc_intr; 70 void *sc_intr_cookie; 71 struct mtx sc_mtx; 72}; 73 74#define GXEMUL_ETHER_LOCK(sc) mtx_lock(&(sc)->sc_mtx) 75#define GXEMUL_ETHER_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx) 76 77static void gx_identify(driver_t *, device_t); 78static int gx_probe(device_t); 79static int gx_attach(device_t); 80static int gx_detach(device_t); 81static int gx_shutdown(device_t); 82 83static void gx_init(void *); 84static int gx_transmit(struct ifnet *, struct mbuf *); 85 86static int gx_medchange(struct ifnet *); 87static void gx_medstat(struct ifnet *, struct ifmediareq *); 88 89static int gx_ioctl(struct ifnet *, u_long, caddr_t); 90 91static void gx_rx_intr(void *); 92 93static device_method_t gx_methods[] = { 94 /* Device interface */ 95 DEVMETHOD(device_identify, gx_identify), 96 DEVMETHOD(device_probe, gx_probe), 97 DEVMETHOD(device_attach, gx_attach), 98 DEVMETHOD(device_detach, gx_detach), 99 DEVMETHOD(device_shutdown, gx_shutdown), 100 101 { 0, 0 } 102}; 103 104static driver_t gx_driver = { 105 "gx", 106 gx_methods, 107 sizeof (struct gx_softc), 108}; 109 110static devclass_t gx_devclass; 111 112DRIVER_MODULE(gx, nexus, gx_driver, gx_devclass, 0, 0); 113 114static void 115gx_identify(driver_t *drv, device_t parent) 116{ 117 BUS_ADD_CHILD(parent, 0, "gx", 0); 118} 119 120static int 121gx_probe(device_t dev) 122{ 123 if (device_get_unit(dev) != 0) 124 return (ENXIO); 125 126 device_set_desc(dev, "GXemul test Ethernet"); 127 128 return (BUS_PROBE_NOWILDCARD); 129} 130 131static int 132gx_attach(device_t dev) 133{ 134 struct ifnet *ifp; 135 struct gx_softc *sc; 136 uint8_t mac[6]; 137 int error; 138 int rid; 139 140 sc = device_get_softc(dev); 141 sc->sc_dev = dev; 142 sc->sc_port = device_get_unit(dev); 143 144 /* Read MAC address. */ 145 GXEMUL_ETHER_DEV_WRITE(GXEMUL_ETHER_DEV_MAC, (uintptr_t)mac); 146 147 /* Allocate and establish interrupt. */ 148 rid = 0; 149 sc->sc_intr = bus_alloc_resource(sc->sc_dev, SYS_RES_IRQ, &rid, 150 GXEMUL_ETHER_DEV_IRQ - 2, GXEMUL_ETHER_DEV_IRQ - 2, 1, RF_ACTIVE); 151 if (sc->sc_intr == NULL) { 152 device_printf(dev, "unable to allocate IRQ.\n"); 153 return (ENXIO); 154 } 155 156 error = bus_setup_intr(sc->sc_dev, sc->sc_intr, INTR_TYPE_NET, NULL, 157 gx_rx_intr, sc, &sc->sc_intr_cookie); 158 if (error != 0) { 159 device_printf(dev, "unable to setup interrupt.\n"); 160 bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_intr); 161 return (ENXIO); 162 } 163 164 bus_describe_intr(sc->sc_dev, sc->sc_intr, sc->sc_intr_cookie, "rx"); 165 166 ifp = if_alloc(IFT_ETHER); 167 if (ifp == NULL) { 168 device_printf(dev, "cannot allocate ifnet.\n"); 169 bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_intr); 170 return (ENOMEM); 171 } 172 173 if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 174 ifp->if_mtu = ETHERMTU; 175 ifp->if_init = gx_init; 176 ifp->if_softc = sc; 177 ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST | IFF_ALLMULTI; 178 ifp->if_ioctl = gx_ioctl; 179 180 sc->sc_ifp = ifp; 181 sc->sc_flags = ifp->if_flags; 182 183 ifmedia_init(&sc->sc_ifmedia, 0, gx_medchange, gx_medstat); 184 185 ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_AUTO, 0, NULL); 186 ifmedia_set(&sc->sc_ifmedia, IFM_ETHER | IFM_AUTO); 187 188 mtx_init(&sc->sc_mtx, "GXemul Ethernet", NULL, MTX_DEF); 189 190 ether_ifattach(ifp, mac); 191 192 ifp->if_transmit = gx_transmit; 193 194 return (bus_generic_attach(dev)); 195} 196 197static int 198gx_detach(device_t dev) 199{ 200 struct gx_softc *sc; 201 202 sc = device_get_softc(dev); 203 204 bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_intr); 205 /* XXX Incomplete. */ 206 207 return (0); 208} 209 210static int 211gx_shutdown(device_t dev) 212{ 213 return (gx_detach(dev)); 214} 215 216static void 217gx_init(void *arg) 218{ 219 struct ifnet *ifp; 220 struct gx_softc *sc; 221 222 sc = arg; 223 ifp = sc->sc_ifp; 224 225 if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) 226 ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 227 228 ifp->if_drv_flags |= IFF_DRV_RUNNING; 229} 230 231static int 232gx_transmit(struct ifnet *ifp, struct mbuf *m) 233{ 234 struct gx_softc *sc; 235 236 sc = ifp->if_softc; 237 238 if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != IFF_DRV_RUNNING) { 239 m_freem(m); 240 return (0); 241 } 242 243 GXEMUL_ETHER_LOCK(sc); 244 GXEMUL_ETHER_DEV_WRITE(GXEMUL_ETHER_DEV_LENGTH, m->m_pkthdr.len); 245 m_copydata(m, 0, m->m_pkthdr.len, (void *)(uintptr_t)GXEMUL_ETHER_DEV_FUNCTION(GXEMUL_ETHER_DEV_BUFFER)); 246 GXEMUL_ETHER_DEV_WRITE(GXEMUL_ETHER_DEV_COMMAND, GXEMUL_ETHER_DEV_COMMAND_TX); 247 GXEMUL_ETHER_UNLOCK(sc); 248 249 ETHER_BPF_MTAP(ifp, m); 250 251 ifp->if_opackets++; 252 ifp->if_obytes += m->m_pkthdr.len; 253 254 m_freem(m); 255 256 return (0); 257} 258 259static int 260gx_medchange(struct ifnet *ifp) 261{ 262 return (ENOTSUP); 263} 264 265static void 266gx_medstat(struct ifnet *ifp, struct ifmediareq *ifm) 267{ 268 struct gx_softc *sc; 269 270 sc = ifp->if_softc; 271 272 /* Lie amazingly. */ 273 ifm->ifm_status = IFM_AVALID | IFM_ACTIVE; 274 ifm->ifm_active = IFT_ETHER | IFM_1000_T | IFM_FDX; 275} 276 277static int 278gx_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 279{ 280 struct gx_softc *sc; 281 struct ifreq *ifr; 282#ifdef INET 283 struct ifaddr *ifa; 284#endif 285 int error; 286 287 sc = ifp->if_softc; 288 ifr = (struct ifreq *)data; 289#ifdef INET 290 ifa = (struct ifaddr *)data; 291#endif 292 293 switch (cmd) { 294 case SIOCSIFADDR: 295#ifdef INET 296 /* 297 * Avoid reinitialization unless it's necessary. 298 */ 299 if (ifa->ifa_addr->sa_family == AF_INET) { 300 ifp->if_flags |= IFF_UP; 301 if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) 302 gx_init(sc); 303 arp_ifinit(ifp, ifa); 304 305 return (0); 306 } 307#endif 308 error = ether_ioctl(ifp, cmd, data); 309 if (error != 0) 310 return (error); 311 return (0); 312 313 case SIOCSIFFLAGS: 314 if (ifp->if_flags == sc->sc_flags) 315 return (0); 316 if ((ifp->if_flags & IFF_UP) != 0) { 317 gx_init(sc); 318 } else { 319 if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) { 320 ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 321 } 322 } 323 sc->sc_flags = ifp->if_flags; 324 return (0); 325 326 case SIOCSIFMTU: 327 if (ifr->ifr_mtu + ifp->if_data.ifi_hdrlen > GXEMUL_ETHER_DEV_MTU) 328 return (ENOTSUP); 329 return (0); 330 331 case SIOCSIFMEDIA: 332 case SIOCGIFMEDIA: 333 error = ifmedia_ioctl(ifp, ifr, &sc->sc_ifmedia, cmd); 334 if (error != 0) 335 return (error); 336 return (0); 337 338 default: 339 error = ether_ioctl(ifp, cmd, data); 340 if (error != 0) 341 return (error); 342 return (0); 343 } 344} 345 346static void 347gx_rx_intr(void *arg) 348{ 349 struct gx_softc *sc = arg; 350 351 GXEMUL_ETHER_LOCK(sc); 352 for (;;) { 353 uint64_t status, length; 354 struct mbuf *m; 355 356 /* 357 * XXX 358 * Limit number of packets received at once? 359 */ 360 status = GXEMUL_ETHER_DEV_READ(GXEMUL_ETHER_DEV_STATUS); 361 if (status == GXEMUL_ETHER_DEV_STATUS_RX_MORE) { 362 GXEMUL_ETHER_DEV_WRITE(GXEMUL_ETHER_DEV_COMMAND, GXEMUL_ETHER_DEV_COMMAND_RX); 363 continue; 364 } 365 if (status != GXEMUL_ETHER_DEV_STATUS_RX_OK) 366 break; 367 length = GXEMUL_ETHER_DEV_READ(GXEMUL_ETHER_DEV_LENGTH); 368 if (length > MCLBYTES - ETHER_ALIGN) { 369 sc->sc_ifp->if_ierrors++; 370 continue; 371 } 372 373 m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); 374 if (m == NULL) { 375 device_printf(sc->sc_dev, "no memory for receive mbuf.\n"); 376 sc->sc_ifp->if_iqdrops++; 377 GXEMUL_ETHER_UNLOCK(sc); 378 return; 379 } 380 381 /* Align incoming frame so IP headers are aligned. */ 382 m->m_data += ETHER_ALIGN; 383 384 memcpy(m->m_data, (const void *)(uintptr_t)GXEMUL_ETHER_DEV_FUNCTION(GXEMUL_ETHER_DEV_BUFFER), length); 385 386 m->m_pkthdr.rcvif = sc->sc_ifp; 387 m->m_pkthdr.len = m->m_len = length; 388 389 sc->sc_ifp->if_ipackets++; 390 391 GXEMUL_ETHER_UNLOCK(sc); 392 393 (*sc->sc_ifp->if_input)(sc->sc_ifp, m); 394 395 GXEMUL_ETHER_LOCK(sc); 396 } 397 GXEMUL_ETHER_UNLOCK(sc); 398} 399