@@ -0,0 +1,618 @@ | |||
#---------------------------------------------------------------------------- | |||
# On command line: | |||
# | |||
# make all = Make software. | |||
# | |||
# make clean = Clean out built project files. | |||
# | |||
# make coff = Convert ELF to AVR COFF. | |||
# | |||
# make extcoff = Convert ELF to AVR Extended COFF. | |||
# | |||
# make program = Download the hex file to the device, using avrdude. | |||
# Please customize the avrdude settings below first! | |||
# | |||
# make debug = Start either simulavr or avarice as specified for debugging, | |||
# with avr-gdb or avr-insight as the front end for debugging. | |||
# | |||
# make filename.s = Just compile filename.c into the assembler code only. | |||
# | |||
# make filename.i = Create a preprocessed source file for use in submitting | |||
# bug reports to the GCC project. | |||
# | |||
# To rebuild project do "make clean" then "make all". | |||
#---------------------------------------------------------------------------- | |||
# Following variables need to be set in <target>/Makefile: | |||
# TARGET | |||
# COMMON_DIR | |||
# TARGET_DIR | |||
# TARGET_SRC | |||
# MCU | |||
# F_CPU | |||
# List C source files here. (C dependencies are automatically generated.) | |||
SRC = usbdrv.c \ | |||
usbdrvasm.S \ | |||
oddebug.c | |||
SRC += $(TARGET_SRC) | |||
# Option modules | |||
ifdef $(or MOUSEKEY_ENABLE, PS2_MOUSE_ENABLE) | |||
SRC += usb_mouse.c | |||
endif | |||
ifdef MOUSEKEY_ENABLE | |||
SRC += mousekey.c | |||
endif | |||
ifdef PS2_MOUSE_ENABLE | |||
SRC += ps2.c \ | |||
ps2_mouse.c | |||
endif | |||
ifdef USB_EXTRA_ENABLE | |||
SRC += usb_extra.c | |||
endif | |||
# C source file search path | |||
VPATH = $(TARGET_DIR):$(COMMON_DIR):$(COMMON_DIR)/usbdrv | |||
# Output format. (can be srec, ihex, binary) | |||
FORMAT = ihex | |||
# Object files directory | |||
# To put object files in current directory, use a dot (.), do NOT make | |||
# this an empty or blank macro! | |||
OBJDIR = . | |||
# Optimization level, can be [0, 1, 2, 3, s]. | |||
# 0 = turn off optimization. s = optimize for size. | |||
# (Note: 3 is not always the best optimization level. See avr-libc FAQ.) | |||
OPT = s | |||
# Debugging format. | |||
# Native formats for AVR-GCC's -g are dwarf-2 [default] or stabs. | |||
# AVR Studio 4.10 requires dwarf-2. | |||
# AVR [Extended] COFF format requires stabs, plus an avr-objcopy run. | |||
DEBUG = dwarf-2 | |||
# List any extra directories to look for include files here. | |||
# Each directory must be seperated by a space. | |||
# Use forward slashes for directory separators. | |||
# For a directory that has spaces, enclose it in quotes. | |||
#EXTRAINCDIRS = $(TARGET_DIR) $(COMMON_DIR) | |||
EXTRAINCDIRS = $(subst :, ,$(VPATH)) | |||
# Compiler flag to set the C Standard level. | |||
# c89 = "ANSI" C | |||
# gnu89 = c89 plus GCC extensions | |||
# c99 = ISO C99 standard (not yet fully implemented) | |||
# gnu99 = c99 plus GCC extensions | |||
CSTANDARD = -std=gnu99 | |||
#OPT_DEFS = | |||
ifdef MOUSEKEY_ENABLE | |||
OPT_DEFS += -DMOUSEKEY_ENABLE | |||
endif | |||
ifdef PS2_MOUSE_ENABLE | |||
OPT_DEFS += -DPS2_MOUSE_ENABLE | |||
endif | |||
ifdef USB_EXTRA_ENABLE | |||
OPT_DEFS += -DUSB_EXTRA_ENABLE | |||
endif | |||
ifdef USB_NKRO_ENABLE | |||
OPT_DEFS += -DUSB_NKRO_ENABLE | |||
endif | |||
ifdef $(or MOUSEKEY_ENABLE, PS2_MOUSE_ENABLE) | |||
OPT_DEFS += -DUSB_MOUSE_ENABLE | |||
endif | |||
# Place -D or -U options here for C sources | |||
CDEFS = -DF_CPU=$(F_CPU)UL | |||
CDEFS += $(OPT_DEFS) | |||
# Place -D or -U options here for ASM sources | |||
ADEFS = -DF_CPU=$(F_CPU) | |||
ADEFS += $(OPT_DEFS) | |||
# Place -D or -U options here for C++ sources | |||
CPPDEFS = -DF_CPU=$(F_CPU)UL | |||
#CPPDEFS += -D__STDC_LIMIT_MACROS | |||
#CPPDEFS += -D__STDC_CONSTANT_MACROS | |||
CPPDEFS += $(OPT_DEFS) | |||
#---------------- Compiler Options C ---------------- | |||
# -g*: generate debugging information | |||
# -O*: optimization level | |||
# -f...: tuning, see GCC manual and avr-libc documentation | |||
# -Wall...: warning level | |||
# -Wa,...: tell GCC to pass this to the assembler. | |||
# -adhlns...: create assembler listing | |||
CFLAGS = -g$(DEBUG) | |||
CFLAGS += $(CDEFS) | |||
CFLAGS += -O$(OPT) | |||
CFLAGS += -funsigned-char | |||
CFLAGS += -funsigned-bitfields | |||
CFLAGS += -ffunction-sections | |||
CFLAGS += -fpack-struct | |||
CFLAGS += -fshort-enums | |||
CFLAGS += -Wall | |||
CFLAGS += -Wstrict-prototypes | |||
#CFLAGS += -mshort-calls | |||
#CFLAGS += -fno-unit-at-a-time | |||
#CFLAGS += -Wundef | |||
#CFLAGS += -Wunreachable-code | |||
#CFLAGS += -Wsign-compare | |||
CFLAGS += -Wa,-adhlns=$(@:%.o=%.lst) | |||
CFLAGS += $(patsubst %,-I%,$(EXTRAINCDIRS)) | |||
CFLAGS += $(CSTANDARD) | |||
CFLAGS += -include config.h | |||
#---------------- Compiler Options C++ ---------------- | |||
# -g*: generate debugging information | |||
# -O*: optimization level | |||
# -f...: tuning, see GCC manual and avr-libc documentation | |||
# -Wall...: warning level | |||
# -Wa,...: tell GCC to pass this to the assembler. | |||
# -adhlns...: create assembler listing | |||
CPPFLAGS = -g$(DEBUG) | |||
CPPFLAGS += $(CPPDEFS) | |||
CPPFLAGS += -O$(OPT) | |||
CPPFLAGS += -funsigned-char | |||
CPPFLAGS += -funsigned-bitfields | |||
CPPFLAGS += -fpack-struct | |||
CPPFLAGS += -fshort-enums | |||
CPPFLAGS += -fno-exceptions | |||
CPPFLAGS += -Wall | |||
CPPFLAGS += -Wundef | |||
#CPPFLAGS += -mshort-calls | |||
#CPPFLAGS += -fno-unit-at-a-time | |||
#CPPFLAGS += -Wstrict-prototypes | |||
#CPPFLAGS += -Wunreachable-code | |||
#CPPFLAGS += -Wsign-compare | |||
CPPFLAGS += -Wa,-adhlns=$(@:%.o=%.lst) | |||
CPPFLAGS += $(patsubst %,-I%,$(EXTRAINCDIRS)) | |||
#CPPFLAGS += $(CSTANDARD) | |||
CPPFLAGS += -include config.h | |||
#---------------- Assembler Options ---------------- | |||
# -Wa,...: tell GCC to pass this to the assembler. | |||
# -adhlns: create listing | |||
# -gstabs: have the assembler create line number information; note that | |||
# for use in COFF files, additional information about filenames | |||
# and function names needs to be present in the assembler source | |||
# files -- see avr-libc docs [FIXME: not yet described there] | |||
# -listing-cont-lines: Sets the maximum number of continuation lines of hex | |||
# dump that will be displayed for a given single line of source input. | |||
ASFLAGS = $(ADEFS) -Wa,-adhlns=$(@:%.o=%.lst),-gstabs,--listing-cont-lines=100 | |||
ASFLAGS += -include config.h | |||
#---------------- Library Options ---------------- | |||
# Minimalistic printf version | |||
PRINTF_LIB_MIN = -Wl,-u,vfprintf -lprintf_min | |||
# Floating point printf version (requires MATH_LIB = -lm below) | |||
PRINTF_LIB_FLOAT = -Wl,-u,vfprintf -lprintf_flt | |||
# If this is left blank, then it will use the Standard printf version. | |||
PRINTF_LIB = | |||
#PRINTF_LIB = $(PRINTF_LIB_MIN) | |||
#PRINTF_LIB = $(PRINTF_LIB_FLOAT) | |||
# Minimalistic scanf version | |||
SCANF_LIB_MIN = -Wl,-u,vfscanf -lscanf_min | |||
# Floating point + %[ scanf version (requires MATH_LIB = -lm below) | |||
SCANF_LIB_FLOAT = -Wl,-u,vfscanf -lscanf_flt | |||
# If this is left blank, then it will use the Standard scanf version. | |||
SCANF_LIB = | |||
#SCANF_LIB = $(SCANF_LIB_MIN) | |||
#SCANF_LIB = $(SCANF_LIB_FLOAT) | |||
MATH_LIB = -lm | |||
# List any extra directories to look for libraries here. | |||
# Each directory must be seperated by a space. | |||
# Use forward slashes for directory separators. | |||
# For a directory that has spaces, enclose it in quotes. | |||
EXTRALIBDIRS = | |||
#---------------- External Memory Options ---------------- | |||
# 64 KB of external RAM, starting after internal RAM (ATmega128!), | |||
# used for variables (.data/.bss) and heap (malloc()). | |||
#EXTMEMOPTS = -Wl,-Tdata=0x801100,--defsym=__heap_end=0x80ffff | |||
# 64 KB of external RAM, starting after internal RAM (ATmega128!), | |||
# only used for heap (malloc()). | |||
#EXTMEMOPTS = -Wl,--section-start,.data=0x801100,--defsym=__heap_end=0x80ffff | |||
EXTMEMOPTS = | |||
#---------------- Linker Options ---------------- | |||
# -Wl,...: tell GCC to pass this to linker. | |||
# -Map: create map file | |||
# --cref: add cross reference to map file | |||
# | |||
# Comennt out "--relax" option to avoid a error such: | |||
# (.vectors+0x30): relocation truncated to fit: R_AVR_13_PCREL against symbol `__vector_12' | |||
# | |||
LDFLAGS = -Wl,-Map=$(TARGET).map,--cref | |||
LDFLAGS += -Wl,--relax | |||
LDFLAGS += -Wl,--gc-sections | |||
LDFLAGS += $(EXTMEMOPTS) | |||
LDFLAGS += $(patsubst %,-L%,$(EXTRALIBDIRS)) | |||
LDFLAGS += $(PRINTF_LIB) $(SCANF_LIB) $(MATH_LIB) | |||
#LDFLAGS += -T linker_script.x | |||
#---------------- Programming Options (avrdude) ---------------- | |||
# Programming hardware | |||
# Type: avrdude -c ? | |||
# to get a full listing. | |||
# | |||
AVRDUDE_PROGRAMMER = stk500v2 | |||
# com1 = serial port. Use lpt1 to connect to parallel port. | |||
AVRDUDE_PORT = com1 # programmer connected to serial device | |||
AVRDUDE_WRITE_FLASH = -U flash:w:$(TARGET).hex | |||
#AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep | |||
# Uncomment the following if you want avrdude's erase cycle counter. | |||
# Note that this counter needs to be initialized first using -Yn, | |||
# see avrdude manual. | |||
#AVRDUDE_ERASE_COUNTER = -y | |||
# Uncomment the following if you do /not/ wish a verification to be | |||
# performed after programming the device. | |||
#AVRDUDE_NO_VERIFY = -V | |||
# Increase verbosity level. Please use this when submitting bug | |||
# reports about avrdude. See <http://savannah.nongnu.org/projects/avrdude> | |||
# to submit bug reports. | |||
#AVRDUDE_VERBOSE = -v -v | |||
AVRDUDE_FLAGS = -p $(MCU) -P $(AVRDUDE_PORT) -c $(AVRDUDE_PROGRAMMER) | |||
AVRDUDE_FLAGS += $(AVRDUDE_NO_VERIFY) | |||
AVRDUDE_FLAGS += $(AVRDUDE_VERBOSE) | |||
AVRDUDE_FLAGS += $(AVRDUDE_ERASE_COUNTER) | |||
#---------------- Debugging Options ---------------- | |||
# For simulavr only - target MCU frequency. | |||
DEBUG_MFREQ = $(F_CPU) | |||
# Set the DEBUG_UI to either gdb or insight. | |||
# DEBUG_UI = gdb | |||
DEBUG_UI = insight | |||
# Set the debugging back-end to either avarice, simulavr. | |||
DEBUG_BACKEND = avarice | |||
#DEBUG_BACKEND = simulavr | |||
# GDB Init Filename. | |||
GDBINIT_FILE = __avr_gdbinit | |||
# When using avarice settings for the JTAG | |||
JTAG_DEV = /dev/com1 | |||
# Debugging port used to communicate between GDB / avarice / simulavr. | |||
DEBUG_PORT = 4242 | |||
# Debugging host used to communicate between GDB / avarice / simulavr, normally | |||
# just set to localhost unless doing some sort of crazy debugging when | |||
# avarice is running on a different computer. | |||
DEBUG_HOST = localhost | |||
#============================================================================ | |||
# Define programs and commands. | |||
SHELL = sh | |||
CC = avr-gcc | |||
OBJCOPY = avr-objcopy | |||
OBJDUMP = avr-objdump | |||
SIZE = avr-size | |||
AR = avr-ar rcs | |||
NM = avr-nm | |||
AVRDUDE = avrdude | |||
REMOVE = rm -f | |||
REMOVEDIR = rm -rf | |||
COPY = cp | |||
WINSHELL = cmd | |||
# Define Messages | |||
# English | |||
MSG_ERRORS_NONE = Errors: none | |||
MSG_BEGIN = -------- begin -------- | |||
MSG_END = -------- end -------- | |||
MSG_SIZE_BEFORE = Size before: | |||
MSG_SIZE_AFTER = Size after: | |||
MSG_COFF = Converting to AVR COFF: | |||
MSG_EXTENDED_COFF = Converting to AVR Extended COFF: | |||
MSG_FLASH = Creating load file for Flash: | |||
MSG_EEPROM = Creating load file for EEPROM: | |||
MSG_EXTENDED_LISTING = Creating Extended Listing: | |||
MSG_SYMBOL_TABLE = Creating Symbol Table: | |||
MSG_LINKING = Linking: | |||
MSG_COMPILING = Compiling C: | |||
MSG_COMPILING_CPP = Compiling C++: | |||
MSG_ASSEMBLING = Assembling: | |||
MSG_CLEANING = Cleaning project: | |||
MSG_CREATING_LIBRARY = Creating library: | |||
# Define all object files. | |||
OBJ = $(patsubst %.c,$(OBJDIR)/%.o,$(patsubst %.cpp,$(OBJDIR)/%.o,$(patsubst %.S,$(OBJDIR)/%.o,$(SRC)))) | |||
# Define all listing files. | |||
LST = $(patsubst %.c,$(OBJDIR)/%.lst,$(patsubst %.cpp,$(OBJDIR)/%.lst,$(patsubst %.S,$(OBJDIR)/%.lst,$(SRC)))) | |||
# Compiler flags to generate dependency files. | |||
GENDEPFLAGS = -MMD -MP -MF .dep/$(@F).d | |||
# Combine all necessary flags and optional flags. | |||
# Add target processor to flags. | |||
ALL_CFLAGS = -mmcu=$(MCU) -I. $(CFLAGS) $(GENDEPFLAGS) | |||
ALL_CPPFLAGS = -mmcu=$(MCU) -I. -x c++ $(CPPFLAGS) $(GENDEPFLAGS) | |||
ALL_ASFLAGS = -mmcu=$(MCU) -I. -x assembler-with-cpp $(ASFLAGS) | |||
# Default target. | |||
all: begin gccversion sizebefore build sizeafter end | |||
# Change the build target to build a HEX file or a library. | |||
build: elf hex eep lss sym | |||
#build: lib | |||
elf: $(TARGET).elf | |||
hex: $(TARGET).hex | |||
eep: $(TARGET).eep | |||
lss: $(TARGET).lss | |||
sym: $(TARGET).sym | |||
LIBNAME=lib$(TARGET).a | |||
lib: $(LIBNAME) | |||
# Eye candy. | |||
# AVR Studio 3.x does not check make's exit code but relies on | |||
# the following magic strings to be generated by the compile job. | |||
begin: | |||
@echo | |||
@echo $(MSG_BEGIN) | |||
end: | |||
@echo $(MSG_END) | |||
@echo | |||
# Display size of file. | |||
HEXSIZE = $(SIZE) --target=$(FORMAT) $(TARGET).hex | |||
#ELFSIZE = $(SIZE) --mcu=$(MCU) --format=avr $(TARGET).elf | |||
ELFSIZE = $(SIZE) $(TARGET).elf | |||
sizebefore: | |||
@if test -f $(TARGET).elf; then echo; echo $(MSG_SIZE_BEFORE); $(ELFSIZE); \ | |||
2>/dev/null; echo; fi | |||
sizeafter: | |||
@if test -f $(TARGET).elf; then echo; echo $(MSG_SIZE_AFTER); $(ELFSIZE); \ | |||
2>/dev/null; echo; fi | |||
# Display compiler version information. | |||
gccversion : | |||
@$(CC) --version | |||
# Program the device. | |||
program: $(TARGET).hex $(TARGET).eep | |||
$(AVRDUDE) $(AVRDUDE_FLAGS) $(AVRDUDE_WRITE_FLASH) $(AVRDUDE_WRITE_EEPROM) | |||
# Generate avr-gdb config/init file which does the following: | |||
# define the reset signal, load the target file, connect to target, and set | |||
# a breakpoint at main(). | |||
gdb-config: | |||
@$(REMOVE) $(GDBINIT_FILE) | |||
@echo define reset >> $(GDBINIT_FILE) | |||
@echo SIGNAL SIGHUP >> $(GDBINIT_FILE) | |||
@echo end >> $(GDBINIT_FILE) | |||
@echo file $(TARGET).elf >> $(GDBINIT_FILE) | |||
@echo target remote $(DEBUG_HOST):$(DEBUG_PORT) >> $(GDBINIT_FILE) | |||
ifeq ($(DEBUG_BACKEND),simulavr) | |||
@echo load >> $(GDBINIT_FILE) | |||
endif | |||
@echo break main >> $(GDBINIT_FILE) | |||
debug: gdb-config $(TARGET).elf | |||
ifeq ($(DEBUG_BACKEND), avarice) | |||
@echo Starting AVaRICE - Press enter when "waiting to connect" message displays. | |||
@$(WINSHELL) /c start avarice --jtag $(JTAG_DEV) --erase --program --file \ | |||
$(TARGET).elf $(DEBUG_HOST):$(DEBUG_PORT) | |||
@$(WINSHELL) /c pause | |||
else | |||
@$(WINSHELL) /c start simulavr --gdbserver --device $(MCU) --clock-freq \ | |||
$(DEBUG_MFREQ) --port $(DEBUG_PORT) | |||
endif | |||
@$(WINSHELL) /c start avr-$(DEBUG_UI) --command=$(GDBINIT_FILE) | |||
# Convert ELF to COFF for use in debugging / simulating in AVR Studio or VMLAB. | |||
COFFCONVERT = $(OBJCOPY) --debugging | |||
COFFCONVERT += --change-section-address .data-0x800000 | |||
COFFCONVERT += --change-section-address .bss-0x800000 | |||
COFFCONVERT += --change-section-address .noinit-0x800000 | |||
COFFCONVERT += --change-section-address .eeprom-0x810000 | |||
coff: $(TARGET).elf | |||
@echo | |||
@echo $(MSG_COFF) $(TARGET).cof | |||
$(COFFCONVERT) -O coff-avr $< $(TARGET).cof | |||
extcoff: $(TARGET).elf | |||
@echo | |||
@echo $(MSG_EXTENDED_COFF) $(TARGET).cof | |||
$(COFFCONVERT) -O coff-ext-avr $< $(TARGET).cof | |||
# Create final output files (.hex, .eep) from ELF output file. | |||
%.hex: %.elf | |||
@echo | |||
@echo $(MSG_FLASH) $@ | |||
$(OBJCOPY) -O $(FORMAT) -R .eeprom -R .fuse -R .lock -R .signature $< $@ | |||
%.eep: %.elf | |||
@echo | |||
@echo $(MSG_EEPROM) $@ | |||
-$(OBJCOPY) -j .eeprom --set-section-flags=.eeprom="alloc,load" \ | |||
--change-section-lma .eeprom=0 --no-change-warnings -O $(FORMAT) $< $@ || exit 0 | |||
# Create extended listing file from ELF output file. | |||
%.lss: %.elf | |||
@echo | |||
@echo $(MSG_EXTENDED_LISTING) $@ | |||
$(OBJDUMP) -h -S -z $< > $@ | |||
# Create a symbol table from ELF output file. | |||
%.sym: %.elf | |||
@echo | |||
@echo $(MSG_SYMBOL_TABLE) $@ | |||
$(NM) -n $< > $@ | |||
# Create library from object files. | |||
.SECONDARY : $(TARGET).a | |||
.PRECIOUS : $(OBJ) | |||
%.a: $(OBJ) | |||
@echo | |||
@echo $(MSG_CREATING_LIBRARY) $@ | |||
$(AR) $@ $(OBJ) | |||
# Link: create ELF output file from object files. | |||
.SECONDARY : $(TARGET).elf | |||
.PRECIOUS : $(OBJ) | |||
%.elf: $(OBJ) | |||
@echo | |||
@echo $(MSG_LINKING) $@ | |||
$(CC) $(ALL_CFLAGS) $^ --output $@ $(LDFLAGS) | |||
# Compile: create object files from C source files. | |||
$(OBJDIR)/%.o : %.c | |||
@echo | |||
@echo $(MSG_COMPILING) $< | |||
$(CC) -c $(ALL_CFLAGS) $< -o $@ | |||
# Compile: create object files from C++ source files. | |||
$(OBJDIR)/%.o : %.cpp | |||
@echo | |||
@echo $(MSG_COMPILING_CPP) $< | |||
$(CC) -c $(ALL_CPPFLAGS) $< -o $@ | |||
# Compile: create assembler files from C source files. | |||
%.s : %.c | |||
$(CC) -S $(ALL_CFLAGS) $< -o $@ | |||
# Compile: create assembler files from C++ source files. | |||
%.s : %.cpp | |||
$(CC) -S $(ALL_CPPFLAGS) $< -o $@ | |||
# Assemble: create object files from assembler source files. | |||
$(OBJDIR)/%.o : %.S | |||
@echo | |||
@echo $(MSG_ASSEMBLING) $< | |||
$(CC) -c $(ALL_ASFLAGS) $< -o $@ | |||
# Create preprocessed source for use in sending a bug report. | |||
%.i : %.c | |||
$(CC) -E -mmcu=$(MCU) -I. $(CFLAGS) $< -o $@ | |||
# Target: clean project. | |||
clean: begin clean_list end | |||
clean_list : | |||
@echo | |||
$(REMOVE) $(TARGET).hex | |||
$(REMOVE) $(TARGET).eep | |||
$(REMOVE) $(TARGET).cof | |||
$(REMOVE) $(TARGET).elf | |||
$(REMOVE) $(TARGET).map | |||
$(REMOVE) $(TARGET).sym | |||
$(REMOVE) $(TARGET).lss | |||
$(REMOVE) $(OBJ) | |||
$(REMOVE) $(LST) | |||
$(REMOVE) $(OBJ:.o=.s) | |||
$(REMOVE) $(OBJ:.o=.i) | |||
$(REMOVEDIR) .dep | |||
# Create object files directory | |||
$(shell mkdir $(OBJDIR) 2>/dev/null) | |||
# Include the dependency files. | |||
-include $(shell mkdir .dep 2>/dev/null) $(wildcard .dep/*) | |||
# Listing of phony targets. | |||
.PHONY : all begin finish end sizebefore sizeafter gccversion \ | |||
build elf hex eep lss sym coff extcoff \ | |||
clean clean_list program debug gdb-config |
@@ -21,11 +21,10 @@ | |||
* THE SOFTWARE. | |||
*/ | |||
// Version 1.0: Initial Release | |||
#include <avr/io.h> | |||
#include <avr/pgmspace.h> | |||
#include "print.h" | |||
#include "sendchar.h" | |||
bool print_enable = false; | |||
@@ -38,15 +37,15 @@ void print_P(const char *s) | |||
while (1) { | |||
c = pgm_read_byte(s++); | |||
if (!c) break; | |||
if (c == '\n') usb_debug_putchar('\r'); | |||
usb_debug_putchar(c); | |||
if (c == '\n') sendchar('\r'); | |||
sendchar(c); | |||
} | |||
} | |||
void phex1(unsigned char c) | |||
{ | |||
if (!print_enable) return; | |||
usb_debug_putchar(c + ((c < 10) ? '0' : 'A' - 10)); | |||
sendchar(c + ((c < 10) ? '0' : 'A' - 10)); | |||
} | |||
void phex(unsigned char c) | |||
@@ -68,7 +67,7 @@ void pbin(unsigned char c) | |||
{ | |||
if (!print_enable) return; | |||
for (int i = 7; i >= 0; i--) { | |||
usb_debug_putchar((c & (1<<i)) ? '1' : '0'); | |||
sendchar((c & (1<<i)) ? '1' : '0'); | |||
} | |||
} | |||
@@ -76,6 +75,6 @@ void pbin_reverse(unsigned char c) | |||
{ | |||
if (!print_enable) return; | |||
for (int i = 0; i < 8; i++) { | |||
usb_debug_putchar((c & (1<<i)) ? '1' : '0'); | |||
sendchar((c & (1<<i)) ? '1' : '0'); | |||
} | |||
} |
@@ -3,15 +3,13 @@ | |||
#include <stdbool.h> | |||
#include <avr/pgmspace.h> | |||
#include "usb_debug.h" | |||
bool print_enable; | |||
extern bool print_enable; | |||
// this macro allows you to write print("some text") and | |||
// the string is automatically placed into flash memory :) | |||
#define print(s) print_P(PSTR(s)) | |||
#define pchar(c) usb_debug_putchar(c) | |||
void print_P(const char *s); | |||
void phex(unsigned char c); |
@@ -0,0 +1,50 @@ | |||
# Target file name (without extension). | |||
TARGET = ps2_vusb | |||
# Directory common source filess exist | |||
COMMON_DIR = .. | |||
# Directory keyboard dependent files exist | |||
TARGET_DIR = . | |||
# keyboard dependent files | |||
TARGET_SRC = main.c \ | |||
keyboard_vusb.c \ | |||
layer.c \ | |||
keymap.c \ | |||
matrix.c \ | |||
ps2.c \ | |||
print.c \ | |||
util.c \ | |||
timer.c \ | |||
usart_print.c | |||
OPT_DEFS = -DDEBUG_LEVEL=0 | |||
# MCU name, you MUST set this to match the board you are using | |||
# type "make clean" after changing this, so all files will be rebuilt | |||
#MCU = at90usb162 # Teensy 1.0 | |||
#MCU = atmega32u4 # Teensy 2.0 | |||
#MCU = at90usb646 # Teensy++ 1.0 | |||
#MCU = at90usb1286 # Teensy++ 2.0 | |||
MCU = atmega168 | |||
# Processor frequency. | |||
# Normally the first thing your program should do is set the clock prescaler, | |||
# so your program will run at the correct speed. You should also set this | |||
# variable to same clock speed. The _delay_ms() macro uses this, and many | |||
# examples use this variable to calculate timings. Do not add a "UL" here. | |||
F_CPU = 16000000 | |||
# Build Options | |||
# comment out to disable the options. | |||
# | |||
#MOUSEKEY_ENABLE = yes # Mouse keys | |||
#USB_EXTRA_ENABLE = yes # Enhanced feature for Windows(Audio control and System control) | |||
#USB_NKRO_ENABLE = yes # USB Nkey Rollover | |||
include $(COMMON_DIR)/Makefile.vusb |
@@ -0,0 +1,165 @@ | |||
# Name: Makefile | |||
# Project: hid-mouse example | |||
# Author: Christian Starkjohann | |||
# Creation Date: 2008-04-07 | |||
# Tabsize: 4 | |||
# Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH | |||
# License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) | |||
# This Revision: $Id: Makefile 692 2008-11-07 15:07:40Z cs $ | |||
DEVICE = atmega168 | |||
F_CPU = 16000000 # in Hz | |||
FUSE_L = # see below for fuse values for particular devices | |||
FUSE_H = | |||
#AVRDUDE = avrdude -c usbasp -p $(DEVICE) # edit this line for your programmer | |||
AVRDUDE = avrdude -P COM1 -b 19200 -c arduino -p $(DEVICE) | |||
CFLAGS = -Iusbdrv -I. -DDEBUG_LEVEL=1 | |||
OBJECTS = usbdrv/usbdrv.o usbdrv/usbdrvasm.o usbdrv/oddebug.o main.o | |||
COMPILE = avr-gcc -Wall -Os -DF_CPU=$(F_CPU) $(CFLAGS) -mmcu=$(DEVICE) | |||
############################################################################## | |||
# Fuse values for particular devices | |||
############################################################################## | |||
# If your device is not listed here, go to | |||
# http://palmavr.sourceforge.net/cgi-bin/fc.cgi | |||
# and choose options for external crystal clock and no clock divider | |||
# | |||
################################## ATMega8 ################################## | |||
# ATMega8 FUSE_L (Fuse low byte): | |||
# 0x9f = 1 0 0 1 1 1 1 1 | |||
# ^ ^ \ / \--+--/ | |||
# | | | +------- CKSEL 3..0 (external >8M crystal) | |||
# | | +--------------- SUT 1..0 (crystal osc, BOD enabled) | |||
# | +------------------ BODEN (BrownOut Detector enabled) | |||
# +-------------------- BODLEVEL (2.7V) | |||
# ATMega8 FUSE_H (Fuse high byte): | |||
# 0xc9 = 1 1 0 0 1 0 0 1 <-- BOOTRST (boot reset vector at 0x0000) | |||
# ^ ^ ^ ^ ^ ^ ^------ BOOTSZ0 | |||
# | | | | | +-------- BOOTSZ1 | |||
# | | | | + --------- EESAVE (don't preserve EEPROM over chip erase) | |||
# | | | +-------------- CKOPT (full output swing) | |||
# | | +---------------- SPIEN (allow serial programming) | |||
# | +------------------ WDTON (WDT not always on) | |||
# +-------------------- RSTDISBL (reset pin is enabled) | |||
# | |||
############################## ATMega48/88/168 ############################## | |||
# ATMega*8 FUSE_L (Fuse low byte): | |||
# 0xdf = 1 1 0 1 1 1 1 1 | |||
# ^ ^ \ / \--+--/ | |||
# | | | +------- CKSEL 3..0 (external >8M crystal) | |||
# | | +--------------- SUT 1..0 (crystal osc, BOD enabled) | |||
# | +------------------ CKOUT (if 0: Clock output enabled) | |||
# +-------------------- CKDIV8 (if 0: divide by 8) | |||
# ATMega*8 FUSE_H (Fuse high byte): | |||
# 0xde = 1 1 0 1 1 1 1 0 | |||
# ^ ^ ^ ^ ^ \-+-/ | |||
# | | | | | +------ BODLEVEL 0..2 (110 = 1.8 V) | |||
# | | | | + --------- EESAVE (preserve EEPROM over chip erase) | |||
# | | | +-------------- WDTON (if 0: watchdog always on) | |||
# | | +---------------- SPIEN (allow serial programming) | |||
# | +------------------ DWEN (debug wire enable) | |||
# +-------------------- RSTDISBL (reset pin is enabled) | |||
# | |||
############################## ATTiny25/45/85 ############################### | |||
# ATMega*5 FUSE_L (Fuse low byte): | |||
# 0xef = 1 1 1 0 1 1 1 1 | |||
# ^ ^ \+/ \--+--/ | |||
# | | | +------- CKSEL 3..0 (clock selection -> crystal @ 12 MHz) | |||
# | | +--------------- SUT 1..0 (BOD enabled, fast rising power) | |||
# | +------------------ CKOUT (clock output on CKOUT pin -> disabled) | |||
# +-------------------- CKDIV8 (divide clock by 8 -> don't divide) | |||
# ATMega*5 FUSE_H (Fuse high byte): | |||
# 0xdd = 1 1 0 1 1 1 0 1 | |||
# ^ ^ ^ ^ ^ \-+-/ | |||
# | | | | | +------ BODLEVEL 2..0 (brownout trigger level -> 2.7V) | |||
# | | | | +---------- EESAVE (preserve EEPROM on Chip Erase -> not preserved) | |||
# | | | +-------------- WDTON (watchdog timer always on -> disable) | |||
# | | +---------------- SPIEN (enable serial programming -> enabled) | |||
# | +------------------ DWEN (debug wire enable) | |||
# +-------------------- RSTDISBL (disable external reset -> enabled) | |||
# | |||
################################ ATTiny2313 ################################# | |||
# ATTiny2313 FUSE_L (Fuse low byte): | |||
# 0xef = 1 1 1 0 1 1 1 1 | |||
# ^ ^ \+/ \--+--/ | |||
# | | | +------- CKSEL 3..0 (clock selection -> crystal @ 12 MHz) | |||
# | | +--------------- SUT 1..0 (BOD enabled, fast rising power) | |||
# | +------------------ CKOUT (clock output on CKOUT pin -> disabled) | |||
# +-------------------- CKDIV8 (divide clock by 8 -> don't divide) | |||
# ATTiny2313 FUSE_H (Fuse high byte): | |||
# 0xdb = 1 1 0 1 1 0 1 1 | |||
# ^ ^ ^ ^ \-+-/ ^ | |||
# | | | | | +---- RSTDISBL (disable external reset -> enabled) | |||
# | | | | +-------- BODLEVEL 2..0 (brownout trigger level -> 2.7V) | |||
# | | | +-------------- WDTON (watchdog timer always on -> disable) | |||
# | | +---------------- SPIEN (enable serial programming -> enabled) | |||
# | +------------------ EESAVE (preserve EEPROM on Chip Erase -> not preserved) | |||
# +-------------------- DWEN (debug wire enable) | |||
# symbolic targets: | |||
help: | |||
@echo "This Makefile has no default rule. Use one of the following:" | |||
@echo "make hex ....... to build main.hex" | |||
@echo "make program ... to flash fuses and firmware" | |||
@echo "make fuse ...... to flash the fuses" | |||
@echo "make flash ..... to flash the firmware (use this on metaboard)" | |||
@echo "make clean ..... to delete objects and hex file" | |||
hex: main.hex | |||
program: flash fuse | |||
# rule for programming fuse bits: | |||
fuse: | |||
@[ "$(FUSE_H)" != "" -a "$(FUSE_L)" != "" ] || \ | |||
{ echo "*** Edit Makefile and choose values for FUSE_L and FUSE_H!"; exit 1; } | |||
$(AVRDUDE) -U hfuse:w:$(FUSE_H):m -U lfuse:w:$(FUSE_L):m | |||
# rule for uploading firmware: | |||
flash: main.hex | |||
$(AVRDUDE) -U flash:w:main.hex:i | |||
# rule for deleting dependent files (those which can be built by Make): | |||
clean: | |||
rm -f main.hex main.lst main.obj main.cof main.list main.map main.eep.hex main.elf *.o usbdrv/*.o main.s usbdrv/oddebug.s usbdrv/usbdrv.s | |||
# Generic rule for compiling C files: | |||
.c.o: | |||
$(COMPILE) -c $< -o $@ | |||
# Generic rule for assembling Assembler source files: | |||
.S.o: | |||
$(COMPILE) -x assembler-with-cpp -c $< -o $@ | |||
# "-x assembler-with-cpp" should not be necessary since this is the default | |||
# file type for the .S (with capital S) extension. However, upper case | |||
# characters are not always preserved on Windows. To ensure WinAVR | |||
# compatibility define the file type manually. | |||
# Generic rule for compiling C to assembler, used for debugging only. | |||
.c.s: | |||
$(COMPILE) -S $< -o $@ | |||
# file targets: | |||
# Since we don't want to ship the driver multipe times, we copy it into this project: | |||
usbdrv: | |||
cp -r ../usbdrv . | |||
main.elf: usbdrv $(OBJECTS) # usbdrv dependency only needed because we copy it | |||
$(COMPILE) -o main.elf $(OBJECTS) | |||
main.hex: main.elf | |||
rm -f main.hex main.eep.hex | |||
avr-objcopy -j .text -j .data -O ihex main.elf main.hex | |||
avr-size main.hex | |||
# debugging targets: | |||
disasm: main.elf | |||
avr-objdump -d main.elf | |||
cpp: | |||
$(COMPILE) -E main.c |
@@ -0,0 +1,36 @@ | |||
#ifndef CONFIG_H | |||
#define CONFIG_H | |||
#define VENDOR_ID 0xFEED | |||
#define PRODUCT_ID 0x6512 | |||
#define MANUFACTURER t.m.k. | |||
#define PRODUCT PS/2 keyboard converter | |||
#define DESCRIPTION convert PS/2 keyboard to USB | |||
/* matrix size */ | |||
#define MATRIX_ROWS 32 // keycode bit: 3-0 | |||
#define MATRIX_COLS 8 // keycode bit: 6-4 | |||
/* define if matrix has ghost */ | |||
//#define MATRIX_HAS_GHOST | |||
/* USB NKey Rollover */ | |||
#ifdef USB_NKRO_ENABLE | |||
#endif | |||
/* mouse keys */ | |||
#ifdef MOUSEKEY_ENABLE | |||
# define MOUSEKEY_DELAY_TIME 255 | |||
#endif | |||
/* PS/2 mouse */ | |||
#define PS2_CLOCK_PORT PORTD | |||
#define PS2_CLOCK_PIN PIND | |||
#define PS2_CLOCK_DDR DDRD | |||
#define PS2_CLOCK_BIT 6 | |||
#define PS2_DATA_PORT PORTD | |||
#define PS2_DATA_PIN PIND | |||
#define PS2_DATA_DDR DDRD | |||
#define PS2_DATA_BIT 7 | |||
#endif |
@@ -0,0 +1,27 @@ | |||
#ifndef KEYBOARD_H | |||
#define KEYBOARD_H | |||
#include "stdbool.h" | |||
#define REPORT_KEYS 6 | |||
typedef struct{ | |||
uint8_t mods; | |||
uint8_t rserved; // not used | |||
uint8_t keys[REPORT_KEYS]; | |||
}report_t; | |||
//extern report_t *report; | |||
//extern report_t *report_prev; | |||
report_t *report_get(void); | |||
bool report_has_key(void); | |||
void report_send(void); | |||
void report_add_mod(uint8_t mod); | |||
void report_add_key(uint8_t key); | |||
void report_add_code(uint8_t code); | |||
void report_swap(void); | |||
void report_clear(void); | |||
#endif |
@@ -0,0 +1,156 @@ | |||
#include "usbdrv.h" | |||
#include "usb_keycodes.h" | |||
#include "keyboard.h" | |||
#include "print.h" | |||
static report_t report0; | |||
static report_t report1; | |||
static report_t *report = &report0; | |||
static report_t *report_prev = &report1; | |||
void report_send(void) | |||
{ | |||
if (usbInterruptIsReady()){ | |||
usbSetInterrupt((void *)report, sizeof(*report)); | |||
} | |||
} | |||
report_t *report_get(void) | |||
{ | |||
return report; | |||
} | |||
uint8_t report_mods(void) | |||
{ | |||
return report->mods; | |||
} | |||
uint8_t *report_keys(void) | |||
{ | |||
return report->keys; | |||
} | |||
bool report_has_key(void) | |||
{ | |||
for (int i = 0; i < REPORT_KEYS; i++) { | |||
if (report->keys[i]) | |||
return true; | |||
} | |||
return false; | |||
} | |||
void report_add_mod(uint8_t mod) | |||
{ | |||
report->mods |= mod; | |||
} | |||
void report_add_key(uint8_t code) | |||
{ | |||
int8_t i = 0; | |||
int8_t empty = -1; | |||
for (; i < REPORT_KEYS; i++) { | |||
if (report_prev->keys[i] == code) { | |||
report->keys[i] = code; | |||
break; | |||
} | |||
if (empty == -1 && report_prev->keys[i] == KB_NO && report->keys[i] == KB_NO) { | |||
empty = i; | |||
} | |||
} | |||
if (i == REPORT_KEYS && empty != -1) { | |||
report->keys[empty] = code; | |||
} | |||
} | |||
void report_add_code(uint8_t code) | |||
{ | |||
if (IS_MOD(code)) { | |||
report_add_mod(code); | |||
} else { | |||
report_add_key(code); | |||
} | |||
} | |||
void report_swap(void) | |||
{ | |||
report_t *tmp = report_prev; | |||
report_prev = report; | |||
report = tmp; | |||
} | |||
void report_clear(void) | |||
{ | |||
report->mods = 0; | |||
for (int8_t i = 0; i < REPORT_KEYS; i++) { | |||
report->keys[i] = 0; | |||
} | |||
} | |||
static uchar idleRate; /* repeat rate for keyboards, never used for mice */ | |||
usbMsgLen_t usbFunctionSetup(uchar data[8]) | |||
{ | |||
usbRequest_t *rq = (void *)data; | |||
print("Setup: "); | |||
if((rq->bmRequestType & USBRQ_TYPE_MASK) == USBRQ_TYPE_CLASS){ /* class request type */ | |||
print("CLASS: "); | |||
phex(rq->bRequest); | |||
if(rq->bRequest == USBRQ_HID_GET_REPORT){ | |||
print("GET_REPORT"); | |||
/* we only have one report type, so don't look at wValue */ | |||
usbMsgPtr = (void *)report; | |||
return sizeof(*report); | |||
}else if(rq->bRequest == USBRQ_HID_GET_IDLE){ | |||
print("GET_IDLE: "); | |||
phex(idleRate); | |||
usbMsgPtr = &idleRate; | |||
return 1; | |||
}else if(rq->bRequest == USBRQ_HID_SET_IDLE){ | |||
idleRate = rq->wValue.bytes[1]; | |||
print("SET_IDLE: "); | |||
phex(idleRate); | |||
} | |||
print("\n"); | |||
}else{ | |||
print("VENDOR\n"); | |||
/* no vendor specific requests implemented */ | |||
} | |||
return 0; /* default for not implemented requests: return no data back to host */ | |||
} | |||
PROGMEM char usbHidReportDescriptor[USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH] = { | |||
0x05, 0x01, // Usage Page (Generic Desktop), | |||
0x09, 0x06, // Usage (Keyboard), | |||
0xA1, 0x01, // Collection (Application), | |||
0x75, 0x01, // Report Size (1), | |||
0x95, 0x08, // Report Count (8), | |||
0x05, 0x07, // Usage Page (Key Codes), | |||
0x19, 0xE0, // Usage Minimum (224), | |||
0x29, 0xE7, // Usage Maximum (231), | |||
0x15, 0x00, // Logical Minimum (0), | |||
0x25, 0x01, // Logical Maximum (1), | |||
0x81, 0x02, // Input (Data, Variable, Absolute), ;Modifier byte | |||
0x95, 0x01, // Report Count (1), | |||
0x75, 0x08, // Report Size (8), | |||
0x81, 0x03, // Input (Constant), ;Reserved byte | |||
0x95, 0x05, // Report Count (5), | |||
0x75, 0x01, // Report Size (1), | |||
0x05, 0x08, // Usage Page (LEDs), | |||
0x19, 0x01, // Usage Minimum (1), | |||
0x29, 0x05, // Usage Maximum (5), | |||
0x91, 0x02, // Output (Data, Variable, Absolute), ;LED report | |||
0x95, 0x01, // Report Count (1), | |||
0x75, 0x03, // Report Size (3), | |||
0x91, 0x03, // Output (Constant), ;LED report padding | |||
0x95, 0x06, // Report Count (6), | |||
0x75, 0x08, // Report Size (8), | |||
0x15, 0x00, // Logical Minimum (0), | |||
0x25, 0xFF, // Logical Maximum(255), | |||
0x05, 0x07, // Usage Page (Key Codes), | |||
0x19, 0x00, // Usage Minimum (0), | |||
0x29, 0xFF, // Usage Maximum (255), | |||
0x81, 0x00, // Input (Data, Array), | |||
0xc0 // End Collection | |||
}; |
@@ -0,0 +1,189 @@ | |||
/* | |||
* Keymap for PS/2 keyboard | |||
*/ | |||
#include <stdint.h> | |||
#include <stdbool.h> | |||
#include <avr/pgmspace.h> | |||
#include "usb_keyboard.h" | |||
#include "usb_keycodes.h" | |||
#include "print.h" | |||
#include "debug.h" | |||
#include "util.h" | |||
#include "keymap_skel.h" | |||
#define KEYCODE(layer, row, col) (pgm_read_byte(&keymaps[(layer)][(row)][(col)])) | |||
// Convert physical keyboard layout to matrix array. | |||
// This is a macro to define keymap easily in keyboard layout form. | |||
#define KEYMAP( \ | |||
K76, K05,K06,K04,K0C, K03,K0B,K83,K0A, K01,K09,K78,K07, KFC,K7E,KFE, KB7,KBF,KDE, \ | |||
K0E,K16,K1E,K26,K25,K2E,K36,K3D,K3E,K46,K45,K4E,K55,K66, KF0,KEC,KFD, K77,KCA,K7C,K7B, \ | |||
K0D,K15,K1D,K24,K2D,K2C,K35,K3C,K43,K44,K4D,K54,K5B,K5D, KF1,KE9,KFA, K6C,K75,K7D, \ | |||
K58,K1C,K1B,K23,K2B,K34,K33,K3B,K42,K4B,K4C,K52, K5A, K6B,K73,K74,K79, \ | |||
K12,K1A,K22,K21,K2A,K32,K31,K3A,K41,K49,K4A, K59, KF5, K69,K72,K7A, \ | |||
K14,K9F,K11, K29, K91,KA7,KAF,K94, KEB,KF2,KF4, K70, K71,KDA \ | |||
) { \ | |||
{ KB_NO, KB_##K01, KB_NO, KB_##K03, KB_##K04, KB_##K05, KB_##K06, KB_##K07 }, \ | |||
{ KB_NO, KB_##K09, KB_##K0A, KB_##K0B, KB_##K0C, KB_##K0D, KB_##K0E, KB_NO }, \ | |||
{ KB_NO, KB_##K11, KB_##K12, KB_NO, KB_##K14, KB_##K15, KB_##K16, KB_NO }, \ | |||
{ KB_NO, KB_NO, KB_##K1A, KB_##K1B, KB_##K1C, KB_##K1D, KB_##K1E, KB_NO }, \ | |||
{ KB_NO, KB_##K21, KB_##K22, KB_##K23, KB_##K24, KB_##K25, KB_##K26, KB_NO }, \ | |||
{ KB_NO, KB_##K29, KB_##K2A, KB_##K2B, KB_##K2C, KB_##K2D, KB_##K2E, KB_NO }, \ | |||
{ KB_NO, KB_##K31, KB_##K32, KB_##K33, KB_##K34, KB_##K35, KB_##K36, KB_NO }, \ | |||
{ KB_NO, KB_NO, KB_##K3A, KB_##K3B, KB_##K3C, KB_##K3D, KB_##K3E, KB_NO }, \ | |||
{ KB_NO, KB_##K41, KB_##K42, KB_##K43, KB_##K44, KB_##K45, KB_##K46, KB_NO }, \ | |||
{ KB_NO, KB_##K49, KB_##K4A, KB_##K4B, KB_##K4C, KB_##K4D, KB_##K4E, KB_NO }, \ | |||
{ KB_NO, KB_NO, KB_##K52, KB_NO, KB_##K54, KB_##K55, KB_NO, KB_NO }, \ | |||
{ KB_##K58, KB_##K59, KB_##K5A, KB_##K5B, KB_NO, KB_##K5D, KB_NO, KB_NO }, \ | |||
{ KB_NO, KB_NO, KB_NO, KB_NO, KB_NO, KB_NO, KB_##K66, KB_NO }, \ | |||
{ KB_NO, KB_##K69, KB_NO, KB_##K6B, KB_##K6C, KB_NO, KB_NO, KB_NO }, \ | |||
{ KB_##K70, KB_##K71, KB_##K72, KB_##K73, KB_##K74, KB_##K75, KB_##K76, KB_##K77 }, \ | |||
{ KB_##K78, KB_##K79, KB_##K7A, KB_##K7B, KB_##K7C, KB_##K7D, KB_##K7E, KB_NO }, \ | |||
{ KB_NO, KB_NO, KB_NO, KB_##K83, KB_NO, KB_NO, KB_NO, KB_NO }, \ | |||
{ KB_NO, KB_NO, KB_NO, KB_NO, KB_NO, KB_NO, KB_NO, KB_NO }, \ | |||
{ KB_NO, KB_##K91, KB_NO, KB_NO, KB_##K94, KB_NO, KB_NO, KB_NO }, \ | |||
{ KB_NO, KB_NO, KB_NO, KB_NO, KB_NO, KB_NO, KB_NO, KB_##K9F }, \ | |||
{ KB_NO, KB_NO, KB_NO, KB_NO, KB_NO, KB_NO, KB_NO, KB_##KA7 }, \ | |||
{ KB_NO, KB_NO, KB_NO, KB_NO, KB_NO, KB_NO, KB_NO, KB_##KAF }, \ | |||
{ KB_NO, KB_NO, KB_NO, KB_NO, KB_NO, KB_NO, KB_NO, KB_##KB7 }, \ | |||
{ KB_NO, KB_NO, KB_NO, KB_NO, KB_NO, KB_NO, KB_NO, KB_##KBF }, \ | |||
{ KB_NO, KB_NO, KB_NO, KB_NO, KB_NO, KB_NO, KB_NO, KB_NO }, \ | |||
{ KB_NO, KB_NO, KB_##KCA, KB_NO, KB_NO, KB_NO, KB_NO, KB_NO }, \ | |||
{ KB_NO, KB_NO, KB_NO, KB_NO, KB_NO, KB_NO, KB_NO, KB_NO }, \ | |||
{ KB_NO, KB_NO, KB_##KDA, KB_NO, KB_NO, KB_NO, KB_##KDE, KB_NO }, \ | |||
{ KB_NO, KB_NO, KB_NO, KB_NO, KB_NO, KB_NO, KB_NO, KB_NO }, \ | |||
{ KB_NO, KB_##KE9, KB_NO, KB_##KEB, KB_##KEC, KB_NO, KB_NO, KB_NO }, \ | |||
{ KB_##KF0, KB_##KF1, KB_##KF2, KB_NO, KB_##KF4, KB_##KF5, KB_NO, KB_NO }, \ | |||
{ KB_NO, KB_NO, KB_##KFA, KB_NO, KB_##KFC, KB_##KFD, KB_##KFE, KB_NO }, \ | |||
} | |||
// Assign Fn key(0-7) to a layer to which switch with the Fn key pressed. | |||
static const uint8_t PROGMEM fn_layer[] = { | |||
5, // Fn0 | |||
6, // Fn1 | |||
5, // Fn2 | |||
0, // Fn3 | |||
0, // Fn4 | |||
0, // Fn5 | |||
0, // Fn6 | |||
0 // Fn7 | |||
}; | |||
// Assign Fn key(0-7) to a keycode sent when release Fn key without use of the layer. | |||
// See layer.c for details. | |||
static const uint8_t PROGMEM fn_keycode[] = { | |||
KB_SCLN, // Fn0 | |||
KB_SLSH, // Fn1 | |||
KB_A, // Fn2 | |||
KB_NO, // Fn3 | |||
KB_NO, // Fn4 | |||
KB_NO, // Fn5 | |||
KB_NO, // Fn6 | |||
KB_NO // Fn7 | |||
}; | |||
static const uint8_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { | |||
/* keymap | |||
* ,---. ,---------------. ,---------------. ,---------------. ,-----------. ,-----------. | |||
* |Esc| |F1 |F2 |F3 |F4 | |F5 |F6 |F7 |F8 | |F9 |F10|F11|F12| |PrS|ScL|Pau| |Pwr|Slp|Wak| | |||
* `---' `---------------' `---------------' `---------------' `-----------' `-----------' | |||
* ,-----------------------------------------------------------. ,-----------. ,---------------. | |||
* | `| 1| 2| 3| 4| 5| 6| 7| 8| 9| 0| -| =|Backspa| |Ins|Hom|PgU| |NmL| /| *| -| | |||
* |-----------------------------------------------------------| |-----------| |---------------| | |||
* |Tab | Q| W| E| R| T| Y| U| I| O| P| [| ]| \| |Del|End|PgD| | 7| 8| 9| | | |||
* |-----------------------------------------------------------| `-----------' |-----------| +| | |||
* |CapsLo| A| S| D| F| G| H| J| K| L| ;| '|Return | | 4| 5| 6| | | |||
* |-----------------------------------------------------------| ,---. |---------------| | |||
* |Shift | Z| X| C| V| B| N| M| ,| ,| /|Shift | |Up | | 1| 2| 3| | | |||
* |-----------------------------------------------------------| ,-----------. |-----------|Ent| | |||
* |Ctrl |Gui |Alt | Space |Alt |Gui |Menu|Ctrl| |Lef|Dow|Rig| | 0| .| | | |||
* `-----------------------------------------------------------' `-----------' `---------------' | |||
*/ | |||
/* 0: default */ | |||
KEYMAP( | |||
ESC, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, PSCR,SLCK,BRK, PWR, F13, F14, | |||
GRV, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, MINS,EQL, BSPC, INS, HOME,PGUP, NLCK,PSLS,PAST,PMNS, | |||
TAB, Q, W, E, R, T, Y, U, I, O, P, LBRC,RBRC,BSLS, DEL, END, PGDN, P7, P8, P9, | |||
CAPS,FN2, S, D, F, G, H, J, K, L, FN0, QUOT, ENT, P4, P5, P6, PPLS, | |||
LSFT,Z, X, C, V, B, N, M, COMM,DOT, FN1, RSFT, UP, P1, P2, P3, | |||
LCTL,LGUI,LALT, SPC, RALT,RGUI,APP, RCTL, LEFT,DOWN,RGHT, P0, PDOT,PENT | |||
), | |||
/* 1: plain Qwerty without layer switching */ | |||
KEYMAP( | |||
ESC, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, PSCR,SLCK,BRK, PWR, F13, F14, | |||
GRV, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, MINS,EQL, BSPC, INS, HOME,PGUP, NLCK,PSLS,PAST,PMNS, | |||
TAB, Q, W, E, R, T, Y, U, I, O, P, LBRC,RBRC,BSLS, DEL, END, PGDN, P7, P8, P9, | |||
CAPS,A, S, D, F, G, H, J, K, L, SCLN,QUOT, ENT, P4, P5, P6, PPLS, | |||
LSFT,Z, X, C, V, B, N, M, COMM,DOT, SLSH, RSFT, UP, P1, P2, P3, | |||
LCTL,LGUI,LALT, SPC, RALT,RGUI,APP, RCTL, LEFT,DOWN,RGHT, P0, PDOT,PENT | |||
), | |||
/* 2: Colemak http://colemak.com */ | |||
KEYMAP( | |||
ESC, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, PSCR,SLCK,BRK, PWR, F13, F14, | |||
GRV, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, MINS,EQL, BSPC, INS, HOME,PGUP, NLCK,PSLS,PAST,PMNS, | |||
TAB, Q, W, F, P, G, J, L, U, Y, SCLN,LBRC,RBRC,BSLS, DEL, END, PGDN, P7, P8, P9, | |||
BSPC,A, R, S, T, D, H, N, E, I, O, QUOT, ENT, P4, P5, P6, PPLS, | |||
LSFT,Z, X, C, V, B, K, M, COMM,DOT, SLSH, RSFT, UP, P1, P2, P3, | |||
LCTL,LGUI,LALT, SPC, RALT,RGUI,APP, RCTL, LEFT,DOWN,RGHT, P0, PDOT,PENT | |||
), | |||
/* 3: Dvorak http://en.wikipedia.org/wiki/Dvorak_Simplified_Keyboard */ | |||
KEYMAP( | |||
ESC, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, PSCR,SLCK,BRK, PWR, F13, F14, | |||
GRV, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, LBRC,RBRC,BSPC, INS, HOME,PGUP, NLCK,PSLS,PAST,PMNS, | |||
TAB, QUOT,COMM,DOT, P, Y, F, G, C, R, L, SLSH,EQL, BSLS, DEL, END, PGDN, P7, P8, P9, | |||
CAPS,A, O, E, U, I, D, H, T, N, S, MINS, ENT, P4, P5, P6, PPLS, | |||
LSFT,SCLN,Q, J, K, X, B, M, W, V, Z, RSFT, UP, P1, P2, P3, | |||
LCTL,LGUI,LALT, SPC, RALT,RGUI,APP, RCTL, LEFT,DOWN,RGHT, P0, PDOT,PENT | |||
), | |||
/* 4: Workman http://viralintrospection.wordpress.com/2010/09/06/a-different-philosophy-in-designing-keyboard-layouts/ */ | |||
KEYMAP( | |||
ESC, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, PSCR,SLCK,BRK, PWR, F13, F14, | |||
GRV, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, MINS,EQL, BSPC, INS, HOME,PGUP, NLCK,PSLS,PAST,PMNS, | |||
TAB, Q, D, R, W, B, J, F, U, P, SCLN,LBRC,RBRC,BSLS, DEL, END, PGDN, P7, P8, P9, | |||
BSPC,A, S, H, T, G, Y, N, E, O, I, QUOT, ENT, P4, P5, P6, PPLS, | |||
LSFT,Z, X, M, C, V, K, L, COMM,DOT, SLSH, RSFT, UP, P1, P2, P3, | |||
LCTL,LGUI,LALT, SPC, RALT,RGUI,APP, RCTL, LEFT,DOWN,RGHT, P0, PDOT,PENT | |||
), | |||
/* 5: Mouse keys */ | |||
KEYMAP( | |||
ESC, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, PSCR,SLCK,BRK, PWR, F13, F14, | |||
ESC, F1, F2, F3, F4, F5, F6, F7, F8, F8, F10, F11, F12, BSPC, INS, HOME,PGUP, NLCK,PSLS,PAST,PMNS, | |||
TAB, WH_L,WH_D,MS_U,WH_U,WH_R,WH_L,WH_D,WH_U,WH_R,NO, NO, NO, BSLS, DEL, END, PGDN, P7, P8, P9, | |||
CAPS,FN2, MS_L,MS_D,MS_R,NO, MS_L,MS_D,MS_U,MS_R,FN0, NO, ENT, P4, P5, P6, PPLS, | |||
LSFT,VOLD,VOLU,MUTE,BTN2,BTN3,BTN2,BTN1,VOLD,VOLU,MUTE, RSFT, UP, P1, P2, P3, | |||
LCTL,LGUI,LALT, BTN1, RALT,RGUI,APP, RCTL, LEFT,DOWN,RGHT, P0, PDOT,PENT | |||
), | |||
/* 6: Cursor keys */ | |||
KEYMAP( | |||
ESC, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, PSCR,SLCK,BRK, PWR, F13, F14, | |||
ESC, F1, F2, F3, F4, F5, F6, F7, F8, F8, F10, F11, F12, BSPC, INS, HOME,PGUP, NLCK,PSLS,PAST,PMNS, | |||
TAB, NO, NO, NO, NO, NO, HOME,PGDN,PGUP,END, NO, NO, NO, BSLS, DEL, END, PGDN, P7, P8, P9, | |||
CAPS,NO, NO, NO, NO, NO, LEFT,DOWN,UP, RGHT,NO, NO, ENT, P4, P5, P6, PPLS, | |||
LSFT,VOLD,VOLU,MUTE,NO, NO, HOME,PGDN,PGUP,END, FN1, RSFT, UP, P1, P2, P3, | |||
LCTL,LGUI,LALT, SPC, RALT,RGUI,APP, RCTL, LEFT,DOWN,RGHT, P0, PDOT,PENT | |||
), | |||
}; | |||
uint8_t keymap_get_keycode(uint8_t layer, uint8_t row, uint8_t col) | |||
{ | |||
return KEYCODE(layer, row, col); | |||
} | |||
uint8_t keymap_fn_layer(uint8_t fn_bits) | |||
{ | |||
return pgm_read_byte(&fn_layer[biton(fn_bits)]); | |||
} | |||
uint8_t keymap_fn_keycode(uint8_t fn_bits) | |||
{ | |||
return pgm_read_byte(&fn_keycode[(biton(fn_bits))]); | |||
} | |||
// define a condition to enter special function mode | |||
bool keymap_is_special_mode(uint8_t fn_bits) | |||
{ | |||
return usb_keyboard_mods == (BIT_LSHIFT | BIT_RSHIFT) || usb_keyboard_mods == (BIT_LCTRL | BIT_RSHIFT); | |||
} |
@@ -0,0 +1,183 @@ | |||
#include "keymap_skel.h" | |||
#include "keyboard.h" | |||
#include "debug.h" | |||
#include "timer.h" | |||
#include "layer.h" | |||
/* | |||
* Parameters: | |||
* ENTER_DELAY |=======| | |||
* SEND_FN_TERM |================| | |||
* | |||
* Fn key processing cases: | |||
* 1. release Fn after SEND_FN_TERM. | |||
* Layer sw ___________|~~~~~~~~~~~|___ | |||
* Fn press ___|~~~~~~~~~~~~~~~~~~~|___ | |||
* Fn send ___________________________ | |||
* | |||
* 2. release Fn during SEND_FN_TERM.(not layer used) | |||
* Layer sw ___________|~~~~~~|________ | |||
* Fn press ___|~~~~~~~~~~~~~~|________ | |||
* Fn key send __________________|~|______ | |||
* other key press ___________________________ | |||
* other key send ___________________________ | |||
* | |||
* 3. release Fn during SEND_FN_TERM.(layer used) | |||
* Layer sw ___________|~~~~~~|________ | |||
* Fn press ___|~~~~~~~~~~~~~~|________ | |||
* Fn key send ___________________________ | |||
* Fn send ___________________________ | |||
* other key press _____________|~~|__________ | |||
* other key send _____________|~~|__________ | |||
* | |||
* 4. press other key during ENTER_DELAY. | |||
* Layer sw ___________________________ | |||
* Fn key press ___|~~~~~~~~~|_____________ | |||
* Fn key send ______|~~~~~~|_____________ | |||
* other key press ______|~~~|________________ | |||
* other key send _______|~~|________________ | |||
* | |||
* 5. press Fn while press other key. | |||
* Layer sw ___________________________ | |||
* Fn key press ___|~~~~~~~~~|_____________ | |||
* Fn key send ___|~~~~~~~~~|_____________ | |||
* other key press ~~~~~~~|___________________ | |||
* other key send ~~~~~~~|___________________ | |||
* | |||
* 6. press Fn twice quickly and keep holding down.(repeat) | |||
* Layer sw ___________________________ | |||
* Fn key press ___|~|____|~~~~~~~~~~~~~~~~ | |||
* Fn key send _____|~|__|~~~~~~~~~~~~~~~~ | |||
*/ | |||
// LAYER_ENTER_DELAY: prevent from moving new layer | |||
#define LAYER_ENTER_DELAY 10 | |||
// LAYER_SEND_FN_TERM: send keycode if release key in this term | |||
#define LAYER_SEND_FN_TERM 40 | |||
uint8_t default_layer = 0; | |||
uint8_t current_layer = 0; | |||
static bool layer_used = false; | |||
static uint8_t new_layer(uint8_t fn_bits); | |||
uint8_t layer_get_keycode(uint8_t row, uint8_t col) | |||
{ | |||
uint8_t code = keymap_get_keycode(current_layer, row, col); | |||
// normal key or mouse key | |||
if ((IS_KEY(code) || IS_MOUSEKEY(code))) { | |||
layer_used = true; | |||
} | |||
return code; | |||
} | |||
// bit substract b from a | |||
#define BIT_SUBT(a, b) (a&(a^b)) | |||
void layer_switching(uint8_t fn_bits) | |||
{ | |||
// layer switching | |||
static uint8_t last_fn = 0; | |||
static uint8_t last_mods = 0; | |||
static uint16_t last_timer = 0; | |||
static uint8_t sent_fn = 0; | |||
if (fn_bits == last_fn) { // Fn state is not changed | |||
if (fn_bits == 0) { | |||
// do nothing | |||
} else { | |||
if (timer_elapsed(last_timer) > LAYER_ENTER_DELAY) { | |||
uint8_t _layer_to_switch = new_layer(BIT_SUBT(fn_bits, sent_fn)); | |||
if (current_layer != _layer_to_switch) { // not switch layer yet | |||
debug("Fn case: 1,2,3(LAYER_ENTER_DELAY passed)\n"); | |||
debug("Switch Layer: "); debug_hex(current_layer); | |||
current_layer = _layer_to_switch; | |||
layer_used = false; | |||
debug(" -> "); debug_hex(current_layer); debug("\n"); | |||
} | |||
} else { | |||
if (report_has_key()) { // other keys is pressed | |||
uint8_t _fn_to_send = BIT_SUBT(fn_bits, sent_fn); | |||
if (_fn_to_send) { | |||
debug("Fn case: 4(send Fn before other key pressed)\n"); | |||
// send only Fn key first | |||
report_swap(); | |||
report_clear(); | |||
report_add_code(keymap_fn_keycode(_fn_to_send)); // TODO: do all Fn keys | |||
report_add_mod(last_mods); | |||
report_send(); | |||
report_swap(); | |||
sent_fn |= _fn_to_send; | |||
} | |||
} | |||
} | |||
// add Fn keys to send | |||
//report_add_code(keymap_fn_keycode(fn_bits&sent_fn)); // TODO: do all Fn keys | |||
} | |||
} else { // Fn state is changed(edge) | |||
uint8_t fn_changed = 0; | |||
debug("fn_bits: "); debug_bin(fn_bits); debug("\n"); | |||
debug("sent_fn: "); debug_bin(sent_fn); debug("\n"); | |||
debug("last_fn: "); debug_bin(last_fn); debug("\n"); | |||
debug("last_mods: "); debug_hex(last_mods); debug("\n"); | |||
debug("last_timer: "); debug_hex16(last_timer); debug("\n"); | |||
// pressed Fn | |||
if ((fn_changed = BIT_SUBT(fn_bits, last_fn))) { | |||
debug("fn_changed: "); debug_bin(fn_changed); debug("\n"); | |||
if (report_has_key()) { | |||
debug("Fn case: 5(pressed Fn with other key)\n"); | |||
sent_fn |= fn_changed; | |||
} else if (fn_changed & sent_fn) { // pressed same Fn in a row | |||
if (timer_elapsed(last_timer) > LAYER_ENTER_DELAY) { | |||
debug("Fn case: 6(repate2)\n"); | |||
// time passed: not repeate | |||
sent_fn &= ~fn_changed; | |||
} else { | |||
debug("Fn case: 6(repate)\n"); | |||
} | |||
} | |||
} | |||
// released Fn | |||
if ((fn_changed = BIT_SUBT(last_fn, fn_bits))) { | |||
debug("fn_changed: "); debug_bin(fn_changed); debug("\n"); | |||
if (timer_elapsed(last_timer) < LAYER_SEND_FN_TERM) { | |||
//if (!layer_used && BIT_SUBT(fn_changed, sent_fn)) { // layer not used && Fn not sent | |||
if (BIT_SUBT(fn_changed, sent_fn)) { // layer not used && Fn not sent | |||
debug("Fn case: 2(send Fn one shot: released Fn during LAYER_SEND_FN_TERM)\n"); | |||
// send only Fn key first | |||
report_swap(); | |||
report_clear(); | |||
report_add_code(keymap_fn_keycode(fn_changed)); // TODO: do all Fn keys | |||
report_add_mod(last_mods); | |||
report_send(); | |||
report_swap(); | |||
sent_fn |= fn_changed; | |||
} | |||
} | |||
debug("Switch Layer(released Fn): "); debug_hex(current_layer); | |||
current_layer = new_layer(BIT_SUBT(fn_bits, sent_fn)); | |||
layer_used = false; | |||
debug(" -> "); debug_hex(current_layer); debug("\n"); | |||
} | |||
last_fn = fn_bits; | |||
last_mods = report_get()->mods; | |||
last_timer = timer_read(); | |||
} | |||
// send Fn keys | |||
for (uint8_t i = 0; i < 8; i++) { | |||
if ((sent_fn & fn_bits) & (1<<i)) { | |||
report_add_code(keymap_fn_keycode(1<<i)); | |||
} | |||
} | |||
} | |||
inline | |||
static uint8_t new_layer(uint8_t fn_bits) | |||
{ | |||
return (fn_bits ? keymap_fn_layer(fn_bits) : default_layer); | |||
} |
@@ -0,0 +1,126 @@ | |||
/* Name: main.c | |||
* Project: hid-mouse, a very simple HID example | |||
* Author: Christian Starkjohann | |||
* Creation Date: 2008-04-07 | |||
* Tabsize: 4 | |||
* Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH | |||
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) | |||
* This Revision: $Id: main.c 790 2010-05-30 21:00:26Z cs $ | |||
*/ | |||
/* | |||
This example should run on most AVRs with only little changes. No special | |||
hardware resources except INT0 are used. You may have to change usbconfig.h for | |||
different I/O pins for USB. Please note that USB D+ must be the INT0 pin, or | |||
at least be connected to INT0 as well. | |||
We use VID/PID 0x046D/0xC00E which is taken from a Logitech mouse. Don't | |||
publish any hardware using these IDs! This is for demonstration only! | |||
*/ | |||
#include <stdint.h> | |||
#include <avr/io.h> | |||
#include <avr/wdt.h> | |||
#include <avr/interrupt.h> /* for sei() */ | |||
#include <util/delay.h> /* for _delay_ms() */ | |||
#include <avr/pgmspace.h> /* required by usbdrv.h */ | |||
#include "usbdrv.h" | |||
#include "usart_print.h" /* This is also an example for using debug macros */ | |||
#include "ps2.h" | |||
#include "usb_keycodes.h" | |||
#include "matrix_skel.h" | |||
#include "keymap_skel.h" | |||
#include "layer.h" | |||
#include "print.h" | |||
#include "debug.h" | |||
#include "sendchar.h" | |||
#include "keyboard.h" | |||
#include "timer.h" | |||
/* ------------------------------------------------------------------------- */ | |||
/* ----------------------------- USB interface ----------------------------- */ | |||
/* ------------------------------------------------------------------------- */ | |||
int main(void) | |||
{ | |||
uchar i; | |||
print_enable = true; | |||
debug_enable = true; | |||
timer_init(); | |||
matrix_init(); | |||
wdt_enable(WDTO_1S); | |||
/* Even if you don't use the watchdog, turn it off here. On newer devices, | |||
* the status of the watchdog (on/off, period) is PRESERVED OVER RESET! | |||
*/ | |||
/* RESET status: all port bits are inputs without pull-up. | |||
* That's the way we need D+ and D-. Therefore we don't need any | |||
* additional hardware initialization. | |||
*/ | |||
odDebugInit(); | |||
DBG1(0x00, 0, 0); /* debug output: main starts */ | |||
usbInit(); | |||
usbDeviceDisconnect(); /* enforce re-enumeration, do this while interrupts are disabled! */ | |||
i = 0; | |||
while(--i){ /* fake USB disconnect for > 250 ms */ | |||
wdt_reset(); | |||
_delay_ms(1); | |||
} | |||
usbDeviceConnect(); | |||
sei(); | |||
uint8_t fn_bits = 0; | |||
while (1) { /* main event loop */ | |||
DBG1(0x02, 0, 0); /* debug output: main loop iterates */ | |||
wdt_reset(); | |||
usbPoll(); | |||
/* | |||
static uint8_t code = 0; | |||
code = ps2_host_recv(); | |||
if (code) { | |||
odDebug(0x05, &code, 1); | |||
} | |||
*/ | |||
matrix_scan(); | |||
if (matrix_is_modified()) { | |||
//matrix_print(); // too heavy on USART | |||
fn_bits = 0; | |||
report_swap(); | |||
report_clear(); | |||
for (int row = 0; row < matrix_rows(); row++) { | |||
for (int col = 0; col < matrix_cols(); col++) { | |||
if (!matrix_is_on(row, col)) continue; | |||
uint8_t code = layer_get_keycode(row, col); | |||
if (code == KB_NO) { | |||
// do nothing | |||
} | |||
else if (IS_MOD(code)) { | |||
report_add_mod(MOD_BIT(code)); | |||
} | |||
else if (IS_KEY(code)) { | |||
report_add_key(code); | |||
} | |||
else if (IS_FN(code)) { | |||
fn_bits |= FN_BIT(code); | |||
} | |||
else { | |||
debug("ignore keycode: "); debug_hex(code); debug("\n"); | |||
} | |||
} | |||
} | |||
} | |||
layer_switching(fn_bits); | |||
if (matrix_is_modified()) { | |||
report_send(); | |||
} | |||
} | |||
} |
@@ -0,0 +1,492 @@ | |||
/* | |||
* scan matrix | |||
*/ | |||
#include <stdint.h> | |||
#include <stdbool.h> | |||
#include <avr/io.h> | |||
#include <util/delay.h> | |||
#include "print.h" | |||
#include "util.h" | |||
#include "debug.h" | |||
#include "ps2.h" | |||
#include "usb_keyboard.h" | |||
#include "matrix_skel.h" | |||
#if (MATRIX_COLS > 16) | |||
# error "MATRIX_COLS must not exceed 16" | |||
#endif | |||
#if (MATRIX_ROWS > 255) | |||
# error "MATRIX_ROWS must not exceed 255" | |||
#endif | |||
/* | |||
* Matrix usage: | |||
* "PS/2 Scan Codes Set 2" is assigned to 256(32x8)cells matrix. | |||
* Hmm, It is very sparse and not efficient :( | |||
* | |||
* 8bit | |||
* --------- | |||
* 0| | | |||
* :| XX | 00-7F for normal codes(without E0-prefix) | |||
* f|_________| | |||
* 10| | | |||
* :| E0 XX | 80-FF for E0-prefix codes(use (XX|0x80) as code) | |||
* 1f| | | |||
* --------- | |||
* exceptions: | |||
* 83: F8[0x83](normal codes but > 0x7F) | |||
* FC: PrintScreen[E0 7C or 84] | |||
* FE: Puause | |||
*/ | |||
#define F8 (0x83) | |||
#define PRINT_SCREEN (0xFC) | |||
#define PAUSE (0xFE) | |||
#define ROW(code) (code>>3) | |||
#define COL(code) (code&0x07) | |||
static bool is_modified = false; | |||
// matrix state buffer(1:on, 0:off) | |||
#if (MATRIX_COLS <= 8) | |||
static uint8_t matrix[MATRIX_ROWS]; | |||
#else | |||
static uint16_t matrix[MATRIX_ROWS]; | |||
#endif | |||
#ifdef MATRIX_HAS_GHOST | |||
static bool matrix_has_ghost_in_row(uint8_t row); | |||
#endif | |||
static void matrix_make(uint8_t code); | |||
static void matrix_break(uint8_t code); | |||
static void ps2_reset(void); | |||
static void ps2_set_leds(uint8_t leds); | |||
inline | |||
uint8_t matrix_rows(void) | |||
{ | |||
return MATRIX_ROWS; | |||
} | |||
inline | |||
uint8_t matrix_cols(void) | |||
{ | |||
return MATRIX_COLS; | |||
} | |||
void matrix_init(void) | |||
{ | |||
ps2_host_init(); | |||
_delay_ms(1000); | |||
// flush LEDs | |||
/* | |||
ps2_set_leds(1<<PS2_LED_NUM_LOCK); | |||
_delay_ms(100); | |||
ps2_set_leds(1<<PS2_LED_NUM_LOCK|1<<PS2_LED_CAPS_LOCK); | |||
_delay_ms(100); | |||
ps2_set_leds(1<<PS2_LED_NUM_LOCK|1<<PS2_LED_CAPS_LOCK|1<<PS2_LED_SCROLL_LOCK); | |||
_delay_ms(300); | |||
ps2_set_leds(0x00); | |||
*/ | |||
// initialize matrix state: all keys off | |||
for (uint8_t i=0; i < MATRIX_ROWS; i++) matrix[i] = 0x00; | |||
return; | |||
} | |||
/* | |||
* PS/2 Scan Code Set 2: Exceptional Handling | |||
* | |||
* There are several keys to be handled exceptionally. | |||
* The scan code for these keys are varied or prefix/postfix'd | |||
* depending on modifier key state. | |||
* | |||
* References: | |||
* http://www.microsoft.com/whdc/archive/scancode.mspx | |||
* http://download.microsoft.com/download/1/6/1/161ba512-40e2-4cc9-843a-923143f3456c/scancode.doc | |||
* | |||
* | |||
* Insert, Delete, Home, End, PageUp, PageDown, Up, Down, Right, Left: | |||
* Num Lock: off | |||
* modifiers | make | break | |||
* ----------+---------------------------+---------------------- | |||
* Ohter | <make> | <break> | |||
* LShift | E0 F0 12 <make> | <break> E0 12 | |||
* RShift | E0 F0 59 <make> | <break> E0 59 | |||
* L+RShift | E0 F0 12 E0 F0 59 <make> | <break> E0 59 E0 12 | |||
* | |||
* Num Lock: on | |||
* modifiers | make | break | |||
* ----------+---------------------------+---------------------- | |||
* Other | E0 12 <make> | <break> E0 F0 12 | |||
* Shift'd | <make> | <break> | |||
* | |||
* Handling: ignore these prefix/postfix codes | |||
* | |||
* | |||
* Keypad-/: | |||
* modifiers | make | break | |||
* ----------+---------------------------+---------------------- | |||
* Ohter | <make> | <break> | |||
* LShift | E0 F0 12 <make> | <break> E0 12 | |||
* RShift | E0 F0 59 <make> | <break> E0 59 | |||
* L+RShift | E0 F0 12 E0 F0 59 <make> | <break> E0 59 E0 12 | |||
* | |||
* Handling: ignore these prefix/postfix codes | |||
* | |||
* | |||
* PrintScreen: | |||
* With hoding down modifiers, the scan code is sent as following: | |||
* | |||
* modifiers | make | break | |||
* ----------+--------------+----------------------------------- | |||
* Other | E0 12 E0 7C | E0 F0 7C E0 F0 12 | |||
* Shift'd | E0 7C | E0 F0 7C | |||
* Control'd | E0 7C | E0 F0 7C | |||
* Alt'd | 84 | F0 84 | |||
* | |||
* Handling: ignore prefix/postfix codes and treat both scan code | |||
* E0 7C and 84 as PrintScreen. | |||
* | |||
* Pause: | |||
* With hoding down modifiers, the scan code is sent as following: | |||
* | |||
* modifiers | make(no break code) | |||
* ----------+-------------------------------------------------- | |||
* no mods | E1 14 77 E1 F0 14 F0 77 | |||
* Control'd | E0 7E E0 F0 7E | |||
* | |||
* Handling: treat these two code sequence as Pause | |||
* | |||
*/ | |||
uint8_t matrix_scan(void) | |||
{ | |||
static enum { | |||
INIT, | |||
F0, | |||
E0, | |||
E0_F0, | |||
// states for Pause/Break | |||
E1, | |||
E1_14, | |||
E1_14_77, | |||
E1_14_77_E1, | |||
E1_14_77_E1_F0, | |||
E1_14_77_E1_F0_14, | |||
E1_14_77_E1_F0_14_F0, | |||
} state = INIT; | |||
is_modified = false; | |||
// Pause/Break off(PS/2 has no break for this key) | |||
if (matrix_is_on(ROW(PAUSE), COL(PAUSE))) { | |||
matrix_break(PAUSE); | |||
} | |||
uint8_t code; | |||
while ((code = ps2_host_recv())) { | |||
//debug_hex(code); debug(" "); | |||
switch (state) { | |||
case INIT: | |||
switch (code) { | |||
case 0xE0: // 2byte make | |||
state = E0; | |||
break; | |||
case 0xF0: // break code | |||
state = F0; | |||
break; | |||
case 0xE1: // Pause/Break | |||
state = E1; | |||
break; | |||
case 0x83: // F8 | |||
matrix_make(F8); | |||
state = INIT; | |||
break; | |||
case 0x84: // PrintScreen | |||
matrix_make(PRINT_SCREEN); | |||
state = INIT; | |||
break; | |||
default: // normal key make | |||
if (code < 0x80) { | |||
matrix_make(code); | |||
} else { | |||
debug("unexpected scan code at INIT: "); debug_hex(code); debug("\n"); | |||
} | |||
state = INIT; | |||
} | |||
break; | |||
case E0: | |||
switch (code) { | |||
case 0x12: // postfix/postfix code for exceptional keys | |||
case 0x59: // postfix/postfix code for exceptional keys | |||
// ignore | |||
state = INIT; | |||
break; | |||
case 0x7E: // former part of Control-Pause[E0 7E E0 F0 7E] | |||
matrix_make(PAUSE); | |||
state = INIT; | |||
break; | |||
case 0xF0: // E0 break | |||
state = E0_F0; | |||
break; | |||
default: // E0 make | |||
if (code < 0x80) { | |||
matrix_make(code|0x80); | |||
} else { | |||
debug("unexpected scan code at E0: "); debug_hex(code); debug("\n"); | |||
} | |||
state = INIT; | |||
} | |||
break; | |||
case F0: | |||
switch (code) { | |||
case 0x83: | |||
matrix_break(F8); | |||
state = INIT; | |||
break; | |||
case 0x84: | |||
matrix_break(PRINT_SCREEN); | |||
state = INIT; | |||
break; | |||
default: | |||
if (code < 0x80) { | |||
matrix_break(code); | |||
} else { | |||
debug("unexpected scan code at F0: "); debug_hex(code); debug("\n"); | |||
} | |||
state = INIT; | |||
} | |||
break; | |||
case E0_F0: // E0 break | |||
switch (code) { | |||
case 0x12: // postfix/postfix code for exceptional keys | |||
case 0x59: // postfix/postfix code for exceptional keys | |||
case 0x7E: // latter part of Control-Pause[E0 7E E0 F0 7E] | |||
// ignore | |||
state = INIT; | |||
break; | |||
default: | |||
if (code < 0x80) { | |||
matrix_break(code|0x80); | |||
} else { | |||
debug("unexpected scan code at E0_F0: "); debug_hex(code); debug("\n"); | |||
} | |||
state = INIT; | |||
} | |||
break; | |||
/* Pause */ | |||
case E1: | |||
switch (code) { | |||
case 0x14: | |||
state = E1_14; | |||
break; | |||
default: | |||
state = INIT; | |||
} | |||
break; | |||
case E1_14: | |||
switch (code) { | |||
case 0x77: | |||
state = E1_14_77; | |||
break; | |||
default: | |||
state = INIT; | |||
} | |||
break; | |||
case E1_14_77: | |||
switch (code) { | |||
case 0xE1: | |||
state = E1_14_77_E1; | |||
break; | |||
default: | |||
state = INIT; | |||
} | |||
break; | |||
case E1_14_77_E1: | |||
switch (code) { | |||
case 0xF0: | |||
state = E1_14_77_E1_F0; | |||
break; | |||
default: | |||
state = INIT; | |||
} | |||
break; | |||
case E1_14_77_E1_F0: | |||
switch (code) { | |||
case 0x14: | |||
state = E1_14_77_E1_F0_14; | |||
break; | |||
default: | |||
state = INIT; | |||
} | |||
break; | |||
case E1_14_77_E1_F0_14: | |||
switch (code) { | |||
case 0xF0: | |||
state = E1_14_77_E1_F0_14_F0; | |||
break; | |||
default: | |||
state = INIT; | |||
} | |||
break; | |||
case E1_14_77_E1_F0_14_F0: | |||
switch (code) { | |||
case 0x77: | |||
matrix_make(PAUSE); | |||
state = INIT; | |||
break; | |||
default: | |||
state = INIT; | |||
} | |||
break; | |||
default: | |||
state = INIT; | |||
} | |||
} | |||
// handle LED indicators | |||
/* | |||
static uint8_t prev_leds = 0; | |||
if (prev_leds != usb_keyboard_leds) { | |||
uint8_t leds = 0; | |||
if (usb_keyboard_leds&(1<<USB_LED_SCROLL_LOCK)) | |||
leds |= (1<<PS2_LED_SCROLL_LOCK); | |||
if (usb_keyboard_leds&(1<<USB_LED_NUM_LOCK)) | |||
leds |= (1<<PS2_LED_NUM_LOCK); | |||
if (usb_keyboard_leds&(1<<USB_LED_CAPS_LOCK)) | |||
leds |= (1<<PS2_LED_CAPS_LOCK); | |||
ps2_set_leds(leds); | |||
prev_leds = usb_keyboard_leds; | |||
} | |||
*/ | |||
return 1; | |||
} | |||
bool matrix_is_modified(void) | |||
{ | |||
return is_modified; | |||
} | |||
inline | |||
bool matrix_has_ghost(void) | |||
{ | |||
#ifdef MATRIX_HAS_GHOST | |||
for (uint8_t i = 0; i < MATRIX_ROWS; i++) { | |||
if (matrix_has_ghost_in_row(i)) | |||
return true; | |||
} | |||
#endif | |||
return false; | |||
} | |||
inline | |||
bool matrix_is_on(uint8_t row, uint8_t col) | |||
{ | |||
return (matrix[row] & (1<<col)); | |||
} | |||
inline | |||
#if (MATRIX_COLS <= 8) | |||
uint8_t matrix_get_row(uint8_t row) | |||
#else | |||
uint16_t matrix_get_row(uint8_t row) | |||
#endif | |||
{ | |||
return matrix[row]; | |||
} | |||
void matrix_print(void) | |||
{ | |||
#if (MATRIX_COLS <= 8) | |||
print("\nr/c 01234567\n"); | |||
#else | |||
print("\nr/c 0123456789ABCDEF\n"); | |||
#endif | |||
for (uint8_t row = 0; row < matrix_rows(); row++) { | |||
phex(row); print(": "); | |||
#if (MATRIX_COLS <= 8) | |||
pbin_reverse(matrix_get_row(row)); | |||
#else | |||
pbin_reverse16(matrix_get_row(row)); | |||
#endif | |||
#ifdef MATRIX_HAS_GHOST | |||
if (matrix_has_ghost_in_row(row)) { | |||
print(" <ghost"); | |||
} | |||
#endif | |||
print("\n"); | |||
} | |||
} | |||
uint8_t matrix_key_count(void) | |||
{ | |||
uint8_t count = 0; | |||
for (uint8_t i = 0; i < MATRIX_ROWS; i++) { | |||
#if (MATRIX_COLS <= 8) | |||
count += bitpop(matrix[i]); | |||
#else | |||
count += bitpop16(matrix[i]); | |||
#endif | |||
} | |||
return count; | |||
} | |||
#ifdef MATRIX_HAS_GHOST | |||
inline | |||
static bool matrix_has_ghost_in_row(uint8_t row) | |||
{ | |||
// no ghost exists in case less than 2 keys on | |||
if (((matrix[row] - 1) & matrix[row]) == 0) | |||
return false; | |||
// ghost exists in case same state as other row | |||
for (uint8_t i=0; i < MATRIX_ROWS; i++) { | |||
if (i != row && (matrix[i] & matrix[row]) == matrix[row]) | |||
return true; | |||
} | |||
return false; | |||
} | |||
#endif | |||
inline | |||
static void matrix_make(uint8_t code) | |||
{ | |||
if (!matrix_is_on(ROW(code), COL(code))) { | |||
matrix[ROW(code)] |= 1<<COL(code); | |||
is_modified = true; | |||
} | |||
} | |||
inline | |||
static void matrix_break(uint8_t code) | |||
{ | |||
if (matrix_is_on(ROW(code), COL(code))) { | |||
matrix[ROW(code)] &= ~(1<<COL(code)); | |||
is_modified = true; | |||
} | |||
} | |||
static void ps2_reset(void) | |||
{ | |||
ps2_host_send(0xFF); | |||
if (ps2_host_recv() != 0xFA) return; | |||
_delay_ms(1000); | |||
if (ps2_host_recv() != 0xAA) return; | |||
} | |||
static void ps2_set_leds(uint8_t leds) | |||
{ | |||
ps2_host_send(0xED); | |||
if (ps2_host_recv() != 0xFA) return; // 0xFA | |||
ps2_host_send(leds); | |||
if (ps2_host_recv() != 0xFA) return; // 0xFA | |||
} |
@@ -0,0 +1,53 @@ | |||
/* Name: oddebug.c | |||
* Project: AVR library | |||
* Author: Christian Starkjohann | |||
* Creation Date: 2005-01-16 | |||
* Tabsize: 4 | |||
* Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH | |||
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) | |||
* This Revision: $Id: oddebug.c 692 2008-11-07 15:07:40Z cs $ | |||
*/ | |||
#include "usart_print.h" | |||
#include "sendchar.h" | |||
int8_t sendchar(uint8_t c) | |||
{ | |||
while(!(ODDBG_USR & (1 << ODDBG_UDRE))); /* wait for data register empty */ | |||
ODDBG_UDR = c; | |||
return 0; | |||
} | |||
void uartPutc(char c) | |||
{ | |||
while(!(ODDBG_USR & (1 << ODDBG_UDRE))); /* wait for data register empty */ | |||
ODDBG_UDR = c; | |||
} | |||
static uchar hexAscii(uchar h) | |||
{ | |||
h &= 0xf; | |||
if(h >= 10) | |||
h += 'a' - (uchar)10 - '0'; | |||
h += '0'; | |||
return h; | |||
} | |||
void printHex(uchar c) | |||
{ | |||
uartPutc(hexAscii(c >> 4)); | |||
uartPutc(hexAscii(c)); | |||
} | |||
void odDebug(uchar prefix, uchar *data, uchar len) | |||
{ | |||
printHex(prefix); | |||
uartPutc(':'); | |||
while(len--){ | |||
uartPutc(' '); | |||
printHex(*data++); | |||
} | |||
uartPutc('\r'); | |||
uartPutc('\n'); | |||
} |
@@ -0,0 +1,121 @@ | |||
/* Name: oddebug.h | |||
* Project: AVR library | |||
* Author: Christian Starkjohann | |||
* Creation Date: 2005-01-16 | |||
* Tabsize: 4 | |||
* Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH | |||
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) | |||
* This Revision: $Id: oddebug.h 692 2008-11-07 15:07:40Z cs $ | |||
*/ | |||
#ifndef __oddebug_h_included__ | |||
#define __oddebug_h_included__ | |||
/* | |||
General Description: | |||
This module implements a function for debug logs on the serial line of the | |||
AVR microcontroller. Debugging can be configured with the define | |||
'DEBUG_LEVEL'. If this macro is not defined or defined to 0, all debugging | |||
calls are no-ops. If it is 1, DBG1 logs will appear, but not DBG2. If it is | |||
2, DBG1 and DBG2 logs will be printed. | |||
A debug log consists of a label ('prefix') to indicate which debug log created | |||
the output and a memory block to dump in hex ('data' and 'len'). | |||
*/ | |||
#ifndef F_CPU | |||
# define F_CPU 12000000 /* 12 MHz */ | |||
#endif | |||
/* make sure we have the UART defines: */ | |||
#include "usbportability.h" | |||
#ifndef uchar | |||
# define uchar unsigned char | |||
#endif | |||
#if DEBUG_LEVEL > 0 && !(defined TXEN || defined TXEN0) /* no UART in device */ | |||
# warning "Debugging disabled because device has no UART" | |||
# undef DEBUG_LEVEL | |||
#endif | |||
#ifndef DEBUG_LEVEL | |||
# define DEBUG_LEVEL 0 | |||
#endif | |||
/* ------------------------------------------------------------------------- */ | |||
#if DEBUG_LEVEL > 0 | |||
# define DBG1(prefix, data, len) odDebug(prefix, data, len) | |||
#else | |||
# define DBG1(prefix, data, len) | |||
#endif | |||
#if DEBUG_LEVEL > 1 | |||
# define DBG2(prefix, data, len) odDebug(prefix, data, len) | |||
#else | |||
# define DBG2(prefix, data, len) | |||
#endif | |||
/* ------------------------------------------------------------------------- */ | |||
extern void odDebug(uchar prefix, uchar *data, uchar len); | |||
void uartPutc(char c); | |||
void printHex(uchar c); | |||
/* Try to find our control registers; ATMEL likes to rename these */ | |||
#if defined UBRR | |||
# define ODDBG_UBRR UBRR | |||
#elif defined UBRRL | |||
# define ODDBG_UBRR UBRRL | |||
#elif defined UBRR0 | |||
# define ODDBG_UBRR UBRR0 | |||
#elif defined UBRR0L | |||
# define ODDBG_UBRR UBRR0L | |||
#endif | |||
#if defined UCR | |||
# define ODDBG_UCR UCR | |||
#elif defined UCSRB | |||
# define ODDBG_UCR UCSRB | |||
#elif defined UCSR0B | |||
# define ODDBG_UCR UCSR0B | |||
#endif | |||
#if defined TXEN | |||
# define ODDBG_TXEN TXEN | |||
#else | |||
# define ODDBG_TXEN TXEN0 | |||
#endif | |||
#if defined USR | |||
# define ODDBG_USR USR | |||
#elif defined UCSRA | |||
# define ODDBG_USR UCSRA | |||
#elif defined UCSR0A | |||
# define ODDBG_USR UCSR0A | |||
#endif | |||
#if defined UDRE | |||
# define ODDBG_UDRE UDRE | |||
#else | |||
# define ODDBG_UDRE UDRE0 | |||
#endif | |||
#if defined UDR | |||
# define ODDBG_UDR UDR | |||
#elif defined UDR0 | |||
# define ODDBG_UDR UDR0 | |||
#endif | |||
static inline void odDebugInit(void) | |||
{ | |||
ODDBG_UCR |= (1<<ODDBG_TXEN); | |||
ODDBG_UBRR = F_CPU / (57600 * 16L) - 1; | |||
} | |||
/* ------------------------------------------------------------------------- */ | |||
#endif /* __oddebug_h_included__ */ |
@@ -0,0 +1,373 @@ | |||
/* Name: usbconfig.h | |||
* Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers | |||
* Author: Christian Starkjohann | |||
* Creation Date: 2005-04-01 | |||
* Tabsize: 4 | |||
* Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH | |||
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) | |||
* This Revision: $Id: usbconfig-prototype.h 785 2010-05-30 17:57:07Z cs $ | |||
*/ | |||
#ifndef __usbconfig_h_included__ | |||
#define __usbconfig_h_included__ | |||
/* | |||
General Description: | |||
This file is an example configuration (with inline documentation) for the USB | |||
driver. It configures V-USB for USB D+ connected to Port D bit 2 (which is | |||
also hardware interrupt 0 on many devices) and USB D- to Port D bit 4. You may | |||
wire the lines to any other port, as long as D+ is also wired to INT0 (or any | |||
other hardware interrupt, as long as it is the highest level interrupt, see | |||
section at the end of this file). | |||
*/ | |||
/* ---------------------------- Hardware Config ---------------------------- */ | |||
#define USB_CFG_IOPORTNAME D | |||
/* This is the port where the USB bus is connected. When you configure it to | |||
* "B", the registers PORTB, PINB and DDRB will be used. | |||
*/ | |||
#define USB_CFG_DMINUS_BIT 4 | |||
/* This is the bit number in USB_CFG_IOPORT where the USB D- line is connected. | |||
* This may be any bit in the port. | |||
*/ | |||
#define USB_CFG_DPLUS_BIT 2 | |||
/* This is the bit number in USB_CFG_IOPORT where the USB D+ line is connected. | |||
* This may be any bit in the port. Please note that D+ must also be connected | |||
* to interrupt pin INT0! [You can also use other interrupts, see section | |||
* "Optional MCU Description" below, or you can connect D- to the interrupt, as | |||
* it is required if you use the USB_COUNT_SOF feature. If you use D- for the | |||
* interrupt, the USB interrupt will also be triggered at Start-Of-Frame | |||
* markers every millisecond.] | |||
*/ | |||
#define USB_CFG_CLOCK_KHZ (F_CPU/1000) | |||
/* Clock rate of the AVR in kHz. Legal values are 12000, 12800, 15000, 16000, | |||
* 16500, 18000 and 20000. The 12.8 MHz and 16.5 MHz versions of the code | |||
* require no crystal, they tolerate +/- 1% deviation from the nominal | |||
* frequency. All other rates require a precision of 2000 ppm and thus a | |||
* crystal! | |||
* Since F_CPU should be defined to your actual clock rate anyway, you should | |||
* not need to modify this setting. | |||
*/ | |||
#define USB_CFG_CHECK_CRC 0 | |||
/* Define this to 1 if you want that the driver checks integrity of incoming | |||
* data packets (CRC checks). CRC checks cost quite a bit of code size and are | |||
* currently only available for 18 MHz crystal clock. You must choose | |||
* USB_CFG_CLOCK_KHZ = 18000 if you enable this option. | |||
*/ | |||
/* ----------------------- Optional Hardware Config ------------------------ */ | |||
/* #define USB_CFG_PULLUP_IOPORTNAME D */ | |||
/* If you connect the 1.5k pullup resistor from D- to a port pin instead of | |||
* V+, you can connect and disconnect the device from firmware by calling | |||
* the macros usbDeviceConnect() and usbDeviceDisconnect() (see usbdrv.h). | |||
* This constant defines the port on which the pullup resistor is connected. | |||
*/ | |||
/* #define USB_CFG_PULLUP_BIT 4 */ | |||
/* This constant defines the bit number in USB_CFG_PULLUP_IOPORT (defined | |||
* above) where the 1.5k pullup resistor is connected. See description | |||
* above for details. | |||
*/ | |||
/* --------------------------- Functional Range ---------------------------- */ | |||
#define USB_CFG_HAVE_INTRIN_ENDPOINT 1 | |||
/* Define this to 1 if you want to compile a version with two endpoints: The | |||
* default control endpoint 0 and an interrupt-in endpoint (any other endpoint | |||
* number). | |||
*/ | |||
#define USB_CFG_HAVE_INTRIN_ENDPOINT3 0 | |||
/* Define this to 1 if you want to compile a version with three endpoints: The | |||
* default control endpoint 0, an interrupt-in endpoint 3 (or the number | |||
* configured below) and a catch-all default interrupt-in endpoint as above. | |||
* You must also define USB_CFG_HAVE_INTRIN_ENDPOINT to 1 for this feature. | |||
*/ | |||
#define USB_CFG_EP3_NUMBER 3 | |||
/* If the so-called endpoint 3 is used, it can now be configured to any other | |||
* endpoint number (except 0) with this macro. Default if undefined is 3. | |||
*/ | |||
/* #define USB_INITIAL_DATATOKEN USBPID_DATA1 */ | |||
/* The above macro defines the startup condition for data toggling on the | |||
* interrupt/bulk endpoints 1 and 3. Defaults to USBPID_DATA1. | |||
* Since the token is toggled BEFORE sending any data, the first packet is | |||
* sent with the oposite value of this configuration! | |||
*/ | |||
#define USB_CFG_IMPLEMENT_HALT 0 | |||
/* Define this to 1 if you also want to implement the ENDPOINT_HALT feature | |||
* for endpoint 1 (interrupt endpoint). Although you may not need this feature, | |||
* it is required by the standard. We have made it a config option because it | |||
* bloats the code considerably. | |||
*/ | |||
#define USB_CFG_SUPPRESS_INTR_CODE 0 | |||
/* Define this to 1 if you want to declare interrupt-in endpoints, but don't | |||
* want to send any data over them. If this macro is defined to 1, functions | |||
* usbSetInterrupt() and usbSetInterrupt3() are omitted. This is useful if | |||
* you need the interrupt-in endpoints in order to comply to an interface | |||
* (e.g. HID), but never want to send any data. This option saves a couple | |||
* of bytes in flash memory and the transmit buffers in RAM. | |||
*/ | |||
#define USB_CFG_INTR_POLL_INTERVAL 10 | |||
/* If you compile a version with endpoint 1 (interrupt-in), this is the poll | |||
* interval. The value is in milliseconds and must not be less than 10 ms for | |||
* low speed devices. | |||
*/ | |||
#define USB_CFG_IS_SELF_POWERED 0 | |||
/* Define this to 1 if the device has its own power supply. Set it to 0 if the | |||
* device is powered from the USB bus. | |||
*/ | |||
#define USB_CFG_MAX_BUS_POWER 100 | |||
/* Set this variable to the maximum USB bus power consumption of your device. | |||
* The value is in milliamperes. [It will be divided by two since USB | |||
* communicates power requirements in units of 2 mA.] | |||
*/ | |||
#define USB_CFG_IMPLEMENT_FN_WRITE 0 | |||
/* Set this to 1 if you want usbFunctionWrite() to be called for control-out | |||
* transfers. Set it to 0 if you don't need it and want to save a couple of | |||
* bytes. | |||
*/ | |||
#define USB_CFG_IMPLEMENT_FN_READ 0 | |||
/* Set this to 1 if you need to send control replies which are generated | |||
* "on the fly" when usbFunctionRead() is called. If you only want to send | |||
* data from a static buffer, set it to 0 and return the data from | |||
* usbFunctionSetup(). This saves a couple of bytes. | |||
*/ | |||
#define USB_CFG_IMPLEMENT_FN_WRITEOUT 0 | |||
/* Define this to 1 if you want to use interrupt-out (or bulk out) endpoints. | |||
* You must implement the function usbFunctionWriteOut() which receives all | |||
* interrupt/bulk data sent to any endpoint other than 0. The endpoint number | |||
* can be found in 'usbRxToken'. | |||
*/ | |||
#define USB_CFG_HAVE_FLOWCONTROL 0 | |||
/* Define this to 1 if you want flowcontrol over USB data. See the definition | |||
* of the macros usbDisableAllRequests() and usbEnableAllRequests() in | |||
* usbdrv.h. | |||
*/ | |||
#define USB_CFG_DRIVER_FLASH_PAGE 0 | |||
/* If the device has more than 64 kBytes of flash, define this to the 64 k page | |||
* where the driver's constants (descriptors) are located. Or in other words: | |||
* Define this to 1 for boot loaders on the ATMega128. | |||
*/ | |||
#define USB_CFG_LONG_TRANSFERS 0 | |||
/* Define this to 1 if you want to send/receive blocks of more than 254 bytes | |||
* in a single control-in or control-out transfer. Note that the capability | |||
* for long transfers increases the driver size. | |||
*/ | |||
/* #define USB_RX_USER_HOOK(data, len) if(usbRxToken == (uchar)USBPID_SETUP) blinkLED(); */ | |||
/* This macro is a hook if you want to do unconventional things. If it is | |||
* defined, it's inserted at the beginning of received message processing. | |||
* If you eat the received message and don't want default processing to | |||
* proceed, do a return after doing your things. One possible application | |||
* (besides debugging) is to flash a status LED on each packet. | |||
*/ | |||
/* #define USB_RESET_HOOK(resetStarts) if(!resetStarts){hadUsbReset();} */ | |||
/* This macro is a hook if you need to know when an USB RESET occurs. It has | |||
* one parameter which distinguishes between the start of RESET state and its | |||
* end. | |||
*/ | |||
/* #define USB_SET_ADDRESS_HOOK() hadAddressAssigned(); */ | |||
/* This macro (if defined) is executed when a USB SET_ADDRESS request was | |||
* received. | |||
*/ | |||
#define USB_COUNT_SOF 0 | |||
/* define this macro to 1 if you need the global variable "usbSofCount" which | |||
* counts SOF packets. This feature requires that the hardware interrupt is | |||
* connected to D- instead of D+. | |||
*/ | |||
/* #ifdef __ASSEMBLER__ | |||
* macro myAssemblerMacro | |||
* in YL, TCNT0 | |||
* sts timer0Snapshot, YL | |||
* endm | |||
* #endif | |||
* #define USB_SOF_HOOK myAssemblerMacro | |||
* This macro (if defined) is executed in the assembler module when a | |||
* Start Of Frame condition is detected. It is recommended to define it to | |||
* the name of an assembler macro which is defined here as well so that more | |||
* than one assembler instruction can be used. The macro may use the register | |||
* YL and modify SREG. If it lasts longer than a couple of cycles, USB messages | |||
* immediately after an SOF pulse may be lost and must be retried by the host. | |||
* What can you do with this hook? Since the SOF signal occurs exactly every | |||
* 1 ms (unless the host is in sleep mode), you can use it to tune OSCCAL in | |||
* designs running on the internal RC oscillator. | |||
* Please note that Start Of Frame detection works only if D- is wired to the | |||
* interrupt, not D+. THIS IS DIFFERENT THAN MOST EXAMPLES! | |||
*/ | |||
#define USB_CFG_CHECK_DATA_TOGGLING 0 | |||
/* define this macro to 1 if you want to filter out duplicate data packets | |||
* sent by the host. Duplicates occur only as a consequence of communication | |||
* errors, when the host does not receive an ACK. Please note that you need to | |||
* implement the filtering yourself in usbFunctionWriteOut() and | |||
* usbFunctionWrite(). Use the global usbCurrentDataToken and a static variable | |||
* for each control- and out-endpoint to check for duplicate packets. | |||
*/ | |||
#define USB_CFG_HAVE_MEASURE_FRAME_LENGTH 0 | |||
/* define this macro to 1 if you want the function usbMeasureFrameLength() | |||
* compiled in. This function can be used to calibrate the AVR's RC oscillator. | |||
*/ | |||
#define USB_USE_FAST_CRC 0 | |||
/* The assembler module has two implementations for the CRC algorithm. One is | |||
* faster, the other is smaller. This CRC routine is only used for transmitted | |||
* messages where timing is not critical. The faster routine needs 31 cycles | |||
* per byte while the smaller one needs 61 to 69 cycles. The faster routine | |||
* may be worth the 32 bytes bigger code size if you transmit lots of data and | |||
* run the AVR close to its limit. | |||
*/ | |||
/* -------------------------- Device Description --------------------------- */ | |||
#define USB_CFG_VENDOR_ID 0xc0, 0x16 /* = 0x16c0 = 5824 = voti.nl */ | |||
/* USB vendor ID for the device, low byte first. If you have registered your | |||
* own Vendor ID, define it here. Otherwise you may use one of obdev's free | |||
* shared VID/PID pairs. Be sure to read USB-IDs-for-free.txt for rules! | |||
* *** IMPORTANT NOTE *** | |||
* This template uses obdev's shared VID/PID pair for Vendor Class devices | |||
* with libusb: 0x16c0/0x5dc. Use this VID/PID pair ONLY if you understand | |||
* the implications! | |||
*/ | |||
#define USB_CFG_DEVICE_ID 0xdd, 0x05 /* = 0x05dc = 1500 */ | |||
/* This is the ID of the product, low byte first. It is interpreted in the | |||
* scope of the vendor ID. If you have registered your own VID with usb.org | |||
* or if you have licensed a PID from somebody else, define it here. Otherwise | |||
* you may use one of obdev's free shared VID/PID pairs. See the file | |||
* USB-IDs-for-free.txt for details! | |||
* *** IMPORTANT NOTE *** | |||
* This template uses obdev's shared VID/PID pair for Vendor Class devices | |||
* with libusb: 0x16c0/0x5dc. Use this VID/PID pair ONLY if you understand | |||
* the implications! | |||
*/ | |||
#define USB_CFG_DEVICE_VERSION 0x00, 0x01 | |||
/* Version number of the device: Minor number first, then major number. | |||
*/ | |||
#define USB_CFG_VENDOR_NAME 't', '.', 'm', '.', 'k', '.' | |||
#define USB_CFG_VENDOR_NAME_LEN 6 | |||
/* These two values define the vendor name returned by the USB device. The name | |||
* must be given as a list of characters under single quotes. The characters | |||
* are interpreted as Unicode (UTF-16) entities. | |||
* If you don't want a vendor name string, undefine these macros. | |||
* ALWAYS define a vendor name containing your Internet domain name if you use | |||
* obdev's free shared VID/PID pair. See the file USB-IDs-for-free.txt for | |||
* details. | |||
*/ | |||
#define USB_CFG_DEVICE_NAME 'k', 'e', 'y', 'b', 'o', 'a', 'r', 'd' | |||
#define USB_CFG_DEVICE_NAME_LEN 8 | |||
/* Same as above for the device name. If you don't want a device name, undefine | |||
* the macros. See the file USB-IDs-for-free.txt before you assign a name if | |||
* you use a shared VID/PID. | |||
*/ | |||
/*#define USB_CFG_SERIAL_NUMBER 'N', 'o', 'n', 'e' */ | |||
/*#define USB_CFG_SERIAL_NUMBER_LEN 0 */ | |||
/* Same as above for the serial number. If you don't want a serial number, | |||
* undefine the macros. | |||
* It may be useful to provide the serial number through other means than at | |||
* compile time. See the section about descriptor properties below for how | |||
* to fine tune control over USB descriptors such as the string descriptor | |||
* for the serial number. | |||
*/ | |||
#define USB_CFG_DEVICE_CLASS 0 | |||
#define USB_CFG_DEVICE_SUBCLASS 0 | |||
/* See USB specification if you want to conform to an existing device class. | |||
* Class 0xff is "vendor specific". | |||
*/ | |||
#define USB_CFG_INTERFACE_CLASS 3 /* HID */ | |||
#define USB_CFG_INTERFACE_SUBCLASS 1 /* Boot */ | |||
#define USB_CFG_INTERFACE_PROTOCOL 1 /* Keyboard */ | |||
/* See USB specification if you want to conform to an existing device class or | |||
* protocol. The following classes must be set at interface level: | |||
* HID class is 3, no subclass and protocol required (but may be useful!) | |||
* CDC class is 2, use subclass 2 and protocol 1 for ACM | |||
*/ | |||
#define USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH 63 | |||
/* Define this to the length of the HID report descriptor, if you implement | |||
* an HID device. Otherwise don't define it or define it to 0. | |||
* If you use this define, you must add a PROGMEM character array named | |||
* "usbHidReportDescriptor" to your code which contains the report descriptor. | |||
* Don't forget to keep the array and this define in sync! | |||
*/ | |||
/* #define USB_PUBLIC static */ | |||
/* Use the define above if you #include usbdrv.c instead of linking against it. | |||
* This technique saves a couple of bytes in flash memory. | |||
*/ | |||
/* ------------------- Fine Control over USB Descriptors ------------------- */ | |||
/* If you don't want to use the driver's default USB descriptors, you can | |||
* provide our own. These can be provided as (1) fixed length static data in | |||
* flash memory, (2) fixed length static data in RAM or (3) dynamically at | |||
* runtime in the function usbFunctionDescriptor(). See usbdrv.h for more | |||
* information about this function. | |||
* Descriptor handling is configured through the descriptor's properties. If | |||
* no properties are defined or if they are 0, the default descriptor is used. | |||
* Possible properties are: | |||
* + USB_PROP_IS_DYNAMIC: The data for the descriptor should be fetched | |||
* at runtime via usbFunctionDescriptor(). If the usbMsgPtr mechanism is | |||
* used, the data is in FLASH by default. Add property USB_PROP_IS_RAM if | |||
* you want RAM pointers. | |||
* + USB_PROP_IS_RAM: The data returned by usbFunctionDescriptor() or found | |||
* in static memory is in RAM, not in flash memory. | |||
* + USB_PROP_LENGTH(len): If the data is in static memory (RAM or flash), | |||
* the driver must know the descriptor's length. The descriptor itself is | |||
* found at the address of a well known identifier (see below). | |||
* List of static descriptor names (must be declared PROGMEM if in flash): | |||
* char usbDescriptorDevice[]; | |||
* char usbDescriptorConfiguration[]; | |||
* char usbDescriptorHidReport[]; | |||
* char usbDescriptorString0[]; | |||
* int usbDescriptorStringVendor[]; | |||
* int usbDescriptorStringDevice[]; | |||
* int usbDescriptorStringSerialNumber[]; | |||
* Other descriptors can't be provided statically, they must be provided | |||
* dynamically at runtime. | |||
* | |||
* Descriptor properties are or-ed or added together, e.g.: | |||
* #define USB_CFG_DESCR_PROPS_DEVICE (USB_PROP_IS_RAM | USB_PROP_LENGTH(18)) | |||
* | |||
* The following descriptors are defined: | |||
* USB_CFG_DESCR_PROPS_DEVICE | |||
* USB_CFG_DESCR_PROPS_CONFIGURATION | |||
* USB_CFG_DESCR_PROPS_STRINGS | |||
* USB_CFG_DESCR_PROPS_STRING_0 | |||
* USB_CFG_DESCR_PROPS_STRING_VENDOR | |||
* USB_CFG_DESCR_PROPS_STRING_PRODUCT | |||
* USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER | |||
* USB_CFG_DESCR_PROPS_HID | |||
* USB_CFG_DESCR_PROPS_HID_REPORT | |||
* USB_CFG_DESCR_PROPS_UNKNOWN (for all descriptors not handled by the driver) | |||
* | |||
* Note about string descriptors: String descriptors are not just strings, they | |||
* are Unicode strings prefixed with a 2 byte header. Example: | |||
* int serialNumberDescriptor[] = { | |||
* USB_STRING_DESCRIPTOR_HEADER(6), | |||
* 'S', 'e', 'r', 'i', 'a', 'l' | |||
* }; | |||
*/ | |||
#define USB_CFG_DESCR_PROPS_DEVICE 0 | |||
#define USB_CFG_DESCR_PROPS_CONFIGURATION 0 | |||
#define USB_CFG_DESCR_PROPS_STRINGS 0 | |||
#define USB_CFG_DESCR_PROPS_STRING_0 0 | |||
#define USB_CFG_DESCR_PROPS_STRING_VENDOR 0 | |||
#define USB_CFG_DESCR_PROPS_STRING_PRODUCT 0 | |||
#define USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER 0 | |||
#define USB_CFG_DESCR_PROPS_HID 0 | |||
#define USB_CFG_DESCR_PROPS_HID_REPORT 0 | |||
#define USB_CFG_DESCR_PROPS_UNKNOWN 0 | |||
/* ----------------------- Optional MCU Description ------------------------ */ | |||
/* The following configurations have working defaults in usbdrv.h. You | |||
* usually don't need to set them explicitly. Only if you want to run | |||
* the driver on a device which is not yet supported or with a compiler | |||
* which is not fully supported (such as IAR C) or if you use a differnt | |||
* interrupt than INT0, you may have to define some of these. | |||
*/ | |||
/* #define USB_INTR_CFG MCUCR */ | |||
/* #define USB_INTR_CFG_SET ((1 << ISC00) | (1 << ISC01)) */ | |||
/* #define USB_INTR_CFG_CLR 0 */ | |||
/* #define USB_INTR_ENABLE GIMSK */ | |||
/* #define USB_INTR_ENABLE_BIT INT0 */ | |||
/* #define USB_INTR_PENDING GIFR */ | |||
/* #define USB_INTR_PENDING_BIT INTF0 */ | |||
/* #define USB_INTR_VECTOR INT0_vect */ | |||
#endif /* __usbconfig_h_included__ */ |
@@ -0,0 +1,8 @@ | |||
#ifndef SENDCHAR_H | |||
#define SENDCHAR_H | |||
/* transmit a character. return 0 on success, -1 on error. */ | |||
int8_t sendchar(uint8_t c); | |||
#endif | |||
@@ -3,7 +3,7 @@ | |||
#include <stdint.h> | |||
#include "timer.h" | |||
uint16_t timer_count = 0; | |||
volatile uint16_t timer_count = 0; | |||
// Configure timer 0 to generate a timer overflow interrupt every | |||
// 256*1024 clock cycles, or approx 61 Hz when using 16 MHz clock | |||
@@ -19,20 +19,21 @@ void timer_init(void) | |||
inline | |||
void timer_clear(void) | |||
{ | |||
uint8_t sreg = SREG; | |||
cli(); | |||
timer_count = 0; | |||
sei(); | |||
SREG = sreg; | |||
} | |||
inline | |||
uint16_t timer_read(void) | |||
{ | |||
uint8_t _sreg = SREG; | |||
uint16_t t; | |||
uint8_t sreg = SREG; | |||
cli(); | |||
t = timer_count; | |||
SREG = _sreg; | |||
SREG = sreg; | |||
return t; | |||
} | |||
@@ -40,12 +41,12 @@ uint16_t timer_read(void) | |||
inline | |||
uint16_t timer_elapsed(uint16_t last) | |||
{ | |||
uint8_t _sreg = SREG; | |||
uint16_t t; | |||
uint8_t sreg = SREG; | |||
cli(); | |||
t = timer_count; | |||
SREG = _sreg; | |||
SREG = sreg; | |||
return TIMER_DIFF(t, last); | |||
} | |||
@@ -58,4 +59,3 @@ ISR(TIMER0_OVF_vect) | |||
{ | |||
timer_count++; | |||
} | |||
@@ -6,7 +6,7 @@ | |||
#define TIMER_DIFF(a, b) ((a) >= (b) ? (a) - (b) : UINT16_MAX - (b) + (a)) | |||
extern uint16_t timer_count; | |||
extern volatile uint16_t timer_count; | |||
void timer_init(void); |
@@ -0,0 +1,308 @@ | |||
This file documents changes in the firmware-only USB driver for atmel's AVR | |||
microcontrollers. New entries are always appended to the end of the file. | |||
Scroll down to the bottom to see the most recent changes. | |||
2005-04-01: | |||
- Implemented endpoint 1 as interrupt-in endpoint. | |||
- Moved all configuration options to usbconfig.h which is not part of the | |||
driver. | |||
- Changed interface for usbVendorSetup(). | |||
- Fixed compatibility with ATMega8 device. | |||
- Various minor optimizations. | |||
2005-04-11: | |||
- Changed interface to application: Use usbFunctionSetup(), usbFunctionRead() | |||
and usbFunctionWrite() now. Added configuration options to choose which | |||
of these functions to compile in. | |||
- Assembler module delivers receive data non-inverted now. | |||
- Made register and bit names compatible with more AVR devices. | |||
2005-05-03: | |||
- Allow address of usbRxBuf on any memory page as long as the buffer does | |||
not cross 256 byte page boundaries. | |||
- Better device compatibility: works with Mega88 now. | |||
- Code optimization in debugging module. | |||
- Documentation updates. | |||
2006-01-02: | |||
- Added (free) default Vendor- and Product-IDs bought from voti.nl. | |||
- Added USBID-License.txt file which defines the rules for using the free | |||
shared VID/PID pair. | |||
- Added Readme.txt to the usbdrv directory which clarifies administrative | |||
issues. | |||
2006-01-25: | |||
- Added "configured state" to become more standards compliant. | |||
- Added "HALT" state for interrupt endpoint. | |||
- Driver passes the "USB Command Verifier" test from usb.org now. | |||
- Made "serial number" a configuration option. | |||
- Minor optimizations, we now recommend compiler option "-Os" for best | |||
results. | |||
- Added a version number to usbdrv.h | |||
2006-02-03: | |||
- New configuration variable USB_BUFFER_SECTION for the memory section where | |||
the USB rx buffer will go. This defaults to ".bss" if not defined. Since | |||
this buffer MUST NOT cross 256 byte pages (not even touch a page at the | |||
end), the user may want to pass a linker option similar to | |||
"-Wl,--section-start=.mybuffer=0x800060". | |||
- Provide structure for usbRequest_t. | |||
- New defines for USB constants. | |||
- Prepared for HID implementations. | |||
- Increased data size limit for interrupt transfers to 8 bytes. | |||
- New macro usbInterruptIsReady() to query interrupt buffer state. | |||
2006-02-18: | |||
- Ensure that the data token which is sent as an ack to an OUT transfer is | |||
always zero sized. This fixes a bug where the host reports an error after | |||
sending an out transfer to the device, although all data arrived at the | |||
device. | |||
- Updated docs in usbdrv.h to reflect changed API in usbFunctionWrite(). | |||
* Release 2006-02-20 | |||
- Give a compiler warning when compiling with debugging turned on. | |||
- Added Oleg Semyonov's changes for IAR-cc compatibility. | |||
- Added new (optional) functions usbDeviceConnect() and usbDeviceDisconnect() | |||
(also thanks to Oleg!). | |||
- Rearranged tests in usbPoll() to save a couple of instructions in the most | |||
likely case that no actions are pending. | |||
- We need a delay between the SET ADDRESS request until the new address | |||
becomes active. This delay was handled in usbPoll() until now. Since the | |||
spec says that the delay must not exceed 2ms, previous versions required | |||
aggressive polling during the enumeration phase. We have now moved the | |||
handling of the delay into the interrupt routine. | |||
- We must not reply with NAK to a SETUP transaction. We can only achieve this | |||
by making sure that the rx buffer is empty when SETUP tokens are expected. | |||
We therefore don't pass zero sized data packets from the status phase of | |||
a transfer to usbPoll(). This change MAY cause troubles if you rely on | |||
receiving a less than 8 bytes long packet in usbFunctionWrite() to | |||
identify the end of a transfer. usbFunctionWrite() will NEVER be called | |||
with a zero length. | |||
* Release 2006-03-14 | |||
- Improved IAR C support: tiny memory model, more devices | |||
- Added template usbconfig.h file under the name usbconfig-prototype.h | |||
* Release 2006-03-26 | |||
- Added provision for one more interrupt-in endpoint (endpoint 3). | |||
- Added provision for one interrupt-out endpoint (endpoint 1). | |||
- Added flowcontrol macros for USB. | |||
- Added provision for custom configuration descriptor. | |||
- Allow ANY two port bits for D+ and D-. | |||
- Merged (optional) receive endpoint number into global usbRxToken variable. | |||
- Use USB_CFG_IOPORTNAME instead of USB_CFG_IOPORT. We now construct the | |||
variable name from the single port letter instead of computing the address | |||
of related ports from the output-port address. | |||
* Release 2006-06-26 | |||
- Updated documentation in usbdrv.h and usbconfig-prototype.h to reflect the | |||
new features. | |||
- Removed "#warning" directives because IAR does not understand them. Use | |||
unused static variables instead to generate a warning. | |||
- Do not include <avr/io.h> when compiling with IAR. | |||
- Introduced USB_CFG_DESCR_PROPS_* in usbconfig.h to configure how each | |||
USB descriptor should be handled. It is now possible to provide descriptor | |||
data in Flash, RAM or dynamically at runtime. | |||
- STALL is now a status in usbTxLen* instead of a message. We can now conform | |||
to the spec and leave the stall status pending until it is cleared. | |||
- Made usbTxPacketCnt1 and usbTxPacketCnt3 public. This allows the | |||
application code to reset data toggling on interrupt pipes. | |||
* Release 2006-07-18 | |||
- Added an #if !defined __ASSEMBLER__ to the warning in usbdrv.h. This fixes | |||
an assembler error. | |||
- usbDeviceDisconnect() takes pull-up resistor to high impedance now. | |||
* Release 2007-02-01 | |||
- Merged in some code size improvements from usbtiny (thanks to Dick | |||
Streefland for these optimizations!) | |||
- Special alignment requirement for usbRxBuf not required any more. Thanks | |||
again to Dick Streefland for this hint! | |||
- Reverted to "#warning" instead of unused static variables -- new versions | |||
of IAR CC should handle this directive. | |||
- Changed Open Source license to GNU GPL v2 in order to make linking against | |||
other free libraries easier. We no longer require publication of the | |||
circuit diagrams, but we STRONGLY encourage it. If you improve the driver | |||
itself, PLEASE grant us a royalty free license to your changes for our | |||
commercial license. | |||
* Release 2007-03-29 | |||
- New configuration option "USB_PUBLIC" in usbconfig.h. | |||
- Set USB version number to 1.10 instead of 1.01. | |||
- Code used USB_CFG_DESCR_PROPS_STRING_DEVICE and | |||
USB_CFG_DESCR_PROPS_STRING_PRODUCT inconsistently. Changed all occurrences | |||
to USB_CFG_DESCR_PROPS_STRING_PRODUCT. | |||
- New assembler module for 16.5 MHz RC oscillator clock with PLL in receiver | |||
code. | |||
- New assembler module for 16 MHz crystal. | |||
- usbdrvasm.S contains common code only, clock-specific parts have been moved | |||
to usbdrvasm12.S, usbdrvasm16.S and usbdrvasm165.S respectively. | |||
* Release 2007-06-25 | |||
- 16 MHz module: Do SE0 check in stuffed bits as well. | |||
* Release 2007-07-07 | |||
- Define hi8(x) for IAR compiler to limit result to 8 bits. This is necessary | |||
for negative values. | |||
- Added 15 MHz module contributed by V. Bosch. | |||
- Interrupt vector name can now be configured. This is useful if somebody | |||
wants to use a different hardware interrupt than INT0. | |||
* Release 2007-08-07 | |||
- Moved handleIn3 routine in usbdrvasm16.S so that relative jump range is | |||
not exceeded. | |||
- More config options: USB_RX_USER_HOOK(), USB_INITIAL_DATATOKEN, | |||
USB_COUNT_SOF | |||
- USB_INTR_PENDING can now be a memory address, not just I/O | |||
* Release 2007-09-19 | |||
- Split out common parts of assembler modules into separate include file | |||
- Made endpoint numbers configurable so that given interface definitions | |||
can be matched. See USB_CFG_EP3_NUMBER in usbconfig-prototype.h. | |||
- Store endpoint number for interrupt/bulk-out so that usbFunctionWriteOut() | |||
can handle any number of endpoints. | |||
- Define usbDeviceConnect() and usbDeviceDisconnect() even if no | |||
USB_CFG_PULLUP_IOPORTNAME is defined. Directly set D+ and D- to 0 in this | |||
case. | |||
* Release 2007-12-01 | |||
- Optimize usbDeviceConnect() and usbDeviceDisconnect() for less code size | |||
when USB_CFG_PULLUP_IOPORTNAME is not defined. | |||
* Release 2007-12-13 | |||
- Renamed all include-only assembler modules from *.S to *.inc so that | |||
people don't add them to their project sources. | |||
- Distribute leap bits in tx loop more evenly for 16 MHz module. | |||
- Use "macro" and "endm" instead of ".macro" and ".endm" for IAR | |||
- Avoid compiler warnings for constant expr range by casting some values in | |||
USB descriptors. | |||
* Release 2008-01-21 | |||
- Fixed bug in 15 and 16 MHz module where the new address set with | |||
SET_ADDRESS was already accepted at the next NAK or ACK we send, not at | |||
the next data packet we send. This caused problems when the host polled | |||
too fast. Thanks to Alexander Neumann for his help and patience debugging | |||
this issue! | |||
* Release 2008-02-05 | |||
- Fixed bug in 16.5 MHz module where a register was used in the interrupt | |||
handler before it was pushed. This bug was introduced with version | |||
2007-09-19 when common parts were moved to a separate file. | |||
- Optimized CRC routine (thanks to Reimar Doeffinger). | |||
* Release 2008-02-16 | |||
- Removed outdated IAR compatibility stuff (code sections). | |||
- Added hook macros for USB_RESET_HOOK() and USB_SET_ADDRESS_HOOK(). | |||
- Added optional routine usbMeasureFrameLength() for calibration of the | |||
internal RC oscillator. | |||
* Release 2008-02-28 | |||
- USB_INITIAL_DATATOKEN defaults to USBPID_DATA1 now, which means that we | |||
start with sending USBPID_DATA0. | |||
- Changed defaults in usbconfig-prototype.h | |||
- Added free USB VID/PID pair for MIDI class devices | |||
- Restructured AVR-USB as separate package, not part of PowerSwitch any more. | |||
* Release 2008-04-18 | |||
- Restructured usbdrv.c so that it is easier to read and understand. | |||
- Better code optimization with gcc 4. | |||
- If a second interrupt in endpoint is enabled, also add it to config | |||
descriptor. | |||
- Added config option for long transfers (above 254 bytes), see | |||
USB_CFG_LONG_TRANSFERS in usbconfig.h. | |||
- Added 20 MHz module contributed by Jeroen Benschop. | |||
* Release 2008-05-13 | |||
- Fixed bug in libs-host/hiddata.c function usbhidGetReport(): length | |||
was not incremented, pointer to length was incremented instead. | |||
- Added code to command line tool(s) which claims an interface. This code | |||
is disabled by default, but may be necessary on newer Linux kernels. | |||
- Added usbconfig.h option "USB_CFG_CHECK_DATA_TOGGLING". | |||
- New header "usbportability.h" prepares ports to other development | |||
environments. | |||
- Long transfers (above 254 bytes) did not work when usbFunctionRead() was | |||
used to supply the data. Fixed this bug. [Thanks to Alexander Neumann!] | |||
- In hiddata.c (example code for sending/receiving data over HID), use | |||
USB_RECIP_DEVICE instead of USB_RECIP_INTERFACE for control transfers so | |||
that we need not claim the interface. | |||
- in usbPoll() loop 20 times polling for RESET state instead of 10 times. | |||
This accounts for the higher clock rates we now support. | |||
- Added a module for 12.8 MHz RC oscillator with PLL in receiver loop. | |||
- Added hook to SOF code so that oscillator can be tuned to USB frame clock. | |||
- Added timeout to waitForJ loop. Helps preventing unexpected hangs. | |||
- Added example code for oscillator tuning to libs-device (thanks to | |||
Henrik Haftmann for the idea to this routine). | |||
- Implemented option USB_CFG_SUPPRESS_INTR_CODE. | |||
* Release 2008-10-22 | |||
- Fixed libs-device/osctune.h: OSCCAL is memory address on ATMega88 and | |||
similar, not offset of 0x20 needs to be added. | |||
- Allow distribution under GPLv3 for those who have to link against other | |||
code distributed under GPLv3. | |||
* Release 2008-11-26 | |||
- Removed libusb-win32 dependency for hid-data example in Makefile.windows. | |||
It was never required and confused many people. | |||
- Added extern uchar usbRxToken to usbdrv.h. | |||
- Integrated a module with CRC checks at 18 MHz by Lukas Schrittwieser. | |||
* Release 2009-03-23 | |||
- Hid-mouse example used settings from hid-data example, fixed that. | |||
- Renamed project to V-USB due to a trademark issue with Atmel(r). | |||
- Changed CommercialLicense.txt and USBID-License.txt to make the | |||
background of USB ID registration clearer. | |||
* Release 2009-04-15 | |||
- Changed CommercialLicense.txt to reflect the new range of PIDs from | |||
Jason Kotzin. | |||
- Removed USBID-License.txt in favor of USB-IDs-for-free.txt and | |||
USB-ID-FAQ.txt | |||
- Fixed a bug in the 12.8 MHz module: End Of Packet decection was made in | |||
the center between bit 0 and 1 of each byte. This is where the data lines | |||
are expected to change and the sampled data may therefore be nonsense. | |||
We therefore check EOP ONLY if bits 0 AND 1 have both been read as 0 on D-. | |||
- Fixed a bitstuffing problem in the 16 MHz module: If bit 6 was stuffed, | |||
the unstuffing code in the receiver routine was 1 cycle too long. If | |||
multiple bytes had the unstuffing in bit 6, the error summed up until the | |||
receiver was out of sync. | |||
- Included option for faster CRC routine. | |||
Thanks to Slawomir Fras (BoskiDialer) for this code! | |||
- Updated bits in Configuration Descriptor's bmAttributes according to | |||
USB 1.1 (in particular bit 7, it is a must-be-set bit now). | |||
* Release 2009-08-22 | |||
- Moved first DBG1() after odDebugInit() in all examples. | |||
- Use vector INT0_vect instead of SIG_INTERRUPT0 if defined. This makes | |||
V-USB compatible with the new "p" suffix devices (e.g. ATMega328p). | |||
- USB_CFG_CLOCK_KHZ setting is now required in usbconfig.h (no default any | |||
more). | |||
- New option USB_CFG_DRIVER_FLASH_PAGE allows boot loaders on devices with | |||
more than 64 kB flash. | |||
- Built-in configuration descriptor allows custom definition for second | |||
endpoint now. | |||
* Release 2010-07-15 |
@@ -0,0 +1,166 @@ | |||
V-USB Driver Software License Agreement | |||
Version 2009-08-03 | |||
THIS LICENSE AGREEMENT GRANTS YOU CERTAIN RIGHTS IN A SOFTWARE. YOU CAN | |||
ENTER INTO THIS AGREEMENT AND ACQUIRE THE RIGHTS OUTLINED BELOW BY PAYING | |||
THE AMOUNT ACCORDING TO SECTION 4 ("PAYMENT") TO OBJECTIVE DEVELOPMENT. | |||
1 DEFINITIONS | |||
1.1 "OBJECTIVE DEVELOPMENT" shall mean OBJECTIVE DEVELOPMENT Software GmbH, | |||
Grosse Schiffgasse 1A/7, 1020 Wien, AUSTRIA. | |||
1.2 "You" shall mean the Licensee. | |||
1.3 "V-USB" shall mean all files included in the package distributed under | |||
the name "vusb" by OBJECTIVE DEVELOPMENT (http://www.obdev.at/vusb/) | |||
unless otherwise noted. This includes the firmware-only USB device | |||
implementation for Atmel AVR microcontrollers, some simple device examples | |||
and host side software examples and libraries. | |||
2 LICENSE GRANTS | |||
2.1 Source Code. OBJECTIVE DEVELOPMENT shall furnish you with the source | |||
code of V-USB. | |||
2.2 Distribution and Use. OBJECTIVE DEVELOPMENT grants you the | |||
non-exclusive right to use, copy and distribute V-USB with your hardware | |||
product(s), restricted by the limitations in section 3 below. | |||
2.3 Modifications. OBJECTIVE DEVELOPMENT grants you the right to modify | |||
the source code and your copy of V-USB according to your needs. | |||
2.4 USB IDs. OBJECTIVE DEVELOPMENT furnishes you with one or two USB | |||
Product ID(s), sent to you in e-mail. These Product IDs are reserved | |||
exclusively for you. OBJECTIVE DEVELOPMENT has obtained USB Product ID | |||
ranges under the Vendor ID 5824 from Wouter van Ooijen (Van Ooijen | |||
Technische Informatica, www.voti.nl) and under the Vendor ID 8352 from | |||
Jason Kotzin (Clay Logic, www.claylogic.com). Both owners of the Vendor IDs | |||
have obtained these IDs from the USB Implementers Forum, Inc. | |||
(www.usb.org). OBJECTIVE DEVELOPMENT disclaims all liability which might | |||
arise from the assignment of USB IDs. | |||
2.5 USB Certification. Although not part of this agreement, we want to make | |||
it clear that you cannot become USB certified when you use V-USB or a USB | |||
Product ID assigned by OBJECTIVE DEVELOPMENT. AVR microcontrollers don't | |||
meet the electrical specifications required by the USB specification and | |||
the USB Implementers Forum certifies only members who bought a Vendor ID of | |||
their own. | |||
3 LICENSE RESTRICTIONS | |||
3.1 Number of Units. Only one of the following three definitions is | |||
applicable. Which one is determined by the amount you pay to OBJECTIVE | |||
DEVELOPMENT, see section 4 ("Payment") below. | |||
Hobby License: You may use V-USB according to section 2 above in no more | |||
than 5 hardware units. These units must not be sold for profit. | |||
Entry Level License: You may use V-USB according to section 2 above in no | |||
more than 150 hardware units. | |||
Professional License: You may use V-USB according to section 2 above in | |||
any number of hardware units, except for large scale production ("unlimited | |||
fair use"). Quantities below 10,000 units are not considered large scale | |||
production. If your reach quantities which are obviously large scale | |||
production, you must pay a license fee of 0.10 EUR per unit for all units | |||
above 10,000. | |||
3.2 Rental. You may not rent, lease, or lend V-USB or otherwise encumber | |||
any copy of V-USB, or any of the rights granted herein. | |||
3.3 Transfer. You may not transfer your rights under this Agreement to | |||
another party without OBJECTIVE DEVELOPMENT's prior written consent. If | |||
such consent is obtained, you may permanently transfer this License to | |||
another party. The recipient of such transfer must agree to all terms and | |||
conditions of this Agreement. | |||
3.4 Reservation of Rights. OBJECTIVE DEVELOPMENT retains all rights not | |||
expressly granted. | |||
3.5 Non-Exclusive Rights. Your license rights under this Agreement are | |||
non-exclusive. | |||
3.6 Third Party Rights. This Agreement cannot grant you rights controlled | |||
by third parties. In particular, you are not allowed to use the USB logo or | |||
other trademarks owned by the USB Implementers Forum, Inc. without their | |||
consent. Since such consent depends on USB certification, it should be | |||
noted that V-USB will not pass certification because it does not | |||
implement checksum verification and the microcontroller ports do not meet | |||
the electrical specifications. | |||
4 PAYMENT | |||
The payment amount depends on the variation of this agreement (according to | |||
section 3.1) into which you want to enter. Concrete prices are listed on | |||
OBJECTIVE DEVELOPMENT's web site, usually at | |||
http://www.obdev.at/vusb/license.html. You agree to pay the amount listed | |||
there to OBJECTIVE DEVELOPMENT or OBJECTIVE DEVELOPMENT's payment processor | |||
or reseller. | |||
5 COPYRIGHT AND OWNERSHIP | |||
V-USB is protected by copyright laws and international copyright | |||
treaties, as well as other intellectual property laws and treaties. V-USB | |||
is licensed, not sold. | |||
6 TERM AND TERMINATION | |||
6.1 Term. This Agreement shall continue indefinitely. However, OBJECTIVE | |||
DEVELOPMENT may terminate this Agreement and revoke the granted license and | |||
USB-IDs if you fail to comply with any of its terms and conditions. | |||
6.2 Survival of Terms. All provisions regarding secrecy, confidentiality | |||
and limitation of liability shall survive termination of this agreement. | |||
7 DISCLAIMER OF WARRANTY AND LIABILITY | |||
LIMITED WARRANTY. V-USB IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||
KIND. TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, OBJECTIVE | |||
DEVELOPMENT AND ITS SUPPLIERS HEREBY DISCLAIM ALL WARRANTIES, EITHER | |||
EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND | |||
NON-INFRINGEMENT, WITH REGARD TO V-USB, AND THE PROVISION OF OR FAILURE | |||
TO PROVIDE SUPPORT SERVICES. THIS LIMITED WARRANTY GIVES YOU SPECIFIC LEGAL | |||
RIGHTS. YOU MAY HAVE OTHERS, WHICH VARY FROM STATE/JURISDICTION TO | |||
STATE/JURISDICTION. | |||
LIMITATION OF LIABILITY. TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, | |||
IN NO EVENT SHALL OBJECTIVE DEVELOPMENT OR ITS SUPPLIERS BE LIABLE FOR ANY | |||
SPECIAL, INCIDENTAL, INDIRECT, OR CONSEQUENTIAL DAMAGES WHATSOEVER | |||
(INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF BUSINESS PROFITS, | |||
BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION, OR ANY OTHER PECUNIARY | |||
LOSS) ARISING OUT OF THE USE OF OR INABILITY TO USE V-USB OR THE | |||
PROVISION OF OR FAILURE TO PROVIDE SUPPORT SERVICES, EVEN IF OBJECTIVE | |||
DEVELOPMENT HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. IN ANY | |||
CASE, OBJECTIVE DEVELOPMENT'S ENTIRE LIABILITY UNDER ANY PROVISION OF THIS | |||
AGREEMENT SHALL BE LIMITED TO THE AMOUNT ACTUALLY PAID BY YOU FOR V-USB. | |||
8 MISCELLANEOUS TERMS | |||
8.1 Marketing. OBJECTIVE DEVELOPMENT has the right to mention for marketing | |||
purposes that you entered into this agreement. | |||
8.2 Entire Agreement. This document represents the entire agreement between | |||
OBJECTIVE DEVELOPMENT and you. It may only be modified in writing signed by | |||
an authorized representative of both, OBJECTIVE DEVELOPMENT and you. | |||
8.3 Severability. In case a provision of these terms and conditions should | |||
be or become partly or entirely invalid, ineffective, or not executable, | |||
the validity of all other provisions shall not be affected. | |||
8.4 Applicable Law. This agreement is governed by the laws of the Republic | |||
of Austria. | |||
8.5 Responsible Courts. The responsible courts in Vienna/Austria will have | |||
exclusive jurisdiction regarding all disputes in connection with this | |||
agreement. | |||
@@ -0,0 +1,361 @@ | |||
OBJECTIVE DEVELOPMENT GmbH's V-USB driver software is distributed under the | |||
terms and conditions of the GNU GPL version 2 or the GNU GPL version 3. It is | |||
your choice whether you apply the terms of version 2 or version 3. The full | |||
text of GPLv2 is included below. In addition to the requirements in the GPL, | |||
we STRONGLY ENCOURAGE you to do the following: | |||
(1) Publish your entire project on a web site and drop us a note with the URL. | |||
Use the form at http://www.obdev.at/vusb/feedback.html for your submission. | |||
(2) Adhere to minimum publication standards. Please include AT LEAST: | |||
- a circuit diagram in PDF, PNG or GIF format | |||
- full source code for the host software | |||
- a Readme.txt file in ASCII format which describes the purpose of the | |||
project and what can be found in which directories and which files | |||
- a reference to http://www.obdev.at/vusb/ | |||
(3) If you improve the driver firmware itself, please give us a free license | |||
to your modifications for our commercial license offerings. | |||
GNU GENERAL PUBLIC LICENSE | |||
Version 2, June 1991 | |||
Copyright (C) 1989, 1991 Free Software Foundation, Inc. | |||
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |||
Everyone is permitted to copy and distribute verbatim copies | |||
of this license document, but changing it is not allowed. | |||
Preamble | |||
The licenses for most software are designed to take away your | |||
freedom to share and change it. By contrast, the GNU General Public | |||
License is intended to guarantee your freedom to share and change free | |||
software--to make sure the software is free for all its users. This | |||
General Public License applies to most of the Free Software | |||
Foundation's software and to any other program whose authors commit to | |||
using it. (Some other Free Software Foundation software is covered by | |||
the GNU Library General Public License instead.) You can apply it to | |||
your programs, too. | |||
When we speak of free software, we are referring to freedom, not | |||
price. Our General Public Licenses are designed to make sure that you | |||
have the freedom to distribute copies of free software (and charge for | |||
this service if you wish), that you receive source code or can get it | |||
if you want it, that you can change the software or use pieces of it | |||
in new free programs; and that you know you can do these things. | |||
To protect your rights, we need to make restrictions that forbid | |||
anyone to deny you these rights or to ask you to surrender the rights. | |||
These restrictions translate to certain responsibilities for you if you | |||
distribute copies of the software, or if you modify it. | |||
For example, if you distribute copies of such a program, whether | |||
gratis or for a fee, you must give the recipients all the rights that | |||
you have. You must make sure that they, too, receive or can get the | |||
source code. And you must show them these terms so they know their | |||
rights. | |||
We protect your rights with two steps: (1) copyright the software, and | |||
(2) offer you this license which gives you legal permission to copy, | |||
distribute and/or modify the software. | |||
Also, for each author's protection and ours, we want to make certain | |||
that everyone understands that there is no warranty for this free | |||
software. If the software is modified by someone else and passed on, we | |||
want its recipients to know that what they have is not the original, so | |||
that any problems introduced by others will not reflect on the original | |||
authors' reputations. | |||
Finally, any free program is threatened constantly by software | |||
patents. We wish to avoid the danger that redistributors of a free | |||
program will individually obtain patent licenses, in effect making the | |||
program proprietary. To prevent this, we have made it clear that any | |||
patent must be licensed for everyone's free use or not licensed at all. | |||
The precise terms and conditions for copying, distribution and | |||
modification follow. | |||
GNU GENERAL PUBLIC LICENSE | |||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | |||
0. This License applies to any program or other work which contains | |||
a notice placed by the copyright holder saying it may be distributed | |||
under the terms of this General Public License. The "Program", below, | |||
refers to any such program or work, and a "work based on the Program" | |||
means either the Program or any derivative work under copyright law: | |||
that is to say, a work containing the Program or a portion of it, | |||
either verbatim or with modifications and/or translated into another | |||
language. (Hereinafter, translation is included without limitation in | |||
the term "modification".) Each licensee is addressed as "you". | |||
Activities other than copying, distribution and modification are not | |||
covered by this License; they are outside its scope. The act of | |||
running the Program is not restricted, and the output from the Program | |||
is covered only if its contents constitute a work based on the | |||
Program (independent of having been made by running the Program). | |||
Whether that is true depends on what the Program does. | |||
1. You may copy and distribute verbatim copies of the Program's | |||
source code as you receive it, in any medium, provided that you | |||
conspicuously and appropriately publish on each copy an appropriate | |||
copyright notice and disclaimer of warranty; keep intact all the | |||
notices that refer to this License and to the absence of any warranty; | |||
and give any other recipients of the Program a copy of this License | |||
along with the Program. | |||
You may charge a fee for the physical act of transferring a copy, and | |||
you may at your option offer warranty protection in exchange for a fee. | |||
2. You may modify your copy or copies of the Program or any portion | |||
of it, thus forming a work based on the Program, and copy and | |||
distribute such modifications or work under the terms of Section 1 | |||
above, provided that you also meet all of these conditions: | |||
a) You must cause the modified files to carry prominent notices | |||
stating that you changed the files and the date of any change. | |||
b) You must cause any work that you distribute or publish, that in | |||
whole or in part contains or is derived from the Program or any | |||
part thereof, to be licensed as a whole at no charge to all third | |||
parties under the terms of this License. | |||
c) If the modified program normally reads commands interactively | |||
when run, you must cause it, when started running for such | |||
interactive use in the most ordinary way, to print or display an | |||
announcement including an appropriate copyright notice and a | |||
notice that there is no warranty (or else, saying that you provide | |||
a warranty) and that users may redistribute the program under | |||
these conditions, and telling the user how to view a copy of this | |||
License. (Exception: if the Program itself is interactive but | |||
does not normally print such an announcement, your work based on | |||
the Program is not required to print an announcement.) | |||
These requirements apply to the modified work as a whole. If | |||
identifiable sections of that work are not derived from the Program, | |||
and can be reasonably considered independent and separate works in | |||
themselves, then this License, and its terms, do not apply to those | |||
sections when you distribute them as separate works. But when you | |||
distribute the same sections as part of a whole which is a work based | |||
on the Program, the distribution of the whole must be on the terms of | |||
this License, whose permissions for other licensees extend to the | |||
entire whole, and thus to each and every part regardless of who wrote it. | |||
Thus, it is not the intent of this section to claim rights or contest | |||
your rights to work written entirely by you; rather, the intent is to | |||
exercise the right to control the distribution of derivative or | |||
collective works based on the Program. | |||
In addition, mere aggregation of another work not based on the Program | |||
with the Program (or with a work based on the Program) on a volume of | |||
a storage or distribution medium does not bring the other work under | |||
the scope of this License. | |||
3. You may copy and distribute the Program (or a work based on it, | |||
under Section 2) in object code or executable form under the terms of | |||
Sections 1 and 2 above provided that you also do one of the following: | |||
a) Accompany it with the complete corresponding machine-readable | |||
source code, which must be distributed under the terms of Sections | |||
1 and 2 above on a medium customarily used for software interchange; or, | |||
b) Accompany it with a written offer, valid for at least three | |||
years, to give any third party, for a charge no more than your | |||
cost of physically performing source distribution, a complete | |||
machine-readable copy of the corresponding source code, to be | |||
distributed under the terms of Sections 1 and 2 above on a medium | |||
customarily used for software interchange; or, | |||
c) Accompany it with the information you received as to the offer | |||
to distribute corresponding source code. (This alternative is | |||
allowed only for noncommercial distribution and only if you | |||
received the program in object code or executable form with such | |||
an offer, in accord with Subsection b above.) | |||
The source code for a work means the preferred form of the work for | |||
making modifications to it. For an executable work, complete source | |||
code means all the source code for all modules it contains, plus any | |||
associated interface definition files, plus the scripts used to | |||
control compilation and installation of the executable. However, as a | |||
special exception, the source code distributed need not include | |||
anything that is normally distributed (in either source or binary | |||
form) with the major components (compiler, kernel, and so on) of the | |||
operating system on which the executable runs, unless that component | |||
itself accompanies the executable. | |||
If distribution of executable or object code is made by offering | |||
access to copy from a designated place, then offering equivalent | |||
access to copy the source code from the same place counts as | |||
distribution of the source code, even though third parties are not | |||
compelled to copy the source along with the object code. | |||
4. You may not copy, modify, sublicense, or distribute the Program | |||
except as expressly provided under this License. Any attempt | |||
otherwise to copy, modify, sublicense or distribute the Program is | |||
void, and will automatically terminate your rights under this License. | |||
However, parties who have received copies, or rights, from you under | |||
this License will not have their licenses terminated so long as such | |||
parties remain in full compliance. | |||
5. You are not required to accept this License, since you have not | |||
signed it. However, nothing else grants you permission to modify or | |||
distribute the Program or its derivative works. These actions are | |||
prohibited by law if you do not accept this License. Therefore, by | |||
modifying or distributing the Program (or any work based on the | |||
Program), you indicate your acceptance of this License to do so, and | |||
all its terms and conditions for copying, distributing or modifying | |||
the Program or works based on it. | |||
6. Each time you redistribute the Program (or any work based on the | |||
Program), the recipient automatically receives a license from the | |||
original licensor to copy, distribute or modify the Program subject to | |||
these terms and conditions. You may not impose any further | |||
restrictions on the recipients' exercise of the rights granted herein. | |||
You are not responsible for enforcing compliance by third parties to | |||
this License. | |||
7. If, as a consequence of a court judgment or allegation of patent | |||
infringement or for any other reason (not limited to patent issues), | |||
conditions are imposed on you (whether by court order, agreement or | |||
otherwise) that contradict the conditions of this License, they do not | |||
excuse you from the conditions of this License. If you cannot | |||
distribute so as to satisfy simultaneously your obligations under this | |||
License and any other pertinent obligations, then as a consequence you | |||
may not distribute the Program at all. For example, if a patent | |||
license would not permit royalty-free redistribution of the Program by | |||
all those who receive copies directly or indirectly through you, then | |||
the only way you could satisfy both it and this License would be to | |||
refrain entirely from distribution of the Program. | |||
If any portion of this section is held invalid or unenforceable under | |||
any particular circumstance, the balance of the section is intended to | |||
apply and the section as a whole is intended to apply in other | |||
circumstances. | |||
It is not the purpose of this section to induce you to infringe any | |||
patents or other property right claims or to contest validity of any | |||
such claims; this section has the sole purpose of protecting the | |||
integrity of the free software distribution system, which is | |||
implemented by public license practices. Many people have made | |||
generous contributions to the wide range of software distributed | |||
through that system in reliance on consistent application of that | |||
system; it is up to the author/donor to decide if he or she is willing | |||
to distribute software through any other system and a licensee cannot | |||
impose that choice. | |||
This section is intended to make thoroughly clear what is believed to | |||
be a consequence of the rest of this License. | |||
8. If the distribution and/or use of the Program is restricted in | |||
certain countries either by patents or by copyrighted interfaces, the | |||
original copyright holder who places the Program under this License | |||
may add an explicit geographical distribution limitation excluding | |||
those countries, so that distribution is permitted only in or among | |||
countries not thus excluded. In such case, this License incorporates | |||
the limitation as if written in the body of this License. | |||
9. The Free Software Foundation may publish revised and/or new versions | |||
of the General Public License from time to time. Such new versions will | |||
be similar in spirit to the present version, but may differ in detail to | |||
address new problems or concerns. | |||
Each version is given a distinguishing version number. If the Program | |||
specifies a version number of this License which applies to it and "any | |||
later version", you have the option of following the terms and conditions | |||
either of that version or of any later version published by the Free | |||
Software Foundation. If the Program does not specify a version number of | |||
this License, you may choose any version ever published by the Free Software | |||
Foundation. | |||
10. If you wish to incorporate parts of the Program into other free | |||
programs whose distribution conditions are different, write to the author | |||
to ask for permission. For software which is copyrighted by the Free | |||
Software Foundation, write to the Free Software Foundation; we sometimes | |||
make exceptions for this. Our decision will be guided by the two goals | |||
of preserving the free status of all derivatives of our free software and | |||
of promoting the sharing and reuse of software generally. | |||
NO WARRANTY | |||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY | |||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN | |||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES | |||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED | |||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS | |||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE | |||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, | |||
REPAIR OR CORRECTION. | |||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING | |||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR | |||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, | |||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING | |||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED | |||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY | |||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER | |||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE | |||
POSSIBILITY OF SUCH DAMAGES. | |||
END OF TERMS AND CONDITIONS | |||
How to Apply These Terms to Your New Programs | |||
If you develop a new program, and you want it to be of the greatest | |||
possible use to the public, the best way to achieve this is to make it | |||
free software which everyone can redistribute and change under these terms. | |||
To do so, attach the following notices to the program. It is safest | |||
to attach them to the start of each source file to most effectively | |||
convey the exclusion of warranty; and each file should have at least | |||
the "copyright" line and a pointer to where the full notice is found. | |||
<one line to give the program's name and a brief idea of what it does.> | |||
Copyright (C) <year> <name of author> | |||
This program is free software; you can redistribute it and/or modify | |||
it under the terms of the GNU General Public License as published by | |||
the Free Software Foundation; either version 2 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 General Public License for more details. | |||
You should have received a copy of the GNU General Public License | |||
along with this program; if not, write to the Free Software | |||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |||
Also add information on how to contact you by electronic and paper mail. | |||
If the program is interactive, make it output a short notice like this | |||
when it starts in an interactive mode: | |||
Gnomovision version 69, Copyright (C) year name of author | |||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. | |||
This is free software, and you are welcome to redistribute it | |||
under certain conditions; type `show c' for details. | |||
The hypothetical commands `show w' and `show c' should show the appropriate | |||
parts of the General Public License. Of course, the commands you use may | |||
be called something other than `show w' and `show c'; they could even be | |||
mouse-clicks or menu items--whatever suits your program. | |||
You should also get your employer (if you work as a programmer) or your | |||
school, if any, to sign a "copyright disclaimer" for the program, if | |||
necessary. Here is a sample; alter the names: | |||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program | |||
`Gnomovision' (which makes passes at compilers) written by James Hacker. | |||
<signature of Ty Coon>, 1 April 1989 | |||
Ty Coon, President of Vice | |||
This General Public License does not permit incorporating your program into | |||
proprietary programs. If your program is a subroutine library, you may | |||
consider it more useful to permit linking proprietary applications with the | |||
library. If this is what you want to do, use the GNU Library General | |||
Public License instead of this License. |
@@ -0,0 +1,172 @@ | |||
This is the Readme file to Objective Development's firmware-only USB driver | |||
for Atmel AVR microcontrollers. For more information please visit | |||
http://www.obdev.at/vusb/ | |||
This directory contains the USB firmware only. Copy it as-is to your own | |||
project and add all .c and .S files to your project (these files are marked | |||
with an asterisk in the list below). Then copy usbconfig-prototype.h as | |||
usbconfig.h to your project and edit it according to your configuration. | |||
TECHNICAL DOCUMENTATION | |||
======================= | |||
The technical documentation (API) for the firmware driver is contained in the | |||
file "usbdrv.h". Please read all of it carefully! Configuration options are | |||
documented in "usbconfig-prototype.h". | |||
The driver consists of the following files: | |||
Readme.txt ............. The file you are currently reading. | |||
Changelog.txt .......... Release notes for all versions of the driver. | |||
usbdrv.h ............... Driver interface definitions and technical docs. | |||
* usbdrv.c ............... High level language part of the driver. Link this | |||
module to your code! | |||
* usbdrvasm.S ............ Assembler part of the driver. This module is mostly | |||
a stub and includes one of the usbdrvasm*.S files | |||
depending on processor clock. Link this module to | |||
your code! | |||
usbdrvasm*.inc ......... Assembler routines for particular clock frequencies. | |||
Included by usbdrvasm.S, don't link it directly! | |||
asmcommon.inc .......... Common assembler routines. Included by | |||
usbdrvasm*.inc, don't link it directly! | |||
usbconfig-prototype.h .. Prototype for your own usbdrv.h file. | |||
* oddebug.c .............. Debug functions. Only used when DEBUG_LEVEL is | |||
defined to a value greater than 0. Link this module | |||
to your code! | |||
oddebug.h .............. Interface definitions of the debug module. | |||
usbportability.h ....... Header with compiler-dependent stuff. | |||
usbdrvasm.asm .......... Compatibility stub for IAR-C-compiler. Use this | |||
module instead of usbdrvasm.S when you assembler | |||
with IAR's tools. | |||
License.txt ............ Open Source license for this driver. | |||
CommercialLicense.txt .. Optional commercial license for this driver. | |||
USB-ID-FAQ.txt ......... General infos about USB Product- and Vendor-IDs. | |||
USB-IDs-for-free.txt ... List and terms of use for free shared PIDs. | |||
(*) ... These files should be linked to your project. | |||
CPU CORE CLOCK FREQUENCY | |||
======================== | |||
We supply assembler modules for clock frequencies of 12 MHz, 12.8 MHz, 15 MHz, | |||
16 MHz, 16.5 MHz 18 MHz and 20 MHz. Other clock rates are not supported. The | |||
actual clock rate must be configured in usbconfig.h. | |||
12 MHz Clock | |||
This is the traditional clock rate of V-USB because it's the lowest clock | |||
rate where the timing constraints of the USB spec can be met. | |||
15 MHz Clock | |||
Similar to 12 MHz, but some NOPs inserted. On the other hand, the higher clock | |||
rate allows for some loops which make the resulting code size somewhat smaller | |||
than the 12 MHz version. | |||
16 MHz Clock | |||
This clock rate has been added for users of the Arduino board and other | |||
ready-made boards which come with a fixed 16 MHz crystal. It's also an option | |||
if you need the slightly higher clock rate for performance reasons. Since | |||
16 MHz is not divisible by the USB low speed bit clock of 1.5 MHz, the code | |||
is somewhat tricky and has to insert a leap cycle every third byte. | |||
12.8 MHz and 16.5 MHz Clock | |||
The assembler modules for these clock rates differ from the other modules | |||
because they have been built for an RC oscillator with only 1% precision. The | |||
receiver code inserts leap cycles to compensate for clock deviations. 1% is | |||
also the precision which can be achieved by calibrating the internal RC | |||
oscillator of the AVR. Please note that only AVRs with internal 64 MHz PLL | |||
oscillator can reach 16.5 MHz with the RC oscillator. This includes the very | |||
popular ATTiny25, ATTiny45, ATTiny85 series as well as the ATTiny26. Almost | |||
all AVRs can reach 12.8 MHz, although this is outside the specified range. | |||
See the EasyLogger example at http://www.obdev.at/vusb/easylogger.html for | |||
code which calibrates the RC oscillator based on the USB frame clock. | |||
18 MHz Clock | |||
This module is closer to the USB specification because it performs an on the | |||
fly CRC check for incoming packets. Packets with invalid checksum are | |||
discarded as required by the spec. If you also implement checks for data | |||
PID toggling on application level (see option USB_CFG_CHECK_DATA_TOGGLING | |||
in usbconfig.h for more info), this ensures data integrity. Due to the CRC | |||
tables and alignment requirements, this code is bigger than modules for other | |||
clock rates. To activate this module, you must define USB_CFG_CHECK_CRC to 1 | |||
and USB_CFG_CLOCK_KHZ to 18000 in usbconfig.h. | |||
20 MHz Clock | |||
This module is for people who won't do it with less than the maximum. Since | |||
20 MHz is not divisible by the USB low speed bit clock of 1.5 MHz, the code | |||
uses similar tricks as the 16 MHz module to insert leap cycles. | |||
USB IDENTIFIERS | |||
=============== | |||
Every USB device needs a vendor- and a product-identifier (VID and PID). VIDs | |||
are obtained from usb.org for a price of 1,500 USD. Once you have a VID, you | |||
can assign PIDs at will. | |||
Since an entry level cost of 1,500 USD is too high for most small companies | |||
and hobbyists, we provide some VID/PID pairs for free. See the file | |||
USB-IDs-for-free.txt for details. | |||
Objective Development also has some license offerings which include product | |||
IDs. See http://www.obdev.at/vusb/ for details. | |||
DEVELOPMENT SYSTEM | |||
================== | |||
This driver has been developed and optimized for the GNU compiler version 3 | |||
and 4. We recommend that you use the GNU compiler suite because it is freely | |||
available. V-USB has also been ported to the IAR compiler and assembler. It | |||
has been tested with IAR 4.10B/W32 and 4.12A/W32 on an ATmega8 with the | |||
"small" and "tiny" memory model. Not every release is tested with IAR CC and | |||
the driver may therefore fail to compile with IAR. Please note that gcc is | |||
more efficient for usbdrv.c because this module has been deliberately | |||
optimized for gcc. | |||
Gcc version 3 produces smaller code than version 4 due to new optimizing | |||
capabilities which don't always improve things on 8 bit CPUs. The code size | |||
generated by gcc 4 can be reduced with the compiler options | |||
-fno-move-loop-invariants, -fno-tree-scev-cprop and | |||
-fno-inline-small-functions in addition to -Os. On devices with more than | |||
8k of flash memory, we also recommend the linker option --relax (written as | |||
-Wl,--relax for gcc) to convert absolute calls into relative where possible. | |||
For more information about optimizing options see: | |||
http://www.tty1.net/blog/2008-04-29-avr-gcc-optimisations_en.html | |||
These optimizations are good for gcc 4.x. Version 3.x of gcc does not support | |||
most of these options and produces good code anyway. | |||
USING V-USB FOR FREE | |||
==================== | |||
The AVR firmware driver is published under the GNU General Public License | |||
Version 2 (GPL2) and the GNU General Public License Version 3 (GPL3). It is | |||
your choice whether you apply the terms of version 2 or version 3. | |||
If you decide for the free GPL2 or GPL3, we STRONGLY ENCOURAGE you to do the | |||
following things IN ADDITION to the obligations from the GPL: | |||
(1) Publish your entire project on a web site and drop us a note with the URL. | |||
Use the form at http://www.obdev.at/vusb/feedback.html for your submission. | |||
If you don't have a web site, you can publish the project in obdev's | |||
documentation wiki at | |||
http://www.obdev.at/goto.php?t=vusb-wiki&p=hosted-projects. | |||
(2) Adhere to minimum publication standards. Please include AT LEAST: | |||
- a circuit diagram in PDF, PNG or GIF format | |||
- full source code for the host software | |||
- a Readme.txt file in ASCII format which describes the purpose of the | |||
project and what can be found in which directories and which files | |||
- a reference to http://www.obdev.at/vusb/ | |||
(3) If you improve the driver firmware itself, please give us a free license | |||
to your modifications for our commercial license offerings. | |||
COMMERCIAL LICENSES FOR V-USB | |||
============================= | |||
If you don't want to publish your source code under the terms of the GPL, | |||
you can simply pay money for V-USB. As an additional benefit you get | |||
USB PIDs for free, reserved exclusively to you. See the file | |||
"CommercialLicense.txt" for details. | |||
@@ -0,0 +1,149 @@ | |||
Version 2009-08-22 | |||
========================== | |||
WHY DO WE NEED THESE IDs? | |||
========================== | |||
USB is more than a low level protocol for data transport. It also defines a | |||
common set of requests which must be understood by all devices. And as part | |||
of these common requests, the specification defines data structures, the | |||
USB Descriptors, which are used to describe the properties of the device. | |||
From the perspective of an operating system, it is therefore possible to find | |||
out basic properties of a device (such as e.g. the manufacturer and the name | |||
of the device) without a device-specific driver. This is essential because | |||
the operating system can choose a driver to load based on this information | |||
(Plug-And-Play). | |||
Among the most important properties in the Device Descriptor are the USB | |||
Vendor- and Product-ID. Both are 16 bit integers. The most simple form of | |||
driver matching is based on these IDs. The driver announces the Vendor- and | |||
Product-IDs of the devices it can handle and the operating system loads the | |||
appropriate driver when the device is connected. | |||
It is obvious that this technique only works if the pair Vendor- plus | |||
Product-ID is unique: Only devices which require the same driver can have the | |||
same pair of IDs. | |||
===================================================== | |||
HOW DOES THE USB STANDARD ENSURE THAT IDs ARE UNIQUE? | |||
===================================================== | |||
Since it is so important that USB IDs are unique, the USB Implementers Forum, | |||
Inc. (usb.org) needs a way to enforce this legally. It is not forbidden by | |||
law to build a device and assign it any random numbers as IDs. Usb.org | |||
therefore needs an agreement to regulate the use of USB IDs. The agreement | |||
binds only parties who agreed to it, of course. Everybody else is free to use | |||
any numbers for their IDs. | |||
So how can usb.org ensure that every manufacturer of USB devices enters into | |||
an agreement with them? They do it via trademark licensing. Usb.org has | |||
registered the trademark "USB", all associated logos and related terms. If | |||
you want to put an USB logo on your product or claim that it is USB | |||
compliant, you must license these trademarks from usb.org. And this is where | |||
you enter into an agreement. See the "USB-IF Trademark License Agreement and | |||
Usage Guidelines for the USB-IF Logo" at | |||
http://www.usb.org/developers/logo_license/. | |||
Licensing the USB trademarks requires that you buy a USB Vendor-ID from | |||
usb.org (one-time fee of ca. 2,000 USD), that you become a member of usb.org | |||
(yearly fee of ca. 4,000 USD) and that you meet all the technical | |||
specifications from the USB spec. | |||
This means that most hobbyists and small companies will never be able to | |||
become USB compliant, just because membership is so expensive. And you can't | |||
be compliant with a driver based on V-USB anyway, because the AVR's port pins | |||
don't meet the electrical specifications for USB. So, in principle, all | |||
hobbyists and small companies are free to choose any random numbers for their | |||
IDs. They have nothing to lose... | |||
There is one exception worth noting, though: If you use a sub-component which | |||
implements USB, the vendor of the sub-components may guarantee USB | |||
compliance. This might apply to some or all of FTDI's solutions. | |||
======================================================================= | |||
WHY SHOULD YOU OBTAIN USB IDs EVEN IF YOU DON'T LICENSE USB TRADEMARKS? | |||
======================================================================= | |||
You have learned in the previous section that you are free to choose any | |||
numbers for your IDs anyway. So why not do exactly this? There is still the | |||
technical issue. If you choose IDs which are already in use by somebody else, | |||
operating systems will load the wrong drivers and your device won't work. | |||
Even if you choose IDs which are not currently in use, they may be in use in | |||
the next version of the operating system or even after an automatic update. | |||
So what you need is a pair of Vendor- and Product-IDs for which you have the | |||
guarantee that no USB compliant product uses them. This implies that no | |||
operating system will ever ship with drivers responsible for these IDs. | |||
============================================== | |||
HOW DOES OBJECTIVE DEVELOPMENT HANDLE USB IDs? | |||
============================================== | |||
Objective Development gives away pairs of USB-IDs with their V-USB licenses. | |||
In order to ensure that these IDs are unique, Objective Development has an | |||
agreement with the company/person who has bought the USB Vendor-ID from | |||
usb.org. This agreement ensures that a range of USB Product-IDs is reserved | |||
for assignment by Objective Development and that the owner of the Vendor-ID | |||
won't give it to anybody else. | |||
This means that you have to trust three parties to ensure uniqueness of | |||
your IDs: | |||
- Objective Development, that they don't give the same PID to more than | |||
one person. | |||
- The owner of the Vendor-ID that they don't assign PIDs from the range | |||
assigned to Objective Development to anybody else. | |||
- Usb.org that they don't assign the same Vendor-ID a second time. | |||
================================== | |||
WHO IS THE OWNER OF THE VENDOR-ID? | |||
================================== | |||
Objective Development has obtained ranges of USB Product-IDs under two | |||
Vendor-IDs: Under Vendor-ID 5824 from Wouter van Ooijen (Van Ooijen | |||
Technische Informatica, www.voti.nl) and under Vendor-ID 8352 from Jason | |||
Kotzin (Clay Logic, www.claylogic.com). Both VID owners have received their | |||
Vendor-ID directly from usb.org. | |||
========================================================================= | |||
CAN I USE USB-IDs FROM OBJECTIVE DEVELOPMENT WITH OTHER DRIVERS/HARDWARE? | |||
========================================================================= | |||
The short answer is: Yes. All you get is a guarantee that the IDs are never | |||
assigned to anybody else. What more do you need? | |||
============================ | |||
WHAT ABOUT SHARED ID PAIRS? | |||
============================ | |||
Objective Development has reserved some PID/VID pairs for shared use. You | |||
have no guarantee of uniqueness for them, except that no USB compliant device | |||
uses them. In order to avoid technical problems, we must ensure that all | |||
devices with the same pair of IDs use the same driver on kernel level. For | |||
details, see the file USB-IDs-for-free.txt. | |||
====================================================== | |||
I HAVE HEARD THAT SUB-LICENSING OF USB-IDs IS ILLEGAL? | |||
====================================================== | |||
A 16 bit integer number cannot be protected by copyright laws. It is not | |||
sufficiently complex. And since none of the parties involved entered into the | |||
USB-IF Trademark License Agreement, we are not bound by this agreement. So | |||
there is no reason why it should be illegal to sub-license USB-IDs. | |||
============================================= | |||
WHO IS LIABLE IF THERE ARE INCOMPATIBILITIES? | |||
============================================= | |||
Objective Development disclaims all liabilities which might arise from the | |||
assignment of IDs. If you guarantee product features to your customers | |||
without proper disclaimer, YOU are liable for that. |
@@ -0,0 +1,148 @@ | |||
Version 2009-08-22 | |||
=========================== | |||
FREE USB-IDs FOR SHARED USE | |||
=========================== | |||
Objective Development has reserved a set of USB Product-IDs for use according | |||
to the guidelines outlined below. For more information about the concept of | |||
USB IDs please see the file USB-ID-FAQ.txt. Objective Development guarantees | |||
that the IDs listed below are not used by any USB compliant devices. | |||
==================== | |||
MECHANISM OF SHARING | |||
==================== | |||
From a technical point of view, two different devices can share the same USB | |||
Vendor- and Product-ID if they require the same driver on operating system | |||
level. We make use of this fact by assigning separate IDs for various device | |||
classes. On application layer, devices must be distinguished by their textual | |||
name or serial number. We offer separate sets of IDs for discrimination by | |||
textual name and for serial number. | |||
Examples for shared use of USB IDs are included with V-USB in the "examples" | |||
subdirectory. | |||
====================================== | |||
IDs FOR DISCRIMINATION BY TEXTUAL NAME | |||
====================================== | |||
If you use one of the IDs listed below, your device and host-side software | |||
must conform to these rules: | |||
(1) The USB device MUST provide a textual representation of the manufacturer | |||
and product identification. The manufacturer identification MUST be available | |||
at least in USB language 0x0409 (English/US). | |||
(2) The textual manufacturer identification MUST contain either an Internet | |||
domain name (e.g. "mycompany.com") registered and owned by you, or an e-mail | |||
address under your control (e.g. "[email protected]"). You can embed the domain | |||
name or e-mail address in any string you like, e.g. "Objective Development | |||
http://www.obdev.at/vusb/". | |||
(3) You are responsible for retaining ownership of the domain or e-mail | |||
address for as long as any of your products are in use. | |||
(4) You may choose any string for the textual product identification, as long | |||
as this string is unique within the scope of your textual manufacturer | |||
identification. | |||
(5) Application side device look-up MUST be based on the textual manufacturer | |||
and product identification in addition to VID/PID matching. The driver | |||
matching MUST be a comparison of the entire strings, NOT a sub-string match. | |||
(6) For devices which implement a particular USB device class (e.g. HID), the | |||
operating system's default class driver MUST be used. If an operating system | |||
driver for Vendor Class devices is needed, this driver must be libusb or | |||
libusb-win32 (see http://libusb.org/ and | |||
http://libusb-win32.sourceforge.net/). | |||
Table if IDs for discrimination by textual name: | |||
PID dec (hex) | VID dec (hex) | Description of use | |||
==============+===============+============================================ | |||
1500 (0x05dc) | 5824 (0x16c0) | For Vendor Class devices with libusb | |||
--------------+---------------+-------------------------------------------- | |||
1503 (0x05df) | 5824 (0x16c0) | For generic HID class devices (which are | |||
| | NOT mice, keyboards or joysticks) | |||
--------------+---------------+-------------------------------------------- | |||
1505 (0x05e1) | 5824 (0x16c0) | For CDC-ACM class devices (modems) | |||
--------------+---------------+-------------------------------------------- | |||
1508 (0x05e4) | 5824 (0x16c0) | For MIDI class devices | |||
--------------+---------------+-------------------------------------------- | |||
Note that Windows caches the textual product- and vendor-description for | |||
mice, keyboards and joysticks. Name-bsed discrimination is therefore not | |||
recommended for these device classes. | |||
======================================= | |||
IDs FOR DISCRIMINATION BY SERIAL NUMBER | |||
======================================= | |||
If you use one of the IDs listed below, your device and host-side software | |||
must conform to these rules: | |||
(1) The USB device MUST provide a textual representation of the serial | |||
number. The serial number string MUST be available at least in USB language | |||
0x0409 (English/US). | |||
(2) The serial number MUST start with either an Internet domain name (e.g. | |||
"mycompany.com") registered and owned by you, or an e-mail address under your | |||
control (e.g. "[email protected]"), both terminated with a colon (":") character. | |||
You MAY append any string you like for further discrimination of your devices. | |||
(3) You are responsible for retaining ownership of the domain or e-mail | |||
address for as long as any of your products are in use. | |||
(5) Application side device look-up MUST be based on the serial number string | |||
in addition to VID/PID matching. The matching must start at the first | |||
character of the serial number string and include the colon character | |||
terminating your domain or e-mail address. It MAY stop anywhere after that. | |||
(6) For devices which implement a particular USB device class (e.g. HID), the | |||
operating system's default class driver MUST be used. If an operating system | |||
driver for Vendor Class devices is needed, this driver must be libusb or | |||
libusb-win32 (see http://libusb.org/ and | |||
http://libusb-win32.sourceforge.net/). | |||
Table if IDs for discrimination by serial number string: | |||
PID dec (hex) | VID dec (hex) | Description of use | |||
===============+===============+=========================================== | |||
10200 (0x27d8) | 5824 (0x16c0) | For Vendor Class devices with libusb | |||
---------------+---------------+------------------------------------------- | |||
10201 (0x27d9) | 5824 (0x16c0) | For generic HID class devices (which are | |||
| | NOT mice, keyboards or joysticks) | |||
---------------+---------------+------------------------------------------- | |||
10202 (0x27da) | 5824 (0x16c0) | For USB Mice | |||
---------------+---------------+------------------------------------------- | |||
10203 (0x27db) | 5824 (0x16c0) | For USB Keyboards | |||
---------------+---------------+------------------------------------------- | |||
10204 (0x27db) | 5824 (0x16c0) | For USB Joysticks | |||
---------------+---------------+------------------------------------------- | |||
10205 (0x27dc) | 5824 (0x16c0) | For CDC-ACM class devices (modems) | |||
---------------+---------------+------------------------------------------- | |||
10206 (0x27dd) | 5824 (0x16c0) | For MIDI class devices | |||
---------------+---------------+------------------------------------------- | |||
================= | |||
ORIGIN OF USB-IDs | |||
================= | |||
OBJECTIVE DEVELOPMENT Software GmbH has obtained all VID/PID pairs listed | |||
here from Wouter van Ooijen (see www.voti.nl) for exclusive disposition. | |||
Wouter van Ooijen has obtained the VID from the USB Implementers Forum, Inc. | |||
(see www.usb.org). The VID is registered for the company name "Van Ooijen | |||
Technische Informatica". | |||
========== | |||
DISCLAIMER | |||
========== | |||
OBJECTIVE DEVELOPMENT Software GmbH disclaims all liability for any | |||
problems which are caused by the shared use of these VID/PID pairs. |
@@ -0,0 +1,188 @@ | |||
/* Name: asmcommon.inc | |||
* Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers | |||
* Author: Christian Starkjohann | |||
* Creation Date: 2007-11-05 | |||
* Tabsize: 4 | |||
* Copyright: (c) 2007 by OBJECTIVE DEVELOPMENT Software GmbH | |||
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) | |||
* Revision: $Id$ | |||
*/ | |||
/* Do not link this file! Link usbdrvasm.S instead, which includes the | |||
* appropriate implementation! | |||
*/ | |||
/* | |||
General Description: | |||
This file contains assembler code which is shared among the USB driver | |||
implementations for different CPU cocks. Since the code must be inserted | |||
in the middle of the module, it's split out into this file and #included. | |||
Jump destinations called from outside: | |||
sofError: Called when no start sequence was found. | |||
se0: Called when a package has been successfully received. | |||
overflow: Called when receive buffer overflows. | |||
doReturn: Called after sending data. | |||
Outside jump destinations used by this module: | |||
waitForJ: Called to receive an already arriving packet. | |||
sendAckAndReti: | |||
sendNakAndReti: | |||
sendCntAndReti: | |||
usbSendAndReti: | |||
The following macros must be defined before this file is included: | |||
.macro POP_STANDARD | |||
.endm | |||
.macro POP_RETI | |||
.endm | |||
*/ | |||
#define token x1 | |||
overflow: | |||
ldi x2, 1<<USB_INTR_PENDING_BIT | |||
USB_STORE_PENDING(x2) ; clear any pending interrupts | |||
ignorePacket: | |||
clr token | |||
rjmp storeTokenAndReturn | |||
;---------------------------------------------------------------------------- | |||
; Processing of received packet (numbers in brackets are cycles after center of SE0) | |||
;---------------------------------------------------------------------------- | |||
;This is the only non-error exit point for the software receiver loop | |||
;we don't check any CRCs here because there is no time left. | |||
se0: | |||
subi cnt, USB_BUFSIZE ;[5] | |||
neg cnt ;[6] | |||
sub YL, cnt ;[7] | |||
sbci YH, 0 ;[8] | |||
ldi x2, 1<<USB_INTR_PENDING_BIT ;[9] | |||
USB_STORE_PENDING(x2) ;[10] clear pending intr and check flag later. SE0 should be over. | |||
ld token, y ;[11] | |||
cpi token, USBPID_DATA0 ;[13] | |||
breq handleData ;[14] | |||
cpi token, USBPID_DATA1 ;[15] | |||
breq handleData ;[16] | |||
lds shift, usbDeviceAddr;[17] | |||
ldd x2, y+1 ;[19] ADDR and 1 bit endpoint number | |||
lsl x2 ;[21] shift out 1 bit endpoint number | |||
cpse x2, shift ;[22] | |||
rjmp ignorePacket ;[23] | |||
/* only compute endpoint number in x3 if required later */ | |||
#if USB_CFG_HAVE_INTRIN_ENDPOINT || USB_CFG_IMPLEMENT_FN_WRITEOUT | |||
ldd x3, y+2 ;[24] endpoint number + crc | |||
rol x3 ;[26] shift in LSB of endpoint | |||
#endif | |||
cpi token, USBPID_IN ;[27] | |||
breq handleIn ;[28] | |||
cpi token, USBPID_SETUP ;[29] | |||
breq handleSetupOrOut ;[30] | |||
cpi token, USBPID_OUT ;[31] | |||
brne ignorePacket ;[32] must be ack, nak or whatever | |||
; rjmp handleSetupOrOut ; fallthrough | |||
;Setup and Out are followed by a data packet two bit times (16 cycles) after | |||
;the end of SE0. The sync code allows up to 40 cycles delay from the start of | |||
;the sync pattern until the first bit is sampled. That's a total of 56 cycles. | |||
handleSetupOrOut: ;[32] | |||
#if USB_CFG_IMPLEMENT_FN_WRITEOUT /* if we have data for endpoint != 0, set usbCurrentTok to address */ | |||
andi x3, 0xf ;[32] | |||
breq storeTokenAndReturn ;[33] | |||
mov token, x3 ;[34] indicate that this is endpoint x OUT | |||
#endif | |||
storeTokenAndReturn: | |||
sts usbCurrentTok, token;[35] | |||
doReturn: | |||
POP_STANDARD ;[37] 12...16 cycles | |||
USB_LOAD_PENDING(YL) ;[49] | |||
sbrc YL, USB_INTR_PENDING_BIT;[50] check whether data is already arriving | |||
rjmp waitForJ ;[51] save the pops and pushes -- a new interrupt is already pending | |||
sofError: | |||
POP_RETI ;macro call | |||
reti | |||
handleData: | |||
#if USB_CFG_CHECK_CRC | |||
CRC_CLEANUP_AND_CHECK ; jumps to ignorePacket if CRC error | |||
#endif | |||
lds shift, usbCurrentTok;[18] | |||
tst shift ;[20] | |||
breq doReturn ;[21] | |||
lds x2, usbRxLen ;[22] | |||
tst x2 ;[24] | |||
brne sendNakAndReti ;[25] | |||
; 2006-03-11: The following two lines fix a problem where the device was not | |||
; recognized if usbPoll() was called less frequently than once every 4 ms. | |||
cpi cnt, 4 ;[26] zero sized data packets are status phase only -- ignore and ack | |||
brmi sendAckAndReti ;[27] keep rx buffer clean -- we must not NAK next SETUP | |||
#if USB_CFG_CHECK_DATA_TOGGLING | |||
sts usbCurrentDataToken, token ; store for checking by C code | |||
#endif | |||
sts usbRxLen, cnt ;[28] store received data, swap buffers | |||
sts usbRxToken, shift ;[30] | |||
lds x2, usbInputBufOffset;[32] swap buffers | |||
ldi cnt, USB_BUFSIZE ;[34] | |||
sub cnt, x2 ;[35] | |||
sts usbInputBufOffset, cnt;[36] buffers now swapped | |||
rjmp sendAckAndReti ;[38] 40 + 17 = 57 until SOP | |||
handleIn: | |||
;We don't send any data as long as the C code has not processed the current | |||
;input data and potentially updated the output data. That's more efficient | |||
;in terms of code size than clearing the tx buffers when a packet is received. | |||
lds x1, usbRxLen ;[30] | |||
cpi x1, 1 ;[32] negative values are flow control, 0 means "buffer free" | |||
brge sendNakAndReti ;[33] unprocessed input packet? | |||
ldi x1, USBPID_NAK ;[34] prepare value for usbTxLen | |||
#if USB_CFG_HAVE_INTRIN_ENDPOINT | |||
andi x3, 0xf ;[35] x3 contains endpoint | |||
#if USB_CFG_SUPPRESS_INTR_CODE | |||
brne sendNakAndReti ;[36] | |||
#else | |||
brne handleIn1 ;[36] | |||
#endif | |||
#endif | |||
lds cnt, usbTxLen ;[37] | |||
sbrc cnt, 4 ;[39] all handshake tokens have bit 4 set | |||
rjmp sendCntAndReti ;[40] 42 + 16 = 58 until SOP | |||
sts usbTxLen, x1 ;[41] x1 == USBPID_NAK from above | |||
ldi YL, lo8(usbTxBuf) ;[43] | |||
ldi YH, hi8(usbTxBuf) ;[44] | |||
rjmp usbSendAndReti ;[45] 57 + 12 = 59 until SOP | |||
; Comment about when to set usbTxLen to USBPID_NAK: | |||
; We should set it back when we receive the ACK from the host. This would | |||
; be simple to implement: One static variable which stores whether the last | |||
; tx was for endpoint 0 or 1 and a compare in the receiver to distinguish the | |||
; ACK. However, we set it back immediately when we send the package, | |||
; assuming that no error occurs and the host sends an ACK. We save one byte | |||
; RAM this way and avoid potential problems with endless retries. The rest of | |||
; the driver assumes error-free transfers anyway. | |||
#if !USB_CFG_SUPPRESS_INTR_CODE && USB_CFG_HAVE_INTRIN_ENDPOINT /* placed here due to relative jump range */ | |||
handleIn1: ;[38] | |||
#if USB_CFG_HAVE_INTRIN_ENDPOINT3 | |||
; 2006-06-10 as suggested by O.Tamura: support second INTR IN / BULK IN endpoint | |||
cpi x3, USB_CFG_EP3_NUMBER;[38] | |||
breq handleIn3 ;[39] | |||
#endif | |||
lds cnt, usbTxLen1 ;[40] | |||
sbrc cnt, 4 ;[42] all handshake tokens have bit 4 set | |||
rjmp sendCntAndReti ;[43] 47 + 16 = 63 until SOP | |||
sts usbTxLen1, x1 ;[44] x1 == USBPID_NAK from above | |||
ldi YL, lo8(usbTxBuf1) ;[46] | |||
ldi YH, hi8(usbTxBuf1) ;[47] | |||
rjmp usbSendAndReti ;[48] 50 + 12 = 62 until SOP | |||
#if USB_CFG_HAVE_INTRIN_ENDPOINT3 | |||
handleIn3: | |||
lds cnt, usbTxLen3 ;[41] | |||
sbrc cnt, 4 ;[43] | |||
rjmp sendCntAndReti ;[44] 49 + 16 = 65 until SOP | |||
sts usbTxLen3, x1 ;[45] x1 == USBPID_NAK from above | |||
ldi YL, lo8(usbTxBuf3) ;[47] | |||
ldi YH, hi8(usbTxBuf3) ;[48] | |||
rjmp usbSendAndReti ;[49] 51 + 12 = 63 until SOP | |||
#endif | |||
#endif |
@@ -0,0 +1,50 @@ | |||
/* Name: oddebug.c | |||
* Project: AVR library | |||
* Author: Christian Starkjohann | |||
* Creation Date: 2005-01-16 | |||
* Tabsize: 4 | |||
* Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH | |||
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) | |||
* This Revision: $Id: oddebug.c 692 2008-11-07 15:07:40Z cs $ | |||
*/ | |||
#include "oddebug.h" | |||
#if DEBUG_LEVEL > 0 | |||
#warning "Never compile production devices with debugging enabled" | |||
static void uartPutc(char c) | |||
{ | |||
while(!(ODDBG_USR & (1 << ODDBG_UDRE))); /* wait for data register empty */ | |||
ODDBG_UDR = c; | |||
} | |||
static uchar hexAscii(uchar h) | |||
{ | |||
h &= 0xf; | |||
if(h >= 10) | |||
h += 'a' - (uchar)10 - '0'; | |||
h += '0'; | |||
return h; | |||
} | |||
static void printHex(uchar c) | |||
{ | |||
uartPutc(hexAscii(c >> 4)); | |||
uartPutc(hexAscii(c)); | |||
} | |||
void odDebug(uchar prefix, uchar *data, uchar len) | |||
{ | |||
printHex(prefix); | |||
uartPutc(':'); | |||
while(len--){ | |||
uartPutc(' '); | |||
printHex(*data++); | |||
} | |||
uartPutc('\r'); | |||
uartPutc('\n'); | |||
} | |||
#endif |
@@ -0,0 +1,123 @@ | |||
/* Name: oddebug.h | |||
* Project: AVR library | |||
* Author: Christian Starkjohann | |||
* Creation Date: 2005-01-16 | |||
* Tabsize: 4 | |||
* Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH | |||
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) | |||
* This Revision: $Id: oddebug.h 692 2008-11-07 15:07:40Z cs $ | |||
*/ | |||
#ifndef __oddebug_h_included__ | |||
#define __oddebug_h_included__ | |||
/* | |||
General Description: | |||
This module implements a function for debug logs on the serial line of the | |||
AVR microcontroller. Debugging can be configured with the define | |||
'DEBUG_LEVEL'. If this macro is not defined or defined to 0, all debugging | |||
calls are no-ops. If it is 1, DBG1 logs will appear, but not DBG2. If it is | |||
2, DBG1 and DBG2 logs will be printed. | |||
A debug log consists of a label ('prefix') to indicate which debug log created | |||
the output and a memory block to dump in hex ('data' and 'len'). | |||
*/ | |||
#ifndef F_CPU | |||
# define F_CPU 12000000 /* 12 MHz */ | |||
#endif | |||
/* make sure we have the UART defines: */ | |||
#include "usbportability.h" | |||
#ifndef uchar | |||
# define uchar unsigned char | |||
#endif | |||
#if DEBUG_LEVEL > 0 && !(defined TXEN || defined TXEN0) /* no UART in device */ | |||
# warning "Debugging disabled because device has no UART" | |||
# undef DEBUG_LEVEL | |||
#endif | |||
#ifndef DEBUG_LEVEL | |||
# define DEBUG_LEVEL 0 | |||
#endif | |||
/* ------------------------------------------------------------------------- */ | |||
#if DEBUG_LEVEL > 0 | |||
# define DBG1(prefix, data, len) odDebug(prefix, data, len) | |||
#else | |||
# define DBG1(prefix, data, len) | |||
#endif | |||
#if DEBUG_LEVEL > 1 | |||
# define DBG2(prefix, data, len) odDebug(prefix, data, len) | |||
#else | |||
# define DBG2(prefix, data, len) | |||
#endif | |||
/* ------------------------------------------------------------------------- */ | |||
#if DEBUG_LEVEL > 0 | |||
extern void odDebug(uchar prefix, uchar *data, uchar len); | |||
/* Try to find our control registers; ATMEL likes to rename these */ | |||
#if defined UBRR | |||
# define ODDBG_UBRR UBRR | |||
#elif defined UBRRL | |||
# define ODDBG_UBRR UBRRL | |||
#elif defined UBRR0 | |||
# define ODDBG_UBRR UBRR0 | |||
#elif defined UBRR0L | |||
# define ODDBG_UBRR UBRR0L | |||
#endif | |||
#if defined UCR | |||
# define ODDBG_UCR UCR | |||
#elif defined UCSRB | |||
# define ODDBG_UCR UCSRB | |||
#elif defined UCSR0B | |||
# define ODDBG_UCR UCSR0B | |||
#endif | |||
#if defined TXEN | |||
# define ODDBG_TXEN TXEN | |||
#else | |||
# define ODDBG_TXEN TXEN0 | |||
#endif | |||
#if defined USR | |||
# define ODDBG_USR USR | |||
#elif defined UCSRA | |||
# define ODDBG_USR UCSRA | |||
#elif defined UCSR0A | |||
# define ODDBG_USR UCSR0A | |||
#endif | |||
#if defined UDRE | |||
# define ODDBG_UDRE UDRE | |||
#else | |||
# define ODDBG_UDRE UDRE0 | |||
#endif | |||
#if defined UDR | |||
# define ODDBG_UDR UDR | |||
#elif defined UDR0 | |||
# define ODDBG_UDR UDR0 | |||
#endif | |||
static inline void odDebugInit(void) | |||
{ | |||
ODDBG_UCR |= (1<<ODDBG_TXEN); | |||
ODDBG_UBRR = F_CPU / (19200 * 16L) - 1; | |||
} | |||
#else | |||
# define odDebugInit() | |||
#endif | |||
/* ------------------------------------------------------------------------- */ | |||
#endif /* __oddebug_h_included__ */ |
@@ -0,0 +1,376 @@ | |||
/* Name: usbconfig.h | |||
* Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers | |||
* Author: Christian Starkjohann | |||
* Creation Date: 2005-04-01 | |||
* Tabsize: 4 | |||
* Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH | |||
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) | |||
* This Revision: $Id: usbconfig-prototype.h 785 2010-05-30 17:57:07Z cs $ | |||
*/ | |||
#ifndef __usbconfig_h_included__ | |||
#define __usbconfig_h_included__ | |||
/* | |||
General Description: | |||
This file is an example configuration (with inline documentation) for the USB | |||
driver. It configures V-USB for USB D+ connected to Port D bit 2 (which is | |||
also hardware interrupt 0 on many devices) and USB D- to Port D bit 4. You may | |||
wire the lines to any other port, as long as D+ is also wired to INT0 (or any | |||
other hardware interrupt, as long as it is the highest level interrupt, see | |||
section at the end of this file). | |||
+ To create your own usbconfig.h file, copy this file to your project's | |||
+ firmware source directory) and rename it to "usbconfig.h". | |||
+ Then edit it accordingly. | |||
*/ | |||
/* ---------------------------- Hardware Config ---------------------------- */ | |||
#define USB_CFG_IOPORTNAME D | |||
/* This is the port where the USB bus is connected. When you configure it to | |||
* "B", the registers PORTB, PINB and DDRB will be used. | |||
*/ | |||
#define USB_CFG_DMINUS_BIT 4 | |||
/* This is the bit number in USB_CFG_IOPORT where the USB D- line is connected. | |||
* This may be any bit in the port. | |||
*/ | |||
#define USB_CFG_DPLUS_BIT 2 | |||
/* This is the bit number in USB_CFG_IOPORT where the USB D+ line is connected. | |||
* This may be any bit in the port. Please note that D+ must also be connected | |||
* to interrupt pin INT0! [You can also use other interrupts, see section | |||
* "Optional MCU Description" below, or you can connect D- to the interrupt, as | |||
* it is required if you use the USB_COUNT_SOF feature. If you use D- for the | |||
* interrupt, the USB interrupt will also be triggered at Start-Of-Frame | |||
* markers every millisecond.] | |||
*/ | |||
#define USB_CFG_CLOCK_KHZ (F_CPU/1000) | |||
/* Clock rate of the AVR in kHz. Legal values are 12000, 12800, 15000, 16000, | |||
* 16500, 18000 and 20000. The 12.8 MHz and 16.5 MHz versions of the code | |||
* require no crystal, they tolerate +/- 1% deviation from the nominal | |||
* frequency. All other rates require a precision of 2000 ppm and thus a | |||
* crystal! | |||
* Since F_CPU should be defined to your actual clock rate anyway, you should | |||
* not need to modify this setting. | |||
*/ | |||
#define USB_CFG_CHECK_CRC 0 | |||
/* Define this to 1 if you want that the driver checks integrity of incoming | |||
* data packets (CRC checks). CRC checks cost quite a bit of code size and are | |||
* currently only available for 18 MHz crystal clock. You must choose | |||
* USB_CFG_CLOCK_KHZ = 18000 if you enable this option. | |||
*/ | |||
/* ----------------------- Optional Hardware Config ------------------------ */ | |||
/* #define USB_CFG_PULLUP_IOPORTNAME D */ | |||
/* If you connect the 1.5k pullup resistor from D- to a port pin instead of | |||
* V+, you can connect and disconnect the device from firmware by calling | |||
* the macros usbDeviceConnect() and usbDeviceDisconnect() (see usbdrv.h). | |||
* This constant defines the port on which the pullup resistor is connected. | |||
*/ | |||
/* #define USB_CFG_PULLUP_BIT 4 */ | |||
/* This constant defines the bit number in USB_CFG_PULLUP_IOPORT (defined | |||
* above) where the 1.5k pullup resistor is connected. See description | |||
* above for details. | |||
*/ | |||
/* --------------------------- Functional Range ---------------------------- */ | |||
#define USB_CFG_HAVE_INTRIN_ENDPOINT 0 | |||
/* Define this to 1 if you want to compile a version with two endpoints: The | |||
* default control endpoint 0 and an interrupt-in endpoint (any other endpoint | |||
* number). | |||
*/ | |||
#define USB_CFG_HAVE_INTRIN_ENDPOINT3 0 | |||
/* Define this to 1 if you want to compile a version with three endpoints: The | |||
* default control endpoint 0, an interrupt-in endpoint 3 (or the number | |||
* configured below) and a catch-all default interrupt-in endpoint as above. | |||
* You must also define USB_CFG_HAVE_INTRIN_ENDPOINT to 1 for this feature. | |||
*/ | |||
#define USB_CFG_EP3_NUMBER 3 | |||
/* If the so-called endpoint 3 is used, it can now be configured to any other | |||
* endpoint number (except 0) with this macro. Default if undefined is 3. | |||
*/ | |||
/* #define USB_INITIAL_DATATOKEN USBPID_DATA1 */ | |||
/* The above macro defines the startup condition for data toggling on the | |||
* interrupt/bulk endpoints 1 and 3. Defaults to USBPID_DATA1. | |||
* Since the token is toggled BEFORE sending any data, the first packet is | |||
* sent with the oposite value of this configuration! | |||
*/ | |||
#define USB_CFG_IMPLEMENT_HALT 0 | |||
/* Define this to 1 if you also want to implement the ENDPOINT_HALT feature | |||
* for endpoint 1 (interrupt endpoint). Although you may not need this feature, | |||
* it is required by the standard. We have made it a config option because it | |||
* bloats the code considerably. | |||
*/ | |||
#define USB_CFG_SUPPRESS_INTR_CODE 0 | |||
/* Define this to 1 if you want to declare interrupt-in endpoints, but don't | |||
* want to send any data over them. If this macro is defined to 1, functions | |||
* usbSetInterrupt() and usbSetInterrupt3() are omitted. This is useful if | |||
* you need the interrupt-in endpoints in order to comply to an interface | |||
* (e.g. HID), but never want to send any data. This option saves a couple | |||
* of bytes in flash memory and the transmit buffers in RAM. | |||
*/ | |||
#define USB_CFG_INTR_POLL_INTERVAL 10 | |||
/* If you compile a version with endpoint 1 (interrupt-in), this is the poll | |||
* interval. The value is in milliseconds and must not be less than 10 ms for | |||
* low speed devices. | |||
*/ | |||
#define USB_CFG_IS_SELF_POWERED 0 | |||
/* Define this to 1 if the device has its own power supply. Set it to 0 if the | |||
* device is powered from the USB bus. | |||
*/ | |||
#define USB_CFG_MAX_BUS_POWER 100 | |||
/* Set this variable to the maximum USB bus power consumption of your device. | |||
* The value is in milliamperes. [It will be divided by two since USB | |||
* communicates power requirements in units of 2 mA.] | |||
*/ | |||
#define USB_CFG_IMPLEMENT_FN_WRITE 0 | |||
/* Set this to 1 if you want usbFunctionWrite() to be called for control-out | |||
* transfers. Set it to 0 if you don't need it and want to save a couple of | |||
* bytes. | |||
*/ | |||
#define USB_CFG_IMPLEMENT_FN_READ 0 | |||
/* Set this to 1 if you need to send control replies which are generated | |||
* "on the fly" when usbFunctionRead() is called. If you only want to send | |||
* data from a static buffer, set it to 0 and return the data from | |||
* usbFunctionSetup(). This saves a couple of bytes. | |||
*/ | |||
#define USB_CFG_IMPLEMENT_FN_WRITEOUT 0 | |||
/* Define this to 1 if you want to use interrupt-out (or bulk out) endpoints. | |||
* You must implement the function usbFunctionWriteOut() which receives all | |||
* interrupt/bulk data sent to any endpoint other than 0. The endpoint number | |||
* can be found in 'usbRxToken'. | |||
*/ | |||
#define USB_CFG_HAVE_FLOWCONTROL 0 | |||
/* Define this to 1 if you want flowcontrol over USB data. See the definition | |||
* of the macros usbDisableAllRequests() and usbEnableAllRequests() in | |||
* usbdrv.h. | |||
*/ | |||
#define USB_CFG_DRIVER_FLASH_PAGE 0 | |||
/* If the device has more than 64 kBytes of flash, define this to the 64 k page | |||
* where the driver's constants (descriptors) are located. Or in other words: | |||
* Define this to 1 for boot loaders on the ATMega128. | |||
*/ | |||
#define USB_CFG_LONG_TRANSFERS 0 | |||
/* Define this to 1 if you want to send/receive blocks of more than 254 bytes | |||
* in a single control-in or control-out transfer. Note that the capability | |||
* for long transfers increases the driver size. | |||
*/ | |||
/* #define USB_RX_USER_HOOK(data, len) if(usbRxToken == (uchar)USBPID_SETUP) blinkLED(); */ | |||
/* This macro is a hook if you want to do unconventional things. If it is | |||
* defined, it's inserted at the beginning of received message processing. | |||
* If you eat the received message and don't want default processing to | |||
* proceed, do a return after doing your things. One possible application | |||
* (besides debugging) is to flash a status LED on each packet. | |||
*/ | |||
/* #define USB_RESET_HOOK(resetStarts) if(!resetStarts){hadUsbReset();} */ | |||
/* This macro is a hook if you need to know when an USB RESET occurs. It has | |||
* one parameter which distinguishes between the start of RESET state and its | |||
* end. | |||
*/ | |||
/* #define USB_SET_ADDRESS_HOOK() hadAddressAssigned(); */ | |||
/* This macro (if defined) is executed when a USB SET_ADDRESS request was | |||
* received. | |||
*/ | |||
#define USB_COUNT_SOF 0 | |||
/* define this macro to 1 if you need the global variable "usbSofCount" which | |||
* counts SOF packets. This feature requires that the hardware interrupt is | |||
* connected to D- instead of D+. | |||
*/ | |||
/* #ifdef __ASSEMBLER__ | |||
* macro myAssemblerMacro | |||
* in YL, TCNT0 | |||
* sts timer0Snapshot, YL | |||
* endm | |||
* #endif | |||
* #define USB_SOF_HOOK myAssemblerMacro | |||
* This macro (if defined) is executed in the assembler module when a | |||
* Start Of Frame condition is detected. It is recommended to define it to | |||
* the name of an assembler macro which is defined here as well so that more | |||
* than one assembler instruction can be used. The macro may use the register | |||
* YL and modify SREG. If it lasts longer than a couple of cycles, USB messages | |||
* immediately after an SOF pulse may be lost and must be retried by the host. | |||
* What can you do with this hook? Since the SOF signal occurs exactly every | |||
* 1 ms (unless the host is in sleep mode), you can use it to tune OSCCAL in | |||
* designs running on the internal RC oscillator. | |||
* Please note that Start Of Frame detection works only if D- is wired to the | |||
* interrupt, not D+. THIS IS DIFFERENT THAN MOST EXAMPLES! | |||
*/ | |||
#define USB_CFG_CHECK_DATA_TOGGLING 0 | |||
/* define this macro to 1 if you want to filter out duplicate data packets | |||
* sent by the host. Duplicates occur only as a consequence of communication | |||
* errors, when the host does not receive an ACK. Please note that you need to | |||
* implement the filtering yourself in usbFunctionWriteOut() and | |||
* usbFunctionWrite(). Use the global usbCurrentDataToken and a static variable | |||
* for each control- and out-endpoint to check for duplicate packets. | |||
*/ | |||
#define USB_CFG_HAVE_MEASURE_FRAME_LENGTH 0 | |||
/* define this macro to 1 if you want the function usbMeasureFrameLength() | |||
* compiled in. This function can be used to calibrate the AVR's RC oscillator. | |||
*/ | |||
#define USB_USE_FAST_CRC 0 | |||
/* The assembler module has two implementations for the CRC algorithm. One is | |||
* faster, the other is smaller. This CRC routine is only used for transmitted | |||
* messages where timing is not critical. The faster routine needs 31 cycles | |||
* per byte while the smaller one needs 61 to 69 cycles. The faster routine | |||
* may be worth the 32 bytes bigger code size if you transmit lots of data and | |||
* run the AVR close to its limit. | |||
*/ | |||
/* -------------------------- Device Description --------------------------- */ | |||
#define USB_CFG_VENDOR_ID 0xc0, 0x16 /* = 0x16c0 = 5824 = voti.nl */ | |||
/* USB vendor ID for the device, low byte first. If you have registered your | |||
* own Vendor ID, define it here. Otherwise you may use one of obdev's free | |||
* shared VID/PID pairs. Be sure to read USB-IDs-for-free.txt for rules! | |||
* *** IMPORTANT NOTE *** | |||
* This template uses obdev's shared VID/PID pair for Vendor Class devices | |||
* with libusb: 0x16c0/0x5dc. Use this VID/PID pair ONLY if you understand | |||
* the implications! | |||
*/ | |||
#define USB_CFG_DEVICE_ID 0xdc, 0x05 /* = 0x05dc = 1500 */ | |||
/* This is the ID of the product, low byte first. It is interpreted in the | |||
* scope of the vendor ID. If you have registered your own VID with usb.org | |||
* or if you have licensed a PID from somebody else, define it here. Otherwise | |||
* you may use one of obdev's free shared VID/PID pairs. See the file | |||
* USB-IDs-for-free.txt for details! | |||
* *** IMPORTANT NOTE *** | |||
* This template uses obdev's shared VID/PID pair for Vendor Class devices | |||
* with libusb: 0x16c0/0x5dc. Use this VID/PID pair ONLY if you understand | |||
* the implications! | |||
*/ | |||
#define USB_CFG_DEVICE_VERSION 0x00, 0x01 | |||
/* Version number of the device: Minor number first, then major number. | |||
*/ | |||
#define USB_CFG_VENDOR_NAME 'o', 'b', 'd', 'e', 'v', '.', 'a', 't' | |||
#define USB_CFG_VENDOR_NAME_LEN 8 | |||
/* These two values define the vendor name returned by the USB device. The name | |||
* must be given as a list of characters under single quotes. The characters | |||
* are interpreted as Unicode (UTF-16) entities. | |||
* If you don't want a vendor name string, undefine these macros. | |||
* ALWAYS define a vendor name containing your Internet domain name if you use | |||
* obdev's free shared VID/PID pair. See the file USB-IDs-for-free.txt for | |||
* details. | |||
*/ | |||
#define USB_CFG_DEVICE_NAME 'T', 'e', 'm', 'p', 'l', 'a', 't', 'e' | |||
#define USB_CFG_DEVICE_NAME_LEN 8 | |||
/* Same as above for the device name. If you don't want a device name, undefine | |||
* the macros. See the file USB-IDs-for-free.txt before you assign a name if | |||
* you use a shared VID/PID. | |||
*/ | |||
/*#define USB_CFG_SERIAL_NUMBER 'N', 'o', 'n', 'e' */ | |||
/*#define USB_CFG_SERIAL_NUMBER_LEN 0 */ | |||
/* Same as above for the serial number. If you don't want a serial number, | |||
* undefine the macros. | |||
* It may be useful to provide the serial number through other means than at | |||
* compile time. See the section about descriptor properties below for how | |||
* to fine tune control over USB descriptors such as the string descriptor | |||
* for the serial number. | |||
*/ | |||
#define USB_CFG_DEVICE_CLASS 0xff /* set to 0 if deferred to interface */ | |||
#define USB_CFG_DEVICE_SUBCLASS 0 | |||
/* See USB specification if you want to conform to an existing device class. | |||
* Class 0xff is "vendor specific". | |||
*/ | |||
#define USB_CFG_INTERFACE_CLASS 0 /* define class here if not at device level */ | |||
#define USB_CFG_INTERFACE_SUBCLASS 0 | |||
#define USB_CFG_INTERFACE_PROTOCOL 0 | |||
/* See USB specification if you want to conform to an existing device class or | |||
* protocol. The following classes must be set at interface level: | |||
* HID class is 3, no subclass and protocol required (but may be useful!) | |||
* CDC class is 2, use subclass 2 and protocol 1 for ACM | |||
*/ | |||
/* #define USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH 42 */ | |||
/* Define this to the length of the HID report descriptor, if you implement | |||
* an HID device. Otherwise don't define it or define it to 0. | |||
* If you use this define, you must add a PROGMEM character array named | |||
* "usbHidReportDescriptor" to your code which contains the report descriptor. | |||
* Don't forget to keep the array and this define in sync! | |||
*/ | |||
/* #define USB_PUBLIC static */ | |||
/* Use the define above if you #include usbdrv.c instead of linking against it. | |||
* This technique saves a couple of bytes in flash memory. | |||
*/ | |||
/* ------------------- Fine Control over USB Descriptors ------------------- */ | |||
/* If you don't want to use the driver's default USB descriptors, you can | |||
* provide our own. These can be provided as (1) fixed length static data in | |||
* flash memory, (2) fixed length static data in RAM or (3) dynamically at | |||
* runtime in the function usbFunctionDescriptor(). See usbdrv.h for more | |||
* information about this function. | |||
* Descriptor handling is configured through the descriptor's properties. If | |||
* no properties are defined or if they are 0, the default descriptor is used. | |||
* Possible properties are: | |||
* + USB_PROP_IS_DYNAMIC: The data for the descriptor should be fetched | |||
* at runtime via usbFunctionDescriptor(). If the usbMsgPtr mechanism is | |||
* used, the data is in FLASH by default. Add property USB_PROP_IS_RAM if | |||
* you want RAM pointers. | |||
* + USB_PROP_IS_RAM: The data returned by usbFunctionDescriptor() or found | |||
* in static memory is in RAM, not in flash memory. | |||
* + USB_PROP_LENGTH(len): If the data is in static memory (RAM or flash), | |||
* the driver must know the descriptor's length. The descriptor itself is | |||
* found at the address of a well known identifier (see below). | |||
* List of static descriptor names (must be declared PROGMEM if in flash): | |||
* char usbDescriptorDevice[]; | |||
* char usbDescriptorConfiguration[]; | |||
* char usbDescriptorHidReport[]; | |||
* char usbDescriptorString0[]; | |||
* int usbDescriptorStringVendor[]; | |||
* int usbDescriptorStringDevice[]; | |||
* int usbDescriptorStringSerialNumber[]; | |||
* Other descriptors can't be provided statically, they must be provided | |||
* dynamically at runtime. | |||
* | |||
* Descriptor properties are or-ed or added together, e.g.: | |||
* #define USB_CFG_DESCR_PROPS_DEVICE (USB_PROP_IS_RAM | USB_PROP_LENGTH(18)) | |||
* | |||
* The following descriptors are defined: | |||
* USB_CFG_DESCR_PROPS_DEVICE | |||
* USB_CFG_DESCR_PROPS_CONFIGURATION | |||
* USB_CFG_DESCR_PROPS_STRINGS | |||
* USB_CFG_DESCR_PROPS_STRING_0 | |||
* USB_CFG_DESCR_PROPS_STRING_VENDOR | |||
* USB_CFG_DESCR_PROPS_STRING_PRODUCT | |||
* USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER | |||
* USB_CFG_DESCR_PROPS_HID | |||
* USB_CFG_DESCR_PROPS_HID_REPORT | |||
* USB_CFG_DESCR_PROPS_UNKNOWN (for all descriptors not handled by the driver) | |||
* | |||
* Note about string descriptors: String descriptors are not just strings, they | |||
* are Unicode strings prefixed with a 2 byte header. Example: | |||
* int serialNumberDescriptor[] = { | |||
* USB_STRING_DESCRIPTOR_HEADER(6), | |||
* 'S', 'e', 'r', 'i', 'a', 'l' | |||
* }; | |||
*/ | |||
#define USB_CFG_DESCR_PROPS_DEVICE 0 | |||
#define USB_CFG_DESCR_PROPS_CONFIGURATION 0 | |||
#define USB_CFG_DESCR_PROPS_STRINGS 0 | |||
#define USB_CFG_DESCR_PROPS_STRING_0 0 | |||
#define USB_CFG_DESCR_PROPS_STRING_VENDOR 0 | |||
#define USB_CFG_DESCR_PROPS_STRING_PRODUCT 0 | |||
#define USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER 0 | |||
#define USB_CFG_DESCR_PROPS_HID 0 | |||
#define USB_CFG_DESCR_PROPS_HID_REPORT 0 | |||
#define USB_CFG_DESCR_PROPS_UNKNOWN 0 | |||
/* ----------------------- Optional MCU Description ------------------------ */ | |||
/* The following configurations have working defaults in usbdrv.h. You | |||
* usually don't need to set them explicitly. Only if you want to run | |||
* the driver on a device which is not yet supported or with a compiler | |||
* which is not fully supported (such as IAR C) or if you use a differnt | |||
* interrupt than INT0, you may have to define some of these. | |||
*/ | |||
/* #define USB_INTR_CFG MCUCR */ | |||
/* #define USB_INTR_CFG_SET ((1 << ISC00) | (1 << ISC01)) */ | |||
/* #define USB_INTR_CFG_CLR 0 */ | |||
/* #define USB_INTR_ENABLE GIMSK */ | |||
/* #define USB_INTR_ENABLE_BIT INT0 */ | |||
/* #define USB_INTR_PENDING GIFR */ | |||
/* #define USB_INTR_PENDING_BIT INTF0 */ | |||
/* #define USB_INTR_VECTOR INT0_vect */ | |||
#endif /* __usbconfig_h_included__ */ |
@@ -0,0 +1,625 @@ | |||
/* Name: usbdrv.c | |||
* Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers | |||
* Author: Christian Starkjohann | |||
* Creation Date: 2004-12-29 | |||
* Tabsize: 4 | |||
* Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH | |||
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) | |||
* This Revision: $Id: usbdrv.c 791 2010-07-15 15:56:13Z cs $ | |||
*/ | |||
#include "usbportability.h" | |||
#include "usbdrv.h" | |||
#include "oddebug.h" | |||
/* | |||
General Description: | |||
This module implements the C-part of the USB driver. See usbdrv.h for a | |||
documentation of the entire driver. | |||
*/ | |||
/* ------------------------------------------------------------------------- */ | |||
/* raw USB registers / interface to assembler code: */ | |||
uchar usbRxBuf[2*USB_BUFSIZE]; /* raw RX buffer: PID, 8 bytes data, 2 bytes CRC */ | |||
uchar usbInputBufOffset; /* offset in usbRxBuf used for low level receiving */ | |||
uchar usbDeviceAddr; /* assigned during enumeration, defaults to 0 */ | |||
uchar usbNewDeviceAddr; /* device ID which should be set after status phase */ | |||
uchar usbConfiguration; /* currently selected configuration. Administered by driver, but not used */ | |||
volatile schar usbRxLen; /* = 0; number of bytes in usbRxBuf; 0 means free, -1 for flow control */ | |||
uchar usbCurrentTok; /* last token received or endpoint number for last OUT token if != 0 */ | |||
uchar usbRxToken; /* token for data we received; or endpont number for last OUT */ | |||
volatile uchar usbTxLen = USBPID_NAK; /* number of bytes to transmit with next IN token or handshake token */ | |||
uchar usbTxBuf[USB_BUFSIZE];/* data to transmit with next IN, free if usbTxLen contains handshake token */ | |||
#if USB_COUNT_SOF | |||
volatile uchar usbSofCount; /* incremented by assembler module every SOF */ | |||
#endif | |||
#if USB_CFG_HAVE_INTRIN_ENDPOINT && !USB_CFG_SUPPRESS_INTR_CODE | |||
usbTxStatus_t usbTxStatus1; | |||
# if USB_CFG_HAVE_INTRIN_ENDPOINT3 | |||
usbTxStatus_t usbTxStatus3; | |||
# endif | |||
#endif | |||
#if USB_CFG_CHECK_DATA_TOGGLING | |||
uchar usbCurrentDataToken;/* when we check data toggling to ignore duplicate packets */ | |||
#endif | |||
/* USB status registers / not shared with asm code */ | |||
uchar *usbMsgPtr; /* data to transmit next -- ROM or RAM address */ | |||
static usbMsgLen_t usbMsgLen = USB_NO_MSG; /* remaining number of bytes */ | |||
static uchar usbMsgFlags; /* flag values see below */ | |||
#define USB_FLG_MSGPTR_IS_ROM (1<<6) | |||
#define USB_FLG_USE_USER_RW (1<<7) | |||
/* | |||
optimizing hints: | |||
- do not post/pre inc/dec integer values in operations | |||
- assign value of USB_READ_FLASH() to register variables and don't use side effects in arg | |||
- use narrow scope for variables which should be in X/Y/Z register | |||
- assign char sized expressions to variables to force 8 bit arithmetics | |||
*/ | |||
/* -------------------------- String Descriptors --------------------------- */ | |||
#if USB_CFG_DESCR_PROPS_STRINGS == 0 | |||
#if USB_CFG_DESCR_PROPS_STRING_0 == 0 | |||
#undef USB_CFG_DESCR_PROPS_STRING_0 | |||
#define USB_CFG_DESCR_PROPS_STRING_0 sizeof(usbDescriptorString0) | |||
PROGMEM char usbDescriptorString0[] = { /* language descriptor */ | |||
4, /* sizeof(usbDescriptorString0): length of descriptor in bytes */ | |||
3, /* descriptor type */ | |||
0x09, 0x04, /* language index (0x0409 = US-English) */ | |||
}; | |||
#endif | |||
#if USB_CFG_DESCR_PROPS_STRING_VENDOR == 0 && USB_CFG_VENDOR_NAME_LEN | |||
#undef USB_CFG_DESCR_PROPS_STRING_VENDOR | |||
#define USB_CFG_DESCR_PROPS_STRING_VENDOR sizeof(usbDescriptorStringVendor) | |||
PROGMEM int usbDescriptorStringVendor[] = { | |||
USB_STRING_DESCRIPTOR_HEADER(USB_CFG_VENDOR_NAME_LEN), | |||
USB_CFG_VENDOR_NAME | |||
}; | |||
#endif | |||
#if USB_CFG_DESCR_PROPS_STRING_PRODUCT == 0 && USB_CFG_DEVICE_NAME_LEN | |||
#undef USB_CFG_DESCR_PROPS_STRING_PRODUCT | |||
#define USB_CFG_DESCR_PROPS_STRING_PRODUCT sizeof(usbDescriptorStringDevice) | |||
PROGMEM int usbDescriptorStringDevice[] = { | |||
USB_STRING_DESCRIPTOR_HEADER(USB_CFG_DEVICE_NAME_LEN), | |||
USB_CFG_DEVICE_NAME | |||
}; | |||
#endif | |||
#if USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER == 0 && USB_CFG_SERIAL_NUMBER_LEN | |||
#undef USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER | |||
#define USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER sizeof(usbDescriptorStringSerialNumber) | |||
PROGMEM int usbDescriptorStringSerialNumber[] = { | |||
USB_STRING_DESCRIPTOR_HEADER(USB_CFG_SERIAL_NUMBER_LEN), | |||
USB_CFG_SERIAL_NUMBER | |||
}; | |||
#endif | |||
#endif /* USB_CFG_DESCR_PROPS_STRINGS == 0 */ | |||
/* --------------------------- Device Descriptor --------------------------- */ | |||
#if USB_CFG_DESCR_PROPS_DEVICE == 0 | |||
#undef USB_CFG_DESCR_PROPS_DEVICE | |||
#define USB_CFG_DESCR_PROPS_DEVICE sizeof(usbDescriptorDevice) | |||
PROGMEM char usbDescriptorDevice[] = { /* USB device descriptor */ | |||
18, /* sizeof(usbDescriptorDevice): length of descriptor in bytes */ | |||
USBDESCR_DEVICE, /* descriptor type */ | |||
0x10, 0x01, /* USB version supported */ | |||
USB_CFG_DEVICE_CLASS, | |||
USB_CFG_DEVICE_SUBCLASS, | |||
0, /* protocol */ | |||
8, /* max packet size */ | |||
/* the following two casts affect the first byte of the constant only, but | |||
* that's sufficient to avoid a warning with the default values. | |||
*/ | |||
(char)USB_CFG_VENDOR_ID,/* 2 bytes */ | |||
(char)USB_CFG_DEVICE_ID,/* 2 bytes */ | |||
USB_CFG_DEVICE_VERSION, /* 2 bytes */ | |||
USB_CFG_DESCR_PROPS_STRING_VENDOR != 0 ? 1 : 0, /* manufacturer string index */ | |||
USB_CFG_DESCR_PROPS_STRING_PRODUCT != 0 ? 2 : 0, /* product string index */ | |||
USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER != 0 ? 3 : 0, /* serial number string index */ | |||
1, /* number of configurations */ | |||
}; | |||
#endif | |||
/* ----------------------- Configuration Descriptor ------------------------ */ | |||
#if USB_CFG_DESCR_PROPS_HID_REPORT != 0 && USB_CFG_DESCR_PROPS_HID == 0 | |||
#undef USB_CFG_DESCR_PROPS_HID | |||
#define USB_CFG_DESCR_PROPS_HID 9 /* length of HID descriptor in config descriptor below */ | |||
#endif | |||
#if USB_CFG_DESCR_PROPS_CONFIGURATION == 0 | |||
#undef USB_CFG_DESCR_PROPS_CONFIGURATION | |||
#define USB_CFG_DESCR_PROPS_CONFIGURATION sizeof(usbDescriptorConfiguration) | |||
PROGMEM char usbDescriptorConfiguration[] = { /* USB configuration descriptor */ | |||
9, /* sizeof(usbDescriptorConfiguration): length of descriptor in bytes */ | |||
USBDESCR_CONFIG, /* descriptor type */ | |||
18 + 7 * USB_CFG_HAVE_INTRIN_ENDPOINT + 7 * USB_CFG_HAVE_INTRIN_ENDPOINT3 + | |||
(USB_CFG_DESCR_PROPS_HID & 0xff), 0, | |||
/* total length of data returned (including inlined descriptors) */ | |||
1, /* number of interfaces in this configuration */ | |||
1, /* index of this configuration */ | |||
0, /* configuration name string index */ | |||
#if USB_CFG_IS_SELF_POWERED | |||
(1 << 7) | USBATTR_SELFPOWER, /* attributes */ | |||
#else | |||
(1 << 7), /* attributes */ | |||
#endif | |||
USB_CFG_MAX_BUS_POWER/2, /* max USB current in 2mA units */ | |||
/* interface descriptor follows inline: */ | |||
9, /* sizeof(usbDescrInterface): length of descriptor in bytes */ | |||
USBDESCR_INTERFACE, /* descriptor type */ | |||
0, /* index of this interface */ | |||
0, /* alternate setting for this interface */ | |||
USB_CFG_HAVE_INTRIN_ENDPOINT + USB_CFG_HAVE_INTRIN_ENDPOINT3, /* endpoints excl 0: number of endpoint descriptors to follow */ | |||
USB_CFG_INTERFACE_CLASS, | |||
USB_CFG_INTERFACE_SUBCLASS, | |||
USB_CFG_INTERFACE_PROTOCOL, | |||
0, /* string index for interface */ | |||
#if (USB_CFG_DESCR_PROPS_HID & 0xff) /* HID descriptor */ | |||
9, /* sizeof(usbDescrHID): length of descriptor in bytes */ | |||
USBDESCR_HID, /* descriptor type: HID */ | |||
0x01, 0x01, /* BCD representation of HID version */ | |||
0x00, /* target country code */ | |||
0x01, /* number of HID Report (or other HID class) Descriptor infos to follow */ | |||
0x22, /* descriptor type: report */ | |||
USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH, 0, /* total length of report descriptor */ | |||
#endif | |||
#if USB_CFG_HAVE_INTRIN_ENDPOINT /* endpoint descriptor for endpoint 1 */ | |||
7, /* sizeof(usbDescrEndpoint) */ | |||
USBDESCR_ENDPOINT, /* descriptor type = endpoint */ | |||
(char)0x81, /* IN endpoint number 1 */ | |||
0x03, /* attrib: Interrupt endpoint */ | |||
8, 0, /* maximum packet size */ | |||
USB_CFG_INTR_POLL_INTERVAL, /* in ms */ | |||
#endif | |||
#if USB_CFG_HAVE_INTRIN_ENDPOINT3 /* endpoint descriptor for endpoint 3 */ | |||
7, /* sizeof(usbDescrEndpoint) */ | |||
USBDESCR_ENDPOINT, /* descriptor type = endpoint */ | |||
(char)(0x80 | USB_CFG_EP3_NUMBER), /* IN endpoint number 3 */ | |||
0x03, /* attrib: Interrupt endpoint */ | |||
8, 0, /* maximum packet size */ | |||
USB_CFG_INTR_POLL_INTERVAL, /* in ms */ | |||
#endif | |||
}; | |||
#endif | |||
/* ------------------------------------------------------------------------- */ | |||
static inline void usbResetDataToggling(void) | |||
{ | |||
#if USB_CFG_HAVE_INTRIN_ENDPOINT && !USB_CFG_SUPPRESS_INTR_CODE | |||
USB_SET_DATATOKEN1(USB_INITIAL_DATATOKEN); /* reset data toggling for interrupt endpoint */ | |||
# if USB_CFG_HAVE_INTRIN_ENDPOINT3 | |||
USB_SET_DATATOKEN3(USB_INITIAL_DATATOKEN); /* reset data toggling for interrupt endpoint */ | |||
# endif | |||
#endif | |||
} | |||
static inline void usbResetStall(void) | |||
{ | |||
#if USB_CFG_IMPLEMENT_HALT && USB_CFG_HAVE_INTRIN_ENDPOINT | |||
usbTxLen1 = USBPID_NAK; | |||
#if USB_CFG_HAVE_INTRIN_ENDPOINT3 | |||
usbTxLen3 = USBPID_NAK; | |||
#endif | |||
#endif | |||
} | |||
/* ------------------------------------------------------------------------- */ | |||
#if !USB_CFG_SUPPRESS_INTR_CODE | |||
#if USB_CFG_HAVE_INTRIN_ENDPOINT | |||
static void usbGenericSetInterrupt(uchar *data, uchar len, usbTxStatus_t *txStatus) | |||
{ | |||
uchar *p; | |||
char i; | |||
#if USB_CFG_IMPLEMENT_HALT | |||
if(usbTxLen1 == USBPID_STALL) | |||
return; | |||
#endif | |||
if(txStatus->len & 0x10){ /* packet buffer was empty */ | |||
txStatus->buffer[0] ^= USBPID_DATA0 ^ USBPID_DATA1; /* toggle token */ | |||
}else{ | |||
txStatus->len = USBPID_NAK; /* avoid sending outdated (overwritten) interrupt data */ | |||
} | |||
p = txStatus->buffer + 1; | |||
i = len; | |||
do{ /* if len == 0, we still copy 1 byte, but that's no problem */ | |||
*p++ = *data++; | |||
}while(--i > 0); /* loop control at the end is 2 bytes shorter than at beginning */ | |||
usbCrc16Append(&txStatus->buffer[1], len); | |||
txStatus->len = len + 4; /* len must be given including sync byte */ | |||
DBG2(0x21 + (((int)txStatus >> 3) & 3), txStatus->buffer, len + 3); | |||
} | |||
USB_PUBLIC void usbSetInterrupt(uchar *data, uchar len) | |||
{ | |||
usbGenericSetInterrupt(data, len, &usbTxStatus1); | |||
} | |||
#endif | |||
#if USB_CFG_HAVE_INTRIN_ENDPOINT3 | |||
USB_PUBLIC void usbSetInterrupt3(uchar *data, uchar len) | |||
{ | |||
usbGenericSetInterrupt(data, len, &usbTxStatus3); | |||
} | |||
#endif | |||
#endif /* USB_CFG_SUPPRESS_INTR_CODE */ | |||
/* ------------------ utilities for code following below ------------------- */ | |||
/* Use defines for the switch statement so that we can choose between an | |||
* if()else if() and a switch/case based implementation. switch() is more | |||
* efficient for a LARGE set of sequential choices, if() is better in all other | |||
* cases. | |||
*/ | |||
#if USB_CFG_USE_SWITCH_STATEMENT | |||
# define SWITCH_START(cmd) switch(cmd){{ | |||
# define SWITCH_CASE(value) }break; case (value):{ | |||
# define SWITCH_CASE2(v1,v2) }break; case (v1): case(v2):{ | |||
# define SWITCH_CASE3(v1,v2,v3) }break; case (v1): case(v2): case(v3):{ | |||
# define SWITCH_DEFAULT }break; default:{ | |||
# define SWITCH_END }} | |||
#else | |||
# define SWITCH_START(cmd) {uchar _cmd = cmd; if(0){ | |||
# define SWITCH_CASE(value) }else if(_cmd == (value)){ | |||
# define SWITCH_CASE2(v1,v2) }else if(_cmd == (v1) || _cmd == (v2)){ | |||
# define SWITCH_CASE3(v1,v2,v3) }else if(_cmd == (v1) || _cmd == (v2) || (_cmd == v3)){ | |||
# define SWITCH_DEFAULT }else{ | |||
# define SWITCH_END }} | |||
#endif | |||
#ifndef USB_RX_USER_HOOK | |||
#define USB_RX_USER_HOOK(data, len) | |||
#endif | |||
#ifndef USB_SET_ADDRESS_HOOK | |||
#define USB_SET_ADDRESS_HOOK() | |||
#endif | |||
/* ------------------------------------------------------------------------- */ | |||
/* We use if() instead of #if in the macro below because #if can't be used | |||
* in macros and the compiler optimizes constant conditions anyway. | |||
* This may cause problems with undefined symbols if compiled without | |||
* optimizing! | |||
*/ | |||
#define GET_DESCRIPTOR(cfgProp, staticName) \ | |||
if(cfgProp){ \ | |||
if((cfgProp) & USB_PROP_IS_RAM) \ | |||
flags = 0; \ | |||
if((cfgProp) & USB_PROP_IS_DYNAMIC){ \ | |||
len = usbFunctionDescriptor(rq); \ | |||
}else{ \ | |||
len = USB_PROP_LENGTH(cfgProp); \ | |||
usbMsgPtr = (uchar *)(staticName); \ | |||
} \ | |||
} | |||
/* usbDriverDescriptor() is similar to usbFunctionDescriptor(), but used | |||
* internally for all types of descriptors. | |||
*/ | |||
static inline usbMsgLen_t usbDriverDescriptor(usbRequest_t *rq) | |||
{ | |||
usbMsgLen_t len = 0; | |||
uchar flags = USB_FLG_MSGPTR_IS_ROM; | |||
SWITCH_START(rq->wValue.bytes[1]) | |||
SWITCH_CASE(USBDESCR_DEVICE) /* 1 */ | |||
GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_DEVICE, usbDescriptorDevice) | |||
SWITCH_CASE(USBDESCR_CONFIG) /* 2 */ | |||
GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_CONFIGURATION, usbDescriptorConfiguration) | |||
SWITCH_CASE(USBDESCR_STRING) /* 3 */ | |||
#if USB_CFG_DESCR_PROPS_STRINGS & USB_PROP_IS_DYNAMIC | |||
if(USB_CFG_DESCR_PROPS_STRINGS & USB_PROP_IS_RAM) | |||
flags = 0; | |||
len = usbFunctionDescriptor(rq); | |||
#else /* USB_CFG_DESCR_PROPS_STRINGS & USB_PROP_IS_DYNAMIC */ | |||
SWITCH_START(rq->wValue.bytes[0]) | |||
SWITCH_CASE(0) | |||
GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_STRING_0, usbDescriptorString0) | |||
SWITCH_CASE(1) | |||
GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_STRING_VENDOR, usbDescriptorStringVendor) | |||
SWITCH_CASE(2) | |||
GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_STRING_PRODUCT, usbDescriptorStringDevice) | |||
SWITCH_CASE(3) | |||
GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER, usbDescriptorStringSerialNumber) | |||
SWITCH_DEFAULT | |||
if(USB_CFG_DESCR_PROPS_UNKNOWN & USB_PROP_IS_DYNAMIC){ | |||
len = usbFunctionDescriptor(rq); | |||
} | |||
SWITCH_END | |||
#endif /* USB_CFG_DESCR_PROPS_STRINGS & USB_PROP_IS_DYNAMIC */ | |||
#if USB_CFG_DESCR_PROPS_HID_REPORT /* only support HID descriptors if enabled */ | |||
SWITCH_CASE(USBDESCR_HID) /* 0x21 */ | |||
GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_HID, usbDescriptorConfiguration + 18) | |||
SWITCH_CASE(USBDESCR_HID_REPORT)/* 0x22 */ | |||
GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_HID_REPORT, usbDescriptorHidReport) | |||
#endif | |||
SWITCH_DEFAULT | |||
if(USB_CFG_DESCR_PROPS_UNKNOWN & USB_PROP_IS_DYNAMIC){ | |||
len = usbFunctionDescriptor(rq); | |||
} | |||
SWITCH_END | |||
usbMsgFlags = flags; | |||
return len; | |||
} | |||
/* ------------------------------------------------------------------------- */ | |||
/* usbDriverSetup() is similar to usbFunctionSetup(), but it's used for | |||
* standard requests instead of class and custom requests. | |||
*/ | |||
static inline usbMsgLen_t usbDriverSetup(usbRequest_t *rq) | |||
{ | |||
uchar len = 0, *dataPtr = usbTxBuf + 9; /* there are 2 bytes free space at the end of the buffer */ | |||
uchar value = rq->wValue.bytes[0]; | |||
#if USB_CFG_IMPLEMENT_HALT | |||
uchar index = rq->wIndex.bytes[0]; | |||
#endif | |||
dataPtr[0] = 0; /* default reply common to USBRQ_GET_STATUS and USBRQ_GET_INTERFACE */ | |||
SWITCH_START(rq->bRequest) | |||
SWITCH_CASE(USBRQ_GET_STATUS) /* 0 */ | |||
uchar recipient = rq->bmRequestType & USBRQ_RCPT_MASK; /* assign arith ops to variables to enforce byte size */ | |||
if(USB_CFG_IS_SELF_POWERED && recipient == USBRQ_RCPT_DEVICE) | |||
dataPtr[0] = USB_CFG_IS_SELF_POWERED; | |||
#if USB_CFG_IMPLEMENT_HALT | |||
if(recipient == USBRQ_RCPT_ENDPOINT && index == 0x81) /* request status for endpoint 1 */ | |||
dataPtr[0] = usbTxLen1 == USBPID_STALL; | |||
#endif | |||
dataPtr[1] = 0; | |||
len = 2; | |||
#if USB_CFG_IMPLEMENT_HALT | |||
SWITCH_CASE2(USBRQ_CLEAR_FEATURE, USBRQ_SET_FEATURE) /* 1, 3 */ | |||
if(value == 0 && index == 0x81){ /* feature 0 == HALT for endpoint == 1 */ | |||
usbTxLen1 = rq->bRequest == USBRQ_CLEAR_FEATURE ? USBPID_NAK : USBPID_STALL; | |||
usbResetDataToggling(); | |||
} | |||
#endif | |||
SWITCH_CASE(USBRQ_SET_ADDRESS) /* 5 */ | |||
usbNewDeviceAddr = value; | |||
USB_SET_ADDRESS_HOOK(); | |||
SWITCH_CASE(USBRQ_GET_DESCRIPTOR) /* 6 */ | |||
len = usbDriverDescriptor(rq); | |||
goto skipMsgPtrAssignment; | |||
SWITCH_CASE(USBRQ_GET_CONFIGURATION) /* 8 */ | |||
dataPtr = &usbConfiguration; /* send current configuration value */ | |||
len = 1; | |||
SWITCH_CASE(USBRQ_SET_CONFIGURATION) /* 9 */ | |||
usbConfiguration = value; | |||
usbResetStall(); | |||
SWITCH_CASE(USBRQ_GET_INTERFACE) /* 10 */ | |||
len = 1; | |||
#if USB_CFG_HAVE_INTRIN_ENDPOINT && !USB_CFG_SUPPRESS_INTR_CODE | |||
SWITCH_CASE(USBRQ_SET_INTERFACE) /* 11 */ | |||
usbResetDataToggling(); | |||
usbResetStall(); | |||
#endif | |||
SWITCH_DEFAULT /* 7=SET_DESCRIPTOR, 12=SYNC_FRAME */ | |||
/* Should we add an optional hook here? */ | |||
SWITCH_END | |||
usbMsgPtr = dataPtr; | |||
skipMsgPtrAssignment: | |||
return len; | |||
} | |||
/* ------------------------------------------------------------------------- */ | |||
/* usbProcessRx() is called for every message received by the interrupt | |||
* routine. It distinguishes between SETUP and DATA packets and processes | |||
* them accordingly. | |||
*/ | |||
static inline void usbProcessRx(uchar *data, uchar len) | |||
{ | |||
usbRequest_t *rq = (void *)data; | |||
/* usbRxToken can be: | |||
* 0x2d 00101101 (USBPID_SETUP for setup data) | |||
* 0xe1 11100001 (USBPID_OUT: data phase of setup transfer) | |||
* 0...0x0f for OUT on endpoint X | |||
*/ | |||
DBG2(0x10 + (usbRxToken & 0xf), data, len + 2); /* SETUP=1d, SETUP-DATA=11, OUTx=1x */ | |||
USB_RX_USER_HOOK(data, len) | |||
#if USB_CFG_IMPLEMENT_FN_WRITEOUT | |||
if(usbRxToken < 0x10){ /* OUT to endpoint != 0: endpoint number in usbRxToken */ | |||
usbFunctionWriteOut(data, len); | |||
return; | |||
} | |||
#endif | |||
if(usbRxToken == (uchar)USBPID_SETUP){ | |||
if(len != 8) /* Setup size must be always 8 bytes. Ignore otherwise. */ | |||
return; | |||
usbMsgLen_t replyLen; | |||
usbTxBuf[0] = USBPID_DATA0; /* initialize data toggling */ | |||
usbTxLen = USBPID_NAK; /* abort pending transmit */ | |||
usbMsgFlags = 0; | |||
uchar type = rq->bmRequestType & USBRQ_TYPE_MASK; | |||
if(type != USBRQ_TYPE_STANDARD){ /* standard requests are handled by driver */ | |||
replyLen = usbFunctionSetup(data); | |||
}else{ | |||
replyLen = usbDriverSetup(rq); | |||
} | |||
#if USB_CFG_IMPLEMENT_FN_READ || USB_CFG_IMPLEMENT_FN_WRITE | |||
if(replyLen == USB_NO_MSG){ /* use user-supplied read/write function */ | |||
/* do some conditioning on replyLen, but on IN transfers only */ | |||
if((rq->bmRequestType & USBRQ_DIR_MASK) != USBRQ_DIR_HOST_TO_DEVICE){ | |||
if(sizeof(replyLen) < sizeof(rq->wLength.word)){ /* help compiler with optimizing */ | |||
replyLen = rq->wLength.bytes[0]; | |||
}else{ | |||
replyLen = rq->wLength.word; | |||
} | |||
} | |||
usbMsgFlags = USB_FLG_USE_USER_RW; | |||
}else /* The 'else' prevents that we limit a replyLen of USB_NO_MSG to the maximum transfer len. */ | |||
#endif | |||
if(sizeof(replyLen) < sizeof(rq->wLength.word)){ /* help compiler with optimizing */ | |||
if(!rq->wLength.bytes[1] && replyLen > rq->wLength.bytes[0]) /* limit length to max */ | |||
replyLen = rq->wLength.bytes[0]; | |||
}else{ | |||
if(replyLen > rq->wLength.word) /* limit length to max */ | |||
replyLen = rq->wLength.word; | |||
} | |||
usbMsgLen = replyLen; | |||
}else{ /* usbRxToken must be USBPID_OUT, which means data phase of setup (control-out) */ | |||
#if USB_CFG_IMPLEMENT_FN_WRITE | |||
if(usbMsgFlags & USB_FLG_USE_USER_RW){ | |||
uchar rval = usbFunctionWrite(data, len); | |||
if(rval == 0xff){ /* an error occurred */ | |||
usbTxLen = USBPID_STALL; | |||
}else if(rval != 0){ /* This was the final package */ | |||
usbMsgLen = 0; /* answer with a zero-sized data packet */ | |||
} | |||
} | |||
#endif | |||
} | |||
} | |||
/* ------------------------------------------------------------------------- */ | |||
/* This function is similar to usbFunctionRead(), but it's also called for | |||
* data handled automatically by the driver (e.g. descriptor reads). | |||
*/ | |||
static uchar usbDeviceRead(uchar *data, uchar len) | |||
{ | |||
if(len > 0){ /* don't bother app with 0 sized reads */ | |||
#if USB_CFG_IMPLEMENT_FN_READ | |||
if(usbMsgFlags & USB_FLG_USE_USER_RW){ | |||
len = usbFunctionRead(data, len); | |||
}else | |||
#endif | |||
{ | |||
uchar i = len, *r = usbMsgPtr; | |||
if(usbMsgFlags & USB_FLG_MSGPTR_IS_ROM){ /* ROM data */ | |||
do{ | |||
uchar c = USB_READ_FLASH(r); /* assign to char size variable to enforce byte ops */ | |||
*data++ = c; | |||
r++; | |||
}while(--i); | |||
}else{ /* RAM data */ | |||
do{ | |||
*data++ = *r++; | |||
}while(--i); | |||
} | |||
usbMsgPtr = r; | |||
} | |||
} | |||
return len; | |||
} | |||
/* ------------------------------------------------------------------------- */ | |||
/* usbBuildTxBlock() is called when we have data to transmit and the | |||
* interrupt routine's transmit buffer is empty. | |||
*/ | |||
static inline void usbBuildTxBlock(void) | |||
{ | |||
usbMsgLen_t wantLen; | |||
uchar len; | |||
wantLen = usbMsgLen; | |||
if(wantLen > 8) | |||
wantLen = 8; | |||
usbMsgLen -= wantLen; | |||
usbTxBuf[0] ^= USBPID_DATA0 ^ USBPID_DATA1; /* DATA toggling */ | |||
len = usbDeviceRead(usbTxBuf + 1, wantLen); | |||
if(len <= 8){ /* valid data packet */ | |||
usbCrc16Append(&usbTxBuf[1], len); | |||
len += 4; /* length including sync byte */ | |||
if(len < 12) /* a partial package identifies end of message */ | |||
usbMsgLen = USB_NO_MSG; | |||
}else{ | |||
len = USBPID_STALL; /* stall the endpoint */ | |||
usbMsgLen = USB_NO_MSG; | |||
} | |||
usbTxLen = len; | |||
DBG2(0x20, usbTxBuf, len-1); | |||
} | |||
/* ------------------------------------------------------------------------- */ | |||
static inline void usbHandleResetHook(uchar notResetState) | |||
{ | |||
#ifdef USB_RESET_HOOK | |||
static uchar wasReset; | |||
uchar isReset = !notResetState; | |||
if(wasReset != isReset){ | |||
USB_RESET_HOOK(isReset); | |||
wasReset = isReset; | |||
} | |||
#endif | |||
} | |||
/* ------------------------------------------------------------------------- */ | |||
USB_PUBLIC void usbPoll(void) | |||
{ | |||
schar len; | |||
uchar i; | |||
len = usbRxLen - 3; | |||
if(len >= 0){ | |||
/* We could check CRC16 here -- but ACK has already been sent anyway. If you | |||
* need data integrity checks with this driver, check the CRC in your app | |||
* code and report errors back to the host. Since the ACK was already sent, | |||
* retries must be handled on application level. | |||
* unsigned crc = usbCrc16(buffer + 1, usbRxLen - 3); | |||
*/ | |||
usbProcessRx(usbRxBuf + USB_BUFSIZE + 1 - usbInputBufOffset, len); | |||
#if USB_CFG_HAVE_FLOWCONTROL | |||
if(usbRxLen > 0) /* only mark as available if not inactivated */ | |||
usbRxLen = 0; | |||
#else | |||
usbRxLen = 0; /* mark rx buffer as available */ | |||
#endif | |||
} | |||
if(usbTxLen & 0x10){ /* transmit system idle */ | |||
if(usbMsgLen != USB_NO_MSG){ /* transmit data pending? */ | |||
usbBuildTxBlock(); | |||
} | |||
} | |||
for(i = 20; i > 0; i--){ | |||
uchar usbLineStatus = USBIN & USBMASK; | |||
if(usbLineStatus != 0) /* SE0 has ended */ | |||
goto isNotReset; | |||
} | |||
/* RESET condition, called multiple times during reset */ | |||
usbNewDeviceAddr = 0; | |||
usbDeviceAddr = 0; | |||
usbResetStall(); | |||
DBG1(0xff, 0, 0); | |||
isNotReset: | |||
usbHandleResetHook(i); | |||
} | |||
/* ------------------------------------------------------------------------- */ | |||
USB_PUBLIC void usbInit(void) | |||
{ | |||
#if USB_INTR_CFG_SET != 0 | |||
USB_INTR_CFG |= USB_INTR_CFG_SET; | |||
#endif | |||
#if USB_INTR_CFG_CLR != 0 | |||
USB_INTR_CFG &= ~(USB_INTR_CFG_CLR); | |||
#endif | |||
USB_INTR_ENABLE |= (1 << USB_INTR_ENABLE_BIT); | |||
usbResetDataToggling(); | |||
#if USB_CFG_HAVE_INTRIN_ENDPOINT && !USB_CFG_SUPPRESS_INTR_CODE | |||
usbTxLen1 = USBPID_NAK; | |||
#if USB_CFG_HAVE_INTRIN_ENDPOINT3 | |||
usbTxLen3 = USBPID_NAK; | |||
#endif | |||
#endif | |||
} | |||
/* ------------------------------------------------------------------------- */ |
@@ -0,0 +1,735 @@ | |||
/* Name: usbdrv.h | |||
* Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers | |||
* Author: Christian Starkjohann | |||
* Creation Date: 2004-12-29 | |||
* Tabsize: 4 | |||
* Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH | |||
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) | |||
* This Revision: $Id: usbdrv.h 793 2010-07-15 15:58:11Z cs $ | |||
*/ | |||
#ifndef __usbdrv_h_included__ | |||
#define __usbdrv_h_included__ | |||
#include "usbconfig.h" | |||
#include "usbportability.h" | |||
/* | |||
Hardware Prerequisites: | |||
======================= | |||
USB lines D+ and D- MUST be wired to the same I/O port. We recommend that D+ | |||
triggers the interrupt (best achieved by using INT0 for D+), but it is also | |||
possible to trigger the interrupt from D-. If D- is used, interrupts are also | |||
triggered by SOF packets. D- requires a pull-up of 1.5k to +3.5V (and the | |||
device must be powered at 3.5V) to identify as low-speed USB device. A | |||
pull-down or pull-up of 1M SHOULD be connected from D+ to +3.5V to prevent | |||
interference when no USB master is connected. If you use Zener diodes to limit | |||
the voltage on D+ and D-, you MUST use a pull-down resistor, not a pull-up. | |||
We use D+ as interrupt source and not D- because it does not trigger on | |||
keep-alive and RESET states. If you want to count keep-alive events with | |||
USB_COUNT_SOF, you MUST use D- as an interrupt source. | |||
As a compile time option, the 1.5k pull-up resistor on D- can be made | |||
switchable to allow the device to disconnect at will. See the definition of | |||
usbDeviceConnect() and usbDeviceDisconnect() further down in this file. | |||
Please adapt the values in usbconfig.h according to your hardware! | |||
The device MUST be clocked at exactly 12 MHz, 15 MHz, 16 MHz or 20 MHz | |||
or at 12.8 MHz resp. 16.5 MHz +/- 1%. See usbconfig-prototype.h for details. | |||
Limitations: | |||
============ | |||
Robustness with respect to communication errors: | |||
The driver assumes error-free communication. It DOES check for errors in | |||
the PID, but does NOT check bit stuffing errors, SE0 in middle of a byte, | |||
token CRC (5 bit) and data CRC (16 bit). CRC checks can not be performed due | |||
to timing constraints: We must start sending a reply within 7 bit times. | |||
Bit stuffing and misplaced SE0 would have to be checked in real-time, but CPU | |||
performance does not permit that. The driver does not check Data0/Data1 | |||
toggling, but application software can implement the check. | |||
Input characteristics: | |||
Since no differential receiver circuit is used, electrical interference | |||
robustness may suffer. The driver samples only one of the data lines with | |||
an ordinary I/O pin's input characteristics. However, since this is only a | |||
low speed USB implementation and the specification allows for 8 times the | |||
bit rate over the same hardware, we should be on the safe side. Even the spec | |||
requires detection of asymmetric states at high bit rate for SE0 detection. | |||
Number of endpoints: | |||
The driver supports the following endpoints: | |||
- Endpoint 0, the default control endpoint. | |||
- Any number of interrupt- or bulk-out endpoints. The data is sent to | |||
usbFunctionWriteOut() and USB_CFG_IMPLEMENT_FN_WRITEOUT must be defined | |||
to 1 to activate this feature. The endpoint number can be found in the | |||
global variable 'usbRxToken'. | |||
- One default interrupt- or bulk-in endpoint. This endpoint is used for | |||
interrupt- or bulk-in transfers which are not handled by any other endpoint. | |||
You must define USB_CFG_HAVE_INTRIN_ENDPOINT in order to activate this | |||
feature and call usbSetInterrupt() to send interrupt/bulk data. | |||
- One additional interrupt- or bulk-in endpoint. This was endpoint 3 in | |||
previous versions of this driver but can now be configured to any endpoint | |||
number. You must define USB_CFG_HAVE_INTRIN_ENDPOINT3 in order to activate | |||
this feature and call usbSetInterrupt3() to send interrupt/bulk data. The | |||
endpoint number can be set with USB_CFG_EP3_NUMBER. | |||
Please note that the USB standard forbids bulk endpoints for low speed devices! | |||
Most operating systems allow them anyway, but the AVR will spend 90% of the CPU | |||
time in the USB interrupt polling for bulk data. | |||
Maximum data payload: | |||
Data payload of control in and out transfers may be up to 254 bytes. In order | |||
to accept payload data of out transfers, you need to implement | |||
'usbFunctionWrite()'. | |||
USB Suspend Mode supply current: | |||
The USB standard limits power consumption to 500uA when the bus is in suspend | |||
mode. This is not a problem for self-powered devices since they don't need | |||
bus power anyway. Bus-powered devices can achieve this only by putting the | |||
CPU in sleep mode. The driver does not implement suspend handling by itself. | |||
However, the application may implement activity monitoring and wakeup from | |||
sleep. The host sends regular SE0 states on the bus to keep it active. These | |||
SE0 states can be detected by using D- as the interrupt source. Define | |||
USB_COUNT_SOF to 1 and use the global variable usbSofCount to check for bus | |||
activity. | |||
Operation without an USB master: | |||
The driver behaves neutral without connection to an USB master if D- reads | |||
as 1. To avoid spurious interrupts, we recommend a high impedance (e.g. 1M) | |||
pull-down or pull-up resistor on D+ (interrupt). If Zener diodes are used, | |||
use a pull-down. If D- becomes statically 0, the driver may block in the | |||
interrupt routine. | |||
Interrupt latency: | |||
The application must ensure that the USB interrupt is not disabled for more | |||
than 25 cycles (this is for 12 MHz, faster clocks allow longer latency). | |||
This implies that all interrupt routines must either have the "ISR_NOBLOCK" | |||
attribute set (see "avr/interrupt.h") or be written in assembler with "sei" | |||
as the first instruction. | |||
Maximum interrupt duration / CPU cycle consumption: | |||
The driver handles all USB communication during the interrupt service | |||
routine. The routine will not return before an entire USB message is received | |||
and the reply is sent. This may be up to ca. 1200 cycles @ 12 MHz (= 100us) if | |||
the host conforms to the standard. The driver will consume CPU cycles for all | |||
USB messages, even if they address another (low-speed) device on the same bus. | |||
*/ | |||
/* ------------------------------------------------------------------------- */ | |||
/* --------------------------- Module Interface ---------------------------- */ | |||
/* ------------------------------------------------------------------------- */ | |||
#define USBDRV_VERSION 20100715 | |||
/* This define uniquely identifies a driver version. It is a decimal number | |||
* constructed from the driver's release date in the form YYYYMMDD. If the | |||
* driver's behavior or interface changes, you can use this constant to | |||
* distinguish versions. If it is not defined, the driver's release date is | |||
* older than 2006-01-25. | |||
*/ | |||
#ifndef USB_PUBLIC | |||
#define USB_PUBLIC | |||
#endif | |||
/* USB_PUBLIC is used as declaration attribute for all functions exported by | |||
* the USB driver. The default is no attribute (see above). You may define it | |||
* to static either in usbconfig.h or from the command line if you include | |||
* usbdrv.c instead of linking against it. Including the C module of the driver | |||
* directly in your code saves a couple of bytes in flash memory. | |||
*/ | |||
#ifndef __ASSEMBLER__ | |||
#ifndef uchar | |||
#define uchar unsigned char | |||
#endif | |||
#ifndef schar | |||
#define schar signed char | |||
#endif | |||
/* shortcuts for well defined 8 bit integer types */ | |||
#if USB_CFG_LONG_TRANSFERS /* if more than 254 bytes transfer size required */ | |||
# define usbMsgLen_t unsigned | |||
#else | |||
# define usbMsgLen_t uchar | |||
#endif | |||
/* usbMsgLen_t is the data type used for transfer lengths. By default, it is | |||
* defined to uchar, allowing a maximum of 254 bytes (255 is reserved for | |||
* USB_NO_MSG below). If the usbconfig.h defines USB_CFG_LONG_TRANSFERS to 1, | |||
* a 16 bit data type is used, allowing up to 16384 bytes (the rest is used | |||
* for flags in the descriptor configuration). | |||
*/ | |||
#define USB_NO_MSG ((usbMsgLen_t)-1) /* constant meaning "no message" */ | |||
struct usbRequest; /* forward declaration */ | |||
USB_PUBLIC void usbInit(void); | |||
/* This function must be called before interrupts are enabled and the main | |||
* loop is entered. We exepct that the PORT and DDR bits for D+ and D- have | |||
* not been changed from their default status (which is 0). If you have changed | |||
* them, set both back to 0 (configure them as input with no internal pull-up). | |||
*/ | |||
USB_PUBLIC void usbPoll(void); | |||
/* This function must be called at regular intervals from the main loop. | |||
* Maximum delay between calls is somewhat less than 50ms (USB timeout for | |||
* accepting a Setup message). Otherwise the device will not be recognized. | |||
* Please note that debug outputs through the UART take ~ 0.5ms per byte | |||
* at 19200 bps. | |||
*/ | |||
extern uchar *usbMsgPtr; | |||
/* This variable may be used to pass transmit data to the driver from the | |||
* implementation of usbFunctionWrite(). It is also used internally by the | |||
* driver for standard control requests. | |||
*/ | |||
USB_PUBLIC usbMsgLen_t usbFunctionSetup(uchar data[8]); | |||
/* This function is called when the driver receives a SETUP transaction from | |||
* the host which is not answered by the driver itself (in practice: class and | |||
* vendor requests). All control transfers start with a SETUP transaction where | |||
* the host communicates the parameters of the following (optional) data | |||
* transfer. The SETUP data is available in the 'data' parameter which can | |||
* (and should) be casted to 'usbRequest_t *' for a more user-friendly access | |||
* to parameters. | |||
* | |||
* If the SETUP indicates a control-in transfer, you should provide the | |||
* requested data to the driver. There are two ways to transfer this data: | |||
* (1) Set the global pointer 'usbMsgPtr' to the base of the static RAM data | |||
* block and return the length of the data in 'usbFunctionSetup()'. The driver | |||
* will handle the rest. Or (2) return USB_NO_MSG in 'usbFunctionSetup()'. The | |||
* driver will then call 'usbFunctionRead()' when data is needed. See the | |||
* documentation for usbFunctionRead() for details. | |||
* | |||
* If the SETUP indicates a control-out transfer, the only way to receive the | |||
* data from the host is through the 'usbFunctionWrite()' call. If you | |||
* implement this function, you must return USB_NO_MSG in 'usbFunctionSetup()' | |||
* to indicate that 'usbFunctionWrite()' should be used. See the documentation | |||
* of this function for more information. If you just want to ignore the data | |||
* sent by the host, return 0 in 'usbFunctionSetup()'. | |||
* | |||
* Note that calls to the functions usbFunctionRead() and usbFunctionWrite() | |||
* are only done if enabled by the configuration in usbconfig.h. | |||
*/ | |||
USB_PUBLIC usbMsgLen_t usbFunctionDescriptor(struct usbRequest *rq); | |||
/* You need to implement this function ONLY if you provide USB descriptors at | |||
* runtime (which is an expert feature). It is very similar to | |||
* usbFunctionSetup() above, but it is called only to request USB descriptor | |||
* data. See the documentation of usbFunctionSetup() above for more info. | |||
*/ | |||
#if USB_CFG_HAVE_INTRIN_ENDPOINT | |||
USB_PUBLIC void usbSetInterrupt(uchar *data, uchar len); | |||
/* This function sets the message which will be sent during the next interrupt | |||
* IN transfer. The message is copied to an internal buffer and must not exceed | |||
* a length of 8 bytes. The message may be 0 bytes long just to indicate the | |||
* interrupt status to the host. | |||
* If you need to transfer more bytes, use a control read after the interrupt. | |||
*/ | |||
#define usbInterruptIsReady() (usbTxLen1 & 0x10) | |||
/* This macro indicates whether the last interrupt message has already been | |||
* sent. If you set a new interrupt message before the old was sent, the | |||
* message already buffered will be lost. | |||
*/ | |||
#if USB_CFG_HAVE_INTRIN_ENDPOINT3 | |||
USB_PUBLIC void usbSetInterrupt3(uchar *data, uchar len); | |||
#define usbInterruptIsReady3() (usbTxLen3 & 0x10) | |||
/* Same as above for endpoint 3 */ | |||
#endif | |||
#endif /* USB_CFG_HAVE_INTRIN_ENDPOINT */ | |||
#if USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH /* simplified interface for backward compatibility */ | |||
#define usbHidReportDescriptor usbDescriptorHidReport | |||
/* should be declared as: PROGMEM char usbHidReportDescriptor[]; */ | |||
/* If you implement an HID device, you need to provide a report descriptor. | |||
* The HID report descriptor syntax is a bit complex. If you understand how | |||
* report descriptors are constructed, we recommend that you use the HID | |||
* Descriptor Tool from usb.org, see http://www.usb.org/developers/hidpage/. | |||
* Otherwise you should probably start with a working example. | |||
*/ | |||
#endif /* USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH */ | |||
#if USB_CFG_IMPLEMENT_FN_WRITE | |||
USB_PUBLIC uchar usbFunctionWrite(uchar *data, uchar len); | |||
/* This function is called by the driver to provide a control transfer's | |||
* payload data (control-out). It is called in chunks of up to 8 bytes. The | |||
* total count provided in the current control transfer can be obtained from | |||
* the 'length' property in the setup data. If an error occurred during | |||
* processing, return 0xff (== -1). The driver will answer the entire transfer | |||
* with a STALL token in this case. If you have received the entire payload | |||
* successfully, return 1. If you expect more data, return 0. If you don't | |||
* know whether the host will send more data (you should know, the total is | |||
* provided in the usbFunctionSetup() call!), return 1. | |||
* NOTE: If you return 0xff for STALL, 'usbFunctionWrite()' may still be called | |||
* for the remaining data. You must continue to return 0xff for STALL in these | |||
* calls. | |||
* In order to get usbFunctionWrite() called, define USB_CFG_IMPLEMENT_FN_WRITE | |||
* to 1 in usbconfig.h and return 0xff in usbFunctionSetup().. | |||
*/ | |||
#endif /* USB_CFG_IMPLEMENT_FN_WRITE */ | |||
#if USB_CFG_IMPLEMENT_FN_READ | |||
USB_PUBLIC uchar usbFunctionRead(uchar *data, uchar len); | |||
/* This function is called by the driver to ask the application for a control | |||
* transfer's payload data (control-in). It is called in chunks of up to 8 | |||
* bytes each. You should copy the data to the location given by 'data' and | |||
* return the actual number of bytes copied. If you return less than requested, | |||
* the control-in transfer is terminated. If you return 0xff, the driver aborts | |||
* the transfer with a STALL token. | |||
* In order to get usbFunctionRead() called, define USB_CFG_IMPLEMENT_FN_READ | |||
* to 1 in usbconfig.h and return 0xff in usbFunctionSetup().. | |||
*/ | |||
#endif /* USB_CFG_IMPLEMENT_FN_READ */ | |||
extern uchar usbRxToken; /* may be used in usbFunctionWriteOut() below */ | |||
#if USB_CFG_IMPLEMENT_FN_WRITEOUT | |||
USB_PUBLIC void usbFunctionWriteOut(uchar *data, uchar len); | |||
/* This function is called by the driver when data is received on an interrupt- | |||
* or bulk-out endpoint. The endpoint number can be found in the global | |||
* variable usbRxToken. You must define USB_CFG_IMPLEMENT_FN_WRITEOUT to 1 in | |||
* usbconfig.h to get this function called. | |||
*/ | |||
#endif /* USB_CFG_IMPLEMENT_FN_WRITEOUT */ | |||
#ifdef USB_CFG_PULLUP_IOPORTNAME | |||
#define usbDeviceConnect() ((USB_PULLUP_DDR |= (1<<USB_CFG_PULLUP_BIT)), \ | |||
(USB_PULLUP_OUT |= (1<<USB_CFG_PULLUP_BIT))) | |||
#define usbDeviceDisconnect() ((USB_PULLUP_DDR &= ~(1<<USB_CFG_PULLUP_BIT)), \ | |||
(USB_PULLUP_OUT &= ~(1<<USB_CFG_PULLUP_BIT))) | |||
#else /* USB_CFG_PULLUP_IOPORTNAME */ | |||
#define usbDeviceConnect() (USBDDR &= ~(1<<USBMINUS)) | |||
#define usbDeviceDisconnect() (USBDDR |= (1<<USBMINUS)) | |||
#endif /* USB_CFG_PULLUP_IOPORTNAME */ | |||
/* The macros usbDeviceConnect() and usbDeviceDisconnect() (intended to look | |||
* like a function) connect resp. disconnect the device from the host's USB. | |||
* If the constants USB_CFG_PULLUP_IOPORT and USB_CFG_PULLUP_BIT are defined | |||
* in usbconfig.h, a disconnect consists of removing the pull-up resisitor | |||
* from D-, otherwise the disconnect is done by brute-force pulling D- to GND. | |||
* This does not conform to the spec, but it works. | |||
* Please note that the USB interrupt must be disabled while the device is | |||
* in disconnected state, or the interrupt handler will hang! You can either | |||
* turn off the USB interrupt selectively with | |||
* USB_INTR_ENABLE &= ~(1 << USB_INTR_ENABLE_BIT) | |||
* or use cli() to disable interrupts globally. | |||
*/ | |||
extern unsigned usbCrc16(unsigned data, uchar len); | |||
#define usbCrc16(data, len) usbCrc16((unsigned)(data), len) | |||
/* This function calculates the binary complement of the data CRC used in | |||
* USB data packets. The value is used to build raw transmit packets. | |||
* You may want to use this function for data checksums or to verify received | |||
* data. We enforce 16 bit calling conventions for compatibility with IAR's | |||
* tiny memory model. | |||
*/ | |||
extern unsigned usbCrc16Append(unsigned data, uchar len); | |||
#define usbCrc16Append(data, len) usbCrc16Append((unsigned)(data), len) | |||
/* This function is equivalent to usbCrc16() above, except that it appends | |||
* the 2 bytes CRC (lowbyte first) in the 'data' buffer after reading 'len' | |||
* bytes. | |||
*/ | |||
#if USB_CFG_HAVE_MEASURE_FRAME_LENGTH | |||
extern unsigned usbMeasureFrameLength(void); | |||
/* This function MUST be called IMMEDIATELY AFTER USB reset and measures 1/7 of | |||
* the number of CPU cycles during one USB frame minus one low speed bit | |||
* length. In other words: return value = 1499 * (F_CPU / 10.5 MHz) | |||
* Since this is a busy wait, you MUST disable all interrupts with cli() before | |||
* calling this function. | |||
* This can be used to calibrate the AVR's RC oscillator. | |||
*/ | |||
#endif | |||
extern uchar usbConfiguration; | |||
/* This value contains the current configuration set by the host. The driver | |||
* allows setting and querying of this variable with the USB SET_CONFIGURATION | |||
* and GET_CONFIGURATION requests, but does not use it otherwise. | |||
* You may want to reflect the "configured" status with a LED on the device or | |||
* switch on high power parts of the circuit only if the device is configured. | |||
*/ | |||
#if USB_COUNT_SOF | |||
extern volatile uchar usbSofCount; | |||
/* This variable is incremented on every SOF packet. It is only available if | |||
* the macro USB_COUNT_SOF is defined to a value != 0. | |||
*/ | |||
#endif | |||
#if USB_CFG_CHECK_DATA_TOGGLING | |||
extern uchar usbCurrentDataToken; | |||
/* This variable can be checked in usbFunctionWrite() and usbFunctionWriteOut() | |||
* to ignore duplicate packets. | |||
*/ | |||
#endif | |||
#define USB_STRING_DESCRIPTOR_HEADER(stringLength) ((2*(stringLength)+2) | (3<<8)) | |||
/* This macro builds a descriptor header for a string descriptor given the | |||
* string's length. See usbdrv.c for an example how to use it. | |||
*/ | |||
#if USB_CFG_HAVE_FLOWCONTROL | |||
extern volatile schar usbRxLen; | |||
#define usbDisableAllRequests() usbRxLen = -1 | |||
/* Must be called from usbFunctionWrite(). This macro disables all data input | |||
* from the USB interface. Requests from the host are answered with a NAK | |||
* while they are disabled. | |||
*/ | |||
#define usbEnableAllRequests() usbRxLen = 0 | |||
/* May only be called if requests are disabled. This macro enables input from | |||
* the USB interface after it has been disabled with usbDisableAllRequests(). | |||
*/ | |||
#define usbAllRequestsAreDisabled() (usbRxLen < 0) | |||
/* Use this macro to find out whether requests are disabled. It may be needed | |||
* to ensure that usbEnableAllRequests() is never called when requests are | |||
* enabled. | |||
*/ | |||
#endif | |||
#define USB_SET_DATATOKEN1(token) usbTxBuf1[0] = token | |||
#define USB_SET_DATATOKEN3(token) usbTxBuf3[0] = token | |||
/* These two macros can be used by application software to reset data toggling | |||
* for interrupt-in endpoints 1 and 3. Since the token is toggled BEFORE | |||
* sending data, you must set the opposite value of the token which should come | |||
* first. | |||
*/ | |||
#endif /* __ASSEMBLER__ */ | |||
/* ------------------------------------------------------------------------- */ | |||
/* ----------------- Definitions for Descriptor Properties ----------------- */ | |||
/* ------------------------------------------------------------------------- */ | |||
/* This is advanced stuff. See usbconfig-prototype.h for more information | |||
* about the various methods to define USB descriptors. If you do nothing, | |||
* the default descriptors will be used. | |||
*/ | |||
#define USB_PROP_IS_DYNAMIC (1 << 14) | |||
/* If this property is set for a descriptor, usbFunctionDescriptor() will be | |||
* used to obtain the particular descriptor. Data directly returned via | |||
* usbMsgPtr are FLASH data by default, combine (OR) with USB_PROP_IS_RAM to | |||
* return RAM data. | |||
*/ | |||
#define USB_PROP_IS_RAM (1 << 15) | |||
/* If this property is set for a descriptor, the data is read from RAM | |||
* memory instead of Flash. The property is used for all methods to provide | |||
* external descriptors. | |||
*/ | |||
#define USB_PROP_LENGTH(len) ((len) & 0x3fff) | |||
/* If a static external descriptor is used, this is the total length of the | |||
* descriptor in bytes. | |||
*/ | |||
/* all descriptors which may have properties: */ | |||
#ifndef USB_CFG_DESCR_PROPS_DEVICE | |||
#define USB_CFG_DESCR_PROPS_DEVICE 0 | |||
#endif | |||
#ifndef USB_CFG_DESCR_PROPS_CONFIGURATION | |||
#define USB_CFG_DESCR_PROPS_CONFIGURATION 0 | |||
#endif | |||
#ifndef USB_CFG_DESCR_PROPS_STRINGS | |||
#define USB_CFG_DESCR_PROPS_STRINGS 0 | |||
#endif | |||
#ifndef USB_CFG_DESCR_PROPS_STRING_0 | |||
#define USB_CFG_DESCR_PROPS_STRING_0 0 | |||
#endif | |||
#ifndef USB_CFG_DESCR_PROPS_STRING_VENDOR | |||
#define USB_CFG_DESCR_PROPS_STRING_VENDOR 0 | |||
#endif | |||
#ifndef USB_CFG_DESCR_PROPS_STRING_PRODUCT | |||
#define USB_CFG_DESCR_PROPS_STRING_PRODUCT 0 | |||
#endif | |||
#ifndef USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER | |||
#define USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER 0 | |||
#endif | |||
#ifndef USB_CFG_DESCR_PROPS_HID | |||
#define USB_CFG_DESCR_PROPS_HID 0 | |||
#endif | |||
#if !(USB_CFG_DESCR_PROPS_HID_REPORT) | |||
# undef USB_CFG_DESCR_PROPS_HID_REPORT | |||
# if USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH /* do some backward compatibility tricks */ | |||
# define USB_CFG_DESCR_PROPS_HID_REPORT USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH | |||
# else | |||
# define USB_CFG_DESCR_PROPS_HID_REPORT 0 | |||
# endif | |||
#endif | |||
#ifndef USB_CFG_DESCR_PROPS_UNKNOWN | |||
#define USB_CFG_DESCR_PROPS_UNKNOWN 0 | |||
#endif | |||
/* ------------------ forward declaration of descriptors ------------------- */ | |||
/* If you use external static descriptors, they must be stored in global | |||
* arrays as declared below: | |||
*/ | |||
#ifndef __ASSEMBLER__ | |||
extern | |||
#if !(USB_CFG_DESCR_PROPS_DEVICE & USB_PROP_IS_RAM) | |||
PROGMEM | |||
#endif | |||
char usbDescriptorDevice[]; | |||
extern | |||
#if !(USB_CFG_DESCR_PROPS_CONFIGURATION & USB_PROP_IS_RAM) | |||
PROGMEM | |||
#endif | |||
char usbDescriptorConfiguration[]; | |||
extern | |||
#if !(USB_CFG_DESCR_PROPS_HID_REPORT & USB_PROP_IS_RAM) | |||
PROGMEM | |||
#endif | |||
char usbDescriptorHidReport[]; | |||
extern | |||
#if !(USB_CFG_DESCR_PROPS_STRING_0 & USB_PROP_IS_RAM) | |||
PROGMEM | |||
#endif | |||
char usbDescriptorString0[]; | |||
extern | |||
#if !(USB_CFG_DESCR_PROPS_STRING_VENDOR & USB_PROP_IS_RAM) | |||
PROGMEM | |||
#endif | |||
int usbDescriptorStringVendor[]; | |||
extern | |||
#if !(USB_CFG_DESCR_PROPS_STRING_PRODUCT & USB_PROP_IS_RAM) | |||
PROGMEM | |||
#endif | |||
int usbDescriptorStringDevice[]; | |||
extern | |||
#if !(USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER & USB_PROP_IS_RAM) | |||
PROGMEM | |||
#endif | |||
int usbDescriptorStringSerialNumber[]; | |||
#endif /* __ASSEMBLER__ */ | |||
/* ------------------------------------------------------------------------- */ | |||
/* ------------------------ General Purpose Macros ------------------------- */ | |||
/* ------------------------------------------------------------------------- */ | |||
#define USB_CONCAT(a, b) a ## b | |||
#define USB_CONCAT_EXPANDED(a, b) USB_CONCAT(a, b) | |||
#define USB_OUTPORT(name) USB_CONCAT(PORT, name) | |||
#define USB_INPORT(name) USB_CONCAT(PIN, name) | |||
#define USB_DDRPORT(name) USB_CONCAT(DDR, name) | |||
/* The double-define trick above lets us concatenate strings which are | |||
* defined by macros. | |||
*/ | |||
/* ------------------------------------------------------------------------- */ | |||
/* ------------------------- Constant definitions -------------------------- */ | |||
/* ------------------------------------------------------------------------- */ | |||
#if !defined __ASSEMBLER__ && (!defined USB_CFG_VENDOR_ID || !defined USB_CFG_DEVICE_ID) | |||
#warning "You should define USB_CFG_VENDOR_ID and USB_CFG_DEVICE_ID in usbconfig.h" | |||
/* If the user has not defined IDs, we default to obdev's free IDs. | |||
* See USB-IDs-for-free.txt for details. | |||
*/ | |||
#endif | |||
/* make sure we have a VID and PID defined, byte order is lowbyte, highbyte */ | |||
#ifndef USB_CFG_VENDOR_ID | |||
# define USB_CFG_VENDOR_ID 0xc0, 0x16 /* = 0x16c0 = 5824 = voti.nl */ | |||
#endif | |||
#ifndef USB_CFG_DEVICE_ID | |||
# if USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH | |||
# define USB_CFG_DEVICE_ID 0xdf, 0x05 /* = 0x5df = 1503, shared PID for HIDs */ | |||
# elif USB_CFG_INTERFACE_CLASS == 2 | |||
# define USB_CFG_DEVICE_ID 0xe1, 0x05 /* = 0x5e1 = 1505, shared PID for CDC Modems */ | |||
# else | |||
# define USB_CFG_DEVICE_ID 0xdc, 0x05 /* = 0x5dc = 1500, obdev's free PID */ | |||
# endif | |||
#endif | |||
/* Derive Output, Input and DataDirection ports from port names */ | |||
#ifndef USB_CFG_IOPORTNAME | |||
#error "You must define USB_CFG_IOPORTNAME in usbconfig.h, see usbconfig-prototype.h" | |||
#endif | |||
#define USBOUT USB_OUTPORT(USB_CFG_IOPORTNAME) | |||
#define USB_PULLUP_OUT USB_OUTPORT(USB_CFG_PULLUP_IOPORTNAME) | |||
#define USBIN USB_INPORT(USB_CFG_IOPORTNAME) | |||
#define USBDDR USB_DDRPORT(USB_CFG_IOPORTNAME) | |||
#define USB_PULLUP_DDR USB_DDRPORT(USB_CFG_PULLUP_IOPORTNAME) | |||
#define USBMINUS USB_CFG_DMINUS_BIT | |||
#define USBPLUS USB_CFG_DPLUS_BIT | |||
#define USBIDLE (1<<USB_CFG_DMINUS_BIT) /* value representing J state */ | |||
#define USBMASK ((1<<USB_CFG_DPLUS_BIT) | (1<<USB_CFG_DMINUS_BIT)) /* mask for USB I/O bits */ | |||
/* defines for backward compatibility with older driver versions: */ | |||
#define USB_CFG_IOPORT USB_OUTPORT(USB_CFG_IOPORTNAME) | |||
#ifdef USB_CFG_PULLUP_IOPORTNAME | |||
#define USB_CFG_PULLUP_IOPORT USB_OUTPORT(USB_CFG_PULLUP_IOPORTNAME) | |||
#endif | |||
#ifndef USB_CFG_EP3_NUMBER /* if not defined in usbconfig.h */ | |||
#define USB_CFG_EP3_NUMBER 3 | |||
#endif | |||
#ifndef USB_CFG_HAVE_INTRIN_ENDPOINT3 | |||
#define USB_CFG_HAVE_INTRIN_ENDPOINT3 0 | |||
#endif | |||
#define USB_BUFSIZE 11 /* PID, 8 bytes data, 2 bytes CRC */ | |||
/* ----- Try to find registers and bits responsible for ext interrupt 0 ----- */ | |||
#ifndef USB_INTR_CFG /* allow user to override our default */ | |||
# if defined EICRA | |||
# define USB_INTR_CFG EICRA | |||
# else | |||
# define USB_INTR_CFG MCUCR | |||
# endif | |||
#endif | |||
#ifndef USB_INTR_CFG_SET /* allow user to override our default */ | |||
# if defined(USB_COUNT_SOF) || defined(USB_SOF_HOOK) | |||
# define USB_INTR_CFG_SET (1 << ISC01) /* cfg for falling edge */ | |||
/* If any SOF logic is used, the interrupt must be wired to D- where | |||
* we better trigger on falling edge | |||
*/ | |||
# else | |||
# define USB_INTR_CFG_SET ((1 << ISC00) | (1 << ISC01)) /* cfg for rising edge */ | |||
# endif | |||
#endif | |||
#ifndef USB_INTR_CFG_CLR /* allow user to override our default */ | |||
# define USB_INTR_CFG_CLR 0 /* no bits to clear */ | |||
#endif | |||
#ifndef USB_INTR_ENABLE /* allow user to override our default */ | |||
# if defined GIMSK | |||
# define USB_INTR_ENABLE GIMSK | |||
# elif defined EIMSK | |||
# define USB_INTR_ENABLE EIMSK | |||
# else | |||
# define USB_INTR_ENABLE GICR | |||
# endif | |||
#endif | |||
#ifndef USB_INTR_ENABLE_BIT /* allow user to override our default */ | |||
# define USB_INTR_ENABLE_BIT INT0 | |||
#endif | |||
#ifndef USB_INTR_PENDING /* allow user to override our default */ | |||
# if defined EIFR | |||
# define USB_INTR_PENDING EIFR | |||
# else | |||
# define USB_INTR_PENDING GIFR | |||
# endif | |||
#endif | |||
#ifndef USB_INTR_PENDING_BIT /* allow user to override our default */ | |||
# define USB_INTR_PENDING_BIT INTF0 | |||
#endif | |||
/* | |||
The defines above don't work for the following chips | |||
at90c8534: no ISC0?, no PORTB, can't find a data sheet | |||
at86rf401: no PORTB, no MCUCR etc, low clock rate | |||
atmega103: no ISC0? (maybe omission in header, can't find data sheet) | |||
atmega603: not defined in avr-libc | |||
at43usb320, at43usb355, at76c711: have USB anyway | |||
at94k: is different... | |||
at90s1200, attiny11, attiny12, attiny15, attiny28: these have no RAM | |||
*/ | |||
/* ------------------------------------------------------------------------- */ | |||
/* ----------------- USB Specification Constants and Types ----------------- */ | |||
/* ------------------------------------------------------------------------- */ | |||
/* USB Token values */ | |||
#define USBPID_SETUP 0x2d | |||
#define USBPID_OUT 0xe1 | |||
#define USBPID_IN 0x69 | |||
#define USBPID_DATA0 0xc3 | |||
#define USBPID_DATA1 0x4b | |||
#define USBPID_ACK 0xd2 | |||
#define USBPID_NAK 0x5a | |||
#define USBPID_STALL 0x1e | |||
#ifndef USB_INITIAL_DATATOKEN | |||
#define USB_INITIAL_DATATOKEN USBPID_DATA1 | |||
#endif | |||
#ifndef __ASSEMBLER__ | |||
typedef struct usbTxStatus{ | |||
volatile uchar len; | |||
uchar buffer[USB_BUFSIZE]; | |||
}usbTxStatus_t; | |||
extern usbTxStatus_t usbTxStatus1, usbTxStatus3; | |||
#define usbTxLen1 usbTxStatus1.len | |||
#define usbTxBuf1 usbTxStatus1.buffer | |||
#define usbTxLen3 usbTxStatus3.len | |||
#define usbTxBuf3 usbTxStatus3.buffer | |||
typedef union usbWord{ | |||
unsigned word; | |||
uchar bytes[2]; | |||
}usbWord_t; | |||
typedef struct usbRequest{ | |||
uchar bmRequestType; | |||
uchar bRequest; | |||
usbWord_t wValue; | |||
usbWord_t wIndex; | |||
usbWord_t wLength; | |||
}usbRequest_t; | |||
/* This structure matches the 8 byte setup request */ | |||
#endif | |||
/* bmRequestType field in USB setup: | |||
* d t t r r r r r, where | |||
* d ..... direction: 0=host->device, 1=device->host | |||
* t ..... type: 0=standard, 1=class, 2=vendor, 3=reserved | |||
* r ..... recipient: 0=device, 1=interface, 2=endpoint, 3=other | |||
*/ | |||
/* USB setup recipient values */ | |||
#define USBRQ_RCPT_MASK 0x1f | |||
#define USBRQ_RCPT_DEVICE 0 | |||
#define USBRQ_RCPT_INTERFACE 1 | |||
#define USBRQ_RCPT_ENDPOINT 2 | |||
/* USB request type values */ | |||
#define USBRQ_TYPE_MASK 0x60 | |||
#define USBRQ_TYPE_STANDARD (0<<5) | |||
#define USBRQ_TYPE_CLASS (1<<5) | |||
#define USBRQ_TYPE_VENDOR (2<<5) | |||
/* USB direction values: */ | |||
#define USBRQ_DIR_MASK 0x80 | |||
#define USBRQ_DIR_HOST_TO_DEVICE (0<<7) | |||
#define USBRQ_DIR_DEVICE_TO_HOST (1<<7) | |||
/* USB Standard Requests */ | |||
#define USBRQ_GET_STATUS 0 | |||
#define USBRQ_CLEAR_FEATURE 1 | |||
#define USBRQ_SET_FEATURE 3 | |||
#define USBRQ_SET_ADDRESS 5 | |||
#define USBRQ_GET_DESCRIPTOR 6 | |||
#define USBRQ_SET_DESCRIPTOR 7 | |||
#define USBRQ_GET_CONFIGURATION 8 | |||
#define USBRQ_SET_CONFIGURATION 9 | |||
#define USBRQ_GET_INTERFACE 10 | |||
#define USBRQ_SET_INTERFACE 11 | |||
#define USBRQ_SYNCH_FRAME 12 | |||
/* USB descriptor constants */ | |||
#define USBDESCR_DEVICE 1 | |||
#define USBDESCR_CONFIG 2 | |||
#define USBDESCR_STRING 3 | |||
#define USBDESCR_INTERFACE 4 | |||
#define USBDESCR_ENDPOINT 5 | |||
#define USBDESCR_HID 0x21 | |||
#define USBDESCR_HID_REPORT 0x22 | |||
#define USBDESCR_HID_PHYS 0x23 | |||
//#define USBATTR_BUSPOWER 0x80 // USB 1.1 does not define this value any more | |||
#define USBATTR_SELFPOWER 0x40 | |||
#define USBATTR_REMOTEWAKE 0x20 | |||
/* USB HID Requests */ | |||
#define USBRQ_HID_GET_REPORT 0x01 | |||
#define USBRQ_HID_GET_IDLE 0x02 | |||
#define USBRQ_HID_GET_PROTOCOL 0x03 | |||
#define USBRQ_HID_SET_REPORT 0x09 | |||
#define USBRQ_HID_SET_IDLE 0x0a | |||
#define USBRQ_HID_SET_PROTOCOL 0x0b | |||
/* ------------------------------------------------------------------------- */ | |||
#endif /* __usbdrv_h_included__ */ |
@@ -0,0 +1,393 @@ | |||
/* Name: usbdrvasm.S | |||
* Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers | |||
* Author: Christian Starkjohann | |||
* Creation Date: 2007-06-13 | |||
* Tabsize: 4 | |||
* Copyright: (c) 2007 by OBJECTIVE DEVELOPMENT Software GmbH | |||
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) | |||
* Revision: $Id: usbdrvasm.S 785 2010-05-30 17:57:07Z cs $ | |||
*/ | |||
/* | |||
General Description: | |||
This module is the assembler part of the USB driver. This file contains | |||
general code (preprocessor acrobatics and CRC computation) and then includes | |||
the file appropriate for the given clock rate. | |||
*/ | |||
#define __SFR_OFFSET 0 /* used by avr-libc's register definitions */ | |||
#include "usbportability.h" | |||
#include "usbdrv.h" /* for common defs */ | |||
/* register names */ | |||
#define x1 r16 | |||
#define x2 r17 | |||
#define shift r18 | |||
#define cnt r19 | |||
#define x3 r20 | |||
#define x4 r21 | |||
#define x5 r22 | |||
#define bitcnt x5 | |||
#define phase x4 | |||
#define leap x4 | |||
/* Some assembler dependent definitions and declarations: */ | |||
#ifdef __IAR_SYSTEMS_ASM__ | |||
extern usbRxBuf, usbDeviceAddr, usbNewDeviceAddr, usbInputBufOffset | |||
extern usbCurrentTok, usbRxLen, usbRxToken, usbTxLen | |||
extern usbTxBuf, usbTxStatus1, usbTxStatus3 | |||
# if USB_COUNT_SOF | |||
extern usbSofCount | |||
# endif | |||
public usbCrc16 | |||
public usbCrc16Append | |||
COMMON INTVEC | |||
# ifndef USB_INTR_VECTOR | |||
ORG INT0_vect | |||
# else /* USB_INTR_VECTOR */ | |||
ORG USB_INTR_VECTOR | |||
# undef USB_INTR_VECTOR | |||
# endif /* USB_INTR_VECTOR */ | |||
# define USB_INTR_VECTOR usbInterruptHandler | |||
rjmp USB_INTR_VECTOR | |||
RSEG CODE | |||
#else /* __IAR_SYSTEMS_ASM__ */ | |||
# ifndef USB_INTR_VECTOR /* default to hardware interrupt INT0 */ | |||
# ifdef INT0_vect | |||
# define USB_INTR_VECTOR INT0_vect // this is the "new" define for the vector | |||
# else | |||
# define USB_INTR_VECTOR SIG_INTERRUPT0 // this is the "old" vector | |||
# endif | |||
# endif | |||
.text | |||
.global USB_INTR_VECTOR | |||
.type USB_INTR_VECTOR, @function | |||
.global usbCrc16 | |||
.global usbCrc16Append | |||
#endif /* __IAR_SYSTEMS_ASM__ */ | |||
#if USB_INTR_PENDING < 0x40 /* This is an I/O address, use in and out */ | |||
# define USB_LOAD_PENDING(reg) in reg, USB_INTR_PENDING | |||
# define USB_STORE_PENDING(reg) out USB_INTR_PENDING, reg | |||
#else /* It's a memory address, use lds and sts */ | |||
# define USB_LOAD_PENDING(reg) lds reg, USB_INTR_PENDING | |||
# define USB_STORE_PENDING(reg) sts USB_INTR_PENDING, reg | |||
#endif | |||
#define usbTxLen1 usbTxStatus1 | |||
#define usbTxBuf1 (usbTxStatus1 + 1) | |||
#define usbTxLen3 usbTxStatus3 | |||
#define usbTxBuf3 (usbTxStatus3 + 1) | |||
;---------------------------------------------------------------------------- | |||
; Utility functions | |||
;---------------------------------------------------------------------------- | |||
#ifdef __IAR_SYSTEMS_ASM__ | |||
/* Register assignments for usbCrc16 on IAR cc */ | |||
/* Calling conventions on IAR: | |||
* First parameter passed in r16/r17, second in r18/r19 and so on. | |||
* Callee must preserve r4-r15, r24-r29 (r28/r29 is frame pointer) | |||
* Result is passed in r16/r17 | |||
* In case of the "tiny" memory model, pointers are only 8 bit with no | |||
* padding. We therefore pass argument 1 as "16 bit unsigned". | |||
*/ | |||
RTMODEL "__rt_version", "3" | |||
/* The line above will generate an error if cc calling conventions change. | |||
* The value "3" above is valid for IAR 4.10B/W32 | |||
*/ | |||
# define argLen r18 /* argument 2 */ | |||
# define argPtrL r16 /* argument 1 */ | |||
# define argPtrH r17 /* argument 1 */ | |||
# define resCrcL r16 /* result */ | |||
# define resCrcH r17 /* result */ | |||
# define ptrL ZL | |||
# define ptrH ZH | |||
# define ptr Z | |||
# define byte r22 | |||
# define bitCnt r19 | |||
# define polyL r20 | |||
# define polyH r21 | |||
# define scratch r23 | |||
#else /* __IAR_SYSTEMS_ASM__ */ | |||
/* Register assignments for usbCrc16 on gcc */ | |||
/* Calling conventions on gcc: | |||
* First parameter passed in r24/r25, second in r22/23 and so on. | |||
* Callee must preserve r1-r17, r28/r29 | |||
* Result is passed in r24/r25 | |||
*/ | |||
# define argLen r22 /* argument 2 */ | |||
# define argPtrL r24 /* argument 1 */ | |||
# define argPtrH r25 /* argument 1 */ | |||
# define resCrcL r24 /* result */ | |||
# define resCrcH r25 /* result */ | |||
# define ptrL XL | |||
# define ptrH XH | |||
# define ptr x | |||
# define byte r18 | |||
# define bitCnt r19 | |||
# define polyL r20 | |||
# define polyH r21 | |||
# define scratch r23 | |||
#endif | |||
#if USB_USE_FAST_CRC | |||
; This implementation is faster, but has bigger code size | |||
; Thanks to Slawomir Fras (BoskiDialer) for this code! | |||
; It implements the following C pseudo-code: | |||
; unsigned table(unsigned char x) | |||
; { | |||
; unsigned value; | |||
; | |||
; value = (unsigned)x << 6; | |||
; value ^= (unsigned)x << 7; | |||
; if(parity(x)) | |||
; value ^= 0xc001; | |||
; return value; | |||
; } | |||
; unsigned usbCrc16(unsigned char *argPtr, unsigned char argLen) | |||
; { | |||
; unsigned crc = 0xffff; | |||
; | |||
; while(argLen--) | |||
; crc = table(lo8(crc) ^ *argPtr++) ^ hi8(crc); | |||
; return ~crc; | |||
; } | |||
; extern unsigned usbCrc16(unsigned char *argPtr, unsigned char argLen); | |||
; argPtr r24+25 / r16+r17 | |||
; argLen r22 / r18 | |||
; temp variables: | |||
; byte r18 / r22 | |||
; scratch r23 | |||
; resCrc r24+r25 / r16+r17 | |||
; ptr X / Z | |||
usbCrc16: | |||
mov ptrL, argPtrL | |||
mov ptrH, argPtrH | |||
ldi resCrcL, 0xFF | |||
ldi resCrcH, 0xFF | |||
rjmp usbCrc16LoopTest | |||
usbCrc16ByteLoop: | |||
ld byte, ptr+ | |||
eor resCrcL, byte ; resCrcL is now 'x' in table() | |||
mov byte, resCrcL ; compute parity of 'x' | |||
swap byte | |||
eor byte, resCrcL | |||
mov scratch, byte | |||
lsr byte | |||
lsr byte | |||
eor byte, scratch | |||
inc byte | |||
lsr byte | |||
andi byte, 1 ; byte is now parity(x) | |||
mov scratch, resCrcL | |||
mov resCrcL, resCrcH | |||
eor resCrcL, byte ; low byte of if(parity(x)) value ^= 0xc001; | |||
neg byte | |||
andi byte, 0xc0 | |||
mov resCrcH, byte ; high byte of if(parity(x)) value ^= 0xc001; | |||
clr byte | |||
lsr scratch | |||
ror byte | |||
eor resCrcH, scratch | |||
eor resCrcL, byte | |||
lsr scratch | |||
ror byte | |||
eor resCrcH, scratch | |||
eor resCrcL, byte | |||
usbCrc16LoopTest: | |||
subi argLen, 1 | |||
brsh usbCrc16ByteLoop | |||
com resCrcL | |||
com resCrcH | |||
ret | |||
#else /* USB_USE_FAST_CRC */ | |||
; This implementation is slower, but has less code size | |||
; | |||
; extern unsigned usbCrc16(unsigned char *argPtr, unsigned char argLen); | |||
; argPtr r24+25 / r16+r17 | |||
; argLen r22 / r18 | |||
; temp variables: | |||
; byte r18 / r22 | |||
; bitCnt r19 | |||
; poly r20+r21 | |||
; scratch r23 | |||
; resCrc r24+r25 / r16+r17 | |||
; ptr X / Z | |||
usbCrc16: | |||
mov ptrL, argPtrL | |||
mov ptrH, argPtrH | |||
ldi resCrcL, 0 | |||
ldi resCrcH, 0 | |||
ldi polyL, lo8(0xa001) | |||
ldi polyH, hi8(0xa001) | |||
com argLen ; argLen = -argLen - 1: modified loop to ensure that carry is set | |||
ldi bitCnt, 0 ; loop counter with starnd condition = end condition | |||
rjmp usbCrcLoopEntry | |||
usbCrcByteLoop: | |||
ld byte, ptr+ | |||
eor resCrcL, byte | |||
usbCrcBitLoop: | |||
ror resCrcH ; carry is always set here (see brcs jumps to here) | |||
ror resCrcL | |||
brcs usbCrcNoXor | |||
eor resCrcL, polyL | |||
eor resCrcH, polyH | |||
usbCrcNoXor: | |||
subi bitCnt, 224 ; (8 * 224) % 256 = 0; this loop iterates 8 times | |||
brcs usbCrcBitLoop | |||
usbCrcLoopEntry: | |||
subi argLen, -1 | |||
brcs usbCrcByteLoop | |||
usbCrcReady: | |||
ret | |||
; Thanks to Reimar Doeffinger for optimizing this CRC routine! | |||
#endif /* USB_USE_FAST_CRC */ | |||
; extern unsigned usbCrc16Append(unsigned char *data, unsigned char len); | |||
usbCrc16Append: | |||
rcall usbCrc16 | |||
st ptr+, resCrcL | |||
st ptr+, resCrcH | |||
ret | |||
#undef argLen | |||
#undef argPtrL | |||
#undef argPtrH | |||
#undef resCrcL | |||
#undef resCrcH | |||
#undef ptrL | |||
#undef ptrH | |||
#undef ptr | |||
#undef byte | |||
#undef bitCnt | |||
#undef polyL | |||
#undef polyH | |||
#undef scratch | |||
#if USB_CFG_HAVE_MEASURE_FRAME_LENGTH | |||
#ifdef __IAR_SYSTEMS_ASM__ | |||
/* Register assignments for usbMeasureFrameLength on IAR cc */ | |||
/* Calling conventions on IAR: | |||
* First parameter passed in r16/r17, second in r18/r19 and so on. | |||
* Callee must preserve r4-r15, r24-r29 (r28/r29 is frame pointer) | |||
* Result is passed in r16/r17 | |||
* In case of the "tiny" memory model, pointers are only 8 bit with no | |||
* padding. We therefore pass argument 1 as "16 bit unsigned". | |||
*/ | |||
# define resL r16 | |||
# define resH r17 | |||
# define cnt16L r30 | |||
# define cnt16H r31 | |||
# define cntH r18 | |||
#else /* __IAR_SYSTEMS_ASM__ */ | |||
/* Register assignments for usbMeasureFrameLength on gcc */ | |||
/* Calling conventions on gcc: | |||
* First parameter passed in r24/r25, second in r22/23 and so on. | |||
* Callee must preserve r1-r17, r28/r29 | |||
* Result is passed in r24/r25 | |||
*/ | |||
# define resL r24 | |||
# define resH r25 | |||
# define cnt16L r24 | |||
# define cnt16H r25 | |||
# define cntH r26 | |||
#endif | |||
# define cnt16 cnt16L | |||
; extern unsigned usbMeasurePacketLength(void); | |||
; returns time between two idle strobes in multiples of 7 CPU clocks | |||
.global usbMeasureFrameLength | |||
usbMeasureFrameLength: | |||
ldi cntH, 6 ; wait ~ 10 ms for D- == 0 | |||
clr cnt16L | |||
clr cnt16H | |||
usbMFTime16: | |||
dec cntH | |||
breq usbMFTimeout | |||
usbMFWaitStrobe: ; first wait for D- == 0 (idle strobe) | |||
sbiw cnt16, 1 ;[0] [6] | |||
breq usbMFTime16 ;[2] | |||
sbic USBIN, USBMINUS ;[3] | |||
rjmp usbMFWaitStrobe ;[4] | |||
usbMFWaitIdle: ; then wait until idle again | |||
sbis USBIN, USBMINUS ;1 wait for D- == 1 | |||
rjmp usbMFWaitIdle ;2 | |||
ldi cnt16L, 1 ;1 represents cycles so far | |||
clr cnt16H ;1 | |||
usbMFWaitLoop: | |||
in cntH, USBIN ;[0] [7] | |||
adiw cnt16, 1 ;[1] | |||
breq usbMFTimeout ;[3] | |||
andi cntH, USBMASK ;[4] | |||
brne usbMFWaitLoop ;[5] | |||
usbMFTimeout: | |||
#if resL != cnt16L | |||
mov resL, cnt16L | |||
mov resH, cnt16H | |||
#endif | |||
ret | |||
#undef resL | |||
#undef resH | |||
#undef cnt16 | |||
#undef cnt16L | |||
#undef cnt16H | |||
#undef cntH | |||
#endif /* USB_CFG_HAVE_MEASURE_FRAME_LENGTH */ | |||
;---------------------------------------------------------------------------- | |||
; Now include the clock rate specific code | |||
;---------------------------------------------------------------------------- | |||
#ifndef USB_CFG_CLOCK_KHZ | |||
# ifdef F_CPU | |||
# define USB_CFG_CLOCK_KHZ (F_CPU/1000) | |||
# else | |||
# error "USB_CFG_CLOCK_KHZ not defined in usbconfig.h and no F_CPU set!" | |||
# endif | |||
#endif | |||
#if USB_CFG_CHECK_CRC /* separate dispatcher for CRC type modules */ | |||
# if USB_CFG_CLOCK_KHZ == 18000 | |||
# include "usbdrvasm18-crc.inc" | |||
# else | |||
# error "USB_CFG_CLOCK_KHZ is not one of the supported crc-rates!" | |||
# endif | |||
#else /* USB_CFG_CHECK_CRC */ | |||
# if USB_CFG_CLOCK_KHZ == 12000 | |||
# include "usbdrvasm12.inc" | |||
# elif USB_CFG_CLOCK_KHZ == 12800 | |||
# include "usbdrvasm128.inc" | |||
# elif USB_CFG_CLOCK_KHZ == 15000 | |||
# include "usbdrvasm15.inc" | |||
# elif USB_CFG_CLOCK_KHZ == 16000 | |||
# include "usbdrvasm16.inc" | |||
# elif USB_CFG_CLOCK_KHZ == 16500 | |||
# include "usbdrvasm165.inc" | |||
# elif USB_CFG_CLOCK_KHZ == 20000 | |||
# include "usbdrvasm20.inc" | |||
# else | |||
# error "USB_CFG_CLOCK_KHZ is not one of the supported non-crc-rates!" | |||
# endif | |||
#endif /* USB_CFG_CHECK_CRC */ |
@@ -0,0 +1,21 @@ | |||
/* Name: usbdrvasm.asm | |||
* Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers | |||
* Author: Christian Starkjohann | |||
* Creation Date: 2006-03-01 | |||
* Tabsize: 4 | |||
* Copyright: (c) 2006 by OBJECTIVE DEVELOPMENT Software GmbH | |||
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) | |||
* This Revision: $Id$ | |||
*/ | |||
/* | |||
General Description: | |||
The IAR compiler/assembler system prefers assembler files with file extension | |||
".asm". We simply provide this file as an alias for usbdrvasm.S. | |||
Thanks to Oleg Semyonov for his help with the IAR tools port! | |||
*/ | |||
#include "usbdrvasm.S" | |||
end |
@@ -0,0 +1,393 @@ | |||
/* Name: usbdrvasm12.inc | |||
* Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers | |||
* Author: Christian Starkjohann | |||
* Creation Date: 2004-12-29 | |||
* Tabsize: 4 | |||
* Copyright: (c) 2007 by OBJECTIVE DEVELOPMENT Software GmbH | |||
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) | |||
* This Revision: $Id: usbdrvasm12.inc 740 2009-04-13 18:23:31Z cs $ | |||
*/ | |||
/* Do not link this file! Link usbdrvasm.S instead, which includes the | |||
* appropriate implementation! | |||
*/ | |||
/* | |||
General Description: | |||
This file is the 12 MHz version of the asssembler part of the USB driver. It | |||
requires a 12 MHz crystal (not a ceramic resonator and not a calibrated RC | |||
oscillator). | |||
See usbdrv.h for a description of the entire driver. | |||
Since almost all of this code is timing critical, don't change unless you | |||
really know what you are doing! Many parts require not only a maximum number | |||
of CPU cycles, but even an exact number of cycles! | |||
Timing constraints according to spec (in bit times): | |||
timing subject min max CPUcycles | |||
--------------------------------------------------------------------------- | |||
EOP of OUT/SETUP to sync pattern of DATA0 (both rx) 2 16 16-128 | |||
EOP of IN to sync pattern of DATA0 (rx, then tx) 2 7.5 16-60 | |||
DATAx (rx) to ACK/NAK/STALL (tx) 2 7.5 16-60 | |||
*/ | |||
;Software-receiver engine. Strict timing! Don't change unless you can preserve timing! | |||
;interrupt response time: 4 cycles + insn running = 7 max if interrupts always enabled | |||
;max allowable interrupt latency: 34 cycles -> max 25 cycles interrupt disable | |||
;max stack usage: [ret(2), YL, SREG, YH, shift, x1, x2, x3, cnt, x4] = 11 bytes | |||
;Numbers in brackets are maximum cycles since SOF. | |||
USB_INTR_VECTOR: | |||
;order of registers pushed: YL, SREG [sofError], YH, shift, x1, x2, x3, cnt | |||
push YL ;2 [35] push only what is necessary to sync with edge ASAP | |||
in YL, SREG ;1 [37] | |||
push YL ;2 [39] | |||
;---------------------------------------------------------------------------- | |||
; Synchronize with sync pattern: | |||
;---------------------------------------------------------------------------- | |||
;sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] | |||
;sync up with J to K edge during sync pattern -- use fastest possible loops | |||
;The first part waits at most 1 bit long since we must be in sync pattern. | |||
;YL is guarenteed to be < 0x80 because I flag is clear. When we jump to | |||
;waitForJ, ensure that this prerequisite is met. | |||
waitForJ: | |||
inc YL | |||
sbis USBIN, USBMINUS | |||
brne waitForJ ; just make sure we have ANY timeout | |||
waitForK: | |||
;The following code results in a sampling window of 1/4 bit which meets the spec. | |||
sbis USBIN, USBMINUS | |||
rjmp foundK | |||
sbis USBIN, USBMINUS | |||
rjmp foundK | |||
sbis USBIN, USBMINUS | |||
rjmp foundK | |||
sbis USBIN, USBMINUS | |||
rjmp foundK | |||
sbis USBIN, USBMINUS | |||
rjmp foundK | |||
#if USB_COUNT_SOF | |||
lds YL, usbSofCount | |||
inc YL | |||
sts usbSofCount, YL | |||
#endif /* USB_COUNT_SOF */ | |||
#ifdef USB_SOF_HOOK | |||
USB_SOF_HOOK | |||
#endif | |||
rjmp sofError | |||
foundK: | |||
;{3, 5} after falling D- edge, average delay: 4 cycles [we want 4 for center sampling] | |||
;we have 1 bit time for setup purposes, then sample again. Numbers in brackets | |||
;are cycles from center of first sync (double K) bit after the instruction | |||
push YH ;2 [2] | |||
lds YL, usbInputBufOffset;2 [4] | |||
clr YH ;1 [5] | |||
subi YL, lo8(-(usbRxBuf));1 [6] | |||
sbci YH, hi8(-(usbRxBuf));1 [7] | |||
sbis USBIN, USBMINUS ;1 [8] we want two bits K [sample 1 cycle too early] | |||
rjmp haveTwoBitsK ;2 [10] | |||
pop YH ;2 [11] undo the push from before | |||
rjmp waitForK ;2 [13] this was not the end of sync, retry | |||
haveTwoBitsK: | |||
;---------------------------------------------------------------------------- | |||
; push more registers and initialize values while we sample the first bits: | |||
;---------------------------------------------------------------------------- | |||
push shift ;2 [16] | |||
push x1 ;2 [12] | |||
push x2 ;2 [14] | |||
in x1, USBIN ;1 [17] <-- sample bit 0 | |||
ldi shift, 0xff ;1 [18] | |||
bst x1, USBMINUS ;1 [19] | |||
bld shift, 0 ;1 [20] | |||
push x3 ;2 [22] | |||
push cnt ;2 [24] | |||
in x2, USBIN ;1 [25] <-- sample bit 1 | |||
ser x3 ;1 [26] [inserted init instruction] | |||
eor x1, x2 ;1 [27] | |||
bst x1, USBMINUS ;1 [28] | |||
bld shift, 1 ;1 [29] | |||
ldi cnt, USB_BUFSIZE;1 [30] [inserted init instruction] | |||
rjmp rxbit2 ;2 [32] | |||
;---------------------------------------------------------------------------- | |||
; Receiver loop (numbers in brackets are cycles within byte after instr) | |||
;---------------------------------------------------------------------------- | |||
unstuff0: ;1 (branch taken) | |||
andi x3, ~0x01 ;1 [15] | |||
mov x1, x2 ;1 [16] x2 contains last sampled (stuffed) bit | |||
in x2, USBIN ;1 [17] <-- sample bit 1 again | |||
ori shift, 0x01 ;1 [18] | |||
rjmp didUnstuff0 ;2 [20] | |||
unstuff1: ;1 (branch taken) | |||
mov x2, x1 ;1 [21] x1 contains last sampled (stuffed) bit | |||
andi x3, ~0x02 ;1 [22] | |||
ori shift, 0x02 ;1 [23] | |||
nop ;1 [24] | |||
in x1, USBIN ;1 [25] <-- sample bit 2 again | |||
rjmp didUnstuff1 ;2 [27] | |||
unstuff2: ;1 (branch taken) | |||
andi x3, ~0x04 ;1 [29] | |||
ori shift, 0x04 ;1 [30] | |||
mov x1, x2 ;1 [31] x2 contains last sampled (stuffed) bit | |||
nop ;1 [32] | |||
in x2, USBIN ;1 [33] <-- sample bit 3 | |||
rjmp didUnstuff2 ;2 [35] | |||
unstuff3: ;1 (branch taken) | |||
in x2, USBIN ;1 [34] <-- sample stuffed bit 3 [one cycle too late] | |||
andi x3, ~0x08 ;1 [35] | |||
ori shift, 0x08 ;1 [36] | |||
rjmp didUnstuff3 ;2 [38] | |||
unstuff4: ;1 (branch taken) | |||
andi x3, ~0x10 ;1 [40] | |||
in x1, USBIN ;1 [41] <-- sample stuffed bit 4 | |||
ori shift, 0x10 ;1 [42] | |||
rjmp didUnstuff4 ;2 [44] | |||
unstuff5: ;1 (branch taken) | |||
andi x3, ~0x20 ;1 [48] | |||
in x2, USBIN ;1 [49] <-- sample stuffed bit 5 | |||
ori shift, 0x20 ;1 [50] | |||
rjmp didUnstuff5 ;2 [52] | |||
unstuff6: ;1 (branch taken) | |||
andi x3, ~0x40 ;1 [56] | |||
in x1, USBIN ;1 [57] <-- sample stuffed bit 6 | |||
ori shift, 0x40 ;1 [58] | |||
rjmp didUnstuff6 ;2 [60] | |||
; extra jobs done during bit interval: | |||
; bit 0: store, clear [SE0 is unreliable here due to bit dribbling in hubs] | |||
; bit 1: se0 check | |||
; bit 2: overflow check | |||
; bit 3: recovery from delay [bit 0 tasks took too long] | |||
; bit 4: none | |||
; bit 5: none | |||
; bit 6: none | |||
; bit 7: jump, eor | |||
rxLoop: | |||
eor x3, shift ;1 [0] reconstruct: x3 is 0 at bit locations we changed, 1 at others | |||
in x1, USBIN ;1 [1] <-- sample bit 0 | |||
st y+, x3 ;2 [3] store data | |||
ser x3 ;1 [4] | |||
nop ;1 [5] | |||
eor x2, x1 ;1 [6] | |||
bst x2, USBMINUS;1 [7] | |||
bld shift, 0 ;1 [8] | |||
in x2, USBIN ;1 [9] <-- sample bit 1 (or possibly bit 0 stuffed) | |||
andi x2, USBMASK ;1 [10] | |||
breq se0 ;1 [11] SE0 check for bit 1 | |||
andi shift, 0xf9 ;1 [12] | |||
didUnstuff0: | |||
breq unstuff0 ;1 [13] | |||
eor x1, x2 ;1 [14] | |||
bst x1, USBMINUS;1 [15] | |||
bld shift, 1 ;1 [16] | |||
rxbit2: | |||
in x1, USBIN ;1 [17] <-- sample bit 2 (or possibly bit 1 stuffed) | |||
andi shift, 0xf3 ;1 [18] | |||
breq unstuff1 ;1 [19] do remaining work for bit 1 | |||
didUnstuff1: | |||
subi cnt, 1 ;1 [20] | |||
brcs overflow ;1 [21] loop control | |||
eor x2, x1 ;1 [22] | |||
bst x2, USBMINUS;1 [23] | |||
bld shift, 2 ;1 [24] | |||
in x2, USBIN ;1 [25] <-- sample bit 3 (or possibly bit 2 stuffed) | |||
andi shift, 0xe7 ;1 [26] | |||
breq unstuff2 ;1 [27] | |||
didUnstuff2: | |||
eor x1, x2 ;1 [28] | |||
bst x1, USBMINUS;1 [29] | |||
bld shift, 3 ;1 [30] | |||
didUnstuff3: | |||
andi shift, 0xcf ;1 [31] | |||
breq unstuff3 ;1 [32] | |||
in x1, USBIN ;1 [33] <-- sample bit 4 | |||
eor x2, x1 ;1 [34] | |||
bst x2, USBMINUS;1 [35] | |||
bld shift, 4 ;1 [36] | |||
didUnstuff4: | |||
andi shift, 0x9f ;1 [37] | |||
breq unstuff4 ;1 [38] | |||
nop2 ;2 [40] | |||
in x2, USBIN ;1 [41] <-- sample bit 5 | |||
eor x1, x2 ;1 [42] | |||
bst x1, USBMINUS;1 [43] | |||
bld shift, 5 ;1 [44] | |||
didUnstuff5: | |||
andi shift, 0x3f ;1 [45] | |||
breq unstuff5 ;1 [46] | |||
nop2 ;2 [48] | |||
in x1, USBIN ;1 [49] <-- sample bit 6 | |||
eor x2, x1 ;1 [50] | |||
bst x2, USBMINUS;1 [51] | |||
bld shift, 6 ;1 [52] | |||
didUnstuff6: | |||
cpi shift, 0x02 ;1 [53] | |||
brlo unstuff6 ;1 [54] | |||
nop2 ;2 [56] | |||
in x2, USBIN ;1 [57] <-- sample bit 7 | |||
eor x1, x2 ;1 [58] | |||
bst x1, USBMINUS;1 [59] | |||
bld shift, 7 ;1 [60] | |||
didUnstuff7: | |||
cpi shift, 0x04 ;1 [61] | |||
brsh rxLoop ;2 [63] loop control | |||
unstuff7: | |||
andi x3, ~0x80 ;1 [63] | |||
ori shift, 0x80 ;1 [64] | |||
in x2, USBIN ;1 [65] <-- sample stuffed bit 7 | |||
nop ;1 [66] | |||
rjmp didUnstuff7 ;2 [68] | |||
macro POP_STANDARD ; 12 cycles | |||
pop cnt | |||
pop x3 | |||
pop x2 | |||
pop x1 | |||
pop shift | |||
pop YH | |||
endm | |||
macro POP_RETI ; 5 cycles | |||
pop YL | |||
out SREG, YL | |||
pop YL | |||
endm | |||
#include "asmcommon.inc" | |||
;---------------------------------------------------------------------------- | |||
; Transmitting data | |||
;---------------------------------------------------------------------------- | |||
txByteLoop: | |||
txBitloop: | |||
stuffN1Delay: ; [03] | |||
ror shift ;[-5] [11] [59] | |||
brcc doExorN1 ;[-4] [60] | |||
subi x4, 1 ;[-3] | |||
brne commonN1 ;[-2] | |||
lsl shift ;[-1] compensate ror after rjmp stuffDelay | |||
nop ;[00] stuffing consists of just waiting 8 cycles | |||
rjmp stuffN1Delay ;[01] after ror, C bit is reliably clear | |||
sendNakAndReti: ;0 [-19] 19 cycles until SOP | |||
ldi x3, USBPID_NAK ;1 [-18] | |||
rjmp usbSendX3 ;2 [-16] | |||
sendAckAndReti: ;0 [-19] 19 cycles until SOP | |||
ldi x3, USBPID_ACK ;1 [-18] | |||
rjmp usbSendX3 ;2 [-16] | |||
sendCntAndReti: ;0 [-17] 17 cycles until SOP | |||
mov x3, cnt ;1 [-16] | |||
usbSendX3: ;0 [-16] | |||
ldi YL, 20 ;1 [-15] 'x3' is R20 | |||
ldi YH, 0 ;1 [-14] | |||
ldi cnt, 2 ;1 [-13] | |||
; rjmp usbSendAndReti fallthrough | |||
; USB spec says: | |||
; idle = J | |||
; J = (D+ = 0), (D- = 1) or USBOUT = 0x01 | |||
; K = (D+ = 1), (D- = 0) or USBOUT = 0x02 | |||
; Spec allows 7.5 bit times from EOP to SOP for replies (= 60 cycles) | |||
;usbSend: | |||
;pointer to data in 'Y' | |||
;number of bytes in 'cnt' -- including sync byte | |||
;uses: x1...x2, x4, shift, cnt, Y [x1 = mirror USBOUT, x2 = USBMASK, x4 = bitstuff cnt] | |||
;Numbers in brackets are time since first bit of sync pattern is sent (start of instruction) | |||
usbSendAndReti: | |||
in x2, USBDDR ;[-12] 12 cycles until SOP | |||
ori x2, USBMASK ;[-11] | |||
sbi USBOUT, USBMINUS ;[-10] prepare idle state; D+ and D- must have been 0 (no pullups) | |||
out USBDDR, x2 ;[-8] <--- acquire bus | |||
in x1, USBOUT ;[-7] port mirror for tx loop | |||
ldi shift, 0x40 ;[-6] sync byte is first byte sent (we enter loop after ror) | |||
ldi x2, USBMASK ;[-5] | |||
push x4 ;[-4] | |||
doExorN1: | |||
eor x1, x2 ;[-2] [06] [62] | |||
ldi x4, 6 ;[-1] [07] [63] | |||
commonN1: | |||
stuffN2Delay: | |||
out USBOUT, x1 ;[00] [08] [64] <--- set bit | |||
ror shift ;[01] | |||
brcc doExorN2 ;[02] | |||
subi x4, 1 ;[03] | |||
brne commonN2 ;[04] | |||
lsl shift ;[05] compensate ror after rjmp stuffDelay | |||
rjmp stuffN2Delay ;[06] after ror, C bit is reliably clear | |||
doExorN2: | |||
eor x1, x2 ;[04] [12] | |||
ldi x4, 6 ;[05] [13] | |||
commonN2: | |||
nop ;[06] [14] | |||
subi cnt, 171 ;[07] [15] trick: (3 * 171) & 0xff = 1 | |||
out USBOUT, x1 ;[08] [16] <--- set bit | |||
brcs txBitloop ;[09] [25] [41] | |||
stuff6Delay: | |||
ror shift ;[42] [50] | |||
brcc doExor6 ;[43] | |||
subi x4, 1 ;[44] | |||
brne common6 ;[45] | |||
lsl shift ;[46] compensate ror after rjmp stuffDelay | |||
nop ;[47] stuffing consists of just waiting 8 cycles | |||
rjmp stuff6Delay ;[48] after ror, C bit is reliably clear | |||
doExor6: | |||
eor x1, x2 ;[45] [53] | |||
ldi x4, 6 ;[46] | |||
common6: | |||
stuff7Delay: | |||
ror shift ;[47] [55] | |||
out USBOUT, x1 ;[48] <--- set bit | |||
brcc doExor7 ;[49] | |||
subi x4, 1 ;[50] | |||
brne common7 ;[51] | |||
lsl shift ;[52] compensate ror after rjmp stuffDelay | |||
rjmp stuff7Delay ;[53] after ror, C bit is reliably clear | |||
doExor7: | |||
eor x1, x2 ;[51] [59] | |||
ldi x4, 6 ;[52] | |||
common7: | |||
ld shift, y+ ;[53] | |||
tst cnt ;[55] | |||
out USBOUT, x1 ;[56] <--- set bit | |||
brne txByteLoop ;[57] | |||
;make SE0: | |||
cbr x1, USBMASK ;[58] prepare SE0 [spec says EOP may be 15 to 18 cycles] | |||
lds x2, usbNewDeviceAddr;[59] | |||
lsl x2 ;[61] we compare with left shifted address | |||
subi YL, 2 + 20 ;[62] Only assign address on data packets, not ACK/NAK in x3 | |||
sbci YH, 0 ;[63] | |||
out USBOUT, x1 ;[00] <-- out SE0 -- from now 2 bits = 16 cycles until bus idle | |||
;2006-03-06: moved transfer of new address to usbDeviceAddr from C-Code to asm: | |||
;set address only after data packet was sent, not after handshake | |||
breq skipAddrAssign ;[01] | |||
sts usbDeviceAddr, x2 ; if not skipped: SE0 is one cycle longer | |||
skipAddrAssign: | |||
;end of usbDeviceAddress transfer | |||
ldi x2, 1<<USB_INTR_PENDING_BIT;[03] int0 occurred during TX -- clear pending flag | |||
USB_STORE_PENDING(x2) ;[04] | |||
ori x1, USBIDLE ;[05] | |||
in x2, USBDDR ;[06] | |||
cbr x2, USBMASK ;[07] set both pins to input | |||
mov x3, x1 ;[08] | |||
cbr x3, USBMASK ;[09] configure no pullup on both pins | |||
pop x4 ;[10] | |||
nop2 ;[12] | |||
nop2 ;[14] | |||
out USBOUT, x1 ;[16] <-- out J (idle) -- end of SE0 (EOP signal) | |||
out USBDDR, x2 ;[17] <-- release bus now | |||
out USBOUT, x3 ;[18] <-- ensure no pull-up resistors are active | |||
rjmp doReturn |
@@ -0,0 +1,750 @@ | |||
/* Name: usbdrvasm128.inc | |||
* Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers | |||
* Author: Christian Starkjohann | |||
* Creation Date: 2008-10-11 | |||
* Tabsize: 4 | |||
* Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH | |||
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) | |||
* This Revision: $Id: usbdrvasm128.inc 758 2009-08-06 10:12:54Z cs $ | |||
*/ | |||
/* Do not link this file! Link usbdrvasm.S instead, which includes the | |||
* appropriate implementation! | |||
*/ | |||
/* | |||
General Description: | |||
This file is the 12.8 MHz version of the USB driver. It is intended for use | |||
with the internal RC oscillator. Although 12.8 MHz is outside the guaranteed | |||
calibration range of the oscillator, almost all AVRs can reach this frequency. | |||
This version contains a phase locked loop in the receiver routine to cope with | |||
slight clock rate deviations of up to +/- 1%. | |||
See usbdrv.h for a description of the entire driver. | |||
LIMITATIONS | |||
=========== | |||
Although it may seem very handy to save the crystal and use the internal | |||
RC oscillator of the CPU, this method (and this module) has some serious | |||
limitations: | |||
(1) The guaranteed calibration range of the oscillator is only 8.1 MHz. | |||
They typical range is 14.5 MHz and most AVRs can actually reach this rate. | |||
(2) Writing EEPROM and Flash may be unreliable (short data lifetime) since | |||
the write procedure is timed from the RC oscillator. | |||
(3) End Of Packet detection (SE0) should be in bit 1, bit it is only checked | |||
if bits 0 and 1 both read as 0 on D- and D+ read as 0 in the middle. This may | |||
cause problems with old hubs which delay SE0 by up to one cycle. | |||
(4) Code size is much larger than that of the other modules. | |||
Since almost all of this code is timing critical, don't change unless you | |||
really know what you are doing! Many parts require not only a maximum number | |||
of CPU cycles, but even an exact number of cycles! | |||
Implementation notes: | |||
====================== | |||
min frequency: 67 cycles for 8 bit -> 12.5625 MHz | |||
max frequency: 69.286 cycles for 8 bit -> 12.99 MHz | |||
nominal frequency: 12.77 MHz ( = sqrt(min * max)) | |||
sampling positions: (next even number in range [+/- 0.5]) | |||
cycle index range: 0 ... 66 | |||
bits: | |||
.5, 8.875, 17.25, 25.625, 34, 42.375, 50.75, 59.125 | |||
[0/1], [9], [17], [25/+26], [34], [+42/43], [51], [59] | |||
bit number: 0 1 2 3 4 5 6 7 | |||
spare cycles 1 2 1 2 1 1 1 0 | |||
operations to perform: duration cycle | |||
---------------- | |||
eor fix, shift 1 -> 00 | |||
andi phase, USBMASK 1 -> 08 | |||
breq se0 1 -> 16 (moved to 11) | |||
st y+, data 2 -> 24, 25 | |||
mov data, fix 1 -> 33 | |||
ser data 1 -> 41 | |||
subi cnt, 1 1 -> 49 | |||
brcs overflow 1 -> 50 | |||
layout of samples and operations: | |||
[##] = sample bit | |||
<##> = sample phase | |||
*##* = operation | |||
0: *00* [01] 02 03 04 <05> 06 07 | |||
1: *08* [09] 10 11 12 <13> 14 15 *16* | |||
2: [17] 18 19 20 <21> 22 23 | |||
3: *24* *25* [26] 27 28 29 <30> 31 32 | |||
4: *33* [34] 35 36 37 <38> 39 40 | |||
5: *41* [42] 43 44 45 <46> 47 48 | |||
6: *49* *50* [51] 52 53 54 <55> 56 57 58 | |||
7: [59] 60 61 62 <63> 64 65 66 | |||
*****************************************************************************/ | |||
/* we prefer positive expressions (do if condition) instead of negative | |||
* (skip if condition), therefore use defines for skip instructions: | |||
*/ | |||
#define ifioclr sbis | |||
#define ifioset sbic | |||
#define ifrclr sbrs | |||
#define ifrset sbrc | |||
/* The registers "fix" and "data" swap their meaning during the loop. Use | |||
* defines to keep their name constant. | |||
*/ | |||
#define fix x2 | |||
#define data x1 | |||
#undef phase /* phase has a default definition to x4 */ | |||
#define phase x3 | |||
USB_INTR_VECTOR: | |||
;order of registers pushed: YL, SREG [sofError], YH, shift, x1, x2, x3, cnt, r0 | |||
push YL ;2 push only what is necessary to sync with edge ASAP | |||
in YL, SREG ;1 | |||
push YL ;2 | |||
;---------------------------------------------------------------------------- | |||
; Synchronize with sync pattern: | |||
;---------------------------------------------------------------------------- | |||
;sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] | |||
;sync up with J to K edge during sync pattern -- use fastest possible loops | |||
;The first part waits at most 1 bit long since we must be in sync pattern. | |||
;YL is guarenteed to be < 0x80 because I flag is clear. When we jump to | |||
;waitForJ, ensure that this prerequisite is met. | |||
waitForJ: | |||
inc YL | |||
sbis USBIN, USBMINUS | |||
brne waitForJ ; just make sure we have ANY timeout | |||
waitForK: | |||
;The following code results in a sampling window of 1/4 bit which meets the spec. | |||
sbis USBIN, USBMINUS | |||
rjmp foundK | |||
sbis USBIN, USBMINUS | |||
rjmp foundK | |||
sbis USBIN, USBMINUS | |||
rjmp foundK | |||
sbis USBIN, USBMINUS | |||
rjmp foundK | |||
sbis USBIN, USBMINUS ;[0] | |||
rjmp foundK ;[1] | |||
#if USB_COUNT_SOF | |||
lds YL, usbSofCount | |||
inc YL | |||
sts usbSofCount, YL | |||
#endif /* USB_COUNT_SOF */ | |||
#ifdef USB_SOF_HOOK | |||
USB_SOF_HOOK | |||
#endif | |||
rjmp sofError | |||
foundK: | |||
;{3, 5} after falling D- edge, average delay: 4 cycles [we want 4 for center sampling] | |||
;we have 1 bit time for setup purposes, then sample again. Numbers in brackets | |||
;are cycles from center of first sync (double K) bit after the instruction | |||
push YH ;[2] | |||
lds YL, usbInputBufOffset;[4] | |||
clr YH ;[6] | |||
subi YL, lo8(-(usbRxBuf));[7] | |||
sbci YH, hi8(-(usbRxBuf));[8] | |||
sbis USBIN, USBMINUS ;[9] we want two bits K [we want to sample at 8 + 4 - 1.5 = 10.5] | |||
rjmp haveTwoBitsK ;[10] | |||
pop YH ;[11] undo the push from before | |||
rjmp waitForK ;[13] this was not the end of sync, retry | |||
haveTwoBitsK: | |||
;---------------------------------------------------------------------------- | |||
; push more registers and initialize values while we sample the first bits: | |||
;---------------------------------------------------------------------------- | |||
#define fix x2 | |||
#define data x1 | |||
push shift ;[12] | |||
push x1 ;[14] | |||
push x2 ;[16] | |||
ldi shift, 0x80 ;[18] prevent bit-unstuffing but init low bits to 0 | |||
ifioset USBIN, USBMINUS ;[19] [01] <--- bit 0 [10.5 + 8 = 18.5] | |||
ori shift, 1<<0 ;[02] | |||
push x3 ;[03] | |||
push cnt ;[05] | |||
push r0 ;[07] | |||
ifioset USBIN, USBMINUS ;[09] <--- bit 1 | |||
ori shift, 1<<1 ;[10] | |||
ser fix ;[11] | |||
ldi cnt, USB_BUFSIZE ;[12] | |||
mov data, shift ;[13] | |||
lsl shift ;[14] | |||
nop2 ;[15] | |||
ifioset USBIN, USBMINUS ;[17] <--- bit 2 | |||
ori data, 3<<2 ;[18] store in bit 2 AND bit 3 | |||
eor shift, data ;[19] do nrzi decoding | |||
andi data, 1<<3 ;[20] | |||
in phase, USBIN ;[21] <- phase | |||
brne jumpToEntryAfterSet ;[22] if USBMINS at bit 3 was 1 | |||
nop ;[23] | |||
rjmp entryAfterClr ;[24] | |||
jumpToEntryAfterSet: | |||
rjmp entryAfterSet ;[24] | |||
;---------------------------------------------------------------------------- | |||
; Receiver loop (numbers in brackets are cycles within byte after instr) | |||
;---------------------------------------------------------------------------- | |||
#undef fix | |||
#define fix x1 | |||
#undef data | |||
#define data x2 | |||
bit7IsSet: | |||
ifrclr phase, USBMINUS ;[62] check phase only if D- changed | |||
lpm ;[63] | |||
in phase, USBIN ;[64] <- phase (one cycle too late) | |||
ori shift, 1 << 7 ;[65] | |||
nop ;[66] | |||
;;;;rjmp bit0AfterSet ; -> [00] == [67] moved block up to save jump | |||
bit0AfterSet: | |||
eor fix, shift ;[00] | |||
#undef fix | |||
#define fix x2 | |||
#undef data | |||
#define data x1 /* we now have result in data, fix is reset to 0xff */ | |||
ifioclr USBIN, USBMINUS ;[01] <--- sample 0 | |||
rjmp bit0IsClr ;[02] | |||
andi shift, ~(7 << 0) ;[03] | |||
breq unstuff0s ;[04] | |||
in phase, USBIN ;[05] <- phase | |||
rjmp bit1AfterSet ;[06] | |||
unstuff0s: | |||
in phase, USBIN ;[06] <- phase (one cycle too late) | |||
andi fix, ~(1 << 0) ;[07] | |||
ifioclr USBIN, USBMINUS ;[00] | |||
ifioset USBIN, USBPLUS ;[01] | |||
rjmp bit0IsClr ;[02] executed if first expr false or second true | |||
se0AndStore: ; executed only if both bits 0 | |||
st y+, x1 ;[15/17] cycles after start of byte | |||
rjmp se0 ;[17/19] | |||
bit0IsClr: | |||
ifrset phase, USBMINUS ;[04] check phase only if D- changed | |||
lpm ;[05] | |||
in phase, USBIN ;[06] <- phase (one cycle too late) | |||
ori shift, 1 << 0 ;[07] | |||
bit1AfterClr: | |||
andi phase, USBMASK ;[08] | |||
ifioset USBIN, USBMINUS ;[09] <--- sample 1 | |||
rjmp bit1IsSet ;[10] | |||
breq se0AndStore ;[11] if D- was 0 in bits 0 AND 1 and D+ was 0 in between, we have SE0 | |||
andi shift, ~(7 << 1) ;[12] | |||
in phase, USBIN ;[13] <- phase | |||
breq unstuff1c ;[14] | |||
rjmp bit2AfterClr ;[15] | |||
unstuff1c: | |||
andi fix, ~(1 << 1) ;[16] | |||
nop2 ;[08] | |||
nop2 ;[10] | |||
bit1IsSet: | |||
ifrclr phase, USBMINUS ;[12] check phase only if D- changed | |||
lpm ;[13] | |||
in phase, USBIN ;[14] <- phase (one cycle too late) | |||
ori shift, 1 << 1 ;[15] | |||
nop ;[16] | |||
bit2AfterSet: | |||
ifioclr USBIN, USBMINUS ;[17] <--- sample 2 | |||
rjmp bit2IsClr ;[18] | |||
andi shift, ~(7 << 2) ;[19] | |||
breq unstuff2s ;[20] | |||
in phase, USBIN ;[21] <- phase | |||
rjmp bit3AfterSet ;[22] | |||
unstuff2s: | |||
in phase, USBIN ;[22] <- phase (one cycle too late) | |||
andi fix, ~(1 << 2) ;[23] | |||
nop2 ;[16] | |||
nop2 ;[18] | |||
bit2IsClr: | |||
ifrset phase, USBMINUS ;[20] check phase only if D- changed | |||
lpm ;[21] | |||
in phase, USBIN ;[22] <- phase (one cycle too late) | |||
ori shift, 1 << 2 ;[23] | |||
bit3AfterClr: | |||
st y+, data ;[24] | |||
entryAfterClr: | |||
ifioset USBIN, USBMINUS ;[26] <--- sample 3 | |||
rjmp bit3IsSet ;[27] | |||
andi shift, ~(7 << 3) ;[28] | |||
breq unstuff3c ;[29] | |||
in phase, USBIN ;[30] <- phase | |||
rjmp bit4AfterClr ;[31] | |||
unstuff3c: | |||
in phase, USBIN ;[31] <- phase (one cycle too late) | |||
andi fix, ~(1 << 3) ;[32] | |||
nop2 ;[25] | |||
nop2 ;[27] | |||
bit3IsSet: | |||
ifrclr phase, USBMINUS ;[29] check phase only if D- changed | |||
lpm ;[30] | |||
in phase, USBIN ;[31] <- phase (one cycle too late) | |||
ori shift, 1 << 3 ;[32] | |||
bit4AfterSet: | |||
mov data, fix ;[33] undo this move by swapping defines | |||
#undef fix | |||
#define fix x1 | |||
#undef data | |||
#define data x2 | |||
ifioclr USBIN, USBMINUS ;[34] <--- sample 4 | |||
rjmp bit4IsClr ;[35] | |||
andi shift, ~(7 << 4) ;[36] | |||
breq unstuff4s ;[37] | |||
in phase, USBIN ;[38] <- phase | |||
rjmp bit5AfterSet ;[39] | |||
unstuff4s: | |||
in phase, USBIN ;[39] <- phase (one cycle too late) | |||
andi fix, ~(1 << 4) ;[40] | |||
nop2 ;[33] | |||
nop2 ;[35] | |||
bit4IsClr: | |||
ifrset phase, USBMINUS ;[37] check phase only if D- changed | |||
lpm ;[38] | |||
in phase, USBIN ;[39] <- phase (one cycle too late) | |||
ori shift, 1 << 4 ;[40] | |||
bit5AfterClr: | |||
ser data ;[41] | |||
ifioset USBIN, USBMINUS ;[42] <--- sample 5 | |||
rjmp bit5IsSet ;[43] | |||
andi shift, ~(7 << 5) ;[44] | |||
breq unstuff5c ;[45] | |||
in phase, USBIN ;[46] <- phase | |||
rjmp bit6AfterClr ;[47] | |||
unstuff5c: | |||
in phase, USBIN ;[47] <- phase (one cycle too late) | |||
andi fix, ~(1 << 5) ;[48] | |||
nop2 ;[41] | |||
nop2 ;[43] | |||
bit5IsSet: | |||
ifrclr phase, USBMINUS ;[45] check phase only if D- changed | |||
lpm ;[46] | |||
in phase, USBIN ;[47] <- phase (one cycle too late) | |||
ori shift, 1 << 5 ;[48] | |||
bit6AfterSet: | |||
subi cnt, 1 ;[49] | |||
brcs jumpToOverflow ;[50] | |||
ifioclr USBIN, USBMINUS ;[51] <--- sample 6 | |||
rjmp bit6IsClr ;[52] | |||
andi shift, ~(3 << 6) ;[53] | |||
cpi shift, 2 ;[54] | |||
in phase, USBIN ;[55] <- phase | |||
brlt unstuff6s ;[56] | |||
rjmp bit7AfterSet ;[57] | |||
jumpToOverflow: | |||
rjmp overflow | |||
unstuff6s: | |||
andi fix, ~(1 << 6) ;[50] | |||
lpm ;[51] | |||
bit6IsClr: | |||
ifrset phase, USBMINUS ;[54] check phase only if D- changed | |||
lpm ;[55] | |||
in phase, USBIN ;[56] <- phase (one cycle too late) | |||
ori shift, 1 << 6 ;[57] | |||
nop ;[58] | |||
bit7AfterClr: | |||
ifioset USBIN, USBMINUS ;[59] <--- sample 7 | |||
rjmp bit7IsSet ;[60] | |||
andi shift, ~(1 << 7) ;[61] | |||
cpi shift, 4 ;[62] | |||
in phase, USBIN ;[63] <- phase | |||
brlt unstuff7c ;[64] | |||
rjmp bit0AfterClr ;[65] -> [00] == [67] | |||
unstuff7c: | |||
andi fix, ~(1 << 7) ;[58] | |||
nop ;[59] | |||
rjmp bit7IsSet ;[60] | |||
bit7IsClr: | |||
ifrset phase, USBMINUS ;[62] check phase only if D- changed | |||
lpm ;[63] | |||
in phase, USBIN ;[64] <- phase (one cycle too late) | |||
ori shift, 1 << 7 ;[65] | |||
nop ;[66] | |||
;;;;rjmp bit0AfterClr ; -> [00] == [67] moved block up to save jump | |||
bit0AfterClr: | |||
eor fix, shift ;[00] | |||
#undef fix | |||
#define fix x2 | |||
#undef data | |||
#define data x1 /* we now have result in data, fix is reset to 0xff */ | |||
ifioset USBIN, USBMINUS ;[01] <--- sample 0 | |||
rjmp bit0IsSet ;[02] | |||
andi shift, ~(7 << 0) ;[03] | |||
breq unstuff0c ;[04] | |||
in phase, USBIN ;[05] <- phase | |||
rjmp bit1AfterClr ;[06] | |||
unstuff0c: | |||
in phase, USBIN ;[06] <- phase (one cycle too late) | |||
andi fix, ~(1 << 0) ;[07] | |||
ifioclr USBIN, USBMINUS ;[00] | |||
ifioset USBIN, USBPLUS ;[01] | |||
rjmp bit0IsSet ;[02] executed if first expr false or second true | |||
rjmp se0AndStore ;[03] executed only if both bits 0 | |||
bit0IsSet: | |||
ifrclr phase, USBMINUS ;[04] check phase only if D- changed | |||
lpm ;[05] | |||
in phase, USBIN ;[06] <- phase (one cycle too late) | |||
ori shift, 1 << 0 ;[07] | |||
bit1AfterSet: | |||
andi shift, ~(7 << 1) ;[08] compensated by "ori shift, 1<<1" if bit1IsClr | |||
ifioclr USBIN, USBMINUS ;[09] <--- sample 1 | |||
rjmp bit1IsClr ;[10] | |||
breq unstuff1s ;[11] | |||
nop2 ;[12] do not check for SE0 if bit 0 was 1 | |||
in phase, USBIN ;[14] <- phase (one cycle too late) | |||
rjmp bit2AfterSet ;[15] | |||
unstuff1s: | |||
in phase, USBIN ;[13] <- phase | |||
andi fix, ~(1 << 1) ;[14] | |||
lpm ;[07] | |||
nop2 ;[10] | |||
bit1IsClr: | |||
ifrset phase, USBMINUS ;[12] check phase only if D- changed | |||
lpm ;[13] | |||
in phase, USBIN ;[14] <- phase (one cycle too late) | |||
ori shift, 1 << 1 ;[15] | |||
nop ;[16] | |||
bit2AfterClr: | |||
ifioset USBIN, USBMINUS ;[17] <--- sample 2 | |||
rjmp bit2IsSet ;[18] | |||
andi shift, ~(7 << 2) ;[19] | |||
breq unstuff2c ;[20] | |||
in phase, USBIN ;[21] <- phase | |||
rjmp bit3AfterClr ;[22] | |||
unstuff2c: | |||
in phase, USBIN ;[22] <- phase (one cycle too late) | |||
andi fix, ~(1 << 2) ;[23] | |||
nop2 ;[16] | |||
nop2 ;[18] | |||
bit2IsSet: | |||
ifrclr phase, USBMINUS ;[20] check phase only if D- changed | |||
lpm ;[21] | |||
in phase, USBIN ;[22] <- phase (one cycle too late) | |||
ori shift, 1 << 2 ;[23] | |||
bit3AfterSet: | |||
st y+, data ;[24] | |||
entryAfterSet: | |||
ifioclr USBIN, USBMINUS ;[26] <--- sample 3 | |||
rjmp bit3IsClr ;[27] | |||
andi shift, ~(7 << 3) ;[28] | |||
breq unstuff3s ;[29] | |||
in phase, USBIN ;[30] <- phase | |||
rjmp bit4AfterSet ;[31] | |||
unstuff3s: | |||
in phase, USBIN ;[31] <- phase (one cycle too late) | |||
andi fix, ~(1 << 3) ;[32] | |||
nop2 ;[25] | |||
nop2 ;[27] | |||
bit3IsClr: | |||
ifrset phase, USBMINUS ;[29] check phase only if D- changed | |||
lpm ;[30] | |||
in phase, USBIN ;[31] <- phase (one cycle too late) | |||
ori shift, 1 << 3 ;[32] | |||
bit4AfterClr: | |||
mov data, fix ;[33] undo this move by swapping defines | |||
#undef fix | |||
#define fix x1 | |||
#undef data | |||
#define data x2 | |||
ifioset USBIN, USBMINUS ;[34] <--- sample 4 | |||
rjmp bit4IsSet ;[35] | |||
andi shift, ~(7 << 4) ;[36] | |||
breq unstuff4c ;[37] | |||
in phase, USBIN ;[38] <- phase | |||
rjmp bit5AfterClr ;[39] | |||
unstuff4c: | |||
in phase, USBIN ;[39] <- phase (one cycle too late) | |||
andi fix, ~(1 << 4) ;[40] | |||
nop2 ;[33] | |||
nop2 ;[35] | |||
bit4IsSet: | |||
ifrclr phase, USBMINUS ;[37] check phase only if D- changed | |||
lpm ;[38] | |||
in phase, USBIN ;[39] <- phase (one cycle too late) | |||
ori shift, 1 << 4 ;[40] | |||
bit5AfterSet: | |||
ser data ;[41] | |||
ifioclr USBIN, USBMINUS ;[42] <--- sample 5 | |||
rjmp bit5IsClr ;[43] | |||
andi shift, ~(7 << 5) ;[44] | |||
breq unstuff5s ;[45] | |||
in phase, USBIN ;[46] <- phase | |||
rjmp bit6AfterSet ;[47] | |||
unstuff5s: | |||
in phase, USBIN ;[47] <- phase (one cycle too late) | |||
andi fix, ~(1 << 5) ;[48] | |||
nop2 ;[41] | |||
nop2 ;[43] | |||
bit5IsClr: | |||
ifrset phase, USBMINUS ;[45] check phase only if D- changed | |||
lpm ;[46] | |||
in phase, USBIN ;[47] <- phase (one cycle too late) | |||
ori shift, 1 << 5 ;[48] | |||
bit6AfterClr: | |||
subi cnt, 1 ;[49] | |||
brcs overflow ;[50] | |||
ifioset USBIN, USBMINUS ;[51] <--- sample 6 | |||
rjmp bit6IsSet ;[52] | |||
andi shift, ~(3 << 6) ;[53] | |||
cpi shift, 2 ;[54] | |||
in phase, USBIN ;[55] <- phase | |||
brlt unstuff6c ;[56] | |||
rjmp bit7AfterClr ;[57] | |||
unstuff6c: | |||
andi fix, ~(1 << 6) ;[50] | |||
lpm ;[51] | |||
bit6IsSet: | |||
ifrclr phase, USBMINUS ;[54] check phase only if D- changed | |||
lpm ;[55] | |||
in phase, USBIN ;[56] <- phase (one cycle too late) | |||
ori shift, 1 << 6 ;[57] | |||
bit7AfterSet: | |||
ifioclr USBIN, USBMINUS ;[59] <--- sample 7 | |||
rjmp bit7IsClr ;[60] | |||
andi shift, ~(1 << 7) ;[61] | |||
cpi shift, 4 ;[62] | |||
in phase, USBIN ;[63] <- phase | |||
brlt unstuff7s ;[64] | |||
rjmp bit0AfterSet ;[65] -> [00] == [67] | |||
unstuff7s: | |||
andi fix, ~(1 << 7) ;[58] | |||
nop ;[59] | |||
rjmp bit7IsClr ;[60] | |||
macro POP_STANDARD ; 14 cycles | |||
pop r0 | |||
pop cnt | |||
pop x3 | |||
pop x2 | |||
pop x1 | |||
pop shift | |||
pop YH | |||
endm | |||
macro POP_RETI ; 5 cycles | |||
pop YL | |||
out SREG, YL | |||
pop YL | |||
endm | |||
#include "asmcommon.inc" | |||
;---------------------------------------------------------------------------- | |||
; Transmitting data | |||
;---------------------------------------------------------------------------- | |||
txByteLoop: | |||
txBitloop: | |||
stuffN1Delay: ; [03] | |||
ror shift ;[-5] [11] [63] | |||
brcc doExorN1 ;[-4] [64] | |||
subi x3, 1 ;[-3] | |||
brne commonN1 ;[-2] | |||
lsl shift ;[-1] compensate ror after rjmp stuffDelay | |||
nop ;[00] stuffing consists of just waiting 8 cycles | |||
rjmp stuffN1Delay ;[01] after ror, C bit is reliably clear | |||
sendNakAndReti: | |||
ldi cnt, USBPID_NAK ;[-19] | |||
rjmp sendCntAndReti ;[-18] | |||
sendAckAndReti: | |||
ldi cnt, USBPID_ACK ;[-17] | |||
sendCntAndReti: | |||
mov r0, cnt ;[-16] | |||
ldi YL, 0 ;[-15] R0 address is 0 | |||
ldi YH, 0 ;[-14] | |||
ldi cnt, 2 ;[-13] | |||
; rjmp usbSendAndReti fallthrough | |||
; USB spec says: | |||
; idle = J | |||
; J = (D+ = 0), (D- = 1) or USBOUT = 0x01 | |||
; K = (D+ = 1), (D- = 0) or USBOUT = 0x02 | |||
; Spec allows 7.5 bit times from EOP to SOP for replies (= 60 cycles) | |||
;usbSend: | |||
;pointer to data in 'Y' | |||
;number of bytes in 'cnt' -- including sync byte | |||
;uses: x1...x3, shift, cnt, Y [x1 = mirror USBOUT, x2 = USBMASK, x3 = bitstuff cnt] | |||
;Numbers in brackets are time since first bit of sync pattern is sent (start of instruction) | |||
usbSendAndReti: | |||
in x2, USBDDR ;[-10] 10 cycles until SOP | |||
ori x2, USBMASK ;[-9] | |||
sbi USBOUT, USBMINUS ;[-8] prepare idle state; D+ and D- must have been 0 (no pullups) | |||
out USBDDR, x2 ;[-6] <--- acquire bus | |||
in x1, USBOUT ;[-5] port mirror for tx loop | |||
ldi shift, 0x40 ;[-4] sync byte is first byte sent (we enter loop after ror) | |||
ldi x2, USBMASK ;[-3] | |||
doExorN1: | |||
eor x1, x2 ;[-2] [06] [62] | |||
ldi x3, 6 ;[-1] [07] [63] | |||
commonN1: | |||
stuffN2Delay: | |||
out USBOUT, x1 ;[00] [08] [64] <--- set bit | |||
ror shift ;[01] | |||
brcc doExorN2 ;[02] | |||
subi x3, 1 ;[03] | |||
brne commonN2 ;[04] | |||
lsl shift ;[05] compensate ror after rjmp stuffDelay | |||
rjmp stuffN2Delay ;[06] after ror, C bit is reliably clear | |||
doExorN2: | |||
eor x1, x2 ;[04] [12] | |||
ldi x3, 6 ;[05] [13] | |||
commonN2: | |||
nop2 ;[06] [14] | |||
subi cnt, 171 ;[08] [16] trick: (3 * 171) & 0xff = 1 | |||
out USBOUT, x1 ;[09] [17] <--- set bit | |||
brcs txBitloop ;[10] [27] [44] | |||
stuff6Delay: | |||
ror shift ;[45] [53] | |||
brcc doExor6 ;[46] | |||
subi x3, 1 ;[47] | |||
brne common6 ;[48] | |||
lsl shift ;[49] compensate ror after rjmp stuffDelay | |||
nop ;[50] stuffing consists of just waiting 8 cycles | |||
rjmp stuff6Delay ;[51] after ror, C bit is reliably clear | |||
doExor6: | |||
eor x1, x2 ;[48] [56] | |||
ldi x3, 6 ;[49] | |||
common6: | |||
stuff7Delay: | |||
ror shift ;[50] [58] | |||
out USBOUT, x1 ;[51] <--- set bit | |||
brcc doExor7 ;[52] | |||
subi x3, 1 ;[53] | |||
brne common7 ;[54] | |||
lsl shift ;[55] compensate ror after rjmp stuffDelay | |||
rjmp stuff7Delay ;[56] after ror, C bit is reliably clear | |||
doExor7: | |||
eor x1, x2 ;[54] [62] | |||
ldi x3, 6 ;[55] | |||
common7: | |||
ld shift, y+ ;[56] | |||
nop ;[58] | |||
tst cnt ;[59] | |||
out USBOUT, x1 ;[60] [00]<--- set bit | |||
brne txByteLoop ;[61] [01] | |||
;make SE0: | |||
cbr x1, USBMASK ;[02] prepare SE0 [spec says EOP may be 15 to 18 cycles] | |||
lds x2, usbNewDeviceAddr;[03] | |||
lsl x2 ;[05] we compare with left shifted address | |||
subi YL, 2 + 0 ;[06] Only assign address on data packets, not ACK/NAK in r0 | |||
sbci YH, 0 ;[07] | |||
out USBOUT, x1 ;[00] <-- out SE0 -- from now 2 bits = 16 cycles until bus idle | |||
;2006-03-06: moved transfer of new address to usbDeviceAddr from C-Code to asm: | |||
;set address only after data packet was sent, not after handshake | |||
breq skipAddrAssign ;[01] | |||
sts usbDeviceAddr, x2 ; if not skipped: SE0 is one cycle longer | |||
skipAddrAssign: | |||
;end of usbDeviceAddress transfer | |||
ldi x2, 1<<USB_INTR_PENDING_BIT;[03] int0 occurred during TX -- clear pending flag | |||
USB_STORE_PENDING(x2) ;[04] | |||
ori x1, USBIDLE ;[05] | |||
in x2, USBDDR ;[06] | |||
cbr x2, USBMASK ;[07] set both pins to input | |||
mov x3, x1 ;[08] | |||
cbr x3, USBMASK ;[09] configure no pullup on both pins | |||
lpm ;[10] | |||
lpm ;[13] | |||
out USBOUT, x1 ;[16] <-- out J (idle) -- end of SE0 (EOP signal) | |||
out USBDDR, x2 ;[17] <-- release bus now | |||
out USBOUT, x3 ;[18] <-- ensure no pull-up resistors are active | |||
rjmp doReturn | |||
/***************************************************************************** | |||
The following PHP script generates a code skeleton for the receiver routine: | |||
<?php | |||
function printCmdBuffer($thisBit) | |||
{ | |||
global $cycle; | |||
$nextBit = ($thisBit + 1) % 8; | |||
$s = ob_get_contents(); | |||
ob_end_clean(); | |||
$s = str_replace("#", $thisBit, $s); | |||
$s = str_replace("@", $nextBit, $s); | |||
$lines = explode("\n", $s); | |||
for($i = 0; $i < count($lines); $i++){ | |||
$s = $lines[$i]; | |||
if(ereg("\\[([0-9-][0-9])\\]", $s, $regs)){ | |||
$c = $cycle + (int)$regs[1]; | |||
$s = ereg_replace("\\[[0-9-][0-9]\\]", sprintf("[%02d]", $c), $s); | |||
} | |||
if(strlen($s) > 0) | |||
echo "$s\n"; | |||
} | |||
} | |||
function printBit($isAfterSet, $bitNum) | |||
{ | |||
ob_start(); | |||
if($isAfterSet){ | |||
?> | |||
ifioclr USBIN, USBMINUS ;[00] <--- sample | |||
rjmp bit#IsClr ;[01] | |||
andi shift, ~(7 << #) ;[02] | |||
breq unstuff#s ;[03] | |||
in phase, USBIN ;[04] <- phase | |||
rjmp bit@AfterSet ;[05] | |||
unstuff#s: | |||
in phase, USBIN ;[05] <- phase (one cycle too late) | |||
andi fix, ~(1 << #) ;[06] | |||
nop2 ;[-1] | |||
nop2 ;[01] | |||
bit#IsClr: | |||
ifrset phase, USBMINUS ;[03] check phase only if D- changed | |||
lpm ;[04] | |||
in phase, USBIN ;[05] <- phase (one cycle too late) | |||
ori shift, 1 << # ;[06] | |||
<?php | |||
}else{ | |||
?> | |||
ifioset USBIN, USBMINUS ;[00] <--- sample | |||
rjmp bit#IsSet ;[01] | |||
andi shift, ~(7 << #) ;[02] | |||
breq unstuff#c ;[03] | |||
in phase, USBIN ;[04] <- phase | |||
rjmp bit@AfterClr ;[05] | |||
unstuff#c: | |||
in phase, USBIN ;[05] <- phase (one cycle too late) | |||
andi fix, ~(1 << #) ;[06] | |||
nop2 ;[-1] | |||
nop2 ;[01] | |||
bit#IsSet: | |||
ifrclr phase, USBMINUS ;[03] check phase only if D- changed | |||
lpm ;[04] | |||
in phase, USBIN ;[05] <- phase (one cycle too late) | |||
ori shift, 1 << # ;[06] | |||
<?php | |||
} | |||
printCmdBuffer($bitNum); | |||
} | |||
$bitStartCycles = array(1, 9, 17, 26, 34, 42, 51, 59); | |||
for($i = 0; $i < 16; $i++){ | |||
$bit = $i % 8; | |||
$emitClrCode = ($i + (int)($i / 8)) % 2; | |||
$cycle = $bitStartCycles[$bit]; | |||
if($emitClrCode){ | |||
printf("bit%dAfterClr:\n", $bit); | |||
}else{ | |||
printf("bit%dAfterSet:\n", $bit); | |||
} | |||
ob_start(); | |||
echo " ***** ;[-1]\n"; | |||
printCmdBuffer($bit); | |||
printBit(!$emitClrCode, $bit); | |||
if($i == 7) | |||
echo "\n"; | |||
} | |||
?> | |||
*****************************************************************************/ |
@@ -0,0 +1,423 @@ | |||
/* Name: usbdrvasm15.inc | |||
* Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers | |||
* Author: contributed by V. Bosch | |||
* Creation Date: 2007-08-06 | |||
* Tabsize: 4 | |||
* Copyright: (c) 2007 by OBJECTIVE DEVELOPMENT Software GmbH | |||
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) | |||
* Revision: $Id: usbdrvasm15.inc 740 2009-04-13 18:23:31Z cs $ | |||
*/ | |||
/* Do not link this file! Link usbdrvasm.S instead, which includes the | |||
* appropriate implementation! | |||
*/ | |||
/* | |||
General Description: | |||
This file is the 15 MHz version of the asssembler part of the USB driver. It | |||
requires a 15 MHz crystal (not a ceramic resonator and not a calibrated RC | |||
oscillator). | |||
See usbdrv.h for a description of the entire driver. | |||
Since almost all of this code is timing critical, don't change unless you | |||
really know what you are doing! Many parts require not only a maximum number | |||
of CPU cycles, but even an exact number of cycles! | |||
*/ | |||
;max stack usage: [ret(2), YL, SREG, YH, bitcnt, shift, x1, x2, x3, x4, cnt] = 12 bytes | |||
;nominal frequency: 15 MHz -> 10.0 cycles per bit, 80.0 cycles per byte | |||
; Numbers in brackets are clocks counted from center of last sync bit | |||
; when instruction starts | |||
;---------------------------------------------------------------------------- | |||
; order of registers pushed: | |||
; YL, SREG [sofError] YH, shift, x1, x2, x3, bitcnt, cnt, x4 | |||
;---------------------------------------------------------------------------- | |||
USB_INTR_VECTOR: | |||
push YL ;2 push only what is necessary to sync with edge ASAP | |||
in YL, SREG ;1 | |||
push YL ;2 | |||
;---------------------------------------------------------------------------- | |||
; Synchronize with sync pattern: | |||
; | |||
; sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] | |||
; sync up with J to K edge during sync pattern -- use fastest possible loops | |||
;The first part waits at most 1 bit long since we must be in sync pattern. | |||
;YL is guarenteed to be < 0x80 because I flag is clear. When we jump to | |||
;waitForJ, ensure that this prerequisite is met. | |||
waitForJ: | |||
inc YL | |||
sbis USBIN, USBMINUS | |||
brne waitForJ ; just make sure we have ANY timeout | |||
;------------------------------------------------------------------------------- | |||
; The following code results in a sampling window of < 1/4 bit | |||
; which meets the spec. | |||
;------------------------------------------------------------------------------- | |||
waitForK: ;- | |||
sbis USBIN, USBMINUS ;1 [00] <-- sample | |||
rjmp foundK ;2 [01] | |||
sbis USBIN, USBMINUS ; <-- sample | |||
rjmp foundK | |||
sbis USBIN, USBMINUS ; <-- sample | |||
rjmp foundK | |||
sbis USBIN, USBMINUS ; <-- sample | |||
rjmp foundK | |||
sbis USBIN, USBMINUS ; <-- sample | |||
rjmp foundK | |||
sbis USBIN, USBMINUS ; <-- sample | |||
rjmp foundK | |||
#if USB_COUNT_SOF | |||
lds YL, usbSofCount | |||
inc YL | |||
sts usbSofCount, YL | |||
#endif /* USB_COUNT_SOF */ | |||
#ifdef USB_SOF_HOOK | |||
USB_SOF_HOOK | |||
#endif | |||
rjmp sofError | |||
;------------------------------------------------------------------------------ | |||
; {3, 5} after falling D- edge, average delay: 4 cycles [we want 5 for | |||
; center sampling] | |||
; we have 1 bit time for setup purposes, then sample again. | |||
; Numbers in brackets are cycles from center of first sync (double K) | |||
; bit after the instruction | |||
;------------------------------------------------------------------------------ | |||
foundK: ;- [02] | |||
lds YL, usbInputBufOffset;2 [03+04] tx loop | |||
push YH ;2 [05+06] | |||
clr YH ;1 [07] | |||
subi YL, lo8(-(usbRxBuf)) ;1 [08] [rx loop init] | |||
sbci YH, hi8(-(usbRxBuf)) ;1 [09] [rx loop init] | |||
push shift ;2 [10+11] | |||
ser shift ;1 [12] | |||
sbis USBIN, USBMINUS ;1 [-1] [13] <--sample:we want two bits K (sample 1 cycle too early) | |||
rjmp haveTwoBitsK ;2 [00] [14] | |||
pop shift ;2 [15+16] undo the push from before | |||
pop YH ;2 [17+18] undo the push from before | |||
rjmp waitForK ;2 [19+20] this was not the end of sync, retry | |||
; The entire loop from waitForK until rjmp waitForK above must not exceed two | |||
; bit times (= 20 cycles). | |||
;---------------------------------------------------------------------------- | |||
; push more registers and initialize values while we sample the first bits: | |||
;---------------------------------------------------------------------------- | |||
haveTwoBitsK: ;- [01] | |||
push x1 ;2 [02+03] | |||
push x2 ;2 [04+05] | |||
push x3 ;2 [06+07] | |||
push bitcnt ;2 [08+09] | |||
in x1, USBIN ;1 [00] [10] <-- sample bit 0 | |||
bst x1, USBMINUS ;1 [01] | |||
bld shift, 0 ;1 [02] | |||
push cnt ;2 [03+04] | |||
ldi cnt, USB_BUFSIZE ;1 [05] | |||
push x4 ;2 [06+07] tx loop | |||
rjmp rxLoop ;2 [08] | |||
;---------------------------------------------------------------------------- | |||
; Receiver loop (numbers in brackets are cycles within byte after instr) | |||
;---------------------------------------------------------------------------- | |||
unstuff0: ;- [07] (branch taken) | |||
andi x3, ~0x01 ;1 [08] | |||
mov x1, x2 ;1 [09] x2 contains last sampled (stuffed) bit | |||
in x2, USBIN ;1 [00] [10] <-- sample bit 1 again | |||
andi x2, USBMASK ;1 [01] | |||
breq se0Hop ;1 [02] SE0 check for bit 1 | |||
ori shift, 0x01 ;1 [03] 0b00000001 | |||
nop ;1 [04] | |||
rjmp didUnstuff0 ;2 [05] | |||
;----------------------------------------------------- | |||
unstuff1: ;- [05] (branch taken) | |||
mov x2, x1 ;1 [06] x1 contains last sampled (stuffed) bit | |||
andi x3, ~0x02 ;1 [07] | |||
ori shift, 0x02 ;1 [08] 0b00000010 | |||
nop ;1 [09] | |||
in x1, USBIN ;1 [00] [10] <-- sample bit 2 again | |||
andi x1, USBMASK ;1 [01] | |||
breq se0Hop ;1 [02] SE0 check for bit 2 | |||
rjmp didUnstuff1 ;2 [03] | |||
;----------------------------------------------------- | |||
unstuff2: ;- [05] (branch taken) | |||
andi x3, ~0x04 ;1 [06] | |||
ori shift, 0x04 ;1 [07] 0b00000100 | |||
mov x1, x2 ;1 [08] x2 contains last sampled (stuffed) bit | |||
nop ;1 [09] | |||
in x2, USBIN ;1 [00] [10] <-- sample bit 3 | |||
andi x2, USBMASK ;1 [01] | |||
breq se0Hop ;1 [02] SE0 check for bit 3 | |||
rjmp didUnstuff2 ;2 [03] | |||
;----------------------------------------------------- | |||
unstuff3: ;- [00] [10] (branch taken) | |||
in x2, USBIN ;1 [01] [11] <-- sample stuffed bit 3 one cycle too late | |||
andi x2, USBMASK ;1 [02] | |||
breq se0Hop ;1 [03] SE0 check for stuffed bit 3 | |||
andi x3, ~0x08 ;1 [04] | |||
ori shift, 0x08 ;1 [05] 0b00001000 | |||
rjmp didUnstuff3 ;2 [06] | |||
;---------------------------------------------------------------------------- | |||
; extra jobs done during bit interval: | |||
; | |||
; bit 0: store, clear [SE0 is unreliable here due to bit dribbling in hubs], | |||
; overflow check, jump to the head of rxLoop | |||
; bit 1: SE0 check | |||
; bit 2: SE0 check, recovery from delay [bit 0 tasks took too long] | |||
; bit 3: SE0 check, recovery from delay [bit 0 tasks took too long] | |||
; bit 4: SE0 check, none | |||
; bit 5: SE0 check, none | |||
; bit 6: SE0 check, none | |||
; bit 7: SE0 check, reconstruct: x3 is 0 at bit locations we changed, 1 at others | |||
;---------------------------------------------------------------------------- | |||
rxLoop: ;- [09] | |||
in x2, USBIN ;1 [00] [10] <-- sample bit 1 (or possibly bit 0 stuffed) | |||
andi x2, USBMASK ;1 [01] | |||
brne SkipSe0Hop ;1 [02] | |||
se0Hop: ;- [02] | |||
rjmp se0 ;2 [03] SE0 check for bit 1 | |||
SkipSe0Hop: ;- [03] | |||
ser x3 ;1 [04] | |||
andi shift, 0xf9 ;1 [05] 0b11111001 | |||
breq unstuff0 ;1 [06] | |||
didUnstuff0: ;- [06] | |||
eor x1, x2 ;1 [07] | |||
bst x1, USBMINUS ;1 [08] | |||
bld shift, 1 ;1 [09] | |||
in x1, USBIN ;1 [00] [10] <-- sample bit 2 (or possibly bit 1 stuffed) | |||
andi x1, USBMASK ;1 [01] | |||
breq se0Hop ;1 [02] SE0 check for bit 2 | |||
andi shift, 0xf3 ;1 [03] 0b11110011 | |||
breq unstuff1 ;1 [04] do remaining work for bit 1 | |||
didUnstuff1: ;- [04] | |||
eor x2, x1 ;1 [05] | |||
bst x2, USBMINUS ;1 [06] | |||
bld shift, 2 ;1 [07] | |||
nop2 ;2 [08+09] | |||
in x2, USBIN ;1 [00] [10] <-- sample bit 3 (or possibly bit 2 stuffed) | |||
andi x2, USBMASK ;1 [01] | |||
breq se0Hop ;1 [02] SE0 check for bit 3 | |||
andi shift, 0xe7 ;1 [03] 0b11100111 | |||
breq unstuff2 ;1 [04] | |||
didUnstuff2: ;- [04] | |||
eor x1, x2 ;1 [05] | |||
bst x1, USBMINUS ;1 [06] | |||
bld shift, 3 ;1 [07] | |||
didUnstuff3: ;- [07] | |||
andi shift, 0xcf ;1 [08] 0b11001111 | |||
breq unstuff3 ;1 [09] | |||
in x1, USBIN ;1 [00] [10] <-- sample bit 4 | |||
andi x1, USBMASK ;1 [01] | |||
breq se0Hop ;1 [02] SE0 check for bit 4 | |||
eor x2, x1 ;1 [03] | |||
bst x2, USBMINUS ;1 [04] | |||
bld shift, 4 ;1 [05] | |||
didUnstuff4: ;- [05] | |||
andi shift, 0x9f ;1 [06] 0b10011111 | |||
breq unstuff4 ;1 [07] | |||
nop2 ;2 [08+09] | |||
in x2, USBIN ;1 [00] [10] <-- sample bit 5 | |||
andi x2, USBMASK ;1 [01] | |||
breq se0 ;1 [02] SE0 check for bit 5 | |||
eor x1, x2 ;1 [03] | |||
bst x1, USBMINUS ;1 [04] | |||
bld shift, 5 ;1 [05] | |||
didUnstuff5: ;- [05] | |||
andi shift, 0x3f ;1 [06] 0b00111111 | |||
breq unstuff5 ;1 [07] | |||
nop2 ;2 [08+09] | |||
in x1, USBIN ;1 [00] [10] <-- sample bit 6 | |||
andi x1, USBMASK ;1 [01] | |||
breq se0 ;1 [02] SE0 check for bit 6 | |||
eor x2, x1 ;1 [03] | |||
bst x2, USBMINUS ;1 [04] | |||
bld shift, 6 ;1 [05] | |||
didUnstuff6: ;- [05] | |||
cpi shift, 0x02 ;1 [06] 0b00000010 | |||
brlo unstuff6 ;1 [07] | |||
nop2 ;2 [08+09] | |||
in x2, USBIN ;1 [00] [10] <-- sample bit 7 | |||
andi x2, USBMASK ;1 [01] | |||
breq se0 ;1 [02] SE0 check for bit 7 | |||
eor x1, x2 ;1 [03] | |||
bst x1, USBMINUS ;1 [04] | |||
bld shift, 7 ;1 [05] | |||
didUnstuff7: ;- [05] | |||
cpi shift, 0x04 ;1 [06] 0b00000100 | |||
brlo unstuff7 ;1 [07] | |||
eor x3, shift ;1 [08] reconstruct: x3 is 0 at bit locations we changed, 1 at others | |||
nop ;1 [09] | |||
in x1, USBIN ;1 [00] [10] <-- sample bit 0 | |||
st y+, x3 ;2 [01+02] store data | |||
eor x2, x1 ;1 [03] | |||
bst x2, USBMINUS ;1 [04] | |||
bld shift, 0 ;1 [05] | |||
subi cnt, 1 ;1 [06] | |||
brcs overflow ;1 [07] | |||
rjmp rxLoop ;2 [08] | |||
;----------------------------------------------------- | |||
unstuff4: ;- [08] | |||
andi x3, ~0x10 ;1 [09] | |||
in x1, USBIN ;1 [00] [10] <-- sample stuffed bit 4 | |||
andi x1, USBMASK ;1 [01] | |||
breq se0 ;1 [02] SE0 check for stuffed bit 4 | |||
ori shift, 0x10 ;1 [03] | |||
rjmp didUnstuff4 ;2 [04] | |||
;----------------------------------------------------- | |||
unstuff5: ;- [08] | |||
ori shift, 0x20 ;1 [09] | |||
in x2, USBIN ;1 [00] [10] <-- sample stuffed bit 5 | |||
andi x2, USBMASK ;1 [01] | |||
breq se0 ;1 [02] SE0 check for stuffed bit 5 | |||
andi x3, ~0x20 ;1 [03] | |||
rjmp didUnstuff5 ;2 [04] | |||
;----------------------------------------------------- | |||
unstuff6: ;- [08] | |||
andi x3, ~0x40 ;1 [09] | |||
in x1, USBIN ;1 [00] [10] <-- sample stuffed bit 6 | |||
andi x1, USBMASK ;1 [01] | |||
breq se0 ;1 [02] SE0 check for stuffed bit 6 | |||
ori shift, 0x40 ;1 [03] | |||
rjmp didUnstuff6 ;2 [04] | |||
;----------------------------------------------------- | |||
unstuff7: ;- [08] | |||
andi x3, ~0x80 ;1 [09] | |||
in x2, USBIN ;1 [00] [10] <-- sample stuffed bit 7 | |||
andi x2, USBMASK ;1 [01] | |||
breq se0 ;1 [02] SE0 check for stuffed bit 7 | |||
ori shift, 0x80 ;1 [03] | |||
rjmp didUnstuff7 ;2 [04] | |||
macro POP_STANDARD ; 16 cycles | |||
pop x4 | |||
pop cnt | |||
pop bitcnt | |||
pop x3 | |||
pop x2 | |||
pop x1 | |||
pop shift | |||
pop YH | |||
endm | |||
macro POP_RETI ; 5 cycles | |||
pop YL | |||
out SREG, YL | |||
pop YL | |||
endm | |||
#include "asmcommon.inc" | |||
;--------------------------------------------------------------------------- | |||
; USB spec says: | |||
; idle = J | |||
; J = (D+ = 0), (D- = 1) | |||
; K = (D+ = 1), (D- = 0) | |||
; Spec allows 7.5 bit times from EOP to SOP for replies | |||
;--------------------------------------------------------------------------- | |||
bitstuffN: ;- [04] | |||
eor x1, x4 ;1 [05] | |||
clr x2 ;1 [06] | |||
nop ;1 [07] | |||
rjmp didStuffN ;1 [08] | |||
;--------------------------------------------------------------------------- | |||
bitstuff6: ;- [04] | |||
eor x1, x4 ;1 [05] | |||
clr x2 ;1 [06] | |||
rjmp didStuff6 ;1 [07] | |||
;--------------------------------------------------------------------------- | |||
bitstuff7: ;- [02] | |||
eor x1, x4 ;1 [03] | |||
clr x2 ;1 [06] | |||
nop ;1 [05] | |||
rjmp didStuff7 ;1 [06] | |||
;--------------------------------------------------------------------------- | |||
sendNakAndReti: ;- [-19] | |||
ldi x3, USBPID_NAK ;1 [-18] | |||
rjmp sendX3AndReti ;1 [-17] | |||
;--------------------------------------------------------------------------- | |||
sendAckAndReti: ;- [-17] | |||
ldi cnt, USBPID_ACK ;1 [-16] | |||
sendCntAndReti: ;- [-16] | |||
mov x3, cnt ;1 [-15] | |||
sendX3AndReti: ;- [-15] | |||
ldi YL, 20 ;1 [-14] x3==r20 address is 20 | |||
ldi YH, 0 ;1 [-13] | |||
ldi cnt, 2 ;1 [-12] | |||
; rjmp usbSendAndReti fallthrough | |||
;--------------------------------------------------------------------------- | |||
;usbSend: | |||
;pointer to data in 'Y' | |||
;number of bytes in 'cnt' -- including sync byte [range 2 ... 12] | |||
;uses: x1...x4, btcnt, shift, cnt, Y | |||
;Numbers in brackets are time since first bit of sync pattern is sent | |||
;We need not to match the transfer rate exactly because the spec demands | |||
;only 1.5% precision anyway. | |||
usbSendAndReti: ;- [-13] 13 cycles until SOP | |||
in x2, USBDDR ;1 [-12] | |||
ori x2, USBMASK ;1 [-11] | |||
sbi USBOUT, USBMINUS ;2 [-09-10] prepare idle state; D+ and D- must have been 0 (no pullups) | |||
in x1, USBOUT ;1 [-08] port mirror for tx loop | |||
out USBDDR, x2 ;1 [-07] <- acquire bus | |||
; need not init x2 (bitstuff history) because sync starts with 0 | |||
ldi x4, USBMASK ;1 [-06] exor mask | |||
ldi shift, 0x80 ;1 [-05] sync byte is first byte sent | |||
ldi bitcnt, 6 ;1 [-04] | |||
txBitLoop: ;- [-04] [06] | |||
sbrs shift, 0 ;1 [-03] [07] | |||
eor x1, x4 ;1 [-02] [08] | |||
ror shift ;1 [-01] [09] | |||
didStuffN: ;- [09] | |||
out USBOUT, x1 ;1 [00] [10] <-- out N | |||
ror x2 ;1 [01] | |||
cpi x2, 0xfc ;1 [02] | |||
brcc bitstuffN ;1 [03] | |||
dec bitcnt ;1 [04] | |||
brne txBitLoop ;1 [05] | |||
sbrs shift, 0 ;1 [06] | |||
eor x1, x4 ;1 [07] | |||
ror shift ;1 [08] | |||
didStuff6: ;- [08] | |||
nop ;1 [09] | |||
out USBOUT, x1 ;1 [00] [10] <-- out 6 | |||
ror x2 ;1 [01] | |||
cpi x2, 0xfc ;1 [02] | |||
brcc bitstuff6 ;1 [03] | |||
sbrs shift, 0 ;1 [04] | |||
eor x1, x4 ;1 [05] | |||
ror shift ;1 [06] | |||
ror x2 ;1 [07] | |||
didStuff7: ;- [07] | |||
ldi bitcnt, 6 ;1 [08] | |||
cpi x2, 0xfc ;1 [09] | |||
out USBOUT, x1 ;1 [00] [10] <-- out 7 | |||
brcc bitstuff7 ;1 [01] | |||
ld shift, y+ ;2 [02+03] | |||
dec cnt ;1 [04] | |||
brne txBitLoop ;1 [05] | |||
makeSE0: | |||
cbr x1, USBMASK ;1 [06] prepare SE0 [spec says EOP may be 19 to 23 cycles] | |||
lds x2, usbNewDeviceAddr;2 [07+08] | |||
lsl x2 ;1 [09] we compare with left shifted address | |||
;2006-03-06: moved transfer of new address to usbDeviceAddr from C-Code to asm: | |||
;set address only after data packet was sent, not after handshake | |||
out USBOUT, x1 ;1 [00] [10] <-- out SE0-- from now 2 bits==20 cycl. until bus idle | |||
subi YL, 20 + 2 ;1 [01] Only assign address on data packets, not ACK/NAK in x3 | |||
sbci YH, 0 ;1 [02] | |||
breq skipAddrAssign ;1 [03] | |||
sts usbDeviceAddr, x2 ;2 [04+05] if not skipped: SE0 is one cycle longer | |||
;---------------------------------------------------------------------------- | |||
;end of usbDeviceAddress transfer | |||
skipAddrAssign: ;- [03/04] | |||
ldi x2, 1<<USB_INTR_PENDING_BIT ;1 [05] int0 occurred during TX -- clear pending flag | |||
USB_STORE_PENDING(x2) ;1 [06] | |||
ori x1, USBIDLE ;1 [07] | |||
in x2, USBDDR ;1 [08] | |||
cbr x2, USBMASK ;1 [09] set both pins to input | |||
mov x3, x1 ;1 [10] | |||
cbr x3, USBMASK ;1 [11] configure no pullup on both pins | |||
ldi x4, 3 ;1 [12] | |||
se0Delay: ;- [12] [15] | |||
dec x4 ;1 [13] [16] | |||
brne se0Delay ;1 [14] [17] | |||
nop2 ;2 [18+19] | |||
out USBOUT, x1 ;1 [20] <--out J (idle) -- end of SE0 (EOP sig.) | |||
out USBDDR, x2 ;1 [21] <--release bus now | |||
out USBOUT, x3 ;1 [22] <--ensure no pull-up resistors are active | |||
rjmp doReturn ;1 [23] | |||
;--------------------------------------------------------------------------- |
@@ -0,0 +1,346 @@ | |||
/* Name: usbdrvasm16.inc | |||
* Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers | |||
* Author: Christian Starkjohann | |||
* Creation Date: 2007-06-15 | |||
* Tabsize: 4 | |||
* Copyright: (c) 2007 by OBJECTIVE DEVELOPMENT Software GmbH | |||
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) | |||
* Revision: $Id: usbdrvasm16.inc 760 2009-08-09 18:59:43Z cs $ | |||
*/ | |||
/* Do not link this file! Link usbdrvasm.S instead, which includes the | |||
* appropriate implementation! | |||
*/ | |||
/* | |||
General Description: | |||
This file is the 16 MHz version of the asssembler part of the USB driver. It | |||
requires a 16 MHz crystal (not a ceramic resonator and not a calibrated RC | |||
oscillator). | |||
See usbdrv.h for a description of the entire driver. | |||
Since almost all of this code is timing critical, don't change unless you | |||
really know what you are doing! Many parts require not only a maximum number | |||
of CPU cycles, but even an exact number of cycles! | |||
*/ | |||
;max stack usage: [ret(2), YL, SREG, YH, bitcnt, shift, x1, x2, x3, x4, cnt] = 12 bytes | |||
;nominal frequency: 16 MHz -> 10.6666666 cycles per bit, 85.333333333 cycles per byte | |||
; Numbers in brackets are clocks counted from center of last sync bit | |||
; when instruction starts | |||
USB_INTR_VECTOR: | |||
;order of registers pushed: YL, SREG YH, [sofError], bitcnt, shift, x1, x2, x3, x4, cnt | |||
push YL ;[-25] push only what is necessary to sync with edge ASAP | |||
in YL, SREG ;[-23] | |||
push YL ;[-22] | |||
push YH ;[-20] | |||
;---------------------------------------------------------------------------- | |||
; Synchronize with sync pattern: | |||
;---------------------------------------------------------------------------- | |||
;sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] | |||
;sync up with J to K edge during sync pattern -- use fastest possible loops | |||
;The first part waits at most 1 bit long since we must be in sync pattern. | |||
;YL is guarenteed to be < 0x80 because I flag is clear. When we jump to | |||
;waitForJ, ensure that this prerequisite is met. | |||
waitForJ: | |||
inc YL | |||
sbis USBIN, USBMINUS | |||
brne waitForJ ; just make sure we have ANY timeout | |||
waitForK: | |||
;The following code results in a sampling window of < 1/4 bit which meets the spec. | |||
sbis USBIN, USBMINUS ;[-15] | |||
rjmp foundK ;[-14] | |||
sbis USBIN, USBMINUS | |||
rjmp foundK | |||
sbis USBIN, USBMINUS | |||
rjmp foundK | |||
sbis USBIN, USBMINUS | |||
rjmp foundK | |||
sbis USBIN, USBMINUS | |||
rjmp foundK | |||
sbis USBIN, USBMINUS | |||
rjmp foundK | |||
#if USB_COUNT_SOF | |||
lds YL, usbSofCount | |||
inc YL | |||
sts usbSofCount, YL | |||
#endif /* USB_COUNT_SOF */ | |||
#ifdef USB_SOF_HOOK | |||
USB_SOF_HOOK | |||
#endif | |||
rjmp sofError | |||
foundK: ;[-12] | |||
;{3, 5} after falling D- edge, average delay: 4 cycles [we want 5 for center sampling] | |||
;we have 1 bit time for setup purposes, then sample again. Numbers in brackets | |||
;are cycles from center of first sync (double K) bit after the instruction | |||
push bitcnt ;[-12] | |||
; [---] ;[-11] | |||
lds YL, usbInputBufOffset;[-10] | |||
; [---] ;[-9] | |||
clr YH ;[-8] | |||
subi YL, lo8(-(usbRxBuf));[-7] [rx loop init] | |||
sbci YH, hi8(-(usbRxBuf));[-6] [rx loop init] | |||
push shift ;[-5] | |||
; [---] ;[-4] | |||
ldi bitcnt, 0x55 ;[-3] [rx loop init] | |||
sbis USBIN, USBMINUS ;[-2] we want two bits K (sample 2 cycles too early) | |||
rjmp haveTwoBitsK ;[-1] | |||
pop shift ;[0] undo the push from before | |||
pop bitcnt ;[2] undo the push from before | |||
rjmp waitForK ;[4] this was not the end of sync, retry | |||
; The entire loop from waitForK until rjmp waitForK above must not exceed two | |||
; bit times (= 21 cycles). | |||
;---------------------------------------------------------------------------- | |||
; push more registers and initialize values while we sample the first bits: | |||
;---------------------------------------------------------------------------- | |||
haveTwoBitsK: | |||
push x1 ;[1] | |||
push x2 ;[3] | |||
push x3 ;[5] | |||
ldi shift, 0 ;[7] | |||
ldi x3, 1<<4 ;[8] [rx loop init] first sample is inverse bit, compensate that | |||
push x4 ;[9] == leap | |||
in x1, USBIN ;[11] <-- sample bit 0 | |||
andi x1, USBMASK ;[12] | |||
bst x1, USBMINUS ;[13] | |||
bld shift, 7 ;[14] | |||
push cnt ;[15] | |||
ldi leap, 0 ;[17] [rx loop init] | |||
ldi cnt, USB_BUFSIZE;[18] [rx loop init] | |||
rjmp rxbit1 ;[19] arrives at [21] | |||
;---------------------------------------------------------------------------- | |||
; Receiver loop (numbers in brackets are cycles within byte after instr) | |||
;---------------------------------------------------------------------------- | |||
; duration of unstuffing code should be 10.66666667 cycles. We adjust "leap" | |||
; accordingly to approximate this value in the long run. | |||
unstuff6: | |||
andi x2, USBMASK ;[03] | |||
ori x3, 1<<6 ;[04] will not be shifted any more | |||
andi shift, ~0x80;[05] | |||
mov x1, x2 ;[06] sampled bit 7 is actually re-sampled bit 6 | |||
subi leap, -1 ;[07] total duration = 11 bits -> subtract 1/3 | |||
rjmp didUnstuff6 ;[08] | |||
unstuff7: | |||
ori x3, 1<<7 ;[09] will not be shifted any more | |||
in x2, USBIN ;[00] [10] re-sample bit 7 | |||
andi x2, USBMASK ;[01] | |||
andi shift, ~0x80;[02] | |||
subi leap, 2 ;[03] total duration = 10 bits -> add 1/3 | |||
rjmp didUnstuff7 ;[04] | |||
unstuffEven: | |||
ori x3, 1<<6 ;[09] will be shifted right 6 times for bit 0 | |||
in x1, USBIN ;[00] [10] | |||
andi shift, ~0x80;[01] | |||
andi x1, USBMASK ;[02] | |||
breq se0 ;[03] | |||
subi leap, -1 ;[04] total duration = 11 bits -> subtract 1/3 | |||
nop2 ;[05] | |||
rjmp didUnstuffE ;[06] | |||
unstuffOdd: | |||
ori x3, 1<<5 ;[09] will be shifted right 4 times for bit 1 | |||
in x2, USBIN ;[00] [10] | |||
andi shift, ~0x80;[01] | |||
andi x2, USBMASK ;[02] | |||
breq se0 ;[03] | |||
subi leap, -1 ;[04] total duration = 11 bits -> subtract 1/3 | |||
nop2 ;[05] | |||
rjmp didUnstuffO ;[06] | |||
rxByteLoop: | |||
andi x1, USBMASK ;[03] | |||
eor x2, x1 ;[04] | |||
subi leap, 1 ;[05] | |||
brpl skipLeap ;[06] | |||
subi leap, -3 ;1 one leap cycle every 3rd byte -> 85 + 1/3 cycles per byte | |||
nop ;1 | |||
skipLeap: | |||
subi x2, 1 ;[08] | |||
ror shift ;[09] | |||
didUnstuff6: | |||
cpi shift, 0xfc ;[10] | |||
in x2, USBIN ;[00] [11] <-- sample bit 7 | |||
brcc unstuff6 ;[01] | |||
andi x2, USBMASK ;[02] | |||
eor x1, x2 ;[03] | |||
subi x1, 1 ;[04] | |||
ror shift ;[05] | |||
didUnstuff7: | |||
cpi shift, 0xfc ;[06] | |||
brcc unstuff7 ;[07] | |||
eor x3, shift ;[08] reconstruct: x3 is 1 at bit locations we changed, 0 at others | |||
st y+, x3 ;[09] store data | |||
rxBitLoop: | |||
in x1, USBIN ;[00] [11] <-- sample bit 0/2/4 | |||
andi x1, USBMASK ;[01] | |||
eor x2, x1 ;[02] | |||
andi x3, 0x3f ;[03] topmost two bits reserved for 6 and 7 | |||
subi x2, 1 ;[04] | |||
ror shift ;[05] | |||
cpi shift, 0xfc ;[06] | |||
brcc unstuffEven ;[07] | |||
didUnstuffE: | |||
lsr x3 ;[08] | |||
lsr x3 ;[09] | |||
rxbit1: | |||
in x2, USBIN ;[00] [10] <-- sample bit 1/3/5 | |||
andi x2, USBMASK ;[01] | |||
breq se0 ;[02] | |||
eor x1, x2 ;[03] | |||
subi x1, 1 ;[04] | |||
ror shift ;[05] | |||
cpi shift, 0xfc ;[06] | |||
brcc unstuffOdd ;[07] | |||
didUnstuffO: | |||
subi bitcnt, 0xab;[08] == addi 0x55, 0x55 = 0x100/3 | |||
brcs rxBitLoop ;[09] | |||
subi cnt, 1 ;[10] | |||
in x1, USBIN ;[00] [11] <-- sample bit 6 | |||
brcc rxByteLoop ;[01] | |||
rjmp overflow | |||
macro POP_STANDARD ; 14 cycles | |||
pop cnt | |||
pop x4 | |||
pop x3 | |||
pop x2 | |||
pop x1 | |||
pop shift | |||
pop bitcnt | |||
endm | |||
macro POP_RETI ; 7 cycles | |||
pop YH | |||
pop YL | |||
out SREG, YL | |||
pop YL | |||
endm | |||
#include "asmcommon.inc" | |||
; USB spec says: | |||
; idle = J | |||
; J = (D+ = 0), (D- = 1) | |||
; K = (D+ = 1), (D- = 0) | |||
; Spec allows 7.5 bit times from EOP to SOP for replies | |||
bitstuffN: | |||
eor x1, x4 ;[5] | |||
ldi x2, 0 ;[6] | |||
nop2 ;[7] | |||
nop ;[9] | |||
out USBOUT, x1 ;[10] <-- out | |||
rjmp didStuffN ;[0] | |||
bitstuff6: | |||
eor x1, x4 ;[5] | |||
ldi x2, 0 ;[6] Carry is zero due to brcc | |||
rol shift ;[7] compensate for ror shift at branch destination | |||
rjmp didStuff6 ;[8] | |||
bitstuff7: | |||
ldi x2, 0 ;[2] Carry is zero due to brcc | |||
rjmp didStuff7 ;[3] | |||
sendNakAndReti: | |||
ldi x3, USBPID_NAK ;[-18] | |||
rjmp sendX3AndReti ;[-17] | |||
sendAckAndReti: | |||
ldi cnt, USBPID_ACK ;[-17] | |||
sendCntAndReti: | |||
mov x3, cnt ;[-16] | |||
sendX3AndReti: | |||
ldi YL, 20 ;[-15] x3==r20 address is 20 | |||
ldi YH, 0 ;[-14] | |||
ldi cnt, 2 ;[-13] | |||
; rjmp usbSendAndReti fallthrough | |||
;usbSend: | |||
;pointer to data in 'Y' | |||
;number of bytes in 'cnt' -- including sync byte [range 2 ... 12] | |||
;uses: x1...x4, btcnt, shift, cnt, Y | |||
;Numbers in brackets are time since first bit of sync pattern is sent | |||
;We don't match the transfer rate exactly (don't insert leap cycles every third | |||
;byte) because the spec demands only 1.5% precision anyway. | |||
usbSendAndReti: ; 12 cycles until SOP | |||
in x2, USBDDR ;[-12] | |||
ori x2, USBMASK ;[-11] | |||
sbi USBOUT, USBMINUS;[-10] prepare idle state; D+ and D- must have been 0 (no pullups) | |||
in x1, USBOUT ;[-8] port mirror for tx loop | |||
out USBDDR, x2 ;[-7] <- acquire bus | |||
; need not init x2 (bitstuff history) because sync starts with 0 | |||
ldi x4, USBMASK ;[-6] exor mask | |||
ldi shift, 0x80 ;[-5] sync byte is first byte sent | |||
txByteLoop: | |||
ldi bitcnt, 0x35 ;[-4] [6] binary 0011 0101 | |||
txBitLoop: | |||
sbrs shift, 0 ;[-3] [7] | |||
eor x1, x4 ;[-2] [8] | |||
out USBOUT, x1 ;[-1] [9] <-- out N | |||
ror shift ;[0] [10] | |||
ror x2 ;[1] | |||
didStuffN: | |||
cpi x2, 0xfc ;[2] | |||
brcc bitstuffN ;[3] | |||
lsr bitcnt ;[4] | |||
brcc txBitLoop ;[5] | |||
brne txBitLoop ;[6] | |||
sbrs shift, 0 ;[7] | |||
eor x1, x4 ;[8] | |||
didStuff6: | |||
out USBOUT, x1 ;[-1] [9] <-- out 6 | |||
ror shift ;[0] [10] | |||
ror x2 ;[1] | |||
cpi x2, 0xfc ;[2] | |||
brcc bitstuff6 ;[3] | |||
ror shift ;[4] | |||
didStuff7: | |||
ror x2 ;[5] | |||
sbrs x2, 7 ;[6] | |||
eor x1, x4 ;[7] | |||
nop ;[8] | |||
cpi x2, 0xfc ;[9] | |||
out USBOUT, x1 ;[-1][10] <-- out 7 | |||
brcc bitstuff7 ;[0] [11] | |||
ld shift, y+ ;[1] | |||
dec cnt ;[3] | |||
brne txByteLoop ;[4] | |||
;make SE0: | |||
cbr x1, USBMASK ;[5] prepare SE0 [spec says EOP may be 21 to 25 cycles] | |||
lds x2, usbNewDeviceAddr;[6] | |||
lsl x2 ;[8] we compare with left shifted address | |||
subi YL, 20 + 2 ;[9] Only assign address on data packets, not ACK/NAK in x3 | |||
sbci YH, 0 ;[10] | |||
out USBOUT, x1 ;[11] <-- out SE0 -- from now 2 bits = 22 cycles until bus idle | |||
;2006-03-06: moved transfer of new address to usbDeviceAddr from C-Code to asm: | |||
;set address only after data packet was sent, not after handshake | |||
breq skipAddrAssign ;[0] | |||
sts usbDeviceAddr, x2; if not skipped: SE0 is one cycle longer | |||
skipAddrAssign: | |||
;end of usbDeviceAddress transfer | |||
ldi x2, 1<<USB_INTR_PENDING_BIT;[2] int0 occurred during TX -- clear pending flag | |||
USB_STORE_PENDING(x2) ;[3] | |||
ori x1, USBIDLE ;[4] | |||
in x2, USBDDR ;[5] | |||
cbr x2, USBMASK ;[6] set both pins to input | |||
mov x3, x1 ;[7] | |||
cbr x3, USBMASK ;[8] configure no pullup on both pins | |||
ldi x4, 4 ;[9] | |||
se0Delay: | |||
dec x4 ;[10] [13] [16] [19] | |||
brne se0Delay ;[11] [14] [17] [20] | |||
out USBOUT, x1 ;[21] <-- out J (idle) -- end of SE0 (EOP signal) | |||
out USBDDR, x2 ;[22] <-- release bus now | |||
out USBOUT, x3 ;[23] <-- ensure no pull-up resistors are active | |||
rjmp doReturn |
@@ -0,0 +1,453 @@ | |||
/* Name: usbdrvasm165.inc | |||
* Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers | |||
* Author: Christian Starkjohann | |||
* Creation Date: 2007-04-22 | |||
* Tabsize: 4 | |||
* Copyright: (c) 2007 by OBJECTIVE DEVELOPMENT Software GmbH | |||
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) | |||
* Revision: $Id: usbdrvasm165.inc 740 2009-04-13 18:23:31Z cs $ | |||
*/ | |||
/* Do not link this file! Link usbdrvasm.S instead, which includes the | |||
* appropriate implementation! | |||
*/ | |||
/* | |||
General Description: | |||
This file is the 16.5 MHz version of the USB driver. It is intended for the | |||
ATTiny45 and similar controllers running on 16.5 MHz internal RC oscillator. | |||
This version contains a phase locked loop in the receiver routine to cope with | |||
slight clock rate deviations of up to +/- 1%. | |||
See usbdrv.h for a description of the entire driver. | |||
Since almost all of this code is timing critical, don't change unless you | |||
really know what you are doing! Many parts require not only a maximum number | |||
of CPU cycles, but even an exact number of cycles! | |||
*/ | |||
;Software-receiver engine. Strict timing! Don't change unless you can preserve timing! | |||
;interrupt response time: 4 cycles + insn running = 7 max if interrupts always enabled | |||
;max allowable interrupt latency: 59 cycles -> max 52 cycles interrupt disable | |||
;max stack usage: [ret(2), r0, SREG, YL, YH, shift, x1, x2, x3, x4, cnt] = 12 bytes | |||
;nominal frequency: 16.5 MHz -> 11 cycles per bit | |||
; 16.3125 MHz < F_CPU < 16.6875 MHz (+/- 1.1%) | |||
; Numbers in brackets are clocks counted from center of last sync bit | |||
; when instruction starts | |||
USB_INTR_VECTOR: | |||
;order of registers pushed: YL, SREG [sofError], r0, YH, shift, x1, x2, x3, x4, cnt | |||
push YL ;[-23] push only what is necessary to sync with edge ASAP | |||
in YL, SREG ;[-21] | |||
push YL ;[-20] | |||
;---------------------------------------------------------------------------- | |||
; Synchronize with sync pattern: | |||
;---------------------------------------------------------------------------- | |||
;sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] | |||
;sync up with J to K edge during sync pattern -- use fastest possible loops | |||
;The first part waits at most 1 bit long since we must be in sync pattern. | |||
;YL is guarenteed to be < 0x80 because I flag is clear. When we jump to | |||
;waitForJ, ensure that this prerequisite is met. | |||
waitForJ: | |||
inc YL | |||
sbis USBIN, USBMINUS | |||
brne waitForJ ; just make sure we have ANY timeout | |||
waitForK: | |||
;The following code results in a sampling window of < 1/4 bit which meets the spec. | |||
sbis USBIN, USBMINUS ;[-15] | |||
rjmp foundK ;[-14] | |||
sbis USBIN, USBMINUS | |||
rjmp foundK | |||
sbis USBIN, USBMINUS | |||
rjmp foundK | |||
sbis USBIN, USBMINUS | |||
rjmp foundK | |||
sbis USBIN, USBMINUS | |||
rjmp foundK | |||
sbis USBIN, USBMINUS | |||
rjmp foundK | |||
#if USB_COUNT_SOF | |||
lds YL, usbSofCount | |||
inc YL | |||
sts usbSofCount, YL | |||
#endif /* USB_COUNT_SOF */ | |||
#ifdef USB_SOF_HOOK | |||
USB_SOF_HOOK | |||
#endif | |||
rjmp sofError | |||
foundK: ;[-12] | |||
;{3, 5} after falling D- edge, average delay: 4 cycles [we want 5 for center sampling] | |||
;we have 1 bit time for setup purposes, then sample again. Numbers in brackets | |||
;are cycles from center of first sync (double K) bit after the instruction | |||
push r0 ;[-12] | |||
; [---] ;[-11] | |||
push YH ;[-10] | |||
; [---] ;[-9] | |||
lds YL, usbInputBufOffset;[-8] | |||
; [---] ;[-7] | |||
clr YH ;[-6] | |||
subi YL, lo8(-(usbRxBuf));[-5] [rx loop init] | |||
sbci YH, hi8(-(usbRxBuf));[-4] [rx loop init] | |||
mov r0, x2 ;[-3] [rx loop init] | |||
sbis USBIN, USBMINUS ;[-2] we want two bits K (sample 2 cycles too early) | |||
rjmp haveTwoBitsK ;[-1] | |||
pop YH ;[0] undo the pushes from before | |||
pop r0 ;[2] | |||
rjmp waitForK ;[4] this was not the end of sync, retry | |||
; The entire loop from waitForK until rjmp waitForK above must not exceed two | |||
; bit times (= 22 cycles). | |||
;---------------------------------------------------------------------------- | |||
; push more registers and initialize values while we sample the first bits: | |||
;---------------------------------------------------------------------------- | |||
haveTwoBitsK: ;[1] | |||
push shift ;[1] | |||
push x1 ;[3] | |||
push x2 ;[5] | |||
push x3 ;[7] | |||
ldi shift, 0xff ;[9] [rx loop init] | |||
ori x3, 0xff ;[10] [rx loop init] == ser x3, clear zero flag | |||
in x1, USBIN ;[11] <-- sample bit 0 | |||
bst x1, USBMINUS ;[12] | |||
bld shift, 0 ;[13] | |||
push x4 ;[14] == phase | |||
; [---] ;[15] | |||
push cnt ;[16] | |||
; [---] ;[17] | |||
ldi phase, 0 ;[18] [rx loop init] | |||
ldi cnt, USB_BUFSIZE;[19] [rx loop init] | |||
rjmp rxbit1 ;[20] | |||
; [---] ;[21] | |||
;---------------------------------------------------------------------------- | |||
; Receiver loop (numbers in brackets are cycles within byte after instr) | |||
;---------------------------------------------------------------------------- | |||
/* | |||
byte oriented operations done during loop: | |||
bit 0: store data | |||
bit 1: SE0 check | |||
bit 2: overflow check | |||
bit 3: catch up | |||
bit 4: rjmp to achieve conditional jump range | |||
bit 5: PLL | |||
bit 6: catch up | |||
bit 7: jump, fixup bitstuff | |||
; 87 [+ 2] cycles | |||
------------------------------------------------------------------ | |||
*/ | |||
continueWithBit5: | |||
in x2, USBIN ;[055] <-- bit 5 | |||
eor r0, x2 ;[056] | |||
or phase, r0 ;[057] | |||
sbrc phase, USBMINUS ;[058] | |||
lpm ;[059] optional nop3; modifies r0 | |||
in phase, USBIN ;[060] <-- phase | |||
eor x1, x2 ;[061] | |||
bst x1, USBMINUS ;[062] | |||
bld shift, 5 ;[063] | |||
andi shift, 0x3f ;[064] | |||
in x1, USBIN ;[065] <-- bit 6 | |||
breq unstuff5 ;[066] *** unstuff escape | |||
eor phase, x1 ;[067] | |||
eor x2, x1 ;[068] | |||
bst x2, USBMINUS ;[069] | |||
bld shift, 6 ;[070] | |||
didUnstuff6: ;[ ] | |||
in r0, USBIN ;[071] <-- phase | |||
cpi shift, 0x02 ;[072] | |||
brlo unstuff6 ;[073] *** unstuff escape | |||
didUnstuff5: ;[ ] | |||
nop2 ;[074] | |||
; [---] ;[075] | |||
in x2, USBIN ;[076] <-- bit 7 | |||
eor x1, x2 ;[077] | |||
bst x1, USBMINUS ;[078] | |||
bld shift, 7 ;[079] | |||
didUnstuff7: ;[ ] | |||
eor r0, x2 ;[080] | |||
or phase, r0 ;[081] | |||
in r0, USBIN ;[082] <-- phase | |||
cpi shift, 0x04 ;[083] | |||
brsh rxLoop ;[084] | |||
; [---] ;[085] | |||
unstuff7: ;[ ] | |||
andi x3, ~0x80 ;[085] | |||
ori shift, 0x80 ;[086] | |||
in x2, USBIN ;[087] <-- sample stuffed bit 7 | |||
nop ;[088] | |||
rjmp didUnstuff7 ;[089] | |||
; [---] ;[090] | |||
;[080] | |||
unstuff5: ;[067] | |||
eor phase, x1 ;[068] | |||
andi x3, ~0x20 ;[069] | |||
ori shift, 0x20 ;[070] | |||
in r0, USBIN ;[071] <-- phase | |||
mov x2, x1 ;[072] | |||
nop ;[073] | |||
nop2 ;[074] | |||
; [---] ;[075] | |||
in x1, USBIN ;[076] <-- bit 6 | |||
eor r0, x1 ;[077] | |||
or phase, r0 ;[078] | |||
eor x2, x1 ;[079] | |||
bst x2, USBMINUS ;[080] | |||
bld shift, 6 ;[081] no need to check bitstuffing, we just had one | |||
in r0, USBIN ;[082] <-- phase | |||
rjmp didUnstuff5 ;[083] | |||
; [---] ;[084] | |||
;[074] | |||
unstuff6: ;[074] | |||
andi x3, ~0x40 ;[075] | |||
in x1, USBIN ;[076] <-- bit 6 again | |||
ori shift, 0x40 ;[077] | |||
nop2 ;[078] | |||
; [---] ;[079] | |||
rjmp didUnstuff6 ;[080] | |||
; [---] ;[081] | |||
;[071] | |||
unstuff0: ;[013] | |||
eor r0, x2 ;[014] | |||
or phase, r0 ;[015] | |||
andi x2, USBMASK ;[016] check for SE0 | |||
in r0, USBIN ;[017] <-- phase | |||
breq didUnstuff0 ;[018] direct jump to se0 would be too long | |||
andi x3, ~0x01 ;[019] | |||
ori shift, 0x01 ;[020] | |||
mov x1, x2 ;[021] mov existing sample | |||
in x2, USBIN ;[022] <-- bit 1 again | |||
rjmp didUnstuff0 ;[023] | |||
; [---] ;[024] | |||
;[014] | |||
unstuff1: ;[024] | |||
eor r0, x1 ;[025] | |||
or phase, r0 ;[026] | |||
andi x3, ~0x02 ;[027] | |||
in r0, USBIN ;[028] <-- phase | |||
ori shift, 0x02 ;[029] | |||
mov x2, x1 ;[030] | |||
rjmp didUnstuff1 ;[031] | |||
; [---] ;[032] | |||
;[022] | |||
unstuff2: ;[035] | |||
eor r0, x2 ;[036] | |||
or phase, r0 ;[037] | |||
andi x3, ~0x04 ;[038] | |||
in r0, USBIN ;[039] <-- phase | |||
ori shift, 0x04 ;[040] | |||
mov x1, x2 ;[041] | |||
rjmp didUnstuff2 ;[042] | |||
; [---] ;[043] | |||
;[033] | |||
unstuff3: ;[043] | |||
in x2, USBIN ;[044] <-- bit 3 again | |||
eor r0, x2 ;[045] | |||
or phase, r0 ;[046] | |||
andi x3, ~0x08 ;[047] | |||
ori shift, 0x08 ;[048] | |||
nop ;[049] | |||
in r0, USBIN ;[050] <-- phase | |||
rjmp didUnstuff3 ;[051] | |||
; [---] ;[052] | |||
;[042] | |||
unstuff4: ;[053] | |||
andi x3, ~0x10 ;[054] | |||
in x1, USBIN ;[055] <-- bit 4 again | |||
ori shift, 0x10 ;[056] | |||
rjmp didUnstuff4 ;[057] | |||
; [---] ;[058] | |||
;[048] | |||
rxLoop: ;[085] | |||
eor x3, shift ;[086] reconstruct: x3 is 0 at bit locations we changed, 1 at others | |||
in x1, USBIN ;[000] <-- bit 0 | |||
st y+, x3 ;[001] | |||
; [---] ;[002] | |||
eor r0, x1 ;[003] | |||
or phase, r0 ;[004] | |||
eor x2, x1 ;[005] | |||
in r0, USBIN ;[006] <-- phase | |||
ser x3 ;[007] | |||
bst x2, USBMINUS ;[008] | |||
bld shift, 0 ;[009] | |||
andi shift, 0xf9 ;[010] | |||
rxbit1: ;[ ] | |||
in x2, USBIN ;[011] <-- bit 1 | |||
breq unstuff0 ;[012] *** unstuff escape | |||
andi x2, USBMASK ;[013] SE0 check for bit 1 | |||
didUnstuff0: ;[ ] Z only set if we detected SE0 in bitstuff | |||
breq se0 ;[014] | |||
eor r0, x2 ;[015] | |||
or phase, r0 ;[016] | |||
in r0, USBIN ;[017] <-- phase | |||
eor x1, x2 ;[018] | |||
bst x1, USBMINUS ;[019] | |||
bld shift, 1 ;[020] | |||
andi shift, 0xf3 ;[021] | |||
didUnstuff1: ;[ ] | |||
in x1, USBIN ;[022] <-- bit 2 | |||
breq unstuff1 ;[023] *** unstuff escape | |||
eor r0, x1 ;[024] | |||
or phase, r0 ;[025] | |||
subi cnt, 1 ;[026] overflow check | |||
brcs overflow ;[027] | |||
in r0, USBIN ;[028] <-- phase | |||
eor x2, x1 ;[029] | |||
bst x2, USBMINUS ;[030] | |||
bld shift, 2 ;[031] | |||
andi shift, 0xe7 ;[032] | |||
didUnstuff2: ;[ ] | |||
in x2, USBIN ;[033] <-- bit 3 | |||
breq unstuff2 ;[034] *** unstuff escape | |||
eor r0, x2 ;[035] | |||
or phase, r0 ;[036] | |||
eor x1, x2 ;[037] | |||
bst x1, USBMINUS ;[038] | |||
in r0, USBIN ;[039] <-- phase | |||
bld shift, 3 ;[040] | |||
andi shift, 0xcf ;[041] | |||
didUnstuff3: ;[ ] | |||
breq unstuff3 ;[042] *** unstuff escape | |||
nop ;[043] | |||
in x1, USBIN ;[044] <-- bit 4 | |||
eor x2, x1 ;[045] | |||
bst x2, USBMINUS ;[046] | |||
bld shift, 4 ;[047] | |||
didUnstuff4: ;[ ] | |||
eor r0, x1 ;[048] | |||
or phase, r0 ;[049] | |||
in r0, USBIN ;[050] <-- phase | |||
andi shift, 0x9f ;[051] | |||
breq unstuff4 ;[052] *** unstuff escape | |||
rjmp continueWithBit5;[053] | |||
; [---] ;[054] | |||
macro POP_STANDARD ; 16 cycles | |||
pop cnt | |||
pop x4 | |||
pop x3 | |||
pop x2 | |||
pop x1 | |||
pop shift | |||
pop YH | |||
pop r0 | |||
endm | |||
macro POP_RETI ; 5 cycles | |||
pop YL | |||
out SREG, YL | |||
pop YL | |||
endm | |||
#include "asmcommon.inc" | |||
; USB spec says: | |||
; idle = J | |||
; J = (D+ = 0), (D- = 1) | |||
; K = (D+ = 1), (D- = 0) | |||
; Spec allows 7.5 bit times from EOP to SOP for replies | |||
bitstuff7: | |||
eor x1, x4 ;[4] | |||
ldi x2, 0 ;[5] | |||
nop2 ;[6] C is zero (brcc) | |||
rjmp didStuff7 ;[8] | |||
bitstuffN: | |||
eor x1, x4 ;[5] | |||
ldi x2, 0 ;[6] | |||
lpm ;[7] 3 cycle NOP, modifies r0 | |||
out USBOUT, x1 ;[10] <-- out | |||
rjmp didStuffN ;[0] | |||
#define bitStatus x3 | |||
sendNakAndReti: | |||
ldi cnt, USBPID_NAK ;[-19] | |||
rjmp sendCntAndReti ;[-18] | |||
sendAckAndReti: | |||
ldi cnt, USBPID_ACK ;[-17] | |||
sendCntAndReti: | |||
mov r0, cnt ;[-16] | |||
ldi YL, 0 ;[-15] R0 address is 0 | |||
ldi YH, 0 ;[-14] | |||
ldi cnt, 2 ;[-13] | |||
; rjmp usbSendAndReti fallthrough | |||
;usbSend: | |||
;pointer to data in 'Y' | |||
;number of bytes in 'cnt' -- including sync byte [range 2 ... 12] | |||
;uses: x1...x4, shift, cnt, Y | |||
;Numbers in brackets are time since first bit of sync pattern is sent | |||
usbSendAndReti: ; 12 cycles until SOP | |||
in x2, USBDDR ;[-12] | |||
ori x2, USBMASK ;[-11] | |||
sbi USBOUT, USBMINUS;[-10] prepare idle state; D+ and D- must have been 0 (no pullups) | |||
in x1, USBOUT ;[-8] port mirror for tx loop | |||
out USBDDR, x2 ;[-7] <- acquire bus | |||
; need not init x2 (bitstuff history) because sync starts with 0 | |||
ldi x4, USBMASK ;[-6] exor mask | |||
ldi shift, 0x80 ;[-5] sync byte is first byte sent | |||
ldi bitStatus, 0xff ;[-4] init bit loop counter, works for up to 12 bytes | |||
byteloop: | |||
bitloop: | |||
sbrs shift, 0 ;[8] [-3] | |||
eor x1, x4 ;[9] [-2] | |||
out USBOUT, x1 ;[10] [-1] <-- out | |||
ror shift ;[0] | |||
ror x2 ;[1] | |||
didStuffN: | |||
cpi x2, 0xfc ;[2] | |||
brcc bitstuffN ;[3] | |||
nop ;[4] | |||
subi bitStatus, 37 ;[5] 256 / 7 ~=~ 37 | |||
brcc bitloop ;[6] when we leave the loop, bitStatus has almost the initial value | |||
sbrs shift, 0 ;[7] | |||
eor x1, x4 ;[8] | |||
ror shift ;[9] | |||
didStuff7: | |||
out USBOUT, x1 ;[10] <-- out | |||
ror x2 ;[0] | |||
cpi x2, 0xfc ;[1] | |||
brcc bitstuff7 ;[2] | |||
ld shift, y+ ;[3] | |||
dec cnt ;[5] | |||
brne byteloop ;[6] | |||
;make SE0: | |||
cbr x1, USBMASK ;[7] prepare SE0 [spec says EOP may be 21 to 25 cycles] | |||
lds x2, usbNewDeviceAddr;[8] | |||
lsl x2 ;[10] we compare with left shifted address | |||
out USBOUT, x1 ;[11] <-- out SE0 -- from now 2 bits = 22 cycles until bus idle | |||
;2006-03-06: moved transfer of new address to usbDeviceAddr from C-Code to asm: | |||
;set address only after data packet was sent, not after handshake | |||
subi YL, 2 ;[0] Only assign address on data packets, not ACK/NAK in r0 | |||
sbci YH, 0 ;[1] | |||
breq skipAddrAssign ;[2] | |||
sts usbDeviceAddr, x2; if not skipped: SE0 is one cycle longer | |||
skipAddrAssign: | |||
;end of usbDeviceAddress transfer | |||
ldi x2, 1<<USB_INTR_PENDING_BIT;[4] int0 occurred during TX -- clear pending flag | |||
USB_STORE_PENDING(x2) ;[5] | |||
ori x1, USBIDLE ;[6] | |||
in x2, USBDDR ;[7] | |||
cbr x2, USBMASK ;[8] set both pins to input | |||
mov x3, x1 ;[9] | |||
cbr x3, USBMASK ;[10] configure no pullup on both pins | |||
ldi x4, 4 ;[11] | |||
se0Delay: | |||
dec x4 ;[12] [15] [18] [21] | |||
brne se0Delay ;[13] [16] [19] [22] | |||
out USBOUT, x1 ;[23] <-- out J (idle) -- end of SE0 (EOP signal) | |||
out USBDDR, x2 ;[24] <-- release bus now | |||
out USBOUT, x3 ;[25] <-- ensure no pull-up resistors are active | |||
rjmp doReturn | |||
@@ -0,0 +1,707 @@ | |||
/* Name: usbdrvasm18.inc | |||
* Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers | |||
* Author: Lukas Schrittwieser (based on 20 MHz usbdrvasm20.inc by Jeroen Benschop) | |||
* Creation Date: 2009-01-20 | |||
* Tabsize: 4 | |||
* Copyright: (c) 2008 by Lukas Schrittwieser and OBJECTIVE DEVELOPMENT Software GmbH | |||
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) | |||
* Revision: $Id: usbdrvasm18-crc.inc 740 2009-04-13 18:23:31Z cs $ | |||
*/ | |||
/* Do not link this file! Link usbdrvasm.S instead, which includes the | |||
* appropriate implementation! | |||
*/ | |||
/* | |||
General Description: | |||
This file is the 18 MHz version of the asssembler part of the USB driver. It | |||
requires a 18 MHz crystal (not a ceramic resonator and not a calibrated RC | |||
oscillator). | |||
See usbdrv.h for a description of the entire driver. | |||
Since almost all of this code is timing critical, don't change unless you | |||
really know what you are doing! Many parts require not only a maximum number | |||
of CPU cycles, but even an exact number of cycles! | |||
*/ | |||
;max stack usage: [ret(2), YL, SREG, YH, [sofError], bitcnt(x5), shift, x1, x2, x3, x4, cnt, ZL, ZH] = 14 bytes | |||
;nominal frequency: 18 MHz -> 12 cycles per bit | |||
; Numbers in brackets are clocks counted from center of last sync bit | |||
; when instruction starts | |||
;register use in receive loop to receive the data bytes: | |||
; shift assembles the byte currently being received | |||
; x1 holds the D+ and D- line state | |||
; x2 holds the previous line state | |||
; cnt holds the number of bytes left in the receive buffer | |||
; x3 holds the higher crc byte (see algorithm below) | |||
; x4 is used as temporary register for the crc algorithm | |||
; x5 is used for unstuffing: when unstuffing the last received bit is inverted in shift (to prevent further | |||
; unstuffing calls. In the same time the corresponding bit in x5 is cleared to mark the bit as beening iverted | |||
; zl lower crc value and crc table index | |||
; zh used for crc table accesses | |||
;-------------------------------------------------------------------------------------------------------------- | |||
; CRC mods: | |||
; table driven crc checker, Z points to table in prog space | |||
; ZL is the lower crc byte, x3 is the higher crc byte | |||
; x4 is used as temp register to store different results | |||
; the initialization of the crc register is not 0xFFFF but 0xFE54. This is because during the receipt of the | |||
; first data byte an virtual zero data byte is added to the crc register, this results in the correct initial | |||
; value of 0xFFFF at beginning of the second data byte before the first data byte is added to the crc. | |||
; The magic number 0xFE54 results form the crc table: At tabH[0x54] = 0xFF = crcH (required) and | |||
; tabL[0x54] = 0x01 -> crcL = 0x01 xor 0xFE = 0xFF | |||
; bitcnt is renamed to x5 and is used for unstuffing purposes, the unstuffing works like in the 12MHz version | |||
;-------------------------------------------------------------------------------------------------------------- | |||
; CRC algorithm: | |||
; The crc register is formed by x3 (higher byte) and ZL (lower byte). The algorithm uses a 'reversed' form | |||
; i.e. that it takes the least significant bit first and shifts to the right. So in fact the highest order | |||
; bit seen from the polynomial devision point of view is the lsb of ZL. (If this sounds strange to you i | |||
; propose a research on CRC :-) ) | |||
; Each data byte received is xored to ZL, the lower crc byte. This byte now builds the crc | |||
; table index. Next the new high byte is loaded from the table and stored in x4 until we have space in x3 | |||
; (its destination). | |||
; Afterwards the lower table is loaded from the table and stored in ZL (the old index is overwritten as | |||
; we don't need it anymore. In fact this is a right shift by 8 bits.) Now the old crc high value is xored | |||
; to ZL, this is the second shift of the old crc value. Now x4 (the temp reg) is moved to x3 and the crc | |||
; calculation is done. | |||
; Prior to the first byte the two CRC register have to be initialized to 0xFFFF (as defined in usb spec) | |||
; however the crc engine also runs during the receipt of the first byte, therefore x3 and zl are initialized | |||
; to a magic number which results in a crc value of 0xFFFF after the first complete byte. | |||
; | |||
; This algorithm is split into the extra cycles of the different bits: | |||
; bit7: XOR the received byte to ZL | |||
; bit5: load the new high byte to x4 | |||
; bit6: load the lower xor byte from the table, xor zl and x3, store result in zl (=the new crc low value) | |||
; move x4 (the new high byte) to x3, the crc value is ready | |||
; | |||
macro POP_STANDARD ; 18 cycles | |||
pop ZH | |||
pop ZL | |||
pop cnt | |||
pop x5 | |||
pop x3 | |||
pop x2 | |||
pop x1 | |||
pop shift | |||
pop x4 | |||
endm | |||
macro POP_RETI ; 7 cycles | |||
pop YH | |||
pop YL | |||
out SREG, YL | |||
pop YL | |||
endm | |||
macro CRC_CLEANUP_AND_CHECK | |||
; the last byte has already been xored with the lower crc byte, we have to do the table lookup and xor | |||
; x3 is the higher crc byte, zl the lower one | |||
ldi ZH, hi8(usbCrcTableHigh);[+1] get the new high byte from the table | |||
lpm x2, Z ;[+2][+3][+4] | |||
ldi ZH, hi8(usbCrcTableLow);[+5] get the new low xor byte from the table | |||
lpm ZL, Z ;[+6][+7][+8] | |||
eor ZL, x3 ;[+7] xor the old high byte with the value from the table, x2:ZL now holds the crc value | |||
cpi ZL, 0x01 ;[+8] if the crc is ok we have a fixed remainder value of 0xb001 in x2:ZL (see usb spec) | |||
brne ignorePacket ;[+9] detected a crc fault -> paket is ignored and retransmitted by the host | |||
cpi x2, 0xb0 ;[+10] | |||
brne ignorePacket ;[+11] detected a crc fault -> paket is ignored and retransmitted by the host | |||
endm | |||
USB_INTR_VECTOR: | |||
;order of registers pushed: YL, SREG, YH, [sofError], x4, shift, x1, x2, x3, x5, cnt, ZL, ZH | |||
push YL ;[-28] push only what is necessary to sync with edge ASAP | |||
in YL, SREG ;[-26] | |||
push YL ;[-25] | |||
push YH ;[-23] | |||
;---------------------------------------------------------------------------- | |||
; Synchronize with sync pattern: | |||
;---------------------------------------------------------------------------- | |||
;sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] | |||
;sync up with J to K edge during sync pattern -- use fastest possible loops | |||
;The first part waits at most 1 bit long since we must be in sync pattern. | |||
;YL is guarenteed to be < 0x80 because I flag is clear. When we jump to | |||
;waitForJ, ensure that this prerequisite is met. | |||
waitForJ: | |||
inc YL | |||
sbis USBIN, USBMINUS | |||
brne waitForJ ; just make sure we have ANY timeout | |||
waitForK: | |||
;The following code results in a sampling window of < 1/4 bit which meets the spec. | |||
sbis USBIN, USBMINUS ;[-17] | |||
rjmp foundK ;[-16] | |||
sbis USBIN, USBMINUS | |||
rjmp foundK | |||
sbis USBIN, USBMINUS | |||
rjmp foundK | |||
sbis USBIN, USBMINUS | |||
rjmp foundK | |||
sbis USBIN, USBMINUS | |||
rjmp foundK | |||
sbis USBIN, USBMINUS | |||
rjmp foundK | |||
sbis USBIN, USBMINUS | |||
rjmp foundK | |||
sbis USBIN, USBMINUS | |||
rjmp foundK | |||
sbis USBIN, USBMINUS | |||
rjmp foundK | |||
#if USB_COUNT_SOF | |||
lds YL, usbSofCount | |||
inc YL | |||
sts usbSofCount, YL | |||
#endif /* USB_COUNT_SOF */ | |||
#ifdef USB_SOF_HOOK | |||
USB_SOF_HOOK | |||
#endif | |||
rjmp sofError | |||
foundK: ;[-15] | |||
;{3, 5} after falling D- edge, average delay: 4 cycles | |||
;bit0 should be at 30 (2.5 bits) for center sampling. Currently at 4 so 26 cylces till bit 0 sample | |||
;use 1 bit time for setup purposes, then sample again. Numbers in brackets | |||
;are cycles from center of first sync (double K) bit after the instruction | |||
push x4 ;[-14] | |||
; [---] ;[-13] | |||
lds YL, usbInputBufOffset;[-12] used to toggle the two usb receive buffers | |||
; [---] ;[-11] | |||
clr YH ;[-10] | |||
subi YL, lo8(-(usbRxBuf));[-9] [rx loop init] | |||
sbci YH, hi8(-(usbRxBuf));[-8] [rx loop init] | |||
push shift ;[-7] | |||
; [---] ;[-6] | |||
ldi shift, 0x80 ;[-5] the last bit is the end of byte marker for the pid receiver loop | |||
clc ;[-4] the carry has to be clear for receipt of pid bit 0 | |||
sbis USBIN, USBMINUS ;[-3] we want two bits K (sample 3 cycles too early) | |||
rjmp haveTwoBitsK ;[-2] | |||
pop shift ;[-1] undo the push from before | |||
pop x4 ;[1] | |||
rjmp waitForK ;[3] this was not the end of sync, retry | |||
; The entire loop from waitForK until rjmp waitForK above must not exceed two | |||
; bit times (= 24 cycles). | |||
;---------------------------------------------------------------------------- | |||
; push more registers and initialize values while we sample the first bits: | |||
;---------------------------------------------------------------------------- | |||
haveTwoBitsK: | |||
push x1 ;[0] | |||
push x2 ;[2] | |||
push x3 ;[4] crc high byte | |||
ldi x2, 1<<USBPLUS ;[6] [rx loop init] current line state is K state. D+=="1", D-=="0" | |||
push x5 ;[7] | |||
push cnt ;[9] | |||
ldi cnt, USB_BUFSIZE ;[11] | |||
;-------------------------------------------------------------------------------------------------------------- | |||
; receives the pid byte | |||
; there is no real unstuffing algorithm implemented here as a stuffing bit is impossible in the pid byte. | |||
; That's because the last four bits of the byte are the inverted of the first four bits. If we detect a | |||
; unstuffing condition something went wrong and abort | |||
; shift has to be initialized to 0x80 | |||
;-------------------------------------------------------------------------------------------------------------- | |||
; pid bit 0 - used for even more register saving (we need the z pointer) | |||
in x1, USBIN ;[0] sample line state | |||
andi x1, USBMASK ;[1] filter only D+ and D- bits | |||
eor x2, x1 ;[2] generate inverted of actual bit | |||
sbrc x2, USBMINUS ;[3] if the bit is set we received a zero | |||
sec ;[4] | |||
ror shift ;[5] we perform no unstuffing check here as this is the first bit | |||
mov x2, x1 ;[6] | |||
push ZL ;[7] | |||
;[8] | |||
push ZH ;[9] | |||
;[10] | |||
ldi x3, 0xFE ;[11] x3 is the high order crc value | |||
bitloopPid: | |||
in x1, USBIN ;[0] sample line state | |||
andi x1, USBMASK ;[1] filter only D+ and D- bits | |||
breq nse0 ;[2] both lines are low so handle se0 | |||
eor x2, x1 ;[3] generate inverted of actual bit | |||
sbrc x2, USBMINUS ;[4] set the carry if we received a zero | |||
sec ;[5] | |||
ror shift ;[6] | |||
ldi ZL, 0x54 ;[7] ZL is the low order crc value | |||
ser x4 ;[8] the is no bit stuffing check here as the pid bit can't be stuffed. if so | |||
; some error occured. In this case the paket is discarded later on anyway. | |||
mov x2, x1 ;[9] prepare for the next cycle | |||
brcc bitloopPid ;[10] while 0s drop out of shift we get the next bit | |||
eor x4, shift ;[11] invert all bits in shift and store result in x4 | |||
;-------------------------------------------------------------------------------------------------------------- | |||
; receives data bytes and calculates the crc | |||
; the last USBIN state has to be in x2 | |||
; this is only the first half, due to branch distanc limitations the second half of the loop is near the end | |||
; of this asm file | |||
;-------------------------------------------------------------------------------------------------------------- | |||
rxDataStart: | |||
in x1, USBIN ;[0] sample line state (note: a se0 check is not useful due to bit dribbling) | |||
ser x5 ;[1] prepare the unstuff marker register | |||
eor x2, x1 ;[2] generates the inverted of the actual bit | |||
bst x2, USBMINUS ;[3] copy the bit from x2 | |||
bld shift, 0 ;[4] and store it in shift | |||
mov x2, shift ;[5] make a copy of shift for unstuffing check | |||
andi x2, 0xF9 ;[6] mask the last six bits, if we got six zeros (which are six ones in fact) | |||
breq unstuff0 ;[7] then Z is set now and we branch to the unstuffing handler | |||
didunstuff0: | |||
subi cnt, 1 ;[8] cannot use dec because it doesn't affect the carry flag | |||
brcs nOverflow ;[9] Too many bytes received. Ignore packet | |||
st Y+, x4 ;[10] store the last received byte | |||
;[11] st needs two cycles | |||
; bit1 | |||
in x2, USBIN ;[0] sample line state | |||
andi x1, USBMASK ;[1] check for se0 during bit 0 | |||
breq nse0 ;[2] | |||
andi x2, USBMASK ;[3] check se0 during bit 1 | |||
breq nse0 ;[4] | |||
eor x1, x2 ;[5] | |||
bst x1, USBMINUS ;[6] | |||
bld shift, 1 ;[7] | |||
mov x1, shift ;[8] | |||
andi x1, 0xF3 ;[9] | |||
breq unstuff1 ;[10] | |||
didunstuff1: | |||
nop ;[11] | |||
; bit2 | |||
in x1, USBIN ;[0] sample line state | |||
andi x1, USBMASK ;[1] check for se0 (as there is nothing else to do here | |||
breq nOverflow ;[2] | |||
eor x2, x1 ;[3] generates the inverted of the actual bit | |||
bst x2, USBMINUS ;[4] | |||
bld shift, 2 ;[5] store the bit | |||
mov x2, shift ;[6] | |||
andi x2, 0xE7 ;[7] if we have six zeros here (which means six 1 in the stream) | |||
breq unstuff2 ;[8] the next bit is a stuffing bit | |||
didunstuff2: | |||
nop2 ;[9] | |||
;[10] | |||
nop ;[11] | |||
; bit3 | |||
in x2, USBIN ;[0] sample line state | |||
andi x2, USBMASK ;[1] check for se0 | |||
breq nOverflow ;[2] | |||
eor x1, x2 ;[3] | |||
bst x1, USBMINUS ;[4] | |||
bld shift, 3 ;[5] | |||
mov x1, shift ;[6] | |||
andi x1, 0xCF ;[7] | |||
breq unstuff3 ;[8] | |||
didunstuff3: | |||
nop ;[9] | |||
rjmp rxDataBit4 ;[10] | |||
;[11] | |||
; the avr branch instructions allow an offset of +63 insturction only, so we need this | |||
; 'local copy' of se0 | |||
nse0: | |||
rjmp se0 ;[4] | |||
;[5] | |||
; the same same as for se0 is needed for overflow and StuffErr | |||
nOverflow: | |||
stuffErr: | |||
rjmp overflow | |||
unstuff0: ;[8] this is the branch delay of breq unstuffX | |||
andi x1, USBMASK ;[9] do an se0 check here (if the last crc byte ends with 5 one's we might end up here | |||
breq didunstuff0 ;[10] event tough the message is complete -> jump back and store the byte | |||
ori shift, 0x01 ;[11] invert the last received bit to prevent furhter unstuffing | |||
in x2, USBIN ;[0] we have some free cycles so we could check for bit stuffing errors | |||
andi x5, 0xFE ;[1] mark this bit as inverted (will be corrected before storing shift) | |||
eor x1, x2 ;[2] x1 and x2 have to be different because the stuff bit is always a zero | |||
andi x1, USBMASK ;[3] mask the interesting bits | |||
breq stuffErr ;[4] if the stuff bit is a 1-bit something went wrong | |||
mov x1, x2 ;[5] the next bit expects the last state to be in x1 | |||
rjmp didunstuff0 ;[6] | |||
;[7] jump delay of rjmp didunstuffX | |||
unstuff1: ;[11] this is the jump delay of breq unstuffX | |||
in x1, USBIN ;[0] we have some free cycles so we could check for bit stuffing errors | |||
ori shift, 0x02 ;[1] invert the last received bit to prevent furhter unstuffing | |||
andi x5, 0xFD ;[2] mark this bit as inverted (will be corrected before storing shift) | |||
eor x2, x1 ;[3] x1 and x2 have to be different because the stuff bit is always a zero | |||
andi x2, USBMASK ;[4] mask the interesting bits | |||
breq stuffErr ;[5] if the stuff bit is a 1-bit something went wrong | |||
mov x2, x1 ;[6] the next bit expects the last state to be in x2 | |||
nop2 ;[7] | |||
;[8] | |||
rjmp didunstuff1 ;[9] | |||
;[10] jump delay of rjmp didunstuffX | |||
unstuff2: ;[9] this is the jump delay of breq unstuffX | |||
ori shift, 0x04 ;[10] invert the last received bit to prevent furhter unstuffing | |||
andi x5, 0xFB ;[11] mark this bit as inverted (will be corrected before storing shift) | |||
in x2, USBIN ;[0] we have some free cycles so we could check for bit stuffing errors | |||
eor x1, x2 ;[1] x1 and x2 have to be different because the stuff bit is always a zero | |||
andi x1, USBMASK ;[2] mask the interesting bits | |||
breq stuffErr ;[3] if the stuff bit is a 1-bit something went wrong | |||
mov x1, x2 ;[4] the next bit expects the last state to be in x1 | |||
nop2 ;[5] | |||
;[6] | |||
rjmp didunstuff2 ;[7] | |||
;[8] jump delay of rjmp didunstuffX | |||
unstuff3: ;[9] this is the jump delay of breq unstuffX | |||
ori shift, 0x08 ;[10] invert the last received bit to prevent furhter unstuffing | |||
andi x5, 0xF7 ;[11] mark this bit as inverted (will be corrected before storing shift) | |||
in x1, USBIN ;[0] we have some free cycles so we could check for bit stuffing errors | |||
eor x2, x1 ;[1] x1 and x2 have to be different because the stuff bit is always a zero | |||
andi x2, USBMASK ;[2] mask the interesting bits | |||
breq stuffErr ;[3] if the stuff bit is a 1-bit something went wrong | |||
mov x2, x1 ;[4] the next bit expects the last state to be in x2 | |||
nop2 ;[5] | |||
;[6] | |||
rjmp didunstuff3 ;[7] | |||
;[8] jump delay of rjmp didunstuffX | |||
; the include has to be here due to branch distance restirctions | |||
#define __USE_CRC__ | |||
#include "asmcommon.inc" | |||
; USB spec says: | |||
; idle = J | |||
; J = (D+ = 0), (D- = 1) | |||
; K = (D+ = 1), (D- = 0) | |||
; Spec allows 7.5 bit times from EOP to SOP for replies | |||
; 7.5 bit times is 90 cycles. ...there is plenty of time | |||
sendNakAndReti: | |||
ldi x3, USBPID_NAK ;[-18] | |||
rjmp sendX3AndReti ;[-17] | |||
sendAckAndReti: | |||
ldi cnt, USBPID_ACK ;[-17] | |||
sendCntAndReti: | |||
mov x3, cnt ;[-16] | |||
sendX3AndReti: | |||
ldi YL, 20 ;[-15] x3==r20 address is 20 | |||
ldi YH, 0 ;[-14] | |||
ldi cnt, 2 ;[-13] | |||
; rjmp usbSendAndReti fallthrough | |||
;usbSend: | |||
;pointer to data in 'Y' | |||
;number of bytes in 'cnt' -- including sync byte [range 2 ... 12] | |||
;uses: x1...x4, btcnt, shift, cnt, Y | |||
;Numbers in brackets are time since first bit of sync pattern is sent | |||
usbSendAndReti: ; 12 cycles until SOP | |||
in x2, USBDDR ;[-12] | |||
ori x2, USBMASK ;[-11] | |||
sbi USBOUT, USBMINUS;[-10] prepare idle state; D+ and D- must have been 0 (no pullups) | |||
in x1, USBOUT ;[-8] port mirror for tx loop | |||
out USBDDR, x2 ;[-6] <- acquire bus | |||
ldi x2, 0 ;[-6] init x2 (bitstuff history) because sync starts with 0 | |||
ldi x4, USBMASK ;[-5] exor mask | |||
ldi shift, 0x80 ;[-4] sync byte is first byte sent | |||
txByteLoop: | |||
ldi bitcnt, 0x40 ;[-3]=[9] binary 01000000 | |||
txBitLoop: ; the loop sends the first 7 bits of the byte | |||
sbrs shift, 0 ;[-2]=[10] if we have to send a 1 don't change the line state | |||
eor x1, x4 ;[-1]=[11] | |||
out USBOUT, x1 ;[0] | |||
ror shift ;[1] | |||
ror x2 ;[2] transfers the last sent bit to the stuffing history | |||
didStuffN: | |||
nop ;[3] | |||
nop ;[4] | |||
cpi x2, 0xfc ;[5] if we sent six consecutive ones | |||
brcc bitstuffN ;[6] | |||
lsr bitcnt ;[7] | |||
brne txBitLoop ;[8] restart the loop while the 1 is still in the bitcount | |||
; transmit bit 7 | |||
sbrs shift, 0 ;[9] | |||
eor x1, x4 ;[10] | |||
didStuff7: | |||
ror shift ;[11] | |||
out USBOUT, x1 ;[0] transfer bit 7 to the pins | |||
ror x2 ;[1] move the bit into the stuffing history | |||
cpi x2, 0xfc ;[2] | |||
brcc bitstuff7 ;[3] | |||
ld shift, y+ ;[4] get next byte to transmit | |||
dec cnt ;[5] decrement byte counter | |||
brne txByteLoop ;[7] if we have more bytes start next one | |||
;[8] branch delay | |||
;make SE0: | |||
cbr x1, USBMASK ;[8] prepare SE0 [spec says EOP may be 25 to 30 cycles] | |||
lds x2, usbNewDeviceAddr;[9] | |||
lsl x2 ;[11] we compare with left shifted address | |||
out USBOUT, x1 ;[0] <-- out SE0 -- from now 2 bits = 24 cycles until bus idle | |||
subi YL, 20 + 2 ;[1] Only assign address on data packets, not ACK/NAK in x3 | |||
sbci YH, 0 ;[2] | |||
;2006-03-06: moved transfer of new address to usbDeviceAddr from C-Code to asm: | |||
;set address only after data packet was sent, not after handshake | |||
breq skipAddrAssign ;[3] | |||
sts usbDeviceAddr, x2 ; if not skipped: SE0 is one cycle longer | |||
skipAddrAssign: | |||
;end of usbDeviceAddress transfer | |||
ldi x2, 1<<USB_INTR_PENDING_BIT;[5] int0 occurred during TX -- clear pending flag | |||
USB_STORE_PENDING(x2) ;[6] | |||
ori x1, USBIDLE ;[7] | |||
in x2, USBDDR ;[8] | |||
cbr x2, USBMASK ;[9] set both pins to input | |||
mov x3, x1 ;[10] | |||
cbr x3, USBMASK ;[11] configure no pullup on both pins | |||
ldi x4, 4 ;[12] | |||
se0Delay: | |||
dec x4 ;[13] [16] [19] [22] | |||
brne se0Delay ;[14] [17] [20] [23] | |||
out USBOUT, x1 ;[24] <-- out J (idle) -- end of SE0 (EOP signal) | |||
out USBDDR, x2 ;[25] <-- release bus now | |||
out USBOUT, x3 ;[26] <-- ensure no pull-up resistors are active | |||
rjmp doReturn | |||
bitstuffN: | |||
eor x1, x4 ;[8] generate a zero | |||
ldi x2, 0 ;[9] reset the bit stuffing history | |||
nop2 ;[10] | |||
out USBOUT, x1 ;[0] <-- send the stuffing bit | |||
rjmp didStuffN ;[1] | |||
bitstuff7: | |||
eor x1, x4 ;[5] | |||
ldi x2, 0 ;[6] reset bit stuffing history | |||
clc ;[7] fill a zero into the shift register | |||
rol shift ;[8] compensate for ror shift at branch destination | |||
rjmp didStuff7 ;[9] | |||
;[10] jump delay | |||
;-------------------------------------------------------------------------------------------------------------- | |||
; receives data bytes and calculates the crc | |||
; second half of the data byte receiver loop | |||
; most parts of the crc algorithm are here | |||
;-------------------------------------------------------------------------------------------------------------- | |||
nOverflow2: | |||
rjmp overflow | |||
rxDataBit4: | |||
in x1, USBIN ;[0] sample line state | |||
andi x1, USBMASK ;[1] check for se0 | |||
breq nOverflow2 ;[2] | |||
eor x2, x1 ;[3] | |||
bst x2, USBMINUS ;[4] | |||
bld shift, 4 ;[5] | |||
mov x2, shift ;[6] | |||
andi x2, 0x9F ;[7] | |||
breq unstuff4 ;[8] | |||
didunstuff4: | |||
nop2 ;[9][10] | |||
nop ;[11] | |||
; bit5 | |||
in x2, USBIN ;[0] sample line state | |||
ldi ZH, hi8(usbCrcTableHigh);[1] use the table for the higher byte | |||
eor x1, x2 ;[2] | |||
bst x1, USBMINUS ;[3] | |||
bld shift, 5 ;[4] | |||
mov x1, shift ;[5] | |||
andi x1, 0x3F ;[6] | |||
breq unstuff5 ;[7] | |||
didunstuff5: | |||
lpm x4, Z ;[8] load the higher crc xor-byte and store it for later use | |||
;[9] lpm needs 3 cycles | |||
;[10] | |||
ldi ZH, hi8(usbCrcTableLow);[11] load the lower crc xor byte adress | |||
; bit6 | |||
in x1, USBIN ;[0] sample line state | |||
eor x2, x1 ;[1] | |||
bst x2, USBMINUS ;[2] | |||
bld shift, 6 ;[3] | |||
mov x2, shift ;[4] | |||
andi x2, 0x7E ;[5] | |||
breq unstuff6 ;[6] | |||
didunstuff6: | |||
lpm ZL, Z ;[7] load the lower xor crc byte | |||
;[8] lpm needs 3 cycles | |||
;[9] | |||
eor ZL, x3 ;[10] xor the old high crc byte with the low xor-byte | |||
mov x3, x4 ;[11] move the new high order crc value from temp to its destination | |||
; bit7 | |||
in x2, USBIN ;[0] sample line state | |||
eor x1, x2 ;[1] | |||
bst x1, USBMINUS ;[2] | |||
bld shift, 7 ;[3] now shift holds the complete but inverted data byte | |||
mov x1, shift ;[4] | |||
andi x1, 0xFC ;[5] | |||
breq unstuff7 ;[6] | |||
didunstuff7: | |||
eor x5, shift ;[7] x5 marks all bits which have not been inverted by the unstuffing subs | |||
mov x4, x5 ;[8] keep a copy of the data byte it will be stored during next bit0 | |||
eor ZL, x4 ;[9] feed the actual byte into the crc algorithm | |||
rjmp rxDataStart ;[10] next byte | |||
;[11] during the reception of the next byte this one will be fed int the crc algorithm | |||
unstuff4: ;[9] this is the jump delay of rjmp unstuffX | |||
ori shift, 0x10 ;[10] invert the last received bit to prevent furhter unstuffing | |||
andi x5, 0xEF ;[11] mark this bit as inverted (will be corrected before storing shift) | |||
in x2, USBIN ;[0] we have some free cycles so we could check for bit stuffing errors | |||
eor x1, x2 ;[1] x1 and x2 have to be different because the stuff bit is always a zero | |||
andi x1, USBMASK ;[2] mask the interesting bits | |||
breq stuffErr2 ;[3] if the stuff bit is a 1-bit something went wrong | |||
mov x1, x2 ;[4] the next bit expects the last state to be in x1 | |||
nop2 ;[5] | |||
;[6] | |||
rjmp didunstuff4 ;[7] | |||
;[8] jump delay of rjmp didunstuffX | |||
unstuff5: ;[8] this is the jump delay of rjmp unstuffX | |||
nop ;[9] | |||
ori shift, 0x20 ;[10] invert the last received bit to prevent furhter unstuffing | |||
andi x5, 0xDF ;[11] mark this bit as inverted (will be corrected before storing shift) | |||
in x1, USBIN ;[0] we have some free cycles so we could check for bit stuffing errors | |||
eor x2, x1 ;[1] x1 and x2 have to be different because the stuff bit is always a zero | |||
andi x2, USBMASK ;[2] mask the interesting bits | |||
breq stuffErr2 ;[3] if the stuff bit is a 1-bit something went wrong | |||
mov x2, x1 ;[4] the next bit expects the last state to be in x2 | |||
nop ;[5] | |||
rjmp didunstuff5 ;[6] | |||
;[7] jump delay of rjmp didunstuffX | |||
unstuff6: ;[7] this is the jump delay of rjmp unstuffX | |||
nop2 ;[8] | |||
;[9] | |||
ori shift, 0x40 ;[10] invert the last received bit to prevent furhter unstuffing | |||
andi x5, 0xBF ;[11] mark this bit as inverted (will be corrected before storing shift) | |||
in x2, USBIN ;[0] we have some free cycles so we could check for bit stuffing errors | |||
eor x1, x2 ;[1] x1 and x2 have to be different because the stuff bit is always a zero | |||
andi x1, USBMASK ;[2] mask the interesting bits | |||
breq stuffErr2 ;[3] if the stuff bit is a 1-bit something went wrong | |||
mov x1, x2 ;[4] the next bit expects the last state to be in x1 | |||
rjmp didunstuff6 ;[5] | |||
;[6] jump delay of rjmp didunstuffX | |||
unstuff7: ;[7] this is the jump delay of rjmp unstuffX | |||
nop ;[8] | |||
nop ;[9] | |||
ori shift, 0x80 ;[10] invert the last received bit to prevent furhter unstuffing | |||
andi x5, 0x7F ;[11] mark this bit as inverted (will be corrected before storing shift) | |||
in x1, USBIN ;[0] we have some free cycles so we could check for bit stuffing errors | |||
eor x2, x1 ;[1] x1 and x2 have to be different because the stuff bit is always a zero | |||
andi x2, USBMASK ;[2] mask the interesting bits | |||
breq stuffErr2 ;[3] if the stuff bit is a 1-bit something went wrong | |||
mov x2, x1 ;[4] the next bit expects the last state to be in x2 | |||
rjmp didunstuff7 ;[5] | |||
;[6] jump delay of rjmp didunstuff7 | |||
; local copy of the stuffErr desitnation for the second half of the receiver loop | |||
stuffErr2: | |||
rjmp stuffErr | |||
;-------------------------------------------------------------------------------------------------------------- | |||
; The crc table follows. It has to be aligned to enable a fast loading of the needed bytes. | |||
; There are two tables of 256 entries each, the low and the high byte table. | |||
; Table values were generated with the following C code: | |||
/* | |||
#include <stdio.h> | |||
int main (int argc, char **argv) | |||
{ | |||
int i, j; | |||
for (i=0; i<512; i++){ | |||
unsigned short crc = i & 0xff; | |||
for(j=0; j<8; j++) crc = (crc >> 1) ^ ((crc & 1) ? 0xa001 : 0); | |||
if((i & 7) == 0) printf("\n.byte "); | |||
printf("0x%02x, ", (i > 0xff ? (crc >> 8) : crc) & 0xff); | |||
if(i == 255) printf("\n"); | |||
} | |||
return 0; | |||
} | |||
// Use the following algorithm to compute CRC values: | |||
ushort computeCrc(uchar *msg, uchar msgLen) | |||
{ | |||
uchar i; | |||
ushort crc = 0xffff; | |||
for(i = 0; i < msgLen; i++) | |||
crc = usbCrcTable16[lo8(crc) ^ msg[i]] ^ hi8(crc); | |||
return crc; | |||
} | |||
*/ | |||
.balign 256 | |||
usbCrcTableLow: | |||
.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 | |||
.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 | |||
.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 | |||
.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 | |||
.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 | |||
.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 | |||
.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 | |||
.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 | |||
.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 | |||
.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 | |||
.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 | |||
.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 | |||
.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 | |||
.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 | |||
.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 | |||
.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 | |||
.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 | |||
.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 | |||
.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 | |||
.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 | |||
.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 | |||
.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 | |||
.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 | |||
.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 | |||
.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 | |||
.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 | |||
.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 | |||
.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 | |||
.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 | |||
.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 | |||
.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 | |||
.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 | |||
; .balign 256 | |||
usbCrcTableHigh: | |||
.byte 0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2 | |||
.byte 0xC6, 0x06, 0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04 | |||
.byte 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E | |||
.byte 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 0x08, 0xC8 | |||
.byte 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A | |||
.byte 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC | |||
.byte 0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6 | |||
.byte 0xD2, 0x12, 0x13, 0xD3, 0x11, 0xD1, 0xD0, 0x10 | |||
.byte 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32 | |||
.byte 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4 | |||
.byte 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE | |||
.byte 0xFA, 0x3A, 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38 | |||
.byte 0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA | |||
.byte 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C | |||
.byte 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26 | |||
.byte 0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0 | |||
.byte 0xA0, 0x60, 0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62 | |||
.byte 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4 | |||
.byte 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 0x6E, 0xAE | |||
.byte 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68 | |||
.byte 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA | |||
.byte 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C | |||
.byte 0xB4, 0x74, 0x75, 0xB5, 0x77, 0xB7, 0xB6, 0x76 | |||
.byte 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0 | |||
.byte 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92 | |||
.byte 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54 | |||
.byte 0x9C, 0x5C, 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E | |||
.byte 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98 | |||
.byte 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x4A | |||
.byte 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C | |||
.byte 0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86 | |||
.byte 0x82, 0x42, 0x43, 0x83, 0x41, 0x81, 0x80, 0x40 | |||
@@ -0,0 +1,360 @@ | |||
/* Name: usbdrvasm20.inc | |||
* Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers | |||
* Author: Jeroen Benschop | |||
* Based on usbdrvasm16.inc from Christian Starkjohann | |||
* Creation Date: 2008-03-05 | |||
* Tabsize: 4 | |||
* Copyright: (c) 2008 by Jeroen Benschop and OBJECTIVE DEVELOPMENT Software GmbH | |||
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) | |||
* Revision: $Id: usbdrvasm20.inc 740 2009-04-13 18:23:31Z cs $ | |||
*/ | |||
/* Do not link this file! Link usbdrvasm.S instead, which includes the | |||
* appropriate implementation! | |||
*/ | |||
/* | |||
General Description: | |||
This file is the 20 MHz version of the asssembler part of the USB driver. It | |||
requires a 20 MHz crystal (not a ceramic resonator and not a calibrated RC | |||
oscillator). | |||
See usbdrv.h for a description of the entire driver. | |||
Since almost all of this code is timing critical, don't change unless you | |||
really know what you are doing! Many parts require not only a maximum number | |||
of CPU cycles, but even an exact number of cycles! | |||
*/ | |||
#define leap2 x3 | |||
#ifdef __IAR_SYSTEMS_ASM__ | |||
#define nextInst $+2 | |||
#else | |||
#define nextInst .+0 | |||
#endif | |||
;max stack usage: [ret(2), YL, SREG, YH, bitcnt, shift, x1, x2, x3, x4, cnt] = 12 bytes | |||
;nominal frequency: 20 MHz -> 13.333333 cycles per bit, 106.666667 cycles per byte | |||
; Numbers in brackets are clocks counted from center of last sync bit | |||
; when instruction starts | |||
;register use in receive loop: | |||
; shift assembles the byte currently being received | |||
; x1 holds the D+ and D- line state | |||
; x2 holds the previous line state | |||
; x4 (leap) is used to add a leap cycle once every three bytes received | |||
; X3 (leap2) is used to add a leap cycle once every three stuff bits received | |||
; bitcnt is used to determine when a stuff bit is due | |||
; cnt holds the number of bytes left in the receive buffer | |||
USB_INTR_VECTOR: | |||
;order of registers pushed: YL, SREG YH, [sofError], bitcnt, shift, x1, x2, x3, x4, cnt | |||
push YL ;[-28] push only what is necessary to sync with edge ASAP | |||
in YL, SREG ;[-26] | |||
push YL ;[-25] | |||
push YH ;[-23] | |||
;---------------------------------------------------------------------------- | |||
; Synchronize with sync pattern: | |||
;---------------------------------------------------------------------------- | |||
;sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] | |||
;sync up with J to K edge during sync pattern -- use fastest possible loops | |||
;The first part waits at most 1 bit long since we must be in sync pattern. | |||
;YL is guarenteed to be < 0x80 because I flag is clear. When we jump to | |||
;waitForJ, ensure that this prerequisite is met. | |||
waitForJ: | |||
inc YL | |||
sbis USBIN, USBMINUS | |||
brne waitForJ ; just make sure we have ANY timeout | |||
waitForK: | |||
;The following code results in a sampling window of < 1/4 bit which meets the spec. | |||
sbis USBIN, USBMINUS ;[-19] | |||
rjmp foundK ;[-18] | |||
sbis USBIN, USBMINUS | |||
rjmp foundK | |||
sbis USBIN, USBMINUS | |||
rjmp foundK | |||
sbis USBIN, USBMINUS | |||
rjmp foundK | |||
sbis USBIN, USBMINUS | |||
rjmp foundK | |||
sbis USBIN, USBMINUS | |||
rjmp foundK | |||
sbis USBIN, USBMINUS | |||
rjmp foundK | |||
sbis USBIN, USBMINUS | |||
rjmp foundK | |||
sbis USBIN, USBMINUS | |||
rjmp foundK | |||
#if USB_COUNT_SOF | |||
lds YL, usbSofCount | |||
inc YL | |||
sts usbSofCount, YL | |||
#endif /* USB_COUNT_SOF */ | |||
#ifdef USB_SOF_HOOK | |||
USB_SOF_HOOK | |||
#endif | |||
rjmp sofError | |||
foundK: ;[-16] | |||
;{3, 5} after falling D- edge, average delay: 4 cycles | |||
;bit0 should be at 34 for center sampling. Currently at 4 so 30 cylces till bit 0 sample | |||
;use 1 bit time for setup purposes, then sample again. Numbers in brackets | |||
;are cycles from center of first sync (double K) bit after the instruction | |||
push bitcnt ;[-16] | |||
; [---] ;[-15] | |||
lds YL, usbInputBufOffset;[-14] | |||
; [---] ;[-13] | |||
clr YH ;[-12] | |||
subi YL, lo8(-(usbRxBuf));[-11] [rx loop init] | |||
sbci YH, hi8(-(usbRxBuf));[-10] [rx loop init] | |||
push shift ;[-9] | |||
; [---] ;[-8] | |||
ldi shift,0x40 ;[-7] set msb to "1" so processing bit7 can be detected | |||
nop2 ;[-6] | |||
; [---] ;[-5] | |||
ldi bitcnt, 5 ;[-4] [rx loop init] | |||
sbis USBIN, USBMINUS ;[-3] we want two bits K (sample 3 cycles too early) | |||
rjmp haveTwoBitsK ;[-2] | |||
pop shift ;[-1] undo the push from before | |||
pop bitcnt ;[1] | |||
rjmp waitForK ;[3] this was not the end of sync, retry | |||
; The entire loop from waitForK until rjmp waitForK above must not exceed two | |||
; bit times (= 27 cycles). | |||
;---------------------------------------------------------------------------- | |||
; push more registers and initialize values while we sample the first bits: | |||
;---------------------------------------------------------------------------- | |||
haveTwoBitsK: | |||
push x1 ;[0] | |||
push x2 ;[2] | |||
push x3 ;[4] (leap2) | |||
ldi leap2, 0x55 ;[6] add leap cycle on 2nd,5th,8th,... stuff bit | |||
push x4 ;[7] == leap | |||
ldi leap, 0x55 ;[9] skip leap cycle on 2nd,5th,8th,... byte received | |||
push cnt ;[10] | |||
ldi cnt, USB_BUFSIZE ;[12] [rx loop init] | |||
ldi x2, 1<<USBPLUS ;[13] current line state is K state. D+=="1", D-=="0" | |||
bit0: | |||
in x1, USBIN ;[0] sample line state | |||
andi x1, USBMASK ;[1] filter only D+ and D- bits | |||
rjmp handleBit ;[2] make bit0 14 cycles long | |||
;---------------------------------------------------------------------------- | |||
; Process bit7. However, bit 6 still may need unstuffing. | |||
;---------------------------------------------------------------------------- | |||
b6checkUnstuff: | |||
dec bitcnt ;[9] | |||
breq unstuff6 ;[10] | |||
bit7: | |||
subi cnt, 1 ;[11] cannot use dec becaus it does not affect the carry flag | |||
brcs overflow ;[12] Too many bytes received. Ignore packet | |||
in x1, USBIN ;[0] sample line state | |||
andi x1, USBMASK ;[1] filter only D+ and D- bits | |||
cpse x1, x2 ;[2] when previous line state equals current line state, handle "1" | |||
rjmp b7handle0 ;[3] when line state differs, handle "0" | |||
sec ;[4] | |||
ror shift ;[5] shift "1" into the data | |||
st y+, shift ;[6] store the data into the buffer | |||
ldi shift, 0x40 ;[7] reset data for receiving the next byte | |||
subi leap, 0x55 ;[9] trick to introduce a leap cycle every 3 bytes | |||
brcc nextInst ;[10 or 11] it will fail after 85 bytes. However low speed can only receive 11 | |||
dec bitcnt ;[11 or 12] | |||
brne bit0 ;[12 or 13] | |||
ldi x1, 1 ;[13 or 14] unstuffing bit 7 | |||
in bitcnt, USBIN ;[0] sample stuff bit | |||
rjmp unstuff ;[1] | |||
b7handle0: | |||
mov x2,x1 ;[5] Set x2 to current line state | |||
ldi bitcnt, 6 ;[6] | |||
lsr shift ;[7] shift "0" into the data | |||
st y+, shift ;[8] store data into the buffer | |||
ldi shift, 0x40 ;[10] reset data for receiving the next byte | |||
subi leap, 0x55 ;[11] trick to introduce a leap cycle every 3 bytes | |||
brcs bit0 ;[12] it will fail after 85 bytes. However low speed can only receive 11 | |||
rjmp bit0 ;[13] | |||
;---------------------------------------------------------------------------- | |||
; Handle unstuff | |||
; x1==0xFF indicate unstuffing bit6 | |||
;---------------------------------------------------------------------------- | |||
unstuff6: | |||
ldi x1,0xFF ;[12] indicate unstuffing bit 6 | |||
in bitcnt, USBIN ;[0] sample stuff bit | |||
nop ;[1] fix timing | |||
unstuff: ;b0-5 b6 b7 | |||
mov x2,bitcnt ;[3] [2] [3] Set x2 to match line state | |||
subi leap2, 0x55 ;[4] [3] [4] delay loop | |||
brcs nextInst ;[5] [4] [5] add one cycle every three stuff bits | |||
sbci leap2,0 ;[6] [5] [6] | |||
ldi bitcnt,6 ;[7] [6] [7] reset bit stuff counter | |||
andi x2, USBMASK ;[8] [7] [8] only keep D+ and D- | |||
cpi x1,0 ;[9] [8] [9] | |||
brmi bit7 ;[10] [9] [10] finished unstuffing bit6 When x1<0 | |||
breq bitloop ;[11] --- [11] finished unstuffing bit0-5 when x1=0 | |||
nop ;--- --- [12] | |||
in x1, USBIN ;--- --- [0] sample line state for bit0 | |||
andi x1, USBMASK ;--- --- [1] filter only D+ and D- bits | |||
rjmp handleBit ;--- --- [2] make bit0 14 cycles long | |||
;---------------------------------------------------------------------------- | |||
; Receiver loop (numbers in brackets are cycles within byte after instr) | |||
;---------------------------------------------------------------------------- | |||
bitloop: | |||
in x1, USBIN ;[0] sample line state | |||
andi x1, USBMASK ;[1] filter only D+ and D- bits | |||
breq se0 ;[2] both lines are low so handle se0 | |||
handleBit: | |||
cpse x1, x2 ;[3] when previous line state equals current line state, handle "1" | |||
rjmp handle0 ;[4] when line state differs, handle "0" | |||
sec ;[5] | |||
ror shift ;[6] shift "1" into the data | |||
brcs b6checkUnstuff ;[7] When after shift C is set, next bit is bit7 | |||
nop2 ;[8] | |||
dec bitcnt ;[10] | |||
brne bitloop ;[11] | |||
ldi x1,0 ;[12] indicate unstuff for bit other than bit6 or bit7 | |||
in bitcnt, USBIN ;[0] sample stuff bit | |||
rjmp unstuff ;[1] | |||
handle0: | |||
mov x2, x1 ;[6] Set x2 to current line state | |||
ldi bitcnt, 6 ;[7] reset unstuff counter. | |||
lsr shift ;[8] shift "0" into the data | |||
brcs bit7 ;[9] When after shift C is set, next bit is bit7 | |||
nop ;[10] | |||
rjmp bitloop ;[11] | |||
;---------------------------------------------------------------------------- | |||
; End of receive loop. Now start handling EOP | |||
;---------------------------------------------------------------------------- | |||
macro POP_STANDARD ; 14 cycles | |||
pop cnt | |||
pop x4 | |||
pop x3 | |||
pop x2 | |||
pop x1 | |||
pop shift | |||
pop bitcnt | |||
endm | |||
macro POP_RETI ; 7 cycles | |||
pop YH | |||
pop YL | |||
out SREG, YL | |||
pop YL | |||
endm | |||
#include "asmcommon.inc" | |||
; USB spec says: | |||
; idle = J | |||
; J = (D+ = 0), (D- = 1) | |||
; K = (D+ = 1), (D- = 0) | |||
; Spec allows 7.5 bit times from EOP to SOP for replies | |||
; 7.5 bit times is 100 cycles. This implementation arrives a bit later at se0 | |||
; then specified in the include file but there is plenty of time | |||
bitstuffN: | |||
eor x1, x4 ;[8] | |||
ldi x2, 0 ;[9] | |||
nop2 ;[10] | |||
out USBOUT, x1 ;[12] <-- out | |||
rjmp didStuffN ;[0] | |||
bitstuff7: | |||
eor x1, x4 ;[6] | |||
ldi x2, 0 ;[7] Carry is zero due to brcc | |||
rol shift ;[8] compensate for ror shift at branch destination | |||
nop2 ;[9] | |||
rjmp didStuff7 ;[11] | |||
sendNakAndReti: | |||
ldi x3, USBPID_NAK ;[-18] | |||
rjmp sendX3AndReti ;[-17] | |||
sendAckAndReti: | |||
ldi cnt, USBPID_ACK ;[-17] | |||
sendCntAndReti: | |||
mov x3, cnt ;[-16] | |||
sendX3AndReti: | |||
ldi YL, 20 ;[-15] x3==r20 address is 20 | |||
ldi YH, 0 ;[-14] | |||
ldi cnt, 2 ;[-13] | |||
; rjmp usbSendAndReti fallthrough | |||
;usbSend: | |||
;pointer to data in 'Y' | |||
;number of bytes in 'cnt' -- including sync byte [range 2 ... 12] | |||
;uses: x1...x4, btcnt, shift, cnt, Y | |||
;Numbers in brackets are time since first bit of sync pattern is sent | |||
;We don't match the transfer rate exactly (don't insert leap cycles every third | |||
;byte) because the spec demands only 1.5% precision anyway. | |||
usbSendAndReti: ; 12 cycles until SOP | |||
in x2, USBDDR ;[-12] | |||
ori x2, USBMASK ;[-11] | |||
sbi USBOUT, USBMINUS;[-10] prepare idle state; D+ and D- must have been 0 (no pullups) | |||
in x1, USBOUT ;[-8] port mirror for tx loop | |||
out USBDDR, x2 ;[-7] <- acquire bus | |||
; need not init x2 (bitstuff history) because sync starts with 0 | |||
ldi x4, USBMASK ;[-6] exor mask | |||
ldi shift, 0x80 ;[-5] sync byte is first byte sent | |||
txByteLoop: | |||
ldi bitcnt, 0x49 ;[-4] [10] binary 01001001 | |||
txBitLoop: | |||
sbrs shift, 0 ;[-3] [10] [11] | |||
eor x1, x4 ;[-2] [11] [12] | |||
out USBOUT, x1 ;[-1] [12] [13] <-- out N | |||
ror shift ;[0] [13] [14] | |||
ror x2 ;[1] | |||
didStuffN: | |||
nop2 ;[2] | |||
nop ;[4] | |||
cpi x2, 0xfc ;[5] | |||
brcc bitstuffN ;[6] | |||
lsr bitcnt ;[7] | |||
brcc txBitLoop ;[8] | |||
brne txBitLoop ;[9] | |||
sbrs shift, 0 ;[10] | |||
eor x1, x4 ;[11] | |||
didStuff7: | |||
out USBOUT, x1 ;[-1] [13] <-- out 7 | |||
ror shift ;[0] [14] | |||
ror x2 ;[1] | |||
nop ;[2] | |||
cpi x2, 0xfc ;[3] | |||
brcc bitstuff7 ;[4] | |||
ld shift, y+ ;[5] | |||
dec cnt ;[7] | |||
brne txByteLoop ;[8] | |||
;make SE0: | |||
cbr x1, USBMASK ;[9] prepare SE0 [spec says EOP may be 25 to 30 cycles] | |||
lds x2, usbNewDeviceAddr;[10] | |||
lsl x2 ;[12] we compare with left shifted address | |||
out USBOUT, x1 ;[13] <-- out SE0 -- from now 2 bits = 22 cycles until bus idle | |||
subi YL, 20 + 2 ;[0] Only assign address on data packets, not ACK/NAK in x3 | |||
sbci YH, 0 ;[1] | |||
;2006-03-06: moved transfer of new address to usbDeviceAddr from C-Code to asm: | |||
;set address only after data packet was sent, not after handshake | |||
breq skipAddrAssign ;[2] | |||
sts usbDeviceAddr, x2; if not skipped: SE0 is one cycle longer | |||
skipAddrAssign: | |||
;end of usbDeviceAddress transfer | |||
ldi x2, 1<<USB_INTR_PENDING_BIT;[4] int0 occurred during TX -- clear pending flag | |||
USB_STORE_PENDING(x2) ;[5] | |||
ori x1, USBIDLE ;[6] | |||
in x2, USBDDR ;[7] | |||
cbr x2, USBMASK ;[8] set both pins to input | |||
mov x3, x1 ;[9] | |||
cbr x3, USBMASK ;[10] configure no pullup on both pins | |||
ldi x4, 5 ;[11] | |||
se0Delay: | |||
dec x4 ;[12] [15] [18] [21] [24] | |||
brne se0Delay ;[13] [16] [19] [22] [25] | |||
out USBOUT, x1 ;[26] <-- out J (idle) -- end of SE0 (EOP signal) | |||
out USBDDR, x2 ;[27] <-- release bus now | |||
out USBOUT, x3 ;[28] <-- ensure no pull-up resistors are active | |||
rjmp doReturn |
@@ -0,0 +1,144 @@ | |||
/* Name: usbportability.h | |||
* Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers | |||
* Author: Christian Starkjohann | |||
* Creation Date: 2008-06-17 | |||
* Tabsize: 4 | |||
* Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH | |||
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) | |||
* This Revision: $Id: usbportability.h 785 2010-05-30 17:57:07Z cs $ | |||
*/ | |||
/* | |||
General Description: | |||
This header is intended to contain all (or at least most of) the compiler | |||
and library dependent stuff. The C code is written for avr-gcc and avr-libc. | |||
The API of other development environments is converted to gcc's and avr-libc's | |||
API by means of defines. | |||
This header also contains all system includes since they depend on the | |||
development environment. | |||
Thanks to Oleg Semyonov for his help with the IAR tools port! | |||
*/ | |||
#ifndef __usbportability_h_INCLUDED__ | |||
#define __usbportability_h_INCLUDED__ | |||
/* We check explicitly for IAR and CodeVision. Default is avr-gcc/avr-libc. */ | |||
/* ------------------------------------------------------------------------- */ | |||
#if defined __IAR_SYSTEMS_ICC__ || defined __IAR_SYSTEMS_ASM__ /* check for IAR */ | |||
/* ------------------------------------------------------------------------- */ | |||
#ifndef ENABLE_BIT_DEFINITIONS | |||
# define ENABLE_BIT_DEFINITIONS 1 /* Enable bit definitions */ | |||
#endif | |||
/* Include IAR headers */ | |||
#include <ioavr.h> | |||
#ifndef __IAR_SYSTEMS_ASM__ | |||
# include <inavr.h> | |||
#endif | |||
#define __attribute__(arg) /* not supported on IAR */ | |||
#ifdef __IAR_SYSTEMS_ASM__ | |||
# define __ASSEMBLER__ /* IAR does not define standard macro for asm */ | |||
#endif | |||
#ifdef __HAS_ELPM__ | |||
# define PROGMEM __farflash | |||
#else | |||
# define PROGMEM __flash | |||
#endif | |||
#define USB_READ_FLASH(addr) (*(PROGMEM char *)(addr)) | |||
/* The following definitions are not needed by the driver, but may be of some | |||
* help if you port a gcc based project to IAR. | |||
*/ | |||
#define cli() __disable_interrupt() | |||
#define sei() __enable_interrupt() | |||
#define wdt_reset() __watchdog_reset() | |||
#define _BV(x) (1 << (x)) | |||
/* assembler compatibility macros */ | |||
#define nop2 rjmp $+2 /* jump to next instruction */ | |||
#define XL r26 | |||
#define XH r27 | |||
#define YL r28 | |||
#define YH r29 | |||
#define ZL r30 | |||
#define ZH r31 | |||
#define lo8(x) LOW(x) | |||
#define hi8(x) (((x)>>8) & 0xff) /* not HIGH to allow XLINK to make a proper range check */ | |||
/* Depending on the device you use, you may get problems with the way usbdrv.h | |||
* handles the differences between devices. Since IAR does not use #defines | |||
* for MCU registers, we can't check for the existence of a particular | |||
* register with an #ifdef. If the autodetection mechanism fails, include | |||
* definitions for the required USB_INTR_* macros in your usbconfig.h. See | |||
* usbconfig-prototype.h and usbdrv.h for details. | |||
*/ | |||
/* ------------------------------------------------------------------------- */ | |||
#elif __CODEVISIONAVR__ /* check for CodeVision AVR */ | |||
/* ------------------------------------------------------------------------- */ | |||
/* This port is not working (yet) */ | |||
/* #define F_CPU _MCU_CLOCK_FREQUENCY_ seems to be defined automatically */ | |||
#include <io.h> | |||
#include <delay.h> | |||
#define __attribute__(arg) /* not supported on IAR */ | |||
#define PROGMEM __flash | |||
#define USB_READ_FLASH(addr) (*(PROGMEM char *)(addr)) | |||
#ifndef __ASSEMBLER__ | |||
static inline void cli(void) | |||
{ | |||
#asm("cli"); | |||
} | |||
static inline void sei(void) | |||
{ | |||
#asm("sei"); | |||
} | |||
#endif | |||
#define _delay_ms(t) delay_ms(t) | |||
#define _BV(x) (1 << (x)) | |||
#define USB_CFG_USE_SWITCH_STATEMENT 1 /* macro for if() cascase fails for unknown reason */ | |||
#define macro .macro | |||
#define endm .endmacro | |||
#define nop2 rjmp .+0 /* jump to next instruction */ | |||
/* ------------------------------------------------------------------------- */ | |||
#else /* default development environment is avr-gcc/avr-libc */ | |||
/* ------------------------------------------------------------------------- */ | |||
#include <avr/io.h> | |||
#ifdef __ASSEMBLER__ | |||
# define _VECTOR(N) __vector_ ## N /* io.h does not define this for asm */ | |||
#else | |||
# include <avr/pgmspace.h> | |||
#endif | |||
#if USB_CFG_DRIVER_FLASH_PAGE | |||
# define USB_READ_FLASH(addr) pgm_read_byte_far(((long)USB_CFG_DRIVER_FLASH_PAGE << 16) | (long)(addr)) | |||
#else | |||
# define USB_READ_FLASH(addr) pgm_read_byte(addr) | |||
#endif | |||
#define macro .macro | |||
#define endm .endm | |||
#define nop2 rjmp .+0 /* jump to next instruction */ | |||
#endif /* development environment */ | |||
/* for conveniecne, ensure that PRG_RDB exists */ | |||
#ifndef PRG_RDB | |||
# define PRG_RDB(addr) USB_READ_FLASH(addr) | |||
#endif | |||
#endif /* __usbportability_h_INCLUDED__ */ |