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 67 CFATTACH_DECL_NEW(ipkdb_ofn, 0, 68 ipkdb_probe, ipkdb_attach, NULL, NULL); 69 70 static struct ipkdb_if *kifp; 71 static struct ofnet_softc *ipkdb_of; 72 73 static int ipkdbprobe (cfdata_t, void *); 74 #endif 75 76 struct 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 84 static int ofnet_match (device_t, cfdata_t, void *); 85 static void ofnet_attach (device_t, device_t, void *); 86 87 CFATTACH_DECL_NEW(ofnet, sizeof(struct ofnet_softc), 88 ofnet_match, ofnet_attach, NULL, NULL); 89 90 static void ofnet_read (struct ofnet_softc *); 91 static void ofnet_timer (void *); 92 static void ofnet_init (struct ofnet_softc *); 93 static void ofnet_stop (struct ofnet_softc *); 94 95 static void ofnet_start (struct ifnet *); 96 static int ofnet_ioctl (struct ifnet *, u_long, void *); 97 static void ofnet_watchdog (struct ifnet *); 98 99 static int 100 ofnet_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 123 static void 124 ofnet_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 168 static char buf[ETHER_MAX_LEN]; 169 170 static void 171 ofnet_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 271 static void 272 ofnet_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 280 static void 281 ofnet_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 295 static void 296 ofnet_stop(struct ofnet_softc *of) 297 { 298 callout_stop(&of->sc_callout); 299 of->sc_ethercom.ec_if.if_flags &= ~IFF_RUNNING; 300 } 301 302 static void 303 ofnet_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 360 static int 361 ofnet_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 408 static void 409 ofnet_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 420 static void 421 ipkdbofstart(struct ipkdb_if *kip) 422 { 423 if (ipkdb_of) 424 ipkdbattach(kip, &ipkdb_of->sc_ethercom); 425 } 426 427 static void 428 ipkdbofleave(struct ipkdb_if *kip) 429 { 430 } 431 432 static int 433 ipkdbofrcv(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 445 static void 446 ipkdbofsend(struct ipkdb_if *kip, u_char *buf, int l) 447 { 448 OF_write(kip->port, buf, l); 449 } 450 451 static int 452 ipkdbprobe(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