static void emac_txdone(FAR struct emac_driver_s *priv)
{
  /* Check for errors and update statistics */

  /* If no further xmits are pending, then cancel the TX timeout and
   * disable further Tx interrupts.
   */

  wd_cancel(priv->d_txtimeout);

  /* Then poll uIP for new XMIT data */

  (void)devif_poll(&priv->d_dev, emac_txpoll);
}
Exemple #2
0
static void skel_txdone(FAR struct skel_driver_s *skel)
{
  /* Check for errors and update statistics */

  /* If no further xmits are pending, then cancel the TX timeout and
   * disable further Tx interrupts.
   */

  wd_cancel(skel->sk_txtimeout);

  /* Then poll uIP for new XMIT data */

  (void)uip_poll(&skel->sk_dev, skel_uiptxpoll);
}
Exemple #3
0
void uip_grpfree(FAR struct uip_driver_s *dev, FAR struct igmp_group_s *group)
{
  uip_lock_t flags;

  grplldbg("Free: %p flags: %02x\n", group, group->flags);

  /* Cancel the wdog */

  flags = uip_lock();
  wd_cancel(group->wdog);
  
  /* Remove the group structure from the group list in the device structure */

  sq_rem((FAR sq_entry_t*)group, &dev->grplist);
  
  /* Destroy the wait semapore */

  (void)sem_destroy(&group->sem);

  /* Destroy the wdog */

  wd_delete(group->wdog);
  
  /* 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 CONFIG_PREALLOC_IGMPGROUPS > 0
  if (IS_PREALLOCATED(group->flags))
    {
      grplldbg("Put back on free list\n");
      sq_addlast((FAR sq_entry_t*)group, &g_freelist);
      uip_unlock(flags);
    }
  else
#endif
    {
      /* No.. deallocate the group structure.  Use sched_free() just in case
       * this function is executing within an interrupt handler.
       */

      uip_unlock(flags);
      grplldbg("Call sched_free()\n");
      sched_free(group);
    }
}
void wd_recover(FAR struct tcb_s *tcb)
{
  irqstate_t flags;

  /* The task is being deleted.  If it is waiting for any timed event, then
   * tcb->waitdog will be non-NULL.  Cancel the watchdog now so that no
   * events occur after the watchdog expires.  Obviously there are lots of
   * race conditions here so this will most certainly have to be revisited in
   * the future.
   */

  flags = irqsave();
  if (tcb->waitdog)
    {
      (void)wd_cancel(tcb->waitdog);
      (void)wd_delete(tcb->waitdog);
      tcb->waitdog = NULL;
    }

  irqrestore(flags);
}
Exemple #5
0
static int skel_interrupt(int irq, FAR void *context)
{
  FAR struct skel_driver_s *priv = &g_skel[0];

#ifdef CONFIG_NET_NOINTS
  /* Disable further Ethernet interrupts.  Because Ethernet interrupts are
   * also disabled if the TX timeout event occurs, there can be no race
   * condition here.
   */

  up_disable_irq(CONFIG_skeleton_IRQ);

  /* TODO: Determine if a TX transfer just completed */

    {
      /* If a TX transfer just completed, then cancel the TX timeout so
       * there will be do race condition between any subsequent timeout
       * expiration and the deferred interrupt processing.
       */

       wd_cancel(priv->sk_txtimeout);
    }

  /* Cancel any pending poll work */

  work_cancel(HPWORK, &priv->sk_work);

  /* Schedule to perform the interrupt processing on the worker thread. */

  work_queue(HPWORK, &priv->sk_work, skel_interrupt_work, priv, 0);

#else
  /* Process the interrupt now */

  skel_interrupt_process(priv);
#endif

  return OK;
}
Exemple #6
0
static void misoc_net_txdone(FAR struct misoc_net_driver_s *priv)
{
  /* Check for errors and update statistics */

  NETDEV_TXDONE(priv->misoc_net_dev);

  /* Check if there are pending transmissions */

  /* If no further transmissions are pending, then cancel the TX timeout and
   * disable further Tx interrupts.
   */

  wd_cancel(priv->misoc_net_txtimeout);

  /* And disable further TX interrupts. */

  ethmac_sram_reader_ev_enable_write(0);

  /* In any event, poll the network for new TX data */

  (void)devif_poll(&priv->misoc_net_dev, misoc_net_txpoll);
}
Exemple #7
0
static int max11802_schedule(FAR struct max11802_dev_s *priv)
{
  FAR struct max11802_config_s *config;
  int                           ret;

  /* Get a pointer the callbacks for convenience (and so the code is not so
   * ugly).
   */

  config = priv->config;
  DEBUGASSERT(config != NULL);

  /* Disable further interrupts.  MAX11802 interrupts will be re-enabled
   * after the worker thread executes.
   */

  config->enable(config, false);

  /* Disable the watchdog timer.  It will be re-enabled in the worker thread
   * while the pen remains down.
   */

  wd_cancel(priv->wdog);

  /* Transfer processing to the worker thread.  Since MAX11802 interrupts are
   * disabled while the work is pending, no special action should be required
   * to protected the work queue.
   */

  DEBUGASSERT(priv->work.worker == NULL);
  ret = work_queue(HPWORK, &priv->work, max11802_worker, priv, 0);
  if (ret != 0)
    {
      ierr("ERROR: Failed to queue work: %d\n", ret);
    }

  return OK;
}
Exemple #8
0
static int vnet_ifdown(struct net_driver_s *dev)
{
  FAR struct vnet_driver_s *vnet = (FAR struct vnet_driver_s *)dev->d_private;
  irqstate_t flags;

  /* Disable the Ethernet interrupt */

  flags = enter_critical_section();

  /* Cancel the TX poll timer and TX timeout timers */

  wd_cancel(vnet->sk_txpoll);

  /* Put the EMAC is its reset, non-operational state.  This should be
   * a known configuration that will guarantee the vnet_ifup() always
   * successfully brings the interface back up.
   */

  /* Mark the device "down" */

  vnet->sk_bifup = false;
  leave_critical_section(flags);
  return OK;
}
Exemple #9
0
ssize_t mq_timedreceive(mqd_t mqdes, void *msg, size_t msglen,
                        int *prio, const struct timespec *abstime)
{
  FAR struct tcb_s *rtcb = (FAR struct tcb_s *)g_readytorun.head;
  FAR mqmsg_t *mqmsg;
  irqstate_t saved_state;
  int ret = ERROR;

  DEBUGASSERT(up_interrupt_context() == false && rtcb->waitdog == NULL);

  /* Verify the input parameters and, in case of an error, set
   * errno appropriately.
   */

  if (mq_verifyreceive(mqdes, msg, msglen) != OK)
    {
      return ERROR;
    }

  if (!abstime || abstime->tv_sec < 0 || abstime->tv_nsec > 1000000000)
    {
      set_errno(EINVAL);
      return ERROR;
    }

  /* Create a watchdog.  We will not actually need this watchdog
   * unless the queue is not empty, but we will reserve it up front
   * before we enter the following critical section.
   */

  rtcb->waitdog = wd_create();
  if (!rtcb->waitdog)
    {
      set_errno(EINVAL);
      return ERROR;
    }

  /* Get the next mesage from the message queue.  We will disable
   * pre-emption until we have completed the message received.  This
   * is not too bad because if the receipt takes a long time, it will
   * be because we are blocked waiting for a message and pre-emption
   * will be re-enabled while we are blocked
   */

  sched_lock();

  /* Furthermore, mq_waitreceive() expects to have interrupts disabled
   * because messages can be sent from interrupt level.
   */

  saved_state = irqsave();

  /* Check if the message queue is empty.  If it is NOT empty, then we
   * will not need to start timer.
   */

  if (mqdes->msgq->msglist.head == NULL)
    {
      int ticks;

      /* Convert the timespec to clock ticks.  We must have interrupts
       * disabled here so that this time stays valid until the wait begins.
       */

      int result = clock_abstime2ticks(CLOCK_REALTIME, abstime, &ticks);

      /* If the time has already expired and the message queue is empty,
       * return immediately.
       */

      if (result == OK && ticks <= 0)
        {
          result = ETIMEDOUT;
        }

      /* Handle any time-related errors */

      if (result != OK)
        {
          set_errno(result);
          irqrestore(saved_state);
          sched_unlock();
          wd_delete(rtcb->waitdog);
          rtcb->waitdog = NULL;
          return ERROR;
        }

      /* Start the watchdog */

      wd_start(rtcb->waitdog, ticks, (wdentry_t)mq_rcvtimeout, 1, getpid());
    }

  /* Get the message from the message queue */

  mqmsg = mq_waitreceive(mqdes);

  /* Stop the watchdog timer (this is not harmful in the case where
   * it was never started)
   */

  wd_cancel(rtcb->waitdog);

  /* We can now restore interrupts */

  irqrestore(saved_state);

  /* Check if we got a message from the message queue.  We might
   * not have a message if:
   *
   * - The message queue is empty and O_NONBLOCK is set in the mqdes
   * - The wait was interrupted by a signal
   * - The watchdog timeout expired
   */

  if (mqmsg)
    {
      ret = mq_doreceive(mqdes, mqmsg, msg, prio);
    }

  sched_unlock();
  wd_delete(rtcb->waitdog);
  rtcb->waitdog = NULL;
  return ret;
}
Exemple #10
0
static irqreturn_t e1000_interrupt_handler(int irq, void *dev_id)
{
    struct e1000_dev *e1000 = (struct e1000_dev *)dev_id;

    /* Get and clear interrupt status bits */

    int intr_cause = e1000_inl(e1000, E1000_ICR);
    e1000_outl(e1000, E1000_ICR, intr_cause);

    /* not for me */

    if (intr_cause == 0)
    {
        return IRQ_NONE;
    }

    /* Handle interrupts according to status bit settings */

    /* Link status change */

    if (intr_cause & (1 << 2))
    {
        if (e1000_inl(e1000, E1000_STATUS) & 2)
        {
            e1000->bifup = true;
        }
        else
        {
            e1000->bifup = false;
        }
    }

    /* Check if we received an incoming packet, if so, call skel_receive() */

    /* Rx-descriptor Timer expired */

    if (intr_cause & (1 << 7))
    {
        e1000_receive(e1000);
    }

    /* Tx queue empty */

    if (intr_cause & (1 << 1))
    {
        wd_cancel(e1000->txtimeout);
    }

    /* Check is a packet transmission just completed.  If so, call skel_txdone.
     * This may disable further Tx interrupts if there are no pending
     * tansmissions.
     */

    /* Tx-descriptor Written back */

    if (intr_cause & (1 << 0))
    {
        devif_poll(&e1000->netdev, e1000_txpoll);
    }


    /* Rx-Descriptors Low */

    if (intr_cause & (1 << 4))
    {
        int tail;

        tail = e1000->rx_ring.tail + e1000->rx_ring.free;
        tail %= CONFIG_E1000_N_RX_DESC;
        e1000->rx_ring.tail = tail;
        e1000->rx_ring.free = 0;
        e1000_outl(e1000, E1000_RDT, tail);
    }

    return IRQ_HANDLED;
}
Exemple #11
0
int wd_start(WDOG_ID wdog, int delay, wdentry_t wdentry,  int argc, ...)
{
  va_list    ap;
  FAR wdog_t *curr;
  FAR wdog_t *prev;
  FAR wdog_t *next;
  int32_t    now;
  irqstate_t saved_state;
  int        i;

  /* Verify the wdog */

  if (!wdog || argc > CONFIG_MAX_WDOGPARMS || delay < 0)
    {
      *get_errno_ptr() = EINVAL;
      return ERROR;
    }

  /* Check if the watchdog has been started. If so, stop it.
   * NOTE:  There is a race condition here... the caller may receive
   * the watchdog between the time that wd_start is called and
   * the critical section is established.
   */

  saved_state = irqsave();
  if (wdog->active)
    {
      wd_cancel(wdog);
    }

  /* Save the data in the watchdog structure */

  wdog->func = wdentry;         /* Function to execute when delay expires */
  up_getpicbase(&wdog->picbase);
  wdog->argc = argc;

  va_start(ap, argc);
  for (i = 0; i < argc; i++)
    {
      wdog->parm[i] = va_arg(ap, uint32_t);
    }
#ifdef CONFIG_DEBUG
  for (; i < CONFIG_MAX_WDOGPARMS; i++)
    {
      wdog->parm[i] = 0;
    }
#endif
  va_end(ap);

  /* Calculate delay+1, forcing the delay into a range that we can handle */

  if (delay <= 0)
    {
      delay = 1;
    }
  else if (++delay <= 0)
    {
      delay--;
    }

  /* Do the easy case first -- when the watchdog timer queue is empty. */

  if (g_wdactivelist.head == NULL)
    {
      sq_addlast((FAR sq_entry_t*)wdog,&g_wdactivelist);
    }

  /* There are other active watchdogs in the timer queue */

  else
    {
      now = 0;
      prev = curr = (FAR wdog_t*)g_wdactivelist.head;

      /* Advance to positive time */

      while ((now += curr->lag) < 0 && curr->next)
        {
          prev = curr;
          curr = curr->next;
        }

      /* Advance past shorter delays */

      while (now <= delay && curr->next)
       {
         prev = curr;
         curr = curr->next;
         now += curr->lag;
       }

      /* Check if the new wdog must be inserted before the curr. */

      if (delay < now)
        {
          /* The relative delay time is smaller or equal to the current delay
           * time, so decrement the current delay time by the new relative
           * delay time.
           */

          delay -= (now - curr->lag);
          curr->lag -= delay;

          /* Insert the new watchdog in the list */

          if (curr == (FAR wdog_t*)g_wdactivelist.head)
            {
              sq_addfirst((FAR sq_entry_t*)wdog, &g_wdactivelist);
            }
          else
            {
              sq_addafter((FAR sq_entry_t*)prev, (FAR sq_entry_t*)wdog,
                          &g_wdactivelist);
            }
        }

      /* The new watchdog delay time is greater than the curr delay time,
       * so the new wdog must be inserted after the curr. This only occurs
       * if the wdog is to be added to the end of the list.
       */

      else
        {
          delay -= now;
          if (!curr->next)
            {
              sq_addlast((FAR sq_entry_t*)wdog, &g_wdactivelist);
            }
          else
            {
              next = curr->next;
              next->lag -= delay;
              sq_addafter((FAR sq_entry_t*)curr, (FAR sq_entry_t*)wdog,
                          &g_wdactivelist);
            }
        }
    }

  /* Put the lag into the watchdog structure and mark it as active. */

  wdog->lag = delay;
  wdog->active = true;

  irqrestore(saved_state);
  return OK;
}
Exemple #12
0
/* Perform a sequence of I2C transfers */
static int up_i2c_transfer(struct i2c_dev_s *idev, struct i2c_msg_s *msgs, int num)
{
    int ret;

    i2cvdbg("msgs: %d\n", num);

    sem_wait(&g_mutex);

    g_msgs = msgs;
    g_msgs_count = num;
    g_tx_index = 0;
    g_rx_index = 0;
    g_rx_outstanding = 0;

    g_cmd_err = 0;
    g_msg_err = 0;
    g_status = TSB_I2C_STATUS_IDLE;
    g_abort_source = 0;

    ret = tsb_i2c_wait_bus_ready();
    if (ret < 0)
        goto done;

    /*
     * start a watchdog to timeout the transfer if
     * the bus is locked up...
     */
    wd_start(g_timeout, TSB_I2C_TIMEOUT, i2c_timeout, 1, 0);

    /* start the transfers */
    tsb_i2c_start_transfer();

    sem_wait(&g_wait);

    wd_cancel(g_timeout);

    if (g_status == TSB_I2C_STATUS_TIMEOUT) {
        i2cdbg("controller timed out\n");

        /* Re-init the adapter */
        tsb_i2c_init();
        ret = -ETIMEDOUT;
        goto done;
    }

    tsb_i2c_disable();

    if (g_msg_err) {
        ret = g_msg_err;
        i2cdbg("error msg_err %x\n", g_msg_err);
        goto done;
    }

    if (!g_cmd_err) {
        ret = 0;
        i2cvdbg("no error %d\n", num);
        goto done;
    }

    /* Handle abort errors */
    if (g_cmd_err == TSB_I2C_ERR_TX_ABRT) {
        ret = tsb_i2c_handle_tx_abort();
        goto done;
    }

    /* default error code */
    ret = -EIO;
    i2cdbg("unknown error %x\n", ret);

done:
    sem_post(&g_mutex);

    return ret;
}
Exemple #13
0
/************************************************************************************
 * Name: taskmgr_ioctl
 *
 * Description:  The ioctl method for task management.
 *
 ************************************************************************************/
static int taskmgr_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
{
	int ret = -EINVAL;
	struct tcb_s *tcb;

	tmvdbg("cmd: %d arg: %ld\n", cmd, arg);

	/* Handle built-in ioctl commands */

	switch (cmd) {
	case TMIOC_START:
		ret = taskmgr_task_init((int)arg);
		if (ret != OK) {
			tmdbg("Fail to init new task\n");
		}
		break;
	case TMIOC_PAUSE:
		tcb = sched_gettcb((int)arg);
		if (tcb == NULL) {
			tmdbg("Invalid pid\n");
			return ERROR;
		}
		if (tcb->task_state == TSTATE_WAIT_SIG && tcb->waitdog != NULL) {
			/* tcb is waiting another signal, e.g. sleep */
			wd_cancel(tcb->waitdog);
		} else if (tcb->task_state == TSTATE_WAIT_SEM) {
			tcb->waitsem = NULL;
			sched_removeblocked(tcb);
			sched_addblocked(tcb, TSTATE_WAIT_SIG);
		}
		ret = OK;
		break;
	case TMIOC_UNICAST:
		tcb = sched_gettcb((int)arg);
		if (tcb == NULL) {
			tmdbg("Invalid pid\n");
			return ERROR;
		}
		ret = (int)sig_is_handler_registered(tcb, SIGTM_UNICAST);
		if ((bool)ret != true) {
			tmdbg("handler is not registered for unicast\n");
			ret = ERROR;
		} else {
			ret = OK;
		}
		break;
	case TMIOC_RESTART:
		break;
	case TMIOC_BROADCAST:
		tcb = sched_gettcb((int)arg);
		if (tcb == NULL) {
			tmdbg("Invalid pid\n");
			return ERROR;
		}
		ret = (int)sig_is_handler_registered(tcb, SIGTM_BROADCAST);
		if ((bool)ret != true) {
			tmdbg("handler is not registered for broadcast\n");
			ret = ERROR;
		} else {
			ret = OK;
		}
		break;
	case TMIOC_CHECK_ALIVE:
		tcb = sched_gettcb((int)arg);
		if (tcb == NULL) {
			tmdbg("Invalid pid\n");
			return ERROR;
		}
		ret = OK;
		break;
	default:
		tmdbg("Unrecognized cmd: %d arg: %ld\n", cmd, arg);
		break;
	}
	return ret;
}
Exemple #14
0
int mq_timedsend(mqd_t mqdes, const char *msg, size_t msglen, int prio,
                 const struct timespec *abstime)
{
  WDOG_ID      wdog;
  FAR msgq_t  *msgq;
  FAR mqmsg_t *mqmsg = NULL;
  irqstate_t   saved_state;
  int          ret = ERROR;

  DEBUGASSERT(up_interrupt_context() == false);

  /* Verify the input parameters -- setting errno appropriately
   * on any failures to verify.
   */

  if (mq_verifysend(mqdes, msg, msglen, prio) != OK)
    {
      return ERROR;
    }

  if (!abstime || abstime->tv_sec < 0 || abstime->tv_nsec > 1000000000)
    {
      set_errno(EINVAL);
      return ERROR;
    }

  /* Get a pointer to the message queue */

  msgq = mqdes->msgq;

  /* Create a watchdog.  We will not actually need this watchdog
   * unless the queue is full, but we will reserve it up front
   * before we enter the following critical section.
   */

  wdog = wd_create();
  if (!wdog)
    {
      set_errno(EINVAL);
      return ERROR;
    }

  /* Allocate a message structure:
   * - If we are called from an interrupt handler, or
   * - If the message queue is not full, or
   */

  sched_lock();
  saved_state = irqsave();
  if (up_interrupt_context()      || /* In an interrupt handler */
      msgq->nmsgs < msgq->maxmsgs)   /* OR Message queue not full */
    {
      /* Allocate the message */

      irqrestore(saved_state);
      mqmsg = mq_msgalloc();
    }
  else
    {
      int ticks;

      /* We are not in an interupt handler and the message queue is full.
       * set up a timed wait for the message queue to become non-full.
       *
       * Convert the timespec to clock ticks.  We must have interrupts
       * disabled here so that this time stays valid until the wait begins.
       */

      int result = clock_abstime2ticks(CLOCK_REALTIME, abstime, &ticks);

      /* If the time has already expired and the message queue is empty,
       * return immediately.
       */

      if (result == OK && ticks <= 0)
        {
          result = ETIMEDOUT;
        }

      /* Handle any time-related errors */

      if (result != OK)
        {
          set_errno(result);
          ret = ERROR;
        }

      /* Start the watchdog and begin the wait for MQ not full */

      if (result == OK)
        {
          /* Start the watchdog */

          wd_start(wdog, ticks, (wdentry_t)mq_sndtimeout, 1, getpid());

          /* And wait for the message queue to be non-empty */

          ret = mq_waitsend(mqdes);

          /* This may return with an error and errno set to either EINTR
           * or ETIMEOUT.  Cancel the watchdog timer in any event.
           */

          wd_cancel(wdog);
        }

      /* That is the end of the atomic operations */

      irqrestore(saved_state);

      /* If any of the above failed, set the errno.  Otherwise, there should
       * be space for another message in the message queue.  NOW we can allocate
       * the message structure.
       */

      if (ret == OK)
        {
          mqmsg = mq_msgalloc();
        }
    }

  /* Check if we were able to get a message structure -- this can fail
   * either because we cannot send the message (and didn't bother trying
   * to allocate it) or because the allocation failed.
   */

  if (mqmsg)
    {
      /* Yes, peform the message send. */

      ret = mq_dosend(mqdes, mqmsg, msg, msglen, prio);
    }

  sched_unlock();
  wd_delete(wdog);
  return ret;
}
Exemple #15
0
static void max11802_worker(FAR void *arg)
{
  FAR struct max11802_dev_s    *priv = (FAR struct max11802_dev_s *)arg;
  FAR struct max11802_config_s *config;
  uint16_t                      x;
  uint16_t                      y;
  uint16_t                      xdiff;
  uint16_t                      ydiff;
  bool                          pendown;
  int                           ret;
  int                           tags, tags2;

  ASSERT(priv != NULL);

  /* Get a pointer the callbacks for convenience (and so the code is not so
   * ugly).
   */

  config = priv->config;
  DEBUGASSERT(config != NULL);

  /* Disable the watchdog timer.  This is safe because it is started only
   * by this function and this function is serialized on the worker thread.
   */

  wd_cancel(priv->wdog);

  /* Lock the SPI bus so that we have exclusive access */

  max11802_lock(priv->spi);

  /* Start coordinate measurement */

  (void)max11802_sendcmd(priv, MAX11802_CMD_MEASUREXY, &tags);

  /* Get exclusive access to the driver data structure */

  do
    {
      ret = sem_wait(&priv->devsem);

      /* This should only fail if the wait was cancelled by an signal
       * (and the worker thread will receive a lot of signals).
       */

      DEBUGASSERT(ret == OK || errno == EINTR);
    }
  while (ret < 0);

  /* Check for pen up or down by reading the PENIRQ GPIO. */

  pendown = config->pendown(config);

  /* Handle the change from pen down to pen up */

  if (pendown)
    {
      iinfo("\nPD\n");
    }
  else
    {
      iinfo("\nPU\n");
    }

  if (!pendown)
    {
      /* The pen is up.. reset thresholding variables. */

      priv->threshx = INVALID_THRESHOLD;
      priv->threshy = INVALID_THRESHOLD;

      /* Ignore the interrupt if the pen was already up (CONTACT_NONE ==
       * pen up and already reported; CONTACT_UP == pen up, but not
       * reported).
       */

      iinfo("\nPC%d\n", priv->sample.contact);

      if (priv->sample.contact == CONTACT_NONE ||
          priv->sample.contact == CONTACT_UP)

        {
          goto ignored;
        }

      /* The pen is up.  NOTE: We know from a previous test, that this is a
       * loss of contact condition.  This will be changed to CONTACT_NONE
       * after the loss of contact is sampled.
       */

       priv->sample.contact = CONTACT_UP;
    }

  /* It is a pen down event.  If the last loss-of-contact event has not been
   * processed yet, then we have to ignore the pen down event (or else it
   * will look like a drag event)
   */

  else if (priv->sample.contact == CONTACT_UP)
    {
      /* If we have not yet processed the last pen up event, then we
       * cannot handle this pen down event. We will have to discard it.
       * That should be okay because we will set the timer to to sample
       * again later.
       */

       iinfo("Previous pen up event still in buffer\n");
       max11802_notify(priv);
       wd_start(priv->wdog, MAX11802_WDOG_DELAY, max11802_wdog, 1,
                (uint32_t)priv);
       goto ignored;
    }
  else
    {
      /* Wait for data ready
       *
       * Note: MAX11802 signals the readiness of the results using
       * the lowest 4 bits of the result. However these are the
       * last bits to be read out of the device. It appears that
       * the hardware value can change in the middle of the readout,
       * causing the upper bits to be still invalid even though lower
       * bits indicate valid result.
       *
       * We work around this by reading the registers once more after
       * the tags indicate they are ready.
       */

      int readycount = 0;
      do
        {
#ifdef CONFIG_MAX11802_SWAPXY
          x = max11802_sendcmd(priv, MAX11802_CMD_YPOSITION, &tags);
          y = max11802_sendcmd(priv, MAX11802_CMD_XPOSITION, &tags2);
#else
          x = max11802_sendcmd(priv, MAX11802_CMD_XPOSITION, &tags);
          y = max11802_sendcmd(priv, MAX11802_CMD_YPOSITION, &tags2);
#endif
          if (tags != 0xF && tags2 != 0xF)
            {
              readycount++;
            }
        }
      while (readycount < 2);

      /* Continue to sample the position while the pen is down */

      wd_start(priv->wdog, MAX11802_WDOG_DELAY, max11802_wdog, 1,
               (uint32_t)priv);

      /* Check if data is valid */

      if ((tags & 0x03) != 0)
        {
          iinfo("Touch ended before measurement\n");
          goto ignored;
        }

      /* Perform a thresholding operation so that the results will be more
       * stable.  If the difference from the last sample is small, then
       * ignore the event.
       *
       * REVISIT:  Should a large change in pressure also generate a event?
       */

      xdiff = x > priv->threshx ? (x - priv->threshx) : (priv->threshx - x);
      ydiff = y > priv->threshy ? (y - priv->threshy) : (priv->threshy - y);

      /* Check the thresholds.  Bail if there is no significant difference */

      if (xdiff < CONFIG_MAX11802_THRESHX && ydiff < CONFIG_MAX11802_THRESHY)
        {
          /* Little or no change in either direction ... don't report anything. */

          goto ignored;
        }

      /* When we see a big difference, snap to the new x/y thresholds */

      priv->threshx       = x;
      priv->threshy       = y;

      /* Update the x/y position in the sample data */

      priv->sample.x      = priv->threshx;
      priv->sample.y      = priv->threshy;

      /* The X/Y positional data is now valid */

      priv->sample.valid = true;

      /* If this is the first (acknowledged) pen down report, then report
       * this as the first contact.  If contact == CONTACT_DOWN, it will be
       * set to set to CONTACT_MOVE after the contact is first sampled.
       */

      if (priv->sample.contact != CONTACT_MOVE)
        {
          /* First contact */

          priv->sample.contact = CONTACT_DOWN;
        }
    }

  /* Indicate the availability of new sample data for this ID */

  priv->sample.id = priv->id;
  priv->penchange = true;

  /* Notify any waiters that new MAX11802 data is available */

  max11802_notify(priv);

ignored:
  config->enable(config, true);

  /* Release our lock on the state structure and unlock the SPI bus */

  sem_post(&priv->devsem);
  max11802_unlock(priv->spi);
}
Exemple #16
0
static void ads7843e_worker(FAR void *arg)
{
  FAR struct ads7843e_dev_s    *priv = (FAR struct ads7843e_dev_s *)arg;
  FAR struct ads7843e_config_s *config;
  uint16_t                      x;
  uint16_t                      y;
  uint16_t                      xdiff;
  uint16_t                      ydiff;
  bool                          pendown;
  int                           ret;

  ASSERT(priv != NULL);

  /* Get a pointer the callbacks for convenience (and so the code is not so
   * ugly).
   */

  config = priv->config;
  DEBUGASSERT(config != NULL);

  /* Disable the watchdog timer.  This is safe because it is started only
   * by this function and this function is serialized on the worker thread.
   */

  wd_cancel(priv->wdog);

  /* Lock the SPI bus so that we have exclusive access */

  ads7843e_lock(priv->spi);

  /* Get exclusive access to the driver data structure */

  do
    {
      ret = sem_wait(&priv->devsem);

      /* This should only fail if the wait was canceled by an signal
       * (and the worker thread will receive a lot of signals).
       */

      DEBUGASSERT(ret == OK || errno == EINTR);
    }
  while (ret < 0);

  /* Check for pen up or down by reading the PENIRQ GPIO. */

  pendown = config->pendown(config);

  /* Handle the change from pen down to pen up */

  if (!pendown)
    {
      /* The pen is up.. reset thresholding variables. */

      priv->threshx = INVALID_THRESHOLD;
      priv->threshy = INVALID_THRESHOLD;

      /* Ignore the interrupt if the pen was already up (CONTACT_NONE == pen up
       * and already reported; CONTACT_UP == pen up, but not reported)
       */

      if (priv->sample.contact == CONTACT_NONE ||
          priv->sample.contact == CONTACT_UP)

        {
          goto ignored;
        }

      /* The pen is up.  NOTE: We know from a previous test, that this is a
       * loss of contact condition.  This will be changed to CONTACT_NONE
       * after the loss of contact is sampled.
       */

       priv->sample.contact = CONTACT_UP;
    }

  /* It is a pen down event.  If the last loss-of-contact event has not been
   * processed yet, then we have to ignore the pen down event (or else it will
   * look like a drag event)
   */

  else if (priv->sample.contact == CONTACT_UP)
    {
      /* If we have not yet processed the last pen up event, then we
       * cannot handle this pen down event. We will have to discard it.  That
       * should be okay because we will set the timer to to sample again
       * later.
       */

       wd_start(priv->wdog, ADS7843E_WDOG_DELAY, ads7843e_wdog, 1, (uint32_t)priv);
       goto ignored;
    }
  else
    {
      /* Handle pen down events.  First, sample positional values. NOTE:
       * that these commands have the side-effect of disabling the PENIRQ.
       */

#ifdef CONFIG_ADS7843E_SWAPXY
      x = ads7843e_sendcmd(priv, ADS7843_CMD_YPOSITION);
      y = ads7843e_sendcmd(priv, ADS7843_CMD_XPOSITION);
#else
      x = ads7843e_sendcmd(priv, ADS7843_CMD_XPOSITION);
      y = ads7843e_sendcmd(priv, ADS7843_CMD_YPOSITION);
#endif

      /* Perform a thresholding operation so that the results will be more stable.
       * If the difference from the last sample is small, then ignore the event.
       * REVISIT:  Should a large change in pressure also generate a event?
       */

      xdiff = x > priv->threshx ? (x - priv->threshx) : (priv->threshx - x);
      ydiff = y > priv->threshy ? (y - priv->threshy) : (priv->threshy - y);

      /* Continue to sample the position while the pen is down */

      wd_start(priv->wdog, ADS7843E_WDOG_DELAY, ads7843e_wdog, 1, (uint32_t)priv);

      /* Check the thresholds.  Bail if there is no significant difference */

      if (xdiff < CONFIG_ADS7843E_THRESHX && ydiff < CONFIG_ADS7843E_THRESHY)
        {
          /* Little or no change in either direction ... don't report anything. */

          goto ignored;
        }

      /* When we see a big difference, snap to the new x/y thresholds */

      priv->threshx       = x;
      priv->threshy       = y;

      /* Update the x/y position in the sample data */

      priv->sample.x      = priv->threshx;
      priv->sample.y      = priv->threshy;

      /* The X/Y positional data is now valid */

      priv->sample.valid = true;

      /* If this is the first (acknowledged) pen down report, then report
       * this as the first contact.  If contact == CONTACT_DOWN, it will be
       * set to set to CONTACT_MOVE after the contact is first sampled.
       */

      if (priv->sample.contact != CONTACT_MOVE)
        {
          /* First contact */

          priv->sample.contact = CONTACT_DOWN;
        }
    }

  /* Indicate the availability of new sample data for this ID */

  priv->sample.id = priv->id;
  priv->penchange = true;

  /* Notify any waiters that new ADS7843E data is available */

  ads7843e_notify(priv);

  /* Exit, re-enabling ADS7843E interrupts */

ignored:

  /* Re-enable the PENIRQ interrupt at the ADS7843E */

  (void)ads7843e_sendcmd(priv, ADS7843_CMD_ENABPENIRQ);

  /* Re-enable the PENIRQ interrupt at the MCU's interrupt controller */

  config->enable(config, true);

  /* Release our lock on the state structure and unlock the SPI bus */

  sem_post(&priv->devsem);
  ads7843e_unlock(priv->spi);
}
int sem_tickwait(FAR sem_t *sem, systime_t start, uint32_t delay)
{
  FAR struct tcb_s *rtcb =  (FAR struct tcb_s *)g_readytorun.head;
  irqstate_t flags;
  systime_t elapsed;
  int ret;

  DEBUGASSERT(sem != NULL && up_interrupt_context() == false &&
              rtcb->waitdog == NULL);

  /* Create a watchdog.  We will not actually need this watchdog
   * unless the semaphore is unavailable, but we will reserve it up
   * front before we enter the following critical section.
   */

  rtcb->waitdog = wd_create();
  if (!rtcb->waitdog)
    {
      return -ENOMEM;
    }

  /* We will disable interrupts until we have completed the semaphore
   * wait.  We need to do this (as opposed to just disabling pre-emption)
   * because there could be interrupt handlers that are asynchronously
   * posting semaphores and to prevent race conditions with watchdog
   * timeout.  This is not too bad because interrupts will be re-
   * enabled while we are blocked waiting for the semaphore.
   */

  flags = enter_critical_section();

  /* Try to take the semaphore without waiting. */

  ret = sem_trywait(sem);
  if (ret == OK)
    {
      /* We got it! */

      goto success_with_irqdisabled;
    }

  /* We will have to wait for the semaphore.  Make sure that we were provided
   * with a valid timeout.
   */

  if (delay == 0)
    {
      /* Return the errno from sem_trywait() */

      ret = -get_errno();
      goto errout_with_irqdisabled;
    }

  /* Adjust the delay for any time since the delay was calculated */

  elapsed = clock_systimer() - start;
  if (/* elapsed >= (UINT32_MAX / 2) || */ elapsed >= delay)
    {
      ret = -ETIMEDOUT;
      goto errout_with_irqdisabled;
    }

  delay -= elapsed;

  /* Start the watchdog with interrupts still disabled */

  (void)wd_start(rtcb->waitdog, delay, (wdentry_t)sem_timeout, 1, getpid());

  /* Now perform the blocking wait */

  ret = sem_wait(sem);
  if (ret < 0)
    {
      /* Return the errno from sem_wait() */

      ret = -get_errno();
      goto errout_with_irqdisabled;
    }

  /* Stop the watchdog timer */

  wd_cancel(rtcb->waitdog);

  /* We can now restore interrupts and delete the watchdog */

  /* Success exits */

success_with_irqdisabled:

  /* Error exits */

errout_with_irqdisabled:
  leave_critical_section(flags);
  wd_delete(rtcb->waitdog);
  rtcb->waitdog = NULL;
  return ret;
}
Exemple #18
0
void stmpe811_tscworker(FAR struct stmpe811_dev_s *priv, uint8_t intsta)
{
  uint16_t xdiff;    /* X difference used in thresholding */
  uint16_t ydiff;    /* Y difference used in thresholding */
  uint16_t x;        /* X position */
  uint16_t y;        /* Y position */
  bool     pendown;  /* true: pen is down */

  ASSERT(priv != NULL);

  /* Cancel the missing pen up timer */

  (void)wd_cancel(priv->wdog);

  /* Check for pen up or down from the TSC_STA ibit n the STMPE811_TSC_CTRL register. */

  pendown = (stmpe811_getreg8(priv, STMPE811_TSC_CTRL) & TSC_CTRL_TSC_STA) != 0;

  /* Handle the change from pen down to pen up */

  if (!pendown)
    {
      /* The pen is up.. reset thresholding variables.  FIFOs will read zero if
       * there is no data available (hence the choice of (0,0))
       */

      priv->threshx = 0;
      priv->threshy = 0;

      /* Ignore the interrupt if the pen was already up (CONTACT_NONE == pen up and
       * already reported; CONTACT_UP == pen up, but not reported)
       */

      if (priv->sample.contact == CONTACT_NONE ||
          priv->sample.contact == CONTACT_UP)
        {
          goto ignored;
        }

      /* A pen-down to up transition has been detected.  CONTACT_UP indicates the
       * initial loss of contact.  The state will be changed to CONTACT_NONE
       * after the loss of contact is sampled.
       */

       priv->sample.contact = CONTACT_UP;
    }

  /* The pen is down... check for data in the FIFO */

  else if ((intsta & (INT_FIFO_TH | INT_FIFO_OFLOW)) != 0)
    {
      /* Read the next x and y positions from the FIFO. */

#ifdef CONFIG_STMPE811_SWAPXY
      x = stmpe811_getreg16(priv, STMPE811_TSC_DATAX);
      y = stmpe811_getreg16(priv, STMPE811_TSC_DATAY);
#else
      x = stmpe811_getreg16(priv, STMPE811_TSC_DATAY);
      y = stmpe811_getreg16(priv, STMPE811_TSC_DATAX);
#endif

      /* If we have not yet processed the last pen up event, then we
       * cannot handle this pen down event. We will have to discard it.  That
       * should be okay because there will be another FIFO event right behind
       * this one.  Other kinds of data overruns are not harmful.
       *
       * Hmm.. a better design might be to disable FIFO interrupts when we
       * detect pen up.  Then re-enable them when CONTACT_UP is reported.
       * That would save processing interrupts just to discard the data.
       */

      if (priv->sample.contact == CONTACT_UP)
        {
          /* We have not closed the loop on the last touch ... don't report
           * anything.
           */

          goto ignored;
        }

      /* Perform a thresholding operation so that the results will be more stable.
       * If the difference from the last sample is small, then ignore the event.
       * REVISIT:  Should a large change in pressure also generate a event?
       */

      xdiff = x > priv->threshx ? (x - priv->threshx) : (priv->threshx - x);
      ydiff = y > priv->threshy ? (y - priv->threshy) : (priv->threshy - y);

      if (xdiff < CONFIG_STMPE811_THRESHX && ydiff < CONFIG_STMPE811_THRESHY)
        {
          /* Little or no change in either direction ... don't report anything. */

          goto ignored;
        }

      /* When we see a big difference, snap to the new x/y thresholds */

      priv->threshx       = x;
      priv->threshy       = y;

      /* Update the x/y position in the sample data */

      priv->sample.x      = priv->threshx;
      priv->sample.y      = priv->threshy;

      /* Update the Z pressure index */

      priv->sample.z      = stmpe811_getreg8(priv, STMPE811_TSC_DATAZ);
      priv->sample.valid  = true;

      /* If this is the first (acknowledged) pen down report, then report
       * this as the first contact.  If contact == CONTACT_DOWN, it will be
       * set to set to CONTACT_MOVE after the contact is first sampled.
       */

      if (priv->sample.contact != CONTACT_MOVE)
        {
          /* First contact */

          priv->sample.contact = CONTACT_DOWN;
        }
    }

  /* Pen down, but no data in FIFO */

  else
    {
      /* Ignore the interrupt... wait until there is data in the FIFO */

      goto ignored;
    }

  /* We get here if (1) we just went from a pen down to a pen up state OR (2)
   * We just get a measurement from the FIFO in a pen down state.  Indicate
   * the availability of new sample data for this ID.
   */

  priv->sample.id = priv->id;
  priv->penchange = true;

  /* Notify any waiters that new STMPE811 data is available */

  stmpe811_notify(priv);

  /* If we think that the pen is still down, the start/re-start the pen up
   * timer.
   */

ignored:
  if (priv->sample.contact == CONTACT_DOWN ||
      priv->sample.contact == CONTACT_MOVE)
    {
      (void)wd_start(priv->wdog, STMPE811_PENUP_TICKS, stmpe811_timeout,
                     1, (uint32_t)((uintptr_t)priv));
    }

  /*  Reset and clear all data in the FIFO */

  stmpe811_putreg8(priv, STMPE811_FIFO_STA, FIFO_STA_FIFO_RESET);
  stmpe811_putreg8(priv, STMPE811_FIFO_STA, 0);
}
Exemple #19
0
int sem_timedwait(FAR sem_t *sem, FAR const struct timespec *abstime)
{
  FAR struct tcb_s *rtcb = (FAR struct tcb_s *)g_readytorun.head;
  irqstate_t flags;
  int        ticks;
  int        err;
  int        ret = ERROR;

  DEBUGASSERT(up_interrupt_context() == false && rtcb->waitdog == NULL);

  /* Verify the input parameters and, in case of an error, set
   * errno appropriately.
   */

#ifdef CONFIG_DEBUG
  if (!abstime || !sem)
    {
      err = EINVAL;
      goto errout;
    }
#endif

  /* Create a watchdog.  We will not actually need this watchdog
   * unless the the semaphore is unavailable, but we will reserve it up
   * front before we enter the following critical section.
   */

  rtcb->waitdog = wd_create();
  if (!rtcb->waitdog)
    {
      err = ENOMEM;
      goto errout;
    }

  /* We will disable interrupts until we have completed the semaphore
   * wait.  We need to do this (as opposed to just disabling pre-emption)
   * because there could be interrupt handlers that are asynchronoulsy
   * posting semaphores and to prevent race conditions with watchdog
   * timeout.  This is not too bad because interrupts will be re-
   * enabled while we are blocked waiting for the semaphore.
   */

  flags = irqsave();

  /* Try to take the semaphore without waiting. */

  ret = sem_trywait(sem);
  if (ret == 0)
    {
      /* We got it! */

      irqrestore(flags);
      wd_delete(rtcb->waitdog);
      rtcb->waitdog = NULL;
      return OK;
    }

  /* We will have to wait for the semphore.  Make sure that we were provided
   * with a valid timeout.
   */

  if (abstime->tv_sec < 0 || abstime->tv_nsec > 1000000000)
    {
      err = EINVAL;
      goto errout_disabled;
    }

  /* Convert the timespec to clock ticks.  We must have interrupts
   * disabled here so that this time stays valid until the wait begins.
   */

  err = clock_abstime2ticks(CLOCK_REALTIME, abstime, &ticks);

  /* If the time has already expired return immediately. */

  if (err == OK && ticks <= 0)
    {
      err = ETIMEDOUT;
      goto errout_disabled;
    }

  /* Handle any time-related errors */

  if (err != OK)
    {
      goto errout_disabled;
    }

  /* Start the watchdog */

  err = OK; 
  wd_start(rtcb->waitdog, ticks, (wdentry_t)sem_timeout, 1, getpid());

  /* Now perform the blocking wait */

  ret = sem_wait(sem);

  /* Stop the watchdog timer */

  wd_cancel(rtcb->waitdog);

  /* We can now restore interrupts and delete the watchdog */

  irqrestore(flags);
  wd_delete(rtcb->waitdog);
  rtcb->waitdog = NULL;

  /* We are either returning success or an error detected by sem_wait()
   * or the timeout detected by sem_timeout().  The 'errno' value has
   * been set appropriately by sem_wait() or sem_timeout() in those
   * cases.
   */

  return ret;

/* Error exits */

errout_disabled:
  irqrestore(flags);
  wd_delete(rtcb->waitdog);
  rtcb->waitdog = NULL;

errout:
  set_errno(err);
  return ERROR;
}
Exemple #20
0
int timer_settime(timer_t timerid, int flags, FAR const struct itimerspec *value,
                  FAR struct itimerspec *ovalue)
{
  FAR struct posix_timer_s *timer = (FAR struct posix_timer_s *)timerid;
  irqstate_t state;
  int delay;
  int ret = OK;

  /* Some sanity checks */

  if (!timer || !value)
    {
      errno = EINVAL;
      return ERROR;
    }

  /* Disarm the timer (in case the timer was already armed when timer_settime()
   * is called).
   */

  (void)wd_cancel(timer->pt_wdog);

  /* If the it_value member of value is zero, the timer will not be re-armed */

  if (value->it_value.tv_sec <= 0 && value->it_value.tv_nsec <= 0)
    {
      return OK;
    }

  /* Setup up any repititive timer */

  if (value->it_interval.tv_sec > 0 || value->it_interval.tv_nsec > 0)
    {
       (void)clock_time2ticks(&value->it_interval, &timer->pt_delay);
    }
  else
    {
       timer->pt_delay = 0;
    }

  /* We need to disable timer interrupts through the following section so
   * that the system timer is stable.
   */

  state = irqsave();

  /* Check if abstime is selected */

  if ((flags & TIMER_ABSTIME) != 0)
    {
#ifdef CONFIG_DISABLE_CLOCK
      /* Absolute timing depends upon having access to clock functionality */

      errno = ENOSYS;
      return ERROR;
#else
      /* Calculate a delay corresponding to the absolute time in 'value'.
       * NOTE:  We have internal knowledge the clock_abstime2ticks only
       * returns an error if clockid != CLOCK_REALTIME.
       */

      (void)clock_abstime2ticks(CLOCK_REALTIME, &value->it_value, &delay);
#endif
    }
  else
    {
      /* Calculate a delay assuming that 'value' holds the relative time
       * to wait.  We have internal knowledge that clock_time2ticks always
       * returns success.
       */

      (void)clock_time2ticks(&value->it_value, &delay);
    }

  /* If the time is in the past or now, then set up the next interval
   * instead (assuming a repititive timer).
   */

  if (delay <= 0)
    {
      delay = timer->pt_delay;
    }

  /* Then start the watchdog */


  if (delay > 0)
    {
      timer->pt_last = delay;
      ret = wd_start(timer->pt_wdog, delay, (wdentry_t)timer_timeout,
                     1, (uint32_t)((uintptr_t)timer));
    }

  irqrestore(state);
  return ret;
}
Exemple #21
0
int mq_timedsend(mqd_t mqdes, FAR const char *msg, size_t msglen, int prio,
                 FAR const struct timespec *abstime)
{
  FAR struct tcb_s *rtcb = this_task();
  FAR struct mqueue_inode_s *msgq;
  FAR struct mqueue_msg_s *mqmsg = NULL;
  irqstate_t saved_state;
  int ticks;
  int result;
  int ret = ERROR;

  DEBUGASSERT(up_interrupt_context() == false && rtcb->waitdog == NULL);

  /* Verify the input parameters -- setting errno appropriately
   * on any failures to verify.
   */

  if (mq_verifysend(mqdes, msg, msglen, prio) != OK)
    {
      /* mq_verifysend() will set the errno appropriately */

      return ERROR;
    }

  /* Pre-allocate a message structure */

  mqmsg = mq_msgalloc();
  if (!mqmsg)
    {
      /* Failed to allocate the message */

      set_errno(ENOMEM);
      return ERROR;
    }

  /* Get a pointer to the message queue */

  sched_lock();
  msgq = mqdes->msgq;

  /* OpenGroup.org: "Under no circumstance shall the operation fail with a
   * timeout if there is sufficient room in the queue to add the message
   * immediately. The validity of the abstime parameter need not be checked
   * when there is sufficient room in the queue."
   *
   * Also ignore the time value if for some crazy reason we were called from
   * an interrupt handler.  This probably really should be an assertion.
   *
   * NOTE: There is a race condition here: What if a message is added by
   * interrupt related logic so that queue again becomes non-empty.  That
   * is handled because mq_dosend() will permit the maxmsgs limit to be
   * exceeded in that case.
   */

  if (msgq->nmsgs < msgq->maxmsgs || up_interrupt_context())
    {
      /* Do the send with no further checks (possibly exceeding maxmsgs)
       * Currently mq_dosend() always returns OK.
       */

      ret = mq_dosend(mqdes, mqmsg, msg, msglen, prio);
      sched_unlock();
      return ret;
    }

  /* The message queue is full... We are going to wait.  Now we must have a
   * valid time value.
   */

  if (!abstime || abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000)
    {
      result = EINVAL;
      goto errout_with_mqmsg;
    }

  /* Create a watchdog.  We will not actually need this watchdog
   * unless the queue is full, but we will reserve it up front
   * before we enter the following critical section.
   */

  rtcb->waitdog = wd_create();
  if (!rtcb->waitdog)
    {
      result = EINVAL;
      goto errout_with_mqmsg;
    }

  /* We are not in an interrupt handler and the message queue is full.
   * Set up a timed wait for the message queue to become non-full.
   *
   * Convert the timespec to clock ticks.  We must have interrupts
   * disabled here so that this time stays valid until the wait begins.
   */

  saved_state = irqsave();
  result = clock_abstime2ticks(CLOCK_REALTIME, abstime, &ticks);

  /* If the time has already expired and the message queue is empty,
   * return immediately.
   */

  if (result == OK && ticks <= 0)
    {
      result = ETIMEDOUT;
    }

  /* Handle any time-related errors */

  if (result != OK)
    {
      goto errout_with_irqsave;
    }

  /* Start the watchdog and begin the wait for MQ not full */

  wd_start(rtcb->waitdog, ticks, (wdentry_t)mq_sndtimeout, 1, getpid());

  /* And wait for the message queue to be non-empty */

  ret = mq_waitsend(mqdes);

  /* This may return with an error and errno set to either EINTR
   * or ETIMEOUT.  Cancel the watchdog timer in any event.
   */

  wd_cancel(rtcb->waitdog);

  /* Check if mq_waitsend() failed */

  if (ret < 0)
    {
      /* mq_waitsend() will set the errno, but the error exit will reset it */

      result = get_errno();
      goto errout_with_irqsave;
    }

  /* That is the end of the atomic operations */

  irqrestore(saved_state);

  /* If any of the above failed, set the errno.  Otherwise, there should
   * be space for another message in the message queue.  NOW we can allocate
   * the message structure.
   *
   * Currently mq_dosend() always returns OK.
   */

  ret = mq_dosend(mqdes, mqmsg, msg, msglen, prio);

  sched_unlock();
  wd_delete(rtcb->waitdog);
  rtcb->waitdog = NULL;
  return ret;

/* Exit here with (1) the scheduler locked, (2) a message allocated, (3) a
 * wdog allocated, and (4) interrupts disabled.  The error code is in
 * 'result'
 */

errout_with_irqsave:
  irqrestore(saved_state);
  wd_delete(rtcb->waitdog);
  rtcb->waitdog = NULL;

/* Exit here with (1) the scheduler locked and 2) a message allocated.  The
 * error code is in 'result'
 */

errout_with_mqmsg:
  mq_msgfree(mqmsg);
  sched_unlock();

  set_errno(result);
  return ERROR;
}