@@ -96,6 +96,7 @@ typedef enum {
9696 HID_INTERFACE_STATE_READY , /**< HID Interface opened and ready to start transfer */
9797 HID_INTERFACE_STATE_ACTIVE , /**< HID Interface is in use */
9898 HID_INTERFACE_STATE_WAIT_USER_DELETION , /**< HID Interface wait user to be removed */
99+ HID_INTERFACE_STATE_SUSPENDED , /**< HID Interface (and the whole device) is suspended */
99100 HID_INTERFACE_STATE_MAX
100101} hid_iface_state_t ;
101102
@@ -116,6 +117,7 @@ typedef struct hid_interface {
116117 hid_host_interface_event_cb_t user_cb ; /**< Interface application callback */
117118 void * user_cb_arg ; /**< Interface application callback arg */
118119 hid_iface_state_t state ; /**< Interface state */
120+ hid_iface_state_t last_state ; /**< Interface last state before entering suspended mode */
119121} hid_iface_t ;
120122
121123/**
@@ -555,6 +557,179 @@ static esp_err_t hid_host_device_disconnected(usb_device_handle_t dev_hdl)
555557 return ESP_OK ;
556558}
557559
560+ #ifdef HID_HOST_SUSPEND_RESUME_API_SUPPORTED
561+
562+ /**
563+ * @brief Suspend interface
564+ *
565+ * @note endpoints are already halted and flushed when a global suspend is issues by the USB Host lib
566+ * @param[in] iface HID interface handle
567+ * @param[in] stop_ep Stop (halt and flush) endpoint
568+ *
569+ * @return esp_err_t
570+ */
571+ static esp_err_t hid_host_suspend_interface (hid_iface_t * iface , bool stop_ep )
572+ {
573+ HID_RETURN_ON_INVALID_ARG (iface );
574+ HID_RETURN_ON_INVALID_ARG (iface -> parent );
575+
576+ HID_RETURN_ON_FALSE (is_interface_in_list (iface ),
577+ ESP_ERR_NOT_FOUND ,
578+ "Interface handle not found" );
579+
580+ HID_RETURN_ON_FALSE ((HID_INTERFACE_STATE_SUSPENDED != iface -> state ),
581+ ESP_ERR_INVALID_STATE ,
582+ "Interface wrong state" );
583+
584+ // EP is usually stopped by usb_host_lib, in case of global suspend, thus no need to Halt->Flush EP again
585+ if (stop_ep ) {
586+ HID_RETURN_ON_ERROR ( usb_host_endpoint_halt (iface -> parent -> dev_hdl , iface -> ep_in ),
587+ "Unable to HALT EP" );
588+ HID_RETURN_ON_ERROR ( usb_host_endpoint_flush (iface -> parent -> dev_hdl , iface -> ep_in ),
589+ "Unable to FLUSH EP" );
590+ // Don't clear EP, it must remain halted, when the device is in suspended state
591+ }
592+
593+ iface -> last_state = iface -> state ;
594+ iface -> state = HID_INTERFACE_STATE_SUSPENDED ;
595+
596+ return ESP_OK ;
597+ }
598+
599+ /**
600+ * @brief Resume interface
601+ *
602+ * @note endpoints are already cleared when a global resume is issues by the USB Host lib
603+ * @param[in] iface HID interface handle
604+ * @param[in] resume_ep Resume (clear) endpoint
605+ *
606+ * @return esp_err_t
607+ */
608+ static esp_err_t hid_host_resume_interface (hid_iface_t * iface , bool resume_ep )
609+ {
610+ HID_RETURN_ON_INVALID_ARG (iface );
611+ HID_RETURN_ON_INVALID_ARG (iface -> parent );
612+
613+ HID_RETURN_ON_FALSE (is_interface_in_list (iface ),
614+ ESP_ERR_NOT_FOUND ,
615+ "Interface handle not found" );
616+
617+ if (HID_INTERFACE_STATE_ACTIVE == iface -> state ) {
618+ // Interface already auto-resumed by hid_host_device_start(), return early and continue to resume event delivery
619+ return ESP_OK ;
620+ }
621+
622+ HID_RETURN_ON_FALSE ((HID_INTERFACE_STATE_SUSPENDED == iface -> state ),
623+ ESP_ERR_INVALID_STATE ,
624+ "Interface wrong state" );
625+
626+ // EP is usually cleared by usb_host_lib, in case of global suspend, thus no need to Clear an EP again
627+ if (resume_ep ) {
628+ usb_host_endpoint_clear (iface -> parent -> dev_hdl , iface -> ep_in );
629+ }
630+
631+ // Use the last device state before the device went to suspended state as the current state
632+ iface -> state = iface -> last_state ;
633+
634+ if (iface -> in_xfer == NULL ) {
635+ return ESP_OK ;
636+ }
637+
638+ // If the last state before the device went to suspended state was active state, start the data transfer
639+ if (iface -> last_state == HID_INTERFACE_STATE_ACTIVE ) {
640+ // start data transfer
641+ HID_RETURN_ON_ERROR ( usb_host_transfer_submit (iface -> in_xfer ), "Unable to start data transfer" );
642+ }
643+
644+ return ESP_OK ;
645+ }
646+
647+ /**
648+ * @brief Suspend device
649+ *
650+ * Go through list, suspend all devices and deliver suspend events
651+ *
652+ * @param[in] dev_hdl USB Device handle
653+ *
654+ * @return esp_err_t
655+ */
656+ static esp_err_t hid_host_device_suspended (usb_device_handle_t dev_hdl )
657+ {
658+ hid_device_t * hid_device = get_hid_device_by_handle (dev_hdl );
659+ HID_RETURN_ON_INVALID_ARG (hid_device );
660+
661+ HID_ENTER_CRITICAL ();
662+ hid_iface_t * hid_iface_curr ;
663+ hid_iface_t * hid_iface_next ;
664+ // Go through list
665+ hid_iface_curr = STAILQ_FIRST (& s_hid_driver -> hid_ifaces_tailq );
666+ while (hid_iface_curr != NULL ) {
667+ hid_iface_next = STAILQ_NEXT (hid_iface_curr , tailq_entry );
668+ HID_EXIT_CRITICAL ();
669+
670+ if (hid_iface_curr -> parent && (hid_iface_curr -> parent -> dev_addr == hid_device -> dev_addr )) {
671+ esp_err_t ret = hid_host_suspend_interface (hid_iface_curr , false);
672+
673+ // Make sure the device is connected and the interface is found otherwise don't deliver suspend event
674+ if (ret != ESP_ERR_NOT_FOUND ) {
675+
676+ // We will deliver the suspend event, if the hid_host_suspend_interface fails with other errors,
677+ // as the usb_host_lib has already suspended the root port anyway
678+ hid_host_user_interface_callback (hid_iface_curr , HID_HOST_INTERFACE_EVENT_SUSPENDED );
679+ }
680+ }
681+ HID_ENTER_CRITICAL ();
682+ hid_iface_curr = hid_iface_next ;
683+ }
684+ HID_EXIT_CRITICAL ();
685+
686+ return ESP_OK ;
687+ }
688+
689+ /**
690+ * @brief Resume device
691+ *
692+ * Go through list, resume all devices and deliver resume events
693+ *
694+ * @param[in] dev_hdl USB Device handle
695+ *
696+ * @return esp_err_t
697+ */
698+ static esp_err_t hid_host_device_resumed (usb_device_handle_t dev_hdl )
699+ {
700+ hid_device_t * hid_device = get_hid_device_by_handle (dev_hdl );
701+ HID_RETURN_ON_INVALID_ARG (hid_device );
702+
703+ HID_ENTER_CRITICAL ();
704+ hid_iface_t * hid_iface_curr ;
705+ hid_iface_t * hid_iface_next ;
706+ // Go through list
707+ hid_iface_curr = STAILQ_FIRST (& s_hid_driver -> hid_ifaces_tailq );
708+ while (hid_iface_curr != NULL ) {
709+ hid_iface_next = STAILQ_NEXT (hid_iface_curr , tailq_entry );
710+ HID_EXIT_CRITICAL ();
711+
712+ if (hid_iface_curr -> parent && (hid_iface_curr -> parent -> dev_addr == hid_device -> dev_addr )) {
713+ esp_err_t ret = hid_host_resume_interface (hid_iface_curr , false);
714+
715+ // Make sure the device is connected and the interface is found otherwise don't deliver resume event
716+ if (ret != ESP_ERR_NOT_FOUND ) {
717+
718+ // We will deliver the resume event, if the hid_host_resume_interface fails with other errors,
719+ // as the usb_host_lib has already resumed the root port anyway
720+ hid_host_user_interface_callback (hid_iface_curr , HID_HOST_INTERFACE_EVENT_RESUMED );
721+ }
722+ }
723+ HID_ENTER_CRITICAL ();
724+ hid_iface_curr = hid_iface_next ;
725+ }
726+ HID_EXIT_CRITICAL ();
727+
728+ return ESP_OK ;
729+ }
730+
731+ #endif // HID_HOST_SUSPEND_RESUME_API_SUPPORTED
732+
558733/**
559734 * @brief USB Host Client's event callback
560735 *
@@ -563,10 +738,28 @@ static esp_err_t hid_host_device_disconnected(usb_device_handle_t dev_hdl)
563738 */
564739static void client_event_cb (const usb_host_client_event_msg_t * event , void * arg )
565740{
566- if (event -> event == USB_HOST_CLIENT_EVENT_NEW_DEV ) {
741+ switch (event -> event ) {
742+ case USB_HOST_CLIENT_EVENT_NEW_DEV :
743+ ESP_LOGD (TAG , "New device connected" );
567744 hid_host_device_init_attempt (event -> new_dev .address );
568- } else if (event -> event == USB_HOST_CLIENT_EVENT_DEV_GONE ) {
745+ break ;
746+ case USB_HOST_CLIENT_EVENT_DEV_GONE :
747+ ESP_LOGD (TAG , "Device suddenly disconnected" );
569748 hid_host_device_disconnected (event -> dev_gone .dev_hdl );
749+ break ;
750+ #ifdef HID_HOST_SUSPEND_RESUME_API_SUPPORTED
751+ case USB_HOST_CLIENT_EVENT_DEV_SUSPENDED :
752+ ESP_LOGD (TAG , "Device suspended" );
753+ hid_host_device_suspended (event -> dev_suspend_resume .dev_hdl );
754+ break ;
755+ case USB_HOST_CLIENT_EVENT_DEV_RESUMED :
756+ ESP_LOGD (TAG , "Device resumed" );
757+ hid_host_device_resumed (event -> dev_suspend_resume .dev_hdl );
758+ break ;
759+ #endif // HID_HOST_SUSPEND_RESUME_API_SUPPORTED
760+ default :
761+ ESP_LOGW (TAG , "Unrecognized USB Host client event" );
762+ break ;
570763 }
571764}
572765
@@ -633,14 +826,19 @@ static esp_err_t hid_host_disable_interface(hid_iface_t *iface)
633826 ESP_ERR_NOT_FOUND ,
634827 "Interface handle not found" );
635828
636- HID_RETURN_ON_FALSE ((HID_INTERFACE_STATE_ACTIVE == iface -> state ),
829+ HID_RETURN_ON_FALSE ((HID_INTERFACE_STATE_ACTIVE == iface -> state ||
830+ HID_INTERFACE_STATE_SUSPENDED == iface -> state ),
637831 ESP_ERR_INVALID_STATE ,
638832 "Interface wrong state" );
639833
640- HID_RETURN_ON_ERROR ( usb_host_endpoint_halt (iface -> parent -> dev_hdl , iface -> ep_in ),
641- "Unable to HALT EP" );
642- HID_RETURN_ON_ERROR ( usb_host_endpoint_flush (iface -> parent -> dev_hdl , iface -> ep_in ),
643- "Unable to FLUSH EP" );
834+ if (HID_INTERFACE_STATE_ACTIVE == iface -> state ) {
835+ HID_RETURN_ON_ERROR ( usb_host_endpoint_halt (iface -> parent -> dev_hdl , iface -> ep_in ),
836+ "Unable to HALT EP" );
837+ HID_RETURN_ON_ERROR ( usb_host_endpoint_flush (iface -> parent -> dev_hdl , iface -> ep_in ),
838+ "Unable to FLUSH EP" );
839+ }
840+ // If interface state is suspended, the EP is already flushed and halted, only clear the EP
841+ // If suspended, may return ESP_ERR_INVALID_STATE
644842 usb_host_endpoint_clear (iface -> parent -> dev_hdl , iface -> ep_in );
645843
646844 iface -> state = HID_INTERFACE_STATE_READY ;
@@ -1220,15 +1418,15 @@ esp_err_t hid_host_device_close(hid_host_device_handle_t hid_dev_handle)
12201418 hid_iface -> dev_params .iface_num ,
12211419 hid_iface -> state );
12221420
1223- if (HID_INTERFACE_STATE_ACTIVE == hid_iface -> state ) {
1421+ if (HID_INTERFACE_STATE_ACTIVE == hid_iface -> state ||
1422+ HID_INTERFACE_STATE_SUSPENDED == hid_iface -> state ) {
12241423 HID_RETURN_ON_ERROR ( hid_host_disable_interface (hid_iface ),
12251424 "Unable to disable HID Interface" );
12261425 }
12271426
12281427 if (HID_INTERFACE_STATE_READY == hid_iface -> state ) {
12291428 HID_RETURN_ON_ERROR ( hid_host_interface_release_and_free_transfer (hid_iface ),
12301429 "Unable to release HID Interface" );
1231-
12321430 // If the device is closing by user before device detached we need to flush user callback here
12331431 free (hid_iface -> report_desc );
12341432 hid_iface -> report_desc = NULL ;
@@ -1329,7 +1527,7 @@ esp_err_t hid_host_device_start(hid_host_device_handle_t hid_dev_handle)
13291527 ESP_ERR_NOT_FOUND ,
13301528 "Interface handle not found" );
13311529
1332- HID_RETURN_ON_FALSE ((HID_INTERFACE_STATE_READY == iface -> state ),
1530+ HID_RETURN_ON_FALSE ((HID_INTERFACE_STATE_READY == iface -> state || HID_INTERFACE_STATE_SUSPENDED == iface -> state ),
13331531 ESP_ERR_INVALID_STATE ,
13341532 "Interface wrong state" );
13351533
@@ -1353,6 +1551,15 @@ esp_err_t hid_host_device_stop(hid_host_device_handle_t hid_dev_handle)
13531551
13541552 HID_RETURN_ON_INVALID_ARG (iface );
13551553
1554+ HID_RETURN_ON_FALSE (is_interface_in_list (iface ), ESP_ERR_NOT_FOUND , "Interface handle not found" );
1555+
1556+ if (iface -> state == HID_INTERFACE_STATE_SUSPENDED ) {
1557+ // If interface is suspended, mark the last state as READY,
1558+ // as if the interface was stopped before entering suspended state
1559+ iface -> last_state = HID_INTERFACE_STATE_READY ;
1560+ return ESP_OK ;
1561+ }
1562+
13561563 return hid_host_disable_interface (iface );
13571564}
13581565
0 commit comments