| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480 |
- // Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- /*
- Note: This header file contains the types and macros belong/relate to the USB2.0 protocol and are HW implementation
- agnostic. In other words, this header is only meant to be used in the HCD layer and above of the USB Host stack. For
- types and macros that are HW implementation specific (i.e., HAL layer and below), add them to the "usb_types.h" header
- instead.
- */
- #pragma once
- #include <stdint.h>
- #ifdef __cplusplus
- extern "C"
- {
- #endif
- #include <stdint.h>
- #include <sys/queue.h>
- #define USB_CTRL_REQ_ATTR __attribute__((packed))
- #define USB_DESC_ATTR __attribute__((packed))
- // ------------------------------------------------ Common USB Types ---------------------------------------------------
- // --------------------- Bus Related -----------------------
- /**
- * @brief USB Standard Speeds
- */
- typedef enum {
- USB_SPEED_LOW = 0, /**< USB Low Speed (1.5 Mbit/s) */
- USB_SPEED_FULL, /**< USB Full Speed (12 Mbit/s) */
- } usb_speed_t;
- // ------------------ Transfer Related ---------------------
- /**
- * @brief The type of USB transfer
- *
- * @note The enum values need to match the bmAttributes field of an EP descriptor
- */
- typedef enum {
- USB_TRANSFER_TYPE_CTRL = 0,
- USB_TRANSFER_TYPE_ISOCHRONOUS,
- USB_TRANSFER_TYPE_BULK,
- USB_TRANSFER_TYPE_INTR,
- } usb_transfer_type_t;
- /**
- * @brief The status of a particular transfer
- */
- typedef enum {
- USB_TRANSFER_STATUS_COMPLETED, /**< The transfer was successful (but may be short) */
- USB_TRANSFER_STATUS_ERROR, /**< The transfer failed because due to excessive errors (e.g. no response or CRC error) */
- USB_TRANSFER_STATUS_TIMED_OUT, /**< The transfer failed due to a time out */
- USB_TRANSFER_STATUS_CANCELED, /**< The transfer was canceled */
- USB_TRANSFER_STATUS_STALL, /**< The transfer was stalled */
- USB_TRANSFER_STATUS_NO_DEVICE, /**< The transfer failed because the device is no longer valid (e.g., disconnected */
- USB_TRANSFER_STATUS_OVERFLOW, /**< The transfer as more data was sent than was requested */
- USB_TRANSFER_STATUS_SKIPPED, /**< ISOC only. The packet was skipped due to system latency */
- } usb_transfer_status_t;
- /**
- * @brief Isochronous packet descriptor
- *
- * If the number of bytes in an IRP (I/O Request Packet, see USB2.0 Spec) is
- * larger than the MPS of the endpoint, the IRP is split over multiple packets
- * (one packet per bInterval of the endpoint). An array of Isochronous packet
- * descriptors describes how an IRP should be split over multiple packets.
- */
- typedef struct {
- int length; /**< Number of bytes to transmit/receive in the packet */
- int actual_length; /**< Actual number of bytes transmitted/received in the packet */
- usb_transfer_status_t status; /**< Status of the packet */
- } usb_iso_packet_desc_t;
- #define USB_IRP_FLAG_ZERO_PACK 0x01 /**< (For bulk OUT only). Indicates that a bulk OUT transfers should always terminate with a short packet, even if it means adding an extra zero length packet */
- /**
- * @brief USB IRP (I/O Request Packet). See USB2.0 Spec
- *
- * An IRP is used to represent data transfer request form a software client to and endpoint over the USB bus. The same
- * IRP object type is used at each layer of the USB stack. This minimizes copying/conversion across the different layers
- * of the stack as each layer will pass a pointer to this type of object.
- *
- * See 10.5.3.1 os USB2.0 specification
- * Bulk: Represents a single bulk transfer which a pipe will transparently split into multiple MPS transactions (until
- * the last)
- * Control: Represents a single control transfer with the setup packet at the first 8 bytes of the buffer.
- * Interrupt: Represents a single interrupt transaction
- * Isochronous: Represents a buffer of a stream of bytes which the pipe will transparently transfer the stream of bytes
- * one or more service periods
- *
- * @note The tailq_entry and reserved variables are used by the USB Host stack internally. Users should not modify those fields.
- * @note Once an IRP is submitted, users should not modify the IRP as the Host stack takes ownership of the IRP.
- */
- struct usb_irp_obj {
- //Internal members
- TAILQ_ENTRY(usb_irp_obj) tailq_entry; /**< TAILQ entry that allows this object to be added to linked lists. Users should NOT modify this field */
- void *reserved_ptr; /**< Reserved pointer variable for internal use in the stack. Users should set this to NULL on allocation and NOT modify this afterwards */
- uint32_t reserved_flags; /**< Reserved variable for flags used internally in the stack. Users should set this to 0 on allocation and NOT modify this afterwards */
- //Public members
- uint8_t *data_buffer; /**< Pointer to data buffer. Must be DMA capable memory */
- int num_bytes; /**< Number of bytes in IRP. Control should exclude size of setup. IN should be integer multiple of MPS */
- int actual_num_bytes; /**< Actual number of bytes transmitted/receives in the IRP */
- uint32_t flags; /**< IRP flags */
- usb_transfer_status_t status; /**< Status of the transfer */
- uint32_t timeout; /**< Timeout (in milliseconds) of the packet (currently not supported yet) */
- void *context; /**< Context variable used to associate the IRP object with another object */
- int num_iso_packets; /**< Only relevant to Isochronous. Number of service periods to transfer data buffer over. Set to 0 for non-iso transfers */
- usb_iso_packet_desc_t iso_packet_desc[0]; /**< Descriptors for each ISO packet */
- };
- typedef struct usb_irp_obj usb_irp_t;
- // ---------------------------------------------------- Chapter 9 ------------------------------------------------------
- #define USB_B_DESCRIPTOR_TYPE_DEVICE 1
- #define USB_B_DESCRIPTOR_TYPE_CONFIGURATION 2
- #define USB_B_DESCRIPTOR_TYPE_STRING 3
- #define USB_B_DESCRIPTOR_TYPE_INTERFACE 4
- #define USB_B_DESCRIPTOR_TYPE_ENDPOINT 5
- #define USB_B_DESCRIPTOR_TYPE_DEVICE_QUALIFIER 6
- #define USB_B_DESCRIPTOR_TYPE_OTHER_SPEED_CONFIGURATION 7
- #define USB_B_DESCRIPTOR_TYPE_INTERFACE_POWER 8
- // ------------------- Control Request ---------------------
- /**
- * @brief Size of a USB control transfer setup packet in bytes
- */
- #define USB_CTRL_REQ_SIZE 8
- /**
- * @brief Structure representing a USB control transfer setup packet
- */
- typedef union {
- struct {
- uint8_t bRequestType;
- uint8_t bRequest;
- uint16_t wValue;
- uint16_t wIndex;
- uint16_t wLength;
- } USB_CTRL_REQ_ATTR;
- uint8_t val[USB_CTRL_REQ_SIZE];
- } usb_ctrl_req_t;
- _Static_assert(sizeof(usb_ctrl_req_t) == USB_CTRL_REQ_SIZE, "Size of usb_ctrl_req_t incorrect");
- /**
- * @brief Bit masks belonging to the bRequestType field of a setup packet
- */
- #define USB_B_REQUEST_TYPE_DIR_OUT (0X00 << 7)
- #define USB_B_REQUEST_TYPE_DIR_IN (0x01 << 7)
- #define USB_B_REQUEST_TYPE_TYPE_STANDARD (0x00 << 5)
- #define USB_B_REQUEST_TYPE_TYPE_CLASS (0x01 << 5)
- #define USB_B_REQUEST_TYPE_TYPE_VENDOR (0x02 << 5)
- #define USB_B_REQUEST_TYPE_TYPE_RESERVED (0x03 << 5)
- #define USB_B_REQUEST_TYPE_TYPE_MASK (0x03 << 5)
- #define USB_B_REQUEST_TYPE_RECIP_DEVICE (0x00 << 0)
- #define USB_B_REQUEST_TYPE_RECIP_INTERFACE (0x01 << 0)
- #define USB_B_REQUEST_TYPE_RECIP_ENDPOINT (0x02 << 0)
- #define USB_B_REQUEST_TYPE_RECIP_OTHER (0x03 << 0)
- #define USB_B_REQUEST_TYPE_RECIP_MASK (0x1f << 0)
- /**
- * @brief Bit masks belonging to the bRequest field of a setup packet
- */
- #define USB_B_REQUEST_GET_STATUS 0x00
- #define USB_B_REQUEST_CLEAR_FEATURE 0x01
- #define USB_B_REQUEST_SET_FEATURE 0x03
- #define USB_B_REQUEST_SET_ADDRESS 0x05
- #define USB_B_REQUEST_GET_DESCRIPTOR 0x06
- #define USB_B_REQUEST_SET_DESCRIPTOR 0x07
- #define USB_B_REQUEST_GET_CONFIGURATION 0x08
- #define USB_B_REQUEST_SET_CONFIGURATION 0x09
- #define USB_B_REQUEST_GET_INTERFACE 0x0A
- #define USB_B_REQUEST_SET_INTERFACE 0x0B
- #define USB_B_REQUEST_SYNCH_FRAME 0x0C
- /**
- * @brief Bit masks belonging to the wValue field of a setup packet
- */
- #define USB_W_VALUE_DT_DEVICE 0x01
- #define USB_W_VALUE_DT_CONFIG 0x02
- #define USB_W_VALUE_DT_STRING 0x03
- #define USB_W_VALUE_DT_INTERFACE 0x04
- #define USB_W_VALUE_DT_ENDPOINT 0x05
- #define USB_W_VALUE_DT_DEVICE_QUALIFIER 0x06
- #define USB_W_VALUE_DT_OTHER_SPEED_CONFIG 0x07
- #define USB_W_VALUE_DT_INTERFACE_POWER 0x08
- /**
- * @brief Initializer for a SET_ADDRESS request
- *
- * Sets the address of a connected device
- */
- #define USB_CTRL_REQ_INIT_SET_ADDR(ctrl_req_ptr, addr) ({ \
- (ctrl_req_ptr)->bRequestType = USB_B_REQUEST_TYPE_DIR_OUT | USB_B_REQUEST_TYPE_TYPE_STANDARD |USB_B_REQUEST_TYPE_RECIP_DEVICE; \
- (ctrl_req_ptr)->bRequest = USB_B_REQUEST_SET_ADDRESS; \
- (ctrl_req_ptr)->wValue = (addr); \
- (ctrl_req_ptr)->wIndex = 0; \
- (ctrl_req_ptr)->wLength = 0; \
- })
- /**
- * @brief Initializer for a request to get a device's device descriptor
- */
- #define USB_CTRL_REQ_INIT_GET_DEVC_DESC(ctrl_req_ptr) ({ \
- (ctrl_req_ptr)->bRequestType = USB_B_REQUEST_TYPE_DIR_IN | USB_B_REQUEST_TYPE_TYPE_STANDARD | USB_B_REQUEST_TYPE_RECIP_DEVICE; \
- (ctrl_req_ptr)->bRequest = USB_B_REQUEST_GET_DESCRIPTOR; \
- (ctrl_req_ptr)->wValue = (USB_W_VALUE_DT_DEVICE << 8); \
- (ctrl_req_ptr)->wIndex = 0; \
- (ctrl_req_ptr)->wLength = 18; \
- })
- /**
- * @brief Initializer for a request to get a device's current configuration number
- */
- #define USB_CTRL_REQ_INIT_GET_CONFIG(ctrl_req_ptr) ({ \
- (ctrl_req_ptr)->bRequestType = USB_B_REQUEST_TYPE_DIR_IN | USB_B_REQUEST_TYPE_TYPE_STANDARD | USB_B_REQUEST_TYPE_RECIP_DEVICE; \
- (ctrl_req_ptr)->bRequest = USB_B_REQUEST_GET_CONFIGURATION; \
- (ctrl_req_ptr)->wValue = 0; \
- (ctrl_req_ptr)->wIndex = 0; \
- (ctrl_req_ptr)->wLength = 1; \
- })
- /**
- * @brief Initializer for a request to get one of the device's current configuration descriptor
- *
- * - desc_index indicates the configuration's index number
- * - Number of bytes of the configuration descriptor to get
- */
- #define USB_CTRL_REQ_INIT_GET_CFG_DESC(ctrl_req_ptr, desc_index, desc_len) ({ \
- (ctrl_req_ptr)->bRequestType = USB_B_REQUEST_TYPE_DIR_IN | USB_B_REQUEST_TYPE_TYPE_STANDARD | USB_B_REQUEST_TYPE_RECIP_DEVICE; \
- (ctrl_req_ptr)->bRequest = USB_B_REQUEST_GET_DESCRIPTOR; \
- (ctrl_req_ptr)->wValue = (USB_W_VALUE_DT_CONFIG << 8) | ((desc_index) & 0xFF); \
- (ctrl_req_ptr)->wIndex = 0; \
- (ctrl_req_ptr)->wLength = (desc_len); \
- })
- /**
- * @brief Initializer for a request to set a device's current configuration number
- */
- #define USB_CTRL_REQ_INIT_SET_CONFIG(ctrl_req_ptr, config_num) ({ \
- (ctrl_req_ptr)->bRequestType = USB_B_REQUEST_TYPE_DIR_OUT | USB_B_REQUEST_TYPE_TYPE_STANDARD | USB_B_REQUEST_TYPE_RECIP_DEVICE; \
- (ctrl_req_ptr)->bRequest = USB_B_REQUEST_SET_CONFIGURATION; \
- (ctrl_req_ptr)->wValue = (config_num); \
- (ctrl_req_ptr)->wIndex = 0; \
- (ctrl_req_ptr)->wLength = 0; \
- })
- /**
- * @brief Initializer for a request to set an interface's alternate setting
- */
- #define USB_CTRL_REQ_INIT_SET_INTERFACE(ctrl_req_ptr, intf_num, alt_setting_num) ({ \
- (ctrl_req_ptr)->bRequestType = USB_B_REQUEST_TYPE_DIR_OUT | USB_B_REQUEST_TYPE_TYPE_STANDARD | USB_B_REQUEST_TYPE_RECIP_INTERFACE; \
- (ctrl_req_ptr)->bRequest = USB_B_REQUEST_SET_INTERFACE; \
- (ctrl_req_ptr)->wValue = (alt_setting_num); \
- (ctrl_req_ptr)->wIndex = (intf_num); \
- (ctrl_req_ptr)->wLength = 0; \
- })
- // ------------------ Device Descriptor --------------------
- /**
- * @brief Size of a USB device descriptor in bytes
- */
- #define USB_DESC_DEVC_SIZE 18
- /**
- * @brief Structure representing a USB device descriptor
- */
- typedef union {
- struct {
- uint8_t bLength;
- uint8_t bDescriptorType;
- uint16_t bcdUSB;
- uint8_t bDeviceClass;
- uint8_t bDeviceSubClass;
- uint8_t bDeviceProtocol;
- uint8_t bMaxPacketSize0;
- uint16_t idVendor;
- uint16_t idProduct;
- uint16_t bcdDevice;
- uint8_t iManufacturer;
- uint8_t iProduct;
- uint8_t iSerialNumber;
- uint8_t bNumConfigurations;
- } USB_DESC_ATTR;
- uint8_t val[USB_DESC_DEVC_SIZE];
- } usb_desc_devc_t;
- _Static_assert(sizeof(usb_desc_devc_t) == USB_DESC_DEVC_SIZE, "Size of usb_desc_devc_t incorrect");
- /**
- * @brief Possible base class values of the bDeviceClass field of a USB device descriptor
- */
- #define USB_CLASS_PER_INTERFACE 0x00
- #define USB_CLASS_AUDIO 0x01
- #define USB_CLASS_COMM 0x02
- #define USB_CLASS_HID 0x03
- #define USB_CLASS_PHYSICAL 0x05
- #define USB_CLASS_STILL_IMAGE 0x06
- #define USB_CLASS_PRINTER 0x07
- #define USB_CLASS_MASS_STORAGE 0x08
- #define USB_CLASS_HUB 0x09
- #define USB_CLASS_CDC_DATA 0x0a
- #define USB_CLASS_CSCID 0x0b
- #define USB_CLASS_CONTENT_SEC 0x0d
- #define USB_CLASS_VIDEO 0x0e
- #define USB_CLASS_WIRELESS_CONTROLLER 0xe0
- #define USB_CLASS_PERSONAL_HEALTHCARE 0x0f
- #define USB_CLASS_AUDIO_VIDEO 0x10
- #define USB_CLASS_BILLBOARD 0x11
- #define USB_CLASS_USB_TYPE_C_BRIDGE 0x12
- #define USB_CLASS_MISC 0xef
- #define USB_CLASS_APP_SPEC 0xfe
- #define USB_CLASS_VENDOR_SPEC 0xff
- /**
- * @brief Vendor specific subclass code
- */
- #define USB_SUBCLASS_VENDOR_SPEC 0xff
- // -------------- Configuration Descriptor -----------------
- /**
- * @brief Size of a short USB configuration descriptor in bytes
- *
- * @note The size of a full USB configuration includes all the interface and endpoint
- * descriptors of that configuration.
- */
- #define USB_DESC_CFG_SIZE 9
- /**
- * @brief Structure representing a short USB configuration descriptor
- *
- * @note The full USB configuration includes all the interface and endpoint
- * descriptors of that configuration.
- */
- typedef union {
- struct {
- uint8_t bLength;
- uint8_t bDescriptorType;
- uint16_t wTotalLength;
- uint8_t bNumInterfaces;
- uint8_t bConfigurationValue;
- uint8_t iConfiguration;
- uint8_t bmAttributes;
- uint8_t bMaxPower;
- } USB_DESC_ATTR;
- uint8_t val[USB_DESC_CFG_SIZE];
- } usb_desc_cfg_t;
- _Static_assert(sizeof(usb_desc_cfg_t) == USB_DESC_CFG_SIZE, "Size of usb_desc_cfg_t incorrect");
- /**
- * @brief Bit masks belonging to the bmAttributes field of a configuration descriptor
- */
- #define USB_BM_ATTRIBUTES_ONE (1 << 7) //Must be set
- #define USB_BM_ATTRIBUTES_SELFPOWER (1 << 6) //Self powered
- #define USB_BM_ATTRIBUTES_WAKEUP (1 << 5) //Can wake-up
- #define USB_BM_ATTRIBUTES_BATTERY (1 << 4) //Battery powered
- // ---------------- Interface Descriptor -------------------
- /**
- * @brief Size of a USB interface descriptor in bytes
- */
- #define USB_DESC_INTF_SIZE 9
- /**
- * @brief Structure representing a USB interface descriptor
- */
- typedef union {
- struct {
- uint8_t bLength;
- uint8_t bDescriptorType;
- uint8_t bInterfaceNumber;
- uint8_t bAlternateSetting;
- uint8_t bNumEndpoints;
- uint8_t bInterfaceClass;
- uint8_t bInterfaceSubClass;
- uint8_t bInterfaceProtocol;
- uint8_t iInterface;
- } USB_DESC_ATTR;
- uint8_t val[USB_DESC_INTF_SIZE];
- } usb_desc_intf_t;
- _Static_assert(sizeof(usb_desc_intf_t) == USB_DESC_INTF_SIZE, "Size of usb_desc_intf_t incorrect");
- // ----------------- Endpoint Descriptor -------------------
- /**
- * @brief Size of a USB endpoint descriptor in bytes
- */
- #define USB_DESC_EP_SIZE 7
- /**
- * @brief Structure representing a USB endpoint descriptor
- */
- typedef union {
- struct {
- uint8_t bLength;
- uint8_t bDescriptorType;
- uint8_t bEndpointAddress;
- uint8_t bmAttributes;
- uint16_t wMaxPacketSize;
- uint8_t bInterval;
- } USB_DESC_ATTR;
- uint8_t val[USB_DESC_EP_SIZE];
- } usb_desc_ep_t;
- _Static_assert(sizeof(usb_desc_ep_t) == USB_DESC_EP_SIZE, "Size of usb_desc_ep_t incorrect");
- /**
- * @brief Bit masks belonging to the bEndpointAddress field of an endpoint descriptor
- */
- #define USB_B_ENDPOINT_ADDRESS_EP_NUM_MASK 0x0f
- #define USB_B_ENDPOINT_ADDRESS_EP_DIR_MASK 0x80
- /**
- * @brief Bit masks belonging to the bmAttributes field of an endpoint descriptor
- */
- #define USB_BM_ATTRIBUTES_XFERTYPE_MASK 0x03
- #define USB_BM_ATTRIBUTES_XFER_CONTROL (0 << 0)
- #define USB_BM_ATTRIBUTES_XFER_ISOC (1 << 0)
- #define USB_BM_ATTRIBUTES_XFER_BULK (2 << 0)
- #define USB_BM_ATTRIBUTES_XFER_INT (3 << 0)
- #define USB_BM_ATTRIBUTES_SYNCTYPE_MASK 0x0C /* in bmAttributes */
- #define USB_BM_ATTRIBUTES_SYNC_NONE (0 << 2)
- #define USB_BM_ATTRIBUTES_SYNC_ASYNC (1 << 2)
- #define USB_BM_ATTRIBUTES_SYNC_ADAPTIVE (2 << 2)
- #define USB_BM_ATTRIBUTES_SYNC_SYNC (3 << 2)
- #define USB_BM_ATTRIBUTES_USAGETYPE_MASK 0x30
- #define USB_BM_ATTRIBUTES_USAGE_DATA (0 << 4)
- #define USB_BM_ATTRIBUTES_USAGE_FEEDBACK (1 << 4)
- #define USB_BM_ATTRIBUTES_USAGE_IMPLICIT_FB (2 << 4)
- /**
- * @brief Macro helpers to get information about an endpoint from its descriptor
- */
- #define USB_DESC_EP_GET_XFERTYPE(desc_ptr) ((usb_transfer_type_t) ((desc_ptr)->bmAttributes & USB_BM_ATTRIBUTES_XFERTYPE_MASK))
- #define USB_DESC_EP_GET_EP_NUM(desc_ptr) ((desc_ptr)->bEndpointAddress & USB_B_ENDPOINT_ADDRESS_EP_NUM_MASK)
- #define USB_DESC_EP_GET_EP_DIR(desc_ptr) (((desc_ptr)->bEndpointAddress & USB_B_ENDPOINT_ADDRESS_EP_DIR_MASK) ? 1 : 0)
- #define USB_DESC_EP_GET_MPS(desc_ptr) ((desc_ptr)->wMaxPacketSize & 0x7FF)
- // ------------------ String Descriptor --------------------
- /**
- * @brief Size of a short USB string descriptor in bytes
- */
- #define USB_DESC_STR_SIZE 4
- /**
- * @brief Structure representing a USB string descriptor
- */
- typedef union {
- struct {
- uint8_t bLength;
- uint8_t bDescriptorType;
- uint16_t wData[1]; /* UTF-16LE encoded */
- } USB_DESC_ATTR;
- uint8_t val[USB_DESC_STR_SIZE];
- } usb_desc_str_t;
- _Static_assert(sizeof(usb_desc_str_t) == USB_DESC_STR_SIZE, "Size of usb_desc_str_t incorrect");
- #ifdef __cplusplus
- }
- #endif
|