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