예제 #1
0
void
testEpochValidate (void)
{
  struct timeval tv;
  unsigned long utt;

  hBSP430uptimeTimer()->hpl->r = 0;
  hBSP430uptimeTimer()->overflow_count = 0;

  BSP430_UNITTEST_ASSERT_TRUE(0 == iBSP430uptimeSetEpochFromTimeval(&basetv, 0UL));
  BSP430_UNITTEST_ASSERT_TRUE(0 == iBSP430uptimeCheckEpochValidity());
  hBSP430uptimeTimer()->overflow_count += HALF_ERA >> 16;
  utt = ulBSP430uptime();
  BSP430_UNITTEST_ASSERT_EQUAL_FMTd(-1, iBSP430uptimeAsTimeval(utt, &tv));
  BSP430_UNITTEST_ASSERT_TRUE(0 > iBSP430uptimeCheckEpochValidity());

  hBSP430uptimeTimer()->overflow_count = 0;
  BSP430_UNITTEST_ASSERT_TRUE(0 == iBSP430uptimeSetEpochFromTimeval(&basetv, 0UL));
  BSP430_UNITTEST_ASSERT_TRUE(0 == iBSP430uptimeCheckEpochValidity());
  hBSP430uptimeTimer()->overflow_count += HALF_ERA >> 16;
  utt = ulBSP430uptime();
  BSP430_UNITTEST_ASSERT_EQUAL_FMTlx((time_t)-1, xBSP430uptimeAsPOSIXTime(utt));
  BSP430_UNITTEST_ASSERT_TRUE(0 > iBSP430uptimeCheckEpochValidity());
}
예제 #2
0
void
testSetEpochFromTimeval (void)
{
  unsigned long utt;

  utt = ulBSP430uptime();
  BSP430_UNITTEST_ASSERT_EQUAL_FMTlu(0UL, utt);
  BSP430_UNITTEST_ASSERT_TRUE(0 == iBSP430uptimeSetEpochFromTimeval(&basetv, utt));
  BSP430_UNITTEST_ASSERT_TRUE(0 == iBSP430uptimeCheckEpochValidity());
  BSP430_UNITTEST_ASSERT_EQUAL_FMTlu(basetv.tv_sec, xBSP430uptimeAsPOSIXTime(utt));
  hBSP430uptimeTimer()->overflow_count = 10;
  utt = ulBSP430uptime();
  BSP430_UNITTEST_ASSERT_EQUAL_FMTlu(655360UL, utt);
  BSP430_UNITTEST_ASSERT_EQUAL_FMTlu(basetv.tv_sec+20, xBSP430uptimeAsPOSIXTime(utt));

  BSP430_UNITTEST_ASSERT_TRUE(0 == iBSP430uptimeSetEpochFromTimeval(&basetv, utt));
  BSP430_UNITTEST_ASSERT_EQUAL_FMTlu(basetv.tv_sec, xBSP430uptimeAsPOSIXTime(utt));
  BSP430_UNITTEST_ASSERT_EQUAL_FMTlu(basetv.tv_sec-20, xBSP430uptimeAsPOSIXTime(0UL));

  hBSP430uptimeTimer()->overflow_count = 0;
}
예제 #3
0
void
testEpochEra (void)
{
  unsigned long utt;
  struct timeval tv;

  BSP430_UNITTEST_ASSERT_EQUAL_FMTlx(BSP430_UPTIME_EPOCH_VALID_OFFSET_UTT, QUARTER_ERA + EIGHTH_ERA);

  /* Test boundaries and wrap into previous era */
  hBSP430uptimeTimer()->hpl->r = 0;
  hBSP430uptimeTimer()->overflow_count = EIGHTH_ERA >> 16;
  utt = ulBSP430uptime();
  BSP430_UNITTEST_ASSERT_EQUAL_FMTlu(EIGHTH_ERA, utt);

  BSP430_UNITTEST_ASSERT_TRUE(0 == iBSP430uptimeSetEpochFromTimeval(&basetv, 0UL));
  BSP430_UNITTEST_ASSERT_TRUE(0 == iBSP430uptimeCheckEpochValidity());
  BSP430_UNITTEST_ASSERT_EQUAL_FMTlu(basetv.tv_sec+2*hBSP430uptimeTimer()->overflow_count, xBSP430uptimeAsPOSIXTime(utt));

  BSP430_UNITTEST_ASSERT_EQUAL_FMTd(1, iBSP430uptimeEpochEra(utt));
  BSP430_UNITTEST_ASSERT_EQUAL_FMTd(1, iBSP430uptimeEpochEra(0));
  BSP430_UNITTEST_ASSERT_EQUAL_FMTd(1, iBSP430uptimeEpochEra(QUARTER_ERA));
  BSP430_UNITTEST_ASSERT_EQUAL_FMTd(1, iBSP430uptimeEpochEra(utt+BSP430_UPTIME_EPOCH_VALID_OFFSET_UTT-1));
  BSP430_UNITTEST_ASSERT_EQUAL_FMTd(-1, iBSP430uptimeEpochEra(utt+BSP430_UPTIME_EPOCH_VALID_OFFSET_UTT));
  BSP430_UNITTEST_ASSERT_EQUAL_FMTd(0, iBSP430uptimeEpochEra(-1));
  BSP430_UNITTEST_ASSERT_EQUAL_FMTd(0, iBSP430uptimeEpochEra(-EIGHTH_ERA));
  BSP430_UNITTEST_ASSERT_EQUAL_FMTd(0, iBSP430uptimeEpochEra(-(QUARTER_ERA-1)));
  BSP430_UNITTEST_ASSERT_EQUAL_FMTd(-1, iBSP430uptimeEpochEra(-QUARTER_ERA));

  BSP430_UNITTEST_ASSERT_TRUE(0 == iBSP430uptimeAsTimeval(0UL, &tv));
  BSP430_UNITTEST_ASSERT_EQUAL_FMTld(basetv.tv_sec, tv.tv_sec);
  BSP430_UNITTEST_ASSERT_EQUAL_FMTld(basetv.tv_usec, tv.tv_usec);
  BSP430_UNITTEST_ASSERT_TRUE(0 == iBSP430uptimeAsTimeval(QUARTER_ERA, &tv));
  BSP430_UNITTEST_ASSERT_EQUAL_FMTld(basetv.tv_sec + UTT_TO_S(QUARTER_ERA), tv.tv_sec);
  BSP430_UNITTEST_ASSERT_EQUAL_FMTld(basetv.tv_usec, tv.tv_usec);
  BSP430_UNITTEST_ASSERT_TRUE(0 == iBSP430uptimeAsTimeval(-EIGHTH_ERA, &tv));
  BSP430_UNITTEST_ASSERT_EQUAL_FMTld(basetv.tv_sec - UTT_TO_S(EIGHTH_ERA), tv.tv_sec);
  BSP430_UNITTEST_ASSERT_EQUAL_FMTld(basetv.tv_usec, tv.tv_usec);

  /* Test boundaries in current era */
  hBSP430uptimeTimer()->hpl->r = 0;
  hBSP430uptimeTimer()->overflow_count = HALF_ERA >> 16;
  utt = ulBSP430uptime();
  BSP430_UNITTEST_ASSERT_EQUAL_FMTlu(HALF_ERA, utt);

  BSP430_UNITTEST_ASSERT_TRUE(0 == iBSP430uptimeSetEpochFromTimeval(&basetv, 0UL));
  BSP430_UNITTEST_ASSERT_TRUE(0 == iBSP430uptimeCheckEpochValidity());
  BSP430_UNITTEST_ASSERT_EQUAL_FMTlu(basetv.tv_sec+2*hBSP430uptimeTimer()->overflow_count, xBSP430uptimeAsPOSIXTime(utt));

  BSP430_UNITTEST_ASSERT_EQUAL_FMTd(1, iBSP430uptimeEpochEra(utt));
  BSP430_UNITTEST_ASSERT_EQUAL_FMTd(-1, iBSP430uptimeEpochEra(0));
  BSP430_UNITTEST_ASSERT_EQUAL_FMTd(-1, iBSP430uptimeEpochEra(utt-BSP430_UPTIME_EPOCH_VALID_OFFSET_UTT));
  BSP430_UNITTEST_ASSERT_EQUAL_FMTd(1, iBSP430uptimeEpochEra(utt-(BSP430_UPTIME_EPOCH_VALID_OFFSET_UTT-1)));
  BSP430_UNITTEST_ASSERT_EQUAL_FMTd(1, iBSP430uptimeEpochEra(utt));
  BSP430_UNITTEST_ASSERT_EQUAL_FMTd(1, iBSP430uptimeEpochEra(utt+BSP430_UPTIME_EPOCH_VALID_OFFSET_UTT-1));
  BSP430_UNITTEST_ASSERT_EQUAL_FMTd(-1, iBSP430uptimeEpochEra(utt+BSP430_UPTIME_EPOCH_VALID_OFFSET_UTT));
  BSP430_UNITTEST_ASSERT_EQUAL_FMTd(-1, iBSP430uptimeEpochEra(-1));

  BSP430_UNITTEST_ASSERT_TRUE(0 == iBSP430uptimeAsTimeval(QUARTER_ERA, &tv));
  BSP430_UNITTEST_ASSERT_EQUAL_FMTld(basetv.tv_sec + UTT_TO_S(QUARTER_ERA), tv.tv_sec);
  BSP430_UNITTEST_ASSERT_EQUAL_FMTld(basetv.tv_usec, tv.tv_usec);
  BSP430_UNITTEST_ASSERT_TRUE(0 == iBSP430uptimeAsTimeval(HALF_ERA, &tv));
  BSP430_UNITTEST_ASSERT_EQUAL_FMTld(basetv.tv_sec + UTT_TO_S(HALF_ERA), tv.tv_sec);
  BSP430_UNITTEST_ASSERT_EQUAL_FMTld(basetv.tv_usec, tv.tv_usec);
  BSP430_UNITTEST_ASSERT_TRUE(0 == iBSP430uptimeAsTimeval(HALF_ERA + QUARTER_ERA, &tv));
  BSP430_UNITTEST_ASSERT_EQUAL_FMTld(basetv.tv_sec + UTT_TO_S(HALF_ERA + QUARTER_ERA), tv.tv_sec);
  BSP430_UNITTEST_ASSERT_EQUAL_FMTld(basetv.tv_usec, tv.tv_usec);

  /* Test boundaries and wrap to next era */
  hBSP430uptimeTimer()->hpl->r = 0;
  hBSP430uptimeTimer()->overflow_count = (HALF_ERA + QUARTER_ERA + EIGHTH_ERA) >> 16;
  utt = ulBSP430uptime();
  BSP430_UNITTEST_ASSERT_EQUAL_FMTlu(HALF_ERA + QUARTER_ERA + EIGHTH_ERA, utt);

  BSP430_UNITTEST_ASSERT_TRUE(0 == iBSP430uptimeSetEpochFromTimeval(&basetv, 0UL));
  BSP430_UNITTEST_ASSERT_TRUE(0 == iBSP430uptimeCheckEpochValidity());
  BSP430_UNITTEST_ASSERT_EQUAL_FMTlu(basetv.tv_sec+2*hBSP430uptimeTimer()->overflow_count, xBSP430uptimeAsPOSIXTime(utt));

  BSP430_UNITTEST_ASSERT_EQUAL_FMTd(1, iBSP430uptimeEpochEra(utt));
  BSP430_UNITTEST_ASSERT_EQUAL_FMTd(-1, iBSP430uptimeEpochEra(utt-BSP430_UPTIME_EPOCH_VALID_OFFSET_UTT));
  BSP430_UNITTEST_ASSERT_EQUAL_FMTd(1, iBSP430uptimeEpochEra(utt-(BSP430_UPTIME_EPOCH_VALID_OFFSET_UTT-1)));
  BSP430_UNITTEST_ASSERT_EQUAL_FMTd(1, iBSP430uptimeEpochEra(utt));
  BSP430_UNITTEST_ASSERT_EQUAL_FMTd(2, iBSP430uptimeEpochEra(utt+BSP430_UPTIME_EPOCH_VALID_OFFSET_UTT-1));
  BSP430_UNITTEST_ASSERT_EQUAL_FMTd(-1, iBSP430uptimeEpochEra(utt+BSP430_UPTIME_EPOCH_VALID_OFFSET_UTT));
  BSP430_UNITTEST_ASSERT_EQUAL_FMTd(1, iBSP430uptimeEpochEra(-1));
  BSP430_UNITTEST_ASSERT_EQUAL_FMTd(2, iBSP430uptimeEpochEra(0));

  BSP430_UNITTEST_ASSERT_TRUE(0 == iBSP430uptimeAsTimeval(HALF_ERA + QUARTER_ERA, &tv));
  BSP430_UNITTEST_ASSERT_EQUAL_FMTld(basetv.tv_sec + UTT_TO_S(HALF_ERA + QUARTER_ERA), tv.tv_sec);
  BSP430_UNITTEST_ASSERT_EQUAL_FMTld(basetv.tv_usec, tv.tv_usec);
  BSP430_UNITTEST_ASSERT_TRUE(0 == iBSP430uptimeAsTimeval(0L, &tv));
  BSP430_UNITTEST_ASSERT_EQUAL_FMTld(basetv.tv_sec + UTT_TO_S(FULL_ERA), tv.tv_sec);
  BSP430_UNITTEST_ASSERT_EQUAL_FMTld(basetv.tv_usec, tv.tv_usec);
  BSP430_UNITTEST_ASSERT_TRUE(0 == iBSP430uptimeAsTimeval(EIGHTH_ERA, &tv));
  BSP430_UNITTEST_ASSERT_EQUAL_FMTld(basetv.tv_sec + UTT_TO_S(FULL_ERA + EIGHTH_ERA), tv.tv_sec);
  BSP430_UNITTEST_ASSERT_EQUAL_FMTld(basetv.tv_usec, tv.tv_usec);
}
예제 #4
0
void
testInitialConditions (void)
{
  BSP430_UNITTEST_ASSERT_EQUAL_FMTlu(0, ulBSP430uptime());
  BSP430_UNITTEST_ASSERT_TRUE(0 > iBSP430uptimeCheckEpochValidity());
}
예제 #5
0
void
testNTPSequence (void)
{
  const uint8_t data0[] = {
    0x24, 0x03, 0x03, 0xea, 0x00, 0x00, 0x11, 0x1d, 0x00, 0x00, 0x11, 0xcb, 0x32, 0x74, 0x26, 0x9d,
    0xd6, 0xd1, 0xc5, 0x6c, 0x55, 0x49, 0x1f, 0x0a, 0xd6, 0x6d, 0xd9, 0x01, 0xbf, 0xf4, 0x00, 0x00,
    0xd6, 0xd1, 0xc8, 0xda, 0xfc, 0x61, 0x70, 0x5f, 0xd6, 0xd1, 0xc8, 0xda, 0xfc, 0x65, 0x26, 0x4f,
  };
  const uint64_t recv0_ntp = 15451244498192695296ULL;
  const int64_t adj0_ntp = 28129738957212503LL;
  const int32_t adj0_ms = 2147483647L;
  const uint32_t rtt0_us = 17643;
  /*
    recvfrom got 48 from 192.168.65.10:123 fam 2
    Recv time 57918 is 15451244498192695296 rc 0 ; invalid epoch 1
    t1=15451244498116673536
    t2=15479374237111775327
    t3=15479374237112018511
    t4=15451244498192695296
    Process got 0, adjustment 28129738957212503 ntp -2040469365 ms, rtt 17643 us
  */
  const uint8_t data1[] = {
    0x24, 0x03, 0x03, 0xea, 0x00, 0x00, 0x11, 0x1d, 0x00, 0x00, 0x12, 0x06, 0x32, 0x74, 0x26, 0x9d,
    0xd6, 0xd1, 0xc5, 0x6c, 0x55, 0x49, 0x1f, 0x0a, 0xd6, 0xd1, 0xc9, 0x16, 0xfb, 0xe9, 0x4b, 0x57,
    0xd6, 0xd1, 0xc9, 0x16, 0xfd, 0xf6, 0x2d, 0x28, 0xd6, 0xd1, 0xc9, 0x16, 0xfd, 0xf8, 0x75, 0x2b,
  };
  const uint64_t recv1_ntp = 15479374494877961047ULL;
  const int64_t adj1_ntp = -3537453LL;
  const int32_t adj1_ms = -1;
  const uint32_t rtt1_us = 17665;
  /*
    recvfrom got 48 from 192.168.65.10:123 fam 2
    Recv time 2024227 is 15479374494877961047 rc 0 ; invalid epoch 0
    t1=15479374494801939287
    t2=15479374494836337960
    t3=15479374494836487467
    t4=15479374494877961047
    Process got 0, adjustment -3537453 ntp -1 ms, rtt 17665 us
  */
  const sBSP430uptimeNTPPacketHeader * ph;
  int64_t adjustment_ntp;
  int32_t adjustment_ms;
  uint32_t rtt_us;

  hBSP430uptimeTimer()->hpl->r = 0;
  hBSP430uptimeTimer()->overflow_count = 0;
  BSP430_UNITTEST_ASSERT_TRUE(0 == iBSP430uptimeSetEpochFromTimeval(&basetv, 0UL));
  BSP430_UNITTEST_ASSERT_EQUAL_FMTlx((time_t)-1, xBSP430uptimeAsPOSIXTime(HALF_ERA));
  BSP430_UNITTEST_ASSERT_TRUE(0 > iBSP430uptimeCheckEpochValidity());

  ph = (const sBSP430uptimeNTPPacketHeader *)data0;
  BSP430_UNITTEST_ASSERT_EQUAL_FMTd(0, iBSP430uptimeProcessNTPResponse(NULL, ph, recv0_ntp,
                                                                       &adjustment_ntp, &adjustment_ms, &rtt_us));
  BSP430_UNITTEST_ASSERT_EQUAL(adj0_ntp, adjustment_ntp);
  BSP430_UNITTEST_ASSERT_EQUAL_FMTld(adj0_ms, adjustment_ms);
  BSP430_UNITTEST_ASSERT_EQUAL_FMTld(rtt0_us, rtt_us);

  ph = (const sBSP430uptimeNTPPacketHeader *)data1;
  BSP430_UNITTEST_ASSERT_EQUAL_FMTd(0, iBSP430uptimeProcessNTPResponse(NULL, ph, recv1_ntp,
                                                                       &adjustment_ntp, &adjustment_ms, &rtt_us));
  BSP430_UNITTEST_ASSERT_EQUAL(adj1_ntp, adjustment_ntp);
  BSP430_UNITTEST_ASSERT_EQUAL_FMTld(adj1_ms, adjustment_ms);
  BSP430_UNITTEST_ASSERT_EQUAL_FMTld(rtt1_us, rtt_us);
}
예제 #6
0
파일: main.c 프로젝트: pabigot/bsp430
void main (void)
{
  unsigned long wake_utt;
  int rc;
  long lrc;
  char as_text[BSP430_UPTIME_AS_TEXT_LENGTH];
  uint32_u ntp_addr;
  uint32_u self_addr;

  vBSP430platformInitialize_ni();
  (void)iBSP430consoleInitialize();
  cprintf("\nntp " __DATE__ " " __TIME__ "\n");

  /* Initialization can be done with interrupts disabled, since the
   * function does nothing but store callbacks.  We use the same
   * callback for all three update capabilities. */
  rc = iBSP430cc3000spiInitialize(wlan_cb, NULL, NULL, NULL);
  if (0 > rc) {
    cprintf("ERR: Initialization failed: %d\n", rc);
    return;
  }

  BSP430_CORE_ENABLE_INTERRUPT();

  /* Local addresses use all zeros for inet addr.  bind does not
   * support dynamic assignment of unused port through sin_port=0. */
  memset(&local_addr, 0, sizeof(local_addr));
  local_addr.sai.sin_family = AF_INET;
  local_addr.sai.sin_port = htons(60123);

  /* Remote server will be determined by DNS from the NTP pool once we
   * start. */
  remote_addr = local_addr;
  remote_addr.sai.sin_port = htons(123);

  ntp_addr.u32 = 0;
  self_addr.u32 = 0;

  cprintf("Remote: %s:%u\n", net_ipv4AsText(&remote_addr.sai.sin_addr), ntohs(remote_addr.sai.sin_port));
  rc = sizeof(sBSP430uptimeNTPPacketHeader);
  if (48 != rc) {
    cprintf("ERR: NTP header size %d\n", rc);
    return;
  }
  wake_utt = ulBSP430uptime();

  (void)rc;
  while (1) {
    unsigned long timeout_utt;

    do {
      tNetappIpconfigRetArgs ipc;
      unsigned long start_utt;
      unsigned long finished_utt;
      int sfd;
      int nfds;
      fd_set rfds;
      int servers_left;
      int retries_left;

      /* Clear everything as we're starting a cycle */
      BSP430_CORE_DISABLE_INTERRUPT();
      do {
        event_flags_v = 0;
        start_utt = ulBSP430uptime_ni();
      } while (0);
      BSP430_CORE_ENABLE_INTERRUPT();

      /* Start the WAN process.  This is asynchronous; wait up to 2
       * seconds for it to complete. */
      cprintf("%s: ", xBSP430uptimeAsText(start_utt, as_text));
      cputchar('W');
      wlan_start(0);
      vBSP430ledSet(BSP430_LED_RED, 1);
      (void)wlan_set_event_mask(0UL);
      lrc = BSP430_UPTIME_MS_TO_UTT(2000);
      timeout_utt = ulBSP430uptime() + lrc;
      while ((! (EVENT_FLAG_WLANCONN & event_flags_v))
             && (0 < ((lrc = lBSP430uptimeSleepUntil(timeout_utt, LPM0_bits))))) {
      }
      if (! (EVENT_FLAG_WLANCONN & event_flags_v)) {
        cprintf("WLAN start failed\n");
        break;
      }

      /* Wait for IP connectivity (signalled by a DHCP event).
       * Continue using the previous timeout. */
      cputchar('D');
      while ((! (EVENT_FLAG_IPCONN & event_flags_v))
             && (0 < ((lrc = lBSP430uptimeSleepUntil(timeout_utt, LPM0_bits))))) {
      }
      if (! (EVENT_FLAG_IPCONN & event_flags_v)) {
        cprintf("IP conn failed\n");
        break;
      }

      /* Inspect the IP configuration.  Sometimes we get the event,
       * but there's no IP assigned. */
      netapp_ipconfig(&ipc);
      memcpy(self_addr.u8, ipc.aucIP, sizeof(self_addr));
      if (! self_addr.u32) {
        cprintf("IP assignment failed\n");
        break;
      }
      vBSP430ledSet(BSP430_LED_GREEN, 1);

      /* Obtain a UDP socket and bind it for local operations. */
      cputchar('I');
      sfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
      if (0 > sfd) {
        cprintf("socket() failed: %d\n", sfd);
        break;
      }
      cputchar('S');
      lrc = bind(sfd, &local_addr.sa, sizeof(local_addr.sa));
      if (0 > lrc) {
        cprintf("bind() failed: %ld\n", lrc);
        break;
      }
      cputchar('B');

      servers_left = NTP_SERVERS_PER_ATTEMPT;
      retries_left = NTP_REQUESTS_PER_SERVER;
      do {
        sBSP430uptimeNTPPacketHeader ntp0;
        sBSP430uptimeNTPPacketHeader ntp1;
        int have_invalid_epoch;
        struct timeval tv;
        sockaddr_u src;
        socklen_t slen = sizeof(src);
        unsigned long recv_utt;
        uint64_t recv_ntp;
        int64_t adjustment_ntp;
        long adjustment_ms;
        unsigned long rtt_us;

        have_invalid_epoch = 0 != iBSP430uptimeCheckEpochValidity();
        if (! remote_addr.sai.sin_addr.s_addr) {
          const char ntp_fqdn[] = "0.pool.ntp.org";
          ntp_addr.u32 = 0;
          rc = gethostbyname((char *)ntp_fqdn, sizeof(ntp_fqdn)-1, &ntp_addr.u32);
          cputchar('d');
          if (-95 == rc) { /* ARP request failed; retry usually works */
            rc = gethostbyname((char *)ntp_fqdn, sizeof(ntp_fqdn)-1, &ntp_addr.u32);
            cputchar('d');
          }
          if (0 == ntp_addr.u32) {
            cprintf("gethostbyname(%s) failed: %d\n", ntp_fqdn, rc);
            rc = -1;
            break;
          }
          remote_addr.sai.sin_addr.s_addr = htonl(ntp_addr.u32);
          cprintf("{%s}", net_ipv4AsText(&remote_addr.sai.sin_addr));
          retries_left = NTP_REQUESTS_PER_SERVER;
        }

        /* Configure the NTP request and send it */
        iBSP430uptimeInitializeNTPRequest(&ntp0);
        iBSP430uptimeSetNTPXmtField(&ntp0, NULL);
        BSP430_CORE_DISABLE_INTERRUPT();
        do {
          /* Clear the shutdown bit, so we know when it's ok to shut
           * down after this send */
          event_flags_v &= ~EVENT_FLAG_SHUTDOWN;
        } while (0);
        BSP430_CORE_ENABLE_INTERRUPT();
        rc = sendto(sfd, &ntp0, sizeof(ntp0), 0, &remote_addr.sa, sizeof(remote_addr.sai));
        if (sizeof(ntp0) != rc) {
          cprintf("sendto %s:%u failed: %d\n", net_ipv4AsText(&remote_addr.sai.sin_addr), ntohs(remote_addr.sai.sin_port), rc);
          rc = -1;
          break;
        }
        cputchar('s');

        /* If we get an answer it should be here in less than 100
         * ms, but give it 400 ms just to be kind. */
        tv.tv_sec = 0;
        tv.tv_usec = 400000UL;
        FD_ZERO(&rfds);
        FD_SET(sfd, &rfds);
        nfds = sfd+1;
        rc = select(nfds, &rfds, NULL, NULL, &tv);
        if (! FD_ISSET(sfd, &rfds)) {
          /* We didn't get an answer.  If there are any retries left, use them. */
          if (0 < retries_left--) {
            rc = 1;
            continue;
          }
          /* No retries left on this server: forget about it.  If
           * there are any servers left, try another. */
          cputchar('!');
          remote_addr.sai.sin_addr.s_addr = 0;
          if (0 < servers_left--) {
            rc = 1;
            continue;
          }
          /* No retries from all available servers.  Fail this attempt */
          cprintf("no responsive NTP server found\n");
          rc = -1;
          break;
        }

        /* Got a response.  Record the time it came in and then read
         * it (no high-resolution packet RX time available, but we
         * believe it's here already so set the RX time first).  The
         * message is unacceptable if it isn't an NTP packet. */
        recv_utt = ulBSP430uptime();
        rc = recvfrom(sfd, &ntp1, sizeof(ntp1), 0, &src.sa, &slen);
        if (sizeof(ntp1) != rc) {
          cprintf("recv failed: %d\n", rc);
          rc = -1;
          break;
        }
        cputchar('r');

        /* Convert the RX time to NTP, then process the message to
         * determine the offset. */
        rc = iBSP430uptimeAsNTP(recv_utt, &recv_ntp, have_invalid_epoch);
        if (0 != rc) {
          cprintf("NTP decode failed: %d\n", rc);
          continue;
        }
        rc = iBSP430uptimeProcessNTPResponse(&ntp0, &ntp1, recv_ntp, &adjustment_ntp, &adjustment_ms, &rtt_us);
        if (0 != rc) {
          cprintf("Process failed: %d\n", rc);
          continue;
        }
        if (have_invalid_epoch) {
          rc = iBSP430uptimeSetEpochFromNTP(BSP430_UPTIME_BYPASS_EPOCH_NTP + adjustment_ntp);
          cputchar('E');
          if (0 != rc) {
            cprintf("\nERR: SetEpoch failed: %d\n", rc);
          }
#if (NTP_ADJUST_EACH_ITER - 0)
        } else {
          rc = iBSP430uptimeAdjustEpochFromNTP(adjustment_ntp);
          cputchar('A');
          if (0 != rc) {
            cprintf("\nERR: AdjustEpoch failed: %d\n", rc);
          }
#endif
        }
        cprintf("[%s:%u adj %lld ntp = %ld ms, rtt %lu us]",
                net_ipv4AsText(&remote_addr.sai.sin_addr), ntohs(remote_addr.sai.sin_port),
                adjustment_ntp, adjustment_ms, rtt_us);
      } while (0 != rc);
      if (0 != rc) {
        cprintf("NTP query failed\n");
        break;
      }
#if 0
      /* The shutdown OK seems to arrive about 1000 ms after the last
       * transmit, which is unnecessarily long.  As we're not doing
       * TCP, there's no reason to wait for it. */
      lrc = BSP430_UPTIME_MS_TO_UTT(4000);
      timeout_utt = ulBSP430uptime() + lrc;
      while ((! (EVENT_FLAG_SHUTDOWN & event_flags_v))
             && (0 < ((lrc = lBSP430uptimeSleepUntil(timeout_utt, LPM0_bits))))) {
      }
      if (! (EVENT_FLAG_SHUTDOWN & event_flags_v)) {
        cprintf("SHUTDOWN ok never received\n");
        break;
      }
#endif
      finished_utt = ulBSP430uptime();
      cprintf("[%s]\n", xBSP430uptimeAsText(finished_utt - start_utt, as_text));
    } while (0);

    BSP430_CORE_DISABLE_INTERRUPT();
    do {
      event_flags_v = 0;
    } while (0);
    BSP430_CORE_ENABLE_INTERRUPT();
    wlan_stop();
    vBSP430ledSet(BSP430_LED_GREEN, 0);
    vBSP430ledSet(BSP430_LED_RED, 0);
    wake_utt += 60 * ulBSP430uptimeConversionFrequency_Hz();
    while (0 < lBSP430uptimeSleepUntil(wake_utt, LPM2_bits)) {
    }
  }
  cprintf("Fell off end\n");
}