@@ -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,55 @@ 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+ uint32_t notify_blocking_ms = (task_ctx -> blocking_timeout_ms == 0 ) ? 1 : 0 ; // Insignificant blocking time to check notifications, to not to waste CPU when tud_task_ext(0, false)
107+ while (1 ) {
108+ uint32_t notif = 0 ;
109+ // Poll for notification bits
110+ if (xTaskNotifyWait (
111+ 0 , // don't clear any bits on entry
112+ TASK_NOTIF_STOP_BIT , // clear STOP bit on exit if it was set
113+ & notif ,
114+ pdMS_TO_TICKS (notify_blocking_ms )
115+ ) == pdTRUE && (notif & TASK_NOTIF_STOP_BIT )) {
116+ // Stop requested
117+ stop_in_pending = true;
118+ break ;
119+ }
120+ // TinyUSB Device task
121+ tud_task_ext (task_ctx -> blocking_timeout_ms , false);
89122 }
90123
124+ // Stop TinyUSB stack
125+ if (!tusb_deinit (task_ctx -> rhport )) {
126+ ESP_LOGE (TAG , "Unable to deinit TinyUSB stack" );
127+ }
128+
129+ ESP_LOGD (TAG , "TinyUSB task has stopped" );
130+
91131desc_free :
92132 tinyusb_descriptors_free ();
93133del :
94134 TINYUSB_TASK_ENTER_CRITICAL ();
95- _task_is_running = false; // Task is not running anymore
135+ _task_is_pending = false; // Clear pending flag
136+ p_tusb_task_ctx = NULL ; // Clear global task context pointer
96137 TINYUSB_TASK_EXIT_CRITICAL ();
138+
139+ if (stop_in_pending ) {
140+ // Notify that TinyUSB stack was stopped
141+ xSemaphoreGive (task_ctx -> start_stop_sem );
142+ }
97143 vTaskDelete (NULL );
98144 // No return needed here: vTaskDelete(NULL) does not return
99145}
@@ -117,22 +163,25 @@ esp_err_t tinyusb_task_start(tinyusb_port_t port, const tinyusb_task_config_t *c
117163
118164 TINYUSB_TASK_ENTER_CRITICAL ();
119165 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
166+ TINYUSB_TASK_CHECK_FROM_CRIT (!_task_is_pending , ESP_ERR_INVALID_STATE ); // Task shouldn't be pending
167+ _task_is_pending = true; // Task is pending flag, will be cleared in task
122168 TINYUSB_TASK_EXIT_CRITICAL ();
123169
124170 esp_err_t ret ;
125171 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 ;
172+ SemaphoreHandle_t sem = xSemaphoreCreateBinary ();
173+
174+ if (task_ctx == NULL || sem == NULL ) {
175+ ret = ESP_ERR_NO_MEM ;
176+ goto no_task ;
128177 }
129178
130- task_ctx -> awaiting_handle = xTaskGetCurrentTaskHandle (); // Save parent task handle
131- task_ctx -> handle = NULL ; // TinyUSB task is not started
179+ task_ctx -> start_stop_sem = sem ; // TunyUSB Device task start stop semaphore
132180 task_ctx -> rhport = port ; // Peripheral port number
133181 task_ctx -> rhport_init .role = TUSB_ROLE_DEVICE ; // Role selection: esp_tinyusb is always a device
134182 task_ctx -> rhport_init .speed = (port == TINYUSB_PORT_FULL_SPEED_0 ) ? TUSB_SPEED_FULL : TUSB_SPEED_HIGH ; // Speed selection
135183 task_ctx -> desc_cfg = desc_cfg ;
184+ task_ctx -> blocking_timeout_ms = config -> blocking_timeout_ms ;
136185
137186 TaskHandle_t task_hdl = NULL ;
138187 ESP_LOGD (TAG , "Creating TinyUSB main task on CPU%d" , config -> xCoreID );
@@ -147,19 +196,32 @@ esp_err_t tinyusb_task_start(tinyusb_port_t port, const tinyusb_task_config_t *c
147196 if (task_hdl == NULL ) {
148197 ESP_LOGE (TAG , "Create TinyUSB main task failed" );
149198 ret = ESP_ERR_NOT_FINISHED ;
150- goto err ;
199+ goto no_task ;
151200 }
152201
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 ) {
202+ // Wait for "start" semophore with timeout
203+ if (xSemaphoreTake ( task_ctx -> start_stop_sem , pdMS_TO_TICKS (TINYUSB_TASK_SEM_TAKE_TIMEOUT_MS )) != pdTRUE ) {
155204 ESP_LOGE (TAG , "Task wasn't able to start TinyUSB stack" );
156205 ret = ESP_ERR_TIMEOUT ;
157206 goto err ;
158207 }
159208
209+ // Check the global task context pointer and pending state
210+ TINYUSB_TASK_ENTER_CRITICAL ();
211+ TINYUSB_TASK_CHECK_FROM_CRIT (!_task_is_pending , ESP_ERR_INVALID_STATE );
212+ TINYUSB_TASK_CHECK_FROM_CRIT (p_tusb_task_ctx == task_ctx , ESP_ERR_NOT_FOUND );
213+ TINYUSB_TASK_EXIT_CRITICAL ();
214+
160215 return ESP_OK ;
161216
217+ no_task :
218+ TINYUSB_TASK_ENTER_CRITICAL ();
219+ _task_is_pending = false; // Clear pending flag
220+ TINYUSB_TASK_EXIT_CRITICAL ();
162221err :
222+ if (sem ) {
223+ vSemaphoreDelete (sem );
224+ }
163225 heap_caps_free (task_ctx );
164226 return ret ;
165227}
@@ -168,20 +230,43 @@ esp_err_t tinyusb_task_stop(void)
168230{
169231 TINYUSB_TASK_ENTER_CRITICAL ();
170232 TINYUSB_TASK_CHECK_FROM_CRIT (p_tusb_task_ctx != NULL , ESP_ERR_INVALID_STATE );
233+ TINYUSB_TASK_CHECK_FROM_CRIT (!_task_is_pending , ESP_ERR_INVALID_STATE ); // Task shouldn't be pending
171234 tinyusb_task_ctx_t * task_ctx = p_tusb_task_ctx ;
172- p_tusb_task_ctx = NULL ;
173- _task_is_running = false;
235+ _task_is_pending = true; // Task is pending to stop
174236 TINYUSB_TASK_EXIT_CRITICAL ();
175237
176- if (task_ctx -> handle != NULL ) {
238+ if (task_ctx -> blocking_timeout_ms != UINT32_MAX ) {
239+ // Request TinyUSB task to stop
240+ if (xTaskNotify (task_ctx -> handle , TASK_NOTIF_STOP_BIT , eSetBits ) != pdPASS ) {
241+ ESP_LOGE (TAG , "Unable to notify TinyUSB task to stop" );
242+ return ESP_ERR_INVALID_STATE ;
243+ }
244+ // Wait for "stop" semaphore with timeout
245+ if (xSemaphoreTake (task_ctx -> start_stop_sem , pdMS_TO_TICKS (TINYUSB_TASK_SEM_TAKE_TIMEOUT_MS )) != pdTRUE ) {
246+ ESP_LOGE (TAG , "Timeout while TinyUSB task stop" );
247+ return ESP_ERR_TIMEOUT ;
248+ }
249+ // Check the global task context pointer and pending state
250+ TINYUSB_TASK_ENTER_CRITICAL ();
251+ TINYUSB_TASK_CHECK_FROM_CRIT (!_task_is_pending , ESP_ERR_INVALID_STATE );
252+ TINYUSB_TASK_CHECK_FROM_CRIT (p_tusb_task_ctx == NULL , ESP_ERR_INVALID_STATE );
253+ TINYUSB_TASK_EXIT_CRITICAL ();
254+ } else {
255+ // TinyUSB Task is in forever loop, delete the task the old way
177256 vTaskDelete (task_ctx -> handle );
178257 task_ctx -> handle = NULL ;
258+ // Free descriptors
259+ tinyusb_descriptors_free ();
260+ // Deinit TinyUSB Stask
261+ ESP_RETURN_ON_FALSE (tusb_deinit (task_ctx -> rhport ), ESP_ERR_INVALID_STATE , TAG , "Unable to deinit TinyUSB stack" );
262+ // Clean global context and pending state
263+ TINYUSB_TASK_ENTER_CRITICAL ();
264+ _task_is_pending = false;
265+ p_tusb_task_ctx = NULL ;
266+ TINYUSB_TASK_EXIT_CRITICAL ();
179267 }
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
268+
269+ vSemaphoreDelete (task_ctx -> start_stop_sem );
185270 heap_caps_free (task_ctx );
186271 return ESP_OK ;
187272}
0 commit comments