The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/etherswitch/arswitch/arswitch_vlans.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
    3  *
    4  * Copyright (c) 2013 Luiz Otavio O Souza.
    5  * Copyright (c) 2011-2012 Stefan Bethke.
    6  * Copyright (c) 2012 Adrian Chadd.
    7  * All rights reserved.
    8  *
    9  * Redistribution and use in source and binary forms, with or without
   10  * modification, are permitted provided that the following conditions
   11  * are met:
   12  * 1. Redistributions of source code must retain the above copyright
   13  *    notice, this list of conditions and the following disclaimer.
   14  * 2. Redistributions in binary form must reproduce the above copyright
   15  *    notice, this list of conditions and the following disclaimer in the
   16  *    documentation and/or other materials provided with the distribution.
   17  *
   18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   28  * SUCH DAMAGE.
   29  *
   30  * $FreeBSD$
   31  */
   32 
   33 #include <sys/param.h>
   34 #include <sys/bus.h>
   35 #include <sys/errno.h>
   36 #include <sys/lock.h>
   37 #include <sys/kernel.h>
   38 #include <sys/mutex.h>
   39 #include <sys/systm.h>
   40 #include <sys/socket.h>
   41 
   42 #include <net/if.h>
   43 #include <dev/mii/mii.h>
   44 
   45 #include <dev/etherswitch/etherswitch.h>
   46 #include <dev/etherswitch/arswitch/arswitchreg.h>
   47 #include <dev/etherswitch/arswitch/arswitchvar.h>
   48 #include <dev/etherswitch/arswitch/arswitch_reg.h>
   49 #include <dev/etherswitch/arswitch/arswitch_vlans.h>
   50 
   51 #include "mdio_if.h"
   52 #include "miibus_if.h"
   53 #include "etherswitch_if.h"
   54 
   55 /*
   56  * XXX TODO: teach about the AR934x SoC switch
   57  */
   58 
   59 static int
   60 ar8xxx_vlan_op(struct arswitch_softc *sc, uint32_t op, uint32_t vid,
   61         uint32_t data)
   62 {
   63         int err;
   64 
   65         if (arswitch_waitreg(sc->sc_dev, AR8X16_REG_VLAN_CTRL,
   66             AR8X16_VLAN_ACTIVE, 0, 5))
   67                 return (EBUSY);
   68 
   69         /* Load the vlan data if needed. */
   70         if (op == AR8X16_VLAN_OP_LOAD) {
   71                 err = arswitch_writereg(sc->sc_dev, AR8X16_REG_VLAN_DATA,
   72                     (data & AR8X16_VLAN_MEMBER) | AR8X16_VLAN_VALID);
   73                 if (err)
   74                         return (err);
   75         }
   76 
   77         if (vid != 0)
   78                 op |= ((vid & ETHERSWITCH_VID_MASK) << AR8X16_VLAN_VID_SHIFT);
   79         op |= AR8X16_VLAN_ACTIVE;
   80         arswitch_writereg(sc->sc_dev, AR8X16_REG_VLAN_CTRL, op);
   81 
   82         /* Wait for command processing. */
   83         if (arswitch_waitreg(sc->sc_dev, AR8X16_REG_VLAN_CTRL,
   84             AR8X16_VLAN_ACTIVE, 0, 5))
   85                 return (EBUSY);
   86 
   87         return (0);
   88 }
   89 
   90 int
   91 ar8xxx_flush_dot1q_vlan(struct arswitch_softc *sc)
   92 {
   93 
   94         ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
   95         return (ar8xxx_vlan_op(sc, AR8X16_VLAN_OP_FLUSH, 0, 0));
   96 }
   97 
   98 int
   99 ar8xxx_purge_dot1q_vlan(struct arswitch_softc *sc, int vid)
  100 {
  101 
  102         ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
  103         return (ar8xxx_vlan_op(sc, AR8X16_VLAN_OP_PURGE, vid, 0));
  104 }
  105 
  106 int
  107 ar8xxx_get_dot1q_vlan(struct arswitch_softc *sc, uint32_t *ports,
  108     uint32_t *untagged_ports, int vid)
  109 {
  110         uint32_t reg;
  111         int err;
  112 
  113         ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
  114         err = ar8xxx_vlan_op(sc, AR8X16_VLAN_OP_GET, vid, 0);
  115         if (err)
  116                 return (err);
  117 
  118         reg = arswitch_readreg(sc->sc_dev, AR8X16_REG_VLAN_DATA);
  119         if ((reg & AR8X16_VLAN_VALID) == 0) {
  120                 *ports = 0;
  121                 return (EINVAL);
  122         }
  123         reg &= ((1 << (sc->numphys + 1)) - 1);
  124         *ports = reg;
  125         *untagged_ports = reg;
  126         return (0);
  127 }
  128 
  129 int
  130 ar8xxx_set_dot1q_vlan(struct arswitch_softc *sc, uint32_t ports,
  131     uint32_t untagged_ports, int vid)
  132 {
  133         int err;
  134 
  135         ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
  136         err = ar8xxx_vlan_op(sc, AR8X16_VLAN_OP_LOAD, vid, ports);
  137         if (err)
  138                 return (err);
  139         return (0);
  140 }
  141 
  142 int
  143 ar8xxx_get_port_vlan(struct arswitch_softc *sc, uint32_t *ports, int vid)
  144 {
  145         int port;
  146         uint32_t reg;
  147 
  148         ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
  149         /* For port based vlans the vlanid is the same as the port index. */
  150         port = vid & ETHERSWITCH_VID_MASK;
  151         reg = arswitch_readreg(sc->sc_dev, AR8X16_REG_PORT_VLAN(port));
  152         *ports = (reg >> AR8X16_PORT_VLAN_DEST_PORTS_SHIFT);
  153         *ports &= AR8X16_VLAN_MEMBER;
  154         return (0);
  155 }
  156 
  157 int
  158 ar8xxx_set_port_vlan(struct arswitch_softc *sc, uint32_t ports, int vid)
  159 {
  160         int err, port;
  161 
  162         ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
  163         /* For port based vlans the vlanid is the same as the port index. */
  164         port = vid & ETHERSWITCH_VID_MASK;
  165         err = arswitch_modifyreg(sc->sc_dev, AR8X16_REG_PORT_VLAN(port),
  166             AR8X16_VLAN_MEMBER << AR8X16_PORT_VLAN_DEST_PORTS_SHIFT,
  167             (ports & AR8X16_VLAN_MEMBER) << AR8X16_PORT_VLAN_DEST_PORTS_SHIFT);
  168         if (err)
  169                 return (err);
  170         return (0);
  171 }
  172 
  173 /*
  174  * Reset vlans to default state.
  175  */
  176 void
  177 ar8xxx_reset_vlans(struct arswitch_softc *sc)
  178 {
  179         uint32_t ports;
  180         int i, j;
  181 
  182         ARSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED);
  183 
  184         ARSWITCH_LOCK(sc);
  185 
  186         /* Reset all vlan data. */
  187         memset(sc->vid, 0, sizeof(sc->vid));
  188 
  189         /* Disable the QinQ and egress filters for all ports. */
  190         for (i = 0; i <= sc->numphys; i++) {
  191                 if (arswitch_modifyreg(sc->sc_dev, AR8X16_REG_PORT_CTRL(i),
  192                     0x3 << AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_SHIFT |
  193                     AR8X16_PORT_CTRL_DOUBLE_TAG, 0)) {
  194                         ARSWITCH_UNLOCK(sc);
  195                         return;
  196                 }
  197         }
  198 
  199         if (sc->hal.arswitch_flush_dot1q_vlan(sc)) {
  200                 ARSWITCH_UNLOCK(sc);
  201                 return;
  202         }
  203 
  204         if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
  205                 /*
  206                  * Reset the port based vlan settings and turn on the
  207                  * ingress filter for all ports.
  208                  */
  209                 ports = 0;
  210                 for (i = 0; i <= sc->numphys; i++)
  211                         arswitch_modifyreg(sc->sc_dev,
  212                             AR8X16_REG_PORT_VLAN(i),
  213                             AR8X16_PORT_VLAN_MODE_MASK |
  214                             AR8X16_VLAN_MEMBER <<
  215                             AR8X16_PORT_VLAN_DEST_PORTS_SHIFT,
  216                             AR8X16_PORT_VLAN_MODE_SECURE <<
  217                             AR8X16_PORT_VLAN_MODE_SHIFT);
  218 
  219                 /*
  220                  * Setup vlan 1 as PVID for all switch ports.  Add all ports
  221                  * as members of vlan 1.
  222                  */
  223                 sc->vid[0] = 1;
  224                 /* Set PVID for everyone. */
  225                 for (i = 0; i <= sc->numphys; i++)
  226                         sc->hal.arswitch_vlan_set_pvid(sc, i, sc->vid[0]);
  227                 ports = 0;
  228                 for (i = 0; i <= sc->numphys; i++)
  229                         ports |= (1 << i);
  230                 sc->hal.arswitch_set_dot1q_vlan(sc, ports, sc->vid[0], sc->vid[0]);
  231                 sc->vid[0] |= ETHERSWITCH_VID_VALID;
  232         } else if (sc->vlan_mode == ETHERSWITCH_VLAN_PORT) {
  233                 /* Initialize the port based vlans. */
  234                 for (i = 0; i <= sc->numphys; i++) {
  235                         sc->vid[i] = i | ETHERSWITCH_VID_VALID;
  236                         ports = 0;
  237                         for (j = 0; j <= sc->numphys; j++)
  238                                 ports |= (1 << j);
  239                         arswitch_modifyreg(sc->sc_dev,
  240                             AR8X16_REG_PORT_VLAN(i),
  241                             AR8X16_PORT_VLAN_MODE_MASK |
  242                             AR8X16_VLAN_MEMBER <<
  243                             AR8X16_PORT_VLAN_DEST_PORTS_SHIFT,
  244                             ports << AR8X16_PORT_VLAN_DEST_PORTS_SHIFT |
  245                             AR8X16_PORT_VLAN_MODE_SECURE <<
  246                             AR8X16_PORT_VLAN_MODE_PORT_ONLY);
  247                             /* XXX TODO: SECURE / PORT_ONLY is wrong? */
  248                 }
  249         } else {
  250                 /* Disable the ingress filter and get everyone on all vlans. */
  251                 for (i = 0; i <= sc->numphys; i++)
  252                         arswitch_modifyreg(sc->sc_dev,
  253                             AR8X16_REG_PORT_VLAN(i),
  254                             AR8X16_PORT_VLAN_MODE_MASK |
  255                             AR8X16_VLAN_MEMBER <<
  256                             AR8X16_PORT_VLAN_DEST_PORTS_SHIFT,
  257                             AR8X16_VLAN_MEMBER <<
  258                             AR8X16_PORT_VLAN_DEST_PORTS_SHIFT |
  259                             AR8X16_PORT_VLAN_MODE_SECURE <<
  260                             AR8X16_PORT_VLAN_MODE_PORT_ONLY);
  261         }
  262         ARSWITCH_UNLOCK(sc);
  263 }
  264 
  265 int
  266 ar8xxx_getvgroup(struct arswitch_softc *sc, etherswitch_vlangroup_t *vg)
  267 {
  268         int err;
  269 
  270         ARSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED);
  271 
  272         if (vg->es_vlangroup > sc->info.es_nvlangroups)
  273                 return (EINVAL);
  274 
  275         /* Reset the members ports. */
  276         vg->es_untagged_ports = 0;
  277         vg->es_member_ports = 0;
  278 
  279         /* Not supported. */
  280         vg->es_fid = 0;
  281 
  282         /* Vlan ID. */
  283         ARSWITCH_LOCK(sc);
  284         vg->es_vid = sc->vid[vg->es_vlangroup];
  285         if ((vg->es_vid & ETHERSWITCH_VID_VALID) == 0) {
  286                 ARSWITCH_UNLOCK(sc);
  287                 return (0);
  288         }
  289 
  290         /* Member Ports. */
  291         switch (sc->vlan_mode) {
  292         case ETHERSWITCH_VLAN_DOT1Q:
  293                 err = sc->hal.arswitch_get_dot1q_vlan(sc, &vg->es_member_ports,
  294                     &vg->es_untagged_ports,
  295                     vg->es_vid);
  296                 break;
  297         case ETHERSWITCH_VLAN_PORT:
  298                 err = sc->hal.arswitch_get_port_vlan(sc, &vg->es_member_ports,
  299                     vg->es_vid);
  300                 vg->es_untagged_ports = vg->es_member_ports;
  301                 break;
  302         default:
  303                 vg->es_member_ports = 0;
  304                 vg->es_untagged_ports = 0;
  305                 err = -1;
  306         }
  307         ARSWITCH_UNLOCK(sc);
  308 
  309         return (err);
  310 }
  311 
  312 int
  313 ar8xxx_setvgroup(struct arswitch_softc *sc, etherswitch_vlangroup_t *vg)
  314 {
  315         int err, vid;
  316 
  317         ARSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED);
  318 
  319         /* Check VLAN mode. */
  320         if (sc->vlan_mode == 0)
  321                 return (EINVAL);
  322 
  323         /*
  324          * Check if we are changing the vlanid for an already used vtu entry.
  325          * Then purge the entry first.
  326          */
  327         ARSWITCH_LOCK(sc);
  328         vid = sc->vid[vg->es_vlangroup];
  329         if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q &&
  330             (vid & ETHERSWITCH_VID_VALID) != 0 &&
  331             (vid & ETHERSWITCH_VID_MASK) !=
  332             (vg->es_vid & ETHERSWITCH_VID_MASK)) {
  333                 err = sc->hal.arswitch_purge_dot1q_vlan(sc, vid);
  334                 if (err) {
  335                         ARSWITCH_UNLOCK(sc);
  336                         return (err);
  337                 }
  338         }
  339 
  340         /* Vlan ID. */
  341         if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
  342                 sc->vid[vg->es_vlangroup] = vg->es_vid & ETHERSWITCH_VID_MASK;
  343                 /* Setting the vlanid to zero disables the vlangroup. */
  344                 if (sc->vid[vg->es_vlangroup] == 0) {
  345                         ARSWITCH_UNLOCK(sc);
  346                         return (0);
  347                 }
  348                 sc->vid[vg->es_vlangroup] |= ETHERSWITCH_VID_VALID;
  349                 vid = sc->vid[vg->es_vlangroup];
  350         }
  351 
  352         /* Member Ports. */
  353         switch (sc->vlan_mode) {
  354         case ETHERSWITCH_VLAN_DOT1Q:
  355                 err = sc->hal.arswitch_set_dot1q_vlan(sc, vg->es_member_ports,
  356                     vg->es_untagged_ports, vid);
  357                 break;
  358         case ETHERSWITCH_VLAN_PORT:
  359                 err = sc->hal.arswitch_set_port_vlan(sc, vg->es_member_ports, vid);
  360                 break;
  361         default:
  362                 err = -1;
  363         }
  364         ARSWITCH_UNLOCK(sc);
  365         return (err);
  366 }
  367 
  368 int
  369 ar8xxx_get_pvid(struct arswitch_softc *sc, int port, int *pvid)
  370 {
  371         uint32_t reg;
  372 
  373         ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
  374         reg = arswitch_readreg(sc->sc_dev, AR8X16_REG_PORT_VLAN(port));
  375         *pvid = reg & 0xfff;
  376         return (0);
  377 }
  378 
  379 int
  380 ar8xxx_set_pvid(struct arswitch_softc *sc, int port, int pvid)
  381 {
  382 
  383         ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
  384         return (arswitch_modifyreg(sc->sc_dev,
  385             AR8X16_REG_PORT_VLAN(port), 0xfff, pvid));
  386 }

Cache object: c0af7c1698534e06f0bb82f716c4ec58


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.