text: /****************************************************************************
   * net/mld/mld_group.c
   *
   * Licensed to the Apache Software Foundation (ASF) under one or more
   * contributor license agreements.  See the NOTICE file distributed with
   * this work for additional information regarding copyright ownership.  The
   * ASF licenses this file to you under the Apache License, Version 2.0 (the
   * "License"); you may not use this file except in compliance with the
   * License.  You may obtain a copy of the License at
   *
   *   http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
   * License for the specific language governing permissions and limitations
   * under the License.
   *
   ****************************************************************************/
  
  /****************************************************************************
   * Included Files
   ****************************************************************************/
  
  #include <nuttx/config.h>
  #include <nuttx/compiler.h>
  
  #include <stdlib.h>
  #include <string.h>
  #include <queue.h>
  #include <debug.h>
  
  #include <arch/irq.h>
  
  #include <nuttx/arch.h>
  #include <nuttx/wdog.h>
  #include <nuttx/kmalloc.h>
  #include <nuttx/net/net.h>
  #include <nuttx/net/netdev.h>
  #include <nuttx/net/ip.h>
  #include <nuttx/net/mld.h>
  
  #include "devif/devif.h"
  #include "mld/mld.h"
  
  #ifdef CONFIG_NET_MLD
  
  /****************************************************************************
   * Private Functions
   ****************************************************************************/
  
  /****************************************************************************
   * Name:  mld_ngroups
   *
   * Description:
   *   Return the number of groups joined by applications.
   *
   ****************************************************************************/
  
  static int mld_ngroups(FAR struct net_driver_s *dev)
  {
    FAR struct mld_group_s *group;
    int ngroups = 0;
  
    /* Count the number of groups in the group list */
  
    for (group = (FAR struct mld_group_s *)dev->d_mld.grplist.head;
         group != NULL;
         group = group->next)
      {
        ngroups++;
      }
  
    /* REVISIT:  Subtract one for the IPv6 allnodes group.  Why is this here?
     * what do we need it for?  It was cloned from IGMP and is probably not
     * needed.
     */
  
    return ngroups > 0 ? ngroups - 1 : 0;
  }
  
  /****************************************************************************
   * Public Functions
   ****************************************************************************/
  
  /****************************************************************************
   * Name:  mld_grpalloc
   *
   * Description:
   *   Allocate a new group from heap memory.
   *
   * Assumptions:
   *   The network is locked.
   *
   ****************************************************************************/
  
  FAR struct mld_group_s *mld_grpalloc(FAR struct net_driver_s *dev,
                                       FAR const net_ipv6addr_t addr)
  {
    FAR struct mld_group_s *group;
  
    mldinfo("addr: %08x dev: %p\n", *addr, dev);
    group = (FAR struct mld_group_s *)kmm_zalloc(sizeof(struct mld_group_s));
  
    mldinfo("group: %p\n", group);
  
    /* Check if we successfully allocated a group structure */
  
    if (group != NULL)
      {
        /* Initialize the non-zero elements of the group structure */
  
        net_ipv6addr_copy(group->grpaddr, addr);
  
        /* This semaphore is used for signaling and, hence, should not have
         * priority inheritance enabled.
         */
  
        nxsem_init(&group->sem, 0, 0);
        nxsem_set_protocol(&group->sem, SEM_PRIO_NONE);
  
        /* Save the interface index */
  
        group->ifindex = dev->d_ifindex;
  
  #ifndef CONFIG_NET_MLD_ROUTER
        /* Start the query timer if we are the Querier and this is the first
         * group member of the group.
         */
  
        if (mld_ngroups(dev) == 0)
          {
            mld_start_gentimer(dev, MSEC2TICK(MLD_QUERY_MSEC));
          }
  #endif
  
        /* Add the group structure to the list in the device structure */
  
        sq_addfirst((FAR sq_entry_t *)group, &dev->d_mld.grplist);
      }
  
    return group;
  }
  
  /****************************************************************************
   * Name:  mld_grpfind
   *
   * Description:
   *   Find an existing group.
   *
   * Assumptions:
   *   The network is locked.
   *
   ****************************************************************************/
  
  FAR struct mld_group_s *mld_grpfind(FAR struct net_driver_s *dev,
                                      FAR const net_ipv6addr_t addr)
  {
    FAR struct mld_group_s *group;
  
    mldinfo("Searching for group: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
            addr[0], addr[1], addr[2], addr[3], addr[4], addr[5], addr[6],
            addr[7]);
  
    for (group = (FAR struct mld_group_s *)dev->d_mld.grplist.head;
         group;
         group = group->next)
      {
        mldinfo("Compare: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
                group->grpaddr[0], group->grpaddr[1], group->grpaddr[2],
                group->grpaddr[3], group->grpaddr[4], group->grpaddr[5],
                group->grpaddr[6], group->grpaddr[7]);
  
        if (net_ipv6addr_cmp(group->grpaddr, addr))
          {
            mldinfo("Match!\n");
            DEBUGASSERT(group->ifindex == dev->d_ifindex);
            break;
          }
      }
  
    return group;
  }
  
  /****************************************************************************
   * Name:  mld_grpallocfind
   *
   * Description:
   *   Find an existing group.  If not found, create a new group for the
   *   address.
   *
   ****************************************************************************/
  
  FAR struct mld_group_s *mld_grpallocfind(FAR struct net_driver_s *dev,
                                           FAR const net_ipv6addr_t addr)
  {
    FAR struct mld_group_s *group = mld_grpfind(dev, addr);
  
    mldinfo("group: %p addr: %08x\n", group, (int)*addr);
    if (!group)
      {
        group = mld_grpalloc(dev, addr);
      }
  
    mldinfo("group: %p\n", group);
    return group;
  }
  
  /****************************************************************************
   * Name:  mld_grpfree
   *
   * Description:
   *   Release a previously allocated group.
   *
   * Assumptions:
   *   The network is locked.
   *
   ****************************************************************************/
  
  void mld_grpfree(FAR struct net_driver_s *dev, FAR struct mld_group_s *group)
  {
    mldinfo("Free: %p flags: %02x\n", group, group->flags);
  
    /* Cancel the timers */
  
    wd_cancel(&group->polldog);
  
    /* Remove the group structure from the group list in the device structure */
  
    sq_rem((FAR sq_entry_t *)group, &dev->d_mld.grplist);
  
    /* Destroy the wait semaphore */
  
    nxsem_destroy(&group->sem);
  
    /* Cancel the watchdog timer */
  
    wd_cancel(&group->polldog);
  
    /* Then release the group structure resources. */
  
    mldinfo("Call kmm_free()\n");
    kmm_free(group);
  
  #ifndef CONFIG_NET_MLD_ROUTER
    /* If there are no longer any groups, then stop the general query and v1
     * compatibility timers.
     */
  
    if (mld_ngroups(dev) == 0)
      {
        wd_cancel(&dev->d_mld.gendog);
        wd_cancel(&dev->d_mld.v1dog);
      }
  #endif
  }
  
  /****************************************************************************
   * Name:  mld_new_pollcycle
   *
   * Description:
   *   Update accumulated membership at the beginning of each new poll cycle
   *
   ****************************************************************************/
  
  #ifdef CONFIG_NET_MLD_ROUTER
  void mld_new_pollcycle(FAR struct net_driver_s *dev)
  {
    FAR struct mld_group_s *member;
  
    /* Update member ship in every group  */
  
    for (member = (FAR struct mld_group_s *)dev->d_mld.grplist.head;
         member;
         member = member->next)
      {
        /* Save the number of members that reported in the previous query
         * cycle; reset the number of members that have reported in the new
         * query cycle.
         */
  
        member->lstmbrs = member->members;
        member->members = 0;
      }
  }
  #endif
  
  #endif /* CONFIG_NET_MLD */
  