1/* $NetBSD: ofnet.c,v 1.63 2020/01/29 06:18:17 thorpej 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.63 2020/01/29 06:18:17 thorpej 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 61struct ofnet_softc { 62 device_t sc_dev; 63 int sc_phandle; 64 int sc_ihandle; 65 struct ethercom sc_ethercom; 66 struct callout sc_callout; 67}; 68 69static int ofnet_match (device_t, cfdata_t, void *); 70static void ofnet_attach (device_t, device_t, void *); 71 72CFATTACH_DECL_NEW(ofnet, sizeof(struct ofnet_softc), 73 ofnet_match, ofnet_attach, NULL, NULL); 74 75static void ofnet_read (struct ofnet_softc *); 76static void ofnet_timer (void *); 77static void ofnet_init (struct ofnet_softc *); 78static void ofnet_stop (struct ofnet_softc *); 79 80static void ofnet_start (struct ifnet *); 81static int ofnet_ioctl (struct ifnet *, u_long, void *); 82static void ofnet_watchdog (struct ifnet *); 83 84static int 85ofnet_match(device_t parent, cfdata_t match, void *aux) 86{ 87 struct ofbus_attach_args *oba = aux; 88 char type[32]; 89 int l; 90 91 if (strcmp(oba->oba_busname, "ofw")) 92 return (0); 93 if ((l = OF_getprop(oba->oba_phandle, "device_type", type, 94 sizeof type - 1)) < 0) 95 return 0; 96 if (l >= sizeof type) 97 return 0; 98 type[l] = 0; 99 if (strcmp(type, "network")) 100 return 0; 101 return 1; 102} 103 104static void 105ofnet_attach(device_t parent, device_t self, void *aux) 106{ 107 struct ofnet_softc *of = device_private(self); 108 struct ifnet *ifp = &of->sc_ethercom.ec_if; 109 struct ofbus_attach_args *oba = aux; 110 char path[256]; 111 int l; 112 u_int8_t myaddr[ETHER_ADDR_LEN]; 113 114 of->sc_dev = self; 115 116 of->sc_phandle = oba->oba_phandle; 117 118 if ((l = OF_package_to_path(oba->oba_phandle, path, 119 sizeof path - 1)) < 0 || 120 l >= sizeof path || 121 (path[l] = 0, !(of->sc_ihandle = OF_open(path)))) 122 panic("ofnet_attach: unable to open"); 123 if (OF_getprop(oba->oba_phandle, "mac-address", myaddr, 124 sizeof myaddr) < 0) 125 panic("ofnet_attach: no mac-address"); 126 printf(": address %s\n", ether_sprintf(myaddr)); 127 128 callout_init(&of->sc_callout, 0); 129 130 strlcpy(ifp->if_xname, device_xname(of->sc_dev), IFNAMSIZ); 131 ifp->if_softc = of; 132 ifp->if_start = ofnet_start; 133 ifp->if_ioctl = ofnet_ioctl; 134 ifp->if_watchdog = ofnet_watchdog; 135 ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX; 136 IFQ_SET_READY(&ifp->if_snd); 137 138 if_attach(ifp); 139 ether_ifattach(ifp, myaddr); 140} 141 142static char buf[ETHER_MAX_LEN]; 143 144static void 145ofnet_read(struct ofnet_softc *of) 146{ 147 struct ifnet *ifp = &of->sc_ethercom.ec_if; 148 struct mbuf *m, **mp, *head; 149 int s, l, len; 150 char *bufp; 151 152 s = splnet(); 153 154 for (;;) { 155 len = OF_read(of->sc_ihandle, buf, sizeof buf); 156 if (len == -2 || len == 0) 157 break; 158 if (len < sizeof(struct ether_header)) { 159 if_statinc(ifp, if_ierrors); 160 continue; 161 } 162 bufp = buf; 163 164 /* 165 * We don't know if the interface included the FCS 166 * or not. For now, assume that it did if we got 167 * a packet length that looks like it could include 168 * the FCS. 169 * 170 * XXX Yuck. 171 */ 172 if (len > ETHER_MAX_LEN - ETHER_CRC_LEN) 173 len = ETHER_MAX_LEN - ETHER_CRC_LEN; 174 175 /* Allocate a header mbuf */ 176 MGETHDR(m, M_DONTWAIT, MT_DATA); 177 if (m == 0) { 178 if_statinc(ifp, if_ierrors); 179 continue; 180 } 181 m_set_rcvif(m, ifp); 182 m->m_pkthdr.len = len; 183 184 l = MHLEN; 185 head = 0; 186 mp = &head; 187 188 while (len > 0) { 189 if (head) { 190 MGET(m, M_DONTWAIT, MT_DATA); 191 if (m == 0) { 192 if_statinc(ifp, if_ierrors); 193 m_freem(head); 194 head = 0; 195 break; 196 } 197 l = MLEN; 198 } 199 if (len >= MINCLSIZE) { 200 MCLGET(m, M_DONTWAIT); 201 if ((m->m_flags & M_EXT) == 0) { 202 if_statinc(ifp, if_ierrors); 203 m_free(m); 204 m_freem(head); 205 head = 0; 206 break; 207 } 208 l = MCLBYTES; 209 } 210 211 /* 212 * Make sure the data after the Ethernet header 213 * is aligned. 214 * 215 * XXX Assumes the device is an ethernet, but 216 * XXX then so does other code in this driver. 217 */ 218 if (head == NULL) { 219 char *newdata = (char *)ALIGN(m->m_data + 220 sizeof(struct ether_header)) - 221 sizeof(struct ether_header); 222 l -= newdata - m->m_data; 223 m->m_data = newdata; 224 } 225 226 m->m_len = l = uimin(len, l); 227 memcpy(mtod(m, char *), bufp, l); 228 bufp += l; 229 len -= l; 230 *mp = m; 231 mp = &m->m_next; 232 } 233 if (head == 0) 234 continue; 235 236 if_percpuq_enqueue(ifp->if_percpuq, head); 237 } 238 splx(s); 239} 240 241static void 242ofnet_timer(void *arg) 243{ 244 struct ofnet_softc *of = arg; 245 246 ofnet_read(of); 247 callout_reset(&of->sc_callout, 1, ofnet_timer, of); 248} 249 250static void 251ofnet_init(struct ofnet_softc *of) 252{ 253 struct ifnet *ifp = &of->sc_ethercom.ec_if; 254 255 if (ifp->if_flags & IFF_RUNNING) 256 return; 257 258 ifp->if_flags |= IFF_RUNNING; 259 /* Start reading from interface */ 260 ofnet_timer(of); 261 /* Attempt to start output */ 262 ofnet_start(ifp); 263} 264 265static void 266ofnet_stop(struct ofnet_softc *of) 267{ 268 callout_stop(&of->sc_callout); 269 of->sc_ethercom.ec_if.if_flags &= ~IFF_RUNNING; 270} 271 272static void 273ofnet_start(struct ifnet *ifp) 274{ 275 struct ofnet_softc *of = ifp->if_softc; 276 struct mbuf *m, *m0; 277 char *bufp; 278 int len; 279 280 if (!(ifp->if_flags & IFF_RUNNING)) 281 return; 282 283 for (;;) { 284 /* First try reading any packets */ 285 ofnet_read(of); 286 287 /* Now get the first packet on the queue */ 288 IFQ_DEQUEUE(&ifp->if_snd, m0); 289 if (!m0) 290 return; 291 292 if (!(m0->m_flags & M_PKTHDR)) 293 panic("ofnet_start: no header mbuf"); 294 len = m0->m_pkthdr.len; 295 296 bpf_mtap(ifp, m0, BPF_D_OUT); 297 298 if (len > ETHERMTU + sizeof(struct ether_header)) { 299 /* packet too large, toss it */ 300 if_statinc(ifp, if_oerrors); 301 m_freem(m0); 302 continue; 303 } 304 305 for (bufp = buf; (m = m0) != NULL;) { 306 memcpy(bufp, mtod(m, char *), m->m_len); 307 bufp += m->m_len; 308 m0 = m_free(m); 309 } 310 311 /* 312 * We don't know if the interface will auto-pad for 313 * us, so make sure it's at least as large as a 314 * minimum size Ethernet packet. 315 */ 316 317 if (len < (ETHER_MIN_LEN - ETHER_CRC_LEN)) { 318 memset(bufp, 0, ETHER_MIN_LEN - ETHER_CRC_LEN - len); 319 bufp += ETHER_MIN_LEN - ETHER_CRC_LEN - len; 320 } else 321 len = bufp - buf; 322 323 if (OF_write(of->sc_ihandle, buf, len) != len) 324 if_statinc(ifp, if_oerrors); 325 else 326 if_statinc(ifp, if_opackets); 327 } 328} 329 330static int 331ofnet_ioctl(struct ifnet *ifp, u_long cmd, void *data) 332{ 333 struct ofnet_softc *of = ifp->if_softc; 334 struct ifaddr *ifa = (struct ifaddr *)data; 335 /* struct ifreq *ifr = (struct ifreq *)data; */ 336 int error = 0; 337 338 switch (cmd) { 339 case SIOCINITIFADDR: 340 ifp->if_flags |= IFF_UP; 341 342 switch (ifa->ifa_addr->sa_family) { 343#ifdef INET 344 case AF_INET: 345 arp_ifinit(ifp, ifa); 346 break; 347#endif 348 default: 349 break; 350 } 351 ofnet_init(of); 352 break; 353 case SIOCSIFFLAGS: 354 if ((error = ifioctl_common(ifp, cmd, data)) != 0) 355 break; 356 /* XXX re-use ether_ioctl() */ 357 switch (ifp->if_flags & (IFF_UP|IFF_RUNNING)) { 358 case IFF_RUNNING: 359 /* If interface is down, but running, stop it. */ 360 ofnet_stop(of); 361 break; 362 case IFF_UP: 363 /* If interface is up, but not running, start it. */ 364 ofnet_init(of); 365 break; 366 default: 367 /* Other flags are ignored. */ 368 break; 369 } 370 break; 371 default: 372 error = ether_ioctl(ifp, cmd, data); 373 break; 374 } 375 return error; 376} 377 378static void 379ofnet_watchdog(struct ifnet *ifp) 380{ 381 struct ofnet_softc *of = ifp->if_softc; 382 383 log(LOG_ERR, "%s: device timeout\n", device_xname(of->sc_dev)); 384 if_statinc(ifp, if_oerrors); 385 ofnet_stop(of); 386 ofnet_init(of); 387} 388