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 57struct cfattach ipkdb_ofn_ca = { 58 0, ipkdb_probe, ipkdb_attach 59}; 60 61static struct ipkdb_if *kifp; 62static struct ofn_softc *ipkdb_of; 63 64static int ipkdbprobe __P((void *, void *)); 65#endif 66 67struct ofn_softc { 68 struct device sc_dev; 69 int sc_phandle; 70 int sc_ihandle; 71 struct ethercom sc_ethercom; 72}; 73 74static int ofnprobe __P((struct device *, void *, void *)); 75static void ofnattach __P((struct device *, struct device *, void *)); 76 77struct cfattach ofnet_ca = { 78 sizeof(struct ofn_softc), ofnprobe, ofnattach 79}; 80 81struct cfdriver ofnet_cd = { 82 NULL, "ofnet", DV_IFNET 83}; 84 85static void ofnread __P((struct ofn_softc *)); 86static void ofntimer __P((struct ofn_softc *)); 87static void ofninit __P((struct ofn_softc *)); 88static void ofnstop __P((struct ofn_softc *)); 89 90static void ofnstart __P((struct ifnet *)); 91static int ofnioctl __P((struct ifnet *, u_long, caddr_t)); 92static void ofnwatchdog __P((struct ifnet *)); 93 94static int 95ofnprobe(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 117static void 118ofnattach(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 165static char buf[ETHERMTU + sizeof(struct ether_header)]; 166 167static void 168ofnread(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 242static void 243ofntimer(of) 244 struct ofn_softc *of; 245{ 246 ofnread(of); 247 timeout(ofntimer, of, 1); 248} 249 250static void 251ofninit(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 266static void 267ofnstop(of) 268 struct ofn_softc *of; 269{ 270 untimeout(ofntimer, of); 271 of->sc_ethercom.ec_if.if_flags &= ~IFF_RUNNING; 272} 273 274static void 275ofnstart(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 322static int 323ofnioctl(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 368static void 369ofnwatchdog(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 381static void 382ipkdbofstart(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 391static void 392ipkdbofleave(kip) 393 struct ipkdb_if *kip; 394{ 395} 396 397static int 398ipkdbofrcv(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 413static void 414ipkdbofsend(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 422static int 423ipkdbprobe(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