Skip to content

Add sleep manager API #4912

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Sep 7, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions TESTS/mbed_hal/sleep_manager/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/* mbed Microcontroller Library
* Copyright (c) 2017 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "utest/utest.h"
#include "unity/unity.h"
#include "greentea-client/test_env.h"

#if !DEVICE_SLEEP
#error [NOT_SUPPORTED] test not supported
#endif

using namespace utest::v1;

void sleep_manager_deepsleep_counter_test()
{
bool deep_sleep_allowed = sleep_manager_can_deep_sleep();
TEST_ASSERT_TRUE(deep_sleep_allowed);

sleep_manager_lock_deep_sleep();
deep_sleep_allowed = sleep_manager_can_deep_sleep();
TEST_ASSERT_FALSE(deep_sleep_allowed);

sleep_manager_unlock_deep_sleep();
deep_sleep_allowed = sleep_manager_can_deep_sleep();
TEST_ASSERT_TRUE(deep_sleep_allowed);
}

utest::v1::status_t greentea_failure_handler(const Case *const source, const failure_t reason)
{
greentea_case_failure_abort_handler(source, reason);
return STATUS_CONTINUE;
}

utest::v1::status_t greentea_test_setup(const size_t number_of_cases)
{
GREENTEA_SETUP(20, "default_auto");
return greentea_test_setup_handler(number_of_cases);
}

Case cases[] = {
Case("sleep manager - deep sleep counter", sleep_manager_deepsleep_counter_test, greentea_failure_handler),
};

Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler);

int main() {
Harness::run(specification);
}
99 changes: 99 additions & 0 deletions TESTS/mbed_hal/sleep_manager_racecondition/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/* mbed Microcontroller Library
* Copyright (c) 2017 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include "utest/utest.h"
#include "unity/unity.h"
#include "greentea-client/test_env.h"

#if !DEVICE_SLEEP
#error [NOT_SUPPORTED] test not supported
#endif

using namespace utest::v1;

void sleep_manager_locking_thread_test()
{
for (uint32_t i = 0; i < 100; i++) {
sleep_manager_lock_deep_sleep();
Thread::wait(25);
sleep_manager_unlock_deep_sleep();
}
}

void sleep_manager_multithread_test()
{
{
Callback<void()> cb(sleep_manager_locking_thread_test);
Thread t1;
Thread t2;

t1.start(callback(cb));
Thread::wait(25);
t2.start(callback(cb));

// Wait for the threads to finish
t1.join();
t2.join();
}

bool deep_sleep_allowed = sleep_manager_can_deep_sleep();
TEST_ASSERT_TRUE_MESSAGE(deep_sleep_allowed, "Deep sleep should be allowed");
}

void sleep_manager_locking_irq_test()
{
sleep_manager_lock_deep_sleep();
sleep_manager_unlock_deep_sleep();
}

void sleep_manager_irq_test()
{
{
Ticker ticker1;
Timer timer;

ticker1.attach_us(&sleep_manager_locking_irq_test, 500);

// run this for 5 seconds
timer.start();
int start = timer.read();
int end = start + 5;
while (timer.read() < end) {
sleep_manager_locking_irq_test();
}
timer.stop();
}

bool deep_sleep_allowed = sleep_manager_can_deep_sleep();
TEST_ASSERT_TRUE_MESSAGE(deep_sleep_allowed, "Deep sleep should be allowed");
}

utest::v1::status_t greentea_test_setup(const size_t number_of_cases)
{
GREENTEA_SETUP(30, "default_auto");
return greentea_test_setup_handler(number_of_cases);
}

Case cases[] = {
Case("sleep manager HAL - locking multithreaded", sleep_manager_multithread_test),
Case("sleep manager HAL - locking IRQ", sleep_manager_irq_test),
};

Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler);

int main() {
Harness::run(specification);
}
21 changes: 15 additions & 6 deletions drivers/CAN.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,15 @@
#if DEVICE_CAN

#include "cmsis.h"
#include "platform/mbed_sleep.h"

namespace mbed {

static void donothing() {}

CAN::CAN(PinName rd, PinName td) : _can(), _irq() {
// No lock needed in constructor

for (size_t i = 0; i < sizeof _irq / sizeof _irq[0]; i++) {
_irq[i] = callback(donothing);
_irq[i] = NULL;
}

can_init(&_can, rd, td);
Expand All @@ -38,7 +37,7 @@ CAN::CAN(PinName rd, PinName td, int hz) : _can(), _irq() {
// No lock needed in constructor

for (size_t i = 0; i < sizeof _irq / sizeof _irq[0]; i++) {
_irq[i] = callback(donothing);
_irq[i] = NULL;
}

can_init_freq(&_can, rd, td, hz);
Expand Down Expand Up @@ -115,18 +114,28 @@ int CAN::filter(unsigned int id, unsigned int mask, CANFormat format, int handle
void CAN::attach(Callback<void()> func, IrqType type) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Attach could also be called to replace a callback of the same type. If this is the case then deep sleep shouldn't be locked a second time.

lock();
if (func) {
// lock deep sleep only the first time
if (!_irq[(CanIrqType)type]) {
sleep_manager_lock_deep_sleep();
}
_irq[(CanIrqType)type] = func;
can_irq_set(&_can, (CanIrqType)type, 1);
} else {
_irq[(CanIrqType)type] = callback(donothing);
// unlock deep sleep only the first time
if (_irq[(CanIrqType)type]) {
sleep_manager_unlock_deep_sleep();
}
_irq[(CanIrqType)type] = NULL;
can_irq_set(&_can, (CanIrqType)type, 0);
}
unlock();
}

void CAN::_irq_handler(uint32_t id, CanIrqType type) {
CAN *handler = (CAN*)id;
handler->_irq[type].call();
if (handler->_irq[type]) {
handler->_irq[type].call();
}
}

void CAN::lock() {
Expand Down
4 changes: 3 additions & 1 deletion drivers/CAN.h
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,9 @@ class CAN : private NonCopyable<CAN> {

/** Attach a function to call whenever a CAN frame received interrupt is
* generated.
*
*
* This function locks the deep sleep while a callback is attached
*
* @param func A pointer to a void function, or 0 to set as none
* @param type Which CAN interrupt to attach the member function to (CAN::RxIrq for message received, CAN::TxIrq for transmitted or aborted, CAN::EwIrq for error warning, CAN::DoIrq for data overrun, CAN::WuIrq for wake-up, CAN::EpIrq for error passive, CAN::AlIrq for arbitration lost, CAN::BeIrq for bus error)
*/
Expand Down
9 changes: 9 additions & 0 deletions drivers/I2C.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@

#if DEVICE_I2C

#if DEVICE_I2C_ASYNCH
#include "platform/mbed_sleep.h"
#endif

namespace mbed {

I2C *I2C::_owner = NULL;
Expand Down Expand Up @@ -129,6 +133,7 @@ int I2C::transfer(int address, const char *tx_buffer, int tx_length, char *rx_bu
unlock();
return -1; // transaction ongoing
}
sleep_manager_lock_deep_sleep();
aquire();

_callback = callback;
Expand All @@ -143,6 +148,7 @@ void I2C::abort_transfer(void)
{
lock();
i2c_abort_asynch(&_i2c);
sleep_manager_unlock_deep_sleep();
unlock();
}

Expand All @@ -152,6 +158,9 @@ void I2C::irq_handler_asynch(void)
if (_callback && event) {
_callback.call(event);
}
if (event) {
sleep_manager_unlock_deep_sleep();
}

}

Expand Down
2 changes: 2 additions & 0 deletions drivers/I2C.h
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,8 @@ class I2C : private NonCopyable<I2C> {

/** Start non-blocking I2C transfer.
*
* This function locks the deep sleep until any event has occured
*
* @param address 8/10 bit I2c slave address
* @param tx_buffer The TX buffer with data to be transfered
* @param tx_length The length of TX buffer in bytes
Expand Down
7 changes: 7 additions & 0 deletions drivers/SPI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@
#include "drivers/SPI.h"
#include "platform/mbed_critical.h"

#if DEVICE_SPI_ASYNCH
#include "platform/mbed_sleep.h"
#endif

#if DEVICE_SPI

namespace mbed {
Expand Down Expand Up @@ -136,6 +140,7 @@ int SPI::transfer(const void *tx_buffer, int tx_length, void *rx_buffer, int rx_
void SPI::abort_transfer()
{
spi_abort_asynch(&_spi);
sleep_manager_unlock_deep_sleep();
#if TRANSACTION_QUEUE_SIZE_SPI
dequeue_transaction();
#endif
Expand Down Expand Up @@ -195,6 +200,7 @@ int SPI::queue_transfer(const void *tx_buffer, int tx_length, void *rx_buffer, i

void SPI::start_transfer(const void *tx_buffer, int tx_length, void *rx_buffer, int rx_length, unsigned char bit_width, const event_callback_t& callback, int event)
{
sleep_manager_lock_deep_sleep();
_acquire();
_callback = callback;
_irq.callback(&SPI::irq_handler_asynch);
Expand Down Expand Up @@ -224,6 +230,7 @@ void SPI::irq_handler_asynch(void)
{
int event = spi_irq_handler_asynch(&_spi);
if (_callback && (event & SPI_EVENT_ALL)) {
sleep_manager_unlock_deep_sleep();
_callback.call(event & SPI_EVENT_ALL);
}
#if TRANSACTION_QUEUE_SIZE_SPI
Expand Down
2 changes: 2 additions & 0 deletions drivers/SPI.h
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,8 @@ class SPI : private NonCopyable<SPI> {

/** Start non-blocking SPI transfer using 8bit buffers.
*
* This function locks the deep sleep until any event has occured
*
* @param tx_buffer The TX buffer with data to be transfered. If NULL is passed,
* the default SPI value is sent
* @param tx_length The length of TX buffer in bytes
Expand Down
Loading