Skip to content

Commit d2250d4

Browse files
committed
refactor(tinyusb): Made the tinyusb task deletion notification driven
- Fixed the _task_pending status change on error during tinyusb_task_start() - Kept the legace way of deleting task when blocking time is UINT32_MAX (task deleted in the user context) - Added blocking_timeout_ms parameter to the driver config, tinyusb task parameters
1 parent 773f8c6 commit d2250d4

File tree

3 files changed

+149
-27
lines changed

3 files changed

+149
-27
lines changed

device/esp_tinyusb/include/tinyusb.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,19 @@ typedef struct {
6060
size_t size; /*!< USB Device Task size. */
6161
uint8_t priority; /*!< USB Device Task priority. */
6262
int xCoreID; /*!< USB Device Task core affinity. */
63+
uint32_t blocking_timeout_ms; /*!< USB Device Task blocking timeout in milliseconds.
64+
This feature is used to control the behavior of the tud_task_ext() function called
65+
inside the dedicated TinyUSB task. The timeout defines how long the tud_task_ext() function
66+
will block waiting for USB events and how frequently the driver will poll for the uninstall notififcation.
67+
If tinyusb_driver_uninstall() is not used, the blocking timeout can block indefinitely to reduce CPU usage.
68+
69+
Values:
70+
0 - non-blocking mode.
71+
UINT_MAX - blocks indefinetely (legacy mode).
72+
73+
Default value: 1000 ms.
74+
*/
75+
6376
} tinyusb_task_config_t;
6477

6578
/**

device/esp_tinyusb/include/tinyusb_default_config.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ extern "C" {
6363
#define TINYUSB_DEFAULT_TASK_SIZE 4096
6464
// Default priority for task used in TinyUSB task creation
6565
#define TINYUSB_DEFAULT_TASK_PRIO 5
66+
// Default blocking timeout_ms for tud_task_ext(timeout_ms, false)
67+
#define TINYUSB_DEFAULT_BLOCK_TIME_MS 1000
6668

6769
#define TINYUSB_CONFIG_FULL_SPEED(event_hdl, arg) \
6870
(tinyusb_config_t) { \
@@ -111,13 +113,16 @@ extern "C" {
111113
.size = TINYUSB_DEFAULT_TASK_SIZE, \
112114
.priority = TINYUSB_DEFAULT_TASK_PRIO, \
113115
.xCoreID = TINYUSB_DEFAULT_TASK_AFFINITY, \
116+
.blocking_timeout_ms = TINYUSB_DEFAULT_BLOCK_TIME_MS,\
114117
}
115118

116119
/**
117120
* @brief TinyUSB Task configuration structure initializer
118121
*
119122
* This macro is used to create a custom TinyUSB Task configuration structure.
120123
*
124+
* Note: blocking_timeout_ms is set to UINT32_MAX (blocking indefinitely).
125+
*
121126
* @param s Stack size of the task
122127
* @param p Task priority
123128
* @param a Task affinity (CPU core)
@@ -127,6 +132,25 @@ extern "C" {
127132
.size = (s), \
128133
.priority = (p), \
129134
.xCoreID = (a), \
135+
.blocking_timeout_ms = UINT32_MAX, \
136+
}
137+
138+
/**
139+
* @brief TinyUSB Task configuration structure initializer
140+
*
141+
* This macro is used to create a custom TinyUSB Task configuration structure.
142+
*
143+
* @param s Stack size of the task
144+
* @param p Task priority
145+
* @param a Task affinity (CPU core)
146+
* @param t Blocking timeout in milliseconds for tud_task_ext() function
147+
*/
148+
#define TINYUSB_TASK_CUSTOM_NON_BLOCKING(s, p, a, t) \
149+
(tinyusb_task_config_t) { \
150+
.size = (s), \
151+
.priority = (p), \
152+
.xCoreID = (a), \
153+
.blocking_timeout_ms = (t), \
130154
}
131155

132156
#ifdef __cplusplus

device/esp_tinyusb/tinyusb_task.c

Lines changed: 112 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ static portMUX_TYPE tusb_task_lock = portMUX_INITIALIZER_UNLOCKED;
2424
#define TINYUSB_TASK_ENTER_CRITICAL() portENTER_CRITICAL(&tusb_task_lock)
2525
#define TINYUSB_TASK_EXIT_CRITICAL() portEXIT_CRITICAL(&tusb_task_lock)
2626

27+
// Timeout for taking the start/stop semaphore in milliseconds
28+
#define TINYUSB_TASK_SEM_TAKE_TIMEOUT_MS (5000)
29+
2730
#define TINYUSB_TASK_CHECK(cond, ret_val) ({ \
2831
if (!(cond)) { \
2932
return (ret_val); \
@@ -44,11 +47,14 @@ typedef struct {
4447
tusb_rhport_init_t rhport_init; /*!< USB Device RH port initialization configuration pointer */
4548
const tinyusb_desc_config_t *desc_cfg; /*!< USB Device descriptors configuration pointer */
4649
// Task related
47-
TaskHandle_t handle; /*!< Task handle */
48-
volatile TaskHandle_t awaiting_handle; /*!< Task handle, waiting to be notified after successful start of TinyUSB stack */
50+
TaskHandle_t handle; /*!< TinyUSB Device task handle */
51+
SemaphoreHandle_t start_stop_sem; /*!< Semaphore to start or stop the task */
52+
uint32_t blocking_timeout_ms; /*!< USB Device Task blocking timeout in milliseconds. */
4953
} tinyusb_task_ctx_t;
5054

51-
static bool _task_is_running = false; // Locking flag for the task, access only from the critical section
55+
#define TASK_NOTIF_STOP_BIT (1u << 0)
56+
57+
static bool _task_is_pending = false; // Locking flag for the task, access only from the critical section
5258
static tinyusb_task_ctx_t *p_tusb_task_ctx = NULL; // TinyUSB task context
5359

5460
/**
@@ -57,12 +63,19 @@ static tinyusb_task_ctx_t *p_tusb_task_ctx = NULL; // TinyUSB task context
5763
static void tinyusb_device_task(void *arg)
5864
{
5965
tinyusb_task_ctx_t *task_ctx = (tinyusb_task_ctx_t *)arg;
66+
bool stop_in_pending = false;
6067

6168
// Sanity check
6269
assert(task_ctx != NULL);
63-
assert(task_ctx->awaiting_handle != NULL);
70+
assert(task_ctx->start_stop_sem != NULL);
6471

6572
ESP_LOGD(TAG, "TinyUSB task started");
73+
ESP_LOGD(TAG, "\tPort %d", task_ctx->rhport);
74+
if (task_ctx->blocking_timeout_ms == UINT32_MAX) {
75+
ESP_LOGD(TAG, "\tBlocking timeout: MAX");
76+
} else {
77+
ESP_LOGD(TAG, "\tBlocking timeout: %" PRIu32 " ms", task_ctx->blocking_timeout_ms);
78+
}
6679

6780
if (tud_inited()) {
6881
ESP_LOGE(TAG, "TinyUSB stack is already initialized");
@@ -78,22 +91,54 @@ static void tinyusb_device_task(void *arg)
7891
}
7992

8093
TINYUSB_TASK_ENTER_CRITICAL();
81-
task_ctx->handle = xTaskGetCurrentTaskHandle(); // Save task handle
82-
p_tusb_task_ctx = task_ctx; // Save global task context pointer
94+
_task_is_pending = false; // Clear pending flag, task is running now
95+
task_ctx->handle = xTaskGetCurrentTaskHandle(); // Save task handle
96+
p_tusb_task_ctx = task_ctx; // Save global task context pointer
8397
TINYUSB_TASK_EXIT_CRITICAL();
8498

85-
xTaskNotifyGive(task_ctx->awaiting_handle); // Notify parent task that TinyUSB stack was started successfully
99+
// Notify parent task that TinyUSB stack was started successfully
100+
if (xSemaphoreGive(task_ctx->start_stop_sem) != pdTRUE) {
101+
// Fail only if semaphore queue is full, should never happen
102+
ESP_LOGE(TAG, "Unable to notify that TinyUSB stack was started");
103+
goto desc_free;
104+
}
86105

87-
while (1) { // RTOS forever loop
88-
tud_task();
106+
while (1) {
107+
// Non-blocking check to stop the task
108+
uint32_t notif = 0;
109+
if (xTaskNotifyWait(
110+
0, // don't clear any bits on entry
111+
TASK_NOTIF_STOP_BIT, // clear STOP bit on exit if it was set
112+
&notif,
113+
pdMS_TO_TICKS(10) // Insignificant blocking time, not to waste CPU when blocking_timeout_ms == 0 for tud_task_ext()
114+
) == pdTRUE && (notif & TASK_NOTIF_STOP_BIT)) {
115+
// Stop requested
116+
stop_in_pending = true;
117+
break;
118+
}
119+
// TinyUSB Device task
120+
tud_task_ext(task_ctx->blocking_timeout_ms, false);
89121
}
90122

123+
// Stop TinyUSB stack
124+
if (!tusb_deinit(task_ctx->rhport)) {
125+
ESP_LOGE(TAG, "Unable to deinit TinyUSB stack");
126+
}
127+
128+
ESP_LOGD(TAG, "TinyUSB task has stopped");
129+
91130
desc_free:
92131
tinyusb_descriptors_free();
93132
del:
94133
TINYUSB_TASK_ENTER_CRITICAL();
95-
_task_is_running = false; // Task is not running anymore
134+
_task_is_pending = false; // Clear pending flag
135+
p_tusb_task_ctx = NULL; // Clear global task context pointer
96136
TINYUSB_TASK_EXIT_CRITICAL();
137+
138+
if (stop_in_pending) {
139+
// Notify that TinyUSB stack was stopped
140+
xSemaphoreGive(task_ctx->start_stop_sem);
141+
}
97142
vTaskDelete(NULL);
98143
// No return needed here: vTaskDelete(NULL) does not return
99144
}
@@ -117,22 +162,25 @@ esp_err_t tinyusb_task_start(tinyusb_port_t port, const tinyusb_task_config_t *c
117162

118163
TINYUSB_TASK_ENTER_CRITICAL();
119164
TINYUSB_TASK_CHECK_FROM_CRIT(p_tusb_task_ctx == NULL, ESP_ERR_INVALID_STATE); // Task shouldn't started
120-
TINYUSB_TASK_CHECK_FROM_CRIT(!_task_is_running, ESP_ERR_INVALID_STATE); // Task shouldn't be running
121-
_task_is_running = true; // Task is running flag, will be cleared in task in case of the error
165+
TINYUSB_TASK_CHECK_FROM_CRIT(!_task_is_pending, ESP_ERR_INVALID_STATE); // Task shouldn't be pending
166+
_task_is_pending = true; // Task is pending flag, will be cleared in task
122167
TINYUSB_TASK_EXIT_CRITICAL();
123168

124169
esp_err_t ret;
125170
tinyusb_task_ctx_t *task_ctx = heap_caps_calloc(1, sizeof(tinyusb_task_ctx_t), MALLOC_CAP_DEFAULT);
126-
if (task_ctx == NULL) {
127-
return ESP_ERR_NO_MEM;
171+
SemaphoreHandle_t sem = xSemaphoreCreateBinary();
172+
173+
if (task_ctx == NULL || sem == NULL) {
174+
ret = ESP_ERR_NO_MEM;
175+
goto no_task;
128176
}
129177

130-
task_ctx->awaiting_handle = xTaskGetCurrentTaskHandle(); // Save parent task handle
131-
task_ctx->handle = NULL; // TinyUSB task is not started
178+
task_ctx->start_stop_sem = sem; // TunyUSB Device task start stop semaphore
132179
task_ctx->rhport = port; // Peripheral port number
133180
task_ctx->rhport_init.role = TUSB_ROLE_DEVICE; // Role selection: esp_tinyusb is always a device
134181
task_ctx->rhport_init.speed = (port == TINYUSB_PORT_FULL_SPEED_0) ? TUSB_SPEED_FULL : TUSB_SPEED_HIGH; // Speed selection
135182
task_ctx->desc_cfg = desc_cfg;
183+
task_ctx->blocking_timeout_ms = config->blocking_timeout_ms;
136184

137185
TaskHandle_t task_hdl = NULL;
138186
ESP_LOGD(TAG, "Creating TinyUSB main task on CPU%d", config->xCoreID);
@@ -147,19 +195,32 @@ esp_err_t tinyusb_task_start(tinyusb_port_t port, const tinyusb_task_config_t *c
147195
if (task_hdl == NULL) {
148196
ESP_LOGE(TAG, "Create TinyUSB main task failed");
149197
ret = ESP_ERR_NOT_FINISHED;
150-
goto err;
198+
goto no_task;
151199
}
152200

153-
// Wait until the Task notify that port is active, 5 sec is more than enough
154-
if (ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(5000)) == 0) {
201+
// Wait for "start" semophore with timeout
202+
if (xSemaphoreTake(task_ctx->start_stop_sem, pdMS_TO_TICKS(TINYUSB_TASK_SEM_TAKE_TIMEOUT_MS)) != pdTRUE) {
155203
ESP_LOGE(TAG, "Task wasn't able to start TinyUSB stack");
156204
ret = ESP_ERR_TIMEOUT;
157205
goto err;
158206
}
159207

208+
// Check the global task context pointer and pending state
209+
TINYUSB_TASK_ENTER_CRITICAL();
210+
TINYUSB_TASK_CHECK_FROM_CRIT(!_task_is_pending, ESP_ERR_INVALID_STATE);
211+
TINYUSB_TASK_CHECK_FROM_CRIT(p_tusb_task_ctx == task_ctx, ESP_ERR_NOT_FOUND);
212+
TINYUSB_TASK_EXIT_CRITICAL();
213+
160214
return ESP_OK;
161215

216+
no_task:
217+
TINYUSB_TASK_ENTER_CRITICAL();
218+
_task_is_pending = false; // Clear pending flag
219+
TINYUSB_TASK_EXIT_CRITICAL();
162220
err:
221+
if (sem) {
222+
vSemaphoreDelete(sem);
223+
}
163224
heap_caps_free(task_ctx);
164225
return ret;
165226
}
@@ -168,20 +229,44 @@ esp_err_t tinyusb_task_stop(void)
168229
{
169230
TINYUSB_TASK_ENTER_CRITICAL();
170231
TINYUSB_TASK_CHECK_FROM_CRIT(p_tusb_task_ctx != NULL, ESP_ERR_INVALID_STATE);
232+
TINYUSB_TASK_CHECK_FROM_CRIT(!_task_is_pending, ESP_ERR_INVALID_STATE); // Task shouldn't be pending
171233
tinyusb_task_ctx_t *task_ctx = p_tusb_task_ctx;
172-
p_tusb_task_ctx = NULL;
173-
_task_is_running = false;
234+
_task_is_pending = true; // Task is pending to stop
174235
TINYUSB_TASK_EXIT_CRITICAL();
175236

176-
if (task_ctx->handle != NULL) {
237+
if (task_ctx->blocking_timeout_ms != UINT32_MAX) {
238+
/* Request TinyUSB task to stop */
239+
if (xTaskNotify(task_ctx->handle, TASK_NOTIF_STOP_BIT, eSetBits) != pdPASS) {
240+
ESP_LOGE(TAG, "Unable to notify TinyUSB task to stop");
241+
return ESP_ERR_INVALID_STATE;
242+
}
243+
/* Wait for "stop" semaphore with timeout */
244+
if (xSemaphoreTake(task_ctx->start_stop_sem, pdMS_TO_TICKS(TINYUSB_TASK_SEM_TAKE_TIMEOUT_MS)) != pdTRUE) {
245+
ESP_LOGE(TAG, "Timeout while TinyUSB task stop");
246+
return ESP_ERR_TIMEOUT;
247+
}
248+
/* Check the global task context pointer and pending state */
249+
TINYUSB_TASK_ENTER_CRITICAL();
250+
TINYUSB_TASK_CHECK_FROM_CRIT(!_task_is_pending, ESP_ERR_INVALID_STATE);
251+
TINYUSB_TASK_CHECK_FROM_CRIT(p_tusb_task_ctx == NULL, ESP_ERR_INVALID_STATE);
252+
TINYUSB_TASK_EXIT_CRITICAL();
253+
} else {
254+
/* TinyUSB Task is forever loop, delete the task the old way */
177255
vTaskDelete(task_ctx->handle);
178256
task_ctx->handle = NULL;
257+
/* Free descriptors */
258+
tinyusb_descriptors_free();
259+
/* Deinit TinyUSB Stask */
260+
ESP_RETURN_ON_FALSE(tusb_deinit(task_ctx->rhport), ESP_ERR_INVALID_STATE, TAG, "Unable to deinit TinyUSB stack");
261+
/* Clean global context and pending state */
262+
TINYUSB_TASK_ENTER_CRITICAL();
263+
_task_is_pending = false;
264+
p_tusb_task_ctx = NULL;
265+
TINYUSB_TASK_EXIT_CRITICAL();
179266
}
180-
// Free descriptors
181-
tinyusb_descriptors_free();
182-
// Stop TinyUSB stack
183-
ESP_RETURN_ON_FALSE(tusb_deinit(task_ctx->rhport), ESP_ERR_NOT_FINISHED, TAG, "Unable to teardown TinyUSB stack");
184-
// Cleanup
267+
268+
/* Cleanup */
269+
vSemaphoreDelete(task_ctx->start_stop_sem);
185270
heap_caps_free(task_ctx);
186271
return ESP_OK;
187272
}

0 commit comments

Comments
 (0)