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