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); }
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); }
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); }
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; }
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); }
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; }
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; }
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; }
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; }
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; }
/* 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; }
/************************************************************************************ * 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; }
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; }
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); }
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; }
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); }
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; }
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; }
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; }