diff --git a/include/net/uip/uip-igmp.h b/include/net/uip/uip-igmp.h index dd540d90303..00c01737275 100755 --- a/include/net/uip/uip-igmp.h +++ b/include/net/uip/uip-igmp.h @@ -50,6 +50,7 @@ #include #include +#include #include @@ -89,21 +90,25 @@ #define IGMP_LASTREPORT (1 << 1) #define IGMP_IDLEMEMBER (1 << 2) #define IGMP_SCHEDMSG (1 << 3) +#define IGMP_WAITMSG (1 << 4) #define SET_PREALLOCATED(f) do { (f) |= IGMP_PREALLOCATED; } while (0) #define SET_LASTREPORT(f) do { (f) |= IGMP_LASTREPORT; } while (0) #define SET_IDLEMEMBER(f) do { (f) |= IGMP_IDLEMEMBER; } while (0) -#define SET_SCHEDMSG(f) do { (f) |= IGMP_IDLEMEMBER; } while (0) +#define SET_SCHEDMSG(f) do { (f) |= IGMP_SCHEDMSG; } while (0) +#define SET_WAITMSG(f) do { (f) |= IGMP_WAITMSG; } while (0) #define CLR_PREALLOCATED(f) do { (f) &= ~IGMP_PREALLOCATED; } while (0) #define CLR_LASTREPORT(f) do { (f) &= ~IGMP_LASTREPORT; } while (0) -#define CLR_IDLEMEMBER(f) do { (f) &= ~IGMP_LASTREPORT; } while (0) -#define CLR_SCHEDMSG(f) do { (f) &= ~IGMP_LASTREPORT; } while (0) +#define CLR_IDLEMEMBER(f) do { (f) &= ~IGMP_IDLEMEMBER; } while (0) +#define CLR_SCHEDMSG(f) do { (f) &= ~IGMP_SCHEDMSG; } while (0) +#define CLR_WAITMSG(f) do { (f) &= ~IGMP_WAITMSG; } while (0) #define IS_PREALLOCATED(f) (((f) & IGMP_PREALLOCATED) != 0) #define IS_LASTREPORT(f) (((f) & IGMP_LASTREPORT) != 0) -#define IS_IDLEMEMBER(f) (((f) & IGMP_LASTREPORT) != 0) -#define IS_SCHEDMSG(f) (((f) & IGMP_LASTREPORT) != 0) +#define IS_IDLEMEMBER(f) (((f) & IGMP_IDLEMEMBER) != 0) +#define IS_SCHEDMSG(f) (((f) & IGMP_SCHEDMSG) != 0) +#define IS_WAITMSG(f) (((f) & IGMP_WAITMSG) != 0) #define IGMP_TTL 1 @@ -175,6 +180,7 @@ struct uip_igmp_stats_s uint32_t chksum_errors; uint32_t v1_received; uint32_t joins; + uint32_t leaves; uint32_t leave_sched; uint32_t report_sched; uint32_t poll_send; @@ -202,7 +208,8 @@ struct igmp_group_s struct igmp_group_s *next; /* Implements a singly-linked list */ uip_ipaddr_t grpaddr; /* Group IP address */ WDOG_ID wdog; /* WDOG used to detect timeouts */ - uint8_t flags; /* See IGMP_ flags definitions */ + sem_t sem; /* Used to wait for message transmission */ + volatile uint8_t flags; /* See IGMP_ flags definitions */ uint8_t msgid; /* Pending message ID (if non-zero) */ }; diff --git a/net/uip/Make.defs b/net/uip/Make.defs index 42210731865..e51b4645751 100644 --- a/net/uip/Make.defs +++ b/net/uip/Make.defs @@ -1,7 +1,7 @@ ############################################################################ # Make.defs # -# Copyright (C) 2007, 2009 Gregory Nutt. All rights reserved. +# Copyright (C) 2007, 2009-20010 Gregory Nutt. All rights reserved. # Author: Gregory Nutt # # Redistribution and use in source and binary forms, with or without @@ -79,7 +79,7 @@ endif endif ifeq ($(CONFIG_NET_IGMP),y) -UIP_CSRCS += uip_igmpinit.c uip_igmpgroup.c +UIP_CSRCS += uip_igmpinit.c uip_igmpgroup.c uip_igmpjoin.c uip_igmpleave.c uip_igmpmsg.c endif endif diff --git a/net/uip/uip_igmpgroup.c b/net/uip/uip_igmpgroup.c index f51b8d542ed..62d3a7a5df7 100755 --- a/net/uip/uip_igmpgroup.c +++ b/net/uip/uip_igmpgroup.c @@ -191,6 +191,7 @@ FAR struct igmp_group_s *uip_grpalloc(FAR struct uip_driver_s *dev, FAR uip_ipad /* Initialize the non-zero elements of the group structure */ uip_ipaddr_copy(group->grpaddr, addr); + sem_init(&group->sem, 0, 0); /* Interrupts must be disabled in order to modify the group list */ @@ -277,6 +278,15 @@ void uip_grpfree(FAR struct uip_driver_s *dev, FAR struct igmp_group_s *group) irqstate_t flags = irqsave(); DEBUGASSERT(sq_rem((FAR sq_entry_t*)group, &dev->grplist) != NULL); + + /* Destroy the wait semapore */ + + (void)sem_destroy(&group->sem); + + /* Then release the group structure resources. Check first if this is one + * of the pre-allocated group structures that we will retain in a free list. + */ + if (IS_PREALLOCATED(group->flags)) { sq_addlast((FAR sq_entry_t*)group, &g_freelist); @@ -284,6 +294,10 @@ void uip_grpfree(FAR struct uip_driver_s *dev, FAR struct igmp_group_s *group) } else { + /* No.. deallocate the group structure. Use sched_free() just in case + * this function is executing within an interrupt handler. + */ + irqrestore(flags); sched_free(group); } diff --git a/net/uip/uip_igmpjoin.c b/net/uip/uip_igmpjoin.c new file mode 100755 index 00000000000..3f0224a92aa --- /dev/null +++ b/net/uip/uip_igmpjoin.c @@ -0,0 +1,153 @@ +/**************************************************************************** + * net/uip/uip_igmpjoin.c + * + * Copyright (C) 2010 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * The NuttX implementation of IGMP was inspired by the IGMP add-on for the + * lwIP TCP/IP stack by Steve Reynolds: + * + * Copyright (c) 2002 CITEL Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of CITEL Technologies Ltd nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CITEL TECHNOLOGIES AND CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL CITEL TECHNOLOGIES OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include +#include +#include + +#include "uip_internal.h" + +#ifdef CONFIG_NET_IGMP + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: igmp_joingroup + * + * Description: + * Add the specified group address to the group. + * + * RFC 2236, 3. Protocol Description: + * + * "When a host joins a multicast group, it should immediately transmit + * an unsolicited Version 2 Membership Report for that group, in case it + * is the first member of that group on the network. To cover the + * possibility of the initial Membership Report being lost or damaged, + * it is recommended that it be repeated once or twice after short + * delays [Unsolicited Report Interval]. (A simple way to accomplish + * this is to send the initial Version 2 Membership Report and then act + * as if a Group-Specific Query was received for that group, and set a + * timer appropriately)." + * ________________ + * | | + * | | + * | | + * | | + * +--------->| Non-Member |<---------+ + * | | | | + * | | | | + * | | | | + * | |________________| | + * | | | + * | leave group | join group | leave group + * | (stop timer, |(send report, | (send leave + * | send leave if | set flag, | if flag set) + * | flag set) | start timer) | + * ________|________ | ________|________ + * | |<---------+ | | + * | | | | + * | |<-------------------| | + * | | query received | | + * | Delaying Member | (start timer) | Idle Member | + * +---->| |------------------->| | + * | | | report received | | + * | | | (stop timer, | | + * | | | clear flag) | | + * | |_________________|------------------->|_________________| + * | query received | timer expired + * | (reset timer if | (send report, + * | Max Resp Time | set flag) + * | < current timer) | + * +-------------------+ + * + * Assumptions: + * This function cannot be called from interrupt handling logic! + * + ****************************************************************************/ + +void igmp_joingroup(struct uip_driver_s *dev, uip_ipaddr_t *grpaddr) +{ + struct igmp_group_s *group; + + /* Check if a this address is already in the group */ + + group = uip_grpfind(dev, grpaddr); + if (!group) + { + /* No... allocate a new entry */ + + nvdbg("Join to new group\n"); + group = uip_grpalloc(dev, grpaddr); + IGMP_STATINCR(uip_stat.igmp.joins); + + /* Send the Membership Report */ + + IGMP_STATINCR(uip_stat.igmp.report_sched); + uip_igmpwaitmsg(group, IGMPv2_MEMBERSHIP_REPORT); + + /* And start the timer at 10*100 msec */ + + uip_igmpstarttimer(group, 10); + + /* Add the group (MAC) address to the ether drivers MAC filter list */ + + uip_igmpmac(dev, grpaddr, true); + } +} + +#endif /* CONFIG_NET_IGMP */ diff --git a/net/uip/uip_igmpleave.c b/net/uip/uip_igmpleave.c new file mode 100755 index 00000000000..22775ede8ea --- /dev/null +++ b/net/uip/uip_igmpleave.c @@ -0,0 +1,163 @@ +/**************************************************************************** + * net/uip/uip_igmpleave.c + * + * Copyright (C) 2010 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * The NuttX implementation of IGMP was inspired by the IGMP add-on for the + * lwIP TCP/IP stack by Steve Reynolds: + * + * Copyright (c) 2002 CITEL Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of CITEL Technologies Ltd nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CITEL TECHNOLOGIES AND CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL CITEL TECHNOLOGIES OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include + +#include +#include +#include + +#include "uip_internal.h" + +#ifdef CONFIG_NET_IGMP + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: igmp_leavegroup + * + * Description: + * Remove the specified group address to the group. + * + * RFC 2236, 3. Protocol Description: + * + * "When a host leaves a multicast group, if it was the last host to + * reply to a Query with a Membership Report for that group, it SHOULD + * send a Leave Group message to the all-routers multicast group + * (224.0.0.2). If it was not the last host to reply to a Query, it MAY + * send nothing as there must be another member on the subnet. This is + * an optimization to reduce traffic; a host without sufficient storage + * to remember whether or not it was the last host to reply MAY always + * send a Leave Group message when it leaves a group. Routers SHOULD + * accept a Leave Group message addressed to the group being left, in + * order to accommodate implementations of an earlier version of this + * standard. Leave Group messages are addressed to the all-routers + * group because other group members have no need to know that a host + * has left the group, but it does no harm to address the message to the + * group." + * + * ________________ + * | | + * | | + * | | + * | | + * +--------->| Non-Member |<---------+ + * | | | | + * | | | | + * | | | | + * | |________________| | + * | | | + * | leave group | join group | leave group + * | (stop timer, |(send report, | (send leave + * | send leave if | set flag, | if flag set) + * | flag set) | start timer) | + * ________|________ | ________|________ + * | |<---------+ | | + * | | | | + * | |<-------------------| | + * | | query received | | + * | Delaying Member | (start timer) | Idle Member | + * +---->| |------------------->| | + * | | | report received | | + * | | | (stop timer, | | + * | | | clear flag) | | + * | |_________________|------------------->|_________________| + * | query received | timer expired + * | (reset timer if | (send report, + * | Max Resp Time | set flag) + * | < current timer) | + * +-------------------+ + * + * Assumptions: + * This function cannot be called from interrupt handling logic! + * + ****************************************************************************/ + +void igmp_leavegroup(struct uip_driver_s *dev, uip_ipaddr_t *grpaddr) +{ + struct igmp_group_s *group; + + /* Find the entry corresponding to the address leaving the group */ + + group = uip_grpfind(dev, grpaddr); + if (group) + { + /* Cancel the timer */ + + wd_cancel(group->wdog); + IGMP_STATINCR(uip_stat.igmp.leaves); + + /* Send a leave if the flag is set according to the state diagram */ + + if (IS_LASTREPORT(group->flags)) + { + ndbg("Leaving group\n"); + IGMP_STATINCR(uip_stat.igmp.leave_sched); + uip_igmpwaitmsg(group, IGMP_LEAVE_GROUP); + } + + /* Free the group structure (state is now Non-Member */ + + uip_grpfree(dev, group); + + /* And remove the group address from the ethernet drivers MAC filter set */ + + uip_igmpmac(dev, grpaddr, false); + } +} + +#endif /* CONFIG_NET_IGMP */ diff --git a/net/uip/uip_igmpmsg.c b/net/uip/uip_igmpmsg.c new file mode 100755 index 00000000000..44d0f245934 --- /dev/null +++ b/net/uip/uip_igmpmsg.c @@ -0,0 +1,139 @@ +/**************************************************************************** + * net/uip/uip_igmpmgs.c + * + * Copyright (C) 2010 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * The NuttX implementation of IGMP was inspired by the IGMP add-on for the + * lwIP TCP/IP stack by Steve Reynolds: + * + * Copyright (c) 2002 CITEL Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of CITEL Technologies Ltd nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CITEL TECHNOLOGIES AND CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL CITEL TECHNOLOGIES OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include +#include +#include + +#include "uip_internal.h" + +#ifdef CONFIG_NET_IGMP + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: uip_igmpschedmsg + * + * Description: + * Schedule a message to be send at the next driver polling interval. + * + * Assumptions: + * This function may be called in most any context. + * + ****************************************************************************/ + +void uip_igmpschedmsg(FAR struct igmp_group_s *group, uint8_t msgid) +{ + irqstate_t flags = irqsave(); + + /* The following should be atomic */ + + flags = irqsave(); + DEBUGASSERT(!IS_SCHEDMSG(group->flags)); + group->msgid = msgid; + SET_SCHEDMSG(group->flags); + irqrestore(flags); +} + +/**************************************************************************** + * Name: uip_igmpwaitmsg + * + * Description: + * Schedule a message to be send at the next driver polling interval and + * block, waiting for the message to be sent. + * + * Assumptions: + * This function cannot be called from an interrupt handler (if you try it, + * sem_wait will assert). + * + ****************************************************************************/ + +void uip_igmpwaitmsg(FAR struct igmp_group_s *group, uint8_t msgid) +{ + irqstate_t flags; + + /* Schedule to send the message */ + + flags = irqsave(); + DEBUGASSERT(!IS_WAITMSG(group->flags)); + SET_WAITMSG(group->flags); + uip_igmpschedmsg(group, msgid); + + /* Then wait for the message to be sent */ + + while (IS_SCHEDMSG(group->flags)) + { + /* Wait for the semaphore to be posted */ + + while (sem_wait(&group->sem) != 0) + { + /* The only error that should occur from sem_wait() is if + * the wait is awakened by a signal. + */ + + ASSERT(errno == EINTR); + } + } + + /* The message has been sent and we are no longer waiting */ + + CLR_WAITMSG(group->flags); + irqrestore(flags); +} + +#endif /* CONFIG_NET_IGMP */ diff --git a/net/uip/uip_igmppoll.c b/net/uip/uip_igmppoll.c index 7ba4b50a086..5d74732fa90 100755 --- a/net/uip/uip_igmppoll.c +++ b/net/uip/uip_igmppoll.c @@ -1,7 +1,5 @@ /**************************************************************************** - * net/uip/uip-igmp.h - * The definitions in this header file are intended only for internal use - * by the NuttX port of the uIP stack. + * net/uip/uip_igmppoll.c * * Copyright (C) 2010 Gregory Nutt. All rights reserved. * Author: Gregory Nutt @@ -85,7 +83,9 @@ static inline void uip_schedsend(FAR struct uip_driver_s *dev, FAR struct igmp_g { uip_ipaddr_t *dest; - /* IGMP header + IP header + 8 bytes of data */ + /* Check what kind of messsage we need to send. There are only two + * possibilities: + */ if (group->msgid == IGMPv2_MEMBERSHIP_REPORT) { @@ -104,7 +104,21 @@ static inline void uip_schedsend(FAR struct uip_driver_s *dev, FAR struct igmp_g uiphdr_ipaddr_copy(IGMPBUF->grpaddr, &group->grpaddr); } + /* Send the message */ + uip_igmpsend(dev, dest); + + /* Indicate that the message has been sent */ + + CLR_SCHEDMSG(group->flags); + group->msgid = 0; + + /* If there is a thread waiting fore the message to be sent, wake it up */ + + if (IS_WAITMSG(group->flags)) + { + sem_post(&group->sem); + } } /**************************************************************************** diff --git a/net/uip/uip_internal.h b/net/uip/uip_internal.h index 43f1771b18a..9148a4ab24e 100644 --- a/net/uip/uip_internal.h +++ b/net/uip/uip_internal.h @@ -230,6 +230,11 @@ EXTERN FAR struct igmp_group_s *uip_grpallocfind(FAR struct uip_driver_s *dev, EXTERN void uip_grpfree(FAR struct uip_driver_s *dev, FAR struct igmp_group_s *group); +/* Defined in uip_igmpmsg.c **************************************************/ + +EXTERN void uip_igmpschedmsg(FAR struct igmp_group_s *group, uint8_t msgid); +EXTERN void uip_igmpwaitmsg(FAR struct igmp_group_s *group, uint8_t msgid); + /* Defined in uip_igmppoll.c *************************************************/ EXTERN void uip_igmppoll(FAR struct uip_driver_s *dev); @@ -238,6 +243,10 @@ EXTERN void uip_igmppoll(FAR struct uip_driver_s *dev); EXTERN void uip_igmpsend(FAR struct uip_driver_s *dev, uip_ipaddr_t *dest); +/* Defined in uip_igmptimer.c ************************************************/ + +EXTERN void uip_igmpstarttimer(struct igmp_group_s *group, uint8_t decisecs); + /* Defined in TBD ************************************************************/ EXTERN void uip_igmpmac(struct uip_driver_s *dev, uip_ipaddr_t *ip, bool on);