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