ofnet.c revision 1.18 1 /* $NetBSD: ofnet.c,v 1.18 2000/03/23 07:01:37 thorpej Exp $ */
2
3 /*
4 * Copyright (C) 1995, 1996 Wolfgang Solfrank.
5 * Copyright (C) 1995, 1996 TooLs GmbH.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by TooLs GmbH.
19 * 4. The name of TooLs GmbH may not be used to endorse or promote products
20 * derived from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
27 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
28 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
30 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
31 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33 #include "ofnet.h"
34 #include "opt_inet.h"
35 #include "bpfilter.h"
36
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/callout.h>
40 #include <sys/device.h>
41 #include <sys/ioctl.h>
42 #include <sys/mbuf.h>
43 #include <sys/socket.h>
44 #include <sys/syslog.h>
45
46 #include <net/if.h>
47 #include <net/if_ether.h>
48
49 #ifdef INET
50 #include <netinet/in.h>
51 #include <netinet/if_inarp.h>
52 #endif
53
54 #if NBPFILTER > 0
55 #include <net/bpf.h>
56 #include <net/bpfdesc.h>
57 #endif
58
59 #include <dev/ofw/openfirm.h>
60
61 #if NIPKDB_OFN > 0
62 #include <ipkdb/ipkdb.h>
63 #include <machine/ipkdb.h>
64
65 struct cfattach ipkdb_ofn_ca = {
66 0, ipkdb_probe, ipkdb_attach
67 };
68
69 static struct ipkdb_if *kifp;
70 static struct ofnet_softc *ipkdb_of;
71
72 static int ipkdbprobe __P((struct cfdata *, void *));
73 #endif
74
75 struct ofnet_softc {
76 struct device sc_dev;
77 int sc_phandle;
78 int sc_ihandle;
79 struct ethercom sc_ethercom;
80 struct callout sc_callout;
81 };
82
83 static int ofnet_match __P((struct device *, struct cfdata *, void *));
84 static void ofnet_attach __P((struct device *, struct device *, void *));
85
86 struct cfattach ofnet_ca = {
87 sizeof(struct ofnet_softc), ofnet_match, ofnet_attach
88 };
89
90 static void ofnet_read __P((struct ofnet_softc *));
91 static void ofnet_timer __P((void *));
92 static void ofnet_init __P((struct ofnet_softc *));
93 static void ofnet_stop __P((struct ofnet_softc *));
94
95 static void ofnet_start __P((struct ifnet *));
96 static int ofnet_ioctl __P((struct ifnet *, u_long, caddr_t));
97 static void ofnet_watchdog __P((struct ifnet *));
98
99 static int
100 ofnet_match(parent, match, aux)
101 struct device *parent;
102 struct cfdata *match;
103 void *aux;
104 {
105 struct ofbus_attach_args *oba = aux;
106 char type[32];
107 int l;
108
109 #if NIPKDB_OFN > 0
110 if (!parent)
111 return ipkdbprobe(match, aux);
112 #endif
113 if (strcmp(oba->oba_busname, "ofw"))
114 return (0);
115 if ((l = OF_getprop(oba->oba_phandle, "device_type", type,
116 sizeof type - 1)) < 0)
117 return 0;
118 if (l >= sizeof type)
119 return 0;
120 type[l] = 0;
121 if (strcmp(type, "network"))
122 return 0;
123 return 1;
124 }
125
126 static void
127 ofnet_attach(parent, self, aux)
128 struct device *parent, *self;
129 void *aux;
130 {
131 struct ofnet_softc *of = (void *)self;
132 struct ifnet *ifp = &of->sc_ethercom.ec_if;
133 struct ofbus_attach_args *oba = aux;
134 char path[256];
135 int l;
136 u_int8_t myaddr[ETHER_ADDR_LEN];
137
138 of->sc_phandle = oba->oba_phandle;
139 #if NIPKDB_OFN > 0
140 if (kifp &&
141 kifp->unit - 1 == of->sc_dev.dv_unit &&
142 OF_instance_to_package(kifp->port) == oba->oba_phandle) {
143 ipkdb_of = of;
144 of->sc_ihandle = kifp->port;
145 } else
146 #endif
147 if ((l = OF_package_to_path(oba->oba_phandle, path,
148 sizeof path - 1)) < 0 ||
149 l >= sizeof path ||
150 (path[l] = 0, !(of->sc_ihandle = OF_open(path))))
151 panic("ofnet_attach: unable to open");
152 if (OF_getprop(oba->oba_phandle, "mac-address", myaddr,
153 sizeof myaddr) < 0)
154 panic("ofnet_attach: no mac-address");
155 printf(": address %s\n", ether_sprintf(myaddr));
156
157 callout_init(&of->sc_callout);
158
159 bcopy(of->sc_dev.dv_xname, ifp->if_xname, IFNAMSIZ);
160 ifp->if_softc = of;
161 ifp->if_start = ofnet_start;
162 ifp->if_ioctl = ofnet_ioctl;
163 ifp->if_watchdog = ofnet_watchdog;
164 ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS;
165
166 if_attach(ifp);
167 ether_ifattach(ifp, myaddr);
168
169 #if NBPFILTER > 0
170 bpfattach(&ifp->if_bpf, ifp, DLT_EN10MB, sizeof(struct ether_header));
171 #endif
172
173 dk_establish(0, self); /* XXX */
174 }
175
176 static char buf[ETHERMTU + sizeof(struct ether_header)];
177
178 static void
179 ofnet_read(of)
180 struct ofnet_softc *of;
181 {
182 struct ifnet *ifp = &of->sc_ethercom.ec_if;
183 struct mbuf *m, **mp, *head;
184 int l, len;
185 char *bufp;
186
187 #if NIPKDB_OFN > 0
188 ipkdbrint(kifp, ifp);
189 #endif
190 while (1) {
191 if ((len = OF_read(of->sc_ihandle, buf, sizeof buf)) < 0) {
192 if (len == -2 || len == 0)
193 return;
194 ifp->if_ierrors++;
195 continue;
196 }
197 if (len < sizeof(struct ether_header)) {
198 ifp->if_ierrors++;
199 continue;
200 }
201 bufp = buf;
202
203 /* Allocate a header mbuf */
204 MGETHDR(m, M_DONTWAIT, MT_DATA);
205 if (m == 0) {
206 ifp->if_ierrors++;
207 continue;
208 }
209 m->m_pkthdr.rcvif = ifp;
210 m->m_pkthdr.len = len;
211 l = MHLEN;
212 head = 0;
213 mp = &head;
214
215 while (len > 0) {
216 if (head) {
217 MGET(m, M_DONTWAIT, MT_DATA);
218 if (m == 0) {
219 ifp->if_ierrors++;
220 m_freem(head);
221 head = 0;
222 break;
223 }
224 l = MLEN;
225 }
226 if (len >= MINCLSIZE) {
227 MCLGET(m, M_DONTWAIT);
228 if ((m->m_flags & M_EXT) == 0) {
229 ifp->if_ierrors++;
230 m_free(m);
231 m_freem(head);
232 head = 0;
233 break;
234 }
235 l = MCLBYTES;
236 }
237
238 /*
239 * Make sure the data after the Ethernet header
240 * is aligned.
241 *
242 * XXX Assumes the device is an ethernet, but
243 * XXX then so does other code in this driver.
244 */
245 if (head == NULL) {
246 caddr_t newdata = (caddr_t)ALIGN(m->m_data +
247 sizeof(struct ether_header)) -
248 sizeof(struct ether_header);
249 l -= newdata - m->m_data;
250 m->m_data = newdata;
251 }
252
253 m->m_len = l = min(len, l);
254 bcopy(bufp, mtod(m, char *), l);
255 bufp += l;
256 len -= l;
257 *mp = m;
258 mp = &m->m_next;
259 }
260 if (head == 0)
261 continue;
262
263 #if NBPFILTER > 0
264 if (ifp->if_bpf)
265 bpf_mtap(ifp->if_bpf, m);
266 #endif
267 ifp->if_ipackets++;
268 (*ifp->if_input)(ifp, head);
269 }
270 }
271
272 static void
273 ofnet_timer(arg)
274 void *arg;
275 {
276 struct ofnet_softc *of = arg;
277
278 ofnet_read(of);
279 callout_reset(&of->sc_callout, 1, ofnet_timer, of);
280 }
281
282 static void
283 ofnet_init(of)
284 struct ofnet_softc *of;
285 {
286 struct ifnet *ifp = &of->sc_ethercom.ec_if;
287
288 if (ifp->if_flags & IFF_RUNNING)
289 return;
290
291 ifp->if_flags |= IFF_RUNNING;
292 /* Start reading from interface */
293 ofnet_timer(of);
294 /* Attempt to start output */
295 ofnet_start(ifp);
296 }
297
298 static void
299 ofnet_stop(of)
300 struct ofnet_softc *of;
301 {
302 callout_stop(&of->sc_callout);
303 of->sc_ethercom.ec_if.if_flags &= ~IFF_RUNNING;
304 }
305
306 static void
307 ofnet_start(ifp)
308 struct ifnet *ifp;
309 {
310 struct ofnet_softc *of = ifp->if_softc;
311 struct mbuf *m, *m0;
312 char *bufp;
313 int len;
314
315 if (!(ifp->if_flags & IFF_RUNNING))
316 return;
317
318 for (;;) {
319 /* First try reading any packets */
320 ofnet_read(of);
321
322 /* Now get the first packet on the queue */
323 IF_DEQUEUE(&ifp->if_snd, m0);
324 if (!m0)
325 return;
326
327 if (!(m0->m_flags & M_PKTHDR))
328 panic("ofnet_start: no header mbuf");
329 len = m0->m_pkthdr.len;
330
331 #if NBPFILTER > 0
332 if (ifp->if_bpf)
333 bpf_mtap(ifp->if_bpf, m0);
334 #endif
335
336 if (len > ETHERMTU + sizeof(struct ether_header)) {
337 /* packet too large, toss it */
338 ifp->if_oerrors++;
339 m_freem(m0);
340 continue;
341 }
342
343 for (bufp = buf; m = m0;) {
344 bcopy(mtod(m, char *), bufp, m->m_len);
345 bufp += m->m_len;
346 MFREE(m, m0);
347 }
348 if (OF_write(of->sc_ihandle, buf, bufp - buf) != bufp - buf)
349 ifp->if_oerrors++;
350 else
351 ifp->if_opackets++;
352 }
353 }
354
355 static int
356 ofnet_ioctl(ifp, cmd, data)
357 struct ifnet *ifp;
358 u_long cmd;
359 caddr_t data;
360 {
361 struct ofnet_softc *of = ifp->if_softc;
362 struct ifaddr *ifa = (struct ifaddr *)data;
363 struct ifreq *ifr = (struct ifreq *)data;
364 int error = 0;
365
366 switch (cmd) {
367 case SIOCSIFADDR:
368 ifp->if_flags |= IFF_UP;
369
370 switch (ifa->ifa_addr->sa_family) {
371 #ifdef INET
372 case AF_INET:
373 arp_ifinit(ifp, ifa);
374 break;
375 #endif
376 default:
377 break;
378 }
379 ofnet_init(of);
380 break;
381 case SIOCSIFFLAGS:
382 if ((ifp->if_flags & IFF_UP) == 0 &&
383 (ifp->if_flags & IFF_RUNNING) != 0) {
384 /* If interface is down, but running, stop it. */
385 ofnet_stop(of);
386 } else if ((ifp->if_flags & IFF_UP) != 0 &&
387 (ifp->if_flags & IFF_RUNNING) == 0) {
388 /* If interface is up, but not running, start it. */
389 ofnet_init(of);
390 } else {
391 /* Other flags are ignored. */
392 }
393 break;
394 default:
395 error = EINVAL;
396 break;
397 }
398 return error;
399 }
400
401 static void
402 ofnet_watchdog(ifp)
403 struct ifnet *ifp;
404 {
405 struct ofnet_softc *of = ifp->if_softc;
406
407 log(LOG_ERR, "%s: device timeout\n", of->sc_dev.dv_xname);
408 ifp->if_oerrors++;
409 ofnet_stop(of);
410 ofnet_init(of);
411 }
412
413 #if NIPKDB_OFN > 0
414 static void
415 ipkdbofstart(kip)
416 struct ipkdb_if *kip;
417 {
418 int unit = kip->unit - 1;
419
420 if (ipkdb_of)
421 ipkdbattach(kip, &ipkdb_of->sc_ethercom);
422 }
423
424 static void
425 ipkdbofleave(kip)
426 struct ipkdb_if *kip;
427 {
428 }
429
430 static int
431 ipkdbofrcv(kip, buf, poll)
432 struct ipkdb_if *kip;
433 u_char *buf;
434 int poll;
435 {
436 int l;
437
438 do {
439 l = OF_read(kip->port, buf, ETHERMTU);
440 if (l < 0)
441 l = 0;
442 } while (!poll && !l);
443 return l;
444 }
445
446 static void
447 ipkdbofsend(kip, buf, l)
448 struct ipkdb_if *kip;
449 u_char *buf;
450 int l;
451 {
452 OF_write(kip->port, buf, l);
453 }
454
455 static int
456 ipkdbprobe(match, aux)
457 struct cfdata *match;
458 void *aux;
459 {
460 struct ipkdb_if *kip = aux;
461 static char name[256];
462 int len;
463 int phandle;
464
465 kip->unit = match->cf_unit + 1;
466
467 if (!(kip->port = OF_open("net")))
468 return -1;
469 if ((len = OF_instance_to_path(kip->port, name, sizeof name - 1)) < 0 ||
470 len >= sizeof name)
471 return -1;
472 name[len] = 0;
473 if ((phandle = OF_instance_to_package(kip->port)) == -1)
474 return -1;
475 if (OF_getprop(phandle, "mac-address", kip->myenetaddr,
476 sizeof kip->myenetaddr) < 0)
477 return -1;
478
479 kip->flags |= IPKDB_MYHW;
480 kip->name = name;
481 kip->start = ipkdbofstart;
482 kip->leave = ipkdbofleave;
483 kip->receive = ipkdbofrcv;
484 kip->send = ipkdbofsend;
485
486 kifp = kip;
487
488 return 0;
489 }
490 #endif
491