Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

libfx2-migration: audio example #51

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@
[submodule "third_party/fx2lib-linux-headers"]
path = third_party/fx2lib-linux-headers
url = https://github.com/mithro/fx2lib-linux-headers
[submodule "third_party/libfx2"]
path = third_party/libfx2
url = https://github.com/whitequark/libfx2.git
28 changes: 5 additions & 23 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,44 +8,26 @@ addons:

install:
- export BOARD="$B" && echo "BOARD='$BOARD'"
- export TARGETS="$T" && echo "TARGETS='$TARGETS'"
- export DIR="$D" && echo "DIR='$DIR'"
- make conda
- export PATH=$PWD/conda/bin:$PATH
- which sdcc
- sdcc --version
- ./.travis/setup.sh

script:
- make $TARGETS
- make -C $DIR

jobs:
fail_fast: true
include:
#-----------------------------------------
# Video targets
# audio
#-----------------------------------------
- stage: Build
env: B=opsis T="firmware-fx2"
env: B=opsis D="examples/audio"
- stage: Build
env: B=atlys T="firmware-fx2"

#-----------------------------------------
# Audio targets
#-----------------------------------------
- stage: Build
env: B=opsis T="firmware-audio-fx2"
- stage: Build
env: B=atlys T="firmware-audio-fx2"
- stage: Build
env: B=fx2miniboard T="firmware-audio-fx2"

#-----------------------------------------
# Unconfigured targets
#-----------------------------------------
- stage: Build
env: B=opsis T="firmware-unconfigured"
- stage: Build
env: B=atlys T="firmware-unconfigured"
env: B=atlys D="examples/audio"

#-----------------------------------------
# Build and deploy docs
Expand Down
141 changes: 35 additions & 106 deletions common/common.mk
Original file line number Diff line number Diff line change
@@ -1,114 +1,43 @@
#
# Copyright (C) 2009-2012 Chris McClelland
# Copyright 2015 Joel Stanley <[email protected]>
# Copyright 2017 Kyle Robbertze <[email protected]>
#
# 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 <http://www.gnu.org/licenses/>.
#
# 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
CFLAGS += -DVID=0x$(VID) -DPID=0x$(PID) -DDID=0x$(DID)

$(TARGET).hex: $(CC_OBJS) $(AS_OBJS)
$(Q_LINK)$(CC) $(CFLAGS) -o $@ $+ $(LIBS)
LIBFX2DIR ?= ../third_party/libfx2
# variable required by libfx2 build system
LIBFX2 = $(LIBFX2DIR)/firmware/library
include $(LIBFX2)/fx2rules.mk

%.rel: %.a51
$(Q_AS)$(AS) -logs $?
# force proper dependecy to automatically build libfx2
$(LIBFX2)/lib/$(MODEL)/fx2.lib: $(LIBFX2)/.stamp
175 changes: 175 additions & 0 deletions common/uac.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
#include "uac.h"

extern usb_descriptor_set_c usb_descriptor_set;

// keeps track of current alternate setting of streaming interface
__xdata uint8_t uac_as_alt_setting = 0;
// store the interface numbers for set/get interface handlers
__xdata static uint8_t if_num_ctrl;
__xdata static uint8_t if_num_streaming;

bool uac_handle_usb_set_interface(uint8_t interface, uint8_t alt_setting) {
if (interface == if_num_ctrl && alt_setting == 0) {
usb_reset_data_toggles(&usb_descriptor_set, interface, alt_setting);
return true;
}
if (interface == if_num_streaming && (alt_setting == 0 || alt_setting == 1)) {
// I belive we do not need to reset our endpoint configuration regsiters, as host should know
// that in alt_setting 0 there are no endpoints associated with this interface, so host will
// not send any IN requests
uac_as_alt_setting = alt_setting;
usb_reset_data_toggles(&usb_descriptor_set, interface, alt_setting);
return true;
}
return false; // not handled
}

bool uac_handle_usb_get_interface(uint8_t interface) {
if (interface == if_num_ctrl) {
EP0BUF[0] = 0; // only 1 alternate setting
SETUP_EP0_BUF(1);
}
if (interface == if_num_ctrl) {
EP0BUF[0] = uac_as_alt_setting;
SETUP_EP0_BUF(1);
}
return false; // not handled
}

void uac_config(struct uac_configuration *config) {
if_num_ctrl = config->if_num_ctrl;
if_num_streaming = config->if_num_ctrl + 1;
// interface numbers
((__xdata struct usb_desc_interface *) &usb_uac_std_ac_interface)->bInterfaceNumber = if_num_ctrl;
((__xdata struct usb_desc_interface *) &usb_uac_std_streaming_interface_alt0)->bInterfaceNumber = if_num_streaming;
((__xdata struct usb_desc_interface *) &usb_uac_std_streaming_interface_alt1)->bInterfaceNumber = if_num_streaming;
((__xdata struct usb_desc_uac1_ac_header *) &usb_uac_ac_header)->baInterfaceNr[0] = if_num_streaming;
// endpoint numbers
((__xdata struct usb_desc_endpoint *) &usb_uac_audio_endpoint)->bEndpointAddress = config->ep_addr_streaming | USB_DIR_IN;
// strings
((__xdata struct usb_desc_uac_input_terminal *) &usb_uac_input_terminal)->iChannelNames = config->i_str_channel_left;
}

/*** Descriptors **************************************************************/

usb_desc_interface_c usb_uac_std_ac_interface = {
.bLength = sizeof(struct usb_desc_interface),
.bDescriptorType = USB_DESC_INTERFACE,
.bInterfaceNumber = 0, // uac_config
.bAlternateSetting = 0,
.bNumEndpoints = 0,
.bInterfaceClass = USB_CLASS_AUDIO,
.bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL,
.bInterfaceProtocol = 0,
.iInterface = 0,
};

#define LENGTH_uac_input_terminal (sizeof(struct usb_desc_uac_input_terminal))
usb_desc_uac_input_terminal_c usb_uac_input_terminal = {
.bLength = LENGTH_uac_input_terminal,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = UAC_INPUT_TERMINAL,
.bTerminalID = 1,
.wTerminalType = UAC_INPUT_TERMINAL_MICROPHONE,
.bAssocTerminal = 0,
.bNrChannels = 2, // stereo
.wChannelConfig = (UAC_CHANNEL_LEFT | UAC_CHANNEL_RIGHT),
.iChannelNames = 0, // uvc_config // first channel name, other channels must have consequetive indices
.iTerminal = 0,
};

#define LENGTH_uac_output_terminal (sizeof(struct usb_desc_uac1_output_terminal))
usb_desc_uac1_output_terminal_c usb_uac_output_terminal = {
.bLength = LENGTH_uac_output_terminal,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = UAC_OUTPUT_TERMINAL,
.bTerminalID = 2,
.wTerminalType = UAC_OUTPUT_TERMINAL_STREAMING,
.bAssocTerminal = 0,
.bSourceID = 1, // connected to input terminal
.iTerminal = 0,
};

#define LENGTH_uac_ac_header (sizeof(struct usb_desc_uac1_ac_header) + 1)
usb_desc_uac1_ac_header_c usb_uac_ac_header = {
.bLength = LENGTH_uac_ac_header,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = UAC_MS_HEADER,
.bcdADC = UAC_BCD_V10,
.wTotalLength = // this descriptor + all unit and terminal descriptors
LENGTH_uac_ac_header +
LENGTH_uac_input_terminal +
LENGTH_uac_output_terminal,
.bInCollection = 1, // one interface in collection
.baInterfaceNr = { 0 }, // uac_config // streaing interface
};

// Setting 0 of streaming interface, no endpoints which means this is a zero-bandwidth
// setting to allow host to temporarily disable audio in case of bandiwdth problems
usb_desc_interface_c usb_uac_std_streaming_interface_alt0 = {
.bLength = sizeof(struct usb_desc_interface),
.bDescriptorType = USB_DESC_INTERFACE,
.bInterfaceNumber = 0, // uac_config
.bAlternateSetting = 0,
.bNumEndpoints = 0,
.bInterfaceClass = USB_CLASS_AUDIO,
.bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING,
.bInterfaceProtocol = 0,
.iInterface = 0,
};

// Setting 1 of streaming interface, regular operation
usb_desc_interface_c usb_uac_std_streaming_interface_alt1 = {
.bLength = sizeof(struct usb_desc_interface),
.bDescriptorType = USB_DESC_INTERFACE,
.bInterfaceNumber = 0, // uac_config
.bAlternateSetting = 1,
.bNumEndpoints = 1,
.bInterfaceClass = USB_CLASS_AUDIO,
.bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING,
.bInterfaceProtocol = 0,
.iInterface = 0,
};

usb_desc_uac1_as_header_c usb_uac_as_header = {
.bLength = sizeof(struct usb_desc_uac1_as_header),
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = UAC_AS_GENERAL,
.bTerminalLink = 2, // connected to output terminal
.bDelay = 1,
.wFormatTag = UAC_FORMAT_TYPE_I_PCM,
};

usb_desc_uac_format_type_i_discrete_c usb_uac_format = {
.bLength = sizeof(struct usb_desc_uac_format_type_i_discrete) + 3,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = UAC_FORMAT_TYPE,
.bFormatType = UAC_FORMAT_TYPE_I,
.bNrChannels = 2,
.bSubframeSize = 2,
.bBitResolution = 16,
.bSamFreqType = 1,
.tSamFreq = {{0x40, 0x1F, 0x00}}, // 8000Hz, little endian
};

/*** UAC: endpoints ***********************************************************/

usb_desc_audio_endpoint_c usb_uac_audio_endpoint = {
.bLength = sizeof(struct usb_desc_audio_endpoint),
.bDescriptorType = USB_DESC_ENDPOINT,
.bEndpointAddress = 0, // uac_config
.bmAttributes = USB_XFER_ISOCHRONOUS,
.wMaxPacketSize = 512,
.bInterval = 4,
.bRefresh = 0,
.bSynchAddress = 0,
};

usb_desc_uac_iso_endpoint_c usb_uac_iso_endpoint = {
.bLength = sizeof(struct usb_desc_uac_iso_endpoint),
.bDescriptorType = USB_DT_CS_ENDPOINT,
.bDescriptorSubtype = UAC_AS_GENERAL,
.bmAttributes = 0,
.bLockDelayUnits = 0,
.wLockDelay = 0,
};
Loading