@@ -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
5258static 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
5763static 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+
91130desc_free :
92131 tinyusb_descriptors_free ();
93132del :
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 ();
162220err :
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