ofnet.c revision 1.53
1/* $NetBSD: ofnet.c,v 1.53 2012/10/27 17:18:28 chs 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 34#include <sys/cdefs.h> 35__KERNEL_RCSID(0, "$NetBSD: ofnet.c,v 1.53 2012/10/27 17:18:28 chs Exp $"); 36 37#include "ofnet.h" 38#include "opt_inet.h" 39 40#include <sys/param.h> 41#include <sys/systm.h> 42#include <sys/callout.h> 43#include <sys/device.h> 44#include <sys/disk.h> 45#include <sys/ioctl.h> 46#include <sys/mbuf.h> 47#include <sys/socket.h> 48#include <sys/syslog.h> 49 50#include <net/if.h> 51#include <net/if_ether.h> 52 53#ifdef INET 54#include <netinet/in.h> 55#include <netinet/if_inarp.h> 56#endif 57 58#include <net/bpf.h> 59#include <net/bpfdesc.h> 60 61#include <dev/ofw/openfirm.h> 62 63#if NIPKDB_OFN > 0 64#include <ipkdb/ipkdb.h> 65#include <machine/ipkdb.h> 66 67CFATTACH_DECL_NEW(ipkdb_ofn, 0, 68 ipkdb_probe, ipkdb_attach, NULL, NULL); 69 70static struct ipkdb_if *kifp; 71static struct ofnet_softc *ipkdb_of; 72 73static int ipkdbprobe (cfdata_t, void *); 74#endif 75 76struct ofnet_softc { 77 device_t sc_dev; 78 int sc_phandle; 79 int sc_ihandle; 80 struct ethercom sc_ethercom; 81 struct callout sc_callout; 82}; 83 84static int ofnet_match (device_t, cfdata_t, void *); 85static void ofnet_attach (device_t, device_t, void *); 86 87CFATTACH_DECL_NEW(ofnet, sizeof(struct ofnet_softc), 88 ofnet_match, ofnet_attach, NULL, NULL); 89 90static void ofnet_read (struct ofnet_softc *); 91static void ofnet_timer (void *); 92static void ofnet_init (struct ofnet_softc *); 93static void ofnet_stop (struct ofnet_softc *); 94 95static void ofnet_start (struct ifnet *); 96static int ofnet_ioctl (struct ifnet *, u_long, void *); 97static void ofnet_watchdog (struct ifnet *); 98 99static int 100ofnet_match(device_t parent, cfdata_t match, void *aux) 101{ 102 struct ofbus_attach_args *oba = aux; 103 char type[32]; 104 int l; 105 106#if NIPKDB_OFN > 0 107 if (!parent) 108 return ipkdbprobe(match, aux); 109#endif 110 if (strcmp(oba->oba_busname, "ofw")) 111 return (0); 112 if ((l = OF_getprop(oba->oba_phandle, "device_type", type, 113 sizeof type - 1)) < 0) 114 return 0; 115 if (l >= sizeof type) 116 return 0; 117 type[l] = 0; 118 if (strcmp(type, "network")) 119 return 0; 120 return 1; 121} 122 123static void 124ofnet_attach(device_t parent, device_t self, void *aux) 125{ 126 struct ofnet_softc *of = device_private(self); 127 struct ifnet *ifp = &of->sc_ethercom.ec_if; 128 struct ofbus_attach_args *oba = aux; 129 char path[256]; 130 int l; 131 u_int8_t myaddr[ETHER_ADDR_LEN]; 132 133 of->sc_dev = self; 134 135 of->sc_phandle = oba->oba_phandle; 136#if NIPKDB_OFN > 0 137 if (kifp && 138 kifp->unit - 1 == device_unit(of->sc_dev) && 139 OF_instance_to_package(kifp->port) == oba->oba_phandle) { 140 ipkdb_of = of; 141 of->sc_ihandle = kifp->port; 142 } else 143#endif 144 if ((l = OF_package_to_path(oba->oba_phandle, path, 145 sizeof path - 1)) < 0 || 146 l >= sizeof path || 147 (path[l] = 0, !(of->sc_ihandle = OF_open(path)))) 148 panic("ofnet_attach: unable to open"); 149 if (OF_getprop(oba->oba_phandle, "mac-address", myaddr, 150 sizeof myaddr) < 0) 151 panic("ofnet_attach: no mac-address"); 152 printf(": address %s\n", ether_sprintf(myaddr)); 153 154 callout_init(&of->sc_callout, 0); 155 156 strlcpy(ifp->if_xname, device_xname(of->sc_dev), IFNAMSIZ); 157 ifp->if_softc = of; 158 ifp->if_start = ofnet_start; 159 ifp->if_ioctl = ofnet_ioctl; 160 ifp->if_watchdog = ofnet_watchdog; 161 ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS; 162 IFQ_SET_READY(&ifp->if_snd); 163 164 if_attach(ifp); 165 ether_ifattach(ifp, myaddr); 166} 167 168static char buf[ETHER_MAX_LEN]; 169 170static void 171ofnet_read(struct ofnet_softc *of) 172{ 173 struct ifnet *ifp = &of->sc_ethercom.ec_if; 174 struct mbuf *m, **mp, *head; 175 int s, l, len; 176 char *bufp; 177 178 s = splnet(); 179#if NIPKDB_OFN > 0 180 ipkdbrint(kifp, ifp); 181#endif 182 for (;;) { 183 len = OF_read(of->sc_ihandle, buf, sizeof buf); 184 if (len == -2 || len == 0) 185 break; 186 if (len < sizeof(struct ether_header)) { 187 ifp->if_ierrors++; 188 continue; 189 } 190 bufp = buf; 191 192 /* 193 * We don't know if the interface included the FCS 194 * or not. For now, assume that it did if we got 195 * a packet length that looks like it could include 196 * the FCS. 197 * 198 * XXX Yuck. 199 */ 200 if (len > ETHER_MAX_LEN - ETHER_CRC_LEN) 201 len = ETHER_MAX_LEN - ETHER_CRC_LEN; 202 203 /* Allocate a header mbuf */ 204 MGETHDR(m, M_DONTWAIT, MT_DATA); 205 if (m == 0) { 206 ifp->if_ierrors++; 207 continue; 208 } 209 m->m_pkthdr.rcvif = ifp; 210 m->m_pkthdr.len = len; 211 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 char *newdata = (char *)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 memcpy(mtod(m, char *), bufp, l); 256 bufp += l; 257 len -= l; 258 *mp = m; 259 mp = &m->m_next; 260 } 261 if (head == 0) 262 continue; 263 264 bpf_mtap(ifp, m); 265 ifp->if_ipackets++; 266 (*ifp->if_input)(ifp, head); 267 } 268 splx(s); 269} 270 271static void 272ofnet_timer(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 280static void 281ofnet_init(struct ofnet_softc *of) 282{ 283 struct ifnet *ifp = &of->sc_ethercom.ec_if; 284 285 if (ifp->if_flags & IFF_RUNNING) 286 return; 287 288 ifp->if_flags |= IFF_RUNNING; 289 /* Start reading from interface */ 290 ofnet_timer(of); 291 /* Attempt to start output */ 292 ofnet_start(ifp); 293} 294 295static void 296ofnet_stop(struct ofnet_softc *of) 297{ 298 callout_stop(&of->sc_callout); 299 of->sc_ethercom.ec_if.if_flags &= ~IFF_RUNNING; 300} 301 302static void 303ofnet_start(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 IFQ_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 bpf_mtap(ifp, m0); 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) != NULL;) { 336 memcpy(bufp, mtod(m, char *), m->m_len); 337 bufp += m->m_len; 338 MFREE(m, m0); 339 } 340 341 /* 342 * We don't know if the interface will auto-pad for 343 * us, so make sure it's at least as large as a 344 * minimum size Ethernet packet. 345 */ 346 347 if (len < (ETHER_MIN_LEN - ETHER_CRC_LEN)) { 348 memset(bufp, 0, ETHER_MIN_LEN - ETHER_CRC_LEN - len); 349 bufp += ETHER_MIN_LEN - ETHER_CRC_LEN - len; 350 } else 351 len = bufp - buf; 352 353 if (OF_write(of->sc_ihandle, buf, len) != len) 354 ifp->if_oerrors++; 355 else 356 ifp->if_opackets++; 357 } 358} 359 360static int 361ofnet_ioctl(struct ifnet *ifp, u_long cmd, void *data) 362{ 363 struct ofnet_softc *of = ifp->if_softc; 364 struct ifaddr *ifa = (struct ifaddr *)data; 365 /* struct ifreq *ifr = (struct ifreq *)data; */ 366 int error = 0; 367 368 switch (cmd) { 369 case SIOCINITIFADDR: 370 ifp->if_flags |= IFF_UP; 371 372 switch (ifa->ifa_addr->sa_family) { 373#ifdef INET 374 case AF_INET: 375 arp_ifinit(ifp, ifa); 376 break; 377#endif 378 default: 379 break; 380 } 381 ofnet_init(of); 382 break; 383 case SIOCSIFFLAGS: 384 if ((error = ifioctl_common(ifp, cmd, data)) != 0) 385 break; 386 /* XXX re-use ether_ioctl() */ 387 switch (ifp->if_flags & (IFF_UP|IFF_RUNNING)) { 388 case IFF_RUNNING: 389 /* If interface is down, but running, stop it. */ 390 ofnet_stop(of); 391 break; 392 case IFF_UP: 393 /* If interface is up, but not running, start it. */ 394 ofnet_init(of); 395 break; 396 default: 397 /* Other flags are ignored. */ 398 break; 399 } 400 break; 401 default: 402 error = ether_ioctl(ifp, cmd, data); 403 break; 404 } 405 return error; 406} 407 408static void 409ofnet_watchdog(struct ifnet *ifp) 410{ 411 struct ofnet_softc *of = ifp->if_softc; 412 413 log(LOG_ERR, "%s: device timeout\n", device_xname(of->sc_dev)); 414 ifp->if_oerrors++; 415 ofnet_stop(of); 416 ofnet_init(of); 417} 418 419#if NIPKDB_OFN > 0 420static void 421ipkdbofstart(struct ipkdb_if *kip) 422{ 423 if (ipkdb_of) 424 ipkdbattach(kip, &ipkdb_of->sc_ethercom); 425} 426 427static void 428ipkdbofleave(struct ipkdb_if *kip) 429{ 430} 431 432static int 433ipkdbofrcv(struct ipkdb_if *kip, u_char *buf, int poll) 434{ 435 int l; 436 437 do { 438 l = OF_read(kip->port, buf, ETHERMTU); 439 if (l < 0) 440 l = 0; 441 } while (!poll && !l); 442 return l; 443} 444 445static void 446ipkdbofsend(struct ipkdb_if *kip, u_char *buf, int l) 447{ 448 OF_write(kip->port, buf, l); 449} 450 451static int 452ipkdbprobe(cfdata_t match, void *aux) 453{ 454 struct ipkdb_if *kip = aux; 455 static char name[256]; 456 int len; 457 int phandle; 458 459 kip->unit = match->cf_unit + 1; 460 461 if (!(kip->port = OF_open("net"))) 462 return -1; 463 if ((len = OF_instance_to_path(kip->port, name, sizeof name - 1)) < 0 || 464 len >= sizeof name) 465 return -1; 466 name[len] = 0; 467 if ((phandle = OF_instance_to_package(kip->port)) == -1) 468 return -1; 469 if (OF_getprop(phandle, "mac-address", kip->myenetaddr, 470 sizeof kip->myenetaddr) < 0) 471 return -1; 472 473 kip->flags |= IPKDB_MYHW; 474 kip->name = name; 475 kip->start = ipkdbofstart; 476 kip->leave = ipkdbofleave; 477 kip->receive = ipkdbofrcv; 478 kip->send = ipkdbofsend; 479 480 kifp = kip; 481 482 return 0; 483} 484#endif 485