diff --git a/usb/device/utilities/events/Task.h b/usb/device/utilities/events/Task.h new file mode 100644 index 0000000000..1d51ac12e6 --- /dev/null +++ b/usb/device/utilities/events/Task.h @@ -0,0 +1,597 @@ +/* events + * Copyright (c) 2018 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. + */ + +#ifndef MBED_TASK_H +#define MBED_TASK_H + +#include "events/EventQueue.h" +#include "events/TaskBase.h" +#include "platform/mbed_assert.h" +#include "platform/Callback.h" + +namespace events { +/** \addtogroup events */ + + +template +struct AllArgs; + +template +struct AllArgs { + typedef AllArgs Self; + B0 b0; + + AllArgs(B0 b0=B0()): b0(b0) {} + + template + struct Operations { + static void copy(void *_dest, void *_src) + { + new (_dest) Self(*(Self*)_src); + } + + static void call(void *data) { + Self *s = static_cast(data); + s->b0(); + s->~Self(); + } + }; + + typedef Operations ops; +}; + +template +struct AllArgs { + typedef AllArgs Self; + B0 b0; B1 b1; + + AllArgs(B0 b0=B0(), B1 b1=B1()): b0(b0), b1(b1) {} + + template + struct Operations { + static void copy(void *_dest, void *_src) + { + new (_dest) Self(*(Self*)_src); + } + + static void call(void *data) { + Self *s = static_cast(data); + s->b0(s->b1); + s->~Self(); + } + }; + + template + struct Operations { + static void copy(void *_dest, void *_src) + { + new (_dest) Self(*(Self*)_src); + } + + static void call(void *data) { + Self *s = static_cast(data); + ((s->b0)->*(s->b1))(); + s->~Self(); + } + }; + + template + struct Operations { + static void copy(void *_dest, void *_src) + { + new (_dest) Self(*(Self*)_src); + } + + static void call(void *data) { + Self *s = static_cast(data); + ((s->b0)->*(s->b1))(); + s->~Self(); + } + }; + + template + struct Operations { + static void copy(void *_dest, void *_src) + { + new (_dest) Self(*(Self*)_src); + } + + static void call(void *data) { + Self *s = static_cast(data); + ((s->b0)->*(s->b1))(); + s->~Self(); + } + }; + + template + struct Operations { + static void copy(void *_dest, void *_src) + { + new (_dest) Self(*(Self*)_src); + } + + static void call(void *data) { + Self *s = static_cast(data); + ((s->b0)->*(s->b1))(); + s->~Self(); + } + }; + + typedef Operations ops; +}; + +template +struct AllArgs { + typedef AllArgs Self; + B0 b0; B1 b1; B2 b2; + + + AllArgs(B0 b0=B0(), B1 b1=B1(), B2 b2=B2()): b0(b0), b1(b1), b2(b2) {} + + template + struct Operations { + static void copy(void *_dest, void *_src) + { + new (_dest) Self(*(Self*)_src); + } + + static void call(void *data) { + Self *s = static_cast(data); + s->b0(s->b1, s->b2); + s->~Self(); + } + }; + + template + struct Operations { + static void copy(void *_dest, void *_src) + { + new (_dest) Self(*(Self*)_src); + } + + static void call(void *data) { + Self *s = static_cast(data); + ((s->b0)->*(s->b1))(s->b2); + s->~Self(); + } + }; + + template + struct Operations { + static void copy(void *_dest, void *_src) + { + new (_dest) Self(*(Self*)_src); + } + + static void call(void *data) { + Self *s = static_cast(data); + ((s->b0)->*(s->b1))(s->b2); + s->~Self(); + } + }; + + template + struct Operations { + static void copy(void *_dest, void *_src) + { + new (_dest) Self(*(Self*)_src); + } + + static void call(void *data) { + Self *s = static_cast(data); + ((s->b0)->*(s->b1))(s->b2); + s->~Self(); + } + }; + + template + struct Operations { + static void copy(void *_dest, void *_src) + { + new (_dest) Self(*(Self*)_src); + } + + static void call(void *data) { + Self *s = static_cast(data); + ((s->b0)->*(s->b1))(s->b2); + s->~Self(); + } + }; + + typedef Operations ops; +}; + +template +struct AllArgs { + typedef AllArgs Self; + B0 b0; B1 b1; B2 b2; B3 b3; + + + AllArgs(B0 b0=B0(), B1 b1=B1(), B2 b2=B2(), B3 b3=B3()): b0(b0), b1(b1), b2(b2), b3(b3) {} + + template + struct Operations { + static void copy(void *_dest, void *_src) + { + new (_dest) Self(*(Self*)_src); + } + + static void call(void *data) { + Self *s = static_cast(data); + s->b0(s->b1, s->b2, s->b3); + s->~Self(); + } + }; + + template + struct Operations { + static void copy(void *_dest, void *_src) + { + new (_dest) Self(*(Self*)_src); + } + + static void call(void *data) { + Self *s = static_cast(data); + ((s->b0)->*(s->b1))(s->b2, s->b3); + s->~Self(); + } + }; + + template + struct Operations { + static void copy(void *_dest, void *_src) + { + new (_dest) Self(*(Self*)_src); + } + + static void call(void *data) { + Self *s = static_cast(data); + ((s->b0)->*(s->b1))(s->b2, s->b3); + s->~Self(); + } + }; + + template + struct Operations { + static void copy(void *_dest, void *_src) + { + new (_dest) Self(*(Self*)_src); + } + + static void call(void *data) { + Self *s = static_cast(data); + ((s->b0)->*(s->b1))(s->b2, s->b3); + s->~Self(); + } + }; + + template + struct Operations { + static void copy(void *_dest, void *_src) + { + new (_dest) Self(*(Self*)_src); + } + + static void call(void *data) { + Self *s = static_cast(data); + ((s->b0)->*(s->b1))(s->b2, s->b3); + s->~Self(); + } + }; + + typedef Operations ops; +}; + +template +struct AllArgs { + typedef AllArgs Self; + B0 b0; B1 b1; B2 b2; B3 b3; B4 b4; + + + AllArgs(B0 b0=B0(), B1 b1=B1(), B2 b2=B2(), B3 b3=B3(), B4 b4=B4()): b0(b0), b1(b1), b2(b2), b3(b3), b4(b4) {} + + template + struct Operations { + static void copy(void *_dest, void *_src) + { + new (_dest) Self(*(Self*)_src); + } + + static void call(void *data) { + Self *s = static_cast(data); + s->b0(s->b1, s->b2, s->b3, s->b4); + s->~Self(); + } + }; + + template + struct Operations { + static void copy(void *_dest, void *_src) + { + new (_dest) Self(*(Self*)_src); + } + + static void call(void *data) { + Self *s = static_cast(data); + ((s->b0)->*(s->b1))(s->b2, s->b3, s->b4); + s->~Self(); + } + }; + + template + struct Operations { + static void copy(void *_dest, void *_src) + { + new (_dest) Self(*(Self*)_src); + } + + static void call(void *data) { + Self *s = static_cast(data); + ((s->b0)->*(s->b1))(s->b2, s->b3, s->b4); + s->~Self(); + } + }; + + template + struct Operations { + static void copy(void *_dest, void *_src) + { + new (_dest) Self(*(Self*)_src); + } + + static void call(void *data) { + Self *s = static_cast(data); + ((s->b0)->*(s->b1))(s->b2, s->b3, s->b4); + s->~Self(); + } + }; + + template + struct Operations { + static void copy(void *_dest, void *_src) + { + new (_dest) Self(*(Self*)_src); + } + + static void call(void *data) { + Self *s = static_cast(data); + ((s->b0)->*(s->b1))(s->b2, s->b3, s->b4); + s->~Self(); + } + }; + + typedef Operations ops; +}; + +template +struct AllArgs { + typedef AllArgs Self; + B0 b0; B1 b1; B2 b2; B3 b3; B4 b4; B5 b5; + + + AllArgs(B0 b0=B0(), B1 b1=B1(), B2 b2=B2(), B3 b3=B3(), B4 b4=B4(), B5 b5=B5()): b0(b0), b1(b1), b2(b2), b3(b3), b4(b4), b5(b5) {} + + template + struct Operations { + static void copy(void *_dest, void *_src) + { + new (_dest) Self(*(Self*)_src); + } + + static void call(void *data) { + Self *s = static_cast(data); + s->b0(s->b1, s->b2, s->b3, s->b4, s->b5); + s->~Self(); + } + }; + + template + struct Operations { + static void copy(void *_dest, void *_src) + { + new (_dest) Self(*(Self*)_src); + } + + static void call(void *data) { + Self *s = static_cast(data); + ((s->b0)->*(s->b1))(s->b2, s->b3, s->b4, s->b5); + s->~Self(); + } + }; + + template + struct Operations { + static void copy(void *_dest, void *_src) + { + new (_dest) Self(*(Self*)_src); + } + + static void call(void *data) { + Self *s = static_cast(data); + ((s->b0)->*(s->b1))(s->b2, s->b3, s->b4, s->b5); + s->~Self(); + } + }; + + template + struct Operations { + static void copy(void *_dest, void *_src) + { + new (_dest) Self(*(Self*)_src); + } + + static void call(void *data) { + Self *s = static_cast(data); + ((s->b0)->*(s->b1))(s->b2, s->b3, s->b4, s->b5); + s->~Self(); + } + }; + + template + struct Operations { + static void copy(void *_dest, void *_src) + { + new (_dest) Self(*(Self*)_src); + } + + static void call(void *data) { + Self *s = static_cast(data); + ((s->b0)->*(s->b1))(s->b2, s->b3, s->b4, s->b5); + s->~Self(); + } + }; + + typedef Operations ops; +}; + + +template +class Task; + +template +class Task: public TaskBase { +public: + + Task(TaskQueue *q=NULL, mbed::Callback cb=mbed::Callback()) + : TaskBase(q), _args(cb) { + } + + Task& operator=( mbed::Callback cb) { + _args.b0 = cb; + return *this; + } + + void call() { + post(); + } + +protected: + + virtual uint32_t size() { + return sizeof(_args); + } + + virtual run_callback_t start(void *data, uint32_t max_size) { + All::ops::copy(data, (void*)&_args); + return &All::ops::call; + } + +private: + typedef AllArgs > All; + All _args; +}; + +template +class Task: public TaskBase { +public: + + Task(TaskQueue *q=NULL, mbed::Callback cb=mbed::Callback()) + : TaskBase(q), _args(cb) { + } + + Task& operator=( mbed::Callback cb) { + _args.b0 = cb; + return *this; + } + + void call(A0 a0) { + _args.b1 = a0; + post(); + } + +protected: + + virtual uint32_t size() { + return sizeof(_args); + } + + virtual run_callback_t start(void *data, uint32_t max_size) { + All::ops::copy(data, (void*)&_args); + return &All::ops::call; + } + +private: + typedef AllArgs, A0> All; + All _args; +}; + +/** Task + * + * Representation of a postable task + * @ingroup events + */ +template +class Task: public TaskBase { +public: + + /** + * Construct a new task + * + * @param q TaskQueue to post to + * @param cb Callback to run + */ + Task(TaskQueue *q=NULL, mbed::Callback cb=mbed::Callback()) + : TaskBase(q), _args(cb) { + } + + /** + * Set the callback of this task + * + * @param cb Callback to run + */ + Task& operator=(mbed::Callback cb) { + _args.b0 = cb; + return *this; + } + + /** + * Post this task for execution + * + * The number of arguments to call should match + * the type of the callback. For example Task + * expects two integers as arguments to call, while Task + * expects no arguments. + * + * @param a0 First callback parameter + * @param a1 Second callback parameter + */ + void call(A0 a0, A1 a1) { + _args.b1 = a0; + _args.b2 = a1; + post(); + } + +protected: + + virtual uint32_t size() { + return sizeof(_args); + } + + virtual run_callback_t start(void *data, uint32_t max_size) { + All::ops::copy(data, (void*)&_args); + return &All::ops::call; + } + +private: + typedef AllArgs, A0, A1> All; + All _args; +}; + +} + +/** @}*/ + +#endif diff --git a/usb/device/utilities/events/TaskBase.cpp b/usb/device/utilities/events/TaskBase.cpp new file mode 100644 index 0000000000..ce974f529f --- /dev/null +++ b/usb/device/utilities/events/TaskBase.cpp @@ -0,0 +1,151 @@ +/* events + * Copyright (c) 2018 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 "events/TaskBase.h" +#include "events/TaskQueue.h" +#include "events/mbed_events.h" +#include "rtos/Semaphore.h" +#include "mbed.h" + +TaskBase::TaskBase(TaskQueue *q) + : _queue(q), _posted(false), _start_count(0), _flush_sem(NULL) +{ + +} + +TaskBase::~TaskBase() +{ + cancel(); + wait(); +} + +void TaskBase::set(TaskQueue *q) +{ + core_util_critical_section_enter(); + + // Cannot set the queue when it has been posted but has not been finished + MBED_ASSERT(!_posted); + _queue = q; + + core_util_critical_section_exit(); +} + +void TaskBase::cancel() +{ + core_util_critical_section_enter(); + + if (_posted) { + _queue->cancel(this); + _posted = false; + _wake_check(); + } + + core_util_critical_section_exit(); +} + +void TaskBase::wait() +{ + // Fast path check for finished + core_util_critical_section_enter(); + if (finished()) { + core_util_critical_section_exit(); + return; + } + core_util_critical_section_exit(); + + rtos::Semaphore sem; + + // If the event is in-flight then wait for it to complete + core_util_critical_section_enter(); + if (finished()) { + // This element has been flushed from the queue + core_util_critical_section_exit(); + return; + } + _flush_sem = &sem; + core_util_critical_section_exit(); + + sem.wait(); +} + +bool TaskBase::ready() +{ + core_util_critical_section_enter(); + + bool is_ready = !_posted; + + core_util_critical_section_exit(); + return is_ready; +} + +bool TaskBase::finished() +{ + core_util_critical_section_enter(); + + bool is_finished = !_posted && (_start_count == 0); + + core_util_critical_section_exit(); + return is_finished; +} + +void TaskBase::finish() +{ + // Nothing to do +} + +void TaskBase::post() +{ + core_util_critical_section_enter(); + + MBED_ASSERT(_queue); + if (_queue) { + MBED_ASSERT(!_posted); + _queue->post(this); + _posted = true; + } + + core_util_critical_section_exit(); +} + +TaskBase::run_callback_t TaskBase::_start(void *buffer, uint32_t size) +{ + // Each call to _start must result in a call to _finish + MBED_ASSERT(_start_count < 0xFFFF); + _start_count++; + _posted = false; + + return start(buffer, size); +} + +void TaskBase::_finish() +{ + // Each call to _finish must be preceded by a call to _start + MBED_ASSERT(_start_count > 0); + _start_count--; + _wake_check(); + finish(); +} + +void TaskBase::_wake_check() +{ + if (!finished()) { + return; + } + if (_flush_sem) { + _flush_sem->release(); + _flush_sem = NULL; + } +} diff --git a/usb/device/utilities/events/TaskBase.h b/usb/device/utilities/events/TaskBase.h new file mode 100644 index 0000000000..a4ce4c9cdb --- /dev/null +++ b/usb/device/utilities/events/TaskBase.h @@ -0,0 +1,163 @@ +/* events + * Copyright (c) 2018 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. + */ +#ifndef TASK_BASE_H +#define TASK_BASE_H + +#include "platform/Callback.h" +#include "platform/mbed_assert.h" +#include "LinkEntry.h" + +namespace rtos { +class Semaphore; +} + +namespace events { +/** \addtogroup events */ + + +class TaskQueue; + +/** TaskBase + * + * Representation of a caller allocated task + * @ingroup events + */ +class TaskBase : public LinkEntry { +public: + + typedef void (*run_callback_t)(void *data); + + /** + * Construct a new TaskBase object + * + * @param q Queue for posting to + */ + TaskBase(TaskQueue *q); + + /** + * Destroy this TaskBase + */ + virtual ~TaskBase(); + + /** + * Set the queue of this task + * + * @param q TaskQueue to post to + */ + void set(TaskQueue *q); + + /** + * Cancel the execution of this task + * + * Once cancelled the task can be posted again. Previous + * calls to post may still run. If you need to ensure the + * callback has finished the function wait() can be used. + * + * @note This function is interrupt safe + */ + void cancel(); + + /** + * Return true if this task is ready to be posted + * + * Check if this task is on a queue waiting to be run. + * + * @return true if it is safe to call post + */ + bool ready(); + + /** + * Wait for this task to finish execution + * + * When this function returns then this task is in the finished state. + */ + void wait(); + + /** + * Check if the callback has run to completion or been fully canceled + * + * When an task is finished the queue is completely done with it and the + * callback is either fully complete or has been canceled and will not run. + * + * @return true if this task has been flushed from the queue, false otherwise + */ + bool finished(); + +protected: + + /** + * Size of buffer required for TaskBase::start + * + * @return requested buffer size + */ + virtual uint32_t size() = 0; + + /** + * Copy any callback data and return a callback to run + * + * @param data Buffer to copy data to. Do not copy more than TaskBase::size() data. + * @param size Maximum size to copy + */ + virtual run_callback_t start(void *data, uint32_t size) = 0; + + /** + * Inform this task that execution has finished. + * + */ + virtual void finish(); + + /** + * Post this task to the set TaskQueue for execution + */ + void post(); + +private: + + TaskQueue *_queue; + bool _posted; + uint16_t _start_count; + rtos::Semaphore *_flush_sem; + + friend class TaskQueue; + + /* + * Must be called in a critical section + * + * This function should not be called directly. Instead + * TaskQueue::task_start should be used instead. + */ + run_callback_t _start(void *buffer, uint32_t size); + + /* + * Must be called in a critical section + * + * This function should not be called directly. Instead + * TaskQueue::task_finish should be used instead. + * + */ + void _finish(); + + /* + * Unblock wait if this task is finished + */ + void _wake_check(); +}; + +} + +#endif + +/** @}*/ diff --git a/usb/device/utilities/events/TaskQueue.h b/usb/device/utilities/events/TaskQueue.h new file mode 100644 index 0000000000..521876c11e --- /dev/null +++ b/usb/device/utilities/events/TaskQueue.h @@ -0,0 +1,138 @@ +/* events + * Copyright (c) 2018 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. + */ + +#ifndef TASK_QUEUE_H +#define TASK_QUEUE_H + +#include "events/TaskBase.h" +#include "platform/Callback.h" +#include "mbed_critical.h" + +#define MBED_MAX_TASK_SIZE 32 + +namespace events { +/** \addtogroup events */ + + + +/** TaskQueue + * + * Flexible task queue for dispatching tasks + * @ingroup events + */ +class TaskQueue { +public: + + /** Create a TaskQueue + * + * Create an event queue. + */ + TaskQueue() + { + + } + + /** Destroy a TaskQueue + */ + virtual ~TaskQueue() + { + + } + + /** + * Add this event to the queue for execution + * + * If the event is already in the queue then it is canceled and + * added to the end of the queue. + * + * @param event Pointer to the event + */ + virtual void post(TaskBase *event) = 0; + + /** Cancel an in-flight event + * + * Cancels the given event so the event's memory can be reused. + * + * The cancel function is irq safe. + * + * If called while the event queue's dispatch loop is active, the cancel + * function does not guarantee that the event will not execute after it + * returns, as the event may have already begun executing. It does + * guarantee that the event queue is no longer using event data so + * the event can be freed or reused. + * + * @param event Pointer to the event + */ + virtual void cancel(TaskBase *event) = 0; + +protected: + + /** + * Get the size required to run this task + * + * Get the minimum size required for TaskQueue::task_start + * + * @param task The task to check size on + * @return required size + * @note This call must be made in a critical section + */ + static uint32_t task_size(TaskBase *task) + { + + return task->size(); + } + + /** + * Start processing this event by copying out its data + * + * Inform this event both that callback execution has started + * and that the event is free to be posted again. + * + * @param task The task to start processing + * @param dest The buffer to copy the callback arguments to + * @param size maximum size to copy + * @return Pointer to function run + * + * @note event_start must not be called on a canceled event as the + * memory may have been freed already + * @note Every call to event_start must be paired with event_finish + * @note This call must be made in a critical section + */ + static TaskBase::run_callback_t task_start(TaskBase *task, uint8_t *dest, uint32_t size) + { + + return task->_start(dest, size); + } + + /** + * Finish processing this event + * + * Inform this event that the callback has run to completion. + * + * @param task The task to finish processing + * + * @note Every call to event_finish must be preceded by a call to event_start + * @note This call must be made in a critical section + */ + static void task_finish(TaskBase *task) + { + task->_finish(); + } +}; + +} +#endif +