2 * Copyright (c) 2008-2012 Juli Mallett <jmallett@FreeBSD.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
31 #include <sys/param.h>
32 #include <sys/systm.h>
34 #include <sys/endian.h>
35 #include <sys/kernel.h>
38 #include <sys/module.h>
39 #include <sys/mutex.h>
41 #include <sys/socket.h>
42 #include <sys/sockio.h>
43 #include <sys/sysctl.h>
46 #include <net/ethernet.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>
55 #include <netinet/in.h>
56 #include <netinet/if_ether.h>
59 #include <machine/cpuregs.h>
61 #include <dev/gxemul/ether/gxreg.h>
68 struct ifmedia sc_ifmedia;
69 struct resource *sc_intr;
74 #define GXEMUL_ETHER_LOCK(sc) mtx_lock(&(sc)->sc_mtx)
75 #define GXEMUL_ETHER_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx)
77 static void gx_identify(driver_t *, device_t);
78 static int gx_probe(device_t);
79 static int gx_attach(device_t);
80 static int gx_detach(device_t);
81 static int gx_shutdown(device_t);
83 static void gx_init(void *);
84 static int gx_transmit(struct ifnet *, struct mbuf *);
86 static int gx_medchange(struct ifnet *);
87 static void gx_medstat(struct ifnet *, struct ifmediareq *);
89 static int gx_ioctl(struct ifnet *, u_long, caddr_t);
91 static void gx_rx_intr(void *);
93 static 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),
104 static driver_t gx_driver = {
107 sizeof (struct gx_softc),
110 static devclass_t gx_devclass;
112 DRIVER_MODULE(gx, nexus, gx_driver, gx_devclass, 0, 0);
115 gx_identify(driver_t *drv, device_t parent)
117 BUS_ADD_CHILD(parent, 0, "gx", 0);
121 gx_probe(device_t dev)
123 if (device_get_unit(dev) != 0)
126 device_set_desc(dev, "GXemul test Ethernet");
128 return (BUS_PROBE_NOWILDCARD);
132 gx_attach(device_t dev)
140 sc = device_get_softc(dev);
142 sc->sc_port = device_get_unit(dev);
144 /* Read MAC address. */
145 GXEMUL_ETHER_DEV_WRITE(GXEMUL_ETHER_DEV_MAC, (uintptr_t)mac);
147 /* Allocate and establish interrupt. */
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");
156 error = bus_setup_intr(sc->sc_dev, sc->sc_intr, INTR_TYPE_NET, NULL,
157 gx_rx_intr, sc, &sc->sc_intr_cookie);
159 device_printf(dev, "unable to setup interrupt.\n");
160 bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_intr);
164 bus_describe_intr(sc->sc_dev, sc->sc_intr, sc->sc_intr_cookie, "rx");
166 ifp = if_alloc(IFT_ETHER);
168 device_printf(dev, "cannot allocate ifnet.\n");
169 bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_intr);
173 if_initname(ifp, device_get_name(dev), device_get_unit(dev));
174 ifp->if_mtu = ETHERMTU;
175 ifp->if_init = gx_init;
177 ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST | IFF_ALLMULTI;
178 ifp->if_ioctl = gx_ioctl;
181 sc->sc_flags = ifp->if_flags;
183 ifmedia_init(&sc->sc_ifmedia, 0, gx_medchange, gx_medstat);
185 ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_AUTO, 0, NULL);
186 ifmedia_set(&sc->sc_ifmedia, IFM_ETHER | IFM_AUTO);
188 mtx_init(&sc->sc_mtx, "GXemul Ethernet", NULL, MTX_DEF);
190 ether_ifattach(ifp, mac);
192 ifp->if_transmit = gx_transmit;
194 return (bus_generic_attach(dev));
198 gx_detach(device_t dev)
202 sc = device_get_softc(dev);
204 bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_intr);
205 /* XXX Incomplete. */
211 gx_shutdown(device_t dev)
213 return (gx_detach(dev));
225 if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0)
226 ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
228 ifp->if_drv_flags |= IFF_DRV_RUNNING;
232 gx_transmit(struct ifnet *ifp, struct mbuf *m)
238 if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != IFF_DRV_RUNNING) {
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);
249 ETHER_BPF_MTAP(ifp, m);
251 if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
252 if_inc_counter(ifp, IFCOUNTER_OBYTES, m->m_pkthdr.len);
260 gx_medchange(struct ifnet *ifp)
266 gx_medstat(struct ifnet *ifp, struct ifmediareq *ifm)
273 ifm->ifm_status = IFM_AVALID | IFM_ACTIVE;
274 ifm->ifm_active = IFT_ETHER | IFM_1000_T | IFM_FDX;
278 gx_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
288 ifr = (struct ifreq *)data;
290 ifa = (struct ifaddr *)data;
297 * Avoid reinitialization unless it's necessary.
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)
303 arp_ifinit(ifp, ifa);
308 error = ether_ioctl(ifp, cmd, data);
314 if (ifp->if_flags == sc->sc_flags)
316 if ((ifp->if_flags & IFF_UP) != 0) {
319 if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) {
320 ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
323 sc->sc_flags = ifp->if_flags;
327 if (ifr->ifr_mtu + ifp->if_hdrlen > GXEMUL_ETHER_DEV_MTU)
333 error = ifmedia_ioctl(ifp, ifr, &sc->sc_ifmedia, cmd);
339 error = ether_ioctl(ifp, cmd, data);
347 gx_rx_intr(void *arg)
349 struct gx_softc *sc = arg;
351 GXEMUL_ETHER_LOCK(sc);
353 uint64_t status, length;
358 * Limit number of packets received at once?
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);
365 if (status != GXEMUL_ETHER_DEV_STATUS_RX_OK)
367 length = GXEMUL_ETHER_DEV_READ(GXEMUL_ETHER_DEV_LENGTH);
368 if (length > MCLBYTES - ETHER_ALIGN) {
369 if_inc_counter(sc->sc_ifp, IFCOUNTER_IERRORS, 1);
373 m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
375 device_printf(sc->sc_dev, "no memory for receive mbuf.\n");
376 if_inc_counter(sc->sc_ifp, IFCOUNTER_IQDROPS, 1);
377 GXEMUL_ETHER_UNLOCK(sc);
381 /* Align incoming frame so IP headers are aligned. */
382 m->m_data += ETHER_ALIGN;
384 memcpy(m->m_data, (const void *)(uintptr_t)GXEMUL_ETHER_DEV_FUNCTION(GXEMUL_ETHER_DEV_BUFFER), length);
386 m->m_pkthdr.rcvif = sc->sc_ifp;
387 m->m_pkthdr.len = m->m_len = length;
389 if_inc_counter(sc->sc_ifp, IFCOUNTER_IPACKETS, 1);
391 GXEMUL_ETHER_UNLOCK(sc);
393 (*sc->sc_ifp->if_input)(sc->sc_ifp, m);
395 GXEMUL_ETHER_LOCK(sc);
397 GXEMUL_ETHER_UNLOCK(sc);