@@ -91,6 +91,7 @@ typedef enum {
9191 HID_INTERFACE_STATE_READY , /**< HID Interface opened and ready to start transfer */
9292 HID_INTERFACE_STATE_ACTIVE , /**< HID Interface is in use */
9393 HID_INTERFACE_STATE_WAIT_USER_DELETION , /**< HID Interface wait user to be removed */
94+ HID_INTERFACE_STATE_SUSPENDED , /**< HID Interface (and the whole device) is suspended */
9495 HID_INTERFACE_STATE_MAX
9596} hid_iface_state_t ;
9697
@@ -111,6 +112,7 @@ typedef struct hid_interface {
111112 hid_host_interface_event_cb_t user_cb ; /**< Interface application callback */
112113 void * user_cb_arg ; /**< Interface application callback arg */
113114 hid_iface_state_t state ; /**< Interface state */
115+ hid_iface_state_t last_state ; /**< Interface last state before entering suspended mode */
114116} hid_iface_t ;
115117
116118/**
@@ -550,6 +552,155 @@ static esp_err_t hid_host_device_disconnected(usb_device_handle_t dev_hdl)
550552 return ESP_OK ;
551553}
552554
555+ #ifdef HID_HOST_SUSPEND_RESUME_API_SUPPORTED
556+
557+ /**
558+ * @brief Suspend interface
559+ *
560+ * @note endpoints are already halted and flushed when a global suspend is issues by the USB Host lib
561+ * @param[in] iface HID interface handle
562+ * @param[in] stop_ep Stop (halt and flush) endpoint
563+ *
564+ * @return esp_err_t
565+ */
566+ static esp_err_t hid_host_suspend_interface (hid_iface_t * iface , bool stop_ep )
567+ {
568+ HID_RETURN_ON_INVALID_ARG (iface );
569+ HID_RETURN_ON_INVALID_ARG (iface -> parent );
570+
571+ HID_RETURN_ON_FALSE (is_interface_in_list (iface ),
572+ ESP_ERR_NOT_FOUND ,
573+ "Interface handle not found" );
574+
575+ HID_RETURN_ON_FALSE ((HID_INTERFACE_STATE_SUSPENDED != iface -> state ),
576+ ESP_ERR_INVALID_STATE ,
577+ "Interface wrong state" );
578+
579+ // EP is usually stopped by usb_host_lib, in case of global suspend, thus no need to Halt->Flush EP again
580+ if (stop_ep ) {
581+ HID_RETURN_ON_ERROR ( usb_host_endpoint_halt (iface -> parent -> dev_hdl , iface -> ep_in ),
582+ "Unable to HALT EP" );
583+ HID_RETURN_ON_ERROR ( usb_host_endpoint_flush (iface -> parent -> dev_hdl , iface -> ep_in ),
584+ "Unable to FLUSH EP" );
585+ // Don't clear EP, it must remain halted, when the device is in suspended state
586+ }
587+
588+ iface -> last_state = iface -> state ;
589+ iface -> state = HID_INTERFACE_STATE_SUSPENDED ;
590+
591+ return ESP_OK ;
592+ }
593+
594+ /**
595+ * @brief Resume interface
596+ *
597+ * @note endpoints are already cleared when a global resume is issues by the USB Host lib
598+ * @param[in] iface HID interface handle
599+ * @param[in] resume_ep Resume (clear) endpoint
600+ *
601+ * @return esp_err_t
602+ */
603+ static esp_err_t hid_host_resume_interface (hid_iface_t * iface , bool resume_ep )
604+ {
605+ HID_RETURN_ON_INVALID_ARG (iface );
606+ HID_RETURN_ON_INVALID_ARG (iface -> parent );
607+
608+ HID_RETURN_ON_FALSE (is_interface_in_list (iface ),
609+ ESP_ERR_NOT_FOUND ,
610+ "Interface handle not found" );
611+
612+ HID_RETURN_ON_FALSE ((HID_INTERFACE_STATE_SUSPENDED == iface -> state ),
613+ ESP_ERR_INVALID_STATE ,
614+ "Interface wrong state" );
615+
616+ // EP is usually cleared by usb_host_lib, in case of global suspend, thus no need to Clear an EP again
617+ if (resume_ep ) {
618+ usb_host_endpoint_clear (iface -> parent -> dev_hdl , iface -> ep_in );
619+ }
620+
621+ iface -> state = iface -> last_state ;
622+
623+ if (iface -> in_xfer == NULL ) {
624+ return ESP_OK ;
625+ }
626+
627+ // start data transfer
628+ HID_RETURN_ON_ERROR ( usb_host_transfer_submit (iface -> in_xfer ), "Unable to start data transfer" );
629+ return ESP_OK ;
630+ }
631+
632+ /**
633+ * @brief Suspend device
634+ *
635+ * Go through list, suspend all devices and deliver suspend events
636+ *
637+ * @param[in] dev_hdl USB Device handle
638+ *
639+ * @return esp_err_t
640+ */
641+ static esp_err_t hid_host_device_suspended (usb_device_handle_t dev_hdl )
642+ {
643+ hid_device_t * hid_device = get_hid_device_by_handle (dev_hdl );
644+ HID_RETURN_ON_INVALID_ARG (hid_device );
645+
646+ HID_ENTER_CRITICAL ();
647+ hid_iface_t * hid_iface_curr ;
648+ hid_iface_t * hid_iface_next ;
649+ // Go through list
650+ hid_iface_curr = STAILQ_FIRST (& s_hid_driver -> hid_ifaces_tailq );
651+ while (hid_iface_curr != NULL ) {
652+ hid_iface_next = STAILQ_NEXT (hid_iface_curr , tailq_entry );
653+ HID_EXIT_CRITICAL ();
654+
655+ if (hid_iface_curr -> parent && (hid_iface_curr -> parent -> dev_addr == hid_device -> dev_addr )) {
656+ hid_host_suspend_interface (hid_iface_curr , false);
657+ hid_host_user_interface_callback (hid_iface_curr , HID_HOST_INTERFACE_EVENT_SUSPENDED );
658+ }
659+ HID_ENTER_CRITICAL ();
660+ hid_iface_curr = hid_iface_next ;
661+ }
662+ HID_EXIT_CRITICAL ();
663+
664+ return ESP_OK ;
665+ }
666+
667+ /**
668+ * @brief Resume device
669+ *
670+ * Go through list, resume all devices and deliver resume events
671+ *
672+ * @param[in] dev_hdl USB Device handle
673+ *
674+ * @return esp_err_t
675+ */
676+ static esp_err_t hid_host_device_resumed (usb_device_handle_t dev_hdl )
677+ {
678+ hid_device_t * hid_device = get_hid_device_by_handle (dev_hdl );
679+ HID_RETURN_ON_INVALID_ARG (hid_device );
680+
681+ HID_ENTER_CRITICAL ();
682+ hid_iface_t * hid_iface_curr ;
683+ hid_iface_t * hid_iface_next ;
684+ // Go through list
685+ hid_iface_curr = STAILQ_FIRST (& s_hid_driver -> hid_ifaces_tailq );
686+ while (hid_iface_curr != NULL ) {
687+ hid_iface_next = STAILQ_NEXT (hid_iface_curr , tailq_entry );
688+ HID_EXIT_CRITICAL ();
689+
690+ if (hid_iface_curr -> parent && (hid_iface_curr -> parent -> dev_addr == hid_device -> dev_addr )) {
691+ hid_host_resume_interface (hid_iface_curr , false);
692+ hid_host_user_interface_callback (hid_iface_curr , HID_HOST_INTERFACE_EVENT_RESUMED );
693+ }
694+ HID_ENTER_CRITICAL ();
695+ hid_iface_curr = hid_iface_next ;
696+ }
697+ HID_EXIT_CRITICAL ();
698+
699+ return ESP_OK ;
700+ }
701+
702+ #endif // HID_HOST_SUSPEND_RESUME_API_SUPPORTED
703+
553704/**
554705 * @brief USB Host Client's event callback
555706 *
@@ -558,10 +709,28 @@ static esp_err_t hid_host_device_disconnected(usb_device_handle_t dev_hdl)
558709 */
559710static void client_event_cb (const usb_host_client_event_msg_t * event , void * arg )
560711{
561- if (event -> event == USB_HOST_CLIENT_EVENT_NEW_DEV ) {
712+ switch (event -> event ) {
713+ case USB_HOST_CLIENT_EVENT_NEW_DEV :
714+ ESP_LOGD (TAG , "New device connected" );
562715 hid_host_device_init_attempt (event -> new_dev .address );
563- } else if (event -> event == USB_HOST_CLIENT_EVENT_DEV_GONE ) {
716+ break ;
717+ case USB_HOST_CLIENT_EVENT_DEV_GONE :
718+ ESP_LOGD (TAG , "Device suddenly disconnected" );
564719 hid_host_device_disconnected (event -> dev_gone .dev_hdl );
720+ break ;
721+ #ifdef HID_HOST_SUSPEND_RESUME_API_SUPPORTED
722+ case USB_HOST_CLIENT_EVENT_DEV_SUSPENDED :
723+ ESP_LOGD (TAG , "Device suspended" );
724+ hid_host_device_suspended (event -> dev_suspend_resume .dev_hdl );
725+ break ;
726+ case USB_HOST_CLIENT_EVENT_DEV_RESUMED :
727+ ESP_LOGD (TAG , "Device resumed" );
728+ hid_host_device_resumed (event -> dev_suspend_resume .dev_hdl );
729+ break ;
730+ #endif // HID_HOST_SUSPEND_RESUME_API_SUPPORTED
731+ default :
732+ ESP_LOGW (TAG , "Unrecognized USB Host client event" );
733+ break ;
565734 }
566735}
567736
@@ -628,14 +797,19 @@ static esp_err_t hid_host_disable_interface(hid_iface_t *iface)
628797 ESP_ERR_NOT_FOUND ,
629798 "Interface handle not found" );
630799
631- HID_RETURN_ON_FALSE ((HID_INTERFACE_STATE_ACTIVE == iface -> state ),
800+ HID_RETURN_ON_FALSE ((HID_INTERFACE_STATE_ACTIVE == iface -> state ||
801+ HID_INTERFACE_STATE_SUSPENDED == iface -> state ),
632802 ESP_ERR_INVALID_STATE ,
633803 "Interface wrong state" );
634804
635- HID_RETURN_ON_ERROR ( usb_host_endpoint_halt (iface -> parent -> dev_hdl , iface -> ep_in ),
636- "Unable to HALT EP" );
637- HID_RETURN_ON_ERROR ( usb_host_endpoint_flush (iface -> parent -> dev_hdl , iface -> ep_in ),
638- "Unable to FLUSH EP" );
805+ if (HID_INTERFACE_STATE_ACTIVE == iface -> state ) {
806+ HID_RETURN_ON_ERROR ( usb_host_endpoint_halt (iface -> parent -> dev_hdl , iface -> ep_in ),
807+ "Unable to HALT EP" );
808+ HID_RETURN_ON_ERROR ( usb_host_endpoint_flush (iface -> parent -> dev_hdl , iface -> ep_in ),
809+ "Unable to FLUSH EP" );
810+ }
811+ // If interface state is suspended, the EP is already flushed and halted, only clear the EP
812+ // If suspended, may return ESP_ERR_INVALID_STATE
639813 usb_host_endpoint_clear (iface -> parent -> dev_hdl , iface -> ep_in );
640814
641815 iface -> state = HID_INTERFACE_STATE_READY ;
@@ -1204,15 +1378,15 @@ esp_err_t hid_host_device_close(hid_host_device_handle_t hid_dev_handle)
12041378 hid_iface -> dev_params .iface_num ,
12051379 hid_iface -> state );
12061380
1207- if (HID_INTERFACE_STATE_ACTIVE == hid_iface -> state ) {
1381+ if (HID_INTERFACE_STATE_ACTIVE == hid_iface -> state ||
1382+ HID_INTERFACE_STATE_SUSPENDED == hid_iface -> state ) {
12081383 HID_RETURN_ON_ERROR ( hid_host_disable_interface (hid_iface ),
12091384 "Unable to disable HID Interface" );
12101385 }
12111386
12121387 if (HID_INTERFACE_STATE_READY == hid_iface -> state ) {
12131388 HID_RETURN_ON_ERROR ( hid_host_interface_release_and_free_transfer (hid_iface ),
12141389 "Unable to release HID Interface" );
1215-
12161390 // If the device is closing by user before device detached we need to flush user callback here
12171391 free (hid_iface -> report_desc );
12181392 hid_iface -> report_desc = NULL ;
0 commit comments