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.