incubator-nuttx/net/mld/mld_timer.c

490 lines
12 KiB
C

/****************************************************************************
* net/mld/mld_timer.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 <assert.h>
#include <debug.h>
#include <nuttx/wdog.h>
#include <nuttx/irq.h>
#include <nuttx/wqueue.h>
#include <nuttx/net/netconfig.h>
#include <nuttx/net/net.h>
#include <nuttx/net/netdev.h>
#include <nuttx/net/netstats.h>
#include <nuttx/net/mld.h>
#include "devif/devif.h"
#include "netdev/netdev.h"
#include "mld/mld.h"
#include "utils/utils.h"
#ifdef CONFIG_NET_MLD
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define MLD_FREE_WORK 4
/****************************************************************************
* Private Data
****************************************************************************/
/* This is a small pool of work structures that are used for rare, general
* timer and MLDv1 compatibility timers.
*
* REVISIT: A better place would be in the device structure. But then we
* would either have to pass a device structure in the timeout (which could
* become stale) or look up the device using an interface index. But we
* cannot access the network device list from the timer interrupt handler.
*/
static struct work_s g_mld_work[MLD_FREE_WORK];
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: mld_find_work
*
* Description:
* Fin an available work queue structure.
*
* Assumptions:
* Called only from the timer interrupt handler with interrupts disabled.
*
****************************************************************************/
static FAR struct work_s *mld_find_work(void)
{
int i;
for (i = 0; i < MLD_FREE_WORK; i++)
{
if (work_available(&g_mld_work[i]))
{
return &g_mld_work[i];
}
}
return NULL;
}
/****************************************************************************
* Name: mld_gendog_work
*
* Description:
* General query watchdog timeout work.
*
* Assumptions:
* This function is called from a work queue thread.
*
****************************************************************************/
static void mld_gendog_work(FAR void *arg)
{
FAR struct net_driver_s *dev;
int ifindex;
/* Recover the interface index and find the device associated with the
* index.
*/
ifindex = (intptr_t)arg;
DEBUGASSERT(ifindex > 0);
net_lock();
dev = netdev_findbyindex(ifindex);
if (dev == NULL)
{
/* This could be a normal consequence if the device is unregistered
* while the timer is pending.
*/
fwarn("WARNING: No device associated with ifindex=%d\n", ifindex);
net_unlock();
return;
}
/* Check for an Other Querier Present Timeout. This timer is set in non-
* Querier mode to detect the case where we have lost the Querier.
*/
if (!IS_MLD_QUERIER(dev->d_mld.flags))
{
/* We are not the Querier. This is an Other Querier Present Timeout.
* If this timeout expires, it means that there are no Queriers for
* the group. Let's revert to Querier mode.
*/
SET_MLD_QUERIER(dev->d_mld.flags);
}
/* Check if this we are the Querier */
if (IS_MLD_QUERIER(dev->d_mld.flags))
{
/* Schedule (and forget) the general query. */
MLD_STATINCR(g_netstats.mld.query_sched);
SET_MLD_GENPEND(dev->d_mld.flags);
/* Notify the device that we have a packet to send */
netdev_txnotify_dev(dev);
/* Restart the Querier timer */
mld_start_gentimer(dev, MSEC2TICK(MLD_QUERY_MSEC));
}
else
{
/* Not the Querier... Restart the Other Querier Present Timeout */
mld_start_gentimer(dev, MSEC2TICK(MLD_OQUERY_MSEC));
}
net_unlock();
}
/****************************************************************************
* Name: mld_gendog_timout
*
* Description:
* General query watchdog handler.
*
* Assumptions:
* This function is called from the context of the timer interrupt handler.
*
****************************************************************************/
static void mld_gendog_timout(wdparm_t arg)
{
FAR struct work_s *work;
int ret;
mldinfo("Timeout!\n");
DEBUGASSERT(arg != 0);
/* Find an available work structure. This is awkward. */
work = mld_find_work();
DEBUGASSERT(work != NULL);
if (work == NULL)
{
mlderr("ERROR: No free work structures.\n");
}
else
{
/* Perform the general query-related operations on (preferably) the
* low priority work queue.
*/
ret = work_queue(LPWORK, work, mld_gendog_work, (FAR void *)arg, 0);
if (ret < 0)
{
mlderr("ERROR: Failed to queue general query work: %d\n", ret);
}
}
}
/****************************************************************************
* Name: mld_v1dog_work
*
* Description:
* Timeout watchdog work
*
* Assumptions:
* This function is called from a work queue thread.
*
****************************************************************************/
static void mld_v1dog_work(FAR void *arg)
{
FAR struct net_driver_s *dev;
int ifindex;
/* Recover the interface index and find the device associated with the
* index.
*/
ifindex = (intptr_t)arg;
DEBUGASSERT(ifindex > 0);
net_lock();
dev = netdev_findbyindex(ifindex);
if (dev == NULL)
{
/* This could be a normal consequence if the device is unregistered
* while the timer is pending.
*/
fwarn("WARNING: No device associated with ifindex=%d\n", ifindex);
}
else
{
/* Exit MLDv1 compatibility mode. */
CLR_MLD_V1COMPAT(dev->d_mld.flags);
/* REVISIT: Whenever a host changes its compatibility mode, it cancels
* all of its pending responses and retransmission timers.
*/
}
net_unlock();
}
/****************************************************************************
* Name: mld_v1dog_timout
*
* Description:
* Timeout watchdog handler.
*
* Assumptions:
* This function is called from the context of the timer interrupt handler.
*
****************************************************************************/
static void mld_v1dog_timout(wdparm_t arg)
{
FAR struct work_s *work;
int ret;
mldinfo("Timeout!\n");
DEBUGASSERT(arg != 0);
/* Find an available work structure. This is awkward. */
work = mld_find_work();
DEBUGASSERT(work != NULL);
if (work == NULL)
{
mlderr("ERROR: No free work structures.\n");
}
else
{
/* Perform the general query-related operations on (preferably) the
* low priority work queue.
*/
ret = work_queue(LPWORK, work, mld_v1dog_work, (FAR void *)arg, 0);
if (ret < 0)
{
mlderr("ERROR: Failed to queue MLDv1 timeout work: %d\n", ret);
}
}
}
/****************************************************************************
* Name: mld_polldog_work
*
* Description:
* Timeout watchdog work
*
* Assumptions:
* This function is called from a work queue thread.
*
****************************************************************************/
static void mld_polldog_work(FAR void *arg)
{
FAR struct mld_group_s *group;
FAR struct net_driver_s *dev;
int ret;
/* Recover the reference to the group */
group = (FAR struct mld_group_s *)arg;
DEBUGASSERT(group != NULL);
/* Check if this a new join to the multicast group. */
net_lock();
if (IS_MLD_STARTUP(group->flags))
{
MLD_STATINCR(g_netstats.mld.report_sched);
/* Get a reference to the device serving the sub-net */
dev = netdev_findbyindex(group->ifindex);
if (dev == NULL)
{
/* This could be a normal consequence if the device is
* unregistered while the timer is pending.
*/
fwarn("WARNING: No device associated with ifindex=%d\n",
group->ifindex);
net_unlock();
return;
}
/* Schedule (and forget) the Report. */
ret = mld_schedmsg(group, mld_report_msgtype(dev));
if (ret < 0)
{
mlderr("ERROR: Failed to schedule message: %d\n", ret);
}
/* Send the report until the unsolicited report count goes to zero
* then terminate the start-up sequence.
*/
if (group->count > 0)
{
/* Decrement the count and restart the timer */
group->count--;
mld_start_polltimer(group, MSEC2TICK(MLD_UNSOLREPORT_MSEC));
}
else
{
/* Terminate the start-up sequence */
CLR_MLD_STARTUP(group->flags);
}
}
net_unlock();
}
/****************************************************************************
* Name: mld_polldog_timout
*
* Description:
* Timeout watchdog handler.
*
* Assumptions:
* This function is called from the context of the timer interrupt handler.
*
****************************************************************************/
static void mld_polldog_timout(wdparm_t arg)
{
FAR struct mld_group_s *group;
int ret;
mldinfo("Timeout!\n");
/* Recover the reference to the group */
group = (FAR struct mld_group_s *)arg;
DEBUGASSERT(group != NULL);
/* Perform the timeout-related operations on (preferably) the low priority
* work queue.
*/
ret = work_queue(LPWORK, &group->work, mld_polldog_work, group, 0);
if (ret < 0)
{
mlderr("ERROR: Failed to queue timeout work: %d\n", ret);
}
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: mld_start_gentimer
*
* Description:
* Start/Re-start the general query timer.
*
****************************************************************************/
void mld_start_gentimer(FAR struct net_driver_s *dev, clock_t ticks)
{
int ret;
/* Start the timer */
mldinfo("ticks: %lu\n", (unsigned long)ticks);
ret = wd_start(&dev->d_mld.gendog, ticks,
mld_gendog_timout, dev->d_ifindex);
DEBUGASSERT(ret == OK);
UNUSED(ret);
}
/****************************************************************************
* Name: mld_start_v1timer
*
* Description:
* Start the MLDv1 compatibility timer.
*
* REVISIT: This should be a single global timer, not a per-group timer.
*
****************************************************************************/
void mld_start_v1timer(FAR struct net_driver_s *dev, clock_t ticks)
{
int ret;
/* Start the timer */
mldinfo("ticks: %lu\n", (unsigned long)ticks);
ret = wd_start(&dev->d_mld.v1dog, ticks,
mld_v1dog_timout, dev->d_ifindex);
DEBUGASSERT(ret == OK);
UNUSED(ret);
}
/****************************************************************************
* Name: mld_start_polltimer
*
* Description:
* Start the MLD poll timer.
*
****************************************************************************/
void mld_start_polltimer(FAR struct mld_group_s *group, clock_t ticks)
{
int ret;
/* Start the timer */
mldinfo("ticks: %lu\n", (unsigned long)ticks);
ret = wd_start(&group->polldog, ticks,
mld_polldog_timout, (wdparm_t)group);
DEBUGASSERT(ret == OK);
UNUSED(ret);
}
#endif /* CONFIG_NET_MLD */