__interrupt void usb_general_interrupt() #endif { #if (USB_HOST_PIPE_INTERRUPT_TRANSFER == ENABLE) U8 i; U8 save_pipe_nb; #endif // ---------- DEVICE events management ----------------------------------- #if (USB_DEVICE_FEATURE == ENABLED) // - Device start of frame received if (Is_usb_sof() && Is_sof_interrupt_enabled()) { Usb_ack_sof(); Usb_sof_action(); } #ifdef WA_USB_SUSPEND_PERTUBATION // - Device Suspend event (no more USB activity detected) if (Is_usb_suspend() && Is_suspend_interrupt_enabled()) { usb_suspended=TRUE; Usb_ack_wake_up(); // clear wake up to detect next event Usb_send_event(EVT_USB_SUSPEND); Usb_ack_suspend(); Usb_enable_wake_up_interrupt(); Usb_disable_resume_interrupt(); Usb_freeze_clock(); Stop_pll(); Usb_suspend_action(); } // - Wake up event (USB activity detected): Used to resume if (Is_usb_wake_up() && Is_wake_up_interrupt_enabled()) { if(Is_pll_ready()==FALSE) { Pll_start_auto(); Wait_pll_ready(); } Usb_unfreeze_clock(); Usb_ack_wake_up(); if(usb_suspended) { Usb_enable_resume_interrupt(); Usb_enable_reset_interrupt(); while(Is_usb_wake_up()) { Usb_ack_wake_up(); } usb_delay_ms(2); if(Is_usb_sof() || Is_usb_resume() || Is_usb_reset() ) { Usb_disable_wake_up_interrupt(); Usb_wake_up_action(); Usb_send_event(EVT_USB_WAKE_UP); Usb_enable_suspend_interrupt(); Usb_enable_resume_interrupt(); Usb_enable_reset_interrupt(); } else // Workarround to make the USB enter power down mode again (spurious transcient detected on the USB lines) { Usb_ack_wake_up(); // clear wake up to detect next event Usb_send_event(EVT_USB_SUSPEND); Usb_enable_wake_up_interrupt(); Usb_disable_resume_interrupt(); Usb_freeze_clock(); Stop_pll(); Usb_suspend_action(); } } } // - Resume state bus detection if (Is_usb_resume() && Is_resume_interrupt_enabled()) { usb_suspended = FALSE; Usb_disable_wake_up_interrupt(); Usb_ack_resume(); Usb_disable_resume_interrupt(); Usb_resume_action(); Usb_send_event(EVT_USB_RESUME); } #else // - Device Suspend event (no more USB activity detected) if (Is_usb_suspend() && Is_suspend_interrupt_enabled()) { // Remote wake-up handler if ((remote_wakeup_feature == ENABLED) && (usb_configuration_nb != 0)) { Usb_disable_suspend_interrupt(); Usb_ack_wake_up(); Usb_enable_wake_up_interrupt(); Stop_pll(); Usb_freeze_clock(); Usb_suspend_action(); // After that user can execute "Usb_initiate_remote_wake_up()" to initiate a remote wake-up // Note that the suspend interrupt flag SUSPI must still be set to enable upstream resume // So the SUSPE enable bit must be cleared to avoid redundant interrupt // **************** // Please note also that is Vbus is lost during an upstream resume (Host disconnection), // the RMWKUP bit (used to initiate remote wake up and that is normally cleared by hardware when sent) // remains set after the event, so that a good way to handle this feature is : // Usb_unfreeze_clock(); // Usb_initiate_remote_wake_up(); // while (Is_usb_pending_remote_wake_up()) // { // if (Is_usb_vbus_low()) // { // // Emergency action (reset macro, etc.) if Vbus lost during resuming // break; // } // } // Usb_ack_remote_wake_up_start(); // **************** } else { // No remote wake-up supported Usb_ack_wake_up(); // clear wake up to detect next event Usb_send_event(EVT_USB_SUSPEND); Usb_ack_suspend(); // must be executed last (after Usb_suspend_action()) to allow upstream resume Usb_enable_wake_up_interrupt(); Usb_freeze_clock(); Stop_pll(); Usb_suspend_action(); } } // - Wake up event (USB activity detected): Used to resume if (Is_usb_wake_up() && Is_wake_up_interrupt_enabled()) { if(Is_pll_ready()==FALSE) { Pll_start_auto(); Wait_pll_ready(); } Usb_unfreeze_clock(); Usb_ack_wake_up(); Usb_disable_wake_up_interrupt(); Usb_wake_up_action(); Usb_send_event(EVT_USB_WAKE_UP); Usb_enable_suspend_interrupt(); } // - Resume state bus detection if (Is_usb_resume() && Is_resume_interrupt_enabled()) { Usb_disable_wake_up_interrupt(); Usb_ack_resume(); Usb_disable_resume_interrupt(); Usb_resume_action(); Usb_send_event(EVT_USB_RESUME); } #endif // - USB bus reset detection if (Is_usb_reset()&& Is_reset_interrupt_enabled()) { Usb_ack_reset(); usb_init_device(); Usb_reset_action(); Usb_send_event(EVT_USB_RESET); } #endif// End DEVICE FEATURE MODE // ---------- HOST events management ----------------------------------- #if (USB_HOST_FEATURE == ENABLED && USB_DEVICE_FEATURE == ENABLED) // - ID pin change detection if(Is_usb_id_transition()&&Is_usb_id_interrupt_enabled()) { if(Is_usb_id_device()) { g_usb_mode=USB_MODE_DEVICE;} else { g_usb_mode=USB_MODE_HOST;} Usb_ack_id_transition(); if( g_usb_mode != g_old_usb_mode) // Basic Debounce { if(Is_usb_id_device()) // Going to device mode { Usb_send_event(EVT_USB_DEVICE_FUNCTION); } else // Going to host mode { Usb_send_event(EVT_USB_HOST_FUNCTION); } Usb_id_transition_action(); LOG_STR_CODE(log_id_change); #if ( ID_PIN_CHANGE_GENERATE_RESET == ENABLE) // Hot ID transition generates wdt reset wdtdrv_enable(WDTO_16MS); while(1); #endif } } #endif #if (USB_HOST_FEATURE == ENABLED) // - The device has been disconnected if(Is_device_disconnection() && Is_host_device_disconnection_interrupt_enabled()) { host_disable_all_pipe(); Host_ack_device_disconnection(); device_state=DEVICE_DISCONNECTED; Usb_send_event(EVT_HOST_DISCONNECTION); init_usb_tree(); LOG_STR_CODE(log_device_disconnect); Host_device_disconnection_action(); } // - Device connection if(Is_device_connection() && Is_host_device_connection_interrupt_enabled()) { Host_ack_device_connection(); host_disable_all_pipe(); Host_device_connection_action(); } // - Host Start of frame has been sent if (Is_host_sof() && Is_host_sof_interrupt_enabled()) { Host_ack_sof(); Usb_send_event(EVT_HOST_SOF); private_sof_counter++; #if (USB_HUB_SUPPORT==ENABLE) hub_interrupt_sof++; #endif // delay timeout management for interrupt tranfer mode in host mode #if ((USB_HOST_PIPE_INTERRUPT_TRANSFER==ENABLE) && (TIMEOUT_DELAY_ENABLE==ENABLE)) if (private_sof_counter>=250) // Count 1/4 sec { private_sof_counter=0; for(i=0;i<MAX_EP_NB;i++) { if(it_pipe_str[i].enable==ENABLE) { save_pipe_nb=Host_get_selected_pipe(); Host_select_pipe(i); if((++it_pipe_str[i].timeout>TIMEOUT_DELAY) && (Host_get_pipe_type()!=TYPE_INTERRUPT)) { it_pipe_str[i].enable=DISABLE; it_pipe_str[i].status=PIPE_DELAY_TIMEOUT; Host_stop_pipe_interrupt(i); if (is_any_interrupt_pipe_active()==FALSE) // If no more transfer is armed { if (g_sav_int_sof_enable==FALSE) { Host_disable_sof_interrupt(); } } it_pipe_str[i].handle(PIPE_DELAY_TIMEOUT,it_pipe_str[i].nb_byte_processed); } Host_select_pipe(save_pipe_nb); } } } #endif // (USB_HOST_PIPE_INTERRUPT_TRANSFER==ENABLE) && (TIMEOUT_DELAY_ENABLE==ENABLE)) Host_sof_action(); } // - Host Wake-up has been received if (Is_host_hwup() && Is_host_hwup_interrupt_enabled()) { Host_disable_hwup_interrupt(); // Wake up interrupt should be disable host is now wake up ! // CAUTION HWUP can be cleared only when USB clock is active (not frozen)! Pll_start_auto(); // First Restart the PLL for USB operation Wait_pll_ready(); // Get sure pll is lock Usb_unfreeze_clock(); // Enable clock on USB interface Host_ack_hwup(); // Clear HWUP interrupt flag Usb_send_event(EVT_HOST_HWUP); // Send software event Host_hwup_action(); // Map custom action } #endif // End HOST FEATURE MODE }
/** * @brief Entry point of the USB host management * * The aim is to manage the device target connection and enumeration * depending on the device_state, the function performs the required operations * to get the device enumerated and configured * Once the device is operationnal, the device_state value is DEVICE_READY * This state should be tested by the host task application before performing * any applicative requests to the device. * * @param none * * @return none * * \image html host_task.gif */ void usb_host_task(void) { switch (device_state) { //------------------------------------------------------ // DEVICE_UNATTACHED state // // - Default init state // - Try to give device power supply // case DEVICE_UNATTACHED: Host_clear_device_supported(); // Reset Device status Host_clear_configured(); Host_clear_device_ready(); Usb_clear_all_event(); // Clear all software events new_device_connected=FALSE; selected_device=0; #if (USB_HUB_SUPPORT==ENABLE) nb_hub_present = 0; #endif #if (SOFTWARE_VBUS_CTRL==ENABLE) if( Is_usb_bconnection_error_interrupt()||Is_usb_vbus_error_interrupt()) { Usb_ack_bconnection_error_interrupt(); Usb_ack_vbus_error_interrupt(); Host_clear_vbus_request(); } Usb_disable_vbus_pad(); Usb_enable_manual_vbus(); if(Is_usb_srp_interrupt()) { Usb_ack_srp_interrupt(); Usb_enable_vbus_pad(); Usb_enable_vbus(); device_state=DEVICE_ATTACHED; } #else Usb_enable_vbus(); // Give at least device power supply!!! if(Is_usb_vbus_high()) { device_state=DEVICE_ATTACHED; } // If VBUS ok goto to device connection expectation #endif break; //------------------------------------------------------ // DEVICE_ATTACHED state // // - Vbus is on // - Try to detected device connection // case DEVICE_ATTACHED : if (Is_device_connection() || (force_enumeration==TRUE)) // Device pull-up detected { Host_ack_device_connection(); Host_clear_device_supported(); // Reset Device status Host_clear_configured(); Host_clear_device_ready(); Usb_clear_all_event(); // Clear all software events new_device_connected=FALSE; force_enumeration=FALSE; // Now device is connected, enable disconnection interrupt Host_enable_device_disconnection_interrupt(); Enable_interrupt(); // Reset device status Host_clear_device_supported(); Host_clear_configured(); Host_clear_device_ready(); Host_enable_sof(); // Start Start Of Frame generation Host_enable_sof_interrupt(); // SOF will be detected under interrupt c = 0; while (c<100) // wait 100ms before USB reset { if (Is_usb_event(EVT_HOST_SOF)) { Usb_ack_event(EVT_HOST_SOF); c++; }// Count Start Of frame if (Is_host_emergency_exit() || Is_usb_bconnection_error_interrupt()) {goto device_attached_error;} } Host_disable_device_disconnection_interrupt(); Host_send_reset(); // First USB reset Usb_ack_event(EVT_HOST_SOF); while (Is_host_reset()); // Active wait of end of reset send Host_ack_reset(); //Workaround for some bugly devices with powerless pull up //usually low speed where data line rise slowly and can be interpretaded as disconnection for(c=0;c!=0xFFFF;c++) // Basic Timeout counter { if(Is_usb_event(EVT_HOST_SOF)) //If we detect SOF, device is still alive and connected, just clear false disconnect flag { if(Is_device_disconnection()) { Host_ack_device_connection(); Host_ack_device_disconnection(); break; } } } Host_enable_device_disconnection_interrupt(); // All USB pipes must be reconfigured after a USB reset generation host_configure_pipe(PIPE_CONTROL, \ TYPE_CONTROL, \ TOKEN_SETUP, \ EP_CONTROL, \ SIZE_64, \ ONE_BANK, \ 0 ); c = 0; while (c<100) // wait 100ms after USB reset { if (Is_usb_event(EVT_HOST_SOF)) { Usb_ack_event(EVT_HOST_SOF); c++; }// Count Start Of frame if (Is_host_emergency_exit() || Is_usb_bconnection_error_interrupt()) {goto device_attached_error;} } device_state = DEVICE_POWERED; c=0; } device_attached_error: // Device connection error, or vbus pb -> Retry the connection process from the begining if( Is_usb_bconnection_error_interrupt()||Is_usb_vbus_error_interrupt()||Is_usb_vbus_low()) { Usb_ack_bconnection_error_interrupt(); Usb_enable_vbus_hw_control(); device_state=DEVICE_UNATTACHED; Usb_disable_vbus(); Usb_disable_vbus_pad(); Usb_enable_vbus_pad(); Usb_ack_vbus_error_interrupt(); Usb_enable_vbus(); Usb_disable_vbus_hw_control(); Host_disable_sof(); } break; //------------------------------------------------------ // DEVICE_POWERED state // // - Device connection (attach) as been detected, // - Wait 100ms and configure default control pipe // case DEVICE_POWERED : LOG_STR_CODE(log_device_connected); Host_device_connection_action(); if (Is_usb_event(EVT_HOST_SOF)) { Usb_ack_event(EVT_HOST_SOF); if (c++ >= 100) // Wait 100ms { device_state = DEVICE_DEFAULT; } } break; //------------------------------------------------------ // DEVICE_DEFAULT state // // - Get device descriptor // - Reconfigure Pipe 0 according to Device EP0 // - Attribute device address // case DEVICE_DEFAULT : // Get first device descriptor Host_select_device(0); usb_tree.device[0].ep_ctrl_size=8; if( CONTROL_GOOD == host_get_device_descriptor_uncomplete()) { c = 0; while(c<20) // wait 20ms before USB reset (special buggly devices...) { if (Is_usb_event(EVT_HOST_SOF)) { Usb_ack_event(EVT_HOST_SOF); c++; } if (Is_host_emergency_exit() || Is_usb_bconnection_error_interrupt()) {break;} } Host_disable_device_disconnection_interrupt(); Host_send_reset(); // First USB reset Usb_ack_event(EVT_HOST_SOF); while (Is_host_reset()); // Active wait of end of reset send Host_ack_reset(); //Workaround for some bugly devices with powerless pull up //usually low speed where data line rise slowly and can be interpretaded as disconnection for(c=0;c!=0xFFFF;c++) // Basic Timeout counter { if(Is_usb_event(EVT_HOST_SOF)) //If we detect SOF, device is still alive and connected, just clear false disconnect flag { if(Is_device_disconnection()) { Host_ack_device_connection(); Host_ack_device_disconnection(); break; } } } Host_enable_device_disconnection_interrupt(); c = 0; host_configure_pipe(PIPE_CONTROL, \ TYPE_CONTROL, \ TOKEN_SETUP, \ EP_CONTROL, \ SIZE_64, \ ONE_BANK, \ 0 ); while(c<200) // wait 200ms after USB reset { if (Is_usb_event(EVT_HOST_SOF)) { Usb_ack_event(EVT_HOST_SOF); c++; } if (Is_host_emergency_exit() || Is_usb_bconnection_error_interrupt()) {break;} } usb_tree.device[0].ep_ctrl_size=data_stage[OFFSET_FIELD_MAXPACKETSIZE]; // Give an absolute device address host_set_address(DEVICE_BASE_ADDRESS); usb_tree.device[0].device_address=DEVICE_BASE_ADDRESS; device_state = DEVICE_ADDRESSED; } else { device_state = DEVICE_ERROR; } break; //------------------------------------------------------ // DEVICE_BASE_ADDRESSED state // // - Check if VID PID is in supported list // case DEVICE_ADDRESSED : if (CONTROL_GOOD == host_get_device_descriptor()) { // Detect if the device connected belongs to the supported devices table if (HOST_TRUE == host_check_VID_PID()) { Host_set_device_supported(); Host_device_supported_action(); device_state = DEVICE_CONFIGURED; } else { #if (HOST_STRICT_VID_PID_TABLE==ENABLE) Host_device_not_supported_action(); device_state = DEVICE_ERROR; #else device_state = DEVICE_CONFIGURED; #endif } } else // Can not get device descriptor { device_state = DEVICE_ERROR; } break; //------------------------------------------------------ // DEVICE_CONFIGURED state // // - Configure pipes for the supported interface // - Send Set_configuration() request // - Goto full operating mode (device ready) // case DEVICE_CONFIGURED : if (CONTROL_GOOD == host_get_configuration_descriptor()) { if (HOST_FALSE != host_check_class()) // Class support OK? { usb_tree.nb_device++; #if (HOST_AUTO_CFG_ENDPOINT==ENABLE) if(host_auto_configure_endpoint()) #else Host_set_configured(); // Assumes config is OK with user config if(User_configure_endpoint()) // User call here instead of autoconfig #endif { if (CONTROL_GOOD== host_set_configuration(1)) // Send Set_configuration { //host_set_interface(interface_bound,interface_bound_alt_set); // device and host are now fully configured // goto DEVICE READY normal operation device_state = DEVICE_READY; Host_set_device_ready(); // monitor device disconnection under interrupt Host_enable_device_disconnection_interrupt(); // If user host application requires SOF interrupt event // Keep SOF interrupt enable otherwize, disable this interrupt #if (HOST_CONTINUOUS_SOF_INTERRUPT==DISABLE && USB_HUB_SUPPORT==DISABLE) Host_disable_sof_interrupt(); #endif #if (USB_HUB_SUPPORT==ENABLE) // Check if the connected device is a hub if(Get_class(0)==HUB_CLASS && Get_subclass(0)==0x00 && Get_protocol(0)==0x00) { // Get hub descriptor if( Get_hub_descriptor()==CONTROL_GOOD) { // Power each port of the hub i=data_stage[NB_PORT_OFFSET]; for(c=1;c<=i;c++) { Set_port_feature(PORT_POWER,c); } nb_hub_present = 1; hub_device_address[0]=DEVICE_BASE_ADDRESS; hub_init(nb_hub_present-1); } } else { nb_hub_present = 0; new_device_connected=TRUE; } #else new_device_connected=TRUE; #endif Enable_interrupt(); LOG_STR_CODE(log_device_enumerated); } else// Problem during Set_configuration request... { device_state = DEVICE_ERROR; } } } else // device class not supported... { device_state = DEVICE_ERROR; LOG_STR_CODE(log_device_unsupported); Host_device_class_not_supported_action(); } } else // Can not get configuration descriptors... { device_state = DEVICE_ERROR; } break; //------------------------------------------------------ // DEVICE_READY state // // - Full std operatinf mode // - Nothing to do... // case DEVICE_READY: // Host full std operating mode! new_device_connected=FALSE; #if (USB_HUB_SUPPORT==ENABLE) f_hub_port_disconnect=FALSE; // If one hub is present in the USB tree and the period interval // for the interrupt hub endpoint occurs if(nb_hub_present && hub_interrupt_sof==0) { saved_device=selected_device; // Backup user selected device for(j=1;j<=nb_hub_present;j++) { for(i=0;i<MAX_DEVICE_IN_USB_TREE;i++) { if(usb_tree.device[i].device_address==hub_device_address[j-1]) break; } Host_select_device(i); Host_select_pipe(usb_tree.device[i].interface[0].ep[0].pipe_number); Host_ack_nak_received(); Host_ack_in_received(); Host_unfreeze_pipe(); Host_send_in(); while(1) { if(Is_host_nak_received()) break; if(Is_host_emergency_exit()) break; if(Is_host_in_received()) break; } Host_freeze_pipe(); if(Is_host_nak_received()) { Host_ack_nak_received(); } if(Is_host_in_received()) { if(Is_host_stall()==FALSE) { c=Host_read_byte(); } Host_ack_in_received(); hub_manage_port_change_status(c,j); } } // for all hub Host_select_device(saved_device); // Restore user selected device #if (USER_PERIODIC_PIPE==ENABLE) unfreeze_user_periodic_pipe(); #endif } #endif break; //------------------------------------------------------ // DEVICE_ERROR state // // - Error state // - Do custom action call (probably go to default mode...) // case DEVICE_ERROR : // TODO !!!! #if (HOST_ERROR_RESTART==ENABLE) device_state=DEVICE_UNATTACHED; #endif Host_device_error_action(); break; //------------------------------------------------------ // DEVICE_SUSPENDED state // // - Host application request to suspend the device activity // - State machine comes here thanks to Host_request_suspend() // case DEVICE_SUSPENDED : if(Is_device_supports_remote_wakeup()) // If the connected device supports remote wake up { host_set_feature_remote_wakeup(); // Enable this feature... } LOG_STR_CODE(log_going_to_suspend); c = Is_host_sof_interrupt_enabled(); //Save current sof interrupt enable state Host_disable_sof_interrupt(); Host_ack_sof(); Host_disable_sof(); // Stop start of frame generation, this generates the suspend state Host_ack_hwup(); Host_enable_hwup_interrupt(); // Enable host wake-up interrupt // (this is the unique USB interrupt able to wake up the CPU core from power-down mode) Usb_freeze_clock(); Stop_pll(); Host_suspend_action(); // Custom action here! (for example go to power-save mode...) device_state=DEVICE_WAIT_RESUME; // wait for device resume event break; //------------------------------------------------------ // DEVICE_WAIT_RESUME state // // - Wait in this state till the host receives an upstream resume from the device // - or the host software request the device to resume // case DEVICE_WAIT_RESUME : if(Is_usb_event(EVT_HOST_HWUP)|| Is_host_request_resume())// Remote wake up has been detected // or Local resume request has been received { if(Is_host_request_resume()) // Not a remote wakeup, but an host application request { Host_disable_hwup_interrupt(); // Wake up interrupt should be disable host is now wake up ! // CAUTION HWUP can be cleared only when USB clock is active Pll_start_auto(); // First Restart the PLL for USB operation Wait_pll_ready(); // Get sure pll is lock Usb_unfreeze_clock(); // Enable clock on USB interface Host_ack_hwup(); // Clear HWUP interrupt flag } Host_enable_sof(); Host_send_resume(); // Send down stream resume while (Is_host_down_stream_resume()==FALSE); // Wait Down stream resume sent Host_ack_remote_wakeup(); // Ack remote wake-up reception Host_ack_request_resume(); // Ack software request Host_ack_down_stream_resume(); // Ack down stream resume sent Usb_ack_event(EVT_HOST_HWUP); // Ack software event if(c) { Host_enable_sof_interrupt(); } // Restore SOF interrupt enable state before suspend device_state=DEVICE_READY; // Come back to full operating mode LOG_STR_CODE(log_usb_resumed); } break; //------------------------------------------------------ // DEVICE_DISCONNECTED state // // - Device disconnection has been detected // - Run scheduler in this state at least two times to get sure event is detected by all host application tasks // - Go to DEVICE_DISCONNECTED_ACK state before DEVICE_UNATTACHED, to get sure scheduler calls all app tasks... // case DEVICE_DISCONNECTED : device_state = DEVICE_DISCONNECTED_ACK; break; //------------------------------------------------------ // DEVICE_DISCONNECTED_ACK state // // - Device disconnection has been detected and managed bu applicatives tasks // - Go to DEVICE_UNATTACHED state // case DEVICE_DISCONNECTED_ACK : device_state = DEVICE_UNATTACHED; break; //------------------------------------------------------ // default state // // - Default case: ERROR // - Goto no device state // default : device_state = DEVICE_UNATTACHED; break; } }