ofnet.c revision 1.58
1/* $NetBSD: ofnet.c,v 1.58 2018/06/22 04:17:42 msaitoh 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.58 2018/06/22 04:17:42 msaitoh 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#include <net/bpf.h> 53 54#ifdef INET 55#include <netinet/in.h> 56#include <netinet/if_inarp.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 65CFATTACH_DECL_NEW(ipkdb_ofn, 0, 66 ipkdb_probe, ipkdb_attach, NULL, NULL); 67 68static struct ipkdb_if *kifp; 69static struct ofnet_softc *ipkdb_of; 70 71static int ipkdbprobe (cfdata_t, void *); 72#endif 73 74struct ofnet_softc { 75 device_t sc_dev; 76 int sc_phandle; 77 int sc_ihandle; 78 struct ethercom sc_ethercom; 79 struct callout sc_callout; 80}; 81 82static int ofnet_match (device_t, cfdata_t, void *); 83static void ofnet_attach (device_t, device_t, void *); 84 85CFATTACH_DECL_NEW(ofnet, sizeof(struct ofnet_softc), 86 ofnet_match, ofnet_attach, NULL, NULL); 87 88static void ofnet_read (struct ofnet_softc *); 89static void ofnet_timer (void *); 90static void ofnet_init (struct ofnet_softc *); 91static void ofnet_stop (struct ofnet_softc *); 92 93static void ofnet_start (struct ifnet *); 94static int ofnet_ioctl (struct ifnet *, u_long, void *); 95static void ofnet_watchdog (struct ifnet *); 96 97static int 98ofnet_match(device_t parent, cfdata_t match, void *aux) 99{ 100 struct ofbus_attach_args *oba = aux; 101 char type[32]; 102 int l; 103 104#if NIPKDB_OFN > 0 105 if (!parent) 106 return ipkdbprobe(match, aux); 107#endif 108 if (strcmp(oba->oba_busname, "ofw")) 109 return (0); 110 if ((l = OF_getprop(oba->oba_phandle, "device_type", type, 111 sizeof type - 1)) < 0) 112 return 0; 113 if (l >= sizeof type) 114 return 0; 115 type[l] = 0; 116 if (strcmp(type, "network")) 117 return 0; 118 return 1; 119} 120 121static void 122ofnet_attach(device_t parent, device_t self, void *aux) 123{ 124 struct ofnet_softc *of = device_private(self); 125 struct ifnet *ifp = &of->sc_ethercom.ec_if; 126 struct ofbus_attach_args *oba = aux; 127 char path[256]; 128 int l; 129 u_int8_t myaddr[ETHER_ADDR_LEN]; 130 131 of->sc_dev = self; 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 166static char buf[ETHER_MAX_LEN]; 167 168static void 169ofnet_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_set_rcvif(m, 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 if_percpuq_enqueue(ifp->if_percpuq, head); 263 } 264 splx(s); 265} 266 267static void 268ofnet_timer(void *arg) 269{ 270 struct ofnet_softc *of = arg; 271 272 ofnet_read(of); 273 callout_reset(&of->sc_callout, 1, ofnet_timer, of); 274} 275 276static void 277ofnet_init(struct ofnet_softc *of) 278{ 279 struct ifnet *ifp = &of->sc_ethercom.ec_if; 280 281 if (ifp->if_flags & IFF_RUNNING) 282 return; 283 284 ifp->if_flags |= IFF_RUNNING; 285 /* Start reading from interface */ 286 ofnet_timer(of); 287 /* Attempt to start output */ 288 ofnet_start(ifp); 289} 290 291static void 292ofnet_stop(struct ofnet_softc *of) 293{ 294 callout_stop(&of->sc_callout); 295 of->sc_ethercom.ec_if.if_flags &= ~IFF_RUNNING; 296} 297 298static void 299ofnet_start(struct ifnet *ifp) 300{ 301 struct ofnet_softc *of = ifp->if_softc; 302 struct mbuf *m, *m0; 303 char *bufp; 304 int len; 305 306 if (!(ifp->if_flags & IFF_RUNNING)) 307 return; 308 309 for (;;) { 310 /* First try reading any packets */ 311 ofnet_read(of); 312 313 /* Now get the first packet on the queue */ 314 IFQ_DEQUEUE(&ifp->if_snd, m0); 315 if (!m0) 316 return; 317 318 if (!(m0->m_flags & M_PKTHDR)) 319 panic("ofnet_start: no header mbuf"); 320 len = m0->m_pkthdr.len; 321 322 bpf_mtap(ifp, m0); 323 324 if (len > ETHERMTU + sizeof(struct ether_header)) { 325 /* packet too large, toss it */ 326 ifp->if_oerrors++; 327 m_freem(m0); 328 continue; 329 } 330 331 for (bufp = buf; (m = m0) != NULL;) { 332 memcpy(bufp, mtod(m, char *), m->m_len); 333 bufp += m->m_len; 334 m0 = m_free(m); 335 } 336 337 /* 338 * We don't know if the interface will auto-pad for 339 * us, so make sure it's at least as large as a 340 * minimum size Ethernet packet. 341 */ 342 343 if (len < (ETHER_MIN_LEN - ETHER_CRC_LEN)) { 344 memset(bufp, 0, ETHER_MIN_LEN - ETHER_CRC_LEN - len); 345 bufp += ETHER_MIN_LEN - ETHER_CRC_LEN - len; 346 } else 347 len = bufp - buf; 348 349 if (OF_write(of->sc_ihandle, buf, len) != len) 350 ifp->if_oerrors++; 351 else 352 ifp->if_opackets++; 353 } 354} 355 356static int 357ofnet_ioctl(struct ifnet *ifp, u_long cmd, void *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 SIOCINITIFADDR: 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 ((error = ifioctl_common(ifp, cmd, data)) != 0) 381 break; 382 /* XXX re-use ether_ioctl() */ 383 switch (ifp->if_flags & (IFF_UP|IFF_RUNNING)) { 384 case IFF_RUNNING: 385 /* If interface is down, but running, stop it. */ 386 ofnet_stop(of); 387 break; 388 case IFF_UP: 389 /* If interface is up, but not running, start it. */ 390 ofnet_init(of); 391 break; 392 default: 393 /* Other flags are ignored. */ 394 break; 395 } 396 break; 397 default: 398 error = ether_ioctl(ifp, cmd, data); 399 break; 400 } 401 return error; 402} 403 404static void 405ofnet_watchdog(struct ifnet *ifp) 406{ 407 struct ofnet_softc *of = ifp->if_softc; 408 409 log(LOG_ERR, "%s: device timeout\n", device_xname(of->sc_dev)); 410 ifp->if_oerrors++; 411 ofnet_stop(of); 412 ofnet_init(of); 413} 414 415#if NIPKDB_OFN > 0 416static void 417ipkdbofstart(struct ipkdb_if *kip) 418{ 419 if (ipkdb_of) 420 ipkdbattach(kip, &ipkdb_of->sc_ethercom); 421} 422 423static void 424ipkdbofleave(struct ipkdb_if *kip) 425{ 426} 427 428static int 429ipkdbofrcv(struct ipkdb_if *kip, u_char *buf, 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(struct ipkdb_if *kip, u_char *buf, int l) 443{ 444 OF_write(kip->port, buf, l); 445} 446 447static int 448ipkdbprobe(cfdata_t match, void *aux) 449{ 450 struct ipkdb_if *kip = aux; 451 static char name[256]; 452 int len; 453 int phandle; 454 455 kip->unit = match->cf_unit + 1; 456 457 if (!(kip->port = OF_open("net"))) 458 return -1; 459 if ((len = OF_instance_to_path(kip->port, name, sizeof name - 1)) < 0 || 460 len >= sizeof name) 461 return -1; 462 name[len] = 0; 463 if ((phandle = OF_instance_to_package(kip->port)) == -1) 464 return -1; 465 if (OF_getprop(phandle, "mac-address", kip->myenetaddr, 466 sizeof kip->myenetaddr) < 0) 467 return -1; 468 469 kip->flags |= IPKDB_MYHW; 470 kip->name = name; 471 kip->start = ipkdbofstart; 472 kip->leave = ipkdbofleave; 473 kip->receive = ipkdbofrcv; 474 kip->send = ipkdbofsend; 475 476 kifp = kip; 477 478 return 0; 479} 480#endif 481