2021-08-24 11:20:50 -04:00
/*
2024-02-22 15:25:10 -05:00
* SPDX - FileCopyrightText : 2015 - 2024 Espressif Systems ( Shanghai ) CO LTD
2021-08-24 11:20:50 -04:00
*
* SPDX - License - Identifier : Apache - 2.0
*/
# pragma once
# include <stdint.h>
# include <sys/queue.h>
# include "freertos/FreeRTOS.h"
# include "freertos/task.h"
# include "hcd.h"
# include "usb/usb_types_ch9.h"
# include "usb/usb_types_stack.h"
# ifdef __cplusplus
extern " C " {
# endif
// ------------------------------------------------------ Types --------------------------------------------------------
2023-05-08 12:43:32 -04:00
// ----------------------- Handles -------------------------
/**
* @ brief Handle of a allocated endpoint
*/
2023-05-08 12:53:27 -04:00
typedef struct usbh_ep_handle_s * usbh_ep_handle_t ;
2023-05-08 12:43:32 -04:00
2021-08-24 11:20:50 -04:00
// ----------------------- Events --------------------------
2024-02-22 15:25:10 -05:00
/**
* @ brief Enumerator for various USBH events
*/
2021-08-24 11:20:50 -04:00
typedef enum {
2024-02-22 15:44:01 -05:00
USBH_EVENT_CTRL_XFER , /**< A control transfer has completed */
2024-02-22 15:25:10 -05:00
USBH_EVENT_NEW_DEV , /**< A new device has been enumerated and added to the device pool */
2021-12-14 05:28:04 -05:00
USBH_EVENT_DEV_GONE , /**< A device is gone. Clients should close the device */
2024-03-21 08:57:32 -04:00
USBH_EVENT_DEV_FREE , /**< A device has been freed. Its upstream port can now be recycled */
2024-02-22 15:25:10 -05:00
USBH_EVENT_ALL_FREE , /**< All devices have been freed */
2021-08-24 11:20:50 -04:00
} usbh_event_t ;
2024-02-22 15:25:10 -05:00
/**
* @ brief Event data object for USBH events
*/
typedef struct {
usbh_event_t event ;
union {
2024-02-22 15:44:01 -05:00
struct {
usb_device_handle_t dev_hdl ;
urb_t * urb ;
} ctrl_xfer_data ;
2024-02-22 15:25:10 -05:00
struct {
uint8_t dev_addr ;
} new_dev_data ;
struct {
uint8_t dev_addr ;
usb_device_handle_t dev_hdl ;
} dev_gone_data ;
2024-03-21 08:57:32 -04:00
struct {
uint8_t dev_addr ;
} dev_free_data ;
2024-02-22 15:25:10 -05:00
} ;
} usbh_event_data_t ;
2021-12-14 05:28:04 -05:00
/**
2023-05-08 12:43:32 -04:00
* @ brief Endpoint events
2021-12-14 05:28:04 -05:00
*
2023-05-08 12:43:32 -04:00
* @ note Optimization : Keep this identical to hcd_pipe_event_t
2021-12-14 05:28:04 -05:00
*/
typedef enum {
2023-05-08 12:43:32 -04:00
USBH_EP_EVENT_NONE , /**< The EP has no events (used to indicate no events when polling) */
USBH_EP_EVENT_URB_DONE , /**< The EP has completed a URB. The URB can be dequeued */
USBH_EP_EVENT_ERROR_XFER , /**< The EP encountered excessive errors when transferring a URB i.e., three three consecutive transaction errors (e.g., no ACK, bad CRC etc) */
USBH_EP_EVENT_ERROR_URB_NOT_AVAIL , /**< The EP tried to execute a transfer but no URB was available */
USBH_EP_EVENT_ERROR_OVERFLOW , /**< The EP received more data than requested. Usually a Packet babble error (i.e., an IN packet has exceeded the EP's MPS) */
USBH_EP_EVENT_ERROR_STALL , /**< EP received a STALL response */
} usbh_ep_event_t ;
2021-12-14 05:28:04 -05:00
2023-05-08 12:43:32 -04:00
// ------------------ Requests/Commands --------------------
/**
* @ brief Endpoint commands
*
* @ note Optimization : Keep this identical to hcd_pipe_cmd_t
*/
typedef enum {
USBH_EP_CMD_HALT , /**< Halt an active endpoint. Any currently executing URB will be canceled. Enqueued URBs are left untouched */
USBH_EP_CMD_FLUSH , /**< Can only be called when halted. Will cause all enqueued URBs to be canceled */
USBH_EP_CMD_CLEAR , /**< Causes a halted endpoint to become active again. Any enqueued URBs will being executing.*/
} usbh_ep_cmd_t ;
2021-08-24 11:20:50 -04:00
// ---------------------- Callbacks ------------------------
/**
* @ brief Callback used to indicate that the USBH has an event
*
* @ note This callback is called from within usbh_process ( )
*/
2024-02-22 15:25:10 -05:00
typedef void ( * usbh_event_cb_t ) ( usbh_event_data_t * event_data , void * arg ) ;
2021-08-24 11:20:50 -04:00
2023-05-08 12:43:32 -04:00
/**
* @ brief Callback used to indicate an event on an endpoint
*
* Return whether to yield or not if called from an ISR . Always return false if not called from an ISR
*/
typedef bool ( * usbh_ep_cb_t ) ( usbh_ep_handle_t ep_hdl , usbh_ep_event_t ep_event , void * arg , bool in_isr ) ;
2021-08-24 11:20:50 -04:00
// ----------------------- Objects -------------------------
/**
* @ brief Configuration for an endpoint being allocated using usbh_ep_alloc ( )
*/
typedef struct {
2023-05-08 12:43:32 -04:00
uint8_t bInterfaceNumber ; /**< Interface number */
uint8_t bAlternateSetting ; /**< Alternate setting number of the interface */
uint8_t bEndpointAddress ; /**< Endpoint address */
usbh_ep_cb_t ep_cb ; /**< Endpoint event callback */
void * ep_cb_arg ; /**< Endpoint callback argument */
void * context ; /**< Endpoint context */
2021-08-24 11:20:50 -04:00
} usbh_ep_config_t ;
/**
* @ brief USBH configuration used in usbh_install ( )
*/
typedef struct {
2023-05-08 12:43:32 -04:00
usb_proc_req_cb_t proc_req_cb ; /**< Processing request callback */
void * proc_req_cb_arg ; /**< Processing request callback argument */
2021-08-24 11:20:50 -04:00
usbh_event_cb_t event_cb ; /**< USBH event callback */
void * event_cb_arg ; /**< USBH event callback argument */
} usbh_config_t ;
2024-03-29 09:29:01 -04:00
// -------------------------------------------- USBH Processing Functions ----------------------------------------------
2021-08-24 11:20:50 -04:00
/**
* @ brief Installs the USBH driver
*
* - This function will internally install the HCD
* - This must be called before calling any Hub driver functions
*
2021-11-17 13:07:53 -05:00
* @ note Before calling this function , the Host Controller must already be un - clock gated and reset . The USB PHY
* ( internal or external , and associated GPIOs ) must already be configured .
2021-08-24 11:20:50 -04:00
* @ param usbh_config USBH driver configuration
* @ return esp_err_t
*/
esp_err_t usbh_install ( const usbh_config_t * usbh_config ) ;
/**
* @ brief Uninstall the USBH driver
*
* - This function will uninstall the HCD
* - The Hub driver must be uninstalled before calling this function
*
2021-11-17 13:07:53 -05:00
* @ note This function will simply free the resources used by the USBH . The underlying Host Controller and USB PHY will
* not be disabled .
2021-08-24 11:20:50 -04:00
* @ return esp_err_t
*/
esp_err_t usbh_uninstall ( void ) ;
/**
* @ brief USBH processing function
*
* - USBH processing function that must be called repeatedly to process USBH events
2023-05-08 12:43:32 -04:00
* - If blocking , the caller can block until the proc_req_cb ( ) is called with USB_PROC_REQ_SOURCE_USBH as the request
* source . The USB_PROC_REQ_SOURCE_USBH source indicates that this function should be called .
2021-08-24 11:20:50 -04:00
*
2021-10-27 09:15:46 -04:00
* @ note This function can block
2021-08-24 11:20:50 -04:00
* @ return esp_err_t
*/
esp_err_t usbh_process ( void ) ;
2024-03-29 09:29:01 -04:00
// ---------------------------------------------- Device Pool Functions ------------------------------------------------
2021-12-08 06:46:46 -05:00
/**
* @ brief Get the current number of devices
*
* @ note This function can block
* @ param [ out ] num_devs_ret Current number of devices
* @ return esp_err_t
*/
2024-04-02 23:24:03 -04:00
esp_err_t usbh_devs_num ( int * num_devs_ret ) ;
2021-12-08 06:46:46 -05:00
2021-10-27 12:54:27 -04:00
/**
* @ brief Fill list with address of currently connected devices
*
* - This function fills the provided list with the address of current connected devices
2024-04-02 23:24:03 -04:00
* - Device address can then be used in usbh_devs_open ( )
2021-10-27 12:54:27 -04:00
* - If there are more devices than the list_len , this function will only fill
* up to list_len number of devices .
*
* @ param [ in ] list_len Length of empty list
* @ param [ inout ] dev_addr_list Empty list to be filled
* @ param [ out ] num_dev_ret Number of devices filled into list
* @ return esp_err_t
*/
2024-04-02 23:24:03 -04:00
esp_err_t usbh_devs_addr_list_fill ( int list_len , uint8_t * dev_addr_list , int * num_dev_ret ) ;
2021-10-27 12:54:27 -04:00
2024-03-29 09:29:01 -04:00
/**
* @ brief Mark that all devices should be freed at the next possible opportunity
*
2024-04-02 23:24:03 -04:00
* A device marked as free will not be freed until the last client using the device has called usbh_devs_close ( )
2024-03-29 09:29:01 -04:00
*
* @ return
* - ESP_OK : There were no devices to free to begin with . Current state is all free
* - ESP_ERR_NOT_FINISHED : One or more devices still need to be freed ( but have been marked " to be freed " )
*/
2024-04-02 23:24:03 -04:00
esp_err_t usbh_devs_mark_all_free ( void ) ;
2024-03-29 09:29:01 -04:00
2021-08-24 11:20:50 -04:00
/**
* @ brief Open a device by address
*
* A device must be opened before it can be used
*
* @ param [ in ] dev_addr Device address
* @ param [ out ] dev_hdl Device handle
* @ return esp_err_t
*/
2024-04-02 23:24:03 -04:00
esp_err_t usbh_devs_open ( uint8_t dev_addr , usb_device_handle_t * dev_hdl ) ;
2021-08-24 11:20:50 -04:00
/**
* @ brief CLose a device
*
2024-04-02 23:24:03 -04:00
* Device can be opened by calling usbh_devs_open ( )
2021-08-24 11:20:50 -04:00
*
* @ param [ in ] dev_hdl Device handle
* @ return esp_err_t
*/
2024-04-02 23:24:03 -04:00
esp_err_t usbh_devs_close ( usb_device_handle_t dev_hdl ) ;
2021-08-24 11:20:50 -04:00
2024-03-29 09:29:01 -04:00
// ------------------------------------------------ Device Functions ---------------------------------------------------
2021-08-24 11:20:50 -04:00
2024-03-29 09:29:01 -04:00
// ----------------------- Getters -------------------------
2021-08-24 11:20:50 -04:00
/**
* @ brief Get a device ' s address
*
* @ note Can be called without opening the device
*
* @ param [ in ] dev_hdl Device handle
* @ param [ out ] dev_addr Device ' s address
* @ return esp_err_t
*/
esp_err_t usbh_dev_get_addr ( usb_device_handle_t dev_hdl , uint8_t * dev_addr ) ;
/**
* @ brief Get a device ' s information
*
2021-10-27 09:15:46 -04:00
* @ note This function can block
2021-08-24 11:20:50 -04:00
* @ param [ in ] dev_hdl Device handle
* @ param [ out ] dev_info Device information
* @ return esp_err_t
*/
esp_err_t usbh_dev_get_info ( usb_device_handle_t dev_hdl , usb_device_info_t * dev_info ) ;
/**
* @ brief Get a device ' s device descriptor
*
* - The device descriptor is cached when the device is created by the Hub driver
*
* @ param [ in ] dev_hdl Device handle
* @ param [ out ] dev_desc_ret Device descriptor
* @ return esp_err_t
*/
esp_err_t usbh_dev_get_desc ( usb_device_handle_t dev_hdl , const usb_device_desc_t * * dev_desc_ret ) ;
/**
* @ brief Get a device ' s active configuration descriptor
*
* Simply returns a reference to the internally cached configuration descriptor
*
2021-10-27 09:15:46 -04:00
* @ note This function can block
2021-08-24 11:20:50 -04:00
* @ param [ in ] dev_hdl Device handle
* @ param config_desc_ret
* @ return esp_err_t
*/
esp_err_t usbh_dev_get_config_desc ( usb_device_handle_t dev_hdl , const usb_config_desc_t * * config_desc_ret ) ;
// ----------------------------------------------- Endpoint Functions -------------------------------------------------
/**
* @ brief Allocate an endpoint on a device
*
2023-05-08 12:43:32 -04:00
* This function allows clients to allocate a non - default endpoint ( i . e . , not EP0 ) on a connected device
*
2024-04-02 23:24:03 -04:00
* - A client must have opened the device using usbh_devs_open ( ) before attempting to allocate an endpoint on the device
2023-05-08 12:43:32 -04:00
* - A client should call this function to allocate all endpoints in an interface that the client has claimed .
* - A client must allocate an endpoint using this function before attempting to communicate with it
* - Once the client allocates an endpoint , the client is now owns / manages the endpoint . No other client should use or
2023-05-08 11:12:25 -04:00
* deallocate the endpoint .
2021-08-24 11:20:50 -04:00
*
2021-10-27 09:15:46 -04:00
* @ note This function can block
2023-05-08 12:43:32 -04:00
* @ note Default endpoints ( EP0 ) are owned by the USBH . For control transfers , use usbh_dev_submit_ctrl_urb ( ) instead
2021-08-24 11:20:50 -04:00
*
* @ param [ in ] dev_hdl Device handle
2023-05-08 12:43:32 -04:00
* @ param [ in ] ep_config Endpoint configuration
* @ param [ out ] ep_hdl_ret Endpoint handle
2021-08-24 11:20:50 -04:00
* @ return esp_err_t
*/
2023-05-08 12:43:32 -04:00
esp_err_t usbh_ep_alloc ( usb_device_handle_t dev_hdl , usbh_ep_config_t * ep_config , usbh_ep_handle_t * ep_hdl_ret ) ;
2021-08-24 11:20:50 -04:00
/**
* @ brief Free and endpoint on a device
*
2023-05-08 12:43:32 -04:00
* This function frees an endpoint previously allocated by the client using usbh_ep_alloc ( )
*
* - Only the client that allocated the endpoint should free it
* - The client must have halted and flushed the endpoint using usbh_ep_command ( ) before attempting to free it
* - The client must ensure that there are no more function calls to the endpoint before freeing it
2021-08-24 11:20:50 -04:00
*
2021-10-27 09:15:46 -04:00
* @ note This function can block
2023-05-08 12:43:32 -04:00
* @ param [ in ] ep_hdl Endpoint handle
* @ return esp_err_t
*/
esp_err_t usbh_ep_free ( usbh_ep_handle_t ep_hdl ) ;
/**
* @ brief Get the handle of an endpoint using its address
*
* The endpoint must have been previously allocated using usbh_ep_alloc ( )
*
2021-08-24 11:20:50 -04:00
* @ param [ in ] dev_hdl Device handle
2023-05-08 12:43:32 -04:00
* @ param [ in ] bEndpointAddress Endpoint address
* @ param [ out ] ep_hdl_ret Endpoint handle
* @ return esp_err_t
*/
esp_err_t usbh_ep_get_handle ( usb_device_handle_t dev_hdl , uint8_t bEndpointAddress , usbh_ep_handle_t * ep_hdl_ret ) ;
/**
2024-03-29 09:29:01 -04:00
* @ brief Execute a command on a particular endpoint
2023-05-08 12:43:32 -04:00
*
2024-03-29 09:29:01 -04:00
* Endpoint commands allows executing a certain action on an endpoint ( e . g . , halting , flushing , clearing etc )
2023-05-08 12:43:32 -04:00
*
* @ param [ in ] ep_hdl Endpoint handle
2024-03-29 09:29:01 -04:00
* @ param [ in ] command Endpoint command
2023-05-08 12:43:32 -04:00
* @ return esp_err_t
*/
2024-03-29 09:29:01 -04:00
esp_err_t usbh_ep_command ( usbh_ep_handle_t ep_hdl , usbh_ep_cmd_t command ) ;
2023-05-08 12:43:32 -04:00
/**
2024-03-29 09:29:01 -04:00
* @ brief Get the context of an endpoint
2023-05-08 12:43:32 -04:00
*
2024-03-29 09:29:01 -04:00
* Get the context variable assigned to and endpoint on allocation .
2023-05-08 12:43:32 -04:00
*
2024-03-29 09:29:01 -04:00
* @ note This function can block
2023-05-08 12:43:32 -04:00
* @ param [ in ] ep_hdl Endpoint handle
2024-03-29 09:29:01 -04:00
* @ return Endpoint context
*/
void * usbh_ep_get_context ( usbh_ep_handle_t ep_hdl ) ;
// ----------------------------------------------- Transfer Functions --------------------------------------------------
/**
* @ brief Submit a control transfer ( URB ) to a device
*
* @ param [ in ] dev_hdl Device handle
* @ param [ in ] urb URB
2021-08-24 11:20:50 -04:00
* @ return esp_err_t
*/
2024-03-29 09:29:01 -04:00
esp_err_t usbh_dev_submit_ctrl_urb ( usb_device_handle_t dev_hdl , urb_t * urb ) ;
2023-05-08 12:43:32 -04:00
/**
2024-03-29 09:29:01 -04:00
* @ brief Enqueue a URB to an endpoint
2023-05-08 12:43:32 -04:00
*
2024-03-29 09:29:01 -04:00
* The URB will remain enqueued until it completes ( successfully or errors out ) . Use usbh_ep_dequeue_urb ( ) to dequeue
* a completed URB .
2023-05-08 12:43:32 -04:00
*
* @ param [ in ] ep_hdl Endpoint handle
2024-03-29 09:29:01 -04:00
* @ param [ in ] urb URB to enqueue
2023-05-08 12:43:32 -04:00
* @ return esp_err_t
*/
2024-03-29 09:29:01 -04:00
esp_err_t usbh_ep_enqueue_urb ( usbh_ep_handle_t ep_hdl , urb_t * urb ) ;
2021-08-24 11:20:50 -04:00
/**
2024-03-29 09:29:01 -04:00
* @ brief Dequeue a URB from an endpoint
2021-08-24 11:20:50 -04:00
*
2024-03-29 09:29:01 -04:00
* Dequeue a completed URB from an endpoint . The USBH_EP_EVENT_URB_DONE indicates that URBs can be dequeued
2021-08-24 11:20:50 -04:00
*
2023-05-08 12:43:32 -04:00
* @ param [ in ] ep_hdl Endpoint handle
2024-03-29 09:29:01 -04:00
* @ param [ out ] urb_ret Dequeued URB , or NULL if no more URBs to dequeue
* @ return esp_err_t
2021-08-24 11:20:50 -04:00
*/
2024-03-29 09:29:01 -04:00
esp_err_t usbh_ep_dequeue_urb ( usbh_ep_handle_t ep_hdl , urb_t * * urb_ret ) ;
2021-08-24 11:20:50 -04:00
// -------------------------------------------------- Hub Functions ----------------------------------------------------
// ------------------- Device Related ----------------------
/**
* @ brief Indicates to USBH the start of enumeration for a device
*
* - The Hub driver calls this function before it starts enumerating a new device .
* - The USBH will allocate a new device that will be initialized by the Hub driver using the remaining hub enumeration
* functions .
* - The new device ' s default pipe handle is returned to all the Hub driver to be used during enumeration .
*
* @ note Hub Driver only
* @ param [ in ] port_hdl Handle of the port that the device is connected to
* @ param [ in ] dev_speed Device ' s speed
* @ param [ out ] new_dev_hdl Device ' s handle
* @ param [ out ] default_pipe_hdl Device ' s default pipe handle
* @ return esp_err_t
*/
esp_err_t usbh_hub_add_dev ( hcd_port_handle_t port_hdl , usb_speed_t dev_speed , usb_device_handle_t * new_dev_hdl , hcd_pipe_handle_t * default_pipe_hdl ) ;
/**
2024-03-21 08:57:32 -04:00
* @ brief Indicates to the USBH that a device is gone
2021-08-24 11:20:50 -04:00
*
2021-12-14 05:28:04 -05:00
* @ param dev_hdl Device handle
2021-08-24 11:20:50 -04:00
* @ return esp_err_t
*/
2024-03-21 08:57:32 -04:00
esp_err_t usbh_hub_dev_gone ( usb_device_handle_t dev_hdl ) ;
2021-08-24 11:20:50 -04:00
// ----------------- Enumeration Related -------------------
/**
2021-12-08 06:46:46 -05:00
* @ brief Assign the enumerating device ' s address
2021-08-24 11:20:50 -04:00
*
* @ note Hub Driver only
* @ note Must call in sequence
* @ param [ in ] dev_hdl Device handle
2021-12-08 06:46:46 -05:00
* @ param dev_addr
2021-08-24 11:20:50 -04:00
* @ return esp_err_t
*/
2021-12-08 06:46:46 -05:00
esp_err_t usbh_hub_enum_fill_dev_addr ( usb_device_handle_t dev_hdl , uint8_t dev_addr ) ;
2021-08-24 11:20:50 -04:00
/**
2021-12-08 06:46:46 -05:00
* @ brief Fill the enumerating device ' s descriptor
2021-08-24 11:20:50 -04:00
*
* @ note Hub Driver only
* @ note Must call in sequence
* @ param [ in ] dev_hdl Device handle
2021-12-08 06:46:46 -05:00
* @ param device_desc
2021-08-24 11:20:50 -04:00
* @ return esp_err_t
*/
2021-12-08 06:46:46 -05:00
esp_err_t usbh_hub_enum_fill_dev_desc ( usb_device_handle_t dev_hdl , const usb_device_desc_t * device_desc ) ;
2021-08-24 11:20:50 -04:00
/**
* @ brief Fill the enumerating device ' s active configuration descriptor
*
* @ note Hub Driver only
* @ note Must call in sequence
2021-10-27 09:15:46 -04:00
* @ note This function can block
2021-08-24 11:20:50 -04:00
* @ param [ in ] dev_hdl Device handle
* @ param config_desc_full
* @ return esp_err_t
*/
esp_err_t usbh_hub_enum_fill_config_desc ( usb_device_handle_t dev_hdl , const usb_config_desc_t * config_desc_full ) ;
/**
2021-12-08 06:46:46 -05:00
* @ brief Fill one of the string descriptors of the enumerating device
2021-08-24 11:20:50 -04:00
*
* @ note Hub Driver only
* @ note Must call in sequence
2021-12-08 06:46:46 -05:00
* @ param dev_hdl Device handle
* @ param str_desc Pointer to string descriptor
2023-05-08 11:12:25 -04:00
* @ param select Select which string descriptor . 0 / 1 / 2 for Manufacturer / Product / Serial Number string descriptors respectively
2021-08-24 11:20:50 -04:00
* @ return esp_err_t
*/
2021-12-08 06:46:46 -05:00
esp_err_t usbh_hub_enum_fill_str_desc ( usb_device_handle_t dev_hdl , const usb_str_desc_t * str_desc , int select ) ;
2021-08-24 11:20:50 -04:00
/**
* @ brief Indicate the device enumeration is completed
*
2024-02-22 15:25:10 -05:00
* This will allow the device to be opened by clients , and also trigger a USBH_EVENT_NEW_DEV event .
2021-08-24 11:20:50 -04:00
*
* @ note Hub Driver only
* @ note Must call in sequence
2021-10-27 09:15:46 -04:00
* @ note This function can block
2021-08-24 11:20:50 -04:00
* @ param [ in ] dev_hdl Device handle
* @ return esp_err_t
*/
esp_err_t usbh_hub_enum_done ( usb_device_handle_t dev_hdl ) ;
/**
* @ brief Indicate that device enumeration has failed
*
* This will cause the enumerating device ' s resources to be cleaned up
* The Hub Driver must guarantee that the enumerating device ' s default pipe is already halted , flushed , and dequeued .
*
* @ note Hub Driver only
* @ note Must call in sequence
2021-10-27 09:15:46 -04:00
* @ note This function can block
2021-08-24 11:20:50 -04:00
* @ param [ in ] dev_hdl Device handle
* @ return esp_err_t
*/
esp_err_t usbh_hub_enum_failed ( usb_device_handle_t dev_hdl ) ;
# ifdef __cplusplus
}
# endif