diff --git a/common/common.mk b/common/common.mk index 4344bb9..8038713 100644 --- a/common/common.mk +++ b/common/common.mk @@ -1,114 +1,40 @@ -# -# Copyright (C) 2009-2012 Chris McClelland -# Copyright 2015 Joel Stanley -# Copyright 2017 Kyle Robbertze -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . -# +# configuration +BOARD ?= opsis +MODEL ?= small +SYNCDELAYLEN ?= 4 -# To build the firmware you will need: -# SDCC from http://sdcc.sourceforge.net -# Git from https://git-scm.com/ -# -# To build a firmware suitable for loading into RAM: -# make -# -# To build a firmware suitable for loading from EEPROM: -# make FLAGS="-DEEPROM" -# -# To load a firmware: -# make load -# You will need HDMI2USB-mode-switch from -# https://github.com/timvideos/HDMI2USB-mode-switch -# -# Common rules +# use conda enviorment if it exists MAKEFILE_PATH := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) export PATH := $(MAKEFILE_PATH)../conda/bin:$(PATH) -LIBS ?= $(FX2LIBDIR)/lib/fx2.lib -INCS += -I sdcc -I$(FX2LIBDIR)/include -I. -I$(COMMON_DIR)/boards - -# Settings specific for the TimVideo hdmi2usb firmware -BOARD ?= opsis -FLAGS +=-DDEBUG -DBOARD_$(BOARD) - -# Must only be hex numbers -FIRMWARE_VERSION := $(shell date +%Y%m%d) - -CC_OBJS := $(CC_SRCS:%.c=%.rel) -AS_OBJS := $(AS_SRCS:%.a51=%.rel) - -CC := sdcc -AS8051 := sdas8051 -AS := $(AS8051) - -CFLAGS += -DDATE=0x$(FIRMWARE_VERSION) -mmcs51 $(FLAGS) -CFLAGS += --std-c99 -DSDCC -Wa"-p" --xram-size 0x0200 - -# Use make V=1 for a verbose build. -ifndef V - Q_CC=@echo ' CC ' $@; - Q_AS=@echo ' AS ' $@; - Q_LINK=@echo ' LINK ' $@; - Q_RM=@echo ' CLEAN '; - Q_OBJCOPY=@echo ' OBJCOPY ' $@; - Q_GEN=@echo ' GEN ' $@; +CFLAGS = +CFLAGS += --std-sdcc99 +CFLAGS += -Wa"-p" +CFLAGS += --xram-size 0x0200 +CFLAGS += -DSYNCDELAYLEN=$(SYNCDELAYLEN) + +# should to override previously set flags +FLAGS ?= +CFLAGS += $(FLAGS) + +# set USB IDs depending on board +ifeq (${BOARD},atlys) + VID := 1D50 + PID := 60B7 + DID := 0002 +else + ifeq (${BOARD},opsis) + VID := 2A19 + PID := 5442 + DID := 0002 + else + $(error "Unknown board type '$(BOARD)'") + endif endif -.PHONY: all clean distclean check check-descriptors check_int2jt load - -all: $(TARGET).hex - -check_int2jt: $(TARGET).hex - @export REQUESTED=$(shell grep "INT2JT=" $(TARGET).map | sed -e's/INT2JT=//'); \ - export ACTUAL=$(shell grep "C:.*INT2JT" $(TARGET).map | sed -e's/C: *0*\([^ ]*\) _INT2JT.*/0x\1/' | tr A-Z a-z ); \ - if [ "$$REQUESTED" != "$$ACTUAL" ]; then \ - echo "INT2JT at $$ACTUAL but requested $$REQUESTED"; \ - exit 1; \ - fi - -check: check_int2jt - -clean: - $(Q_RM)$(RM) *.adb *.asm *.cdb *.iic *.lk *.lnk *.lst *.omf *.map \ - *.mem *.rel *.rst *.sym descriptors_strings.* a.out date.h \ - date.inc progOffsets.h version_data.h version_data.c ${TARGET}.hex - cd $(FX2LIBDIR) && make clean - -distclean: clean - $(RM) -r $(FX2LIBDIR) - -load: $(TARGET).hex - hdmi2usb-mode-switch --load-fx2-firmware $(TARGET).hex - -$(CC_SRCS) $(AS_SRCS): $(FX2LIBDIR)/lib/fx2.lib - -$(FX2LIBDIR)/lib/fx2.lib: $(FX2LIBDIR)/.git - cd $(dir $@) && make -j1 - -# We depend on a file inside the directory as git creates an -# empty dir for us. -# -# Note that although we have the variable FX2LIBDIR, the submodule -# magic will always check it out in fx2lib/ -$(FX2LIBDIR)/.git: ../.gitmodules - git submodule sync --recursive -- $$(dirname $@) - git submodule update --recursive --init $$(dirname $@) - touch $@ -r ../.gitmodules - -$(TARGET).hex: $(CC_OBJS) $(AS_OBJS) - $(Q_LINK)$(CC) $(CFLAGS) -o $@ $+ $(LIBS) +CFLAGS += -DVID=0x$(VID) -DPID=0x$(PID) -DDID=0x$(DID) -%.rel: %.a51 - $(Q_AS)$(AS) -logs $? +LIBFX2DIR ?= ../third_party/libfx2 +# variable required by libfx2 build system +LIBFX2 = $(LIBFX2DIR)/firmware/library +include $(LIBFX2)/fx2rules.mk diff --git a/common/usb_defs.h b/common/usb_defs.h new file mode 100644 index 0000000..37a7870 --- /dev/null +++ b/common/usb_defs.h @@ -0,0 +1,36 @@ +#ifndef USB_DEFS_H +#define USB_DEFS_H + +#include +#include + +// Macro to easily define typedefs for descriptor structures +#define USB_DESC_CONST_CODE_TYPEDEF(desc) \ + typedef __code const struct desc \ + desc ## _c; + +enum { + // Interface association descriptor, requires proper device descriptor + // see: USB Interface Association Descriptor Device Class Code and Use Model, 1.0 + // https://www.usb.org/sites/default/files/iadclasscode_r10.pdf + USB_DEV_CLASS_MISCELLANEOUS = 0xef, + USB_DEV_SUBCLASS_COMMON = 0x02, + USB_DEV_PROTOCOL_INTERFACE_ASSOCIATION_DESCRIPTOR = 0x01, + USB_DESC_IF_ASSOC = 0x0b, +}; + +struct usb_desc_if_assoc { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bFirstInterface; + uint8_t bInterfaceCount; + uint8_t bFunctionClass; + uint8_t bFunctionSubClass; + uint8_t bFunctionProtocol; + uint8_t iFunction; +}; + +USB_DESC_CONST_CODE_TYPEDEF(usb_desc_if_assoc) + + +#endif /* USB_DEFS_H */ diff --git a/common/uvc.c b/common/uvc.c new file mode 100644 index 0000000..4b7a064 --- /dev/null +++ b/common/uvc.c @@ -0,0 +1,414 @@ +#include "uvc.h" + +__xdata uint8_t valuesArray[26] = + { + 0x01, 0x00, /* bmHint : No fixed parameters */ + 0x01, /* Use 1st Video format index */ + 0x01, /* Use 1st Video frame index */ + 0x2A, 0x2C, 0x0A, 0x00, /* Desired frame interval in 100ns */ + + 0x00, 0x00, /* Key frame rate in key frame/video frame units */ + 0x00, 0x00, /* PFrame rate in PFrame / key frame units */ + 0x00, 0x00, /* Compression quality control */ + 0x00, 0x00, /* Window size for average bit rate */ + + 0x05, 0x00, /* Internal video streaming i/f latency in ms */ + + 0x00, 0x20, 0x1C, 0x00, /* Max video frame size in bytes*/ + 0x00, 0x04, 0x00, 0x00 /* No. of bytes device can rx in single payload (1024) */ +}; + +__xdata uint8_t fps[2][4] = {{0x2A, 0x2C, 0x0A, 0x00}, {0x54, 0x58, 0x14, 0x00}}; // 15 ,7 +__xdata uint8_t frameSize[2][4] = {{0x00, 0x00, 0x18, 0x00}, {0x00, 0x20, 0x1C, 0x00}}; // Dvi , HDMI + +bool uvc_handle_usb_setup(__xdata struct usb_req_setup *req) { + if (req->bmRequestType == (USB_RECIP_IFACE|USB_TYPE_CLASS|USB_DIR_OUT) && + req->bRequest == USB_REQ_CLEAR_FEATURE) + { + EP0BCH = 0; + EP0BCL = 26; + SYNCDELAY; + while (EP0CS & _BUSY) + ; + while (EP0BCL != 26) + ; + + valuesArray[2] = EP0BUF[2]; // formate + valuesArray[3] = EP0BUF[3]; // frame + + // fps + valuesArray[4] = fps[EP0BUF[2] - 1][0]; + valuesArray[5] = fps[EP0BUF[2] - 1][1]; + valuesArray[6] = fps[EP0BUF[2] - 1][2]; + valuesArray[7] = fps[EP0BUF[2] - 1][3]; + + valuesArray[18] = frameSize[EP0BUF[3] - 1][0]; + valuesArray[19] = frameSize[EP0BUF[3] - 1][1]; + valuesArray[20] = frameSize[EP0BUF[3] - 1][2]; + valuesArray[21] = frameSize[EP0BUF[3] - 1][3]; + + EP0BCH = 0; // ACK + EP0BCL = 0; // ACK + return true; + } + + if (req->bRequest == USB_UVC_GET_CUR || req->bRequest == USB_UVC_GET_MIN || + req->bRequest == USB_UVC_GET_MAX) + { + int i; + SUDPTRCTL = 0x01; + + for (i = 0; i < 26; i++) + EP0BUF[i] = valuesArray[i]; + + EP0BCH = 0x00; + SYNCDELAY; + EP0BCL = 26; + return true; + + // FIXME: What do these do???? + // case UVC_SET_CUR: + // case UVC_GET_RES: + // case UVC_GET_LEN: + // case UVC_GET_INFO: + + // case UVC_GET_DEF: + // FIXME: Missing this case causes the following errors + // uvcvideo: UVC non compliance - GET_DEF(PROBE) not supported. Enabling workaround. + // Unhandled Vendor Command: 87 + + } + + return false; +} + +void uvc_config(struct uvc_configuration *config) { + uint8_t if_num_streaming = config->if_num_ctrl + 1; + // interface association + ((__xdata struct usb_desc_interface *) &usb_uvc_if_assoc)->bInterfaceNumber = config->if_num_ctrl; + // interface numbers + ((__xdata struct usb_desc_if_assoc *) &usb_uvc_std_ctrl_iface)->bFirstInterface = config->if_num_ctrl; + ((__xdata struct usb_desc_interface *) &usb_uvc_std_streaming_iface_0)->bInterfaceNumber = if_num_streaming; + ((__xdata struct usb_desc_interface *) &usb_uvc_std_streaming_iface_1)->bInterfaceNumber = if_num_streaming; + // endpoint numbers + ((__xdata struct usb_desc_vs_if_in_header *) &usb_uvc_vs_if_in_header)->bEndpointAddress = config->ep_addr_streaming | USB_DIR_IN; + ((__xdata struct usb_desc_endpoint *) &usb_uvc_ep_in)->bEndpointAddress = config->ep_addr_streaming | USB_DIR_IN; +} + +/*** Descriptors **************************************************************/ + +/* Interface association descriptor */ +usb_desc_if_assoc_c usb_uvc_if_assoc = { + .bLength = sizeof(struct usb_desc_if_assoc), + .bDescriptorType = USB_DESC_IF_ASSOC, + .bFirstInterface = 0, // uvc_config + .bInterfaceCount = 2, + .bFunctionClass = USB_UVC_CC_VIDEO, + .bFunctionSubClass = USB_UVC_SC_VIDEO_INTERFACE_COLLECTION, + .bFunctionProtocol = 0, + .iFunction = 1, +}; + +/* Standard video control interface descriptor */ +usb_desc_interface_c usb_uvc_std_ctrl_iface = { + .bLength = sizeof(struct usb_desc_interface), + .bDescriptorType = USB_DESC_INTERFACE, + .bInterfaceNumber = 0, // uvc_config + .bAlternateSetting = 0, + .bNumEndpoints = 0, + .bInterfaceClass = USB_UVC_CC_VIDEO, + .bInterfaceSubClass = USB_UVC_SUBCLASS_CC_VIDEOCONTROL, + .bInterfaceProtocol = 0, + .iInterface = 0, +}; + +/*** UVC: Streaming interface 0 ***********************************************/ + +/* Input (camera) terminal descriptor */ +#define LENGTH_usb_uvc_camera (sizeof(struct usb_desc_uvc_camera_terminal) + 3) +usb_desc_uvc_camera_terminal_c usb_uvc_camera = { + .bLength = LENGTH_usb_uvc_camera, + .bDescriptorType = USB_UVC_CS_INTERFACE, + .bDescriptorSubType = USB_UVC_VC_INPUT_TERMINAL, + .bTerminalID = 1, + .wTerminalType = 0x0201, + .bAssocTerminal = 0, + .iTerminal = 0, + .wObjectiveFocalLengthMin = 0x0000, // no optical zoom supported + .wObjectiveFocalLengthMax = 0x0000, // no optical zoom supported + .wOcularFocalLength = 0x0000, // no optical zoom supported + .bControlSize = 3, + .bmControls = {0x00, 0x00, 0x00}, // no controls supported +}; + +/* Processing unit descriptor */ +#define LENGTH_usb_uvc_processing_unit (sizeof(struct usb_desc_uvc_processing_unit) + 3 + 1 + 1) +usb_desc_uvc_processing_unit_c usb_uvc_processing_unit = { + .bLength = LENGTH_usb_uvc_processing_unit, + .bDescriptorType = USB_UVC_CS_INTERFACE, + .bDescriptorSubType = USB_UVC_VC_PROCESSING_UNIT, + .bUnitID = 2, + .bSourceID = 1, + .wMaxMultiplier = 0, + .bControlSize = 3, + ._tail = { + 0x00, 0x00, 0x00, // bmControls[] + 0x00, // iProcessing + 0x00, // bmVideoStandards + }, +}; + +/* Extension unit descriptor */ +#define LENGTH_usb_uvc_extension_unit (sizeof(struct usb_desc_uvc_extension_unit) + 1 + 1 + 3 + 1) +usb_desc_uvc_extension_unit_c usb_uvc_extension_unit = { + .bLength = LENGTH_usb_uvc_extension_unit, + .bDescriptorType = USB_UVC_CS_INTERFACE, + .bDescriptorSubType = USB_UVC_VC_EXTENSION_UNIT, + .bUnitID = 3, + .guidExtensionCode = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + .bNumControls = 0, + .bNrInPins = 1, + ._tail = { + 2, // baSourceID[] + 3, // bControlSize + 0x00, 0x00, 0x00, // bmControls[] + 0, // iExtension + }, +}; + +/* Output terminal descriptor */ +#define LENGTH_usb_uvc_output_terminal (sizeof(struct usb_desc_uvc_output_terminal) + 0) +usb_desc_uvc_output_terminal_c usb_uvc_output_terminal = { + .bLength = LENGTH_usb_uvc_output_terminal, + .bDescriptorType = USB_UVC_CS_INTERFACE, + .bDescriptorSubType = USB_UVC_VC_OUTPUT_TERMINAL, + .bTerminalID = 4, + .wTerminalType = 0x0101, + .bAssocTerminal = 0, + .bSourceID = 3, + .iTerminal = 0, + // ._tail = {}, +}; + +/* Class specific VC interface header descriptor */ +#define LENGTH_usb_uvc_vc_if_header (sizeof(struct usb_desc_vc_if_header) + 1) +usb_desc_vc_if_header_c usb_uvc_vc_if_header = { + .bLength = LENGTH_usb_uvc_vc_if_header, + .bDescriptorType = USB_UVC_CS_INTERFACE, + .bDescriptorSubType = USB_UVC_VC_HEADER, + .bcdUVC = 0x0100, + .wTotalLength = + // total size of all unit and terminal descriptors, flexible array members counted manually + // macros as intializers must be constant + LENGTH_usb_uvc_vc_if_header + + LENGTH_usb_uvc_camera + + LENGTH_usb_uvc_processing_unit + + LENGTH_usb_uvc_extension_unit + + LENGTH_usb_uvc_output_terminal, + .dwClockFrequency = 48000000, + .bInCollection = 1, + .baInterfaceNr = {1}, +}; + +/* Standard video streaming interface descriptor (alternate setting 0) */ +usb_desc_interface_c usb_uvc_std_streaming_iface_0 = { + .bLength = sizeof(struct usb_desc_interface), + .bDescriptorType = USB_DESC_INTERFACE, + .bInterfaceNumber = 0, // uvc_config + .bAlternateSetting = 0, + .bNumEndpoints = 0, + .bInterfaceClass = USB_UVC_CC_VIDEO, + .bInterfaceSubClass = USB_UVC_SUBCLASS_CC_VIDEOSTREAMING, + .bInterfaceProtocol = 0, + .iInterface = 0, +}; + +/*** UVC: Streaming interface 1 ***********************************************/ + +/*** UVC: MJPEG ***/ + +/* Class specific VS format descriptor */ +#define LENGTH_usb_uvc_mjpeg_vs_format (sizeof(struct usb_desc_uvc_vs_format_mjpeg)) +usb_desc_uvc_vs_format_mjpeg_c usb_uvc_mjpeg_vs_format = { + .bLength = LENGTH_usb_uvc_mjpeg_vs_format, + .bDescriptorType = USB_UVC_CS_INTERFACE, + .bDescriptorSubtype = USB_UVC_VS_FORMAT_MJPEG, + .bFormatIndex = 1, + .bNumFrameDescriptors = 2, + .bmFlags = 1, // fixed sample size + .bDefaultFrameIndex = 1, + .bAspectRatioX = 0, + .bAspectRatioY = 0, + .bmInterlaceFlags = 0, + .bCopyProtect = 0, // duplication unrestricted +}; + +/* Class specific VS frame descriptor */ +#define LENGTH_usb_uvc_mjpeg_vs_frame_1 (sizeof(struct usb_desc_uvc_vs_frame) + sizeof(uint32_t) * 1) +usb_desc_uvc_vs_frame_c usb_uvc_mjpeg_vs_frame_1 = { + .bLength = LENGTH_usb_uvc_mjpeg_vs_frame_1, + .bDescriptorType = USB_UVC_CS_INTERFACE, + .bDescriptorSubtype = USB_UVC_VS_FRAME_MJPEG, + .bFrameIndex = 1, + .bmCapabilities = 0x02, + .wWidth = 1024, + .wHeight = 768, + .dwMinBitRate = 0x0e000000, + .dwMaxBitRate = 0x0e000000, + .dwMaxVideoFrameBufferSize = 2ul * 1024 * 768, + .dwDefaultFrameInterval = 666666, + .bFrameIntervalType = 1, + .frameIntervals = { { .dwFrameInterval = 666666, }, } +}; + +/* Class specific VS frame descriptor */ +#define LENGTH_usb_uvc_mjpeg_vs_frame_2 (sizeof(struct usb_desc_uvc_vs_frame) + sizeof(uint32_t) * 1) +usb_desc_uvc_vs_frame_c usb_uvc_mjpeg_vs_frame_2 = { + .bLength = LENGTH_usb_uvc_mjpeg_vs_frame_2, + .bDescriptorType = USB_UVC_CS_INTERFACE, + .bDescriptorSubtype = USB_UVC_VS_FRAME_MJPEG, + .bFrameIndex = 2, + .bmCapabilities = 0x02, + .wWidth = 1280, + .wHeight = 720, + .dwMinBitRate = 0x0e000000, + .dwMaxBitRate = 0x0e000000, + .dwMaxVideoFrameBufferSize = 2ul * 1280 * 720, + .dwDefaultFrameInterval = 666666, + .bFrameIntervalType = 1, + .frameIntervals = { { .dwFrameInterval = 666666, }, } +}; + +/* VS Color Matching Descriptor Descriptor */ +#define LENGTH_usb_uvc_mjpeg_color_matching (sizeof(struct usb_desc_uvc_color_matching)) +usb_desc_uvc_color_matching_c usb_uvc_mjpeg_color_matching = { + .bLength = LENGTH_usb_uvc_mjpeg_color_matching, + .bDescriptorType = USB_UVC_CS_INTERFACE, + .bDescriptorSubtype = USB_UVC_VS_COLORFORMAT, + .bColorPrimaries = 1, // BT.709, sRGB + .bTransferCharacteristics = 1, // BT.709 + .bMatrixCoefficients = 4, // SMPTE 170M, BT.601 +}; + +/*** UVC: YUY2 ***/ + +/* Class specific VS format descriptor */ +#define LENGTH_usb_uvc_yuy2_vs_format (sizeof(struct usb_desc_uvc_vs_format_uncompressed)) +usb_desc_uvc_vs_format_uncompressed_c usb_uvc_yuy2_vs_format = { + .bLength = LENGTH_usb_uvc_yuy2_vs_format, + .bDescriptorType = USB_UVC_CS_INTERFACE, + .bDescriptorSubtype = USB_UVC_VS_FORMAT_UNCOMPRESSED, + .bFormatIndex = 2, + .bNumFrameDescriptors = 2, + .guidFormat = { + 0x59, 0x55, 0x59, 0x32, 0x00, 0x00, 0x10, 0x00, + 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71, + }, + .bBitsPerPixel = 0x10, + .bDefaultFrameIndex = 1, + .bAspectRatioX = 0, // non-interlaced in progressive scan + .bAspectRatioY = 0, // non-interlaced in progressive scan + .bmInterlaceFlags = 0, // non-interlaced + .bCopyProtect = 0, // no restrictions +}; + +/* Frame descriptors 1 */ +#define LENGTH_usb_uvc_yuy2_vs_frame_1 (sizeof(struct usb_desc_uvc_vs_frame) + sizeof(uint32_t) * 1) +usb_desc_uvc_vs_frame_c usb_uvc_yuy2_vs_frame_1 = { + .bLength = LENGTH_usb_uvc_yuy2_vs_frame_1, + .bDescriptorType = USB_UVC_CS_INTERFACE, + .bDescriptorSubtype = USB_UVC_VS_FRAME_UNCOMPRESSED, + .bFrameIndex = 1, + .bmCapabilities = 0x02, + .wWidth = 1024, + .wHeight = 768, + .dwMinBitRate = 0x0e000000, + .dwMaxBitRate = 0x0e000000, + .dwMaxVideoFrameBufferSize = 2ul * 1024 * 768, + .dwDefaultFrameInterval = 1333332, + .bFrameIntervalType = 1, + .frameIntervals = { { .dwFrameInterval = 1333332, }, } +}; + +/* Frame descriptors 2 */ +#define LENGTH_usb_uvc_yuy2_vs_frame_2 (sizeof(struct usb_desc_uvc_vs_frame) + sizeof(uint32_t) * 1) +usb_desc_uvc_vs_frame_c usb_uvc_yuy2_vs_frame_2 = { + .bLength = LENGTH_usb_uvc_yuy2_vs_frame_2, + .bDescriptorType = USB_UVC_CS_INTERFACE, + .bDescriptorSubtype = USB_UVC_VS_FRAME_UNCOMPRESSED, + .bFrameIndex = 2, + .bmCapabilities = 0x02, + .wWidth = 1280, + .wHeight = 720, + .dwMinBitRate = 0x0e000000, + .dwMaxBitRate = 0x0e000000, + .dwMaxVideoFrameBufferSize = 2ul * 1280 * 720, + .dwDefaultFrameInterval = 1333332, + .bFrameIntervalType = 1, + .frameIntervals = { { .dwFrameInterval = 1333332, }, } +}; + +/* VS Color Matching Descriptor Descriptor */ +#define LENGTH_usb_uvc_yuy2_color_matching (sizeof(struct usb_desc_uvc_color_matching)) +usb_desc_uvc_color_matching_c usb_uvc_yuy2_color_matching = { + .bLength = LENGTH_usb_uvc_yuy2_color_matching, + .bDescriptorType = USB_UVC_CS_INTERFACE, + .bDescriptorSubtype = USB_UVC_VS_COLORFORMAT, + .bColorPrimaries = 1, // BT.709, sRGB + .bTransferCharacteristics = 1, // BT.709 + .bMatrixCoefficients = 4, // SMPTE 170M, BT.601 +}; + +/* Class-specific video streaming input header descriptor */ +#define LENGTH_usb_uvc_vs_if_in_header (sizeof(struct usb_desc_vs_if_in_header) + 2) +usb_desc_vs_if_in_header_c usb_uvc_vs_if_in_header = { + .bLength = LENGTH_usb_uvc_vs_if_in_header, + .bDescriptorType = USB_UVC_CS_INTERFACE, + .bDescriptorSubType = USB_UVC_VS_INPUT_HEADER, + .bNumFormats = 2, + .wTotalLength = + LENGTH_usb_uvc_vs_if_in_header + + LENGTH_usb_uvc_mjpeg_vs_format + + LENGTH_usb_uvc_mjpeg_vs_frame_1 + + LENGTH_usb_uvc_mjpeg_vs_frame_2 + + LENGTH_usb_uvc_mjpeg_color_matching + + LENGTH_usb_uvc_yuy2_vs_format + + LENGTH_usb_uvc_yuy2_vs_frame_1 + + LENGTH_usb_uvc_yuy2_vs_frame_2 + + LENGTH_usb_uvc_yuy2_color_matching, + .bEndpointAddress = 0, // uvc_config + .bmInfo = 0, + .bTerminalLink = 4, + .bStillCaptureMethod = 1, + .bTriggerSupport = 1, + .bTriggerUsage = 0, + .bControlSize = 1, + .bmaControls = {0x00, 0x00}, +}; + +/* Standard video streaming interface descriptor (alternate setting 1) */ +usb_desc_interface_c usb_uvc_std_streaming_iface_1 = { + .bLength = sizeof(struct usb_desc_interface), + .bDescriptorType = USB_DESC_INTERFACE, + .bInterfaceNumber = 0, // uvc_config + .bAlternateSetting = 1, + .bNumEndpoints = 1, + .bInterfaceClass = USB_UVC_CC_VIDEO, + .bInterfaceSubClass = USB_UVC_SUBCLASS_CC_VIDEOSTREAMING, + .bInterfaceProtocol = 0, + .iInterface = 0, +}; + +/*** UVC: endpoints ***********************************************************/ + +usb_desc_endpoint_c usb_uvc_ep_in = { + .bLength = sizeof(struct usb_desc_endpoint), + .bDescriptorType = USB_DESC_ENDPOINT, + .bEndpointAddress = 0, // uvc_config + .bmAttributes = USB_XFER_ISOCHRONOUS, + .wMaxPacketSize = 512, + .bInterval = 1, +}; diff --git a/common/uvc.h b/common/uvc.h new file mode 100644 index 0000000..11abf02 --- /dev/null +++ b/common/uvc.h @@ -0,0 +1,71 @@ +#ifndef UVC_H +#define UVC_H + +#include +#include + +#include "uvc_defs.h" + +// control interface and streaming interface +#define UVC_NUM_INTERFACES 2 + +// UVC descritors +extern usb_desc_if_assoc_c usb_uvc_if_assoc; +extern usb_desc_interface_c usb_uvc_std_ctrl_iface; +extern usb_desc_uvc_camera_terminal_c usb_uvc_camera; +extern usb_desc_uvc_processing_unit_c usb_uvc_processing_unit; +extern usb_desc_uvc_extension_unit_c usb_uvc_extension_unit; +extern usb_desc_uvc_output_terminal_c usb_uvc_output_terminal; +extern usb_desc_vc_if_header_c usb_uvc_vc_if_header; +extern usb_desc_interface_c usb_uvc_std_streaming_iface_0; +extern usb_desc_uvc_vs_format_mjpeg_c usb_uvc_mjpeg_vs_format; +extern usb_desc_uvc_vs_frame_c usb_uvc_mjpeg_vs_frame_1; +extern usb_desc_uvc_vs_frame_c usb_uvc_mjpeg_vs_frame_2; +extern usb_desc_uvc_color_matching_c usb_uvc_mjpeg_color_matching; +extern usb_desc_uvc_vs_format_uncompressed_c usb_uvc_yuy2_vs_format; +extern usb_desc_uvc_vs_frame_c usb_uvc_yuy2_vs_frame_1; +extern usb_desc_uvc_vs_frame_c usb_uvc_yuy2_vs_frame_2; +extern usb_desc_uvc_color_matching_c usb_uvc_yuy2_color_matching; +extern usb_desc_vs_if_in_header_c usb_uvc_vs_if_in_header; +extern usb_desc_interface_c usb_uvc_std_streaming_iface_1; +extern usb_desc_endpoint_c usb_uvc_ep_in; + +// Marco for adding descriptors to usb_configuration_c +#define UVC_DESCRIPTORS_LIST \ + { .generic = (struct usb_desc_generic *) &usb_uvc_if_assoc }, \ + { .interface = &usb_uvc_std_ctrl_iface }, \ + { .generic = (struct usb_desc_generic *) &usb_uvc_vc_if_header }, \ + { .generic = (struct usb_desc_generic *) &usb_uvc_camera }, \ + { .generic = (struct usb_desc_generic *) &usb_uvc_processing_unit }, \ + { .generic = (struct usb_desc_generic *) &usb_uvc_extension_unit }, \ + { .generic = (struct usb_desc_generic *) &usb_uvc_output_terminal }, \ + { .interface = &usb_uvc_std_streaming_iface_0 }, \ + { .generic = (struct usb_desc_generic *) &usb_uvc_vs_if_in_header }, \ + { .generic = (struct usb_desc_generic *) &usb_uvc_mjpeg_vs_format }, \ + { .generic = (struct usb_desc_generic *) &usb_uvc_mjpeg_vs_frame_1 }, \ + { .generic = (struct usb_desc_generic *) &usb_uvc_mjpeg_vs_frame_2 }, \ + { .generic = (struct usb_desc_generic *) &usb_uvc_mjpeg_color_matching }, \ + { .generic = (struct usb_desc_generic *) &usb_uvc_yuy2_vs_format }, \ + { .generic = (struct usb_desc_generic *) &usb_uvc_yuy2_vs_frame_1 }, \ + { .generic = (struct usb_desc_generic *) &usb_uvc_yuy2_vs_frame_2 }, \ + { .generic = (struct usb_desc_generic *) &usb_uvc_yuy2_color_matching }, \ + { .interface = &usb_uvc_std_streaming_iface_1 }, \ + { .endpoint = &usb_uvc_ep_in }, + +struct uvc_configuration { + uint8_t if_num_ctrl; + // if_num_streaming = if_num_ctrl + 1 + uint8_t ep_addr_streaming; +}; + +/** + * Modifies descriptors to use requested configuration + */ +void uvc_config(struct uvc_configuration *config); + +/** + * Handle UVC-specific USB setup requests. Return true if request has been handled. + */ +bool uvc_handle_usb_setup(__xdata struct usb_req_setup *req); + +#endif /* UVC_H */ diff --git a/common/uvc_defs.h b/common/uvc_defs.h new file mode 100644 index 0000000..ac83cd9 --- /dev/null +++ b/common/uvc_defs.h @@ -0,0 +1,223 @@ +#ifndef UVC_DEFS_H +#define UVC_DEFS_H + +/* USB Video Class */ + +#include "usb_defs.h" + +enum { // TODO: reorganise logically + USB_UVC_CC_VIDEO = 0x0e, + USB_UVC_SC_VIDEO_INTERFACE_COLLECTION = 0x03, + USB_UVC_SUBCLASS_CC_VIDEOCONTROL = 0x01, + USB_UVC_SUBCLASS_CC_VIDEOSTREAMING = 0x02, + + USB_UVC_CS_INTERFACE = 0x24, + USB_UVC_VC_HEADER = 0x01, + USB_UVC_VC_INPUT_TERMINAL = 0x02, + USB_UVC_VC_OUTPUT_TERMINAL = 0x03, + USB_UVC_VC_PROCESSING_UNIT = 0x05, + USB_UVC_VC_EXTENSION_UNIT = 0x06, + USB_UVC_VS_INPUT_HEADER = 0x01, + USB_UVC_VS_FORMAT_MJPEG = 0x06, + USB_UVC_VS_FRAME_MJPEG = 0x07, + USB_UVC_VS_COLORFORMAT = 0x0d, + USB_UVC_VS_FORMAT_UNCOMPRESSED = 0x04, + USB_UVC_VS_FRAME_UNCOMPRESSED = 0x05, + + // commands + USB_UVC_SET_CUR = 0x01, + USB_UVC_GET_CUR = 0x81, + USB_UVC_GET_MIN = 0x82, + USB_UVC_GET_MAX = 0x83, + USB_UVC_GET_RES = 0x84, + USB_UVC_GET_LEN = 0x85, + USB_UVC_GET_INFO = 0x86, + USB_UVC_GET_DEF = 0x87, +}; + +struct usb_desc_vc_if_header { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubType; + uint16_t bcdUVC; + uint16_t wTotalLength; + uint32_t dwClockFrequency; + uint8_t bInCollection; + uint8_t baInterfaceNr[]; +}; + +USB_DESC_CONST_CODE_TYPEDEF(usb_desc_vc_if_header) + +struct usb_desc_uvc_input_terminal { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubType; + uint8_t bTerminalID; + uint16_t wTerminalType; + uint8_t bAssocTerminal; + uint8_t iTerminal; + uint8_t _tail[]; // additional fields depends on Terminal type +}; + +USB_DESC_CONST_CODE_TYPEDEF(usb_desc_uvc_input_terminal) + +struct usb_desc_uvc_output_terminal { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubType; + uint8_t bTerminalID; + uint16_t wTerminalType; + uint8_t bAssocTerminal; + uint8_t bSourceID; + uint8_t iTerminal; + uint8_t _tail[]; // additional fields depends on Terminal type +}; + +USB_DESC_CONST_CODE_TYPEDEF(usb_desc_uvc_output_terminal) + +struct usb_desc_uvc_camera_terminal { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubType; + uint8_t bTerminalID; + uint16_t wTerminalType; + uint8_t bAssocTerminal; + uint8_t iTerminal; + uint16_t wObjectiveFocalLengthMin; + uint16_t wObjectiveFocalLengthMax; + uint16_t wOcularFocalLength; + uint8_t bControlSize; + uint8_t bmControls[]; +}; + +USB_DESC_CONST_CODE_TYPEDEF(usb_desc_uvc_camera_terminal) + +struct usb_desc_uvc_processing_unit { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubType; + uint8_t bUnitID; + uint8_t bSourceID; + uint16_t wMaxMultiplier; + uint8_t bControlSize; + uint8_t _tail[]; // FIXME: better inteface? + // put those in '_tail' + /* uint8_t bmControls[]; */ + /* uint8_t iProcessing; */ + /* uint8_t bmVideoStandards; */ +}; + +USB_DESC_CONST_CODE_TYPEDEF(usb_desc_uvc_processing_unit) + +struct usb_desc_uvc_extension_unit { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubType; + uint8_t bUnitID; + uint8_t guidExtensionCode[16]; + uint8_t bNumControls; + uint8_t bNrInPins; + uint8_t _tail[]; + // put these in '_tail' + /* uint8_t baSourceID[]; */ + /* uint8_t bControlSize; */ + /* uint8_t bmControls[]; */ + /* uint8_t iExtension; */ +}; + +USB_DESC_CONST_CODE_TYPEDEF(usb_desc_uvc_extension_unit) + +struct usb_desc_vs_if_in_header { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubType; + uint8_t bNumFormats; + uint16_t wTotalLength; + uint8_t bEndpointAddress; + uint8_t bmInfo; + uint8_t bTerminalLink; + uint8_t bStillCaptureMethod; + uint8_t bTriggerSupport; + uint8_t bTriggerUsage; + uint8_t bControlSize; + uint8_t bmaControls[]; +}; + +USB_DESC_CONST_CODE_TYPEDEF(usb_desc_vs_if_in_header) + +struct usb_desc_uvc_vs_format_mjpeg { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubtype; + uint8_t bFormatIndex; + uint8_t bNumFrameDescriptors; + uint8_t bmFlags; + uint8_t bDefaultFrameIndex; + uint8_t bAspectRatioX; + uint8_t bAspectRatioY; + uint8_t bmInterlaceFlags; + uint8_t bCopyProtect; +}; + +USB_DESC_CONST_CODE_TYPEDEF(usb_desc_uvc_vs_format_mjpeg) + +struct usb_desc_uvc_vs_format_uncompressed { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubtype; + uint8_t bFormatIndex; + uint8_t bNumFrameDescriptors; + uint8_t guidFormat[16]; + uint8_t bBitsPerPixel; + uint8_t bDefaultFrameIndex; + uint8_t bAspectRatioX; + uint8_t bAspectRatioY; + uint8_t bmInterlaceFlags; + uint8_t bCopyProtect; +}; + +USB_DESC_CONST_CODE_TYPEDEF(usb_desc_uvc_vs_format_uncompressed) + +struct usb_desc_uvc_vs_frame { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubtype; + uint8_t bFrameIndex; + uint8_t bmCapabilities; + uint16_t wWidth; + uint16_t wHeight; + uint32_t dwMinBitRate; + uint32_t dwMaxBitRate; + uint32_t dwMaxVideoFrameBufferSize; + uint32_t dwDefaultFrameInterval; + uint8_t bFrameIntervalType; + /* + * frameIntervals must be either: + * dwMinFrameInterval, dwMaxFrameInterval, dwFrameIntervalStep, + * or: + * N times dwFrameInterval + */ + union { + // for continuous frame intervals + uint32_t dwMinFrameInterval; + uint32_t dwMaxFrameInterval; + uint32_t dwFrameIntervalStep; + // for discrete frame intervals + uint32_t dwFrameInterval; + } frameIntervals[]; +}; + +USB_DESC_CONST_CODE_TYPEDEF(usb_desc_uvc_vs_frame) + +struct usb_desc_uvc_color_matching { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubtype; + uint8_t bColorPrimaries; + uint8_t bTransferCharacteristics; + uint8_t bMatrixCoefficients; +}; + +USB_DESC_CONST_CODE_TYPEDEF(usb_desc_uvc_color_matching) + +#endif /* UVC_DEFS_H */