rt-thread/bsp/gd32450z-eval/Libraries/GD32F4xx_usb_driver/Source/usbd_int.c

759 lines
21 KiB
C
Raw Normal View History

2017-08-22 15:52:57 +08:00
/*!
\file usbd_int.c
\brief USB device mode interrupt routines
*/
/*
Copyright (C) 2016 GigaDevice
2016-08-15, V1.0.0, firmware for GD32F4xx
*/
#include "usbd_int.h"
#include "usbd_std.h"
/* interrupt handlers */
static uint32_t usbd_intf_outep (usb_core_handle_struct *pudev);
static uint32_t usbd_intf_inep (usb_core_handle_struct *pudev);
static uint32_t usbd_intf_earlysuspend (usb_core_handle_struct *pudev);
static uint32_t usbd_intf_suspend (usb_core_handle_struct *pudev);
static uint32_t usbd_intf_resume (usb_core_handle_struct *pudev);
static uint32_t usbd_intf_sof (usb_core_handle_struct *pudev);
static uint32_t usbd_intf_rxfifo (usb_core_handle_struct *pudev);
static uint32_t usbd_intf_reset (usb_core_handle_struct *pudev);
static uint32_t usbd_intf_enumfinish (usb_core_handle_struct *pudev);
static uint32_t usbd_intf_isoinincomplete (usb_core_handle_struct *pudev);
static uint32_t usbd_intf_isooutincomplete (usb_core_handle_struct *pudev);
static uint32_t usbd_emptytxfifo_write (usb_core_handle_struct *pudev, uint8_t ep_num);
#ifdef VBUS_SENSING_ENABLED
static uint32_t usbd_intf_otg (usb_core_handle_struct *pudev);
static uint32_t usbd_intf_sessionrequest (usb_core_handle_struct *pudev);
#endif
static usb_speed_enum USB_SPEED[4] = {
[DSTAT_ENUMSPD_HS_PHY_30MHZ_OR_60MHZ] = USB_SPEED_HIGH,
[DSTAT_ENUMSPD_FS_PHY_30MHZ_OR_60MHZ] = USB_SPEED_FULL,
[DSTAT_ENUMSPD_FS_PHY_48MHZ] = USB_SPEED_FULL,
[DSTAT_ENUMSPD_LS_PHY_6MHZ] = USB_SPEED_LOW
};
static const uint8_t EP0_MAXLEN[4] = {
[DSTAT_ENUMSPD_HS_PHY_30MHZ_OR_60MHZ] = EP0MPL_64,
[DSTAT_ENUMSPD_FS_PHY_30MHZ_OR_60MHZ] = EP0MPL_64,
[DSTAT_ENUMSPD_FS_PHY_48MHZ] = EP0MPL_64,
[DSTAT_ENUMSPD_LS_PHY_6MHZ] = EP0MPL_8
};
#ifdef USBHS_DEDICATED_EP1_ENABLED
/*!
\brief USB dedicated OUT endpoint 1 interrupt service routine handler
\param[in] pudev: pointer to usb device instance
\param[out] none
\retval operation status
*/
uint32_t USBD_EP1OUT_ISR_Handler (usb_core_handle_struct *pudev)
{
uint32_t out_endp_int = 0;
uint32_t out_endp_size = 0;
out_endp_int = USB_DOEPxINTF(1);
out_endp_int &= USB_DOEP1INTEN;
/* transfer complete */
if (out_endp_int & DOEPINTF_TF) {
/* clear the interrupt bit */
USB_DOEPxINTF(1) = DOEPINTF_TF;
if (1U == pudev->cfg.dma_enable) {
out_endp_size = USB_DOEPxLEN(1);
/* handle more than one single MPS size packet */
pudev->dev.out_ep[1].xfer_count = pudev->dev.out_ep[1].endp_mps - \
(out_endp_size & DOEPLEN_TLEN);
}
/* inform upper layer: data ready */
/* receive complete */
usbd_out_transaction(pudev, 1);
}
/* endpoint disable interrupt */
if (out_endp_int & DOEPINTF_EPDIS) {
/* clear the interrupt bit */
USB_DOEPxINTF(1) = DOEPINTF_EPDIS;
}
return 1;
}
/*!
\brief USB dedicated IN endpoint 1 interrupt service routine handler
\param[in] pudev: pointer to usb device instance
\param[out] none
\retval operation status
*/
uint32_t USBD_EP1IN_ISR_Handler (usb_core_handle_struct *pudev)
{
uint32_t fifoemptymask = 0, mask = 0;
uint32_t in_endp_int = 0;
mask = USB_DIEP1INTEN;
mask |= ((USB_DIEPFEINTEN >> 1) & 0x01) << 7;
in_endp_int = USB_DIEPxINTF(1) & mask;
if (in_endp_int & DIEPINTF_TF) {
fifoemptymask = 0x01 << 1;
USB_DIEPFEINTEN &= ~fifoemptymask;
USB_DIEPxINTF(1) = DIEPINTF_TF;
/* transmit complete */
usbd_in_transaction(pudev , 1);
}
if (in_endp_int & DIEPINTF_EPDIS) {
USB_DIEPxINTF(1) = DIEPINTF_EPDIS;
}
if (in_endp_int & DIEPINTF_CITO) {
USB_DIEPxINTF(1) = DIEPINTF_CITO;
}
if (in_endp_int & DIEPINTF_EPTXFUD) {
USB_DIEPxINTF(1) = DIEPINTF_EPTXFUD;
}
if (in_endp_int & DIEPINTF_IEPNE) {
USB_DIEPxINTF(1) = DIEPINTF_IEPNE;
}
if (in_endp_int & DIEPINTF_TXFE) {
usbd_emptytxfifo_write(pudev, 1);
USB_DIEPxINTF(1) = DIEPINTF_IEPNE;
}
return 1;
}
#endif
/*!
\brief USB device-mode interrupts global service routine handler
\param[in] pudev: pointer to usb device instance
\param[out] none
\retval operation status
*/
uint32_t usbd_isr (usb_core_handle_struct *pudev)
{
uint32_t retval = 0U;
uint32_t int_status = 0U, gintf = USB_GINTF, ginten = USB_GINTEN;
/* ensure the core is in device mode */
if (DEVICE_MODE == USB_CURRENT_MODE_GET()) {
int_status = gintf & ginten;
/* there are no interrupts, avoid spurious interrupt */
if (!int_status) {
return 0U;
}
/* OUT endpoints interrupts */
if (int_status & GINTF_OEPIF) {
retval |= usbd_intf_outep(pudev);
}
/* IN endpoints interrupts */
if (int_status & GINTF_IEPIF) {
retval |= usbd_intf_inep(pudev);
}
/* mode mismatch interrupt */
if (int_status & GINTF_MFIF) {
/* clear interrupt */
USB_GINTF = GINTF_MFIF;
}
/* early suspend interrupt */
if (int_status & GINTF_ESP) {
retval |= usbd_intf_earlysuspend(pudev);
}
/* suspend interrupt */
if (int_status & GINTF_SP) {
retval |= usbd_intf_suspend(pudev);
}
/* wakeup interrupt */
if (int_status & GINTF_WKUPIF) {
retval |= usbd_intf_resume(pudev);
}
/* start of frame interrupt */
if (int_status & GINTF_SOF) {
retval |= usbd_intf_sof(pudev);
}
/* reveive fifo not empty interrupt */
if (int_status & GINTF_RXFNEIF) {
retval |= usbd_intf_rxfifo(pudev);
}
/* USB reset interrupt */
if (int_status & GINTF_RST) {
retval |= usbd_intf_reset(pudev);
}
/* enumeration has been finished interrupt */
if (int_status & GINTF_ENUMFIF) {
retval |= usbd_intf_enumfinish(pudev);
}
/* incomplete synchronization in transfer interrupt*/
if (int_status & GINTF_ISOINCIF) {
retval |= usbd_intf_isoinincomplete(pudev);
}
/* incomplete synchronization out transfer interrupt*/
if (int_status & GINTF_ISOONCIF) {
retval |= usbd_intf_isooutincomplete(pudev);
}
#ifdef VBUS_SENSING_ENABLED
/* session request interrupt */
if (int_status & GINTF_SESIF) {
retval |= usbd_intf_sessionrequest(pudev);
}
/* OTG mode interrupt */
if (int_status & GINTF_OTGIF) {
retval |= usbd_intf_otg(pudev);
}
#endif /* VBUS_SENSING_ENABLED */
}
return retval;
}
/*!
\brief indicates that an OUT endpoint has a pending interrupt
\param[in] pudev: pointer to usb device instance
\param[out] none
\retval operation status
*/
static uint32_t usbd_intf_outep (usb_core_handle_struct *pudev)
{
uint8_t endp_num = 0U;
uint32_t endp_intr = 0U;
__IO uint32_t out_endp_intr = 0U;
/* read in the device interrupt bits */
USB_DAOEP_INTR_READ(endp_intr);
while (endp_intr) {
if (endp_intr & 0x1U) {
USB_DOEP_INTR_READ(out_endp_intr, (uint16_t)endp_num);
/* transfer complete interrupt */
if (out_endp_intr & DOEPINTF_TF) {
USB_DOEPxINTF((uint16_t)endp_num) = DOEPINTF_TF;
if (1U == pudev->cfg.dma_enable) {
uint32_t xfer_size = USB_DOEPxLEN((uint16_t)endp_num) & DOEPLEN_TLEN;
pudev->dev.out_ep[endp_num].xfer_count = pudev->dev.out_ep[endp_num].endp_mps - \
xfer_size;
}
/* data receive is completed */
usbd_out_transaction(pudev, endp_num);
if (1U == pudev->cfg.dma_enable) {
if ((0U == endp_num) && (USB_CTRL_STATUS_OUT == pudev->dev.ctl_status)) {
/* prepare to receive more setup packets */
usb_ep0_startout(pudev);
}
}
}
/* endpoint disable interrupt */
if (out_endp_intr & DOEPINTF_EPDIS) {
USB_DOEPxINTF((uint16_t)endp_num) = DOEPINTF_EPDIS;
}
/* setup phase finished interrupt (just for control endpoints) */
if (out_endp_intr & DOEPINTF_STPF) {
/* setup phase is completed */
usbd_setup_transaction(pudev);
USB_DOEPxINTF((uint16_t)endp_num) = DOEPINTF_STPF;
}
/* back to back setup packets received */
if (out_endp_intr & DOEPINTF_BTBSTP) {
USB_DOEPxINTF((uint16_t)endp_num) = DOEPINTF_BTBSTP;
}
}
endp_num ++;
endp_intr >>= 1;
}
return 1U;
}
/*!
\brief indicates that an IN endpoint has a pending interrupt
\param[in] pudev: pointer to usb device instance
\param[out] none
\retval operation status
*/
static uint32_t usbd_intf_inep(usb_core_handle_struct *pudev)
{
uint8_t endp_num = 0U;
uint32_t endp_intr = 0U;
__IO uint32_t in_endp_intr = 0U;
/* get all in endpoints which have interrupts */
USB_DAIEP_INTR_READ(endp_intr);
while (endp_intr) {
if (endp_intr & 0x1U) {
USB_DIEP_INTR_READ(in_endp_intr, (uint16_t)endp_num);
if (in_endp_intr & DIEPINTF_TF) {
/* disable the fifo empty interrupt for the endpoint */
USB_DIEPFEINTEN &= ~(0x1U << endp_num);
USB_DIEPxINTF((uint16_t)endp_num) = DIEPINTF_TF;
/* data transmittion is completed */
usbd_in_transaction(pudev, endp_num);
if (1U == pudev->cfg.dma_enable) {
if ((0U == endp_num) && (USB_CTRL_STATUS_IN == pudev->dev.ctl_status)) {
/* prepare to receive more setup packets */
usb_ep0_startout(pudev);
}
}
}
if (in_endp_intr & DIEPINTF_CITO) {
USB_DIEPxINTF((uint16_t)endp_num) = DIEPINTF_CITO;
}
if (in_endp_intr & DIEPINTF_IEPNE) {
USB_DIEPxINTF((uint16_t)endp_num) = DIEPINTF_IEPNE;
}
if (in_endp_intr & DIEPINTF_EPDIS) {
USB_DIEPxINTF((uint16_t)endp_num) = DIEPINTF_EPDIS;
}
if (in_endp_intr & DIEPINTF_TXFE) {
usbd_emptytxfifo_write(pudev, endp_num);
USB_DIEPxINTF((uint16_t)endp_num) = DIEPINTF_TXFE;
}
}
endp_num ++;
endp_intr >>= 1;
}
return 1U;
}
/*!
\brief indicates that early SUSPEND state has been detected on the USB
\param[in] pudev: pointer to usb device instance
\param[out] none
\retval operation status
*/
static uint32_t usbd_intf_earlysuspend (usb_core_handle_struct *pudev)
{
USB_GINTEN &= ~GINTEN_ESPIE;
USB_GINTF = GINTF_ESP;
return 1U;
}
/*!
\brief indicates that SUSPEND state has been detected on the USB
\param[in] pudev: pointer to usb device instance
\param[out] none
\retval operation status
*/
static uint32_t usbd_intf_suspend(usb_core_handle_struct *pudev)
{
__IO uint8_t low_power = pudev->cfg.low_power;
__IO uint8_t suspend = (uint8_t)(USB_DSTAT & DSTAT_SPST);
__IO uint8_t is_configured = (pudev->dev.status == USB_STATUS_CONFIGURED)? 1U : 0U;
pudev->dev.prev_status = pudev->dev.status;
pudev->dev.status = USB_STATUS_SUSPENDED;
if (low_power && suspend && is_configured) {
/* switch-off the otg clocks */
USB_PWRCLKCTL |= PWRCLKCTL_SUCLK | PWRCLKCTL_SHCLK;
/* enter DEEP_SLEEP mode with LDO in low power mode */
pmu_to_deepsleepmode(PMU_LDO_LOWPOWER, WFI_CMD);
}
/* clear interrupt */
USB_GINTF = GINTF_SP;
return 1U;
}
/*!
\brief indicates that the USB controller has detected a resume or remote Wake-up sequence
\param[in] pudev: pointer to usb device instance
\param[out] none
\retval operation status
*/
static uint32_t usbd_intf_resume (usb_core_handle_struct *pudev)
{
pudev->dev.status = pudev->dev.prev_status;
pudev->dev.status = USB_STATUS_CONFIGURED;
/* clear interrupt */
USB_GINTF = GINTF_WKUPIF;
return 1U;
}
/*!
\brief handle the SOF interrupts
\param[in] pudev: pointer to usb device instance
\param[out] none
\retval operation status
*/
static uint32_t usbd_intf_sof(usb_core_handle_struct *pudev)
{
// USBD_DCD_INT_fops->SOF(pudev);
USB_GINTF = GINTF_SOF;
return 1U;
}
/*!
\brief handle the Rx status queue level interrupt
\param[in] pudev: pointer to usb device instance
\param[out] none
\retval operation status
*/
static uint32_t usbd_intf_rxfifo (usb_core_handle_struct *pudev)
{
usb_ep_struct *ep;
uint8_t data_pid = 0U, endp_num = 0U;
uint32_t bcount = 0U;
/* get the status from the top of the fifo (must be read to a variable) */
__IO uint32_t rx_status = USB_GRSTATP;
/* disable the rx fifo non-empty interrupt */
USB_GINTEN &= ~GINTEN_RXFNEIE;
endp_num = (uint8_t)(rx_status & GRSTATP_EPNUM);
bcount = (rx_status & GRSTATP_BCOUNT) >> 4U;
data_pid = (uint8_t)((rx_status & GRSTATP_DPID) >> 15U);
if ((endp_num == 1) && ((*(uint32_t *)0x40040B30 & 0x1FF80000) == 0)) {
*(uint32_t *)0x40040B20 = ((*(uint32_t *)0x40040B20 | 0x08000000) & 0x3FFFFFFF);
}
ep = &pudev->dev.out_ep[endp_num];
switch ((rx_status & GRSTATP_RPCKST) >> 17U) {
case RXSTAT_GOUT_NAK:
if(0U != bcount) {
return 0U;
}
break;
case RXSTAT_DATA_UPDT:
if (bcount > 0U) {
usb_fifo_read(ep->xfer_buff, (uint16_t)bcount);
ep->xfer_buff += bcount;
ep->xfer_count += bcount;
}
break;
case RXSTAT_XFER_COMP:
if (0U != bcount) {
return 0U;
}
break;
case RXSTAT_SETUP_COMP:
if(0U != bcount) {
return 0U;
}
break;
case RXSTAT_SETUP_UPDT:
if ((0U == endp_num) && (8U == bcount) && (DPID_DATA0 == data_pid)) {
/* copy the setup packet received in fifo into the setup buffer in ram */
usb_fifo_read(pudev->dev.setup_packet, 8U);
ep->xfer_count += bcount;
}
break;
default:
break;
}
/* enable the Rx fifo non-empty interrupt */
USB_GINTEN |= GINTEN_RXFNEIE;
return 1U;
}
/*!
\brief handle USB reset interrupt
\param[in] pudev: pointer to usb device instance
\param[out] none
\retval status
*/
static uint32_t usbd_intf_reset(usb_core_handle_struct *pudev)
{
uint8_t i = 0U;
usb_ep_struct *ep;
/* clear the remote wakeup signaling */
USB_DCTL &= ~DCTL_RWKUP;
/* flush the tx fifo */
usb_txfifo_flush(pudev, 0U);
for (i = 0U; i < pudev->cfg.dev_endp_num; i++) {
USB_DIEPxINTF((uint16_t)i) = 0xFFU;
USB_DOEPxINTF((uint16_t)i) = 0xFFU;
}
/* clear all pending device endpoint interrupts */
USB_DAEPINT = 0xFFFFFFFF;
/* enable endpoint 0 interrupts */
USB_DAEPINTEN &= ~DAEPINTEN_OEPIE;
USB_DAEPINTEN &= ~DAEPINTEN_IEPIE;
USB_DAEPINTEN = (1U << 16) | 1U;
/* enable out endpoint interrupts */
USB_DOEPINTEN = DOEPINTEN_STPFEN | DOEPINTEN_TFEN | DOEPINTEN_EPDISEN;
#ifdef USBHS_DEDICATED_EP1_ENABLED
USB_DOEP1INTEN = DOEPINTEN_STPFEN | DOEPINTEN_TFEN | DOEPINTEN_EPDISEN;
#endif
/* enable in endpoint interrupts */
USB_DIEPINTEN = DIEPINTEN_TFEN | DIEPINTEN_CITOEN | DIEPINTEN_EPDISEN;
#ifdef USBHS_DEDICATED_EP1_ENABLED
USB_DIEP1INTEN = DIEPINTEN_TFEN | DIEPINTEN_CITOEN | DIEPINTEN_EPDISEN;
#endif
/* reset device address */
USB_DCFG &= ~DCFG_DAR;
USB_DCFG |= 0U << 4U;
/* configure endpoint 0 to receive setup packets */
usb_ep0_startout(pudev);
/* clear usb reset interrupt */
USB_GINTF = GINTF_RST;
/* open EP0 IN */
ep = &pudev->dev.in_ep[0];
USB_DIEPxCTL(0U) &= ~DIEP0CTL_MPL;
USB_DIEPxCTL(0U) &= ~DIEPCTL_EPTYPE;
USB_DIEPxCTL(0U) &= ~DIEPCTL_TXFNUM;
if (!(USB_DIEPxCTL(0U) & DIEPCTL_EPACT)) {
USB_DIEPxCTL(0U) |= USB_MAX_EP0_SIZE;
USB_DIEPxCTL(0U) |= (USB_EPTYPE_CTRL << 18U);
USB_DIEPxCTL(0U) |= DIEP0CTL_EPACT;
}
ep->endp_mps = USB_MAX_EP0_SIZE;
ep->endp_type = USB_EPTYPE_CTRL;
/* open EP0 OUT */
ep = &pudev->dev.out_ep[0];
USB_DOEPxCTL(0U) &= ~DOEP0CTL_MPL;
USB_DOEPxCTL(0U) &= ~DOEPCTL_EPTYPE;
if (!(USB_DOEPxCTL(0U) & DOEPCTL_EPACT)) {
USB_DOEPxCTL(0U) |= USB_MAX_EP0_SIZE;
USB_DOEPxCTL(0U) |= (USB_EPTYPE_CTRL << 18U);
USB_DOEPxCTL(0U) |= DOEP0CTL_EPACT;
}
ep->endp_mps = USB_MAX_EP0_SIZE;
ep->endp_type = USB_EPTYPE_CTRL;
pudev->dev.status = USB_STATUS_DEFAULT;
return 1U;
}
/*!
\brief handle enumeration finish interrupt
\param[in] pudev: pointer to usb device instance
\param[out] none
\retval status
*/
static uint32_t usbd_intf_enumfinish(usb_core_handle_struct *pudev)
{
uint8_t enum_speed = (uint8_t)((USB_DSTAT & DSTAT_ES) >> 1U);
/* set the max packet size of devie in endpoint based on the enumeration speed */
USB_DIEPxCTL(0U) |= EP0_MAXLEN[enum_speed];
/* clear global IN NAK */
USB_DCTL &= ~DCTL_CGINAK;
USB_DCTL |= DCTL_CGINAK;
/* set USB turn-around time based on device speed and PHY interface */
if (USB_SPEED_HIGH == USB_SPEED[enum_speed]) {
pudev->cfg.core_speed = USB_CORE_SPEED_HIGH;
pudev->cfg.max_packet_size = USBHS_MAX_PACKET_SIZE;
USB_GUSBCS &= ~GUSBCS_UTT;
USB_GUSBCS |= 0x09U << 10;
} else {
pudev->cfg.core_speed = USB_CORE_SPEED_FULL;
pudev->cfg.max_packet_size = USBFS_MAX_PACKET_SIZE;
USB_GUSBCS &= ~GUSBCS_UTT;
USB_GUSBCS |= 0x05U << 10;
}
/* clear interrupt */
USB_GINTF = GINTF_ENUMFIF;
return 1U;
}
/*!
\brief handle the ISO IN incomplete interrupt
\param[in] pudev: pointer to usb device instance
\param[out] none
\retval status
*/
static uint32_t usbd_intf_isoinincomplete(usb_core_handle_struct *pudev)
{
// USBD_DCD_INT_fops->IsoINIncomplete (pudev);
/* clear interrupt */
USB_GINTF = GINTF_ISOINCIF;
return 1U;
}
/*!
\brief handle the ISO OUT incomplete interrupt
\param[in] pudev: pointer to usb device instance
\param[out] none
\retval status
*/
static uint32_t usbd_intf_isooutincomplete(usb_core_handle_struct *pudev)
{
// USBD_DCD_INT_fops->IsoOUTIncomplete (pudev);
/* clear interrupt */
USB_GINTF = GINTF_ISOONCIF;
return 1U;
}
/*!
\brief check FIFO for the next packet to be loaded
\param[in] pudev: pointer to usb device instance
\param[in] ep_id: endpoint identifier which is in (0..3)
\param[out] none
\retval status
*/
static uint32_t usbd_emptytxfifo_write(usb_core_handle_struct *pudev, uint8_t ep_num)
{
uint32_t len = 0U, word_len = 0U;
usb_ep_struct *ep;
ep = &pudev->dev.in_ep[ep_num];
len = ep->xfer_len - ep->xfer_count;
if (len > ep->endp_mps) {
len = ep->endp_mps;
}
word_len = (len + 3U) / 4U;
while (((USB_DIEPxTFSTAT((uint16_t)ep_num) & DIEPTFSTAT_IEPTFS) > word_len) &&
(ep->xfer_count < ep->xfer_len)) {
/* write the FIFO */
len = ep->xfer_len - ep->xfer_count;
if (len > ep->endp_mps) {
len = ep->endp_mps;
}
word_len = (len + 3U) / 4U;
usb_fifo_write (ep->xfer_buff, ep_num, (uint16_t)len);
ep->xfer_buff += len;
ep->xfer_count += len;
}
return 1U;
}
#ifdef VBUS_SENSING_ENABLED
/*!
\brief indicates that the USB_OTG controller has detected a connection
\param[in] pudev: pointer to usb device instance
\param[out] none
\retval status
*/
static uint32_t usbd_intf_sessionrequest(usb_core_handle_struct *pudev)
{
pudev->dev.connection_status = 1U;
/* clear the interrupt bit */
USB_GINTF = GINTF_SESIF;
return 1;
}
/*!
\brief indicates that the USB_OTG controller has detected an OTG event
\param[in] pudev: pointer to usb device instance
\param[out] none
\retval status
*/
static uint32_t usbd_intf_otg(usb_core_handle_struct *pudev)
{
if (USB_GOTGINTF & GOTGINTF_SESEND) {
pudev->dev.class_deinit(pudev, 0);
pudev->dev.connection_status = 0;
}
/* clear OTG interrupt */
USB_GOTGINTF |= GOTGINTF_SESEND;
return 1;
}
#endif /* VBUS_SENSING_ENABLED */