USB HOST UVC Camera FTDI Vinculum-II not reading video stream

I have extracted the minimal program down to a single file necessary to demonstrate the issue I am having outside the context of the larger program. At the highest level of description the USB Host (FTDI Vinculum-II) connects to a UVC camera, properly enumerates the device interfaces, end points, etc., sets up for transfer, but in the end when it finally does a vos_dev_read() (line 511), it returns with no error, and zero bytes read. The Host -> UVC camera ioctl r/w work fine and all values retrieved from the UVC camera (multiple cameras have been used in testing) are validated properly against the USB dumps from USB Device viewer. So, the bi-direction conversation is working great, just no video data. If I change values to invalid values or unplug the camera, I do get the expected error codes.

To be clear, this derivative program does nothing with the data read beyond it writes out to the UART terminal (9600 baud, no flow-control) the number of bytes read. Bottom line; find a fix for this program and the same will work for the real program.

I know it would be finding a Unicorn with someone who has experience with the Vinculum-II USB 2.0 chip, but I believe the issues is a USB/UVC issue and not a problem with the chip or what it is attempting to do. There is a slightly larger hive mind of UVC than the FTDI host chip. I have tried to find the magic incantation; I have tried yelling 4-letter words at the chip, and have even asked nicely, “Please” to no avail. The code was original derived from FTDI’s WebCam demo program which was very poorly written and contains a large number of un-described hardcoded numeric constants.

I have looked at every USB/UVC drivers (mostly Linux and Windows) on the entire internet. Calling all USB Host gurus for help please.

Thank you!

...
#include "vos.h"
#include "USBHost.h"
#include "USB.h"
#include "UART.h"
#include "ioctl.h"
#include "stdio.h"

#define VOS_QUANTUM                 50
#define VOS_TICK_INTERVAL           1

#define NUMBER_OF_DEVICES           3
#define VOS_DEV_USBHOST             0
#define VOS_DEV_UVC                 1
#define VOS_DEV_UART                2

#define ISO_TRANSFER_SIZE   192
#define ISO_TRANSFER_COUNT  1
#define WEBCAM_HEADER_SIZE  12
#define WEBCAM_PAYLOAD_SIZE (ISO_TRANSFER_SIZE - WEBCAM_HEADER_SIZE)
#define BUF_SIZE            (ISO_TRANSFER_SIZE * ISO_TRANSFER_COUNT)

enum uvc_ret_e
{
    UVC_OK,
    UVC_INVALID_PARAMETER,
    UVC_NOT_FOUND,
    UVC_ERROR
};  

// configure request structure for VOS_IOCTL_UVC_CLASS_REQUEST
#define set_class_request(d, iface, ent, cs, code, len) \
    do \
    { \
        (d).bRequest = (code); \
        (d).wValue = ((cs) << 8); \
        (d).wIndex = (((ent) << 8) | (iface)); \
        (d).wLength = (len); \
    } \
    while (0)

// UVC ioctl parameter structure
typedef struct _uvc_ioctl_cb_t
{
    unsigned char ioctl_code;
    unsigned char *get;
    unsigned char *set;
} uvc_ioctl_cb_t;

typedef struct _uvc_context_t
{
    // host controller handle
    VOS_HANDLE                 hc;

    unsigned short             vid;
    unsigned short             pid;

    // endpoint handles
    usbhost_ep_handle_ex       epInt;
    usbhost_ep_handle_ex       epCtrl;
    usbhost_ep_handle_ex       epIsoIn;

    // endpoint info
    usbhost_ioctl_cb_ep_info_t epInfo;

    unsigned char              state;
} uvc_context_t;

typedef enum _VS_InterfaceControlStatus_e
{
    VS_CONTROL_UNDEFINED = 0x00,       // 0x00
    VS_PROBE_CONTROL,                  // 0x01
    VS_COMMIT_CONTROL,                 // 0x02
    VS_STILL_PROBE_CONTROL,            // 0x03
    VS_STILL_COMMIT_CONTROL,           // 0x04
    VS_STILL_IMAGE_TRIGGER_CONTROL,    // 0x05
    VS_STREAM_ERROR_CODE_CONTROL,      // 0x06
    VS_GENERATE_KEY_FRAME_CONTROL,     // 0x07
    VS_UPDATE_FRAME_SEGMENT_CONTROL,   // 0x08
    VS_SYNCH_DELAY_CONTROL             // 0x09
} VS_InterfaceControlStatus_e;

typedef enum _VS_ControlRequest_e
{
    RC_UNDEFINED = 0x00,               // 0x00
    SET_CUR,                           // 0x01
    GET_CUR = 0x81,                    // 0x81
    GET_MIN,                           // 0x82
    GET_MAX,                           // 0x83
    GET_RES,                           // 0x84
    GET_LEN,                           // 0x85
    GET_INFO,                          // 0x86
    GET_DEF                            // 0x87
} VS_ControlRequest_e;

typedef struct _VideoProbeAndCommiteControl_t
{
    unsigned short bmHint;
    unsigned char  bFormatIndex;
    unsigned char  bFrameIndex;
    unsigned int   dwFrameInterval;
    unsigned short wKeyFrameRate;
    unsigned short wPFrameRate;
    unsigned short wCompQuality;
    unsigned short wCompWindowSize;
    unsigned short wDelay;
    unsigned int   dwMaxVideoFrameSize;
    unsigned int   dwMaxPayloadTransferSize;
    unsigned int   dwClockFrequency;
    unsigned char  bmFramingInfo;
    unsigned char  bPreferedVersion;
    unsigned char  bMinVersion;
    unsigned char  bMaxVersion;
} VideoProbeAndCommiteControl_t;

// IOCTLS
#define VOS_IOCTL_UVC_ATTACH        1
#define VOS_IOCTL_UVC_DETACH        2
#define VOS_IOCTL_UVC_CLASS_REQUEST 3

unsigned char uvc_init(unsigned char vos_dev_num);
unsigned char uvc_ioctl(uvc_ioctl_cb_t *cb, uvc_context_t *ctx);
void uvc_open(uvc_context_t *ctx);
void uvc_close(uvc_context_t *ctx);
unsigned char uvc_read(char *buffer, unsigned short num_to_read, unsigned short *num_read, uvc_context_t *ctx);
unsigned char uvc_write(char *buffer, unsigned short num_to_write, unsigned short *num_written, uvc_context_t *ctx);

void setupUART(void);
void firmware(void);
void open_drivers(void);
void attach_drivers(void);

vos_tcb_t *tcbFIRMWARE;
VOS_HANDLE hUsbHost;
VOS_HANDLE hUvc;
VOS_HANDLE hUART;      
unsigned char buffer1[BUF_SIZE];
VideoProbeAndCommiteControl_t VProbeAndCom;     

void firmware(void)
{
    unsigned short iRead;
    unsigned char iStatus;
    unsigned int iLoopCnt = 0;
    uvc_ioctl_cb_t uvc_iocb;
    usb_deviceRequest_t desc_dev;
    unsigned char USBState;
    hUsbHost = vos_dev_open(VOS_DEV_USBHOST);
    hUvc = vos_dev_open(VOS_DEV_UVC);
    hUART = vos_dev_open(VOS_DEV_UART);
    
    setupUART();
    stdioAttach(hUART);
    printf ("Start\n");
    
    do
    {
        uvc_iocb.ioctl_code = VOS_IOCTL_UVC_ATTACH;
        uvc_iocb.set = (void *) hUsbHost;
        if (vos_dev_ioctl(hUvc, &uvc_iocb) != UVC_OK)
        {
            asm {HALT};
        }

        // Get the CUR supported res
        set_class_request(desc_dev, 1, 0, VS_PROBE_CONTROL, GET_CUR, 26 /*sizeof (VProbeAndCom)*/);

        uvc_iocb.ioctl_code = VOS_IOCTL_UVC_CLASS_REQUEST;
        uvc_iocb.set = &desc_dev;
        uvc_iocb.get = &VProbeAndCom;
        iStatus = vos_dev_ioctl(hUvc, &uvc_iocb);
        if (iStatus != UVC_OK)
        {
            asm {HALT};
        }
    
        VProbeAndCom.dwMaxPayloadTransferSize = ISO_TRANSFER_SIZE;

        set_class_request(desc_dev, 1, 0, VS_COMMIT_CONTROL, SET_CUR, 26 /*sizeof (VProbeAndCom)*/);

        uvc_iocb.ioctl_code = VOS_IOCTL_UVC_CLASS_REQUEST;
        uvc_iocb.set = &desc_dev;
        uvc_iocb.get = &VProbeAndCom;
        iStatus = vos_dev_ioctl(hUvc, &uvc_iocb);
        if (iStatus != UVC_OK)
        {
            asm {HALT};
        }
        
        break;
    }
    while (1);
    
    vos_delay_msecs(300);

    do
    {
        iStatus = iRead = 0;
        iStatus = vos_dev_read(hUvc, buffer1, ISO_TRANSFER_SIZE * ISO_TRANSFER_COUNT, &iRead);  // read from the ISO endpoint
        printf("%d: status = %d, iRead = %d\n", iLoopCnt++, iStatus, iRead);
    }
    while (1);
}
    
void main(void)
{
    uart_context_t uartContext;
    usbhost_context_t usbhostContext;
    
    vos_set_clock_frequency(VOS_48MHZ_CLOCK_FREQUENCY);
    vos_init(VOS_QUANTUM, VOS_TICK_INTERVAL, NUMBER_OF_DEVICES);

    // configure USB Host port 1 only
    // use a max of 4 USB devices
    usbhostContext.if_count = 16;
    usbhostContext.ep_count = 10;
    usbhostContext.xfer_count = 2;
    usbhostContext.iso_xfer_count = 36;
    
    uartContext.buffer_size = VOS_BUFFER_SIZE_128_BYTES;

    usbhost_init(VOS_DEV_USBHOST, -1, &usbhostContext);
    uvc_init(VOS_DEV_UVC);
    uart_init(VOS_DEV_UART, &uartContext);

    tcbFIRMWARE = vos_create_thread_ex(20, 4096, firmware, "Application", 0);   
    vos_start_scheduler();

main_loop:
    goto main_loop;
}

static vos_driver_t uvc_cb;

unsigned char uvc_init(unsigned char vos_dev_num)
{
    uvc_context_t *ctx;

    // allocate  context
    ctx = vos_malloc(sizeof(uvc_context_t));
    if (ctx == 0)
    {
        return UVC_ERROR;
    }

    // Set up function pointers to the driver
    uvc_cb.flags = 0;
    uvc_cb.read = uvc_read;
    uvc_cb.write = uvc_write;
    uvc_cb.ioctl = uvc_ioctl;
    uvc_cb.interrupt = (PF_INT) 0;
    uvc_cb.open = uvc_open;
    uvc_cb.close = uvc_close;

    // OK - register with device manager
    vos_dev_init(vos_dev_num, &uvc_cb, ctx);

    return UVC_OK;
}

void uvc_open(uvc_context_t *ctx)
{
    vos_memset(ctx, 0, sizeof(uvc_context_t));
}

void uvc_close(uvc_context_t *ctx)
{
    vos_memset(ctx, 0, sizeof(uvc_context_t));
}

unsigned char uvc_detach(uvc_context_t *ctx)
{
    vos_memset(ctx, 0, sizeof(uvc_context_t));
    return UVC_OK;
}

unsigned char uvc_attach(uvc_context_t *ctx, void *params)
{
    usbhost_ioctl_cb_t hc_iocb;        // USBHost ioctl request block
    usbhost_ioctl_cb_class_t hc_class;
    usbhost_ioctl_cb_dev_info_t devInfo;
    usbhost_device_handle_ex ifDev;    // device handle
    int ifs, aset;
    unsigned char status;
    unsigned char ret;
    unsigned char num_dev;
    unsigned char i;

    status = ret = UVC_OK;

    ctx->hc = (VOS_HANDLE) params;

    do
    {
        vos_delay_msecs(1000);

        // poll for enumeration to complete
        hc_iocb.ioctl_code = VOS_IOCTL_USBHOST_GET_CONNECT_STATE;
        hc_iocb.get = &i;
        ret = vos_dev_ioctl(ctx->hc, &hc_iocb);
        if (ret != USBHOST_OK)
        {
            ret = UVC_ERROR;
            break;
        }

        if (i == PORT_STATE_ENUMERATED)
        {
            num_dev = 0;
            // user ioctl to find device count
            hc_iocb.ioctl_code = VOS_IOCTL_USBHOST_DEVICE_GET_COUNT;
            hc_iocb.set = NULL;
            hc_iocb.get = &num_dev;

            if (vos_dev_ioctl(ctx->hc, &hc_iocb) != USBHOST_OK)
            {
                status = UVC_ERROR;
                break;
            }

            ifDev = NULL;

            for (i = 0; i < num_dev; )
            {
                // user ioctl to find first device
                hc_iocb.ioctl_code = VOS_IOCTL_USBHOST_DEVICE_GET_NEXT_HANDLE;
                hc_iocb.handle.dif = ifDev;
                hc_iocb.get = &ifDev;

                if (vos_dev_ioctl(ctx->hc, &hc_iocb) != USBHOST_OK)
                {
                    ret = UVC_ERROR;
                    break;
                }

                if (ifDev)
                {
                    vos_memset(&hc_class, 0, sizeof(usbhost_ioctl_cb_class_t));
                    hc_iocb.ioctl_code = VOS_IOCTL_USBHOST_DEVICE_GET_CLASS_INFO;
                    hc_iocb.get = &hc_class;
                    vos_dev_ioctl(ctx->hc, &hc_iocb);

                    hc_iocb.ioctl_code = VOS_IOCTL_USBHOST_DEVICE_GET_DEV_INFO;
                    hc_iocb.handle.dif = ifDev;
                    hc_iocb.set = NULL;
                    hc_iocb.get = &devInfo;
                    ret = vos_dev_ioctl(ctx->hc, &hc_iocb);
                    if (ret != USBHOST_OK)
                    {
                        ret = UVC_ERROR;
                        break;
                    }

                    ifs = devInfo.interface_number;
                    aset = devInfo.alt;

                    if ((hc_class.dev_class == USB_CLASS_VIDEO) &&
                        (hc_class.dev_subclass == USB_SUBCLASS_VIDEO_VIDEOSTREAMING))
                    {
                        // find interface 1, alternate 1
                        // this is an interface with an isochromous endpoint
                        // it is 192 bytes max packet size
                        if ((ifs == 1 /*1*/) && (aset == 1 /*1*/ ))
                        {
                            // user ioctl to find iso out endpoint on this device
                            hc_iocb.ioctl_code = VOS_IOCTL_USBHOST_DEVICE_GET_ISO_IN_ENDPOINT_HANDLE;
                            hc_iocb.handle.dif = ifDev;
                            hc_iocb.get = &ctx->epIsoIn;

                            if (vos_dev_ioctl(ctx->hc, &hc_iocb) == USBHOST_OK)
                            {
                                break;
                            }
                        }
                    }
                }

                i++;
            }

            if (ret == UVC_ERROR)
            {
                status = UVC_ERROR;
                break;
            }

            if (ifDev == NULL)
            {
                status = UVC_ERROR;
                break;
            }

            // user ioctl to find interrupt endpoint on this device
            hc_iocb.ioctl_code = VOS_IOCTL_USBHOST_DEVICE_GET_CONTROL_ENDPOINT_HANDLE;
            hc_iocb.handle.dif = ifDev;
            hc_iocb.get = &ctx->epCtrl;
            if (vos_dev_ioctl(ctx->hc, &hc_iocb) != USBHOST_OK)
            {
                status = UVC_ERROR;
                break;
            }

            // set this interface to be enabled
            hc_iocb.ioctl_code = VOS_IOCTL_USBHOST_SET_INTERFACE;
            hc_iocb.handle.dif = ifDev;
            if (vos_dev_ioctl(ctx->hc, &hc_iocb) != USBHOST_OK)
            {
                status = UVC_ERROR;
                break;
            }

            // user ioctl to find interrupt endpoint on this device
            hc_iocb.ioctl_code = VOS_IOCTL_USBHOST_DEVICE_GET_ENDPOINT_INFO;
            hc_iocb.handle.ep = ctx->epIsoIn;
            hc_iocb.get = &ctx->epInfo;
            if (vos_dev_ioctl(ctx->hc, &hc_iocb) != USBHOST_OK)
            {
                status = UVC_ERROR;
                break;
            }

            break;
        }
    }
    while (1);

    return status;
}

unsigned char host_device_setup_transfer(uvc_context_t *ctx, usb_deviceRequest_t *req_desc, void *out_data)
{
    // USBHost ioctl request block
    usbhost_ioctl_cb_t iocb;

    iocb.ioctl_code = VOS_IOCTL_USBHOST_DEVICE_SETUP_TRANSFER;
    iocb.handle.ep = ctx->epCtrl;
    iocb.set = req_desc;
    iocb.get = out_data;

    if (vos_dev_ioctl(ctx->hc, &iocb) != USBHOST_OK)
        return UVC_ERROR;

    return UVC_OK;
}

unsigned char uvc_class_request(uvc_context_t *ctx, void *in_data, void *out_data)
{
    usb_deviceRequest_t *req = (usb_deviceRequest_t *) in_data;

    if (req->bRequest == SET_CUR)
    {
        req->bmRequestType = USB_BMREQUESTTYPE_HOST_TO_DEV |
            USB_BMREQUESTTYPE_CLASS |
            USB_BMREQUESTTYPE_INTERFACE;
    }
    else
    {
        req->bmRequestType = USB_BMREQUESTTYPE_DEV_TO_HOST |
            USB_BMREQUESTTYPE_CLASS |
            USB_BMREQUESTTYPE_INTERFACE;
    }

    return host_device_setup_transfer(ctx, req, out_data);
}

// UVC read function
unsigned char uvc_read(char *buffer,
                       unsigned short num_to_read,
                       unsigned short *num_read,
                       uvc_context_t *ctx)
{
    usbhost_ioctl_cb_t hc_iocb;
    usbhost_xfer_iso_t xfer;
    vos_semaphore_t semDum;
    unsigned char status;
    unsigned short frame;
    unsigned char i = 0;
    unsigned short xfer_size = ISO_TRANSFER_SIZE; // LAZ, Was ctx->epInfo.max_size; Snake cam returns too big of size for example

    vos_init_semaphore(&semDum, 0);

    // form isochronous transfer descriptor
    xfer.cond_code = USBHOST_CC_NOTACCESSED;
    xfer.flags = 0;
    xfer.s = &semDum;
    xfer.ep = ctx->epIsoIn;
    xfer.buf = buffer;
    xfer.len = num_to_read;

    xfer.count = (unsigned char) (num_to_read / xfer_size);

    while (num_to_read)
    {
        xfer.len_psw[i].size = (num_to_read > xfer_size)?xfer_size:num_to_read;
        num_to_read -= xfer_size;
        i++;
    }
    
    // user ioctl to find frame number (as close as possible to use)
    hc_iocb.ioctl_code = VOS_IOCTL_USBHOST_HW_GET_FRAME_NUMBER;
    hc_iocb.get = &frame;
    if (vos_dev_ioctl(ctx->hc, &hc_iocb) != USBHOST_OK)
    {
        return UVC_ERROR;
    }

    xfer.frame = frame + 1;
    
    num_read = 0; // LAZ, Added
    
    // now start the read from the ISO endpoint (line 511)
    status = vos_dev_read(ctx->hc, (unsigned char *) &xfer, sizeof(usbhost_xfer_t), num_read);

    if (status != USBHOST_OK)
    {
        return UVC_ERROR;
    }

    if (xfer.cond_code != USBHOST_CC_NOERROR)
    {
        return UVC_ERROR;
    }

    return UVC_OK;
}

// UVC write function
unsigned char uvc_write(char *buffer,
                        unsigned short num_to_write,
                        unsigned short *num_written,
                        uvc_context_t *ctx)
{
    unsigned char status = UVC_OK;

    return status;
}

// UVC IOCTL function
unsigned char uvc_ioctl(uvc_ioctl_cb_t *cb, uvc_context_t *ctx)
{
    unsigned char status = UVC_INVALID_PARAMETER;

    switch (cb->ioctl_code)
    {
    case VOS_IOCTL_UVC_ATTACH:
        status = uvc_attach(ctx, cb->set);
        break;

    case VOS_IOCTL_UVC_DETACH:
        status = uvc_detach(ctx);
        break;

    case VOS_IOCTL_UVC_CLASS_REQUEST:
        status = uvc_class_request(ctx, cb->set, cb->get);
        break;

    default:
        break;
    }

    return status;
}


void setupUART(void)
{
    common_ioctl_cb_t uart_iocb;
    
    // setup the UART interface
    uart_iocb.ioctl_code = VOS_IOCTL_COMMON_ENABLE_DMA;
    vos_dev_ioctl(hUART, &uart_iocb);

    // set baud rate
    uart_iocb.ioctl_code = VOS_IOCTL_UART_SET_BAUD_RATE;
    uart_iocb.set.uart_baud_rate = UART_BAUD_9600;
    vos_dev_ioctl(hUART, &uart_iocb);

    // set flow control
    uart_iocb.ioctl_code = VOS_IOCTL_UART_SET_FLOW_CONTROL;
    uart_iocb.set.param = UART_FLOW_NONE; 
    vos_dev_ioctl(hUART, &uart_iocb);

    // set data bits
    uart_iocb.ioctl_code = VOS_IOCTL_UART_SET_DATA_BITS;
    uart_iocb.set.param = UART_DATA_BITS_8;
    vos_dev_ioctl(hUART, &uart_iocb);

    // set stop bits
    uart_iocb.ioctl_code = VOS_IOCTL_UART_SET_STOP_BITS;
    uart_iocb.set.param = UART_STOP_BITS_1;
    vos_dev_ioctl(hUART, &uart_iocb);

    // set parity
    uart_iocb.ioctl_code = VOS_IOCTL_UART_SET_PARITY;
    uart_iocb.set.param = UART_PARITY_NONE;
    vos_dev_ioctl(hUART, &uart_iocb);
}   

...


Read more here: https://stackoverflow.com/questions/66999191/usb-host-uvc-camera-ftdi-vinculum-ii-not-reading-video-stream

Content Attribution

This content was originally published by lzerman at Recent Questions - Stack Overflow, and is syndicated here via their RSS feed. You can read the original post over there.

%d bloggers like this: