/**
 * \file
 *
 * \brief USB Device Stack Core Layer Implementation.
 *
 * Copyright (c) 2015-2018 Microchip Technology Inc. and its subsidiaries.
 *
 * \asf_license_start
 *
 * \page License
 *
 * Subject to your compliance with these terms, you may use Microchip
 * software and any derivatives exclusively with Microchip products.
 * It is your responsibility to comply with third party license terms applicable
 * to your use of third party software (including open source software) that
 * may accompany Microchip software.
 *
 * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES,
 * WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE,
 * INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY,
 * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE
 * LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL
 * LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE
 * SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS BEEN ADVISED OF THE
 * POSSIBILITY OR THE DAMAGES ARE FORESEEABLE.  TO THE FULLEST EXTENT
 * ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY
 * RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY,
 * THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE.
 *
 * \asf_license_stop
 *
 */

#include "usbdc.h"

#define USBDC_VERSION 0x00000001u

extern bool old_extpwer_state;
volatile bool was_unconfigured_flag = false;
extern void reset_all_stuff_irq(void);

/**
 * \brief USB Device Core Sof Handler
 */
struct usbdc_sof_handler {
	struct usbdc_sof_handler *next;
	usbdc_sof_cb_t            cb;
};

/**
 * \brief USB Device Core Request Handler
 */
struct usbdc_req_handler {
	struct usbdc_req_handler *next;
	usbdc_req_cb_t            cb;
};

/**
 * \brief USB Device Core Change Handler
 */
struct usbdc_change_handler {
	struct usbdc_change_handler *next;
	usbdc_change_cb_t            cb;
};

/**
 * \brief USB Device Core Handler
 */
struct usbdc_handlers {
	struct list_descriptor sof_list;
	struct list_descriptor req_list;
	struct list_descriptor change_list;
};

/**
 * \brief USB Device Core Driver Structure
 */
struct usbdc_driver {
	/** Pointer to descriptions of descriptors. */
	struct usbdc_descriptors desces;
	/** Callback handlers. */
	struct usbdc_handlers handlers;
	/** list of function drivers. */
	struct list_descriptor func_list;
	/** Control buffer. */
	uint8_t *ctrl_buf;
	/** Device status. */
	uint16_t status;
	/** Device state. */
	uint8_t state;
	/** Configuration value. */
	uint8_t cfg_value;
	/** Control endpoint size. */
	uint8_t ctrl_size;
	/** Alternate interface used map */
	uint8_t ifc_alt_map;
};

/**
 * \brief USB Device Core Driver Instance
 */
static struct usbdc_driver usbdc;

/**
 * \brief Process the GetDeviceDescriptor request
 * \param[in] ep Endpoint address.
 * \param[in] req Pointer to the request.
 * \return Operation status.
 * \retval true Request is handled OK.
 * \retval false Request not supported.
 */
static bool usbdc_get_dev_desc(const uint8_t ep, struct usb_req *req)
{
	uint8_t *dev_desc = NULL;
	uint16_t length   = req->wLength;
	if (length > 0x12) {
		length = 0x12;
	}
#if CONF_USBD_HS_SP
	if (usb_d_get_speed() == USB_SPEED_HS && usbdc.desces.hs) {
		dev_desc = usb_find_desc(usbdc.desces.hs->sod, usbdc.desces.hs->eod, USB_DT_DEVICE);
	} else {
		/* Obtain descriptor from FS descriptors */
	}
#endif
	if (!dev_desc) {
		dev_desc = usb_find_desc(usbdc.desces.ls_fs->sod, usbdc.desces.ls_fs->eod, USB_DT_DEVICE);
	}
	if (!dev_desc) {
		return false;
	}
	if (ERR_NONE != usbdc_xfer(ep, dev_desc, length, false)) {
		return false;
	}
	return true;
}

/**
 * \brief Process the GetConfigurationDescriptor request
 * \param[in] ep Endpoint address.
 * \param[in] req Pointer to the request.
 * \return Operation status.
 * \retval true Request is handled OK.
 * \retval false Request not supported.
 */
static bool usbdc_get_cfg_desc(const uint8_t ep, struct usb_req *req)
{
	uint8_t *cfg_desc = NULL;
	uint16_t total_len;
	uint16_t length   = req->wLength;
	uint8_t  index    = req->wValue & 0x00FF;
	bool     need_zlp = !(length & (usbdc.ctrl_size - 1));

#if CONF_USBD_HS_SP
	if (usb_d_get_speed() == USB_SPEED_HS && usbdc.desces.hs) {
		cfg_desc = usb_find_cfg_desc(usbdc.desces.hs->sod, usbdc.desces.hs->eod, index + 1);
	} else {
		/* Obtain descriptor from FS descriptors */
	}
#endif
	if (!cfg_desc) {
		cfg_desc = usb_find_cfg_desc(usbdc.desces.ls_fs->sod, usbdc.desces.ls_fs->eod, index + 1);
	}
	if (NULL == cfg_desc) {
		return false;
	}
	total_len = usb_cfg_desc_total_len(cfg_desc);
	if (length <= total_len) {
		need_zlp = false;
	} else {
		length = total_len;
	}
	if (ERR_NONE != usbdc_xfer(ep, cfg_desc, length, need_zlp)) {
		return false;
	}
	return true;
}

/**
 * \brief Process the GetStringDescriptor request
 * \param[in] ep Endpoint address.
 * \param[in] req Pointer to the request.
 * \return Operation status.
 * \retval true Request is handled OK.
 * \retval false Request not supported.
 */
static bool usbdc_get_str_desc(const uint8_t ep, struct usb_req *req)
{
	uint8_t *str_desc;
	uint16_t length   = req->wLength;
	uint8_t  index    = req->wValue & 0x00FF;
	bool     need_zlp = !(length & (usbdc.ctrl_size - 1));
	/* All string are in default descriptors block: FS/LS */
	str_desc = usb_find_str_desc(usbdc.desces.ls_fs->sod, usbdc.desces.ls_fs->eod, index);
	if (NULL == str_desc) {
		return false;
	}
	if (length <= str_desc[0]) {
		need_zlp = false;
	} else {
		length = str_desc[0];
	}
	if (ERR_NONE != usbdc_xfer(ep, str_desc, length, need_zlp)) {
		return false;
	}
	return true;
}

#if CONF_USBD_HS_SP
/**
 * \brief Process the GetDeviceQualifierDescriptor request
 * \param[in] ep Endpoint address.
 * \param[in] req Pointer to the request.
 * \return Operation status.
 * \retval true Request is handled OK.
 * \retval false Request not supported.
 */
static bool usbdc_get_devqual_desc(const uint8_t ep, struct usb_req *req)
{
	uint8_t *dev_desc = NULL;
	uint16_t length   = req->wLength;
	if (length > 0x12) {
		length = 0x12;
	}
	if (usb_d_get_speed() == USB_SPEED_HS && usbdc.desces.hs) {
		dev_desc = usb_find_desc(usbdc.desces.hs->sod, usbdc.desces.hs->eod, USB_DT_DEVICE_QUALIFIER);
	}
	if (!dev_desc) {
		dev_desc = usb_find_desc(usbdc.desces.ls_fs->sod, usbdc.desces.ls_fs->eod, USB_DT_DEVICE_QUALIFIER);
	}
	if (!dev_desc) {
		return false;
	}
	if (ERR_NONE != usbdc_xfer(ep, dev_desc, length, false)) {
		return false;
	}
	return true;
}

/**
 * \brief Process the GetOtherSpeedConfigurationDescriptor request
 * \param[in] ep Endpoint address.
 * \param[in] req Pointer to the request.
 * \return Operation status.
 * \retval true Request is handled OK.
 * \retval false Request not supported.
 */
static bool usbdc_get_othspdcfg_desc(const uint8_t ep, struct usb_req *req)
{
	uint8_t *cfg_desc = NULL;
	uint16_t total_len;
	uint16_t length   = req->wLength;
	uint8_t  index    = req->wValue & 0x00FF;
	bool     need_zlp = !(length & (usbdc.ctrl_size - 1));

	if (usb_d_get_speed() == USB_SPEED_HS && usbdc.desces.hs) {
		cfg_desc = usb_find_othspdcfg_desc(usbdc.desces.hs->sod, usbdc.desces.hs->eod, index + 1);
	} else {
		/* Obtain descriptor from FS descriptors */
	}
	if (!cfg_desc) {
		cfg_desc = usb_find_othspdcfg_desc(usbdc.desces.ls_fs->sod, usbdc.desces.ls_fs->eod, index + 1);
	}
	if (NULL == cfg_desc) {
		return false;
	}
	total_len = usb_cfg_desc_total_len(cfg_desc);
	if (length <= total_len) {
		need_zlp = false;
	} else {
		length = total_len;
	}
	if (ERR_NONE != usbdc_xfer(ep, cfg_desc, length, need_zlp)) {
		return false;
	}
	return true;
}
#endif

/**
 * \brief Process the GetDescriptor request
 * \param[in] ep Endpoint address.
 * \param[in] req Pointer to the request.
 * \return Operation status.
 * \retval true Request is handled OK.
 * \retval false Request not supported.
 */
static bool usbdc_get_desc_req(const uint8_t ep, struct usb_req *req)
{
	uint8_t type = (uint8_t)(req->wValue >> 8);
	switch (type) {
	case USB_DT_DEVICE:
		return usbdc_get_dev_desc(ep, req);
	case USB_DT_CONFIG:
		return usbdc_get_cfg_desc(ep, req);
#if CONF_USBD_HS_SP
	case USB_DT_DEVICE_QUALIFIER:
		return usbdc_get_devqual_desc(ep, req);
	case USB_DT_OTHER_SPEED_CONFIG:
		return usbdc_get_othspdcfg_desc(ep, req);
#endif
	case USB_DT_STRING:
		return usbdc_get_str_desc(ep, req);
	default:
		break;
	}
	return false;
}

/**
 * \brief Process the GetStatus request
 * \param[in] ep Endpoint address.
 * \param[in] req Pointer to the request.
 * \return Operation status.
 * \retval true Request is handled OK.
 * \retval false Request not supported.
 */
static bool usbdc_get_status_req(const uint8_t ep, const struct usb_req *req)
{
	int32_t st;
	(void)ep;

	switch (req->bmRequestType & USB_REQT_RECIP_MASK) {
	case USB_REQT_RECIP_DEVICE:
		st = old_extpwer_state ? 0x0 : 0x1;
		break;
	case USB_REQT_RECIP_INTERFACE:
		st = 0;
		break;
	case USB_REQT_RECIP_ENDPOINT:
		st = usb_d_ep_halt(req->wIndex & 0xFF, USB_EP_HALT_GET);
		if (st < 0) {
			return false;
		}
		st = st & 0x1;
		break;
	default:
		return false;
	}
	memcpy(usbdc.ctrl_buf, &st, 2);
	usbdc_xfer(ep, usbdc.ctrl_buf, 2, false);
	return true;
}

/**
 * \brief Process the standard Get Interface
 * \param[in] req Point to usb request struct.
 * \return Operation status.
 * \retval true Request is handled OK.
 * \retval false Request not supported.
 */
static bool usbdc_get_interface(struct usb_req *req)
{
	struct usbdf_driver *func = (struct usbdf_driver *)usbdc.func_list.head;
	int32_t              rc;

	if (!(usbdc.ifc_alt_map & (1 << req->wIndex))) {
		/* Return 0 if alternate is not used */
		usbdc.ctrl_buf[0] = 0;
		usbdc_xfer(0, usbdc.ctrl_buf, 1, false);
		return true;
	}
	/* Check function drivers only if alternate is used */
	while (NULL != func) {
		if (0 > (rc = func->ctrl(func, USBDF_GET_IFACE, req))) {
			func = func->next;
		} else {
			usbdc.ctrl_buf[0] = (uint8_t)rc;
			usbdc_xfer(0, usbdc.ctrl_buf, 1, false);
			return true;
		}
	}
	return false;
}

/**
 * \brief Process the standard Get request
 * \param[in] ep Endpoint address.
 * \param[in] req Pointer to the request.
 * \return Operation status.
 * \retval true Request is handled OK.
 * \retval false Request not supported.
 */
static bool usbdc_get_req(const uint8_t ep, struct usb_req *req)
{
	switch (req->bRequest) {
	case USB_REQ_GET_DESC:
		return usbdc_get_desc_req(ep, req);
	case USB_REQ_GET_CONFIG:
		*(uint8_t *)usbdc.ctrl_buf = usbdc.cfg_value;
		usbdc_xfer(ep, usbdc.ctrl_buf, 1, false);
		return true;
	case USB_REQ_GET_STATUS:
		return usbdc_get_status_req(ep, req);
	case USB_REQ_GET_INTERFACE:
		return usbdc_get_interface(req);
	default:
		return false;
	}
}

/**
 * \brief Process the standard ClearFeature request
 * \param[in] ep Endpoint address.
 * \param[in] req Pointer to the request.
 * \return Operation status.
 * \retval true Request is handled OK.
 * \retval false Request not supported.
 */
static bool usbdc_clear_ftr_req(const uint8_t ep, const struct usb_req *req)
{
	(void)ep;
	switch (req->bmRequestType & USB_REQT_RECIP_MASK) {
	case USB_REQT_RECIP_ENDPOINT:
		if (req->wLength != 0) {
			return false;
		}
		usb_d_ep_halt(req->wIndex & 0xFF, USB_EP_HALT_CLR);
		usbdc_xfer(ep, NULL, 0, true);
		return true;
	default:
		return false;
	}
}

/**
 * \brief Process the standard SetFeature request
 * \param[in] ep Endpoint address.
 * \param[in] req Pointer to the request.
 * \return Operation status.
 * \retval true Request is handled OK.
 * \retval false Request not supported.
 */
static bool usbdc_set_ftr_req(const uint8_t ep, const struct usb_req *req)
{
	(void)ep;
	switch (req->bmRequestType & USB_REQT_RECIP_MASK) {
	case USB_REQT_RECIP_ENDPOINT:
		if (req->wLength != 0) {
			return false;
		}
		usb_d_ep_halt(req->wIndex & 0xFF, USB_EP_HALT_SET);
		usbdc_xfer(ep, NULL, 0, true);
		return true;
	default:
		return false;
	}
}

/**
 * \brief Unconfig, close all interfaces
 */
static void usbdc_unconfig(void)
{
	reset_all_stuff_irq();
	was_unconfigured_flag = true;

	struct usbdf_driver *func = (struct usbdf_driver *)usbdc.func_list.head;
	while (NULL != func) {
		func->ctrl(func, USBDF_DISABLE, NULL);
		func = func->next;
	}
}

/**
 * \brief Apply Set Configuration Value
 * \param[in] cfg_value Configuration Value
 * \retval true Set configuration OK.
 * \retval false Request error.
 */
static bool usbdc_set_config(uint8_t cfg_value)
{
	struct usbd_descriptors desc;
	struct usbdf_driver *   func;
	uint8_t *               cfg_desc = NULL;
	uint16_t                total_len;
	uint8_t                 last_iface = 0xFF;

	if (cfg_value == 0) {
		usbdc_unconfig();
		return true;
	}

#if CONF_USBD_HS_SP
	if (usb_d_get_speed() == USB_SPEED_HS && usbdc.desces.hs) {
		cfg_desc = usb_find_cfg_desc(usbdc.desces.hs->sod, usbdc.desces.hs->eod, cfg_value);
	} else {
		/* Obtain descriptor from FS descriptors */
	}
#endif
	if (!cfg_desc) {
		cfg_desc = usb_find_cfg_desc(usbdc.desces.ls_fs->sod, usbdc.desces.ls_fs->eod, cfg_value);
	}
	if (NULL == cfg_desc) {
		return false;
	}

	total_len = usb_cfg_desc_total_len(cfg_desc);
	desc.eod  = cfg_desc + total_len;
	desc.sod  = usb_find_desc(cfg_desc, desc.eod, USB_DT_INTERFACE);

	while (NULL != desc.sod) {
		/* Apply very first alternate setting (must be 0) of the interface */
		if (last_iface != desc.sod[2] /* bInterfaceNumber */) {
			last_iface = desc.sod[2];
			func       = (struct usbdf_driver *)usbdc.func_list.head;
			while (NULL != func) {
				if (func->ctrl(func, USBDF_ENABLE, &desc)) {
					func = func->next;
				} else {
					break;
				}
			}
		}
		desc.sod = usb_desc_next(desc.sod);
		desc.sod = usb_find_desc(desc.sod, desc.eod, USB_DT_INTERFACE);
	}
	return true;
}

/**
 * \brief Apply the USB device address
 * \param[in] addr address to be set.
 */
static void usbdc_set_address(uint8_t addr)
{
	usb_d_set_address(addr);
}

/**
 * \brief Process the standard Set Interface
 * \param[in] alt_set Alternate Setting.
 * \param[in] ifc_id Interface Index.
 * \return Operation status.
 * \retval true Request is handled OK.
 * \retval false Request not supported.
 */
static bool usbdc_set_interface(uint16_t alt_set, uint16_t ifc_id)
{
	struct usbd_descriptors desc;
	struct usbdf_driver *   func;
	uint8_t *               ifc = NULL;

#if CONF_USBD_HS_SP
	if (usb_d_get_speed() == USB_SPEED_HS && usbdc.desces.hs) {
		ifc = usb_find_cfg_desc(usbdc.desces.hs->sod, usbdc.desces.hs->eod, usbdc.cfg_value);
	} else {
		/* Obtain descriptor from FS descriptors */
	}
#endif
	if (!ifc) {
		ifc = usb_find_cfg_desc(usbdc.desces.ls_fs->sod, usbdc.desces.ls_fs->eod, usbdc.cfg_value);
	}
	if (NULL == ifc) {
		return false;
	}
	desc.sod = ifc;
	desc.eod = ifc + usb_cfg_desc_total_len(ifc);

	if (NULL == (ifc = usb_find_desc(desc.sod, desc.eod, USB_DT_INTERFACE))) {
		return false;
	}

	while (ifc[2] != ifc_id || ifc[3] != alt_set) {
		desc.sod = usb_desc_next(desc.sod);
		ifc      = usb_find_desc(desc.sod, desc.eod, USB_DT_INTERFACE);
		if (NULL == ifc) {
			return false;
		}
	}

	desc.sod = ifc;
	func     = (struct usbdf_driver *)usbdc.func_list.head;

	while (NULL != func) {
		if (func->ctrl(func, USBDF_DISABLE, &desc)) {
			func = func->next;
		} else if (ERR_NONE == func->ctrl(func, USBDF_ENABLE, &desc)) {
			if (alt_set) {
				/* Alternate settings are used from now on */
				usbdc.ifc_alt_map |= 1 << ifc_id;
			}
			usbdc_xfer(0, NULL, 0, 0);
			return true;
		} else {
			return false;
		}
	}

	return false;
}

/**
 * \brief Process the standard Set request
 * \param[in] ep Endpoint address.
 * \param[in] req Pointer to the request.
 * \return Operation status.
 * \retval true Request is handled OK.
 * \retval false Request not supported.
 */
static bool usbdc_set_req(const uint8_t ep, struct usb_req *req)
{
	switch (req->bRequest) {
	case USB_REQ_SET_ADDRESS:
		return (ERR_NONE == usbdc_xfer(ep, NULL, 0, true));
	case USB_REQ_SET_CONFIG:
		if (!usbdc_set_config(req->wValue)) {
			return false;
		}
		return (ERR_NONE == usbdc_xfer(ep, NULL, 0, true));
	case USB_REQ_CLEAR_FTR:
		return usbdc_clear_ftr_req(ep, req);
	case USB_REQ_SET_FTR:
		return usbdc_set_ftr_req(ep, req);
	case USB_REQ_SET_INTERFACE:
		return usbdc_set_interface(req->wValue, req->wIndex);
	default:
		return false;
	}
}

/** Invoke all registered SOF callbacks. */
static void usbdc_sof_notify(void)
{
	struct usbdc_sof_handler *sof = (struct usbdc_sof_handler *)usbdc.handlers.sof_list.head;

	while (sof != NULL) {
		if (NULL != sof->cb) {
			sof->cb();
		}
		sof = sof->next;
	}
}

/** Invoke all registered Change notification callbacks. */
static void usbdc_change_notify(enum usbdc_change_type change, uint32_t value)
{
	struct usbdc_change_handler *cg = (struct usbdc_change_handler *)usbdc.handlers.change_list.head;

	while (cg != NULL) {
		if (NULL != cg->cb) {
			cg->cb(change, value);
		}
		cg = cg->next;
	}
}

/** Invoke all registered request callbacks until request handled. */
static int32_t usbdc_request_handler(uint8_t ep, struct usb_req *req, enum usb_ctrl_stage stage)
{
	struct usbdc_req_handler *h = (struct usbdc_req_handler *)usbdc.handlers.req_list.head;
	int32_t                   rc;

	while (h != NULL) {
		if (NULL != h->cb) {
			rc = h->cb(ep, req, stage);
			if (0 == rc) {
				return true;
			} else if (ERR_NOT_FOUND != rc) {
				return -1;
			}
		}
		h = h->next;
	}
	return false;
}

/**
 * \brief Callback invoked on USB device SOF
 */
static void usbd_sof_cb(void)
{
	usbdc_sof_notify();
}

/**
 * \brief Callback invoked when control request is received
 * \param[in] ep Endpoint address.
 * \param[in] req Pointer to the request.
 * \return Operation status.
 * \retval true Request is handled OK.
 * \retval false Request not supported.
 */
static bool usbdc_cb_ctl_req(const uint8_t ep, struct usb_req *req)
{
	switch (usbdc_request_handler(ep, req, USB_SETUP_STAGE)) {
	case true:
		return true;
	case -1:
		return false;
	default:
		break;
	}

	// STD request handling
	switch (req->bmRequestType & (USB_REQT_TYPE_MASK | USB_REQT_DIR_IN)) {
	case USB_REQT_TYPE_STANDARD:
		return usbdc_set_req(ep, req);
	case (USB_REQT_TYPE_STANDARD | USB_REQT_DIR_IN):
		return usbdc_get_req(ep, req);
	default:
		return false;
	}
}

/**
 * \brief When control status stage is end
 * \param[in] req Pointer to the request.
 */
static void usbdc_ctrl_status_end(const struct usb_req *req)
{
	if (req->bmRequestType != USB_REQT_TYPE_STANDARD) {
		return;
	}
	switch (req->bRequest) {
	case USB_REQ_SET_CONFIG:
		usbdc.cfg_value = req->wValue;
		usbdc.state     = req->wValue ? USBD_S_CONFIG : USBD_S_ADDRESS;
		usbdc_change_notify(USBDC_C_STATE, usbdc.state);
		break;
	case USB_REQ_SET_ADDRESS:
		usbdc_set_address(req->wValue);
		usbdc.state = req->wValue ? USBD_S_ADDRESS : USBD_S_DEFAULT;
		usbdc_change_notify(USBDC_C_STATE, usbdc.state);
		break;
	default:
		break;
	}
}

/**
 * \brief When control data stage is end
 * \param[in] req Pointer to the request.
 */
static bool usbdc_ctrl_data_end(struct usb_req *req)
{
	usbdc_request_handler(0, req, USB_DATA_STAGE);
	return false;
}

/**
 * \brief Callback invoked when control data done or status done
 * \param[in] ep Endpoint number with direction on bit 8.
 * \param[in] code Status code.
 * \param[in] req Pointer to the control setup request.
 * \return Data has error or not.
 * \retval true There is data error, protocol error.
 * \retval false There is no data error.
 */
static bool usbdc_cb_ctl_done(const uint8_t ep, const enum usb_xfer_code code, struct usb_req *req)
{
	(void)ep;

	switch (code) {
	case USB_XFER_DONE:
		usbdc_ctrl_status_end(req);
		break;
	case USB_XFER_DATA:
		return usbdc_ctrl_data_end(req);
	default:
		break;
	}
	return false;
}

/**
 * \brief USB Device Core Reset
 */
void usbdc_reset(void)
{
	usbdc_unconfig();

	usbdc.state       = USBD_S_DEFAULT;
	usbdc.cfg_value   = 0;
	usbdc.ifc_alt_map = 0;

	// Setup EP0
	usb_d_ep_deinit(0);
	usb_d_ep0_init(usbdc.ctrl_size);
	usb_d_ep_register_callback(0, USB_D_EP_CB_SETUP, (FUNC_PTR)usbdc_cb_ctl_req);
	usb_d_ep_register_callback(0, USB_D_EP_CB_XFER, (FUNC_PTR)usbdc_cb_ctl_done);
	usb_d_ep_enable(0);
}

/**
 * \brief Callback invoked on USB device events
 * \param[in] ev Event code.
 * \param[in] param Event parameter for event handling.
 */
static void usbd_event_cb(const enum usb_event ev, const uint32_t param)
{
	(void)param;

	switch (ev) {
	case USB_EV_VBUS:
		usbdc_change_notify(USBDC_C_CONN, param);
		break;

	case USB_EV_RESET:
		usbdc_reset();
		break;

	default:
		break;
	}
}

/**
 * \brief Issue USB device transfer
 */
int32_t usbdc_xfer(uint8_t ep, uint8_t *buf, uint32_t size, bool zlp)
{
	struct usb_d_transfer xfer = {(uint8_t *)buf, size, ep, zlp};
	return usb_d_ep_transfer(&xfer);
}

/**
 * \brief Register the handler
 */
void usbdc_register_handler(enum usbdc_handler_type type, const struct usbdc_handler *h)
{
	switch (type) {
	case USBDC_HDL_SOF:
		list_insert_at_end(&usbdc.handlers.sof_list, (void *)h);
		break;
	case USBDC_HDL_REQ:
		list_insert_at_end(&usbdc.handlers.req_list, (void *)h);
		break;
	case USBDC_HDL_CHANGE:
		list_insert_at_end(&usbdc.handlers.change_list, (void *)h);
		break;
	default:
		break;
	}
}

/**
 * \brief Unregister the handler
 */
void usbdc_unregister_handler(enum usbdc_handler_type type, const struct usbdc_handler *h)
{
	switch (type) {
	case USBDC_HDL_SOF:
		list_delete_element(&usbdc.handlers.sof_list, (void *)h);
		break;
	case USBDC_HDL_REQ:
		list_delete_element(&usbdc.handlers.req_list, (void *)h);
		break;
	case USBDC_HDL_CHANGE:
		list_delete_element(&usbdc.handlers.change_list, (void *)h);
		break;
	default:
		break;
	}
}

/**
 * \brief Initialize the USB device core driver
 */
int32_t usbdc_init(uint8_t *ctrl_buf)
{
	ASSERT(ctrl_buf);

	int32_t rc;

	rc = usb_d_init();
	if (rc < 0) {
		return rc;
	}

	memset(&usbdc, 0, sizeof(usbdc));
	usbdc.ctrl_buf = ctrl_buf;
	usb_d_register_callback(USB_D_CB_SOF, (FUNC_PTR)usbd_sof_cb);
	usb_d_register_callback(USB_D_CB_EVENT, (FUNC_PTR)usbd_event_cb);

	return 0;
}

/**
 * \brief De-initialize the USB device core driver
 */
int32_t usbdc_deinit(void)
{
	usb_d_deinit();
	return 0;
}

/**
 * \brief Register/unregister function support of a USB device function
 *
 * Must be invoked when USB device is stopped.
 */
void usbdc_register_function(struct usbdf_driver *func)
{
	list_insert_at_end(&usbdc.func_list, func);
}

/**
 * \brief Unregister function support of a USB device function
 *
 * Must be invoked when USB device is stopped.
 */
void usbdc_unregister_function(struct usbdf_driver *func)
{
	list_delete_element(&usbdc.func_list, func);
}

/**
 * \brief Validate the descriptor
 */
int32_t usbdc_validate_desces(struct usbd_descriptors *desces)
{
	uint8_t *sod, *eod;
	if (desces == NULL) {
		return ERR_NOT_FOUND;
	}
	sod = usb_find_desc(desces->sod, desces->eod, USB_DT_DEVICE);
	if (sod == NULL) {
		return ERR_BAD_DATA;
	}
	sod = usb_find_desc(desces->sod, desces->eod, USB_DT_CONFIG);
	if (sod == NULL) {
		return ERR_BAD_DATA;
	}
	eod = sod + usb_cfg_desc_total_len(sod);
	if (eod > desces->eod) {
		return ERR_BAD_DATA;
	}
	return 0;
}

/**
 * \brief Validate the descriptor
 */
int32_t usbdc_check_desces(struct usbdc_descriptors *desces)
{
#if CONF_USBD_HS_SP
	int32_t rc;
	if (desces->hs == NULL && desces->ls_fs == NULL) {
		return ERR_NOT_FOUND;
	}
	if (desces->hs) {
		rc = usbdc_validate_desces(desces->hs);
		if (rc < 0) {
			return rc;
		}
	}
#endif
	return usbdc_validate_desces(desces->ls_fs);
}

/**
 * \brief Start the USB device driver with specific descriptors set
 */
int32_t usbdc_start(struct usbd_descriptors *desces)
{
	if (usbdc.state >= USBD_S_POWER) {
		return ERR_BUSY;
	}

	if (desces) {
		usbdc.desces.ls_fs = desces;
#if CONF_USBD_HS_SP
		usbdc.desces.hs = &desces[1];
#endif
	} else {
		return ERR_BAD_DATA;
	}

	usbdc.ctrl_size = desces->sod[7];
	usbdc.state     = USBD_S_POWER;
	usb_d_enable();
	return ERR_NONE;
}

/**
 * \brief Stop the USB device driver
 */
int32_t usbdc_stop(void)
{
	usb_d_disable();
	usbdc.state = USBD_S_OFF;
	return ERR_NONE;
}

/**
 * \brief Attach the USB device to host
 */
void usbdc_attach(void)
{
	usb_d_attach();
}

/**
 * \brief Detach the USB device from host
 */
void usbdc_detach(void)
{
	usb_d_detach();
}

/**
 * \brief Send remote wakeup to host
 */
void usbdc_remotewakeup(void)
{
	usb_d_send_remotewakeup();
	usbdc.state = USBD_S_POWER;
}

/**
 * \brief Return USB Device endpoint0 buffer
 */
uint8_t *usbdc_get_ctrl_buffer(void)
{
	return usbdc.ctrl_buf;
}

/**
 * \brief Return current USB state
 */
uint8_t usbdc_get_state(void)
{
	if (usbdc.state & USBD_S_SUSPEND) {
		return USBD_S_SUSPEND;
	}
	return usbdc.state;
}

/**
 * \brief Return version
 */
uint32_t usbdc_get_version(void)
{
	return USBDC_VERSION;
}
