LibreSatCam/Cypress_FX3_USB_Firmware/Source/uvc.c

2153 lines
83 KiB
C

#include <cyu3system.h>
#include <cyu3os.h>
#include <cyu3dma.h>
#include <cyu3error.h>
#include <cyu3usb.h>
#include <cyu3uart.h>
#include <cyu3gpif.h>
#include <cyu3i2c.h>
#include <cyu3gpio.h>
#include <cyu3pib.h>
#include <cyu3utils.h>
#include <cyu3socket.h>
#include "uvc.h"
#include "camera_ptzcontrol.h"
#include "cyfxgpif2config.h"
#include "uvc_settings.h"
/*************************************************************************************************
Global Variables
*************************************************************************************************/
static CyU3PThread uvcAppThread; /* UVC video streaming thread. */
static CyU3PThread uvcAppEP0Thread; /* UVC control request handling thread. */
static CyU3PEvent glFxUVCEvent; /* Event group used to signal threads. */
CyU3PDmaMultiChannel glChHandleUVCStream; /* DMA multi-channel handle. */
/* Current UVC control request fields. See USB specification for definition. */
uint8_t bmReqType, bRequest; /* bmReqType and bRequest fields. */
uint16_t wValue, wIndex, wLength; /* wValue, wIndex and wLength fields. */
CyU3PUSBSpeed_t usbSpeed = CY_U3P_NOT_CONNECTED; /* Current USB connection speed. */
CyBool_t streamingStarted = CyFalse; /* Whether USB host has started streaming data */
CyBool_t glIsApplnActive = CyFalse; /* When CLEAR_FEATURE (stop streaming) request is sent this variable is reset and set when start
streaming request is sent by Host. This is used during commit buffer failure event. */
static CyBool_t glIsConfigured = CyFalse; /* Whether Application is in configured state or not */
/* Mac OS does not send EP Clear feature when the app is closed. It just stops issuing IN tokens. So buffer commit failures
* can be counted and if it reaches beyond a limit, streaming can be stopped. Buffer commit failure code can be cleared
* on DMA Consumer event so that the limit is not reached under streaming conditions. */
static uint8_t glCommitBufferFailureCount = 0;
/*Variable to track whether the reason for DMA Reset is Frame Timer overflow or Commit Buffer Failure*/
static uint8_t glDmaResetFlag = CY_FX_UVC_DMA_RESET_EVENT_NOT_ACTIVE;
static uint8_t glUvcVcErrorCode = CY_FX_UVC_VC_ERROR_CODE_NO_ERROR;
#ifdef FRAME_TIMER_ENABLE
/* Maximum frame transfer time in milli-seconds. The value is updated for every resolution and frame rate.
* Note: The value should always be greater than the frame blanking period */
uint16_t glFrameTimerPeriod = CY_FX_UVC_FRAME_TIMER_VAL_200MS;
/* Timer used to track frame transfer time. */
static CyU3PTimer UvcTimer;
/* Frame timer overflow call back function */
static void CyFxUvcAppProgressTimer(uint32_t arg)
{
if(glDmaResetFlag == CY_FX_UVC_DMA_RESET_EVENT_NOT_ACTIVE)
{
glDmaResetFlag = CY_FX_UVC_DMA_RESET_FRAME_TIMER_OVERFLOW;
CyU3PEventSet(&glFxUVCEvent, CY_FX_UVC_DMA_RESET_EVENT, CYU3P_EVENT_OR);
}
}
#endif
#ifdef BACKFLOW_DETECT
uint8_t back_flow_detected = 0; /* Whether buffer overflow error is detected. */
#endif
#ifdef USB_DEBUG_INTERFACE
CyU3PDmaChannel glDebugCmdChannel; /* Channel to receive debug commands on. */
CyU3PDmaChannel glDebugRspChannel; /* Channel to send debug responses on. */
uint8_t *glDebugRspBuffer; /* Buffer used to send debug responses. */
#endif
/* UVC Probe Control Settings for a USB 3.0 connection. */
uint8_t glProbeCtrl[CY_FX_UVC_MAX_PROBE_SETTING] = {
0x00, 0x00, /* bmHint : no hit */
0x01, /* Use 1st Video format index */
0x01, /* Use 1st Video frame index */
DBVAL(INTERVAL_30), /* Desired frame interval in the unit of 100ns: 30 fps */
0x00, 0x00, /* Key frame rate in key frame/video frame units: only applicable
to video streaming with adjustable compression parameters */
0x00, 0x00, /* PFrame rate in PFrame / key frame units: only applicable to
video streaming with adjustable compression parameters */
0x00, 0x00, /* Compression quality control: only applicable to video streaming
with adjustable compression parameters */
0x00, 0x00, /* Window size for average bit rate: only applicable to video
streaming with adjustable compression parameters */
0x00, 0x00, /* Internal video streaming i/f latency in ms */
DBVAL(MAX_FRAME_SIZE), /* Max video frame size in bytes */
0x00, 0x80, 0x00, 0x00, /* No. of bytes device can rx in single payload = 32 KB */
#ifndef FX3_UVC_1_0_SUPPORT
/* UVC 1.1 Probe Control has additional fields from UVC 1.0 */
0x00, 0x60, 0xE3, 0x16, /* Device Clock */
0x00, /* Framing Information - Ignored for uncompressed format*/
0x00, /* Preferred payload format version */
0x00, /* Minimum payload format version */
0x00 /* Maximum payload format version */
#endif
};
typedef struct __attribute__((packed)) uvc_control_s
{
uint16_t hint;
uint8_t format_index;
uint8_t frame_index;
uint32_t frame_interval;
uint16_t key_frame_rate;
uint16_t PFrame_rate;
uint16_t comp_quality;
uint16_t comp_win_size;
uint16_t delay;
uint32_t max_video_frame_size;
uint32_t max_payload_transfer_size;
#ifndef FX3_UVC_1_0_SUPPORT
uint32_t device_clock;
uint8_t frame_info;
uint8_t prefered_version;
uint8_t min_version;
uint8_t max_version;
#endif
uint8_t align_16[14];
}uvc_control_t;
/* UVC Probe Control Setting for a USB 2.0 connection. */
uint8_t glProbeCtrl20[CY_FX_UVC_MAX_PROBE_SETTING] = {
0x00, 0x00, /* bmHint : no hit */
0x01, /* Use 1st Video format index */
0x01, /* Use 1st Video frame index */
DBVAL(INTERVAL_30), /* Desired frame interval in the unit of 100ns: 15 fps */
0x00, 0x00, /* Key frame rate in key frame/video frame units: only applicable
to video streaming with adjustable compression parameters */
0x00, 0x00, /* PFrame rate in PFrame / key frame units: only applicable to
video streaming with adjustable compression parameters */
0x00, 0x00, /* Compression quality control: only applicable to video streaming
with adjustable compression parameters */
0x00, 0x00, /* Window size for average bit rate: only applicable to video
streaming with adjustable compression parameters */
0x00, 0x00, /* Internal video streaming i/f latency in ms */
DBVAL(MAX_FRAME_SIZE), /* Max video frame size in bytes */
0x00, 0x80, 0x00, 0x00, /* No. of bytes device can rx in single payload = 32 KB */
#ifndef FX3_UVC_1_0_SUPPORT
/* UVC 1.1 Probe Control has additional fields from UVC 1.0 */
0x00, 0x60, 0xE3, 0x16, /* Device Clock */
0x00, /* Framing Information - Ignored for uncompressed format*/
0x00, /* Preferred payload format version */
0x00, /* Minimum payload format version */
0x00 /* Maximum payload format version */
#endif
};
/* Video Probe Commit Control. This array is filled out when the host sends down the SET_CUR request. */
static uint8_t glCommitCtrl[CY_FX_UVC_MAX_PROBE_SETTING_ALIGNED];
/* Scratch buffer used for handling UVC class requests with a data phase. */
static uint8_t glEp0Buffer[32];
/* UVC Header to be prefixed at the top of each 16 KB video data buffer. */
uint8_t volatile glUVCHeader[CY_FX_UVC_MAX_HEADER] =
{
0x0C, /* Header Length */
0x8C, /* Bit field header field */
0x00, 0x00, 0x00, 0x00, /* Presentation time stamp field */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* Source clock reference field */
};
#ifdef UVC_EXTENSION_UNIT
/* Format is: Version 1.0 (Major.Minor) Build date: 9/10/17 (MM/DD/YY) */
static uint8_t glFxUvcFirmwareVersion[5] = {
1, /* Major version */
0, /* Minor version */
9, /* Build month */
22, /* Build day */
17 /* Build Year */
};
#endif
#ifdef DEBUG_PRINT_FRAME_COUNT
volatile static uint32_t glFrameCount = 0; /* Number of video frames transferred so far. */
volatile static uint32_t glDmaDone = 1; /* Number of buffers transferred in the current frame. */
#endif
volatile static uint32_t gTotal_bytes = 0;
/* Add the UVC packet header to the top of the specified DMA buffer. */
void
CyFxUVCAddHeader (
uint8_t *buffer_p, /* Buffer pointer */
uint8_t frameInd /* EOF or normal frame indication */
)
{
/* Copy header to buffer */
CyU3PMemCopy (buffer_p, (uint8_t *)glUVCHeader, CY_FX_UVC_MAX_HEADER);
/* The EOF flag needs to be set if this is the last packet for this video frame. */
if (frameInd & CY_FX_UVC_HEADER_EOF)
{
buffer_p[1] |= CY_FX_UVC_HEADER_EOF;
glUVCHeader[1] ^= CY_FX_UVC_HEADER_FRAME_ID;
}
}
/* Application Error Handler */
void
CyFxAppErrorHandler (
CyU3PReturnStatus_t apiRetStatus /* API return status */
)
{
/* This function is hit when we have hit a critical application error. This is not
expected to happen, and the current implementation of this function does nothing
except stay in a loop printing error messages through the UART port.
This function can be modified to take additional error handling actions such
as cycling the USB connection or performing a warm reset.
*/
for (;;)
{
CyU3PDebugPrint (4, "Error handler...\r\n");
CyU3PThreadSleep (1000);
}
}
/* This function performs the operations for a Video Streaming Abort.
This is called every time there is a USB reset, suspend or disconnect event.
*/
static void
CyFxUVCApplnAbortHandler (
void)
{
/* Set Video Stream Abort Event */
CyU3PEventSet (&glFxUVCEvent, CY_FX_UVC_STREAM_ABORT_EVENT, CYU3P_EVENT_OR);
}
/* This is the Callback function to handle the USB Events */
static void
CyFxUVCApplnUSBEventCB (
CyU3PUsbEventType_t evtype, /* Event type */
uint16_t evdata /* Event data */
)
{
switch (evtype)
{
case CY_U3P_USB_EVENT_SUSPEND:
CyU3PDebugPrint (4, "UsbEventCB: SUSPEND encountered...\r\n");
/* Set USB suspend Event */
CyU3PEventSet (&glFxUVCEvent, CY_FX_USB_SUSPEND_EVENT_HANDLER, CYU3P_EVENT_OR);
break;
case CY_U3P_USB_EVENT_EP_UNDERRUN:
CyU3PDebugPrint (4, "UsbEventCB: CY_U3P_USB_EVENT_EP_UNDERRUN encountered...\r\n");
break;
/* Intentional Fall-through all cases */
case CY_U3P_USB_EVENT_SETCONF:
if (CyU3PUsbGetSpeed() == CY_U3P_SUPER_SPEED)
{
CyU3PDebugPrint(4, "UsbEventCB: Detected SS USB Connection\r\n");
}
else if (CyU3PUsbGetSpeed() == CY_U3P_HIGH_SPEED)
{
CyU3PDebugPrint(4, "UsbEventCB: Detected HS USB Connection\r\n");
}
case CY_U3P_USB_EVENT_RESET:
case CY_U3P_USB_EVENT_DISCONNECT:
case CY_U3P_USB_EVENT_CONNECT:
if (evtype == CY_U3P_USB_EVENT_SETCONF)
glIsConfigured = CyTrue;
else
glIsConfigured = CyFalse;
/* Stop the video streamer application and enable LPM. */
CyU3PUsbLPMEnable();
if (glIsApplnActive)
{
CyU3PDebugPrint(4, "UsbEventCB: Call App Stop\r\n");
CyFxUVCApplnAbortHandler();
}
break;
default:
break;
}
}
/* Callback to handle the USB Setup Requests and UVC Class events */
static CyBool_t
CyFxUVCApplnUSBSetupCB (
uint32_t setupdat0, /* SETUP Data 0 */
uint32_t setupdat1 /* SETUP Data 1 */
)
{
CyBool_t uvcHandleReq = CyFalse;
uint32_t status;
/* Obtain Request Type and Request */
bmReqType = (uint8_t)(setupdat0 & CY_FX_USB_SETUP_REQ_TYPE_MASK);
bRequest = (uint8_t)((setupdat0 & CY_FX_USB_SETUP_REQ_MASK) >> 8);
wValue = (uint16_t)((setupdat0 & CY_FX_USB_SETUP_VALUE_MASK) >> 16);
wIndex = (uint16_t)(setupdat1 & CY_FX_USB_SETUP_INDEX_MASK);
wLength = (uint16_t)((setupdat1 & CY_FX_USB_SETUP_LENGTH_MASK) >> 16);
/* Check for UVC Class Requests */
switch (bmReqType)
{
case CY_FX_USB_UVC_GET_REQ_TYPE:
case CY_FX_USB_UVC_SET_REQ_TYPE:
/* UVC Specific requests are handled in the EP0 thread. */
switch (wIndex & 0xFF)
{
case CY_FX_UVC_CONTROL_INTERFACE:
{
uvcHandleReq = CyTrue;
status = CyU3PEventSet (&glFxUVCEvent, CY_FX_UVC_VIDEO_CONTROL_REQUEST_EVENT, CYU3P_EVENT_OR);
if (status != CY_U3P_SUCCESS)
{
CyU3PDebugPrint (4, "Set CY_FX_UVC_VIDEO_CONTROL_REQUEST_EVENT Failed %x\r\n", status);
CyU3PUsbStall (0, CyTrue, CyFalse);
}
}
break;
case CY_FX_UVC_STREAM_INTERFACE:
{
uvcHandleReq = CyTrue;
status = CyU3PEventSet (&glFxUVCEvent, CY_FX_UVC_VIDEO_STREAM_REQUEST_EVENT, CYU3P_EVENT_OR);
if (status != CY_U3P_SUCCESS)
{
/* Error handling */
CyU3PDebugPrint (4, "Set CY_FX_UVC_VIDEO_STREAM_REQUEST_EVENT Failed %x\r\n", status);
CyU3PUsbStall (0, CyTrue, CyFalse);
}
}
break;
default:
break;
}
break;
case CY_FX_USB_SET_INTF_REQ_TYPE:
if (bRequest == CY_FX_USB_SET_INTERFACE_REQ)
{
/* Some hosts send Set Interface Alternate Setting 0 command while stopping the video
* stream. The application uses this event to stop streaming. */
if ((wIndex == CY_FX_UVC_STREAM_INTERFACE) && (wValue == 0))
{
/* Stop GPIF state machine to stop data transfers through FX3 */
CyU3PDebugPrint (4, "Alternate setting 0..\r\n");
/* Clear the stall condition and sequence numbers. */
CyU3PUsbStall (CY_FX_EP_BULK_VIDEO, CyFalse, CyTrue);
CyFxUVCApplnAbortHandler ();
uvcHandleReq = CyTrue;
/* Complete Control request handshake */
CyU3PUsbAckSetup ();
}
}
else if ((bRequest == CY_U3P_USB_SC_SET_FEATURE) || (bRequest == CY_U3P_USB_SC_CLEAR_FEATURE))
{
CyU3PDebugPrint(4, "USBSetupCB:In SET_FTR %d::%d\r\n", glIsApplnActive, glIsConfigured);
if (glIsConfigured)
{
uvcHandleReq = CyTrue;
CyU3PUsbAckSetup ();
}
}
break;
case CY_U3P_USB_TARGET_ENDPT:
if (bRequest == CY_U3P_USB_SC_CLEAR_FEATURE)
{
if (wIndex == CY_FX_EP_BULK_VIDEO)
{
/* Windows OS sends Clear Feature Request after it stops streaming,
* however MAC OS sends clear feature request right after it sends a
* Commit -> SET_CUR request. Hence, stop the video streaming and clear
* the stall condition and sequence numbers */
CyU3PDebugPrint (4, "Clear feature request detected...\r\n");
/* Clear the stall condition and sequence numbers. */
CyU3PUsbStall (CY_FX_EP_BULK_VIDEO, CyFalse, CyTrue);
CyFxUVCApplnAbortHandler();
uvcHandleReq = CyTrue;
/* Complete Control request handshake */
CyU3PUsbAckSetup ();
}
}
break;
default:
break;
}
/* Return status of request handling to the USB driver */
return uvcHandleReq;
}
/* DMA callback providing notification when data buffers are received from the sensor and when they have
* been drained by the USB host.
*
* The UVC headers are attached to the data, and forwarded to the USB host in this callback function.
*/
void
CyFxUvcApplnDmaCallback (
CyU3PDmaMultiChannel *chHandle,
CyU3PDmaCbType_t type,
CyU3PDmaCBInput_t *input
)
{
CyU3PDmaBuffer_t dmaBuffer;
CyU3PReturnStatus_t status = CY_U3P_SUCCESS;
if (type == CY_U3P_DMA_CB_PROD_EVENT)
{
/* This is a produce event notification to the CPU. This notification is received upon reception of
* every buffer. The buffer will not be sent out unless it is explicitly committed. The call shall fail
* if there is a bus reset / usb disconnect or if there is any application error.
*/
#ifdef FRAME_TIMER_ENABLE
/* Received data from the sensor so stop the frame timer */
CyU3PTimerStop(&UvcTimer);
/* Restart the frame timer so that we receive the next buffer before timer overflows */
CyU3PTimerModify(&UvcTimer, glFrameTimerPeriod, 0);
CyU3PTimerStart(&UvcTimer);
#endif
/* There is a possibility that CyU3PDmaMultiChannelGetBuffer will return CY_U3P_ERROR_INVALID_SEQUENCE here.
* In such a case, do nothing. We make up for this missed produce event by making repeated commit actions
* in subsequent produce event callbacks.
*/
status = CyU3PDmaMultiChannelGetBuffer (chHandle, &dmaBuffer, CYU3P_NO_WAIT);
while (status == CY_U3P_SUCCESS)
{
CyU3PReturnStatus_t apiRetStatus = CY_U3P_SUCCESS;
gTotal_bytes = gTotal_bytes + dmaBuffer.count;
apiRetStatus = CyU3PGpioSetValue (DEBUG1_GPIO, CyFalse);
if (apiRetStatus != CY_U3P_SUCCESS)
{
CyU3PDebugPrint (4, "GPIO Set Value Error, Error Code = %d\n", apiRetStatus);
return;
}
//Wait for some time to allow proper reset.
// Drive the GPIO high to bring the sensor out of reset.
apiRetStatus = CyU3PGpioSetValue (DEBUG1_GPIO, CyTrue);
if (apiRetStatus != CY_U3P_SUCCESS)
{
CyU3PDebugPrint (4, "GPIO Set Value Error, Error Code = %d\n", apiRetStatus);
return;
}
/* Add Headers*/
if (dmaBuffer.count == CY_FX_UVC_BUF_FULL_SIZE)
{
/* A full buffer indicates there is more data to go in this video frame. */
CyFxUVCAddHeader (dmaBuffer.buffer - CY_FX_UVC_MAX_HEADER, CY_FX_UVC_HEADER_FRAME);
}
else
{
/* A partially filled buffer indicates the end of the ongoing video frame. */
CyFxUVCAddHeader (dmaBuffer.buffer - CY_FX_UVC_MAX_HEADER, CY_FX_UVC_HEADER_EOF);
//CyU3PDebugPrint (4, "t %d\n", gTotal_bytes);
// gTotal_bytes =0;
#ifdef DEBUG_PRINT_FRAME_COUNT
glFrameCount++;
// glDmaDone = 0;
#endif
}
/* Commit Buffer to USB*/
status = CyU3PDmaMultiChannelCommitBuffer (chHandle, (dmaBuffer.count + CY_FX_UVC_MAX_HEADER), 0);
if (status == CY_U3P_SUCCESS)
{
#ifdef DEBUG_PRINT_FRAME_COUNT
glDmaDone++;
#endif
}
else
{
if(glDmaResetFlag == CY_FX_UVC_DMA_RESET_EVENT_NOT_ACTIVE)
{
glDmaResetFlag = CY_FX_UVC_DMA_RESET_COMMIT_BUFFER_FAILURE;
CyU3PEventSet(&glFxUVCEvent, CY_FX_UVC_DMA_RESET_EVENT, CYU3P_EVENT_OR);
}
break;
}
/* Check if any more buffers are ready to go, and commit them here. */
status = CyU3PDmaMultiChannelGetBuffer (chHandle, &dmaBuffer, CYU3P_NO_WAIT);
}
}
else if (type == CY_U3P_DMA_CB_CONS_EVENT)
{
streamingStarted = CyTrue;
glCommitBufferFailureCount = 0; /* Reset the counter after data is consumed by USB */
}
}
/* GpifCB callback function is invoked when FV triggers GPIF interrupt */
void
CyFxGpifCB (
uint8_t currentState /* GPIF state which triggered the interrupt. */
)
{
/* The ongoing video frame has ended. If we have a partial buffer sitting on the socket, we need to forcibly
* wrap it up. We also need to toggle the FW_TRG a couple of times to get the state machine ready for the
* next frame.
*
* Note: DMA channel APIs cannot be used here as this is ISR context. We are making use of the raw socket
* APIs.
*/
/*
CyU3PReturnStatus_t apiRetStatus = CY_U3P_SUCCESS;
apiRetStatus = CyU3PGpioSetValue (DEBUG1_GPIO, CyFalse);
if (apiRetStatus != CY_U3P_SUCCESS)
{
CyU3PDebugPrint (4, "GPIO Set Value Error, Error Code = %d\n", apiRetStatus);
return;
}
// Wait for some time to allow proper reset.
// Drive the GPIO high to bring the sensor out of reset.
apiRetStatus = CyU3PGpioSetValue (DEBUG1_GPIO, CyTrue);
if (apiRetStatus != CY_U3P_SUCCESS)
{
CyU3PDebugPrint (4, "GPIO Set Value Error, Error Code = %d\n", apiRetStatus);
return;
}
*/
//CyU3PDebugPrint (4, "FSYNC Callback (\n");
switch (currentState)
{
case PARTIAL_BUF_IN_SCK0:
CyU3PDmaSocketSetWrapUp (CY_U3P_PIB_SOCKET_0);
break;
case FULL_BUF_IN_SCK0:
break;
case PARTIAL_BUF_IN_SCK1:
CyU3PDmaSocketSetWrapUp (CY_U3P_PIB_SOCKET_1);
break;
case FULL_BUF_IN_SCK1:
break;
default:
/* This should not happen. Do nothing. */
return;
}
CyU3PGpifControlSWInput (CyTrue);
CyU3PGpifControlSWInput (CyFalse);
}
/* This function initializes the Debug Module for the UVC Application */
static void
CyFxUVCApplnDebugInit (
void)
{
CyU3PUartConfig_t uartConfig;
CyU3PReturnStatus_t apiRetStatus;
/* Initialize the UART for printing debug messages */
apiRetStatus = CyU3PUartInit ();
if (apiRetStatus != CY_U3P_SUCCESS)
{
CyU3PDebugPrint (4, "UART initialization failed!\n");
CyFxAppErrorHandler (apiRetStatus);
}
/* Set UART Configuration */
uartConfig.baudRate = CY_U3P_UART_BAUDRATE_115200;
uartConfig.stopBit = CY_U3P_UART_ONE_STOP_BIT;
uartConfig.parity = CY_U3P_UART_NO_PARITY;
uartConfig.txEnable = CyTrue;
uartConfig.rxEnable = CyFalse;
uartConfig.flowCtrl = CyFalse;
uartConfig.isDma = CyTrue;
/* Set the UART configuration */
apiRetStatus = CyU3PUartSetConfig (&uartConfig, NULL);
if (apiRetStatus != CY_U3P_SUCCESS)
{
CyFxAppErrorHandler (apiRetStatus);
}
/* Set the UART transfer */
apiRetStatus = CyU3PUartTxSetBlockXfer (0xFFFFFFFF);
if (apiRetStatus != CY_U3P_SUCCESS)
{
CyFxAppErrorHandler (apiRetStatus);
}
/* Initialize the Debug logger module. */
apiRetStatus = CyU3PDebugInit (CY_U3P_LPP_SOCKET_UART_CONS, 4);
if (apiRetStatus != CY_U3P_SUCCESS)
{
CyFxAppErrorHandler (apiRetStatus);
}
/* Disable log message headers. */
CyU3PDebugPreamble (CyFalse);
}
/* I2C initialization. */
static void
CyFxUVCApplnI2CInit (void)
{
CyU3PI2cConfig_t i2cConfig;;
CyU3PReturnStatus_t status;
status = CyU3PI2cInit ();
if (status != CY_U3P_SUCCESS)
{
CyU3PDebugPrint (4, "I2C initialization failed!\n");
CyFxAppErrorHandler (status);
}
/* Set I2C Configuration */
i2cConfig.bitRate = 100000; /* 100 KHz */
i2cConfig.isDma = CyFalse;
i2cConfig.busTimeout = 0xffffffffU;
i2cConfig.dmaTimeout = 0xffff;
status = CyU3PI2cSetConfig (&i2cConfig, 0);
if (CY_U3P_SUCCESS != status)
{
CyU3PDebugPrint (4, "I2C configuration failed!\n");
CyFxAppErrorHandler (status);
}
}
#ifdef BACKFLOW_DETECT
static void CyFxUvcAppPibCallback (
CyU3PPibIntrType cbType,
uint16_t cbArg)
{
if ((cbType == CYU3P_PIB_INTR_ERROR) && ((cbArg == 0x1005) || (cbArg == 0x1006)))
{
if (!back_flow_detected)
{
CyU3PDebugPrint (4, "Backflow detected...\r\n");
back_flow_detected = 1;
}
}
}
#endif
#ifdef USB_DEBUG_INTERFACE
static void
CyFxUvcAppDebugCallback (
CyU3PDmaChannel *handle,
CyU3PDmaCbType_t type,
CyU3PDmaCBInput_t *input)
{
if (type == CY_U3P_DMA_CB_PROD_EVENT)
{
/* Data has been received. Notify the EP0 thread which handles the debug commands as well. */
CyU3PEventSet (&glFxUVCEvent, CY_FX_USB_DEBUG_CMD_EVENT, CYU3P_EVENT_OR);
}
}
#endif
/*
* Load the GPIF configuration on the GPIF-II engine. This operation is performed at start-up.
*/
static void
CyFxUvcAppGpifInit (
void)
{
CyU3PReturnStatus_t apiRetStatus;
apiRetStatus = CyU3PGpifLoad ((CyU3PGpifConfig_t *) &CyFxGpifConfig);
if (apiRetStatus != CY_U3P_SUCCESS)
{
/* Error Handling */
CyU3PDebugPrint (4, "Loading GPIF Configuration failed, Error Code = %d\r\n", apiRetStatus);
CyFxAppErrorHandler (apiRetStatus);
}
}
/* Callback for LPM requests. Always return true to allow host to transition device
* into required LPM state U1/U2/U3. When data transmission is active LPM management
* is explicitly disabled to prevent data transmission errors.
*/
static CyBool_t
CyFxUVCAppLPMRqtCB (
CyU3PUsbLinkPowerMode link_mode /*USB 3.0 linkmode requested by Host */
)
{
return CyTrue;
}
/* This function initializes the USB Module, creates event group,
sets the enumeration descriptors, configures the Endpoints and
configures the DMA module for the UVC Application */
static void
CyFxUVCApplnInit (void)
{
CyU3PDmaMultiChannelConfig_t dmaMultiConfig;
CyU3PEpConfig_t endPointConfig;
CyU3PReturnStatus_t apiRetStatus;
CyU3PGpioClock_t gpioClock;
CyU3PGpioSimpleConfig_t gpioConfig;
CyU3PPibClock_t pibclock;
#ifdef USB_DEBUG_INTERFACE
CyU3PDmaChannelConfig_t channelConfig;
#endif
/* Create UVC event group */
apiRetStatus = CyU3PEventCreate (&glFxUVCEvent);
if (apiRetStatus != 0)
{
CyU3PDebugPrint (4, "UVC Create Event failed, Error Code = %d\n", apiRetStatus);
CyFxAppErrorHandler (apiRetStatus);
}
#ifdef UVC_PTZ_SUPPORT
CyFxUvcAppPTZInit ();
#endif
/* Init the GPIO module */
gpioClock.fastClkDiv = 2;
gpioClock.slowClkDiv = 2;
gpioClock.simpleDiv = CY_U3P_GPIO_SIMPLE_DIV_BY_2;
gpioClock.clkSrc = CY_U3P_SYS_CLK;
gpioClock.halfDiv = 0;
/* Initialize Gpio interface */
apiRetStatus = CyU3PGpioInit (&gpioClock, NULL);
if (apiRetStatus != 0)
{
CyU3PDebugPrint (4, "GPIO Init failed, Error Code = %d\n", apiRetStatus);
CyFxAppErrorHandler (apiRetStatus);
}
apiRetStatus = CyU3PDeviceGpioOverride (DEBUG1_GPIO, CyTrue);
if (apiRetStatus != 0)
{
CyU3PDebugPrint (4, "GPIO Override failed, Error Code = %d\n", apiRetStatus);
CyFxAppErrorHandler (apiRetStatus);
}
gpioConfig.outValue = CyTrue;
gpioConfig.driveLowEn = CyTrue;
gpioConfig.driveHighEn = CyTrue;
gpioConfig.inputEn = CyFalse;
gpioConfig.intrMode = CY_U3P_GPIO_NO_INTR;
apiRetStatus = CyU3PGpioSetSimpleConfig (DEBUG1_GPIO, &gpioConfig);
if (apiRetStatus != CY_U3P_SUCCESS)
{
CyU3PDebugPrint (4, "GPIO Set Config Error, Error Code = %d\n", apiRetStatus);
CyFxAppErrorHandler (apiRetStatus);
}
/* CTL pins are restricted and cannot be configured using I/O matrix configuration function,
* must use GpioOverride to configure it */
apiRetStatus = CyU3PDeviceGpioOverride (SENSOR_RESET_GPIO, CyTrue);
if (apiRetStatus != 0)
{
CyU3PDebugPrint (4, "GPIO Override failed, Error Code = %d\n", apiRetStatus);
CyFxAppErrorHandler (apiRetStatus);
}
/* SENSOR_RESET_GPIO is the Sensor reset pin */
gpioConfig.outValue = CyTrue;
gpioConfig.driveLowEn = CyTrue;
gpioConfig.driveHighEn = CyTrue;
gpioConfig.inputEn = CyFalse;
gpioConfig.intrMode = CY_U3P_GPIO_NO_INTR;
apiRetStatus = CyU3PGpioSetSimpleConfig (SENSOR_RESET_GPIO, &gpioConfig);
if (apiRetStatus != CY_U3P_SUCCESS)
{
CyU3PDebugPrint (4, "GPIO Set Config Error, Error Code = %d\n", apiRetStatus);
CyFxAppErrorHandler (apiRetStatus);
}
CyU3PGpioSetValue (FX3_MUXSEL_GPIO, CyTrue);
/* FX3_MUXSEL_GPIO is the Sensor reset pin */
/* CTL pins are restricted and cannot be configured using I/O matrix configuration function,
* must use GpioOverride to configure it */
apiRetStatus = CyU3PDeviceGpioOverride (FX3_MUXSEL_GPIO, CyTrue);
if (apiRetStatus != 0)
{
CyU3PDebugPrint (4, "GPIO Override failed, Error Code = %d\n", apiRetStatus);
CyFxAppErrorHandler (apiRetStatus);
}
/* SENSOR_RESET_GPIO is the Sensor reset pin */
gpioConfig.outValue = CyTrue;
gpioConfig.driveLowEn = CyTrue;
gpioConfig.driveHighEn = CyTrue;
gpioConfig.inputEn = CyFalse;
gpioConfig.intrMode = CY_U3P_GPIO_NO_INTR;
apiRetStatus = CyU3PGpioSetSimpleConfig (FX3_MUXSEL_GPIO, &gpioConfig);
if (apiRetStatus != CY_U3P_SUCCESS)
{
CyU3PDebugPrint (4, "GPIO Set Config Error, Error Code = %d\n", apiRetStatus);
CyFxAppErrorHandler (apiRetStatus);
}
/* Initialize the P-port. */
pibclock.clkDiv = 2;
pibclock.clkSrc = CY_U3P_SYS_CLK;
pibclock.isDllEnable = CyFalse;
pibclock.isHalfDiv = CyFalse;
apiRetStatus = CyU3PPibInit (CyTrue, &pibclock);
if (apiRetStatus != CY_U3P_SUCCESS)
{
CyU3PDebugPrint (4, "PIB Function Failed to Start, Error Code = %d\n", apiRetStatus);
CyFxAppErrorHandler (apiRetStatus);
}
CyFxUvcAppGpifInit ();
/* Register the GPIF State Machine callback used to get frame end notifications.
* We use the fast callback version which is triggered from ISR context.
*/
CyU3PGpifRegisterSMIntrCallback (CyFxGpifCB);
#ifdef BACKFLOW_DETECT
back_flow_detected = 0;
CyU3PPibRegisterCallback (CyFxUvcAppPibCallback, CYU3P_PIB_INTR_ERROR);
#endif
/* Image sensor initialization. Reset and then initialize with appropriate configuration. */
CyU3PThreadSleep(100);
SensorReset ();
SensorInit ();
/* USB initialization. */
apiRetStatus = CyU3PUsbStart ();
if (apiRetStatus != CY_U3P_SUCCESS)
{
CyU3PDebugPrint (4, "USB Function Failed to Start, Error Code = %d\n", apiRetStatus);
CyFxAppErrorHandler (apiRetStatus);
}
/* Setup the Callback to Handle the USB Setup Requests */
CyU3PUsbRegisterSetupCallback (CyFxUVCApplnUSBSetupCB, CyFalse);
/* Setup the Callback to Handle the USB Events */
CyU3PUsbRegisterEventCallback (CyFxUVCApplnUSBEventCB);
/* Register a callback to handle LPM requests from the USB 3.0 host. */
CyU3PUsbRegisterLPMRequestCallback (CyFxUVCAppLPMRqtCB);
/* Register the USB device descriptors with the driver. */
CyU3PUsbSetDesc (CY_U3P_USB_SET_HS_DEVICE_DESCR, 0, (uint8_t *)CyFxUSBDeviceDscr);
CyU3PUsbSetDesc (CY_U3P_USB_SET_SS_DEVICE_DESCR, 0, (uint8_t *)CyFxUSBDeviceDscrSS);
/* BOS and Device qualifier descriptors. */
CyU3PUsbSetDesc (CY_U3P_USB_SET_DEVQUAL_DESCR, 0, (uint8_t *)CyFxUSBDeviceQualDscr);
CyU3PUsbSetDesc (CY_U3P_USB_SET_SS_BOS_DESCR, 0, (uint8_t *)CyFxUSBBOSDscr);
/* Configuration descriptors. */
CyU3PUsbSetDesc (CY_U3P_USB_SET_HS_CONFIG_DESCR, 0, (uint8_t *)CyFxUSBHSConfigDscr);
CyU3PUsbSetDesc (CY_U3P_USB_SET_FS_CONFIG_DESCR, 0, (uint8_t *)CyFxUSBFSConfigDscr);
CyU3PUsbSetDesc (CY_U3P_USB_SET_SS_CONFIG_DESCR, 0, (uint8_t *)CyFxUSBSSConfigDscr);
/* String Descriptors */
CyU3PUsbSetDesc (CY_U3P_USB_SET_STRING_DESCR, 0, (uint8_t *)CyFxUSBStringLangIDDscr);
CyU3PUsbSetDesc (CY_U3P_USB_SET_STRING_DESCR, 1, (uint8_t *)CyFxUSBManufactureDscr);
CyU3PUsbSetDesc (CY_U3P_USB_SET_STRING_DESCR, 2, (uint8_t *)CyFxUSBProductDscr);
/* Configure the video streaming endpoint. */
endPointConfig.enable = 1;
endPointConfig.epType = CY_U3P_USB_EP_BULK;
endPointConfig.pcktSize = CY_FX_EP_BULK_VIDEO_PKT_SIZE;
endPointConfig.isoPkts = 1;
endPointConfig.burstLen = 16;
endPointConfig.streams = 0;
apiRetStatus = CyU3PSetEpConfig (CY_FX_EP_BULK_VIDEO, &endPointConfig);
if (apiRetStatus != CY_U3P_SUCCESS)
{
/* Error Handling */
CyU3PDebugPrint (4, "USB Set Endpoint config failed, Error Code = %d\n", apiRetStatus);
CyFxAppErrorHandler (apiRetStatus);
}
/* Configure the status interrupt endpoint.
Note: This endpoint is not being used by the application as of now. This can be used in case
UVC device needs to notify the host about any error conditions. A MANUAL_OUT DMA channel
can be associated with this endpoint and used to send these data packets.
*/
endPointConfig.enable = 1;
endPointConfig.epType = CY_U3P_USB_EP_INTR;
endPointConfig.pcktSize = 64;
endPointConfig.isoPkts = 0;
endPointConfig.streams = 0;
endPointConfig.burstLen = 1;
apiRetStatus = CyU3PSetEpConfig (CY_FX_EP_CONTROL_STATUS, &endPointConfig);
if (apiRetStatus != CY_U3P_SUCCESS)
{
/* Error Handling */
CyU3PDebugPrint (4, "USB Set Endpoint config failed, Error Code = %d\n", apiRetStatus);
CyFxAppErrorHandler (apiRetStatus);
}
/* Create a DMA Manual channel for sending the video data to the USB host. */
dmaMultiConfig.size = CY_FX_UVC_STREAM_BUF_SIZE;
dmaMultiConfig.count = CY_FX_UVC_STREAM_BUF_COUNT;
dmaMultiConfig.validSckCount = 2;
dmaMultiConfig.prodSckId [0] = (CyU3PDmaSocketId_t)CY_U3P_PIB_SOCKET_0;
dmaMultiConfig.prodSckId [1] = (CyU3PDmaSocketId_t)CY_U3P_PIB_SOCKET_1;
dmaMultiConfig.consSckId [0] = (CyU3PDmaSocketId_t)(CY_U3P_UIB_SOCKET_CONS_0 | CY_FX_EP_VIDEO_CONS_SOCKET);
dmaMultiConfig.prodAvailCount = 0;
dmaMultiConfig.prodHeader = 12; /* 12 byte UVC header to be added. */
dmaMultiConfig.prodFooter = 4; /* 4 byte footer to compensate for the 12 byte header. */
dmaMultiConfig.consHeader = 0;
dmaMultiConfig.dmaMode = CY_U3P_DMA_MODE_BYTE;
dmaMultiConfig.notification = CY_U3P_DMA_CB_PROD_EVENT | CY_U3P_DMA_CB_CONS_EVENT;
dmaMultiConfig.cb = CyFxUvcApplnDmaCallback;
apiRetStatus = CyU3PDmaMultiChannelCreate (&glChHandleUVCStream, CY_U3P_DMA_TYPE_MANUAL_MANY_TO_ONE,
&dmaMultiConfig);
if (apiRetStatus != CY_U3P_SUCCESS)
{
/* Error handling */
CyU3PDebugPrint (4, "DMA Channel Creation Failed, Error Code = %d\n", apiRetStatus);
CyFxAppErrorHandler (apiRetStatus);
}
#ifdef USB_DEBUG_INTERFACE
/* Configure the endpoints and create DMA channels used by the USB debug interface.
The command (OUT) endpoint is configured in packet mode and enabled to receive data.
Once the CY_U3P_DMA_CB_PROD_EVENT callback is received, the received data packet is
processed and the data is returned through the CyU3PDmaChannelSetupSendBuffer API call.
*/
endPointConfig.enable = 1;
endPointConfig.epType = CY_U3P_USB_EP_BULK;
endPointConfig.pcktSize = 1024; /* Use SuperSpeed settings here. */
endPointConfig.isoPkts = 0;
endPointConfig.streams = 0;
endPointConfig.burstLen = 1;
apiRetStatus = CyU3PSetEpConfig (CY_FX_EP_DEBUG_CMD, &endPointConfig);
if (apiRetStatus != CY_U3P_SUCCESS)
{
CyU3PDebugPrint (4, "Debug Command endpoint config failed, Error code = %d\n", apiRetStatus);
CyFxAppErrorHandler (apiRetStatus);
}
CyU3PUsbSetEpPktMode (CY_FX_EP_DEBUG_CMD, CyTrue);
apiRetStatus = CyU3PSetEpConfig (CY_FX_EP_DEBUG_RSP, &endPointConfig);
if (apiRetStatus != CY_U3P_SUCCESS)
{
CyU3PDebugPrint (4, "Debug Response endpoint config failed, Error code = %d\n", apiRetStatus);
CyFxAppErrorHandler (apiRetStatus);
}
channelConfig.size = 1024;
channelConfig.count = 1;
channelConfig.prodSckId = CY_U3P_UIB_SOCKET_PROD_0 | CY_FX_EP_DEBUG_CMD_SOCKET;
channelConfig.consSckId = CY_U3P_CPU_SOCKET_CONS;
channelConfig.prodAvailCount = 0;
channelConfig.prodHeader = 0;
channelConfig.prodFooter = 0;
channelConfig.consHeader = 0;
channelConfig.dmaMode = CY_U3P_DMA_MODE_BYTE;
channelConfig.notification = CY_U3P_DMA_CB_PROD_EVENT;
channelConfig.cb = CyFxUvcAppDebugCallback;
apiRetStatus = CyU3PDmaChannelCreate (&glDebugCmdChannel, CY_U3P_DMA_TYPE_MANUAL_IN, &channelConfig);
if (apiRetStatus != CY_U3P_SUCCESS)
{
CyU3PDebugPrint (4, "Debug Command channel create failed, Error code = %d\n", apiRetStatus);
CyFxAppErrorHandler (apiRetStatus);
}
apiRetStatus = CyU3PDmaChannelSetXfer (&glDebugCmdChannel, 0);
if (apiRetStatus != CY_U3P_SUCCESS)
{
CyU3PDebugPrint (4, "Debug channel SetXfer failed, Error code = %d\n", apiRetStatus);
CyFxAppErrorHandler (apiRetStatus);
}
channelConfig.size = 1024;
channelConfig.count = 0; /* No buffers allocated. We will only use the SetupSend API. */
channelConfig.prodSckId = CY_U3P_CPU_SOCKET_PROD;
channelConfig.consSckId = CY_U3P_UIB_SOCKET_CONS_0 | CY_FX_EP_DEBUG_RSP_SOCKET;
channelConfig.prodAvailCount = 0;
channelConfig.prodHeader = 0;
channelConfig.prodFooter = 0;
channelConfig.consHeader = 0;
channelConfig.dmaMode = CY_U3P_DMA_MODE_BYTE;
channelConfig.notification = 0;
channelConfig.cb = 0;
apiRetStatus = CyU3PDmaChannelCreate (&glDebugRspChannel, CY_U3P_DMA_TYPE_MANUAL_OUT, &channelConfig);
if (apiRetStatus != CY_U3P_SUCCESS)
{
CyU3PDebugPrint (4, "Debug Response channel create failed, Error code = %d\n", apiRetStatus);
CyFxAppErrorHandler (apiRetStatus);
}
glDebugRspBuffer = (uint8_t *)CyU3PDmaBufferAlloc (1024);
if (glDebugRspBuffer == 0)
{
CyU3PDebugPrint (4, "Failed to allocate memory for debug buffer\r\n");
CyFxAppErrorHandler (CY_U3P_ERROR_MEMORY_ERROR);
}
#endif
#ifdef FRAME_TIMER_ENABLE
CyU3PTimerCreate(&UvcTimer, CyFxUvcAppProgressTimer, 0x00, glFrameTimerPeriod, 0, CYU3P_NO_ACTIVATE);
#endif
/* Disable USB2.0 connection */
apiRetStatus = CyU3PUsbControlUsb2Support (CyFalse);
if (apiRetStatus != CY_U3P_SUCCESS)
{
CyU3PDebugPrint(4, "USB2.0 host disable failed = %d\n", apiRetStatus);
}
/* Check the enumeration MuxControl_GPIO to High */
apiRetStatus = CyU3PConnectState(CyTrue, CyTrue);
if(CyU3PUsbGetSpeed() != CY_U3P_SUPER_SPEED)
{
apiRetStatus = CyU3PConnectState(CyFalse, CyFalse);
CyU3PDebugPrint(4, "First attempt failed = %d\n", apiRetStatus);
/* Check in other orientation */
//CyU3PGpioSetValue(0, CyFalse);
CyU3PGpioSetValue (FX3_MUXSEL_GPIO, CyFalse);
apiRetStatus = CyU3PUsbControlUsb2Support (CyTrue);
apiRetStatus = CyU3PConnectState(CyTrue, CyTrue);
if (apiRetStatus != CY_U3P_SUCCESS)
{
CyU3PDebugPrint(4, "USB Connect failed, Error code = %d\n", apiRetStatus);
}
}
// /* Enable USB connection from the FX3 device, preferably at USB 3.0 speed. */
// apiRetStatus = CyU3PConnectState (CyTrue, CyTrue);
// if (apiRetStatus != CY_U3P_SUCCESS)
// {
// CyU3PDebugPrint (4, "USB Connect failed, Error Code = %d\n", apiRetStatus);
// CyFxAppErrorHandler (apiRetStatus);
// }
}
void
CyFxUvcApplnStop()
{
#ifdef DEBUG_PRINT_FRAME_COUNT
/* Clear state variables. */
glDmaDone = 1;
glFrameCount = 0;
#endif /* DEBUG_PRINT_FRAME_COUNT */
#ifdef FRAME_TIMER_ENABLE
/* Stop the frame timer during an application stop */
CyU3PTimerStop(&UvcTimer);
#endif
/* Disable the GPIF state machine. */
CyU3PGpifDisable (CyFalse);
streamingStarted = CyFalse;
glDmaResetFlag = CY_FX_UVC_DMA_RESET_EVENT_NOT_ACTIVE;
/* Place the EP in NAK mode before cleaning up the pipe. */
CyU3PUsbSetEpNak (CY_FX_EP_BULK_VIDEO, CyTrue);
CyU3PBusyWait (125);
/* Reset and flush the endpoint pipe. */
CyU3PDmaMultiChannelReset (&glChHandleUVCStream);
CyU3PUsbFlushEp (CY_FX_EP_BULK_VIDEO);
CyU3PUsbSetEpNak (CY_FX_EP_BULK_VIDEO, CyFalse);
CyU3PBusyWait (125);
/* Allow USB low power link transitions at this stage. */
CyU3PUsbLPMEnable ();
CyU3PReturnStatus_t apiRetStatus = CY_U3P_SUCCESS;
CyU3PDebugPrint (4, "Application Stopped\r\n");
}
void
CyFxUvcApplnStart()
{
CyU3PReturnStatus_t apiRetStatus;
#ifdef DEBUG_PRINT_FRAME_COUNT
/* Clear state variables. */
glDmaDone = 1;
glFrameCount = 0;
#endif /* DEBUG_PRINT_FRAME_COUNT */
/* Start with frame ID 0. */
glUVCHeader[1] &= ~CY_FX_UVC_HEADER_FRAME_ID;
/* Make sure we return to an active USB link state and stay there. */
CyU3PUsbLPMDisable ();
if (CyU3PUsbGetSpeed () == CY_U3P_SUPER_SPEED)
{
CyU3PUsbSetLinkPowerState (CyU3PUsbLPM_U0);
CyU3PBusyWait (200);
}
else
{
CyU3PUsb2Resume ();
}
/* Place the EP in NAK mode before cleaning up the pipe. */
CyU3PUsbSetEpNak (CY_FX_EP_BULK_VIDEO, CyTrue);
CyU3PBusyWait (125);
/* Reset and flush the endpoint pipe. */
CyU3PUsbFlushEp (CY_FX_EP_BULK_VIDEO);
CyU3PDmaMultiChannelReset (&glChHandleUVCStream);
/* Set DMA Channel transfer size, first producer socket */
apiRetStatus = CyU3PDmaMultiChannelSetXfer (&glChHandleUVCStream, 0, 0);
if (apiRetStatus != CY_U3P_SUCCESS)
{
/* Error handling */
CyU3PDebugPrint (4, "DMA Channel Set Transfer Failed, Error Code = %d\r\n", apiRetStatus);
CyFxAppErrorHandler (apiRetStatus);
}
CyU3PUsbSetEpNak (CY_FX_EP_BULK_VIDEO, CyFalse);
CyU3PBusyWait (125);
#ifdef FRAME_TIMER_ENABLE
/* Start the frame timer so that we receive first buffer on time */
CyU3PTimerModify(&UvcTimer, glFrameTimerPeriod, 0);
CyU3PTimerStart(&UvcTimer);
#endif
glDmaResetFlag = CY_FX_UVC_DMA_RESET_EVENT_NOT_ACTIVE;
/* Start the state machine from the designated start state. */
apiRetStatus = CyU3PGpifSMSwitch(CY_FX_UVC_INVALID_GPIF_STATE, START_SCK0,
CY_FX_UVC_INVALID_GPIF_STATE, ALPHA_START_SCK0, CY_FX_UVC_GPIF_SWITCH_TIMEOUT);
if (apiRetStatus != CY_U3P_SUCCESS)
{
/* Error Handling */
CyU3PDebugPrint (4, "Switching GPIF state machine failed, Error Code = %d\r\n", apiRetStatus);
CyFxAppErrorHandler (apiRetStatus);
}
CyU3PDebugPrint (4, "Application Started\r\n");
}
/*
* Entry function for the UVC Application Thread
*/
void
UVCAppThread_Entry (
uint32_t input)
{
CyU3PUsbLinkPowerMode usb3mode;
uint16_t wakeReason;
CyU3PReturnStatus_t apiRetStatus;
uint32_t flag;
/* Initialize the Uart Debug Module */
CyFxUVCApplnDebugInit ();
/* Initialize the I2C interface */
CyFxUVCApplnI2CInit ();
/* Initialize the UVC Application */
CyFxUVCApplnInit ();
/*
The actual data forwarding from sensor to USB host is done from the DMA and GPIF callback
functions. The thread is only responsible for checking for streaming start/stop conditions.
The CY_FX_UVC_STREAM_EVENT event flag will indicate that the UVC video stream should be started.
The CY_FX_UVC_STREAM_ABORT_EVENT event indicates that we need to abort the video streaming. This
only happens when we receive a CLEAR_FEATURE request indicating that streaming is to be stopped,
or when we have a critical error in the data path.
The CY_FX_UVC_DMA_RESET_EVENT indicates that we need to reset the DMA and endpoint buffers and
disable GPIF. We restarting the GPIF state machine from first state and device will start streaming
video after it receives the next frame. FOr camera applications, if we discard few frames and resatrt
video stream, it shouldn't be a problem. It may be a bad user experience if we abruptly stop video stream.
Note that we will not restart video stream after few commit buffer failures as this is required for MAC OS.
The CY_FX_USB_SUSPEND_EVENT_HANDLER indicates that device must enter low power USB suspend mode.
There is a provision for users to reset and/or turn OFF power to the sensor/Image signal processor (ISP).
*/
for (;;)
{
apiRetStatus = CyU3PEventGet (&glFxUVCEvent, CY_FX_UVC_STREAM_ABORT_EVENT | CY_FX_UVC_STREAM_EVENT |
CY_FX_UVC_DMA_RESET_EVENT | CY_FX_USB_SUSPEND_EVENT_HANDLER, CYU3P_EVENT_OR_CLEAR, &flag, LOOP_TIMEOUT);
if (apiRetStatus == CY_U3P_SUCCESS)
{
/* Request to start video stream. */
if ((flag & CY_FX_UVC_STREAM_EVENT) != 0)
{
glIsApplnActive = CyTrue;
CyFxUvcApplnStart();
}
/* Video stream abort requested. */
if ((flag & CY_FX_UVC_STREAM_ABORT_EVENT) != 0)
{
glIsApplnActive = CyFalse;
CyFxUvcApplnStop();
}
if (((flag & CY_FX_UVC_DMA_RESET_EVENT) != 0) && glIsApplnActive)
{
if(glDmaResetFlag == CY_FX_UVC_DMA_RESET_COMMIT_BUFFER_FAILURE)
CyU3PDebugPrint (4, "DMA Reset Event: Commit buffer failure\r\n");
#ifdef FRAME_TIMER_ENABLE
else if(glDmaResetFlag == CY_FX_UVC_DMA_RESET_FRAME_TIMER_OVERFLOW)
CyU3PDebugPrint (4, "DMA Reset Event: Frame timer overflow, time period = %d\r\n", glFrameTimerPeriod);
#endif
CyFxUvcApplnStop();
if ((glIsApplnActive) && (++glCommitBufferFailureCount < CY_FX_UVC_MAX_COMMIT_BUF_FAILURE_CNT))
{
CyFxUvcApplnStart();
}
/* Reset the video streaming flags for a MAC OS */
if(glCommitBufferFailureCount == CY_FX_UVC_MAX_COMMIT_BUF_FAILURE_CNT)
{
glIsApplnActive = CyFalse;
CyU3PDebugPrint (4, "Application Stopped after %d Commit buffer failures\r\n", glCommitBufferFailureCount);
glCommitBufferFailureCount = 0;
}
}
/* Handle USB suspend event by putting device into low power mode */
if ((flag & CY_FX_USB_SUSPEND_EVENT_HANDLER) != 0)
{
/* Include your code here... to reset and/or shutdown power to the sensor/ISP
* This will help to reduce the overall power consumption by the kit */
/* Place FX3 in Low Power Suspend mode */
CyU3PDebugPrint(4, "Entering USB Suspend Mode\r\n");
CyU3PThreadSleep(5);
/* As per the USB3 specs, link layer takes 10ms (Max.) to leave U3 state. If the device sees some spurious (unwanted)
* USB activity it will leave suspend mode (even though USB is in U3 state). The device can wait for 10ms (U3 exit LFPS duration)
* and check the Link Layer State. If the link layer is not in U3 state, CX3 device will wake up else it will enter suspend mode */
do
{
apiRetStatus = CyU3PSysEnterSuspendMode(CY_U3P_SYS_USB_BUS_ACTVTY_WAKEUP_SRC, 0, &wakeReason);
if ((apiRetStatus != CY_U3P_SUCCESS) || (CyU3PUsbGetSpeed() != CY_U3P_SUPER_SPEED))
break;
/* Wait for the maximum U3 exit LFPS duration. */
CyU3PThreadSleep(10);
/* If the link is still in U3, we can continue to attempt Suspend mode entry. */
apiRetStatus = CyU3PUsbGetLinkPowerState(&usb3mode);
if ((apiRetStatus != CY_U3P_SUCCESS) || (usb3mode != CyU3PUsbLPM_U3))
break;
}while(1);
/* Leaving Low Power Suspend mode */
CyU3PDebugPrint(4, "Leaving Suspend Mode\r\n");
/* Include your code here... to bring sensor/ISP out of reset and/or switch ON the power to the sensor/ISP */
}
}
#ifdef DEBUG_PRINT_FRAME_COUNT
CyU3PDebugPrint (4, "UVC: Completed %d frames and %d buffers\r\n", glFrameCount,
(glDmaDone != 0) ? (glDmaDone - 1) : 0);
#endif
}
}
/*
* Handler for control requests addressed to the Processing Unit.
*/
static void
UVCHandleProcessingUnitRqts (
void)
{
CyU3PReturnStatus_t apiRetStatus = CY_U3P_SUCCESS;
uint16_t readCount, brightnessVal;
uint16_t exposure_val, gain_val;
switch (wValue)
{
case CY_FX_UVC_PU_BRIGHTNESS_CONTROL:
switch (bRequest)
{
case CY_FX_USB_UVC_GET_LEN_REQ: /* Length of brightness data = 2 byte. */
glEp0Buffer[0] = 2;
glEp0Buffer[1] = 0;
CyU3PUsbSendEP0Data (2, (uint8_t *)glEp0Buffer);
break;
case CY_FX_USB_UVC_GET_CUR_REQ: /* Current brightness value. */
glEp0Buffer[0] = SensorGetBrightness ();
glEp0Buffer[1] = 0;
CyU3PUsbSendEP0Data (2, (uint8_t *)glEp0Buffer);
break;
case CY_FX_USB_UVC_GET_MIN_REQ: /* Minimum brightness = 0. */
glEp0Buffer[0] = 0;
glEp0Buffer[1] = 0;
CyU3PUsbSendEP0Data (2, (uint8_t *)glEp0Buffer);
break;
case CY_FX_USB_UVC_GET_MAX_REQ: /* Maximum brightness . */
gain_val = getMaxBrightness();
glEp0Buffer[0] = gain_val & 0xFF;
glEp0Buffer[1] = (gain_val>>8) & 0xFF;
CyU3PUsbSendEP0Data (2, (uint8_t *)glEp0Buffer);
break;
case CY_FX_USB_UVC_GET_RES_REQ: /* Resolution = 1. */
glEp0Buffer[0] = 1;
glEp0Buffer[1] = 0;
CyU3PUsbSendEP0Data (2, (uint8_t *)glEp0Buffer);
break;
case CY_FX_USB_UVC_GET_INFO_REQ: /* Both GET and SET requests are supported, auto modes not supported */
glEp0Buffer[0] = 3;
glEp0Buffer[1] = 0;
CyU3PUsbSendEP0Data (2, (uint8_t *)glEp0Buffer);
break;
case CY_FX_USB_UVC_GET_DEF_REQ: /* Default brightness value = 55. */
glEp0Buffer[0] = 55;
glEp0Buffer[1] = 0;
CyU3PUsbSendEP0Data (2, (uint8_t *)glEp0Buffer);
break;
case CY_FX_USB_UVC_SET_CUR_REQ: /* Update brightness value. */
apiRetStatus = CyU3PUsbGetEP0Data (CY_FX_UVC_MAX_PROBE_SETTING_ALIGNED,
glEp0Buffer, &readCount);
if (apiRetStatus == CY_U3P_SUCCESS)
{
brightnessVal = CY_U3P_MAKEWORD(glEp0Buffer[1], glEp0Buffer[0]);
/* Update the brightness value only if the value is within the range */
if( brightnessVal <= getMaxBrightness())
{
SensorSetBrightness (brightnessVal);
}
}
break;
default:
glUvcVcErrorCode = CY_FX_UVC_VC_ERROR_CODE_INVALID_REQUEST;
CyU3PUsbStall (0, CyTrue, CyFalse);
break;
}
break;
case CY_FX_UVC_PU_SATURATION_CONTROL:
{
switch (bRequest)
{
case CY_FX_USB_UVC_GET_LEN_REQ: /* Length of brightness data = 2 byte. */
glEp0Buffer[0] = 2;
CyU3PUsbSendEP0Data (2, (uint8_t *)glEp0Buffer);
break;
case CY_FX_USB_UVC_GET_CUR_REQ: /* Current brightness value. */
exposure_val = sensor_get_exposure ();
glEp0Buffer[0] = exposure_val & 0xFF;
glEp0Buffer[1] = (exposure_val>>8) & 0xFF;
CyU3PUsbSendEP0Data (2, (uint8_t *)glEp0Buffer);
break;
case CY_FX_USB_UVC_GET_MIN_REQ: /* Minimum brightness = 0. */
exposure_val = sensor_get_min_exposure ();
glEp0Buffer[0] = exposure_val & 0xFF;
glEp0Buffer[1] = (exposure_val>>8) & 0xFF;
CyU3PUsbSendEP0Data (2, (uint8_t *)glEp0Buffer);
break;
case CY_FX_USB_UVC_GET_MAX_REQ: /* Maximum brightness = 255. */
exposure_val = sensor_get_max_exposure ();
glEp0Buffer[0] = exposure_val & 0xFF;
glEp0Buffer[1] = (exposure_val>>8) & 0xFF;
CyU3PUsbSendEP0Data (2, (uint8_t *)glEp0Buffer);
break;
case CY_FX_USB_UVC_GET_RES_REQ: /* Resolution = 1. */
glEp0Buffer[0] = 1;
glEp0Buffer[1] = 0;
CyU3PUsbSendEP0Data (2, (uint8_t *)glEp0Buffer);
break;
case CY_FX_USB_UVC_GET_INFO_REQ: /* Both GET and SET requests are supported, auto modes not supported */
glEp0Buffer[0] = 3;
CyU3PUsbSendEP0Data (2, (uint8_t *)glEp0Buffer);
break;
case CY_FX_USB_UVC_GET_DEF_REQ: /* Default brightness */
exposure_val = sensor_get_def_exposure ();
glEp0Buffer[0] = exposure_val & 0xFF;
glEp0Buffer[1] = (exposure_val>>8) & 0xFF;
CyU3PUsbSendEP0Data (2, (uint8_t *)glEp0Buffer);
break;
case CY_FX_USB_UVC_SET_CUR_REQ: /* Update brightness value. */
apiRetStatus = CyU3PUsbGetEP0Data (CY_FX_UVC_MAX_PROBE_SETTING_ALIGNED,
glEp0Buffer, &readCount);
if (apiRetStatus == CY_U3P_SUCCESS)
{
exposure_val = CY_U3P_MAKEWORD(glEp0Buffer[1], glEp0Buffer[0]);
sensor_set_exposure (exposure_val);
}
break;
default:
glUvcVcErrorCode = CY_FX_UVC_VC_ERROR_CODE_INVALID_REQUEST;
CyU3PUsbStall (0, CyTrue, CyFalse);
break;
}
break;
}
case CY_FX_UVC_PU_GAMMA_CONTROL: //used as test Pattern
{
switch (bRequest)
{
case CY_FX_USB_UVC_GET_LEN_REQ: /* Length of TestPattern data = 2 byte. */
glEp0Buffer[0] = 2;
glEp0Buffer[1] = 0;
CyU3PUsbSendEP0Data (2, (uint8_t *)glEp0Buffer);
break;
case CY_FX_USB_UVC_GET_CUR_REQ: /* Current TestPattern value. */
glEp0Buffer[0] = sensor_get_test_pattern ();
glEp0Buffer[1] = 0;
CyU3PUsbSendEP0Data (2, (uint8_t *)glEp0Buffer);
break;
case CY_FX_USB_UVC_GET_MIN_REQ: /* Minimum TestPattern = 0. */
glEp0Buffer[0] = 0;
glEp0Buffer[1] = 0;
CyU3PUsbSendEP0Data (2, (uint8_t *)glEp0Buffer);
break;
case CY_FX_USB_UVC_GET_MAX_REQ: /* Maximum TestPattern = 8. */
glEp0Buffer[0] = 8;
glEp0Buffer[1] = 0;
CyU3PUsbSendEP0Data (2, (uint8_t *)glEp0Buffer);
break;
case CY_FX_USB_UVC_GET_RES_REQ: /* Resolution = 1. */
glEp0Buffer[0] = 1;
glEp0Buffer[1] = 0;
CyU3PUsbSendEP0Data (2, (uint8_t *)glEp0Buffer);
break;
case CY_FX_USB_UVC_GET_INFO_REQ: /* Both GET and SET requests are supported, auto modes not supported */
glEp0Buffer[0] = 3;
glEp0Buffer[1] = 0;
CyU3PUsbSendEP0Data (2, (uint8_t *)glEp0Buffer);
break;
case CY_FX_USB_UVC_GET_DEF_REQ: /* Default TestPattern 0 No test pattern */
glEp0Buffer[0] = 0;
glEp0Buffer[1] = 0;
CyU3PUsbSendEP0Data (2, (uint8_t *)glEp0Buffer);
break;
case CY_FX_USB_UVC_SET_CUR_REQ: /* Update TestPattern value. */
apiRetStatus = CyU3PUsbGetEP0Data (CY_FX_UVC_MAX_PROBE_SETTING_ALIGNED,
glEp0Buffer, &readCount);
if (apiRetStatus == CY_U3P_SUCCESS)
{
gain_val = CY_U3P_MAKEWORD(glEp0Buffer[1], glEp0Buffer[0]);
/* Update the gain value only if the value is within the range */
if(gain_val >= 0 && gain_val <= 8)
{
sensor_set_test_pattern (glEp0Buffer[0]);
}
}
break;
default:
glUvcVcErrorCode = CY_FX_UVC_VC_ERROR_CODE_INVALID_REQUEST;
CyU3PUsbStall (0, CyTrue, CyFalse);
break;
}
break;
}
default:
/*
* Add additional code here to support
* other controls.
*/
glUvcVcErrorCode = CY_FX_UVC_VC_ERROR_CODE_INVALID_CONTROL;
CyU3PUsbStall (0, CyTrue, CyFalse);
break;
}
}
/*
* Handler for control requests addressed to the UVC Camera Terminal unit.
*/
static void
UVCHandleCameraTerminalRqts (
void)
{
#ifdef UVC_PTZ_SUPPORT
CyU3PReturnStatus_t apiRetStatus = CY_U3P_SUCCESS;
uint16_t readCount;
uint16_t zoomVal;
int32_t panVal, tiltVal;
CyBool_t sendData = CyFalse;
#endif
switch (wValue)
{
#ifdef UVC_PTZ_SUPPORT
case CY_FX_UVC_CT_ZOOM_ABSOLUTE_CONTROL:
switch (bRequest)
{
case CY_FX_USB_UVC_GET_INFO_REQ:
glEp0Buffer[0] = 3; /* Support GET/SET queries. */
CyU3PUsbSendEP0Data (1, (uint8_t *)glEp0Buffer);
break;
case CY_FX_USB_UVC_GET_CUR_REQ: /* Current zoom control value. */
zoomVal = CyFxUvcAppGetCurrentZoom ();
sendData = CyTrue;
break;
case CY_FX_USB_UVC_GET_MIN_REQ: /* Minimum zoom control value. */
zoomVal = CyFxUvcAppGetMinimumZoom ();
sendData = CyTrue;
break;
case CY_FX_USB_UVC_GET_MAX_REQ: /* Maximum zoom control value. */
zoomVal = CyFxUvcAppGetMaximumZoom ();
sendData = CyTrue;
break;
case CY_FX_USB_UVC_GET_RES_REQ: /* Resolution is one unit. */
zoomVal = CyFxUvcAppGetZoomResolution ();
sendData = CyTrue;
break;
case CY_FX_USB_UVC_GET_DEF_REQ: /* Default zoom setting. */
zoomVal = CyFxUvcAppGetDefaultZoom ();
sendData = CyTrue;
break;
case CY_FX_USB_UVC_SET_CUR_REQ:
apiRetStatus = CyU3PUsbGetEP0Data (CY_FX_UVC_MAX_PROBE_SETTING_ALIGNED,
glEp0Buffer, &readCount);
if (apiRetStatus == CY_U3P_SUCCESS)
{
zoomVal = (glEp0Buffer[0]) | (glEp0Buffer[1] << 8);
CyFxUvcAppModifyZoom (zoomVal);
}
break;
default:
glUvcVcErrorCode = CY_FX_UVC_VC_ERROR_CODE_INVALID_REQUEST;
CyU3PUsbStall (0, CyTrue, CyFalse);
break;
}
if (sendData)
{
/* Send the 2-byte data in zoomVal back to the USB host. */
glEp0Buffer[0] = CY_U3P_GET_LSB (zoomVal);
glEp0Buffer[1] = CY_U3P_GET_MSB (zoomVal);
CyU3PUsbSendEP0Data (wLength, (uint8_t *)glEp0Buffer);
}
break;
case CY_FX_UVC_CT_PANTILT_ABSOLUTE_CONTROL:
switch (bRequest)
{
case CY_FX_USB_UVC_GET_INFO_REQ:
glEp0Buffer[0] = 3; /* GET/SET requests supported for this control */
CyU3PUsbSendEP0Data (1, (uint8_t *)glEp0Buffer);
break;
case CY_FX_USB_UVC_GET_CUR_REQ:
panVal = CyFxUvcAppGetCurrentPan ();
tiltVal = CyFxUvcAppGetCurrentTilt ();
sendData = CyTrue;
break;
case CY_FX_USB_UVC_GET_MIN_REQ:
panVal = CyFxUvcAppGetMinimumPan ();
tiltVal = CyFxUvcAppGetMinimumTilt ();
sendData = CyTrue;
break;
case CY_FX_USB_UVC_GET_MAX_REQ:
panVal = CyFxUvcAppGetMaximumPan ();
tiltVal = CyFxUvcAppGetMaximumTilt ();
sendData = CyTrue;
break;
case CY_FX_USB_UVC_GET_RES_REQ:
panVal = CyFxUvcAppGetPanResolution ();
tiltVal = CyFxUvcAppGetTiltResolution ();
sendData = CyTrue;
break;
case CY_FX_USB_UVC_GET_DEF_REQ:
panVal = CyFxUvcAppGetDefaultPan ();
tiltVal = CyFxUvcAppGetDefaultTilt ();
sendData = CyTrue;
break;
case CY_FX_USB_UVC_SET_CUR_REQ:
apiRetStatus = CyU3PUsbGetEP0Data (CY_FX_UVC_MAX_PROBE_SETTING_ALIGNED,
glEp0Buffer, &readCount);
if (apiRetStatus == CY_U3P_SUCCESS)
{
panVal = (glEp0Buffer[0]) | (glEp0Buffer[1]<<8) |
(glEp0Buffer[2]<<16) | (glEp0Buffer[2]<<24);
tiltVal = (glEp0Buffer[4]) | (glEp0Buffer[5]<<8) |
(glEp0Buffer[6]<<16) | (glEp0Buffer[7]<<24);
CyFxUvcAppModifyPan (panVal);
CyFxUvcAppModifyTilt (tiltVal);
}
break;
default:
CyU3PUsbStall (0, CyTrue, CyFalse);
glUvcVcErrorCode = CY_FX_UVC_VC_ERROR_CODE_INVALID_REQUEST;
break;
}
if (sendData)
{
/* Send the 8-byte PAN and TILT values back to the USB host. */
glEp0Buffer[0] = CY_U3P_DWORD_GET_BYTE0 (panVal);
glEp0Buffer[1] = CY_U3P_DWORD_GET_BYTE1 (panVal);
glEp0Buffer[2] = CY_U3P_DWORD_GET_BYTE2 (panVal);
glEp0Buffer[3] = CY_U3P_DWORD_GET_BYTE3 (panVal);
glEp0Buffer[4] = CY_U3P_DWORD_GET_BYTE0 (tiltVal);
glEp0Buffer[5] = CY_U3P_DWORD_GET_BYTE1 (tiltVal);
glEp0Buffer[6] = CY_U3P_DWORD_GET_BYTE2 (tiltVal);
glEp0Buffer[7] = CY_U3P_DWORD_GET_BYTE3 (tiltVal);
CyU3PUsbSendEP0Data (wLength, (uint8_t *)glEp0Buffer);
}
break;
#endif
default:
glUvcVcErrorCode = CY_FX_UVC_VC_ERROR_CODE_INVALID_CONTROL;
CyU3PUsbStall (0, CyTrue, CyFalse);
break;
}
}
/*
* Handler for UVC Interface control requests.
*/
static void
UVCHandleInterfaceCtrlRqts (
void)
{
switch (wValue)
{
/* Control to send video control errors to the Host. When device stalls a video
* control request, Windows host gets the error through this control */
case CY_FX_UVC_VC_REQUEST_ERROR_CODE_CONTROL:
switch (bRequest)
{
case CY_FX_USB_UVC_GET_CUR_REQ:
CyU3PUsbSendEP0Data(1, &glUvcVcErrorCode);
glUvcVcErrorCode = CY_FX_UVC_VC_ERROR_CODE_NO_ERROR;
break;
}
break;
}
}
/*
* Handler for control requests addressed to the Extension Unit.
*/
static void
UVCHandleExtensionUnitRqts (
void)
{
#ifdef UVC_EXTENSION_UNIT
CyU3PReturnStatus_t apiRetStatus = CY_U3P_SUCCESS;
uint16_t readCount;
CyBool_t sendData = CyFalse;
#endif
switch (wValue)
{
#ifdef UVC_EXTENSION_UNIT
case CY_FX_UVC_XU_GET_FIRMWARE_VERSION_CONTROL:
switch (bRequest)
{
case CY_FX_USB_UVC_GET_INFO_REQ:
glEp0Buffer[0] = 3; /* Support GET/SET queries. */
CyU3PUsbSendEP0Data (1, (uint8_t *)glEp0Buffer);
break;
case CY_FX_USB_UVC_GET_CUR_REQ: /* Current firmware version control value. */
CyU3PMemCopy(glEp0Buffer, glFxUvcFirmwareVersion, 5);
sendData = CyTrue;
break;
case CY_FX_USB_UVC_GET_MIN_REQ: /* Minimum firmware version control value. */
/* Min value is version 0.1 Build date: 1/1/17 */
glEp0Buffer[0] = 0;
CyU3PMemSet(&glEp0Buffer[1], 1, 3);
glEp0Buffer[4] = 17;
sendData = CyTrue;
break;
case CY_FX_USB_UVC_GET_MAX_REQ: /* Maximum firmware version control value. */
/* Max value is version 255.255 Build date: 12/31/99 */
CyU3PMemSet(glEp0Buffer, 0xFF, 2);
glEp0Buffer[2] = 12;
glEp0Buffer[3] = 31;
glEp0Buffer[4] = 99;
sendData = CyTrue;
break;
case CY_FX_USB_UVC_GET_RES_REQ: /* Resolution is one unit for all the fields */
CyU3PMemSet(glEp0Buffer, 1, 5);
sendData = CyTrue;
break;
case CY_FX_USB_UVC_GET_LEN_REQ: /* Length of the control */
/* As per UVC spec, we send 2 bytes of data. Firmware version control length is 5 bytes */
glEp0Buffer[0] = 5;
glEp0Buffer[1] = 0;
CyU3PUsbSendEP0Data (2, (uint8_t *)glEp0Buffer);
break;
case CY_FX_USB_UVC_GET_DEF_REQ: /* Default firmware version setting, is the current value. But it can be changed */
CyU3PMemCopy(glEp0Buffer, glFxUvcFirmwareVersion, 5);
sendData = CyTrue;
break;
case CY_FX_USB_UVC_SET_CUR_REQ:
apiRetStatus = CyU3PUsbGetEP0Data (CY_FX_UVC_MAX_PROBE_SETTING_ALIGNED,
glEp0Buffer, &readCount);
if (apiRetStatus == CY_U3P_SUCCESS)
{
/* Copy firmware version sent by Host application */
CyU3PMemCopy(glFxUvcFirmwareVersion, glEp0Buffer, 5);
}
break;
default:
glUvcVcErrorCode = CY_FX_UVC_VC_ERROR_CODE_INVALID_REQUEST;
CyU3PUsbStall (0, CyTrue, CyFalse);
break;
}
if (sendData)
{
/* Send the data to the USB host. */
CyU3PUsbSendEP0Data (5, (uint8_t *)glEp0Buffer);
}
break;
#endif
default:
glUvcVcErrorCode = CY_FX_UVC_VC_ERROR_CODE_INVALID_CONTROL;
CyU3PUsbStall (0, CyTrue, CyFalse);
break;
}
}
/*
* Handler for the video streaming control requests.
*/
static void UVCHandleVideoStreamingRqts (void)
{
CyU3PReturnStatus_t apiRetStatus = CY_U3P_SUCCESS;
uint16_t readCount;
switch (wValue)
{
case CY_FX_UVC_PROBE_CTRL:
switch (bRequest)
{
case CY_FX_USB_UVC_GET_INFO_REQ:
glEp0Buffer[0] = 3; /* GET/SET requests are supported. */
CyU3PUsbSendEP0Data (1, (uint8_t *)glEp0Buffer);
break;
case CY_FX_USB_UVC_GET_LEN_REQ:
glEp0Buffer[0] = CY_FX_UVC_MAX_PROBE_SETTING;
CyU3PUsbSendEP0Data (1, (uint8_t *)glEp0Buffer);
break;
case CY_FX_USB_UVC_GET_CUR_REQ:
case CY_FX_USB_UVC_GET_MIN_REQ:
case CY_FX_USB_UVC_GET_MAX_REQ:
case CY_FX_USB_UVC_GET_DEF_REQ: /* There is only one setting per USB speed. */
if (usbSpeed == CY_U3P_SUPER_SPEED)
{
CyU3PUsbSendEP0Data (CY_FX_UVC_MAX_PROBE_SETTING, (uint8_t *)glProbeCtrl);
}
else
{
CyU3PUsbSendEP0Data (CY_FX_UVC_MAX_PROBE_SETTING, (uint8_t *)glProbeCtrl20);
}
break;
case CY_FX_USB_UVC_SET_CUR_REQ:
apiRetStatus = CyU3PUsbGetEP0Data (CY_FX_UVC_MAX_PROBE_SETTING_ALIGNED, glCommitCtrl, &readCount);
if (apiRetStatus == CY_U3P_SUCCESS)
{
/* Copy the relevant settings from the host provided data into the
active data structure. */
glProbeCtrl[2] = glCommitCtrl[2];
glProbeCtrl[3] = glCommitCtrl[3];
glProbeCtrl[4] = glCommitCtrl[4];
glProbeCtrl[5] = glCommitCtrl[5];
glProbeCtrl[6] = glCommitCtrl[6];
glProbeCtrl[7] = glCommitCtrl[7];
}
break;
default:
CyU3PUsbStall (0, CyTrue, CyFalse);
break;
}
break;
case CY_FX_UVC_COMMIT_CTRL:
switch (bRequest)
{
case CY_FX_USB_UVC_GET_INFO_REQ:
glEp0Buffer[0] = 3; /* GET/SET requests are supported. */
CyU3PUsbSendEP0Data (1, (uint8_t *)glEp0Buffer);
break;
case CY_FX_USB_UVC_GET_LEN_REQ:
glEp0Buffer[0] = CY_FX_UVC_MAX_PROBE_SETTING;
CyU3PUsbSendEP0Data (1, (uint8_t *)glEp0Buffer);
break;
case CY_FX_USB_UVC_GET_CUR_REQ:
if (usbSpeed == CY_U3P_SUPER_SPEED)
{
CyU3PUsbSendEP0Data (CY_FX_UVC_MAX_PROBE_SETTING, (uint8_t *)glProbeCtrl);
}
else
{
CyU3PUsbSendEP0Data (CY_FX_UVC_MAX_PROBE_SETTING, (uint8_t *)glProbeCtrl20);
}
break;
case CY_FX_USB_UVC_SET_CUR_REQ:
/* The host has selected the parameters for the video stream. Check the desired
resolution settings, configure the sensor and start the video stream.
*/
apiRetStatus = CyU3PUsbGetEP0Data (CY_FX_UVC_MAX_PROBE_SETTING_ALIGNED, glCommitCtrl, &readCount);
if (apiRetStatus == CY_U3P_SUCCESS)
{
uvc_control_t *uvc_control = (uvc_control_t *)glCommitCtrl;
sensor_handle_uvc_control(uvc_control->frame_index, uvc_control->frame_interval);
#ifdef FRAME_TIMER_ENABLE
/* We are using frame timer value of 400ms as the frame time is upto 66ms.
* Having more margin so that DMA reset doen't happen every now and then */
glFrameTimerPeriod = CY_FX_UVC_FRAME_TIMER_VAL_400MS;
#endif
/* We can start streaming video now. */
apiRetStatus = CyU3PEventSet (&glFxUVCEvent, CY_FX_UVC_STREAM_EVENT, CYU3P_EVENT_OR);
if (apiRetStatus != CY_U3P_SUCCESS)
{
CyU3PDebugPrint (4, "Set CY_FX_UVC_STREAM_EVENT failed %x\r\n", apiRetStatus);
}
}
break;
default:
CyU3PUsbStall (0, CyTrue, CyFalse);
break;
}
break;
default:
CyU3PUsbStall (0, CyTrue, CyFalse);
break;
}
}
/*
* Entry function for the UVC control request processing thread.
*/
void
UVCAppEP0Thread_Entry (
uint32_t input)
{
uint32_t eventMask = (CY_FX_UVC_VIDEO_CONTROL_REQUEST_EVENT | CY_FX_UVC_VIDEO_STREAM_REQUEST_EVENT);
uint32_t eventFlag;
#ifdef USB_DEBUG_INTERFACE
CyU3PReturnStatus_t apiRetStatus;
CyU3PDmaBuffer_t dmaInfo;
eventMask |= CY_FX_USB_DEBUG_CMD_EVENT;
#endif
for (;;)
{
/* Wait for a Video control or streaming related request on the control endpoint. */
if (CyU3PEventGet (&glFxUVCEvent, eventMask, CYU3P_EVENT_OR_CLEAR, &eventFlag,
CYU3P_WAIT_FOREVER) == CY_U3P_SUCCESS)
{
usbSpeed = CyU3PUsbGetSpeed ();
if (eventFlag & CY_FX_UVC_VIDEO_CONTROL_REQUEST_EVENT)
{
switch ((wIndex >> 8))
{
case CY_FX_UVC_PROCESSING_UNIT_ID:
UVCHandleProcessingUnitRqts ();
break;
case CY_FX_UVC_CAMERA_TERMINAL_ID:
UVCHandleCameraTerminalRqts ();
break;
case CY_FX_UVC_INTERFACE_CTRL:
UVCHandleInterfaceCtrlRqts ();
break;
case CY_FX_UVC_EXTENSION_UNIT_ID:
UVCHandleExtensionUnitRqts ();
break;
default:
/* Unsupported request. Fail by stalling the control endpoint. */
CyU3PUsbStall (0, CyTrue, CyFalse);
break;
}
}
if (eventFlag & CY_FX_UVC_VIDEO_STREAM_REQUEST_EVENT)
{
if (wIndex != CY_FX_UVC_STREAM_INTERFACE)
{
CyU3PUsbStall (0, CyTrue, CyFalse);
}
else
{
UVCHandleVideoStreamingRqts ();
}
}
#ifdef USB_DEBUG_INTERFACE
if (eventFlag & CY_FX_USB_DEBUG_CMD_EVENT)
{
/* Get the command buffer */
apiRetStatus = CyU3PDmaChannelGetBuffer (&glDebugCmdChannel, &dmaInfo, CYU3P_WAIT_FOREVER);
if (apiRetStatus != CY_U3P_SUCCESS)
{
CyU3PDebugPrint (4, "Failed to receive debug command, Error code = %d\r\n", apiRetStatus);
CyFxAppErrorHandler (apiRetStatus);
}
/* Decode the command from the command buffer, error checking is not implemented,
* so the command is expected to be correctly sent from the host application. First byte indicates
* read (0x00) or write (0x01) command. Second and third bytes are register address high byte and
* register address low byte. For read commands the fourth byte (optional) can be N>0, to read N
* registers in sequence. Response first byte is status (0=Pass, !0=Fail) followed by N pairs of
* register value high byte and register value low byte.
*/
if (dmaInfo.buffer[0] == 0)
{
if (dmaInfo.count == 3)
{
glDebugRspBuffer[0] = SensorRead2B (SENSOR_ADDR_RD, dmaInfo.buffer[1], dmaInfo.buffer[2],
(glDebugRspBuffer+1));
dmaInfo.count = 3;
}
else if (dmaInfo.count == 4)
{
if (dmaInfo.buffer[3] > 0)
{
glDebugRspBuffer[0] = SensorRead (SENSOR_ADDR_RD, dmaInfo.buffer[1], dmaInfo.buffer[2],
(dmaInfo.buffer[3]*2), (glDebugRspBuffer+1));
}
dmaInfo.count = dmaInfo.buffer[3]*2+1;
}
}
/* For write commands, the register address is followed by N pairs (N>0) of register value high byte
* and register value low byte to write in sequence. Response first byte is status (0=Pass, !0=Fail)
* followed by N pairs of register value high byte and register value low byte after modification.
*/
else if (dmaInfo.buffer[0] == 1)
{
glDebugRspBuffer[0] = SensorWrite (SENSOR_ADDR_WR, dmaInfo.buffer[1], dmaInfo.buffer[2],
(dmaInfo.count-3), (dmaInfo.buffer+3));
if (glDebugRspBuffer[0] != CY_U3P_SUCCESS)
break;
glDebugRspBuffer[0] = SensorRead (SENSOR_ADDR_RD, dmaInfo.buffer[1], dmaInfo.buffer[2],
(dmaInfo.count-3), (glDebugRspBuffer+1));
if (glDebugRspBuffer[0] != CY_U3P_SUCCESS)
break;
dmaInfo.count -= 2;
}
/* Default case, prepare buffer for loop back command in response */
else
{
/* For now, we just copy the command into the response buffer; and send it back to the
USB host. This can be expanded to include I2C transfers. */
CyU3PMemCopy (glDebugRspBuffer, dmaInfo.buffer, dmaInfo.count);
}
dmaInfo.buffer = glDebugRspBuffer;
dmaInfo.size = 1024;
dmaInfo.status = 0;
/* Free the command buffer to receive the next command. */
apiRetStatus = CyU3PDmaChannelDiscardBuffer (&glDebugCmdChannel);
if (apiRetStatus != CY_U3P_SUCCESS)
{
CyU3PDebugPrint (4, "Failed to free up command OUT EP buffer, Error code = %d\r\n", apiRetStatus);
CyFxAppErrorHandler (apiRetStatus);
}
/* Wait until the response has gone out. */
CyU3PDmaChannelWaitForCompletion (&glDebugRspChannel, CYU3P_WAIT_FOREVER);
apiRetStatus = CyU3PDmaChannelSetupSendBuffer (&glDebugRspChannel, &dmaInfo);
if (apiRetStatus != CY_U3P_SUCCESS)
{
CyU3PDebugPrint (4, "Failed to send debug response, Error code = %d\r\n", apiRetStatus);
CyFxAppErrorHandler (apiRetStatus);
}
}
#endif
}
/* Allow other ready threads to run. */
CyU3PThreadRelinquish ();
}
}
/*
* This function is called by the FX3 framework once the ThreadX RTOS has started up.
* The application specific threads and other OS resources are created and initialized here.
*/
void
CyFxApplicationDefine (
void)
{
void *ptr1, *ptr2;
uint32_t retThrdCreate;
/* Allocate the memory for the thread stacks. */
ptr1 = CyU3PMemAlloc (UVC_APP_THREAD_STACK);
ptr2 = CyU3PMemAlloc (UVC_APP_THREAD_STACK);
if ((ptr1 == 0) || (ptr2 == 0))
goto fatalErrorHandler;
/* Create the UVC application thread. */
retThrdCreate = CyU3PThreadCreate (&uvcAppThread, /* UVC Thread structure */
"30:UVC App Thread", /* Thread Id and name */
UVCAppThread_Entry, /* UVC Application Thread Entry function */
0, /* No input parameter to thread */
ptr1, /* Pointer to the allocated thread stack */
UVC_APP_THREAD_STACK, /* UVC Application Thread stack size */
UVC_APP_THREAD_PRIORITY, /* UVC Application Thread priority */
UVC_APP_THREAD_PRIORITY, /* Threshold value for thread pre-emption. */
CYU3P_NO_TIME_SLICE, /* No time slice for the application thread */
CYU3P_AUTO_START /* Start the Thread immediately */
);
if (retThrdCreate != 0)
{
goto fatalErrorHandler;
}
/* Create the control request handling thread. */
retThrdCreate = CyU3PThreadCreate (&uvcAppEP0Thread, /* UVC Thread structure */
"31:UVC App EP0 Thread", /* Thread Id and name */
UVCAppEP0Thread_Entry, /* UVC Application EP0 Thread Entry function */
0, /* No input parameter to thread */
ptr2, /* Pointer to the allocated thread stack */
UVC_APP_EP0_THREAD_STACK, /* UVC Application Thread stack size */
UVC_APP_EP0_THREAD_PRIORITY, /* UVC Application Thread priority */
UVC_APP_EP0_THREAD_PRIORITY, /* Threshold value for thread pre-emption. */
CYU3P_NO_TIME_SLICE, /* No time slice for the application thread */
CYU3P_AUTO_START /* Start the Thread immediately */
);
if (retThrdCreate != 0)
{
goto fatalErrorHandler;
}
return;
fatalErrorHandler:
/* Add custom recovery or debug actions here */
/* Loop indefinitely */
while (1);
}
/* Main entry point for the C code. We perform device initialization and start
* the ThreadX RTOS here.
*/
int
main (
void)
{
CyU3PReturnStatus_t apiRetStatus;
CyU3PIoMatrixConfig_t io_cfg;
CyU3PSysClockConfig_t clk_cfg;
/* Initialize the device */
clk_cfg.setSysClk400 = CyTrue;
clk_cfg.cpuClkDiv = 2;
clk_cfg.dmaClkDiv = 2;
clk_cfg.mmioClkDiv = 2;
clk_cfg.useStandbyClk = CyFalse;
clk_cfg.clkSrc = CY_U3P_SYS_CLK;
apiRetStatus = CyU3PDeviceInit (&clk_cfg);
if (apiRetStatus != CY_U3P_SUCCESS)
{
goto handle_fatal_error;
}
/* Turn on instruction cache to improve firmware performance. Use Release build to improve it further */
apiRetStatus = CyU3PDeviceCacheControl (CyTrue, CyFalse, CyFalse);
/* Configure the IO matrix for the device. */
io_cfg.isDQ32Bit = CyTrue;
io_cfg.s0Mode = CyFalse;
io_cfg.s1Mode = CyFalse;
io_cfg.lppMode = CY_U3P_IO_MATRIX_LPP_DEFAULT;
io_cfg.gpioSimpleEn[0] = 0;
io_cfg.gpioSimpleEn[1] = 0;
io_cfg.gpioComplexEn[0] = 0;
io_cfg.gpioComplexEn[1] = 0;
io_cfg.useUart = CyTrue; /* Uart is enabled for logging. */
io_cfg.useI2C = CyTrue; /* I2C is used for the sensor interface. */
io_cfg.useI2S = CyFalse;
io_cfg.useSpi = CyFalse;
apiRetStatus = CyU3PDeviceConfigureIOMatrix (&io_cfg);
if (apiRetStatus != CY_U3P_SUCCESS)
{
goto handle_fatal_error;
}
/* This is a non returnable call for initializing the RTOS kernel */
CyU3PKernelEntry ();
/* Dummy return to make the compiler happy */
return 0;
handle_fatal_error:
/* Cannot recover from this error. */
while (1);
}