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