@@ -40,11 +40,13 @@ typedef struct {
4040 tusb_rhport_init_t rhport_init ; /*!< USB Device RH port initialization configuration pointer */
4141 const tinyusb_desc_config_t * desc_cfg ; /*!< USB Device descriptors configuration pointer */
4242 // Task related
43- TaskHandle_t handle ; /*!< Task handle */
44- volatile TaskHandle_t awaiting_handle ; /*!< Task handle, waiting to be notified after successful start of TinyUSB stack */
43+ TaskHandle_t handle ; /*!< TinyUSB Device task handle */
44+ SemaphoreHandle_t start_stop_sem ; /*!< Semaphore to start or stop the task */
4545} tinyusb_task_ctx_t ;
4646
47- static bool _task_is_running = false; // Locking flag for the task, access only from the critical section
47+ #define TASK_NOTIF_STOP_BIT (1u << 0)
48+
49+ static bool _task_is_pending = false; // Locking flag for the task, access only from the critical section
4850static tinyusb_task_ctx_t * p_tusb_task_ctx = NULL ; // TinyUSB task context
4951
5052/**
@@ -53,10 +55,11 @@ static tinyusb_task_ctx_t *p_tusb_task_ctx = NULL; // TinyUSB task context
5355static void tinyusb_device_task (void * arg )
5456{
5557 tinyusb_task_ctx_t * task_ctx = (tinyusb_task_ctx_t * )arg ;
58+ bool pending_stop = false;
5659
5760 // Sanity check
5861 assert (task_ctx != NULL );
59- assert (task_ctx -> awaiting_handle != NULL );
62+ assert (task_ctx -> start_stop_sem != NULL );
6063
6164 ESP_LOGD (TAG , "TinyUSB task started" );
6265
@@ -74,22 +77,55 @@ static void tinyusb_device_task(void *arg)
7477 }
7578
7679 TINYUSB_TASK_ENTER_CRITICAL ();
77- task_ctx -> handle = xTaskGetCurrentTaskHandle (); // Save task handle
78- p_tusb_task_ctx = task_ctx ; // Save global task context pointer
80+ _task_is_pending = false;
81+ task_ctx -> handle = xTaskGetCurrentTaskHandle (); // Save task handle
82+ p_tusb_task_ctx = task_ctx ; // Save global task context pointer
7983 TINYUSB_TASK_EXIT_CRITICAL ();
8084
81- xTaskNotifyGive (task_ctx -> awaiting_handle ); // Notify parent task that TinyUSB stack was started successfully
85+ // Notify parent task that TinyUSB stack was started successfully
86+ if (xSemaphoreGive (task_ctx -> start_stop_sem ) != pdTRUE ) {
87+ // Fail only if semaphore queue is full, should never happen
88+ ESP_LOGE (TAG , "Unable to notify that TinyUSB stack was started" );
89+ goto desc_free ;
90+ }
91+
92+ while (1 ) {
93+ // Non-blocking check to stop the task
94+ uint32_t notif = 0 ;
95+ if (xTaskNotifyWait (
96+ 0 , // don't clear any bits on entry
97+ TASK_NOTIF_STOP_BIT , // clear STOP bit on exit if it was set
98+ & notif ,
99+ 0 // no wait; just poll the bit
100+ ) == pdTRUE && (notif & TASK_NOTIF_STOP_BIT )) {
101+ // Stop requested
102+ pending_stop = true;
103+ break ;
104+ }
105+ // TinyUSB Device task
106+ tud_task_ext (2000 /* TODO: make dynamically configurable */ , false);
107+ }
108+
109+ ESP_LOGD (TAG , "TinyUSB task is stopping" );
82110
83- while (1 ) { // RTOS forever loop
84- tud_task ();
111+ if (!tusb_rhport_teardown (task_ctx -> rhport )) {
112+ ESP_LOGE (TAG , "Unable to teardown TinyUSB stack" );
113+ // What to do if teardown failed?
114+ return ;
85115 }
86116
87117desc_free :
88118 tinyusb_descriptors_free ();
89119del :
90120 TINYUSB_TASK_ENTER_CRITICAL ();
91- _task_is_running = false; // Task is not running anymore
121+ _task_is_pending = false; // Task is not running anymore
122+ p_tusb_task_ctx = NULL ; // Clear global task context pointer
92123 TINYUSB_TASK_EXIT_CRITICAL ();
124+
125+ if (pending_stop ) {
126+ // Notify that TinyUSB stack was stopped
127+ xSemaphoreGive (task_ctx -> start_stop_sem );
128+ }
93129 vTaskDelete (NULL );
94130 // No return needed here: vTaskDelete(NULL) does not return
95131}
@@ -113,8 +149,8 @@ esp_err_t tinyusb_task_start(tinyusb_port_t port, const tinyusb_task_config_t *c
113149
114150 TINYUSB_TASK_ENTER_CRITICAL ();
115151 TINYUSB_TASK_CHECK_FROM_CRIT (p_tusb_task_ctx == NULL , ESP_ERR_INVALID_STATE ); // Task shouldn't started
116- TINYUSB_TASK_CHECK_FROM_CRIT (!_task_is_running , ESP_ERR_INVALID_STATE ); // Task shouldn't be running
117- _task_is_running = true; // Task is running flag, will be cleared in task in case of the error
152+ TINYUSB_TASK_CHECK_FROM_CRIT (!_task_is_pending , ESP_ERR_INVALID_STATE ); // Task shouldn't be pending
153+ _task_is_pending = true; // Task is running flag, will be cleared in task in case of the error
118154 TINYUSB_TASK_EXIT_CRITICAL ();
119155
120156 esp_err_t ret ;
@@ -123,8 +159,13 @@ esp_err_t tinyusb_task_start(tinyusb_port_t port, const tinyusb_task_config_t *c
123159 return ESP_ERR_NO_MEM ;
124160 }
125161
126- task_ctx -> awaiting_handle = xTaskGetCurrentTaskHandle (); // Save parent task handle
127- task_ctx -> handle = NULL ; // TinyUSB task is not started
162+ SemaphoreHandle_t sem = xSemaphoreCreateBinary ();
163+ if (sem == NULL ) {
164+ ret = ESP_ERR_NO_MEM ;
165+ goto err ;
166+ }
167+
168+ task_ctx -> start_stop_sem = sem ; // TunyUSB Device task start stop semaphore
128169 task_ctx -> rhport = port ; // Peripheral port number
129170 task_ctx -> rhport_init .role = TUSB_ROLE_DEVICE ; // Role selection: esp_tinyusb is always a device
130171 task_ctx -> rhport_init .speed = (port == TINYUSB_PORT_FULL_SPEED_0 ) ? TUSB_SPEED_FULL : TUSB_SPEED_HIGH ; // Speed selection
@@ -147,7 +188,7 @@ esp_err_t tinyusb_task_start(tinyusb_port_t port, const tinyusb_task_config_t *c
147188 }
148189
149190 // Wait until the Task notify that port is active, 5 sec is more than enough
150- if (ulTaskNotifyTake ( pdTRUE , pdMS_TO_TICKS (5000 )) == 0 ) {
191+ if (xSemaphoreTake ( task_ctx -> start_stop_sem , pdMS_TO_TICKS (5000 )) != pdTRUE ) {
151192 ESP_LOGE (TAG , "Task wasn't able to start TinyUSB stack" );
152193 ret = ESP_ERR_TIMEOUT ;
153194 goto err ;
@@ -156,6 +197,9 @@ esp_err_t tinyusb_task_start(tinyusb_port_t port, const tinyusb_task_config_t *c
156197 return ESP_OK ;
157198
158199err :
200+ if (sem ) {
201+ vSemaphoreDelete (sem );
202+ }
159203 heap_caps_free (task_ctx );
160204 return ret ;
161205}
@@ -164,20 +208,25 @@ esp_err_t tinyusb_task_stop(void)
164208{
165209 TINYUSB_TASK_ENTER_CRITICAL ();
166210 TINYUSB_TASK_CHECK_FROM_CRIT (p_tusb_task_ctx != NULL , ESP_ERR_INVALID_STATE );
211+ TINYUSB_TASK_CHECK_FROM_CRIT (!_task_is_pending , ESP_ERR_INVALID_STATE ); // Task shouldn't be pending
167212 tinyusb_task_ctx_t * task_ctx = p_tusb_task_ctx ;
168- p_tusb_task_ctx = NULL ;
169- _task_is_running = false;
213+ _task_is_pending = true; // Task is pending to stop
170214 TINYUSB_TASK_EXIT_CRITICAL ();
171215
172- if (task_ctx -> handle != NULL ) {
173- vTaskDelete (task_ctx -> handle );
174- task_ctx -> handle = NULL ;
216+ /* Request TinyUSB task to stop */
217+ if (xTaskNotify (task_ctx -> handle , TASK_NOTIF_STOP_BIT , eSetBits ) != pdPASS ) {
218+ ESP_LOGE (TAG , "Unable to notify TinyUSB task to stop" );
219+ return ESP_ERR_INVALID_STATE ;
175220 }
176- // Free descriptors
177- tinyusb_descriptors_free ();
178- // Stop TinyUSB stack
179- ESP_RETURN_ON_FALSE (tusb_rhport_teardown (task_ctx -> rhport ), ESP_ERR_NOT_FINISHED , TAG , "Unable to teardown TinyUSB stack" );
180- // Cleanup
221+
222+ /* Wait for "stop" semaphore with timeout, usually 5 sec is enough */
223+ if (xSemaphoreTake (task_ctx -> start_stop_sem , pdMS_TO_TICKS (5000 )) != pdTRUE ) {
224+ ESP_LOGE (TAG , "Timeout while TinyUSB task stop" );
225+ return ESP_ERR_TIMEOUT ;
226+ }
227+
228+ /* Cleanup */
229+ vSemaphoreDelete (task_ctx -> start_stop_sem );
181230 heap_caps_free (task_ctx );
182231 return ESP_OK ;
183232}
0 commit comments