]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - sys/dev/gxemul/ether/if_gx.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / sys / dev / gxemul / ether / if_gx.c
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$
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
63 struct 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
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);
82
83 static void     gx_init(void *);
84 static int      gx_transmit(struct ifnet *, struct mbuf *);
85
86 static int      gx_medchange(struct ifnet *);
87 static void     gx_medstat(struct ifnet *, struct ifmediareq *);
88
89 static int      gx_ioctl(struct ifnet *, u_long, caddr_t);
90
91 static void     gx_rx_intr(void *);
92
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),
100
101         { 0, 0 }
102 };
103
104 static driver_t gx_driver = {
105         "gx",
106         gx_methods,
107         sizeof (struct gx_softc),
108 };
109
110 static devclass_t gx_devclass;
111
112 DRIVER_MODULE(gx, nexus, gx_driver, gx_devclass, 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 (0);
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         ifp->if_opackets++;
252         ifp->if_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_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
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                         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 }