11/*
2- * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
2+ * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
33 *
44 * SPDX-License-Identifier: Apache-2.0
55 */
@@ -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
@@ -550,6 +551,146 @@ static esp_err_t hid_host_device_disconnected(usb_device_handle_t dev_hdl)
550551 return ESP_OK ;
551552}
552553
554+ #ifdef HID_HOST_SUSPEND_RESUME_API_SUPPORTED
555+
556+ /**
557+ * @brief Suspend interface
558+ *
559+ * @note endpoints are already halted and flushed when a global suspend is issues by the USB Host lib
560+ * @param[in] iface HID interface handle
561+ * @param[in] stop_ep Stop (halt and flush) endpoint
562+ *
563+ * @return esp_err_t
564+ */
565+ static esp_err_t hid_host_suspend_interface (hid_iface_t * iface , bool stop_ep )
566+ {
567+ HID_RETURN_ON_INVALID_ARG (iface );
568+ HID_RETURN_ON_INVALID_ARG (iface -> parent );
569+
570+ HID_RETURN_ON_FALSE (is_interface_in_list (iface ),
571+ ESP_ERR_NOT_FOUND ,
572+ "Interface handle not found" );
573+
574+ HID_RETURN_ON_FALSE ((HID_INTERFACE_STATE_ACTIVE == iface -> state ),
575+ ESP_ERR_INVALID_STATE ,
576+ "Interface wrong state" );
577+
578+ // EP is usually stopped by usb_host_lib, in case of global suspend, thus no need to Halt->Flush EP again
579+ if (stop_ep ) {
580+ HID_RETURN_ON_ERROR ( usb_host_endpoint_halt (iface -> parent -> dev_hdl , iface -> ep_in ),
581+ "Unable to HALT EP" );
582+ HID_RETURN_ON_ERROR ( usb_host_endpoint_flush (iface -> parent -> dev_hdl , iface -> ep_in ),
583+ "Unable to FLUSH EP" );
584+ // Don't clear EP, it must remain halted, when the device is in suspended state
585+ }
586+
587+ iface -> state = HID_INTERFACE_STATE_SUSPENDED ;
588+
589+ return ESP_OK ;
590+ }
591+
592+ /**
593+ * @brief Resume interface
594+ *
595+ * @note endpoints are already cleared when a global resume is issues by the USB Host lib
596+ * @param[in] iface HID interface handle
597+ * @param[in] resume_ep Resume (clear) endpoint
598+ *
599+ * @return esp_err_t
600+ */
601+ static esp_err_t hid_host_resume_interface (hid_iface_t * iface , bool resume_ep )
602+ {
603+ HID_RETURN_ON_INVALID_ARG (iface );
604+ HID_RETURN_ON_INVALID_ARG (iface -> in_xfer );
605+ HID_RETURN_ON_INVALID_ARG (iface -> parent );
606+
607+ HID_RETURN_ON_FALSE (is_interface_in_list (iface ),
608+ ESP_ERR_NOT_FOUND ,
609+ "Interface handle not found" );
610+
611+ HID_RETURN_ON_FALSE ((HID_INTERFACE_STATE_SUSPENDED == iface -> state ),
612+ ESP_ERR_INVALID_STATE ,
613+ "Interface wrong state" );
614+
615+ // EP is usually cleared by usb_host_lib, in case of global suspend, thus no need to Clear an EP again
616+ if (resume_ep ) {
617+ usb_host_endpoint_clear (iface -> parent -> dev_hdl , iface -> ep_in );
618+ }
619+
620+ iface -> state = HID_INTERFACE_STATE_ACTIVE ;
621+
622+ // start data transfer
623+ return usb_host_transfer_submit (iface -> in_xfer );
624+ }
625+
626+ /**
627+ * @brief Suspend device
628+ *
629+ * @param[in] dev_hdl USB Device handle
630+ *
631+ * @return esp_err_t
632+ */
633+ static esp_err_t hid_host_device_suspended (usb_device_handle_t dev_hdl )
634+ {
635+ hid_device_t * hid_device = get_hid_device_by_handle (dev_hdl );
636+ HID_RETURN_ON_INVALID_ARG (hid_device );
637+
638+ HID_ENTER_CRITICAL ();
639+ hid_iface_t * hid_iface_curr ;
640+ hid_iface_t * hid_iface_next ;
641+ // Go through list
642+ hid_iface_curr = STAILQ_FIRST (& s_hid_driver -> hid_ifaces_tailq );
643+ while (hid_iface_curr != NULL ) {
644+ hid_iface_next = STAILQ_NEXT (hid_iface_curr , tailq_entry );
645+ HID_EXIT_CRITICAL ();
646+
647+ if (hid_iface_curr -> parent && (hid_iface_curr -> parent -> dev_addr == hid_device -> dev_addr )) {
648+ hid_host_suspend_interface (hid_iface_curr , false);
649+ hid_host_user_device_callback (hid_iface_curr , HID_HOST_DRIVER_EVENT_SUSPENDED );
650+ }
651+ HID_ENTER_CRITICAL ();
652+ hid_iface_curr = hid_iface_next ;
653+ }
654+ HID_EXIT_CRITICAL ();
655+
656+ return ESP_OK ;
657+ }
658+
659+ /**
660+ * @brief Resume device
661+ *
662+ * @param[in] dev_hdl USB Device handle
663+ *
664+ * @return esp_err_t
665+ */
666+ static esp_err_t hid_host_device_resumed (usb_device_handle_t dev_hdl )
667+ {
668+ hid_device_t * hid_device = get_hid_device_by_handle (dev_hdl );
669+ HID_RETURN_ON_INVALID_ARG (hid_device );
670+
671+ HID_ENTER_CRITICAL ();
672+ hid_iface_t * hid_iface_curr ;
673+ hid_iface_t * hid_iface_next ;
674+ // Go through list
675+ hid_iface_curr = STAILQ_FIRST (& s_hid_driver -> hid_ifaces_tailq );
676+ while (hid_iface_curr != NULL ) {
677+ hid_iface_next = STAILQ_NEXT (hid_iface_curr , tailq_entry );
678+ HID_EXIT_CRITICAL ();
679+
680+ if (hid_iface_curr -> parent && (hid_iface_curr -> parent -> dev_addr == hid_device -> dev_addr )) {
681+ hid_host_resume_interface (hid_iface_curr , false);
682+ hid_host_user_device_callback (hid_iface_curr , HID_HOST_DRIVER_EVENT_RESUMED );
683+ }
684+ HID_ENTER_CRITICAL ();
685+ hid_iface_curr = hid_iface_next ;
686+ }
687+ HID_EXIT_CRITICAL ();
688+
689+ return ESP_OK ;
690+ }
691+
692+ #endif // HID_HOST_SUSPEND_RESUME_API_SUPPORTED
693+
553694/**
554695 * @brief USB Host Client's event callback
555696 *
@@ -558,10 +699,28 @@ static esp_err_t hid_host_device_disconnected(usb_device_handle_t dev_hdl)
558699 */
559700static void client_event_cb (const usb_host_client_event_msg_t * event , void * arg )
560701{
561- if (event -> event == USB_HOST_CLIENT_EVENT_NEW_DEV ) {
702+ switch (event -> event ) {
703+ case USB_HOST_CLIENT_EVENT_NEW_DEV :
704+ ESP_LOGD (TAG , "New device connected" );
562705 hid_host_device_init_attempt (event -> new_dev .address );
563- } else if (event -> event == USB_HOST_CLIENT_EVENT_DEV_GONE ) {
706+ break ;
707+ case USB_HOST_CLIENT_EVENT_DEV_GONE :
708+ ESP_LOGD (TAG , "Device suddenly disconnected" );
564709 hid_host_device_disconnected (event -> dev_gone .dev_hdl );
710+ break ;
711+ #ifdef HID_HOST_SUSPEND_RESUME_API_SUPPORTED
712+ case USB_HOST_CLIENT_EVENT_DEV_SUSPENDED :
713+ ESP_LOGD (TAG , "Device suspended" );
714+ hid_host_device_suspended (event -> dev_suspend_resume .dev_hdl );
715+ break ;
716+ case USB_HOST_CLIENT_EVENT_DEV_RESUMED :
717+ ESP_LOGD (TAG , "Device resumed" );
718+ hid_host_device_resumed (event -> dev_suspend_resume .dev_hdl );
719+ break ;
720+ #endif // HID_HOST_SUSPEND_RESUME_API_SUPPORTED
721+ default :
722+ ESP_LOGW (TAG , "Unrecognized USB Host client event" );
723+ break ;
565724 }
566725}
567726
@@ -628,14 +787,19 @@ static esp_err_t hid_host_disable_interface(hid_iface_t *iface)
628787 ESP_ERR_NOT_FOUND ,
629788 "Interface handle not found" );
630789
631- HID_RETURN_ON_FALSE ((HID_INTERFACE_STATE_ACTIVE == iface -> state ),
790+ HID_RETURN_ON_FALSE ((HID_INTERFACE_STATE_ACTIVE == iface -> state ||
791+ HID_INTERFACE_STATE_SUSPENDED == iface -> state ),
632792 ESP_ERR_INVALID_STATE ,
633793 "Interface wrong state" );
634794
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" );
795+ if (HID_INTERFACE_STATE_ACTIVE == iface -> state ) {
796+ HID_RETURN_ON_ERROR ( usb_host_endpoint_halt (iface -> parent -> dev_hdl , iface -> ep_in ),
797+ "Unable to HALT EP" );
798+ HID_RETURN_ON_ERROR ( usb_host_endpoint_flush (iface -> parent -> dev_hdl , iface -> ep_in ),
799+ "Unable to FLUSH EP" );
800+ }
801+ // If interface state is suspended, the EP is already flushed and halted, only clear the EP
802+ // If suspended, may return ESP_ERR_INVALID_STATE
639803 usb_host_endpoint_clear (iface -> parent -> dev_hdl , iface -> ep_in );
640804
641805 iface -> state = HID_INTERFACE_STATE_READY ;
@@ -1204,15 +1368,15 @@ esp_err_t hid_host_device_close(hid_host_device_handle_t hid_dev_handle)
12041368 hid_iface -> dev_params .iface_num ,
12051369 hid_iface -> state );
12061370
1207- if (HID_INTERFACE_STATE_ACTIVE == hid_iface -> state ) {
1371+ if (HID_INTERFACE_STATE_ACTIVE == hid_iface -> state ||
1372+ HID_INTERFACE_STATE_SUSPENDED == hid_iface -> state ) {
12081373 HID_RETURN_ON_ERROR ( hid_host_disable_interface (hid_iface ),
12091374 "Unable to disable HID Interface" );
12101375 }
12111376
12121377 if (HID_INTERFACE_STATE_READY == hid_iface -> state ) {
12131378 HID_RETURN_ON_ERROR ( hid_host_interface_release_and_free_transfer (hid_iface ),
12141379 "Unable to release HID Interface" );
1215-
12161380 // If the device is closing by user before device detached we need to flush user callback here
12171381 free (hid_iface -> report_desc );
12181382 hid_iface -> report_desc = NULL ;
0 commit comments