1 /* $NetBSD: ofnet.c,v 1.51 2010/08/08 18:22:39 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.51 2010/08/08 18:22:39 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(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 struct device 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(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_phandle = oba->oba_phandle; 134 #if NIPKDB_OFN > 0 135 if (kifp && 136 kifp->unit - 1 == device_unit(&of->sc_dev) && 137 OF_instance_to_package(kifp->port) == oba->oba_phandle) { 138 ipkdb_of = of; 139 of->sc_ihandle = kifp->port; 140 } else 141 #endif 142 if ((l = OF_package_to_path(oba->oba_phandle, path, 143 sizeof path - 1)) < 0 || 144 l >= sizeof path || 145 (path[l] = 0, !(of->sc_ihandle = OF_open(path)))) 146 panic("ofnet_attach: unable to open"); 147 if (OF_getprop(oba->oba_phandle, "mac-address", myaddr, 148 sizeof myaddr) < 0) 149 panic("ofnet_attach: no mac-address"); 150 printf(": address %s\n", ether_sprintf(myaddr)); 151 152 callout_init(&of->sc_callout, 0); 153 154 strlcpy(ifp->if_xname, device_xname(&of->sc_dev), IFNAMSIZ); 155 ifp->if_softc = of; 156 ifp->if_start = ofnet_start; 157 ifp->if_ioctl = ofnet_ioctl; 158 ifp->if_watchdog = ofnet_watchdog; 159 ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS; 160 IFQ_SET_READY(&ifp->if_snd); 161 162 if_attach(ifp); 163 ether_ifattach(ifp, myaddr); 164 } 165 166 static char buf[ETHER_MAX_LEN]; 167 168 static void 169 ofnet_read(struct ofnet_softc *of) 170 { 171 struct ifnet *ifp = &of->sc_ethercom.ec_if; 172 struct mbuf *m, **mp, *head; 173 int s, l, len; 174 char *bufp; 175 176 s = splnet(); 177 #if NIPKDB_OFN > 0 178 ipkdbrint(kifp, ifp); 179 #endif 180 for (;;) { 181 len = OF_read(of->sc_ihandle, buf, sizeof buf); 182 if (len == -2 || len == 0) 183 break; 184 if (len < sizeof(struct ether_header)) { 185 ifp->if_ierrors++; 186 continue; 187 } 188 bufp = buf; 189 190 /* 191 * We don't know if the interface included the FCS 192 * or not. For now, assume that it did if we got 193 * a packet length that looks like it could include 194 * the FCS. 195 * 196 * XXX Yuck. 197 */ 198 if (len > ETHER_MAX_LEN - ETHER_CRC_LEN) 199 len = ETHER_MAX_LEN - ETHER_CRC_LEN; 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 210 l = MHLEN; 211 head = 0; 212 mp = &head; 213 214 while (len > 0) { 215 if (head) { 216 MGET(m, M_DONTWAIT, MT_DATA); 217 if (m == 0) { 218 ifp->if_ierrors++; 219 m_freem(head); 220 head = 0; 221 break; 222 } 223 l = MLEN; 224 } 225 if (len >= MINCLSIZE) { 226 MCLGET(m, M_DONTWAIT); 227 if ((m->m_flags & M_EXT) == 0) { 228 ifp->if_ierrors++; 229 m_free(m); 230 m_freem(head); 231 head = 0; 232 break; 233 } 234 l = MCLBYTES; 235 } 236 237 /* 238 * Make sure the data after the Ethernet header 239 * is aligned. 240 * 241 * XXX Assumes the device is an ethernet, but 242 * XXX then so does other code in this driver. 243 */ 244 if (head == NULL) { 245 char *newdata = (char *)ALIGN(m->m_data + 246 sizeof(struct ether_header)) - 247 sizeof(struct ether_header); 248 l -= newdata - m->m_data; 249 m->m_data = newdata; 250 } 251 252 m->m_len = l = min(len, l); 253 memcpy(mtod(m, char *), bufp, l); 254 bufp += l; 255 len -= l; 256 *mp = m; 257 mp = &m->m_next; 258 } 259 if (head == 0) 260 continue; 261 262 bpf_mtap(ifp, m); 263 ifp->if_ipackets++; 264 (*ifp->if_input)(ifp, head); 265 } 266 splx(s); 267 } 268 269 static void 270 ofnet_timer(void *arg) 271 { 272 struct ofnet_softc *of = arg; 273 274 ofnet_read(of); 275 callout_reset(&of->sc_callout, 1, ofnet_timer, of); 276 } 277 278 static void 279 ofnet_init(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 293 static void 294 ofnet_stop(struct ofnet_softc *of) 295 { 296 callout_stop(&of->sc_callout); 297 of->sc_ethercom.ec_if.if_flags &= ~IFF_RUNNING; 298 } 299 300 static void 301 ofnet_start(struct ifnet *ifp) 302 { 303 struct ofnet_softc *of = ifp->if_softc; 304 struct mbuf *m, *m0; 305 char *bufp; 306 int len; 307 308 if (!(ifp->if_flags & IFF_RUNNING)) 309 return; 310 311 for (;;) { 312 /* First try reading any packets */ 313 ofnet_read(of); 314 315 /* Now get the first packet on the queue */ 316 IFQ_DEQUEUE(&ifp->if_snd, m0); 317 if (!m0) 318 return; 319 320 if (!(m0->m_flags & M_PKTHDR)) 321 panic("ofnet_start: no header mbuf"); 322 len = m0->m_pkthdr.len; 323 324 bpf_mtap(ifp, m0); 325 326 if (len > ETHERMTU + sizeof(struct ether_header)) { 327 /* packet too large, toss it */ 328 ifp->if_oerrors++; 329 m_freem(m0); 330 continue; 331 } 332 333 for (bufp = buf; (m = m0) != NULL;) { 334 memcpy(bufp, mtod(m, char *), m->m_len); 335 bufp += m->m_len; 336 MFREE(m, m0); 337 } 338 339 /* 340 * We don't know if the interface will auto-pad for 341 * us, so make sure it's at least as large as a 342 * minimum size Ethernet packet. 343 */ 344 345 if (len < (ETHER_MIN_LEN - ETHER_CRC_LEN)) { 346 memset(bufp, 0, ETHER_MIN_LEN - ETHER_CRC_LEN - len); 347 bufp += ETHER_MIN_LEN - ETHER_CRC_LEN - len; 348 } else 349 len = bufp - buf; 350 351 if (OF_write(of->sc_ihandle, buf, len) != len) 352 ifp->if_oerrors++; 353 else 354 ifp->if_opackets++; 355 } 356 } 357 358 static int 359 ofnet_ioctl(struct ifnet *ifp, u_long cmd, void *data) 360 { 361 struct ofnet_softc *of = ifp->if_softc; 362 struct ifaddr *ifa = (struct ifaddr *)data; 363 /* struct ifreq *ifr = (struct ifreq *)data; */ 364 int error = 0; 365 366 switch (cmd) { 367 case SIOCINITIFADDR: 368 ifp->if_flags |= IFF_UP; 369 370 switch (ifa->ifa_addr->sa_family) { 371 #ifdef INET 372 case AF_INET: 373 arp_ifinit(ifp, ifa); 374 break; 375 #endif 376 default: 377 break; 378 } 379 ofnet_init(of); 380 break; 381 case SIOCSIFFLAGS: 382 if ((error = ifioctl_common(ifp, cmd, data)) != 0) 383 break; 384 /* XXX re-use ether_ioctl() */ 385 switch (ifp->if_flags & (IFF_UP|IFF_RUNNING)) { 386 case IFF_RUNNING: 387 /* If interface is down, but running, stop it. */ 388 ofnet_stop(of); 389 break; 390 case IFF_UP: 391 /* If interface is up, but not running, start it. */ 392 ofnet_init(of); 393 break; 394 default: 395 /* Other flags are ignored. */ 396 break; 397 } 398 break; 399 default: 400 error = ether_ioctl(ifp, cmd, data); 401 break; 402 } 403 return error; 404 } 405 406 static void 407 ofnet_watchdog(struct ifnet *ifp) 408 { 409 struct ofnet_softc *of = ifp->if_softc; 410 411 log(LOG_ERR, "%s: device timeout\n", device_xname(&of->sc_dev)); 412 ifp->if_oerrors++; 413 ofnet_stop(of); 414 ofnet_init(of); 415 } 416 417 #if NIPKDB_OFN > 0 418 static void 419 ipkdbofstart(struct ipkdb_if *kip) 420 { 421 if (ipkdb_of) 422 ipkdbattach(kip, &ipkdb_of->sc_ethercom); 423 } 424 425 static void 426 ipkdbofleave(struct ipkdb_if *kip) 427 { 428 } 429 430 static int 431 ipkdbofrcv(struct ipkdb_if *kip, u_char *buf, int poll) 432 { 433 int l; 434 435 do { 436 l = OF_read(kip->port, buf, ETHERMTU); 437 if (l < 0) 438 l = 0; 439 } while (!poll && !l); 440 return l; 441 } 442 443 static void 444 ipkdbofsend(struct ipkdb_if *kip, u_char *buf, int l) 445 { 446 OF_write(kip->port, buf, l); 447 } 448 449 static int 450 ipkdbprobe(cfdata_t match, void *aux) 451 { 452 struct ipkdb_if *kip = aux; 453 static char name[256]; 454 int len; 455 int phandle; 456 457 kip->unit = match->cf_unit + 1; 458 459 if (!(kip->port = OF_open("net"))) 460 return -1; 461 if ((len = OF_instance_to_path(kip->port, name, sizeof name - 1)) < 0 || 462 len >= sizeof name) 463 return -1; 464 name[len] = 0; 465 if ((phandle = OF_instance_to_package(kip->port)) == -1) 466 return -1; 467 if (OF_getprop(phandle, "mac-address", kip->myenetaddr, 468 sizeof kip->myenetaddr) < 0) 469 return -1; 470 471 kip->flags |= IPKDB_MYHW; 472 kip->name = name; 473 kip->start = ipkdbofstart; 474 kip->leave = ipkdbofleave; 475 kip->receive = ipkdbofrcv; 476 kip->send = ipkdbofsend; 477 478 kifp = kip; 479 480 return 0; 481 } 482 #endif 483