1 1.63 thorpej /* $NetBSD: ofnet.c,v 1.63 2020/01/29 06:18:17 thorpej Exp $ */ 2 1.1 ws 3 1.1 ws /* 4 1.1 ws * Copyright (C) 1995, 1996 Wolfgang Solfrank. 5 1.1 ws * Copyright (C) 1995, 1996 TooLs GmbH. 6 1.1 ws * All rights reserved. 7 1.1 ws * 8 1.1 ws * Redistribution and use in source and binary forms, with or without 9 1.1 ws * modification, are permitted provided that the following conditions 10 1.1 ws * are met: 11 1.1 ws * 1. Redistributions of source code must retain the above copyright 12 1.1 ws * notice, this list of conditions and the following disclaimer. 13 1.1 ws * 2. Redistributions in binary form must reproduce the above copyright 14 1.1 ws * notice, this list of conditions and the following disclaimer in the 15 1.1 ws * documentation and/or other materials provided with the distribution. 16 1.1 ws * 3. All advertising materials mentioning features or use of this software 17 1.1 ws * must display the following acknowledgement: 18 1.1 ws * This product includes software developed by TooLs GmbH. 19 1.1 ws * 4. The name of TooLs GmbH may not be used to endorse or promote products 20 1.1 ws * derived from this software without specific prior written permission. 21 1.1 ws * 22 1.1 ws * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR 23 1.1 ws * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 24 1.1 ws * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 25 1.1 ws * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 1.1 ws * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 27 1.1 ws * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 28 1.1 ws * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 29 1.1 ws * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 30 1.1 ws * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 31 1.1 ws * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 1.1 ws */ 33 1.23 lukem 34 1.23 lukem #include <sys/cdefs.h> 35 1.63 thorpej __KERNEL_RCSID(0, "$NetBSD: ofnet.c,v 1.63 2020/01/29 06:18:17 thorpej Exp $"); 36 1.23 lukem 37 1.1 ws #include "ofnet.h" 38 1.15 jonathan #include "opt_inet.h" 39 1.1 ws 40 1.1 ws #include <sys/param.h> 41 1.14 tv #include <sys/systm.h> 42 1.18 thorpej #include <sys/callout.h> 43 1.1 ws #include <sys/device.h> 44 1.21 matt #include <sys/disk.h> 45 1.1 ws #include <sys/ioctl.h> 46 1.1 ws #include <sys/mbuf.h> 47 1.1 ws #include <sys/socket.h> 48 1.1 ws #include <sys/syslog.h> 49 1.1 ws 50 1.1 ws #include <net/if.h> 51 1.5 is #include <net/if_ether.h> 52 1.58 msaitoh #include <net/bpf.h> 53 1.1 ws 54 1.1 ws #ifdef INET 55 1.1 ws #include <netinet/in.h> 56 1.5 is #include <netinet/if_inarp.h> 57 1.1 ws #endif 58 1.1 ws 59 1.1 ws #include <dev/ofw/openfirm.h> 60 1.1 ws 61 1.13 mycroft struct ofnet_softc { 62 1.52 mrg device_t sc_dev; 63 1.1 ws int sc_phandle; 64 1.1 ws int sc_ihandle; 65 1.5 is struct ethercom sc_ethercom; 66 1.18 thorpej struct callout sc_callout; 67 1.1 ws }; 68 1.1 ws 69 1.48 cegger static int ofnet_match (device_t, cfdata_t, void *); 70 1.48 cegger static void ofnet_attach (device_t, device_t, void *); 71 1.1 ws 72 1.52 mrg CFATTACH_DECL_NEW(ofnet, sizeof(struct ofnet_softc), 73 1.28 thorpej ofnet_match, ofnet_attach, NULL, NULL); 74 1.1 ws 75 1.21 matt static void ofnet_read (struct ofnet_softc *); 76 1.21 matt static void ofnet_timer (void *); 77 1.21 matt static void ofnet_init (struct ofnet_softc *); 78 1.21 matt static void ofnet_stop (struct ofnet_softc *); 79 1.21 matt 80 1.21 matt static void ofnet_start (struct ifnet *); 81 1.38 christos static int ofnet_ioctl (struct ifnet *, u_long, void *); 82 1.21 matt static void ofnet_watchdog (struct ifnet *); 83 1.1 ws 84 1.1 ws static int 85 1.48 cegger ofnet_match(device_t parent, cfdata_t match, void *aux) 86 1.1 ws { 87 1.13 mycroft struct ofbus_attach_args *oba = aux; 88 1.1 ws char type[32]; 89 1.1 ws int l; 90 1.33 perry 91 1.13 mycroft if (strcmp(oba->oba_busname, "ofw")) 92 1.13 mycroft return (0); 93 1.13 mycroft if ((l = OF_getprop(oba->oba_phandle, "device_type", type, 94 1.13 mycroft sizeof type - 1)) < 0) 95 1.1 ws return 0; 96 1.1 ws if (l >= sizeof type) 97 1.1 ws return 0; 98 1.1 ws type[l] = 0; 99 1.1 ws if (strcmp(type, "network")) 100 1.1 ws return 0; 101 1.1 ws return 1; 102 1.1 ws } 103 1.1 ws 104 1.1 ws static void 105 1.48 cegger ofnet_attach(device_t parent, device_t self, void *aux) 106 1.1 ws { 107 1.36 thorpej struct ofnet_softc *of = device_private(self); 108 1.5 is struct ifnet *ifp = &of->sc_ethercom.ec_if; 109 1.13 mycroft struct ofbus_attach_args *oba = aux; 110 1.1 ws char path[256]; 111 1.1 ws int l; 112 1.5 is u_int8_t myaddr[ETHER_ADDR_LEN]; 113 1.33 perry 114 1.52 mrg of->sc_dev = self; 115 1.52 mrg 116 1.13 mycroft of->sc_phandle = oba->oba_phandle; 117 1.60 maxv 118 1.13 mycroft if ((l = OF_package_to_path(oba->oba_phandle, path, 119 1.13 mycroft sizeof path - 1)) < 0 || 120 1.13 mycroft l >= sizeof path || 121 1.13 mycroft (path[l] = 0, !(of->sc_ihandle = OF_open(path)))) 122 1.13 mycroft panic("ofnet_attach: unable to open"); 123 1.13 mycroft if (OF_getprop(oba->oba_phandle, "mac-address", myaddr, 124 1.13 mycroft sizeof myaddr) < 0) 125 1.13 mycroft panic("ofnet_attach: no mac-address"); 126 1.5 is printf(": address %s\n", ether_sprintf(myaddr)); 127 1.18 thorpej 128 1.40 ad callout_init(&of->sc_callout, 0); 129 1.18 thorpej 130 1.52 mrg strlcpy(ifp->if_xname, device_xname(of->sc_dev), IFNAMSIZ); 131 1.1 ws ifp->if_softc = of; 132 1.13 mycroft ifp->if_start = ofnet_start; 133 1.13 mycroft ifp->if_ioctl = ofnet_ioctl; 134 1.13 mycroft ifp->if_watchdog = ofnet_watchdog; 135 1.62 msaitoh ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX; 136 1.24 itojun IFQ_SET_READY(&ifp->if_snd); 137 1.1 ws 138 1.1 ws if_attach(ifp); 139 1.5 is ether_ifattach(ifp, myaddr); 140 1.1 ws } 141 1.1 ws 142 1.32 thorpej static char buf[ETHER_MAX_LEN]; 143 1.1 ws 144 1.1 ws static void 145 1.21 matt ofnet_read(struct ofnet_softc *of) 146 1.1 ws { 147 1.5 is struct ifnet *ifp = &of->sc_ethercom.ec_if; 148 1.1 ws struct mbuf *m, **mp, *head; 149 1.25 chs int s, l, len; 150 1.1 ws char *bufp; 151 1.1 ws 152 1.25 chs s = splnet(); 153 1.60 maxv 154 1.25 chs for (;;) { 155 1.30 chs len = OF_read(of->sc_ihandle, buf, sizeof buf); 156 1.30 chs if (len == -2 || len == 0) 157 1.30 chs break; 158 1.1 ws if (len < sizeof(struct ether_header)) { 159 1.63 thorpej if_statinc(ifp, if_ierrors); 160 1.1 ws continue; 161 1.1 ws } 162 1.1 ws bufp = buf; 163 1.22 billc 164 1.32 thorpej /* 165 1.32 thorpej * We don't know if the interface included the FCS 166 1.32 thorpej * or not. For now, assume that it did if we got 167 1.32 thorpej * a packet length that looks like it could include 168 1.32 thorpej * the FCS. 169 1.32 thorpej * 170 1.32 thorpej * XXX Yuck. 171 1.32 thorpej */ 172 1.32 thorpej if (len > ETHER_MAX_LEN - ETHER_CRC_LEN) 173 1.32 thorpej len = ETHER_MAX_LEN - ETHER_CRC_LEN; 174 1.32 thorpej 175 1.1 ws /* Allocate a header mbuf */ 176 1.1 ws MGETHDR(m, M_DONTWAIT, MT_DATA); 177 1.1 ws if (m == 0) { 178 1.63 thorpej if_statinc(ifp, if_ierrors); 179 1.1 ws continue; 180 1.1 ws } 181 1.55 ozaki m_set_rcvif(m, ifp); 182 1.1 ws m->m_pkthdr.len = len; 183 1.22 billc 184 1.1 ws l = MHLEN; 185 1.1 ws head = 0; 186 1.1 ws mp = &head; 187 1.33 perry 188 1.1 ws while (len > 0) { 189 1.1 ws if (head) { 190 1.1 ws MGET(m, M_DONTWAIT, MT_DATA); 191 1.1 ws if (m == 0) { 192 1.63 thorpej if_statinc(ifp, if_ierrors); 193 1.1 ws m_freem(head); 194 1.1 ws head = 0; 195 1.1 ws break; 196 1.1 ws } 197 1.1 ws l = MLEN; 198 1.1 ws } 199 1.1 ws if (len >= MINCLSIZE) { 200 1.1 ws MCLGET(m, M_DONTWAIT); 201 1.8 mycroft if ((m->m_flags & M_EXT) == 0) { 202 1.63 thorpej if_statinc(ifp, if_ierrors); 203 1.9 mycroft m_free(m); 204 1.7 mycroft m_freem(head); 205 1.7 mycroft head = 0; 206 1.7 mycroft break; 207 1.7 mycroft } 208 1.7 mycroft l = MCLBYTES; 209 1.1 ws } 210 1.12 cgd 211 1.12 cgd /* 212 1.12 cgd * Make sure the data after the Ethernet header 213 1.12 cgd * is aligned. 214 1.12 cgd * 215 1.12 cgd * XXX Assumes the device is an ethernet, but 216 1.12 cgd * XXX then so does other code in this driver. 217 1.12 cgd */ 218 1.12 cgd if (head == NULL) { 219 1.39 matt char *newdata = (char *)ALIGN(m->m_data + 220 1.12 cgd sizeof(struct ether_header)) - 221 1.12 cgd sizeof(struct ether_header); 222 1.12 cgd l -= newdata - m->m_data; 223 1.12 cgd m->m_data = newdata; 224 1.12 cgd } 225 1.12 cgd 226 1.61 riastrad m->m_len = l = uimin(len, l); 227 1.45 cegger memcpy(mtod(m, char *), bufp, l); 228 1.1 ws bufp += l; 229 1.1 ws len -= l; 230 1.1 ws *mp = m; 231 1.1 ws mp = &m->m_next; 232 1.1 ws } 233 1.1 ws if (head == 0) 234 1.1 ws continue; 235 1.1 ws 236 1.54 ozaki if_percpuq_enqueue(ifp->if_percpuq, head); 237 1.1 ws } 238 1.25 chs splx(s); 239 1.1 ws } 240 1.1 ws 241 1.1 ws static void 242 1.43 dsl ofnet_timer(void *arg) 243 1.1 ws { 244 1.16 thorpej struct ofnet_softc *of = arg; 245 1.16 thorpej 246 1.13 mycroft ofnet_read(of); 247 1.18 thorpej callout_reset(&of->sc_callout, 1, ofnet_timer, of); 248 1.1 ws } 249 1.1 ws 250 1.1 ws static void 251 1.21 matt ofnet_init(struct ofnet_softc *of) 252 1.1 ws { 253 1.5 is struct ifnet *ifp = &of->sc_ethercom.ec_if; 254 1.1 ws 255 1.1 ws if (ifp->if_flags & IFF_RUNNING) 256 1.1 ws return; 257 1.1 ws 258 1.1 ws ifp->if_flags |= IFF_RUNNING; 259 1.1 ws /* Start reading from interface */ 260 1.13 mycroft ofnet_timer(of); 261 1.1 ws /* Attempt to start output */ 262 1.13 mycroft ofnet_start(ifp); 263 1.1 ws } 264 1.1 ws 265 1.1 ws static void 266 1.21 matt ofnet_stop(struct ofnet_softc *of) 267 1.1 ws { 268 1.18 thorpej callout_stop(&of->sc_callout); 269 1.5 is of->sc_ethercom.ec_if.if_flags &= ~IFF_RUNNING; 270 1.1 ws } 271 1.1 ws 272 1.1 ws static void 273 1.21 matt ofnet_start(struct ifnet *ifp) 274 1.1 ws { 275 1.13 mycroft struct ofnet_softc *of = ifp->if_softc; 276 1.1 ws struct mbuf *m, *m0; 277 1.1 ws char *bufp; 278 1.1 ws int len; 279 1.33 perry 280 1.1 ws if (!(ifp->if_flags & IFF_RUNNING)) 281 1.1 ws return; 282 1.1 ws 283 1.1 ws for (;;) { 284 1.1 ws /* First try reading any packets */ 285 1.13 mycroft ofnet_read(of); 286 1.33 perry 287 1.1 ws /* Now get the first packet on the queue */ 288 1.24 itojun IFQ_DEQUEUE(&ifp->if_snd, m0); 289 1.1 ws if (!m0) 290 1.1 ws return; 291 1.33 perry 292 1.1 ws if (!(m0->m_flags & M_PKTHDR)) 293 1.13 mycroft panic("ofnet_start: no header mbuf"); 294 1.1 ws len = m0->m_pkthdr.len; 295 1.6 thorpej 296 1.59 msaitoh bpf_mtap(ifp, m0, BPF_D_OUT); 297 1.6 thorpej 298 1.1 ws if (len > ETHERMTU + sizeof(struct ether_header)) { 299 1.1 ws /* packet too large, toss it */ 300 1.63 thorpej if_statinc(ifp, if_oerrors); 301 1.1 ws m_freem(m0); 302 1.1 ws continue; 303 1.1 ws } 304 1.1 ws 305 1.21 matt for (bufp = buf; (m = m0) != NULL;) { 306 1.46 tsutsui memcpy(bufp, mtod(m, char *), m->m_len); 307 1.1 ws bufp += m->m_len; 308 1.56 christos m0 = m_free(m); 309 1.1 ws } 310 1.22 billc 311 1.22 billc /* 312 1.22 billc * We don't know if the interface will auto-pad for 313 1.22 billc * us, so make sure it's at least as large as a 314 1.22 billc * minimum size Ethernet packet. 315 1.22 billc */ 316 1.22 billc 317 1.31 bouyer if (len < (ETHER_MIN_LEN - ETHER_CRC_LEN)) { 318 1.31 bouyer memset(bufp, 0, ETHER_MIN_LEN - ETHER_CRC_LEN - len); 319 1.31 bouyer bufp += ETHER_MIN_LEN - ETHER_CRC_LEN - len; 320 1.31 bouyer } else 321 1.22 billc len = bufp - buf; 322 1.22 billc 323 1.22 billc if (OF_write(of->sc_ihandle, buf, len) != len) 324 1.63 thorpej if_statinc(ifp, if_oerrors); 325 1.1 ws else 326 1.63 thorpej if_statinc(ifp, if_opackets); 327 1.1 ws } 328 1.1 ws } 329 1.1 ws 330 1.1 ws static int 331 1.38 christos ofnet_ioctl(struct ifnet *ifp, u_long cmd, void *data) 332 1.1 ws { 333 1.13 mycroft struct ofnet_softc *of = ifp->if_softc; 334 1.1 ws struct ifaddr *ifa = (struct ifaddr *)data; 335 1.21 matt /* struct ifreq *ifr = (struct ifreq *)data; */ 336 1.1 ws int error = 0; 337 1.33 perry 338 1.1 ws switch (cmd) { 339 1.42 dyoung case SIOCINITIFADDR: 340 1.1 ws ifp->if_flags |= IFF_UP; 341 1.33 perry 342 1.1 ws switch (ifa->ifa_addr->sa_family) { 343 1.1 ws #ifdef INET 344 1.1 ws case AF_INET: 345 1.5 is arp_ifinit(ifp, ifa); 346 1.1 ws break; 347 1.1 ws #endif 348 1.1 ws default: 349 1.1 ws break; 350 1.1 ws } 351 1.13 mycroft ofnet_init(of); 352 1.1 ws break; 353 1.1 ws case SIOCSIFFLAGS: 354 1.42 dyoung if ((error = ifioctl_common(ifp, cmd, data)) != 0) 355 1.42 dyoung break; 356 1.42 dyoung /* XXX re-use ether_ioctl() */ 357 1.42 dyoung switch (ifp->if_flags & (IFF_UP|IFF_RUNNING)) { 358 1.42 dyoung case IFF_RUNNING: 359 1.1 ws /* If interface is down, but running, stop it. */ 360 1.13 mycroft ofnet_stop(of); 361 1.42 dyoung break; 362 1.42 dyoung case IFF_UP: 363 1.1 ws /* If interface is up, but not running, start it. */ 364 1.13 mycroft ofnet_init(of); 365 1.42 dyoung break; 366 1.42 dyoung default: 367 1.1 ws /* Other flags are ignored. */ 368 1.42 dyoung break; 369 1.1 ws } 370 1.1 ws break; 371 1.1 ws default: 372 1.42 dyoung error = ether_ioctl(ifp, cmd, data); 373 1.1 ws break; 374 1.1 ws } 375 1.1 ws return error; 376 1.1 ws } 377 1.1 ws 378 1.1 ws static void 379 1.21 matt ofnet_watchdog(struct ifnet *ifp) 380 1.1 ws { 381 1.13 mycroft struct ofnet_softc *of = ifp->if_softc; 382 1.33 perry 383 1.52 mrg log(LOG_ERR, "%s: device timeout\n", device_xname(of->sc_dev)); 384 1.63 thorpej if_statinc(ifp, if_oerrors); 385 1.13 mycroft ofnet_stop(of); 386 1.13 mycroft ofnet_init(of); 387 1.1 ws } 388