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