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