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