ofnet.c revision 1.14 1 /* $NetBSD: ofnet.c,v 1.14 1998/06/10 22:58:05 tv 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 "bpfilter.h"
35
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/device.h>
39 #include <sys/ioctl.h>
40 #include <sys/mbuf.h>
41 #include <sys/socket.h>
42 #include <sys/syslog.h>
43
44 #include <net/if.h>
45 #include <net/if_ether.h>
46
47 #ifdef INET
48 #include <netinet/in.h>
49 #include <netinet/if_inarp.h>
50 #endif
51
52 #if NBPFILTER > 0
53 #include <net/bpf.h>
54 #include <net/bpfdesc.h>
55 #endif
56
57 #include <dev/ofw/openfirm.h>
58
59 #if NIPKDB_OFN > 0
60 #include <ipkdb/ipkdb.h>
61 #include <machine/ipkdb.h>
62
63 struct cfattach ipkdb_ofn_ca = {
64 0, ipkdb_probe, ipkdb_attach
65 };
66
67 static struct ipkdb_if *kifp;
68 static struct ofnet_softc *ipkdb_of;
69
70 static int ipkdbprobe __P((struct cfdata *, void *));
71 #endif
72
73 struct ofnet_softc {
74 struct device sc_dev;
75 int sc_phandle;
76 int sc_ihandle;
77 struct ethercom sc_ethercom;
78 };
79
80 static int ofnet_match __P((struct device *, struct cfdata *, void *));
81 static void ofnet_attach __P((struct device *, struct device *, void *));
82
83 struct cfattach ofnet_ca = {
84 sizeof(struct ofnet_softc), ofnet_match, ofnet_attach
85 };
86
87 static void ofnet_read __P((struct ofnet_softc *));
88 static void ofnet_timer __P((struct ofnet_softc *));
89 static void ofnet_init __P((struct ofnet_softc *));
90 static void ofnet_stop __P((struct ofnet_softc *));
91
92 static void ofnet_start __P((struct ifnet *));
93 static int ofnet_ioctl __P((struct ifnet *, u_long, caddr_t));
94 static void ofnet_watchdog __P((struct ifnet *));
95
96 static int
97 ofnet_match(parent, match, aux)
98 struct device *parent;
99 struct cfdata *match;
100 void *aux;
101 {
102 struct ofbus_attach_args *oba = aux;
103 char type[32];
104 int l;
105
106 #if NIPKDB_OFN > 0
107 if (!parent)
108 return ipkdbprobe(match, aux);
109 #endif
110 if (strcmp(oba->oba_busname, "ofw"))
111 return (0);
112 if ((l = OF_getprop(oba->oba_phandle, "device_type", type,
113 sizeof type - 1)) < 0)
114 return 0;
115 if (l >= sizeof type)
116 return 0;
117 type[l] = 0;
118 if (strcmp(type, "network"))
119 return 0;
120 return 1;
121 }
122
123 static void
124 ofnet_attach(parent, self, aux)
125 struct device *parent, *self;
126 void *aux;
127 {
128 struct ofnet_softc *of = (void *)self;
129 struct ifnet *ifp = &of->sc_ethercom.ec_if;
130 struct ofbus_attach_args *oba = aux;
131 char path[256];
132 int l;
133 u_int8_t myaddr[ETHER_ADDR_LEN];
134
135 of->sc_phandle = oba->oba_phandle;
136 #if NIPKDB_OFN > 0
137 if (kifp &&
138 kifp->unit - 1 == of->sc_dev.dv_unit &&
139 OF_instance_to_package(kifp->port) == oba->oba_phandle) {
140 ipkdb_of = of;
141 of->sc_ihandle = kifp->port;
142 } else
143 #endif
144 if ((l = OF_package_to_path(oba->oba_phandle, path,
145 sizeof path - 1)) < 0 ||
146 l >= sizeof path ||
147 (path[l] = 0, !(of->sc_ihandle = OF_open(path))))
148 panic("ofnet_attach: unable to open");
149 if (OF_getprop(oba->oba_phandle, "mac-address", myaddr,
150 sizeof myaddr) < 0)
151 panic("ofnet_attach: no mac-address");
152 printf(": address %s\n", ether_sprintf(myaddr));
153
154 bcopy(of->sc_dev.dv_xname, ifp->if_xname, IFNAMSIZ);
155 ifp->if_softc = of;
156 ifp->if_start = ofnet_start;
157 ifp->if_ioctl = ofnet_ioctl;
158 ifp->if_watchdog = ofnet_watchdog;
159 ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS;
160
161 if_attach(ifp);
162 ether_ifattach(ifp, myaddr);
163
164 #if NBPFILTER > 0
165 bpfattach(&ifp->if_bpf, ifp, DLT_EN10MB, sizeof(struct ether_header));
166 #endif
167
168 dk_establish(0, self); /* XXX */
169 }
170
171 static char buf[ETHERMTU + sizeof(struct ether_header)];
172
173 static void
174 ofnet_read(of)
175 struct ofnet_softc *of;
176 {
177 struct ifnet *ifp = &of->sc_ethercom.ec_if;
178 struct ether_header *eh;
179 struct mbuf *m, **mp, *head;
180 int l, len;
181 char *bufp;
182
183 #if NIPKDB_OFN > 0
184 ipkdbrint(kifp, ifp);
185 #endif
186 while (1) {
187 if ((len = OF_read(of->sc_ihandle, buf, sizeof buf)) < 0) {
188 if (len == -2 || len == 0)
189 return;
190 ifp->if_ierrors++;
191 continue;
192 }
193 if (len < sizeof(struct ether_header)) {
194 ifp->if_ierrors++;
195 continue;
196 }
197 bufp = buf;
198
199 /* Allocate a header mbuf */
200 MGETHDR(m, M_DONTWAIT, MT_DATA);
201 if (m == 0) {
202 ifp->if_ierrors++;
203 continue;
204 }
205 m->m_pkthdr.rcvif = ifp;
206 m->m_pkthdr.len = len;
207 l = MHLEN;
208 head = 0;
209 mp = &head;
210
211 while (len > 0) {
212 if (head) {
213 MGET(m, M_DONTWAIT, MT_DATA);
214 if (m == 0) {
215 ifp->if_ierrors++;
216 m_freem(head);
217 head = 0;
218 break;
219 }
220 l = MLEN;
221 }
222 if (len >= MINCLSIZE) {
223 MCLGET(m, M_DONTWAIT);
224 if ((m->m_flags & M_EXT) == 0) {
225 ifp->if_ierrors++;
226 m_free(m);
227 m_freem(head);
228 head = 0;
229 break;
230 }
231 l = MCLBYTES;
232 }
233
234 /*
235 * Make sure the data after the Ethernet header
236 * is aligned.
237 *
238 * XXX Assumes the device is an ethernet, but
239 * XXX then so does other code in this driver.
240 */
241 if (head == NULL) {
242 caddr_t newdata = (caddr_t)ALIGN(m->m_data +
243 sizeof(struct ether_header)) -
244 sizeof(struct ether_header);
245 l -= newdata - m->m_data;
246 m->m_data = newdata;
247 }
248
249 m->m_len = l = min(len, l);
250 bcopy(bufp, mtod(m, char *), l);
251 bufp += l;
252 len -= l;
253 *mp = m;
254 mp = &m->m_next;
255 }
256 if (head == 0)
257 continue;
258 eh = mtod(head, struct ether_header *);
259
260 #if NBPFILTER > 0
261 if (ifp->if_bpf)
262 bpf_mtap(ifp->if_bpf, m);
263 #endif
264 m_adj(head, sizeof(struct ether_header));
265 ifp->if_ipackets++;
266 ether_input(ifp, eh, head);
267 }
268 }
269
270 static void
271 ofnet_timer(of)
272 struct ofnet_softc *of;
273 {
274 ofnet_read(of);
275 timeout(ofnet_timer, of, 1);
276 }
277
278 static void
279 ofnet_init(of)
280 struct ofnet_softc *of;
281 {
282 struct ifnet *ifp = &of->sc_ethercom.ec_if;
283
284 if (ifp->if_flags & IFF_RUNNING)
285 return;
286
287 ifp->if_flags |= IFF_RUNNING;
288 /* Start reading from interface */
289 ofnet_timer(of);
290 /* Attempt to start output */
291 ofnet_start(ifp);
292 }
293
294 static void
295 ofnet_stop(of)
296 struct ofnet_softc *of;
297 {
298 untimeout(ofnet_timer, of);
299 of->sc_ethercom.ec_if.if_flags &= ~IFF_RUNNING;
300 }
301
302 static void
303 ofnet_start(ifp)
304 struct ifnet *ifp;
305 {
306 struct ofnet_softc *of = ifp->if_softc;
307 struct mbuf *m, *m0;
308 char *bufp;
309 int len;
310
311 if (!(ifp->if_flags & IFF_RUNNING))
312 return;
313
314 for (;;) {
315 /* First try reading any packets */
316 ofnet_read(of);
317
318 /* Now get the first packet on the queue */
319 IF_DEQUEUE(&ifp->if_snd, m0);
320 if (!m0)
321 return;
322
323 if (!(m0->m_flags & M_PKTHDR))
324 panic("ofnet_start: no header mbuf");
325 len = m0->m_pkthdr.len;
326
327 #if NBPFILTER > 0
328 if (ifp->if_bpf)
329 bpf_mtap(ifp->if_bpf, m0);
330 #endif
331
332 if (len > ETHERMTU + sizeof(struct ether_header)) {
333 /* packet too large, toss it */
334 ifp->if_oerrors++;
335 m_freem(m0);
336 continue;
337 }
338
339 for (bufp = buf; m = m0;) {
340 bcopy(mtod(m, char *), bufp, m->m_len);
341 bufp += m->m_len;
342 MFREE(m, m0);
343 }
344 if (OF_write(of->sc_ihandle, buf, bufp - buf) != bufp - buf)
345 ifp->if_oerrors++;
346 else
347 ifp->if_opackets++;
348 }
349 }
350
351 static int
352 ofnet_ioctl(ifp, cmd, data)
353 struct ifnet *ifp;
354 u_long cmd;
355 caddr_t data;
356 {
357 struct ofnet_softc *of = ifp->if_softc;
358 struct ifaddr *ifa = (struct ifaddr *)data;
359 struct ifreq *ifr = (struct ifreq *)data;
360 int error = 0;
361
362 switch (cmd) {
363 case SIOCSIFADDR:
364 ifp->if_flags |= IFF_UP;
365
366 switch (ifa->ifa_addr->sa_family) {
367 #ifdef INET
368 case AF_INET:
369 arp_ifinit(ifp, ifa);
370 break;
371 #endif
372 default:
373 break;
374 }
375 ofnet_init(of);
376 break;
377 case SIOCSIFFLAGS:
378 if ((ifp->if_flags & IFF_UP) == 0 &&
379 (ifp->if_flags & IFF_RUNNING) != 0) {
380 /* If interface is down, but running, stop it. */
381 ofnet_stop(of);
382 } else if ((ifp->if_flags & IFF_UP) != 0 &&
383 (ifp->if_flags & IFF_RUNNING) == 0) {
384 /* If interface is up, but not running, start it. */
385 ofnet_init(of);
386 } else {
387 /* Other flags are ignored. */
388 }
389 break;
390 default:
391 error = EINVAL;
392 break;
393 }
394 return error;
395 }
396
397 static void
398 ofnet_watchdog(ifp)
399 struct ifnet *ifp;
400 {
401 struct ofnet_softc *of = ifp->if_softc;
402
403 log(LOG_ERR, "%s: device timeout\n", of->sc_dev.dv_xname);
404 ifp->if_oerrors++;
405 ofnet_stop(of);
406 ofnet_init(of);
407 }
408
409 #if NIPKDB_OFN > 0
410 static void
411 ipkdbofstart(kip)
412 struct ipkdb_if *kip;
413 {
414 int unit = kip->unit - 1;
415
416 if (ipkdb_of)
417 ipkdbattach(kip, &ipkdb_of->sc_ethercom);
418 }
419
420 static void
421 ipkdbofleave(kip)
422 struct ipkdb_if *kip;
423 {
424 }
425
426 static int
427 ipkdbofrcv(kip, buf, poll)
428 struct ipkdb_if *kip;
429 u_char *buf;
430 int poll;
431 {
432 int l;
433
434 do {
435 l = OF_read(kip->port, buf, ETHERMTU);
436 if (l < 0)
437 l = 0;
438 } while (!poll && !l);
439 return l;
440 }
441
442 static void
443 ipkdbofsend(kip, buf, l)
444 struct ipkdb_if *kip;
445 u_char *buf;
446 int l;
447 {
448 OF_write(kip->port, buf, l);
449 }
450
451 static int
452 ipkdbprobe(match, aux)
453 struct cfdata *match;
454 void *aux;
455 {
456 struct ipkdb_if *kip = aux;
457 static char name[256];
458 int len;
459 int phandle;
460
461 kip->unit = match->cf_unit + 1;
462
463 if (!(kip->port = OF_open("net")))
464 return -1;
465 if ((len = OF_instance_to_path(kip->port, name, sizeof name - 1)) < 0 ||
466 len >= sizeof name)
467 return -1;
468 name[len] = 0;
469 if ((phandle = OF_instance_to_package(kip->port)) == -1)
470 return -1;
471 if (OF_getprop(phandle, "mac-address", kip->myenetaddr,
472 sizeof kip->myenetaddr) < 0)
473 return -1;
474
475 kip->flags |= IPKDB_MYHW;
476 kip->name = name;
477 kip->start = ipkdbofstart;
478 kip->leave = ipkdbofleave;
479 kip->receive = ipkdbofrcv;
480 kip->send = ipkdbofsend;
481
482 kifp = kip;
483
484 return 0;
485 }
486 #endif
487