diff --git a/nuttx/ChangeLog b/nuttx/ChangeLog index a39dfad896..c19ec511a2 100644 --- a/nuttx/ChangeLog +++ b/nuttx/ChangeLog @@ -2833,5 +2833,20 @@ * stmpe11*: Fix a massive naming problem. All references to STMPE11 should be STMPE812. * arch/arm/src/stm32/stm32_otgfsdev.c: Need to enabled USB reset interrupt - (contricuted by Erik Van Der Zalm). + (contributed by Erik Van Der Zalm). + * sched/sleep.c: Fix the return value from sleep(). The correct behavior is + to return the number of unwaited seconds; the implementation was always + returning zero. + * sched/usleep.c and include/unistd.h: Was a void function, but should return + 0 on success. usleep() needs to check the return value from sigtimedwait(). + sigtimewait() returns the signal number that awakened it and an error (EAGAIN) + if the timeout expired (normal case). + * sched/sig_timedwait.c: Fix sigtimedwait() return value. On a timeout, it was + setting the 8-bit si_signo field to -1 and eded up reported successfully awakened + by signal 255! Now detects the timeout and errors -1 with errno == EGAIN. If + sigtimedwait() is awakened by an unblocked signal, but it is not one of the + signals in the waited-for set, it will return -1 with errno == EINTR. + * arch/arm/src/stm32_i2c.c: Fix STM32 F2 I2C. It is apparently bug-for-bug + compatible with the F4 and needs the same work-around for the missing BTF + signal that was needed for the F4. diff --git a/nuttx/arch/arm/src/stm32/stm32_i2c.c b/nuttx/arch/arm/src/stm32/stm32_i2c.c index 4f55a5709a..cbd6f6b14b 100644 --- a/nuttx/arch/arm/src/stm32/stm32_i2c.c +++ b/nuttx/arch/arm/src/stm32/stm32_i2c.c @@ -1125,11 +1125,11 @@ static int stm32_i2c_isr(struct stm32_i2c_priv_s *priv) } #endif - /* Was last byte received or sent? Hmmm... the F4 seems to differ from + /* Was last byte received or sent? Hmmm... the F2 and F4 seems to differ from * the F1 in that BTF is not set after data is received (only RXNE). */ -#ifdef CONFIG_STM32_STM32F40XX +#if defined(CONFIG_STM32_STM32F20XX) || defined(CONFIG_STM32_STM32F40XX) if (priv->dcnt <= 0 && (status & (I2C_SR1_BTF|I2C_SR1_RXNE)) != 0) #else if (priv->dcnt <= 0 && (status & I2C_SR1_BTF) != 0) diff --git a/nuttx/include/unistd.h b/nuttx/include/unistd.h index 84c80500db..9713c84d3d 100644 --- a/nuttx/include/unistd.h +++ b/nuttx/include/unistd.h @@ -132,7 +132,7 @@ EXTERN int optopt; /* unrecognized option character */ EXTERN pid_t getpid(void); EXTERN void _exit(int status) noreturn_function; EXTERN unsigned int sleep(unsigned int seconds); -EXTERN void usleep(useconds_t usec); +EXTERN int usleep(useconds_t usec); /* File descriptor operations */ diff --git a/nuttx/sched/sig_timedwait.c b/nuttx/sched/sig_timedwait.c index 2d9afaab11..c0b4d802ae 100644 --- a/nuttx/sched/sig_timedwait.c +++ b/nuttx/sched/sig_timedwait.c @@ -1,8 +1,8 @@ /**************************************************************************** * sched/sig_timedwait.c * - * Copyright (C) 2007-2009 Gregory Nutt. All rights reserved. - * Author: Gregory Nutt + * Copyright (C) 2007-2009, 2012 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -47,15 +47,24 @@ #include #include #include +#include + #include + #include "os_internal.h" #include "sig_internal.h" #include "clock_internal.h" /**************************************************************************** - * Definitions + * Pre-processor Definitions ****************************************************************************/ +/* This is a special value of si_signo that means that it was the timeout + * that awakened the wait... not the receipt of a signal. + */ + +#define SIG_WAIT_TIMEOUT 0xff + /**************************************************************************** * Private Type Declarations ****************************************************************************/ @@ -107,7 +116,7 @@ static void sig_timeout(int argc, uint32_t itcb) if (u.wtcb->task_state == TSTATE_WAIT_SIG) { - u.wtcb->sigunbinfo.si_signo = ERROR; + u.wtcb->sigunbinfo.si_signo = SIG_WAIT_TIMEOUT; u.wtcb->sigunbinfo.si_code = SI_TIMER; u.wtcb->sigunbinfo.si_value.sival_int = 0; up_unblock_task(u.wtcb); @@ -151,7 +160,11 @@ static void sig_timeout(int argc, uint32_t itcb) * * Return Value: * Signal number that cause the wait to be terminated, otherwise -1 (ERROR) - * is returned. + * is returned with errno set to either: + * + * EAGAIN - No signal specified by set was generated within the specified + * timeout period. + * EINTR - The wait was interrupted by an unblocked, caught signal. * * Assumptions: * @@ -270,23 +283,48 @@ int sigtimedwait(FAR const sigset_t *set, FAR struct siginfo *info, rtcb->sigwaitmask = NULL_SIGNAL_SET; - /* When we awaken, the cause will be in the TCB. Return the signal - * info to the caller if so requested + /* When we awaken, the cause will be in the TCB. Get the signal number + * or timeout) that awakened us. */ + if (GOOD_SIGNO(rtcb->sigunbinfo.si_signo)) + { + /* We were awakened by a signal... but is it one of the signals that + * we were waiting for? + */ + + if (sigismember(set, rtcb->sigunbinfo.si_signo)) + { + /* Yes.. the return value is the number of the signal that + * awakened us. + */ + + ret = rtcb->sigunbinfo.si_signo; + } + else + { + /* No... then set EINTR and report an error */ + + set_errno(EINTR); + ret = ERROR; + } + } + else + { + /* Otherwise, we must have been awakened by the timeout. Set EGAIN + * and return an error. + */ + + DEBUGASSERT(rtcb->sigunbinfo.si_signo == SIG_WAIT_TIMEOUT); + set_errno(EAGAIN); + ret = ERROR; + } + + /* Return the signal info to the caller if so requested */ + if (info) { memcpy(info, &rtcb->sigunbinfo, sizeof(struct siginfo)); - - /* The return value is the number of the signal that awakened us */ - - ret = info->si_signo; - } - else - { - /* We don't know which signal awakened us. This is probably a bug. */ - - ret = 0; } irqrestore(saved_state); } diff --git a/nuttx/sched/sleep.c b/nuttx/sched/sleep.c index bf586acd93..7df922c613 100644 --- a/nuttx/sched/sleep.c +++ b/nuttx/sched/sleep.c @@ -1,8 +1,8 @@ /**************************************************************************** * sched/sleep.c * - * Copyright (C) 2007, 2009 Gregory Nutt. All rights reserved. - * Author: Gregory Nutt + * Copyright (C) 2007, 2009, 2012 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -42,6 +42,9 @@ #include #include +#include +#include + /**************************************************************************** * Preprocessor Definitions ****************************************************************************/ @@ -70,15 +73,54 @@ * Function: sleep * * Description: - * As typically declared in unistd.h. sleep() is a simple application of - * sigtimedwait. + * The sleep() function will cause the calling thread to be suspended from + * execution until either the number of real-time seconds specified by the + * argument 'seconds' has elapsed or a signal is delivered to the calling + * thread and its action is to invoke a signal-catching function or to + * terminate the process. The suspension time may be longer than requested + * due to the scheduling of other activity by the system. + * + * If a SIGALRM signal is generated for the calling process during + * execution of sleep() and if the SIGALRM signal is being ignored or + * blocked from delivery, it is unspecified whether sleep() returns + * when the SIGALRM signal is scheduled. If the signal is being blocked, it + * is also unspecified whether it remains pending after sleep() returns or + * it is discarded. + * + * If a SIGALRM signal is generated for the calling process during + * execution of sleep(), except as a result of a prior call to alarm(), + * and if the SIGALRM signal is not being ignored or blocked from delivery, + * it is unspecified whether that signal has any effect other than causing + * sleep() to return. + * + * If a signal-catching function interrupts sleep() and examines or changes + * either the time a SIGALRM is scheduled to be generated, the action + * associated with the SIGALRM signal, or whether the SIGALRM signal is + * blocked from delivery, the results are unspecified. + * + * If a signal-catching function interrupts sleep() and calls siglongjmp() + * or longjmp() to restore an environment saved prior to the sleep() call, + * the action associated with the SIGALRM signal and the time at which a + * SIGALRM signal is scheduled to be generated are unspecified. It is also + * unspecified whether the SIGALRM signal is blocked, unless the process' + * signal mask is restored as part of the environment. + * + * Implementations may place limitations on the granularity of timer values. + * For each interval timer, if the requested timer value requires a finer + * granularity than the implementation supports, the actual timer value will + * be rounded up to the next supported value. + * + * Interactions between sleep() and any of setitimer(), ualarm() or sleep() + * are unspecified. * * Parameters: * seconds * * Returned Value: - * Zero if the requested time has elapsed, or the number of seconds left - * to sleep. + * If sleep() returns because the requested time has elapsed, the value + * returned will be 0. If sleep() returns because of premature arousal due + * to delivery of a signal, the return value will be the "unslept" amount + * (the requested time minus the time actually slept) in seconds. * * Assumptions: * @@ -89,14 +131,66 @@ unsigned int sleep(unsigned int seconds) sigset_t set; struct timespec ts; struct siginfo value; + irqstate_t flags; + uint32_t start; + int32_t elapsed; + int32_t remaining = 0; + + /* Don't sleep if seconds == 0 */ if (seconds) { + /* Set up for the sleep. Using the empty set means that we are not + * waiting for any particualar signal. However, any unmasked signal + * can still awaken sigtimedwait(). + */ + (void)sigemptyset(&set); - ts.tv_sec = seconds; + ts.tv_sec = seconds; ts.tv_nsec = 0; + + /* Interrupts are disabled around the following so that it is atomic */ + + flags = irqsave(); + + /* Get the current time then sleep for the requested time. + * sigtimedwait() cannot succeed. It should always return error with + * either (1) EAGAIN meaning that the timeout occurred, or (2) EINTR + * meaning that some other unblocked signal was caught. + */ + + start = clock_systimer(); (void)sigtimedwait(&set, &value, &ts); + + /* Calculate the elapsed time (in clock ticks) when we wake up from the sleep. + * This is really only necessary if we were awakened from the sleep early + * due to the receipt of a signal. + */ + + elapsed = clock_systimer() - start; + irqrestore(flags); + + /* Get the remaining, un-waited seconds. Note that this calculation + * truncates the elapsed seconds in the division. We may have slept some + * fraction of a second longer than this! But if the calculation is less + * than the 'seconds', we certainly did not sleep for the complete + * requested interval. + */ + + remaining = (int32_t)seconds - elapsed / TICK_PER_SEC; + + /* Make sure that the elapsed time is non-negative (this should always + * be the case unless something exceptional happened while were we + * sleeping -- like the clock was reset or we went into a low power mode, + * OR if we had to wait a long time to run again after calling + * sigtimedwait() making 'elapsed' bigger than it should have been). + */ + + if (remaining < 0) + { + remaining = 0; + } } - return 0; + return (unsigned int)remaining; } diff --git a/nuttx/sched/usleep.c b/nuttx/sched/usleep.c index 2751628eb3..ba2c072c43 100644 --- a/nuttx/sched/usleep.c +++ b/nuttx/sched/usleep.c @@ -1,8 +1,8 @@ /**************************************************************************** * sched/usleep.c * - * Copyright (C) 2007, 2009 Gregory Nutt. All rights reserved. - * Author: Gregory Nutt + * Copyright (C) 2007, 2009, 2012 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -40,6 +40,8 @@ #include #include #include +#include +#include /**************************************************************************** * Definitions @@ -69,30 +71,98 @@ * Function: usleep * * Description: - * BSD version as typically declared in unistd.h. usleep() is a simple - * application of sigtimedwait. + * The usleep() function will cause the calling thread to be suspended + * from execution until either the number of real-time microseconds + * specified by the argument 'usec' has elapsed or a signal is delivered + * to the calling thread. The suspension time may be longer than requested + * due to the scheduling of other activity by the system. * + * The 'usec' argument must be less than 1,000,000. If the value of + * 'usec' is 0, then the call has no effect. + * + * If a SIGALRM signal is generated for the calling process during + * execution of usleep() and if the SIGALRM signal is being ignored or + * blocked from delivery, it is unspecified whether usleep() returns + * when the SIGALRM signal is scheduled. If the signal is being blocked, it + * is also unspecified whether it remains pending after usleep() returns or + * it is discarded. + * + * If a SIGALRM signal is generated for the calling process during + * execution of usleep(), except as a result of a prior call to alarm(), + * and if the SIGALRM signal is not being ignored or blocked from delivery, + * it is unspecified whether that signal has any effect other than causing + * usleep() to return. + * + * If a signal-catching function interrupts usleep() and examines or changes + * either the time a SIGALRM is scheduled to be generated, the action + * associated with the SIGALRM signal, or whether the SIGALRM signal is + * blocked from delivery, the results are unspecified. + * + * If a signal-catching function interrupts usleep() and calls siglongjmp() + * or longjmp() to restore an environment saved prior to the usleep() call, + * the action associated with the SIGALRM signal and the time at which a + * SIGALRM signal is scheduled to be generated are unspecified. It is also + * unspecified whether the SIGALRM signal is blocked, unless the process' + * signal mask is restored as part of the environment. + * + * Implementations may place limitations on the granularity of timer values. + * For each interval timer, if the requested timer value requires a finer + * granularity than the implementation supports, the actual timer value will + * be rounded up to the next supported value. + * + * Interactions between usleep() and any of the following are unspecified: + * + * nanosleep(), setitimer(), timer_create(), timer_delete(), timer_getoverrun(), + * timer_gettime(), timer_settime(), ualarm(), sleep() + * Parameters: - * seconds + * usec - the number of microseconds to wait. * * Returned Value: - * None + * On successful completion, usleep() returns 0. Otherwise, it returns -1 + * and sets errno to indicate the error. * * Assumptions: * ****************************************************************************/ -void usleep(useconds_t usec) +int usleep(useconds_t usec) { sigset_t set; struct timespec ts; struct siginfo value; + int errval; + int ret = 0; if (usec) { + /* Set up for the sleep. Using the empty set means that we are not + * waiting for any particualar signal. However, any unmasked signal + * can still awaken sigtimedwait(). + */ + (void)sigemptyset(&set); ts.tv_sec = usec / 1000000; ts.tv_nsec = (usec % 1000000) * 1000; - (void)sigtimedwait(&set, &value, &ts); + + /* usleep is a simple application of sigtimedwait. */ + + ret = sigtimedwait(&set, &value, &ts); + + /* sigtimedwait() cannot succeed. It should always return error with + * either (1) EAGAIN meaning that the timeout occurred, or (2) EINTR + * meaning that some other unblocked signal was caught. + */ + + errval = errno; + DEBUGASSERT(ret < 0 && (errval == EAGAIN || errval == EINTR)); + if (errval == EAGAIN) + { + /* The timeout "error" is the normal, successful result */ + + ret = 0; + } } + + return ret; }