1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2008-2012 Juli Mallett <jmallett@FreeBSD.org>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 *
28 * $FreeBSD$
29 */
30
31 #include "opt_inet.h"
32
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/bus.h>
36 #include <sys/endian.h>
37 #include <sys/kernel.h>
38 #include <sys/mbuf.h>
39 #include <sys/lock.h>
40 #include <sys/module.h>
41 #include <sys/mutex.h>
42 #include <sys/rman.h>
43 #include <sys/socket.h>
44 #include <sys/sockio.h>
45 #include <sys/sysctl.h>
46
47 #include <net/bpf.h>
48 #include <net/ethernet.h>
49 #include <net/if.h>
50 #include <net/if_dl.h>
51 #include <net/if_media.h>
52 #include <net/if_types.h>
53 #include <net/if_var.h>
54 #include <net/if_vlan_var.h>
55
56 #ifdef INET
57 #include <netinet/in.h>
58 #include <netinet/if_ether.h>
59 #endif
60
61 #include <machine/cpuregs.h>
62
63 #include <dev/gxemul/ether/gxreg.h>
64
65 struct gx_softc {
66 struct ifnet *sc_ifp;
67 device_t sc_dev;
68 unsigned sc_port;
69 int sc_flags;
70 struct ifmedia sc_ifmedia;
71 struct resource *sc_intr;
72 void *sc_intr_cookie;
73 struct mtx sc_mtx;
74 };
75
76 #define GXEMUL_ETHER_LOCK(sc) mtx_lock(&(sc)->sc_mtx)
77 #define GXEMUL_ETHER_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx)
78
79 static void gx_identify(driver_t *, device_t);
80 static int gx_probe(device_t);
81 static int gx_attach(device_t);
82 static int gx_detach(device_t);
83 static int gx_shutdown(device_t);
84
85 static void gx_init(void *);
86 static int gx_transmit(struct ifnet *, struct mbuf *);
87
88 static int gx_medchange(struct ifnet *);
89 static void gx_medstat(struct ifnet *, struct ifmediareq *);
90
91 static int gx_ioctl(struct ifnet *, u_long, caddr_t);
92
93 static void gx_rx_intr(void *);
94
95 static device_method_t gx_methods[] = {
96 /* Device interface */
97 DEVMETHOD(device_identify, gx_identify),
98 DEVMETHOD(device_probe, gx_probe),
99 DEVMETHOD(device_attach, gx_attach),
100 DEVMETHOD(device_detach, gx_detach),
101 DEVMETHOD(device_shutdown, gx_shutdown),
102
103 { 0, 0 }
104 };
105
106 static driver_t gx_driver = {
107 "gx",
108 gx_methods,
109 sizeof (struct gx_softc),
110 };
111
112 DRIVER_MODULE(gx, nexus, gx_driver, 0, 0);
113
114 static void
115 gx_identify(driver_t *drv, device_t parent)
116 {
117 BUS_ADD_CHILD(parent, 0, "gx", 0);
118 }
119
120 static int
121 gx_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
131 static int
132 gx_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
197 static int
198 gx_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
210 static int
211 gx_shutdown(device_t dev)
212 {
213 return (gx_detach(dev));
214 }
215
216 static void
217 gx_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
231 static int
232 gx_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 if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
252 if_inc_counter(ifp, IFCOUNTER_OBYTES, m->m_pkthdr.len);
253
254 m_freem(m);
255
256 return (0);
257 }
258
259 static int
260 gx_medchange(struct ifnet *ifp)
261 {
262 return (ENOTSUP);
263 }
264
265 static void
266 gx_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
277 static int
278 gx_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_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
346 static void
347 gx_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 if_inc_counter(sc->sc_ifp, IFCOUNTER_IERRORS, 1);
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 if_inc_counter(sc->sc_ifp, IFCOUNTER_IQDROPS, 1);
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 if_inc_counter(sc->sc_ifp, IFCOUNTER_IPACKETS, 1);
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 }
Cache object: f89a6939172e548e74b96a470cab4a62
|