#define STANDARD_ENDPOINT_DESC_SIZE             0x09

/* endpoints enumeration */
#define ENDP0       ((uint8_t)0)
#define ENDP1       ((uint8_t)1)
#define ENDP2       ((uint8_t)2)
#define ENDP3       ((uint8_t)3)
#define ENDP4       ((uint8_t)4)
#define ENDP5       ((uint8_t)5)
#define ENDP6       ((uint8_t)6)
#define ENDP7       ((uint8_t)7)

enum RECIPIENT_TYPE
{
  DEVICE_RECIPIENT = 0, /* Recipient device    */
  INTERFACE_RECIPIENT,  /* Recipient interface */
  ENDPOINT_RECIPIENT,   /* Recipient endpoint  */
  OTHER_RECIPIENT
};

enum DESCRIPTOR_TYPE
{
  DEVICE_DESCRIPTOR = 1,
  CONFIG_DESCRIPTOR,
  STRING_DESCRIPTOR,
  INTERFACE_DESCRIPTOR,
  ENDPOINT_DESCRIPTOR
};

#define REQUEST_DIR       0x80  /* Mask to get request dir  */
#define REQUEST_TYPE      0x60  /* Mask to get request type */
#define STANDARD_REQUEST  0x00  /* Standard request         */
#define CLASS_REQUEST     0x20  /* Class request            */
#define VENDOR_REQUEST    0x40  /* Vendor request           */
#define RECIPIENT         0x1F  /* Mask to get recipient    */

#define USB_SETUP_SET(req) ((req & REQUEST_DIR) == 0)
#define USB_SETUP_GET(req) ((req & REQUEST_DIR) != 0)

struct device_req {
  uint8_t type;
  uint8_t request;
  uint16_t value;
  uint16_t index;
  uint16_t len;
};

struct ctrl_data {
  uint8_t *addr;
  uint16_t len;
  uint8_t require_zlp;
};

struct usb_dev {
  uint8_t configuration;
  uint8_t feature;
  uint8_t state;
  struct device_req dev_req;
  struct ctrl_data ctrl_data;
};

enum {
  USB_EVENT_OK=0,		/* Processed in lower layer.  */
  /* Device reset and suspend.  */
  USB_EVENT_DEVICE_RESET,
  USB_EVENT_DEVICE_SUSPEND,
  /* Device Requests (Control WRITE Transfer): Standard */
  USB_EVENT_SET_CONFIGURATION,
  USB_EVENT_SET_INTERFACE,
  USB_EVENT_SET_FEATURE_DEVICE,
  USB_EVENT_SET_FEATURE_ENDPOINT,
  USB_EVENT_CLEAR_FEATURE_DEVICE,
  USB_EVENT_CLEAR_FEATURE_ENDPOINT,
  /* Device Requests (Control READ Transfer): Standard */
  USB_EVENT_GET_STATUS_INTERFACE,
  USB_EVENT_GET_DESCRIPTOR,
  USB_EVENT_GET_INTERFACE,
  /* Device Requests (Control READ/WRITE Transfer): Non-Standard */
  USB_EVENT_CTRL_REQUEST,
  USB_EVENT_CTRL_WRITE_FINISH,
  /* Device addressed.  */
  USB_EVENT_DEVICE_ADDRESSED,
};

enum DEVICE_STATE {
  UNCONNECTED,
  ATTACHED,
  POWERED,
  SUSPENDED,
  ADDRESSED,
  CONFIGURED
};

void usb_lld_init (struct usb_dev *dev, uint8_t feature);

/*
 * Return value is encoded integer:
 *     event-no:    8-bit, 0 if TX/RX
 *     tx/rx-flag:  1-bit, 0 if rx, 1 if tx
 *     endpoint no: 7-bit
 *     length:      16-bit
 */
#define USB_EVENT_TXRX(e) ((e >> 23) & 1)
#define USB_EVENT_LEN(e) (e & 0xffff)
#define USB_EVENT_ENDP(e) ((e >> 16) & 0x7f)
#define USB_EVENT_ID(e) ((e >> 24))
int usb_lld_event_handler (struct usb_dev *dev);

/*
 * Control Endpoint ENDP0 does device requests handling.
 * In response to an event of
 *     USB_EVENT_SET_CONFIGURATION
 *     USB_EVENT_SET_INTERFACE
 *     USB_EVENT_SET_FEATURE_DEVICE
 *     USB_EVENT_SET_FEATURE_ENDPOINT
 *     USB_EVENT_CLEAR_FEATURE_DEVICE
 *     USB_EVENT_CLEAR_FEATURE_ENDPOINT
 *     USB_EVENT_GET_STATUS_INTERFACE
 *     USB_EVENT_GET_DESCRIPTOR
 *     USB_EVENT_GET_INTERFACE
 *     USB_EVENT_CTRL_REQUEST
 *  a single action should be done, which is SEND, RECV, or,
 *  ACKNOWLEDGE (no data to be sent, or to be received).
 *  Otherwise, it's an error.
 */
int usb_lld_ctrl_send (struct usb_dev *dev, const void *buf, size_t buflen);
int usb_lld_ctrl_recv (struct usb_dev *dev, void *p, size_t len);
int usb_lld_ctrl_ack (struct usb_dev *dev);
void usb_lld_ctrl_error (struct usb_dev *dev);

void usb_lld_reset (struct usb_dev *dev, uint8_t feature);
void usb_lld_set_configuration (struct usb_dev *dev, uint8_t config);
uint8_t usb_lld_current_configuration (struct usb_dev *dev);
void usb_lld_prepare_shutdown (void);
void usb_lld_shutdown (void);

#ifdef MCU_KINETIS_L
void usb_lld_tx_enable_buf (int ep_num, const void *buf, size_t len);
void usb_lld_rx_enable_buf (int ep_num, void *buf, size_t len);

void usb_lld_setup_endp (struct usb_dev *dev, int ep_num, int rx_en, int tx_en);
void usb_lld_stall (int ep_num);
#else
/* EP_TYPE[1:0] EndPoint TYPE */
#define EP_BULK        (0x0000) /* EndPoint BULK        */
#define EP_CONTROL     (0x0200) /* EndPoint CONTROL     */
#define EP_ISOCHRONOUS (0x0400) /* EndPoint ISOCHRONOUS */
#define EP_INTERRUPT   (0x0600) /* EndPoint INTERRUPT   */

void usb_lld_tx_enable (int ep_num, size_t len);
void usb_lld_rx_enable (int ep_num);

void usb_lld_setup_endpoint (int ep_num, int ep_type, int ep_kind,
			     int ep_rx_addr, int ep_tx_addr,
			     int ep_rx_memory_size);
void usb_lld_stall_tx (int ep_num);
void usb_lld_stall_rx (int ep_num);

void usb_lld_txcpy (const void *src, int ep_num, int offset, size_t len);
void usb_lld_write (uint8_t ep_num, const void *buf, size_t len);
void usb_lld_rxcpy (uint8_t *dst, int ep_num, int offset, size_t len);
void usb_lld_to_pmabuf (const void *src, uint16_t addr, size_t n);
void usb_lld_from_pmabuf (void *dst, uint16_t addr, size_t n);
#endif