@@ -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,150 @@ 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+ return usb_host_transfer_submit (iface -> in_xfer );
629+ }
630+
631+ /**
632+ * @brief Suspend device
633+ *
634+ * @param[in] dev_hdl USB Device handle
635+ *
636+ * @return esp_err_t
637+ */
638+ static esp_err_t hid_host_device_suspended (usb_device_handle_t dev_hdl )
639+ {
640+ hid_device_t * hid_device = get_hid_device_by_handle (dev_hdl );
641+ HID_RETURN_ON_INVALID_ARG (hid_device );
642+
643+ HID_ENTER_CRITICAL ();
644+ hid_iface_t * hid_iface_curr ;
645+ hid_iface_t * hid_iface_next ;
646+ // Go through list
647+ hid_iface_curr = STAILQ_FIRST (& s_hid_driver -> hid_ifaces_tailq );
648+ while (hid_iface_curr != NULL ) {
649+ hid_iface_next = STAILQ_NEXT (hid_iface_curr , tailq_entry );
650+ HID_EXIT_CRITICAL ();
651+
652+ if (hid_iface_curr -> parent && (hid_iface_curr -> parent -> dev_addr == hid_device -> dev_addr )) {
653+ hid_host_suspend_interface (hid_iface_curr , false);
654+ hid_host_user_interface_callback (hid_iface_curr , HID_HOST_INTERFACE_EVENT_SUSPENDED );
655+ }
656+ HID_ENTER_CRITICAL ();
657+ hid_iface_curr = hid_iface_next ;
658+ }
659+ HID_EXIT_CRITICAL ();
660+
661+ return ESP_OK ;
662+ }
663+
664+ /**
665+ * @brief Resume device
666+ *
667+ * @param[in] dev_hdl USB Device handle
668+ *
669+ * @return esp_err_t
670+ */
671+ static esp_err_t hid_host_device_resumed (usb_device_handle_t dev_hdl )
672+ {
673+ hid_device_t * hid_device = get_hid_device_by_handle (dev_hdl );
674+ HID_RETURN_ON_INVALID_ARG (hid_device );
675+
676+ HID_ENTER_CRITICAL ();
677+ hid_iface_t * hid_iface_curr ;
678+ hid_iface_t * hid_iface_next ;
679+ // Go through list
680+ hid_iface_curr = STAILQ_FIRST (& s_hid_driver -> hid_ifaces_tailq );
681+ while (hid_iface_curr != NULL ) {
682+ hid_iface_next = STAILQ_NEXT (hid_iface_curr , tailq_entry );
683+ HID_EXIT_CRITICAL ();
684+
685+ if (hid_iface_curr -> parent && (hid_iface_curr -> parent -> dev_addr == hid_device -> dev_addr )) {
686+ hid_host_resume_interface (hid_iface_curr , false);
687+ hid_host_user_interface_callback (hid_iface_curr , HID_HOST_INTERFACE_EVENT_RESUMED );
688+ }
689+ HID_ENTER_CRITICAL ();
690+ hid_iface_curr = hid_iface_next ;
691+ }
692+ HID_EXIT_CRITICAL ();
693+
694+ return ESP_OK ;
695+ }
696+
697+ #endif // HID_HOST_SUSPEND_RESUME_API_SUPPORTED
698+
553699/**
554700 * @brief USB Host Client's event callback
555701 *
@@ -558,10 +704,28 @@ static esp_err_t hid_host_device_disconnected(usb_device_handle_t dev_hdl)
558704 */
559705static void client_event_cb (const usb_host_client_event_msg_t * event , void * arg )
560706{
561- if (event -> event == USB_HOST_CLIENT_EVENT_NEW_DEV ) {
707+ switch (event -> event ) {
708+ case USB_HOST_CLIENT_EVENT_NEW_DEV :
709+ ESP_LOGD (TAG , "New device connected" );
562710 hid_host_device_init_attempt (event -> new_dev .address );
563- } else if (event -> event == USB_HOST_CLIENT_EVENT_DEV_GONE ) {
711+ break ;
712+ case USB_HOST_CLIENT_EVENT_DEV_GONE :
713+ ESP_LOGD (TAG , "Device suddenly disconnected" );
564714 hid_host_device_disconnected (event -> dev_gone .dev_hdl );
715+ break ;
716+ #ifdef HID_HOST_SUSPEND_RESUME_API_SUPPORTED
717+ case USB_HOST_CLIENT_EVENT_DEV_SUSPENDED :
718+ ESP_LOGD (TAG , "Device suspended" );
719+ hid_host_device_suspended (event -> dev_suspend_resume .dev_hdl );
720+ break ;
721+ case USB_HOST_CLIENT_EVENT_DEV_RESUMED :
722+ ESP_LOGD (TAG , "Device resumed" );
723+ hid_host_device_resumed (event -> dev_suspend_resume .dev_hdl );
724+ break ;
725+ #endif // HID_HOST_SUSPEND_RESUME_API_SUPPORTED
726+ default :
727+ ESP_LOGW (TAG , "Unrecognized USB Host client event" );
728+ break ;
565729 }
566730}
567731
@@ -628,14 +792,19 @@ static esp_err_t hid_host_disable_interface(hid_iface_t *iface)
628792 ESP_ERR_NOT_FOUND ,
629793 "Interface handle not found" );
630794
631- HID_RETURN_ON_FALSE ((HID_INTERFACE_STATE_ACTIVE == iface -> state ),
795+ HID_RETURN_ON_FALSE ((HID_INTERFACE_STATE_ACTIVE == iface -> state ||
796+ HID_INTERFACE_STATE_SUSPENDED == iface -> state ),
632797 ESP_ERR_INVALID_STATE ,
633798 "Interface wrong state" );
634799
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" );
800+ if (HID_INTERFACE_STATE_ACTIVE == iface -> state ) {
801+ HID_RETURN_ON_ERROR ( usb_host_endpoint_halt (iface -> parent -> dev_hdl , iface -> ep_in ),
802+ "Unable to HALT EP" );
803+ HID_RETURN_ON_ERROR ( usb_host_endpoint_flush (iface -> parent -> dev_hdl , iface -> ep_in ),
804+ "Unable to FLUSH EP" );
805+ }
806+ // If interface state is suspended, the EP is already flushed and halted, only clear the EP
807+ // If suspended, may return ESP_ERR_INVALID_STATE
639808 usb_host_endpoint_clear (iface -> parent -> dev_hdl , iface -> ep_in );
640809
641810 iface -> state = HID_INTERFACE_STATE_READY ;
@@ -1204,15 +1373,15 @@ esp_err_t hid_host_device_close(hid_host_device_handle_t hid_dev_handle)
12041373 hid_iface -> dev_params .iface_num ,
12051374 hid_iface -> state );
12061375
1207- if (HID_INTERFACE_STATE_ACTIVE == hid_iface -> state ) {
1376+ if (HID_INTERFACE_STATE_ACTIVE == hid_iface -> state ||
1377+ HID_INTERFACE_STATE_SUSPENDED == hid_iface -> state ) {
12081378 HID_RETURN_ON_ERROR ( hid_host_disable_interface (hid_iface ),
12091379 "Unable to disable HID Interface" );
12101380 }
12111381
12121382 if (HID_INTERFACE_STATE_READY == hid_iface -> state ) {
12131383 HID_RETURN_ON_ERROR ( hid_host_interface_release_and_free_transfer (hid_iface ),
12141384 "Unable to release HID Interface" );
1215-
12161385 // If the device is closing by user before device detached we need to flush user callback here
12171386 free (hid_iface -> report_desc );
12181387 hid_iface -> report_desc = NULL ;
0 commit comments