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