我已将最小程序提取到一个必要的文件中,以证明我在更大程序的上下文之外遇到的问题。在最高级别的描述中,USB 主机 (FTDI Vinculum-II) 连接到 UVC 相机,正确枚举设备接口、端点等,设置传输,但最终当它最终执行 vos_dev_read() (第 511 行),它没有错误地返回,并且读取了零字节。Host -> UVC camera ioctl r/w 工作正常,所有从 UVC 摄像头检索到的值(测试中使用了多个摄像头)都已针对来自 USB 设备查看器的 USB 转储进行了正确验证。因此,双向对话效果很好,只是没有视频数据。如果我将值更改为无效值或拔下相机,我会得到预期的错误代码。
需要明确的是,这个衍生程序除了将读取的字节数写入 UART 终端(9600 波特,无流量控制)之外,对读取的数据不做任何事情。底线;为这个程序找到一个修复程序,同样适用于真正的程序。
我知道它会与有使用 Vinculum-II USB 2.0 芯片经验的人一起寻找独角兽,但我相信问题是 USB/UVC 问题,而不是芯片或它试图做什么的问题。与 FTDI 主机芯片相比,UVC 的蜂巢思维略大。我试图找到魔法咒语;我曾尝试在芯片上大喊 4 个字母的单词,甚至很好地问了一句“请”,但无济于事。该代码最初源自 FTDI 的 WebCam 演示程序,该程序编写得非常糟糕,并且包含大量未描述的硬编码数字常量。
我查看了整个互联网上的每个 USB/UVC 驱动程序(主要是 Linux 和 Windows)。请致电所有 USB 主机专家寻求帮助。
谢谢!
...
#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);
}
...