
#include <stdlib.h>
#include "cmsis_os2.h" // CMSIS RTOS header file

#include "kmdw_usbh.h"
#include "kmdw_uvc.h"

//#define UVC_USER_ERR

static osThreadId_t usbh_example_uvc_thread;
static USBH_PIPE_HANDLE isoch_pipe;

extern void kmdw_printf(const char *f, ...);
extern uint32_t kmdw_ddr_reserve(uint32_t numbyte);

/*----------------------------------------------------------------------------
*      Thread for usb host examples
*---------------------------------------------------------------------------*/

void usbh_mdw_uvc_thread(void *argument); // thread function

void uvc_example_init(void)
{
    usbh_example_uvc_thread = osThreadNew(usbh_mdw_uvc_thread, NULL, NULL);
}

void usbh_mdw_uvc_thread(void *argument)
{
    usbStatus usb_status; // USB status

    // init USB host through MDK middleware
    usb_status = USBH_Initialize(0U);
    if (usb_status != usbOK)
    {
        kmdw_printf("uvc_example: USBH_Initialize() failed\n");
    }

    kmdw_printf("uvc_example: USBH_Initialize() OK\n");
    kmdw_printf("uvc_example: waiting for usb device enumeration\n");

    // wait for device init done
    osThreadFlagsWait(0x01U, osFlagsWaitAny, osWaitForever);

    osDelay(100); // give the camera some time to go ??

    kmdw_printf("uvc_example: starting isoch transfer..\n");

    USBH_UVC_PipeStart_Isoch(isoch_pipe);
}

#define NUM_FRAME 4
#define FRMAE_SIZE (640 * 480 * 2) // VGA YUV420

/************************** Class Driver Functions ****************************/

uint8_t USBH_UVC_Configure(uint8_t device, const USB_DEVICE_DESCRIPTOR *ptr_dev_desc, const USB_CONFIGURATION_DESCRIPTOR *ptr_cfg_desc)
{
    kmdw_printf("uvc_example: %s()\n", __FUNCTION__);

    // interface 1, alternate 5
    isoch_pipe = USBH_UVC_PipeCreate_Isoch(device, 0x81, 0x1320, 1);

    // allocate some frame buffers
    uint32_t all_frame_buf = kmdw_ddr_reserve(NUM_FRAME * FRMAE_SIZE);

    kmdw_printf("uvc_example: allocated %d bytes start from address 0x%p\n", NUM_FRAME * FRMAE_SIZE, all_frame_buf);

    // queue all frames into UVC middleware
    for (int i = 0; i < NUM_FRAME; i++)
        USBH_UVC_Queue_Frame(isoch_pipe, (uint32_t *)(all_frame_buf + i * FRMAE_SIZE), FRMAE_SIZE);

    // set class interface 1 altr 0 for VideoStreaming interface
    USBH_DeviceRequest_SetInterface(device, 1, 0);

    {
        UVC_PROBE_COMMIT_CONTROL uvc_ctrl;
        uvc_ctrl.bmHint = 0x0001;
        uvc_ctrl.bFormatIndex = 2;
        uvc_ctrl.bFrameIndex = 7;
        uvc_ctrl.dwFrameInterval = 333333;
        uvc_ctrl.wKeyFrameRate = 0;
        uvc_ctrl.wPFrameRate = 0;
        uvc_ctrl.wCompQuality = 0;
        uvc_ctrl.wCompWindowSize = 0;
        uvc_ctrl.wDelay = 0;
        uvc_ctrl.dwMaxVideoFrameSize = 0;
        uvc_ctrl.dwMaxPayloadTransferSize = 0;

        // VideoStreaming request - SET_CUR - Probe Control
        USBH_UVC_VS_Control(device, SET_CUR, VS_PROBE_CONTROL, &uvc_ctrl);

        // VideoStreaming request - GET_CUR - Probe Control
        USBH_UVC_VS_Control(device, GET_CUR, VS_PROBE_CONTROL, &uvc_ctrl);

        // VideoStreaming request - SET_CUR - Commit Control
        USBH_UVC_VS_Control(device, SET_CUR, VS_COMMIT_CONTROL, &uvc_ctrl);
    }

    // set class interface 1 altr 5 to start video streaming
    USBH_DeviceRequest_SetInterface(device, 1, 5);

    return 0;
}

usbStatus USBH_UVC_Initialize(uint8_t instance)
{
    //  Add code for initializing device

    kmdw_printf("uvc_example: %s()\n", __FUNCTION__);

    osThreadFlagsSet(usbh_example_uvc_thread, 0x01U);

    return usbOK;
}

void USBH_UVC_Get_Frame(uint32_t *frame_ptr, uint32_t frame_size)
{
#ifdef UVC_USER_ERR
    if (frame_size != FRMAE_SIZE)
        kmdw_printf("uvc_example: frame_ptr 0x%p frame_size %u is wrong\n", frame_ptr, frame_size);
#endif

    kmdw_printf("UVC_USER: frame_ptr 0x%p frame_size %u\n", frame_ptr, frame_size);

    // and enqueue this frame to UVC middleware again
    USBH_UVC_Queue_Frame(isoch_pipe, frame_ptr, FRMAE_SIZE);
}
