pdq_ifsubr.c revision 48645
1/*- 2 * Copyright (c) 1995, 1996 Matt Thomas <matt@3am-software.com> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. The name of the author may not be used to endorse or promote products 11 * derived from this software withough specific prior written permission 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 18 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 19 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 20 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 * 24 * $Id: pdq_ifsubr.c,v 1.7 1998/02/20 13:11:45 bde Exp $ 25 * 26 */ 27 28/* 29 * DEC PDQ FDDI Controller; code for BSD derived operating systems 30 * 31 * This module provide bus independent BSD specific O/S functions. 32 * (ie. it provides an ifnet interface to the rest of the system) 33 */ 34 35 36#include "opt_inet.h" 37 38#include <sys/param.h> 39#include <sys/socket.h> 40#include <sys/sockio.h> 41#if defined(__bsdi__) || defined(__NetBSD__) 42#include <sys/device.h> 43#endif 44 45#include <net/if.h> 46#include <net/if_dl.h> 47 48#include "bpf.h" 49#if NBPF > 0 50#include <net/bpf.h> 51#endif 52 53#if defined(__FreeBSD__) 54#ifdef INET 55#include <netinet/in.h> 56#include <netinet/if_ether.h> 57#endif 58#include <netinet/if_fddi.h> 59#else 60#include <net/if_fddi.h> 61#endif 62 63#if defined(__bsdi__) 64#include <i386/isa/isavar.h> 65#endif 66 67#ifdef NS 68#include <netns/ns.h> 69#include <netns/ns_if.h> 70#endif 71 72#if defined(__FreeBSD__) 73#include <dev/pdq/pdqvar.h> 74#include <dev/pdq/pdqreg.h> 75#else 76#include "pdqvar.h" 77#include "pdqreg.h" 78#endif 79 80#if defined(__bsdi__) && _BSDI_VERSION < 199506 /* XXX */ 81static void 82arp_ifinit( 83 struct arpcom *ac, 84 struct ifaddr *ifa) 85{ 86 sc->sc_ac.ac_ipaddr = IA_SIN(ifa)->sin_addr; 87 arpwhohas(&sc->sc_ac, &IA_SIN(ifa)->sin_addr); 88#if _BSDI_VERSION >= 199401 89 ifa->ifa_rtrequest = arp_rtrequest; 90 ifa->ifa_flags |= RTF_CLONING; 91#endif 92#endif 93 94 95void 96pdq_ifinit( 97 pdq_softc_t *sc) 98{ 99 if (sc->sc_if.if_flags & IFF_UP) { 100 sc->sc_if.if_flags |= IFF_RUNNING; 101 if (sc->sc_if.if_flags & IFF_PROMISC) { 102 sc->sc_pdq->pdq_flags |= PDQ_PROMISC; 103 } else { 104 sc->sc_pdq->pdq_flags &= ~PDQ_PROMISC; 105 } 106 if (sc->sc_if.if_flags & IFF_ALLMULTI) { 107 sc->sc_pdq->pdq_flags |= PDQ_ALLMULTI; 108 } else { 109 sc->sc_pdq->pdq_flags &= ~PDQ_ALLMULTI; 110 } 111 if (sc->sc_if.if_flags & IFF_LINK1) { 112 sc->sc_pdq->pdq_flags |= PDQ_PASS_SMT; 113 } else { 114 sc->sc_pdq->pdq_flags &= ~PDQ_PASS_SMT; 115 } 116 sc->sc_pdq->pdq_flags |= PDQ_RUNNING; 117 pdq_run(sc->sc_pdq); 118 } else { 119 sc->sc_if.if_flags &= ~IFF_RUNNING; 120 sc->sc_pdq->pdq_flags &= ~PDQ_RUNNING; 121 pdq_stop(sc->sc_pdq); 122 } 123} 124 125void 126pdq_ifwatchdog( 127 struct ifnet *ifp) 128{ 129 /* 130 * No progress was made on the transmit queue for PDQ_OS_TX_TRANSMIT 131 * seconds. Remove all queued packets. 132 */ 133 134 ifp->if_flags &= ~IFF_OACTIVE; 135 ifp->if_timer = 0; 136 for (;;) { 137 struct mbuf *m; 138 IF_DEQUEUE(&ifp->if_snd, m); 139 if (m == NULL) 140 return; 141 m_freem(m); 142 } 143} 144 145ifnet_ret_t 146pdq_ifstart( 147 struct ifnet *ifp) 148{ 149 pdq_softc_t *sc = (pdq_softc_t *) ((caddr_t) ifp - offsetof(pdq_softc_t, sc_ac.ac_if)); 150 struct ifqueue *ifq = &ifp->if_snd; 151 struct mbuf *m; 152 int tx = 0; 153 154 if ((ifp->if_flags & IFF_RUNNING) == 0) 155 return; 156 157 if (sc->sc_if.if_timer == 0) 158 sc->sc_if.if_timer = PDQ_OS_TX_TIMEOUT; 159 160 if ((sc->sc_pdq->pdq_flags & PDQ_TXOK) == 0) { 161 sc->sc_if.if_flags |= IFF_OACTIVE; 162 return; 163 } 164 for (;; tx = 1) { 165 IF_DEQUEUE(ifq, m); 166 if (m == NULL) 167 break; 168 169 if (pdq_queue_transmit_data(sc->sc_pdq, m) == PDQ_FALSE) { 170 ifp->if_flags |= IFF_OACTIVE; 171 IF_PREPEND(ifq, m); 172 break; 173 } 174 } 175 if (tx) 176 PDQ_DO_TYPE2_PRODUCER(sc->sc_pdq); 177} 178 179void 180pdq_os_receive_pdu( 181 pdq_t *pdq, 182 struct mbuf *m, 183 size_t pktlen) 184{ 185 pdq_softc_t *sc = (pdq_softc_t *) pdq->pdq_os_ctx; 186 struct fddi_header *fh = mtod(m, struct fddi_header *); 187 188 sc->sc_if.if_ipackets++; 189#if NBPF > 0 190 if (sc->sc_bpf != NULL) 191 PDQ_BPF_MTAP(sc, m); 192 if ((fh->fddi_fc & (FDDIFC_L|FDDIFC_F)) != FDDIFC_LLC_ASYNC) { 193 m_freem(m); 194 return; 195 } 196#endif 197 198 m->m_data += sizeof(struct fddi_header); 199 m->m_len -= sizeof(struct fddi_header); 200 m->m_pkthdr.len = pktlen - sizeof(struct fddi_header); 201 m->m_pkthdr.rcvif = &sc->sc_if; 202 fddi_input(&sc->sc_if, fh, m); 203} 204 205void 206pdq_os_restart_transmitter( 207 pdq_t *pdq) 208{ 209 pdq_softc_t *sc = (pdq_softc_t *) pdq->pdq_os_ctx; 210 sc->sc_if.if_flags &= ~IFF_OACTIVE; 211 if (sc->sc_if.if_snd.ifq_head != NULL) { 212 sc->sc_if.if_timer = PDQ_OS_TX_TIMEOUT; 213 pdq_ifstart(&sc->sc_if); 214 } else { 215 sc->sc_if.if_timer = 0; 216 } 217} 218 219void 220pdq_os_transmit_done( 221 pdq_t *pdq, 222 struct mbuf *m) 223{ 224 pdq_softc_t *sc = (pdq_softc_t *) pdq->pdq_os_ctx; 225#if NBPF > 0 226 if (sc->sc_bpf != NULL) 227 PDQ_BPF_MTAP(sc, m); 228#endif 229 m_freem(m); 230 sc->sc_if.if_opackets++; 231} 232 233void 234pdq_os_addr_fill( 235 pdq_t *pdq, 236 pdq_lanaddr_t *addr, 237 size_t num_addrs) 238{ 239 pdq_softc_t *sc = (pdq_softc_t *) pdq->pdq_os_ctx; 240 struct ifmultiaddr *ifma; 241 242 for (ifma = sc->sc_if.if_multiaddrs.lh_first; ifma && num_addrs > 0; 243 ifma = ifma->ifma_link.le_next) { 244 char *mcaddr; 245 if (ifma->ifma_addr->sa_family != AF_LINK) 246 continue; 247 mcaddr = LLADDR((struct sockaddr_dl *)ifma->ifma_addr); 248 ((u_short *) addr->lanaddr_bytes)[0] = ((u_short *) mcaddr)[0]; 249 ((u_short *) addr->lanaddr_bytes)[1] = ((u_short *) mcaddr)[1]; 250 ((u_short *) addr->lanaddr_bytes)[2] = ((u_short *) mcaddr)[2]; 251 addr++; 252 num_addrs--; 253 } 254} 255 256int 257pdq_ifioctl( 258 struct ifnet *ifp, 259 ioctl_cmd_t cmd, 260 caddr_t data) 261{ 262 pdq_softc_t *sc = (pdq_softc_t *) ((caddr_t) ifp - offsetof(pdq_softc_t, sc_ac.ac_if)); 263 int s, error = 0; 264 265 s = splimp(); 266 267 switch (cmd) { 268 case SIOCSIFADDR: { 269 struct ifaddr *ifa = (struct ifaddr *)data; 270 271 ifp->if_flags |= IFF_UP; 272 switch(ifa->ifa_addr->sa_family) { 273#if defined(INET) 274 case AF_INET: { 275 pdq_ifinit(sc); 276 arp_ifinit(&sc->sc_ac, ifa); 277 break; 278 } 279#endif /* INET */ 280 281#if defined(NS) 282 /* This magic copied from if_is.c; I don't use XNS, 283 * so I have no way of telling if this actually 284 * works or not. 285 */ 286 case AF_NS: { 287 struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr); 288 if (ns_nullhost(*ina)) { 289 ina->x_host = *(union ns_host *)(sc->sc_ac.ac_enaddr); 290 } else { 291 ifp->if_flags &= ~IFF_RUNNING; 292 bcopy((caddr_t)ina->x_host.c_host, 293 (caddr_t)sc->sc_ac.ac_enaddr, 294 sizeof sc->sc_ac.ac_enaddr); 295 } 296 297 pdq_ifinit(sc); 298 break; 299 } 300#endif /* NS */ 301 302 default: { 303 pdq_ifinit(sc); 304 break; 305 } 306 } 307 break; 308 } 309 case SIOCGIFADDR: { 310 struct ifreq *ifr = (struct ifreq *)data; 311 bcopy((caddr_t) sc->sc_ac.ac_enaddr, 312 (caddr_t) ((struct sockaddr *)&ifr->ifr_data)->sa_data, 313 6); 314 break; 315 } 316 317 case SIOCSIFFLAGS: { 318 pdq_ifinit(sc); 319 break; 320 } 321 322 case SIOCADDMULTI: 323 case SIOCDELMULTI: 324 /* 325 * Update multicast listeners 326 */ 327 if (sc->sc_if.if_flags & IFF_RUNNING) 328 pdq_run(sc->sc_pdq); 329 error = 0; 330 break; 331 332#if defined(SIOCSIFMTU) 333#if !defined(ifr_mtu) 334#define ifr_mtu ifr_metric 335#endif 336 case SIOCSIFMTU: { 337 struct ifreq *ifr = (struct ifreq *)data; 338 /* 339 * Set the interface MTU. 340 */ 341 if (ifr->ifr_mtu > FDDIMTU) { 342 error = EINVAL; 343 break; 344 } 345 ifp->if_mtu = ifr->ifr_mtu; 346 break; 347 } 348#endif /* SIOCSIFMTU */ 349 350 default: { 351 error = EINVAL; 352 break; 353 } 354 } 355 356 splx(s); 357 return error; 358} 359 360#ifndef IFF_NOTRAILERS 361#define IFF_NOTRAILERS 0 362#endif 363 364void 365pdq_ifattach( 366 pdq_softc_t *sc, 367 ifnet_ret_t (*ifwatchdog)(int unit)) 368{ 369 struct ifnet *ifp = &sc->sc_if; 370 371 ifp->if_flags = IFF_BROADCAST|IFF_SIMPLEX|IFF_NOTRAILERS|IFF_MULTICAST; 372 373#if (defined(__FreeBSD__) && BSD >= 199506) || defined(__NetBSD__) 374 ifp->if_watchdog = pdq_ifwatchdog; 375#else 376 ifp->if_watchdog = ifwatchdog; 377#endif 378 379 ifp->if_ioctl = pdq_ifioctl; 380 ifp->if_output = fddi_output; 381 ifp->if_start = pdq_ifstart; 382#warning "Implement fddi_resolvemulti!" 383/* ifp->if_resolvemulti = ether_resolvemulti; XXX */ 384 385 if_attach(ifp); 386 fddi_ifattach(ifp); 387#if NBPF > 0 388 PDQ_BPFATTACH(sc, DLT_FDDI, sizeof(struct fddi_header)); 389#endif 390} 391