Merge branch 'qmk-pre-merge-2021-09-12' into qmk-merge-2021-09-12
This commit is contained in:
+11
-7
@@ -50,14 +50,18 @@ MCUFLAGS += -D__$(ARM_ATSAM)__
|
||||
# For a directory that has spaces, enclose it in quotes.
|
||||
EXTRALIBDIRS =
|
||||
|
||||
cpfirmware: warn-arm_atsam
|
||||
.INTERMEDIATE: warn-arm_atsam
|
||||
warn-arm_atsam: $(FIRMWARE_FORMAT)
|
||||
$(info @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@)
|
||||
$(info This MCU support package has a lack of support from the upstream provider (Massdrop).)
|
||||
$(info There are currently questions about valid licensing, and at this stage it's likely)
|
||||
$(info their boards and supporting code will be removed from QMK in the near future. Please)
|
||||
$(info contact Massdrop for support, and encourage them to align their future board design)
|
||||
$(info choices to gain proper license compatibility with QMK.)
|
||||
$(info @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@)
|
||||
|
||||
# Convert hex to bin.
|
||||
bin: $(BUILD_DIR)/$(TARGET).hex
|
||||
$(OBJCOPY) -Iihex -Obinary $(BUILD_DIR)/$(TARGET).hex $(BUILD_DIR)/$(TARGET).bin
|
||||
$(COPY) $(BUILD_DIR)/$(TARGET).bin $(TARGET).bin;
|
||||
|
||||
flash: bin
|
||||
ifneq ($(strip $(PROGRAM_CMD)),)
|
||||
$(PROGRAM_CMD)
|
||||
else
|
||||
$(PRINT_OK); $(SILENT) || printf "$(MSG_FLASH_ARCH)"
|
||||
endif
|
||||
|
||||
+21
-175
@@ -12,8 +12,6 @@ HEX = $(OBJCOPY) -O $(FORMAT) -R .eeprom -R .fuse -R .lock -R .signature
|
||||
EEP = $(OBJCOPY) -j .eeprom --set-section-flags=.eeprom="alloc,load" --change-section-lma .eeprom=0 --no-change-warnings -O $(FORMAT)
|
||||
BIN =
|
||||
|
||||
COMMON_VPATH += $(DRIVER_PATH)/avr
|
||||
|
||||
COMPILEFLAGS += -funsigned-char
|
||||
COMPILEFLAGS += -funsigned-bitfields
|
||||
COMPILEFLAGS += -ffunction-sections
|
||||
@@ -89,157 +87,15 @@ DEBUG_PORT = 4242
|
||||
DEBUG_HOST = localhost
|
||||
|
||||
#============================================================================
|
||||
# Autodetect teensy loader
|
||||
ifndef TEENSY_LOADER_CLI
|
||||
ifneq (, $(shell which teensy-loader-cli 2>/dev/null))
|
||||
TEENSY_LOADER_CLI ?= teensy-loader-cli
|
||||
else
|
||||
TEENSY_LOADER_CLI ?= teensy_loader_cli
|
||||
endif
|
||||
endif
|
||||
|
||||
define EXEC_TEENSY
|
||||
$(TEENSY_LOADER_CLI) -mmcu=$(MCU) -w -v $(BUILD_DIR)/$(TARGET).hex
|
||||
endef
|
||||
|
||||
teensy: $(BUILD_DIR)/$(TARGET).hex check-size cpfirmware
|
||||
$(call EXEC_TEENSY)
|
||||
|
||||
DFU_PROGRAMMER ?= dfu-programmer
|
||||
GREP ?= grep
|
||||
|
||||
|
||||
define EXEC_DFU
|
||||
if [ "$(1)" ]; then \
|
||||
echo "Flashing '$(1)' for EE_HANDS split keyboard support." ;\
|
||||
fi; \
|
||||
if ! $(DFU_PROGRAMMER) $(MCU) get bootloader-version >/dev/null 2>/dev/null; then\
|
||||
printf "$(MSG_BOOTLOADER_NOT_FOUND_QUICK_RETRY)" ;\
|
||||
sleep $(BOOTLOADER_RETRY_TIME) ;\
|
||||
while ! $(DFU_PROGRAMMER) $(MCU) get bootloader-version >/dev/null 2>/dev/null; do\
|
||||
printf "." ;\
|
||||
sleep $(BOOTLOADER_RETRY_TIME) ;\
|
||||
done ;\
|
||||
printf "\n" ;\
|
||||
fi; \
|
||||
$(DFU_PROGRAMMER) $(MCU) get bootloader-version ;\
|
||||
if $(DFU_PROGRAMMER) --version 2>&1 | $(GREP) -q 0.7 ; then\
|
||||
$(DFU_PROGRAMMER) $(MCU) erase --force; \
|
||||
if [ "$(1)" ]; then \
|
||||
$(DFU_PROGRAMMER) $(MCU) flash --force --eeprom $(QUANTUM_PATH)/split_common/$(1);\
|
||||
fi; \
|
||||
$(DFU_PROGRAMMER) $(MCU) flash --force $(BUILD_DIR)/$(TARGET).hex;\
|
||||
else \
|
||||
$(DFU_PROGRAMMER) $(MCU) erase; \
|
||||
if [ "$(1)" ]; then \
|
||||
$(DFU_PROGRAMMER) $(MCU) flash-eeprom $(QUANTUM_PATH)/split_common/$(1);\
|
||||
fi; \
|
||||
$(DFU_PROGRAMMER) $(MCU) flash $(BUILD_DIR)/$(TARGET).hex;\
|
||||
fi; \
|
||||
$(DFU_PROGRAMMER) $(MCU) reset
|
||||
endef
|
||||
|
||||
dfu: $(BUILD_DIR)/$(TARGET).hex cpfirmware check-size
|
||||
$(call EXEC_DFU)
|
||||
|
||||
dfu-start:
|
||||
$(DFU_PROGRAMMER) $(MCU) reset
|
||||
$(DFU_PROGRAMMER) $(MCU) start
|
||||
|
||||
dfu-ee: $(BUILD_DIR)/$(TARGET).hex $(BUILD_DIR)/$(TARGET).eep
|
||||
if $(DFU_PROGRAMMER) --version 2>&1 | $(GREP) -q 0.7 ; then\
|
||||
$(DFU_PROGRAMMER) $(MCU) flash --force --eeprom $(BUILD_DIR)/$(TARGET).eep;\
|
||||
else\
|
||||
$(DFU_PROGRAMMER) $(MCU) flash-eeprom $(BUILD_DIR)/$(TARGET).eep;\
|
||||
fi
|
||||
$(DFU_PROGRAMMER) $(MCU) reset
|
||||
|
||||
dfu-split-left: $(BUILD_DIR)/$(TARGET).hex cpfirmware check-size
|
||||
$(call EXEC_DFU,eeprom-lefthand.eep)
|
||||
|
||||
dfu-split-right: $(BUILD_DIR)/$(TARGET).hex cpfirmware check-size
|
||||
$(call EXEC_DFU,eeprom-righthand.eep)
|
||||
|
||||
AVRDUDE_PROGRAMMER ?= avrdude
|
||||
|
||||
define EXEC_AVRDUDE
|
||||
list_devices() { \
|
||||
if $(GREP) -q -s icrosoft /proc/version; then \
|
||||
wmic.exe path Win32_SerialPort get DeviceID 2>/dev/null | LANG=C perl -pne 's/COM(\d+)/COM.($$1-1)/e' | sed 's!COM!/dev/ttyS!' | xargs echo -n | sort; \
|
||||
elif [ "`uname`" = "FreeBSD" ]; then \
|
||||
ls /dev/tty* | grep -v '\.lock$$' | grep -v '\.init$$'; \
|
||||
else \
|
||||
ls /dev/tty*; \
|
||||
fi; \
|
||||
}; \
|
||||
USB= ;\
|
||||
printf "Detecting USB port, reset your controller now."; \
|
||||
TMP1=`mktemp`; \
|
||||
TMP2=`mktemp`; \
|
||||
list_devices > $$TMP1; \
|
||||
while [ -z "$$USB" ]; do \
|
||||
sleep $(BOOTLOADER_RETRY_TIME); \
|
||||
printf "."; \
|
||||
list_devices > $$TMP2; \
|
||||
USB=`comm -13 $$TMP1 $$TMP2 | $(GREP) -o '/dev/tty.*'`; \
|
||||
mv $$TMP2 $$TMP1; \
|
||||
done; \
|
||||
rm $$TMP1; \
|
||||
echo ""; \
|
||||
echo "Device $$USB has appeared; assuming it is the controller."; \
|
||||
if $(GREP) -q -s 'MINGW\|MSYS\|icrosoft' /proc/version; then \
|
||||
USB=`echo "$$USB" | LANG=C perl -pne 's/\/dev\/ttyS(\d+)/COM.($$1+1)/e'`; \
|
||||
echo "Remapped USB port to $$USB"; \
|
||||
sleep 1; \
|
||||
else \
|
||||
printf "Waiting for $$USB to become writable."; \
|
||||
while [ ! -w "$$USB" ]; do sleep $(BOOTLOADER_RETRY_TIME); printf "."; done; echo ""; \
|
||||
fi; \
|
||||
if [ -z "$(1)" ]; then \
|
||||
$(AVRDUDE_PROGRAMMER) -p $(MCU) -c avr109 -P $$USB -U flash:w:$(BUILD_DIR)/$(TARGET).hex; \
|
||||
else \
|
||||
$(AVRDUDE_PROGRAMMER) -p $(MCU) -c avr109 -P $$USB -U flash:w:$(BUILD_DIR)/$(TARGET).hex -U eeprom:w:$(QUANTUM_PATH)/split_common/$(1); \
|
||||
fi
|
||||
endef
|
||||
|
||||
avrdude: $(BUILD_DIR)/$(TARGET).hex check-size cpfirmware
|
||||
$(call EXEC_AVRDUDE)
|
||||
|
||||
avrdude-loop: $(BUILD_DIR)/$(TARGET).hex check-size cpfirmware
|
||||
while true; do \
|
||||
$(call EXEC_AVRDUDE) ; \
|
||||
done
|
||||
|
||||
avrdude-split-left: $(BUILD_DIR)/$(TARGET).hex check-size cpfirmware
|
||||
$(call EXEC_AVRDUDE,eeprom-lefthand.eep)
|
||||
|
||||
avrdude-split-right: $(BUILD_DIR)/$(TARGET).hex check-size cpfirmware
|
||||
$(call EXEC_AVRDUDE,eeprom-righthand.eep)
|
||||
|
||||
define EXEC_USBASP
|
||||
$(AVRDUDE_PROGRAMMER) -p $(AVRDUDE_MCU) -c usbasp -U flash:w:$(BUILD_DIR)/$(TARGET).hex
|
||||
endef
|
||||
|
||||
usbasp: $(BUILD_DIR)/$(TARGET).hex check-size cpfirmware
|
||||
$(call EXEC_USBASP)
|
||||
|
||||
BOOTLOADHID_PROGRAMMER ?= bootloadHID
|
||||
|
||||
define EXEC_BOOTLOADHID
|
||||
# bootloadHid executable has no cross platform detect methods
|
||||
# so keep running bootloadHid if the output contains "The specified device was not found"
|
||||
until $(BOOTLOADHID_PROGRAMMER) -r $(BUILD_DIR)/$(TARGET).hex 2>&1 | tee /dev/stderr | grep -v "device was not found"; do\
|
||||
printf "$(MSG_BOOTLOADER_NOT_FOUND)" ;\
|
||||
sleep 5 ;\
|
||||
done
|
||||
endef
|
||||
|
||||
bootloadHID: $(BUILD_DIR)/$(TARGET).hex check-size cpfirmware
|
||||
$(call EXEC_BOOTLOADHID)
|
||||
|
||||
# Convert hex to bin.
|
||||
bin: $(BUILD_DIR)/$(TARGET).hex
|
||||
ifeq ($(BOOTLOADER),lufa-ms)
|
||||
$(eval BIN_PADDING=$(shell n=`expr 32768 - $(BOOTLOADER_SIZE)` && echo $$(($$n)) || echo 0))
|
||||
$(OBJCOPY) -Iihex -Obinary $(BUILD_DIR)/$(TARGET).hex $(BUILD_DIR)/$(TARGET).bin --pad-to $(BIN_PADDING)
|
||||
else
|
||||
$(OBJCOPY) -Iihex -Obinary $(BUILD_DIR)/$(TARGET).hex $(BUILD_DIR)/$(TARGET).bin
|
||||
endif
|
||||
$(COPY) $(BUILD_DIR)/$(TARGET).bin $(TARGET).bin;
|
||||
|
||||
# copy bin to FLASH.bin
|
||||
@@ -295,39 +151,29 @@ extcoff: $(BUILD_DIR)/$(TARGET).elf
|
||||
@$(SECHO) $(MSG_EXTENDED_COFF) $(BUILD_DIR)/$(TARGET).cof
|
||||
$(COFFCONVERT) -O coff-ext-avr $< $(BUILD_DIR)/$(TARGET).cof
|
||||
|
||||
bootloader:
|
||||
ifneq ($(strip $(BOOTLOADER)), qmk-dfu)
|
||||
$(error Please set BOOTLOADER = qmk-dfu first!)
|
||||
ifeq ($(strip $(BOOTLOADER)), qmk-dfu)
|
||||
QMK_BOOTLOADER_TYPE = DFU
|
||||
else ifeq ($(strip $(BOOTLOADER)), qmk-hid)
|
||||
QMK_BOOTLOADER_TYPE = HID
|
||||
endif
|
||||
make -C lib/lufa/Bootloaders/DFU/ clean
|
||||
$(QMK_BIN) generate-dfu-header --quiet --keyboard $(KEYBOARD) --output lib/lufa/Bootloaders/DFU/Keyboard.h
|
||||
$(eval MAX_SIZE=$(shell n=`$(CC) -E -mmcu=$(MCU) $(CFLAGS) $(OPT_DEFS) tmk_core/common/avr/bootloader_size.c 2> /dev/null | sed -ne 's/\r//;/^#/n;/^AVR_SIZE:/,$${s/^AVR_SIZE: //;p;}'` && echo $$(($$n)) || echo 0))
|
||||
|
||||
bootloader:
|
||||
ifeq ($(strip $(QMK_BOOTLOADER_TYPE)),)
|
||||
$(error Please set BOOTLOADER to "qmk-dfu" or "qmk-hid" first!)
|
||||
else
|
||||
make -C lib/lufa/Bootloaders/$(QMK_BOOTLOADER_TYPE)/ clean
|
||||
$(QMK_BIN) generate-dfu-header --quiet --keyboard $(KEYBOARD) --output lib/lufa/Bootloaders/$(QMK_BOOTLOADER_TYPE)/Keyboard.h
|
||||
$(eval MAX_SIZE=$(shell n=`$(CC) -E -mmcu=$(MCU) -D__ASSEMBLER__ $(CFLAGS) $(OPT_DEFS) tmk_core/common/avr/bootloader_size.c 2> /dev/null | sed -ne 's/\r//;/^#/n;/^AVR_SIZE:/,$${s/^AVR_SIZE: //;p;}'` && echo $$(($$n)) || echo 0))
|
||||
$(eval PROGRAM_SIZE_KB=$(shell n=`expr $(MAX_SIZE) / 1024` && echo $$(($$n)) || echo 0))
|
||||
$(eval BOOT_SECTION_SIZE_KB=$(shell n=`expr $(BOOTLOADER_SIZE) / 1024` && echo $$(($$n)) || echo 0))
|
||||
$(eval FLASH_SIZE_KB=$(shell n=`expr $(PROGRAM_SIZE_KB) + $(BOOT_SECTION_SIZE_KB)` && echo $$(($$n)) || echo 0))
|
||||
make -C lib/lufa/Bootloaders/DFU/ MCU=$(MCU) ARCH=$(ARCH) F_CPU=$(F_CPU) FLASH_SIZE_KB=$(FLASH_SIZE_KB) BOOT_SECTION_SIZE_KB=$(BOOT_SECTION_SIZE_KB)
|
||||
printf "BootloaderDFU.hex copied to $(TARGET)_bootloader.hex\n"
|
||||
cp lib/lufa/Bootloaders/DFU/BootloaderDFU.hex $(TARGET)_bootloader.hex
|
||||
make -C lib/lufa/Bootloaders/$(QMK_BOOTLOADER_TYPE)/ MCU=$(MCU) ARCH=$(ARCH) F_CPU=$(F_CPU) FLASH_SIZE_KB=$(FLASH_SIZE_KB) BOOT_SECTION_SIZE_KB=$(BOOT_SECTION_SIZE_KB)
|
||||
printf "Bootloader$(QMK_BOOTLOADER_TYPE).hex copied to $(TARGET)_bootloader.hex\n"
|
||||
cp lib/lufa/Bootloaders/$(QMK_BOOTLOADER_TYPE)/Bootloader$(QMK_BOOTLOADER_TYPE).hex $(TARGET)_bootloader.hex
|
||||
endif
|
||||
|
||||
production: $(BUILD_DIR)/$(TARGET).hex bootloader cpfirmware
|
||||
@cat $(BUILD_DIR)/$(TARGET).hex | awk '/^:00000001FF/ == 0' > $(TARGET)_production.hex
|
||||
@cat $(TARGET)_bootloader.hex >> $(TARGET)_production.hex
|
||||
echo "File sizes:"
|
||||
$(SIZE) $(TARGET).hex $(TARGET)_bootloader.hex $(TARGET)_production.hex
|
||||
|
||||
flash: $(BUILD_DIR)/$(TARGET).hex check-size cpfirmware
|
||||
ifneq ($(strip $(PROGRAM_CMD)),)
|
||||
$(PROGRAM_CMD)
|
||||
else ifeq ($(strip $(BOOTLOADER)), caterina)
|
||||
$(call EXEC_AVRDUDE)
|
||||
else ifeq ($(strip $(BOOTLOADER)), halfkay)
|
||||
$(call EXEC_TEENSY)
|
||||
else ifeq (dfu,$(findstring dfu,$(BOOTLOADER)))
|
||||
$(call EXEC_DFU)
|
||||
else ifeq ($(strip $(BOOTLOADER)), USBasp)
|
||||
$(call EXEC_USBASP)
|
||||
else ifeq ($(strip $(BOOTLOADER)), bootloadHID)
|
||||
$(call EXEC_BOOTLOADHID)
|
||||
else
|
||||
$(PRINT_OK); $(SILENT) || printf "$(MSG_FLASH_BOOTLOADER)"
|
||||
endif
|
||||
|
||||
+21
-104
@@ -67,9 +67,9 @@ else ifneq ("$(wildcard $(KEYBOARD_PATH_2)/boards/$(BOARD)/board.mk)","")
|
||||
else ifneq ("$(wildcard $(KEYBOARD_PATH_1)/boards/$(BOARD)/board.mk)","")
|
||||
BOARD_PATH = $(KEYBOARD_PATH_1)
|
||||
BOARD_MK += $(KEYBOARD_PATH_1)/boards/$(BOARD)/board.mk
|
||||
else ifneq ("$(wildcard $(TOP_DIR)/platforms/chibios/$(BOARD)/board/board.mk)","")
|
||||
BOARD_PATH = $(TOP_DIR)/platforms/chibios/$(BOARD)
|
||||
BOARD_MK += $(TOP_DIR)/platforms/chibios/$(BOARD)/board/board.mk
|
||||
else ifneq ("$(wildcard $(TOP_DIR)/platforms/chibios/boards/$(BOARD)/board/board.mk)","")
|
||||
BOARD_PATH = $(TOP_DIR)/platforms/chibios/boards/$(BOARD)
|
||||
BOARD_MK += $(TOP_DIR)/platforms/chibios/boards/$(BOARD)/board/board.mk
|
||||
KEYBOARD_PATHS += $(BOARD_PATH)/configs
|
||||
ifneq ("$(wildcard $(BOARD_PATH)/rules.mk)","")
|
||||
include $(BOARD_PATH)/rules.mk
|
||||
@@ -124,10 +124,10 @@ else ifneq ("$(wildcard $(KEYBOARD_PATH_2)/chconf.h)","")
|
||||
CHCONFDIR = $(KEYBOARD_PATH_2)
|
||||
else ifneq ("$(wildcard $(KEYBOARD_PATH_1)/chconf.h)","")
|
||||
CHCONFDIR = $(KEYBOARD_PATH_1)
|
||||
else ifneq ("$(wildcard $(TOP_DIR)/platforms/chibios/$(BOARD)/configs/chconf.h)","")
|
||||
CHCONFDIR = $(TOP_DIR)/platforms/chibios/$(BOARD)/configs
|
||||
else ifneq ("$(wildcard $(TOP_DIR)/platforms/chibios/common/configs/chconf.h)","")
|
||||
CHCONFDIR = $(TOP_DIR)/platforms/chibios/common/configs
|
||||
else ifneq ("$(wildcard $(TOP_DIR)/platforms/chibios/boards/$(BOARD)/configs/chconf.h)","")
|
||||
CHCONFDIR = $(TOP_DIR)/platforms/chibios/boards/$(BOARD)/configs
|
||||
else ifneq ("$(wildcard $(TOP_DIR)/platforms/boards/chibios/common/configs/chconf.h)","")
|
||||
CHCONFDIR = $(TOP_DIR)/platforms/chibios/boards/common/configs
|
||||
endif
|
||||
|
||||
ifneq ("$(wildcard $(KEYBOARD_PATH_5)/halconf.h)","")
|
||||
@@ -140,10 +140,10 @@ else ifneq ("$(wildcard $(KEYBOARD_PATH_2)/halconf.h)","")
|
||||
HALCONFDIR = $(KEYBOARD_PATH_2)
|
||||
else ifneq ("$(wildcard $(KEYBOARD_PATH_1)/halconf.h)","")
|
||||
HALCONFDIR = $(KEYBOARD_PATH_1)
|
||||
else ifneq ("$(wildcard $(TOP_DIR)/platforms/chibios/$(BOARD)/configs/halconf.h)","")
|
||||
HALCONFDIR = $(TOP_DIR)/platforms/chibios/$(BOARD)/configs
|
||||
else ifneq ("$(wildcard $(TOP_DIR)/platforms/chibios/common/configs/halconf.h)","")
|
||||
HALCONFDIR = $(TOP_DIR)/platforms/chibios/common/configs
|
||||
else ifneq ("$(wildcard $(TOP_DIR)/platforms/chibios/boards/$(BOARD)/configs/halconf.h)","")
|
||||
HALCONFDIR = $(TOP_DIR)/platforms/chibios/boards/$(BOARD)/configs
|
||||
else ifneq ("$(wildcard $(TOP_DIR)/platforms/chibios/boards/common/configs/halconf.h)","")
|
||||
HALCONFDIR = $(TOP_DIR)/platforms/chibios/boards/common/configs
|
||||
endif
|
||||
|
||||
# HAL-OSAL files (optional).
|
||||
@@ -190,10 +190,11 @@ else ifneq ("$(wildcard $(KEYBOARD_PATH_2)/ld/$(MCU_LDSCRIPT).ld)","")
|
||||
LDSCRIPT = $(KEYBOARD_PATH_2)/ld/$(MCU_LDSCRIPT).ld
|
||||
else ifneq ("$(wildcard $(KEYBOARD_PATH_1)/ld/$(MCU_LDSCRIPT).ld)","")
|
||||
LDSCRIPT = $(KEYBOARD_PATH_1)/ld/$(MCU_LDSCRIPT).ld
|
||||
else ifneq ("$(wildcard $(TOP_DIR)/platforms/chibios/$(BOARD)/ld/$(MCU_LDSCRIPT).ld)","")
|
||||
LDSCRIPT = $(TOP_DIR)/platforms/chibios/$(BOARD)/ld/$(MCU_LDSCRIPT).ld
|
||||
else ifneq ("$(wildcard $(TOP_DIR)/platforms/chibios/common/ld/$(MCU_LDSCRIPT).ld)","")
|
||||
LDSCRIPT = $(TOP_DIR)/platforms/chibios/common/ld/$(MCU_LDSCRIPT).ld
|
||||
else ifneq ("$(wildcard $(TOP_DIR)/platforms/chibios/boards/$(BOARD)/ld/$(MCU_LDSCRIPT).ld)","")
|
||||
LDFLAGS += -L$(TOP_DIR)/platforms/chibios/boards/$(BOARD)/ld
|
||||
LDSCRIPT = $(TOP_DIR)/platforms/chibios/boards/$(BOARD)/ld/$(MCU_LDSCRIPT).ld
|
||||
else ifneq ("$(wildcard $(TOP_DIR)/platforms/chibios/boards/common/ld/$(MCU_LDSCRIPT).ld)","")
|
||||
LDSCRIPT = $(TOP_DIR)/platforms/chibios/boards/common/ld/$(MCU_LDSCRIPT).ld
|
||||
else ifneq ("$(wildcard $(STARTUPLD_CONTRIB)/$(MCU_LDSCRIPT).ld)","")
|
||||
LDSCRIPT = $(STARTUPLD_CONTRIB)/$(MCU_LDSCRIPT).ld
|
||||
USE_CHIBIOS_CONTRIB = yes
|
||||
@@ -210,7 +211,8 @@ CHIBISRC = $(STARTUPSRC) \
|
||||
$(BOARDSRC) \
|
||||
$(STREAMSSRC) \
|
||||
$(CHIBIOS)/os/various/syscalls.c \
|
||||
$(PLATFORM_COMMON_DIR)/syscall-fallbacks.c
|
||||
$(PLATFORM_COMMON_DIR)/syscall-fallbacks.c \
|
||||
$(PLATFORM_COMMON_DIR)/wait.c
|
||||
|
||||
# Ensure the ASM files are not subjected to LTO -- it'll strip out interrupt handlers otherwise.
|
||||
QUANTUM_LIB_SRC += $(STARTUPASM) $(PORTASM) $(OSALASM) $(PLATFORMASM)
|
||||
@@ -218,8 +220,8 @@ QUANTUM_LIB_SRC += $(STARTUPASM) $(PORTASM) $(OSALASM) $(PLATFORMASM)
|
||||
CHIBISRC := $(patsubst $(TOP_DIR)/%,%,$(CHIBISRC))
|
||||
|
||||
EXTRAINCDIRS += $(CHIBIOS)/os/license $(CHIBIOS)/os/oslib/include \
|
||||
$(TOP_DIR)/platforms/chibios/$(BOARD)/configs \
|
||||
$(TOP_DIR)/platforms/chibios/common/configs \
|
||||
$(TOP_DIR)/platforms/chibios/boards/$(BOARD)/configs \
|
||||
$(TOP_DIR)/platforms/chibios/boards/common/configs \
|
||||
$(HALCONFDIR) $(CHCONFDIR) \
|
||||
$(STARTUPINC) $(KERNINC) $(PORTINC) $(OSALINC) \
|
||||
$(HALINC) $(PLATFORMINC) $(BOARDINC) $(TESTINC) \
|
||||
@@ -240,7 +242,7 @@ else ifneq ("$(wildcard $(KEYBOARD_PATH_2)/halconf_community.h)","")
|
||||
USE_CHIBIOS_CONTRIB = yes
|
||||
else ifneq ("$(wildcard $(KEYBOARD_PATH_1)/halconf_community.h)","")
|
||||
USE_CHIBIOS_CONTRIB = yes
|
||||
else ifneq ("$(wildcard $(TOP_DIR)/platforms/chibios/$(BOARD)/configs/halconf_community.h)","")
|
||||
else ifneq ("$(wildcard $(TOP_DIR)/platforms/chibios/boards/$(BOARD)/configs/halconf_community.h)","")
|
||||
USE_CHIBIOS_CONTRIB = yes
|
||||
endif
|
||||
|
||||
@@ -277,8 +279,6 @@ HEX = $(OBJCOPY) -O $(FORMAT)
|
||||
EEP =
|
||||
BIN = $(OBJCOPY) -O binary
|
||||
|
||||
COMMON_VPATH += $(DRIVER_PATH)/chibios
|
||||
|
||||
THUMBFLAGS = -DTHUMB_PRESENT -mno-thumb-interwork -DTHUMB_NO_INTERWORKING -mthumb -DTHUMB
|
||||
|
||||
COMPILEFLAGS += -fomit-frame-pointer
|
||||
@@ -325,91 +325,8 @@ MCUFLAGS = -mcpu=$(MCU)
|
||||
|
||||
DEBUG = gdb
|
||||
|
||||
DFU_ARGS ?=
|
||||
ifneq ("$(SERIAL)","")
|
||||
DFU_ARGS += -S $(SERIAL)
|
||||
endif
|
||||
|
||||
ST_LINK_ARGS ?=
|
||||
ST_FLASH_ARGS ?=
|
||||
|
||||
# List any extra directories to look for libraries here.
|
||||
EXTRALIBDIRS = $(RULESPATH)/ld
|
||||
|
||||
DFU_UTIL ?= dfu-util
|
||||
ST_LINK_CLI ?= st-link_cli
|
||||
ST_FLASH ?= st-flash
|
||||
|
||||
define EXEC_DFU_UTIL
|
||||
if ! $(DFU_UTIL) -l | grep -q "Found DFU"; then \
|
||||
printf "$(MSG_BOOTLOADER_NOT_FOUND_QUICK_RETRY)" ;\
|
||||
sleep $(BOOTLOADER_RETRY_TIME) ;\
|
||||
while ! $(DFU_UTIL) -l | grep -q "Found DFU"; do \
|
||||
printf "." ;\
|
||||
sleep $(BOOTLOADER_RETRY_TIME) ;\
|
||||
done ;\
|
||||
printf "\n" ;\
|
||||
fi
|
||||
$(DFU_UTIL) $(DFU_ARGS) -D $(BUILD_DIR)/$(TARGET).bin
|
||||
endef
|
||||
|
||||
dfu-util: $(BUILD_DIR)/$(TARGET).bin cpfirmware sizeafter
|
||||
$(call EXEC_DFU_UTIL)
|
||||
|
||||
# Legacy alias
|
||||
dfu-util-wait: dfu-util
|
||||
|
||||
# TODO: Remove once ARM has a way to configure EECONFIG_HANDEDNESS
|
||||
# within the emulated eeprom via dfu-util or another tool
|
||||
ifneq (,$(filter $(MAKECMDGOALS),dfu-util-split-left))
|
||||
OPT_DEFS += -DINIT_EE_HANDS_LEFT
|
||||
endif
|
||||
|
||||
ifneq (,$(filter $(MAKECMDGOALS),dfu-util-split-right))
|
||||
OPT_DEFS += -DINIT_EE_HANDS_RIGHT
|
||||
endif
|
||||
|
||||
dfu-util-split-left: dfu-util
|
||||
|
||||
dfu-util-split-right: dfu-util
|
||||
|
||||
|
||||
st-link-cli: $(BUILD_DIR)/$(TARGET).hex sizeafter
|
||||
$(ST_LINK_CLI) $(ST_LINK_ARGS) -q -c SWD -p $(BUILD_DIR)/$(TARGET).hex -Rst
|
||||
|
||||
st-flash: $(BUILD_DIR)/$(TARGET).hex sizeafter
|
||||
$(ST_FLASH) $(ST_FLASH_ARGS) --reset --format ihex write $(BUILD_DIR)/$(TARGET).hex
|
||||
|
||||
|
||||
# Autodetect teensy loader
|
||||
ifndef TEENSY_LOADER_CLI
|
||||
ifneq (, $(shell which teensy-loader-cli 2>/dev/null))
|
||||
TEENSY_LOADER_CLI ?= teensy-loader-cli
|
||||
else
|
||||
TEENSY_LOADER_CLI ?= teensy_loader_cli
|
||||
endif
|
||||
endif
|
||||
|
||||
define EXEC_TEENSY
|
||||
$(TEENSY_LOADER_CLI) -mmcu=$(MCU_LDSCRIPT) -w -v $(BUILD_DIR)/$(TARGET).hex
|
||||
endef
|
||||
|
||||
teensy: $(BUILD_DIR)/$(TARGET).hex cpfirmware sizeafter
|
||||
$(call EXEC_TEENSY)
|
||||
|
||||
bin: $(BUILD_DIR)/$(TARGET).bin sizeafter
|
||||
$(COPY) $(BUILD_DIR)/$(TARGET).bin $(TARGET).bin;
|
||||
|
||||
|
||||
flash: $(BUILD_DIR)/$(TARGET).bin cpfirmware sizeafter
|
||||
ifneq ($(strip $(PROGRAM_CMD)),)
|
||||
$(PROGRAM_CMD)
|
||||
else ifeq ($(strip $(BOOTLOADER)),kiibohd)
|
||||
$(call EXEC_DFU_UTIL)
|
||||
else ifeq ($(strip $(MCU_FAMILY)),KINETIS)
|
||||
$(call EXEC_TEENSY)
|
||||
else ifeq ($(strip $(MCU_FAMILY)),STM32)
|
||||
$(call EXEC_DFU_UTIL)
|
||||
else
|
||||
$(PRINT_OK); $(SILENT) || printf "$(MSG_FLASH_BOOTLOADER)"
|
||||
endif
|
||||
|
||||
+23
-21
@@ -1,29 +1,18 @@
|
||||
COMMON_DIR = common
|
||||
PLATFORM_COMMON_DIR = $(COMMON_DIR)/$(PLATFORM_KEY)
|
||||
|
||||
TMK_COMMON_SRC += $(COMMON_DIR)/host.c \
|
||||
$(COMMON_DIR)/keyboard.c \
|
||||
$(COMMON_DIR)/action.c \
|
||||
$(COMMON_DIR)/action_tapping.c \
|
||||
$(COMMON_DIR)/action_macro.c \
|
||||
$(COMMON_DIR)/action_layer.c \
|
||||
$(COMMON_DIR)/action_util.c \
|
||||
$(COMMON_DIR)/debug.c \
|
||||
$(COMMON_DIR)/sendchar_null.c \
|
||||
$(COMMON_DIR)/eeconfig.c \
|
||||
TMK_COMMON_SRC += \
|
||||
$(COMMON_DIR)/host.c \
|
||||
$(COMMON_DIR)/report.c \
|
||||
$(COMMON_DIR)/sync_timer.c \
|
||||
$(COMMON_DIR)/usb_util.c \
|
||||
$(PLATFORM_COMMON_DIR)/platform.c \
|
||||
$(PLATFORM_COMMON_DIR)/suspend.c \
|
||||
$(PLATFORM_COMMON_DIR)/timer.c \
|
||||
$(COMMON_DIR)/sync_timer.c \
|
||||
$(PLATFORM_COMMON_DIR)/bootloader.c \
|
||||
|
||||
# Use platform provided print - fall back to lib/printf
|
||||
ifneq ("$(wildcard $(TMK_PATH)/$(PLATFORM_COMMON_DIR)/printf.mk)","")
|
||||
include $(TMK_PATH)/$(PLATFORM_COMMON_DIR)/printf.mk
|
||||
else
|
||||
include $(TMK_PATH)/$(COMMON_DIR)/lib_printf.mk
|
||||
endif
|
||||
# Use platform provided print if it exists
|
||||
-include $(TMK_PATH)/$(PLATFORM_COMMON_DIR)/printf.mk
|
||||
|
||||
SHARED_EP_ENABLE = no
|
||||
MOUSE_SHARED_EP ?= yes
|
||||
@@ -36,7 +25,8 @@ ifeq ($(strip $(KEYBOARD_SHARED_EP)), yes)
|
||||
MOUSE_SHARED_EP = yes
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(MOUSEKEY_ENABLE)), yes)
|
||||
ifeq ($(strip $(MOUSE_ENABLE)), yes)
|
||||
OPT_DEFS += -DMOUSE_ENABLE
|
||||
ifeq ($(strip $(MOUSE_SHARED_EP)), yes)
|
||||
TMK_COMMON_DEFS += -DMOUSE_SHARED_EP
|
||||
SHARED_EP_ENABLE = yes
|
||||
@@ -55,6 +45,7 @@ endif
|
||||
ifeq ($(strip $(CONSOLE_ENABLE)), yes)
|
||||
TMK_COMMON_DEFS += -DCONSOLE_ENABLE
|
||||
else
|
||||
# TODO: decouple this so other print backends can exist
|
||||
TMK_COMMON_DEFS += -DNO_PRINT
|
||||
TMK_COMMON_DEFS += -DNO_DEBUG
|
||||
endif
|
||||
@@ -103,9 +94,6 @@ ifeq ($(strip $(BLUETOOTH)), RN42)
|
||||
TMK_COMMON_DEFS += -DNO_USB_STARTUP_CHECK
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(ONEHAND_ENABLE)), yes)
|
||||
SWAP_HANDS_ENABLE = yes # backwards compatibility
|
||||
endif
|
||||
ifeq ($(strip $(SWAP_HANDS_ENABLE)), yes)
|
||||
TMK_COMMON_DEFS += -DSWAP_HANDS_ENABLE
|
||||
endif
|
||||
@@ -114,6 +102,19 @@ ifeq ($(strip $(NO_USB_STARTUP_CHECK)), yes)
|
||||
TMK_COMMON_DEFS += -DNO_USB_STARTUP_CHECK
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(DIGITIZER_SHARED_EP)), yes)
|
||||
TMK_COMMON_DEFS += -DDIGITIZER_SHARED_EP
|
||||
SHARED_EP_ENABLE = yes
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(DIGITIZER_ENABLE)), yes)
|
||||
TMK_COMMON_DEFS += -DDIGITIZER_ENABLE
|
||||
ifeq ($(strip $(SHARED_EP_ENABLE)), yes)
|
||||
TMK_COMMON_DEFS += -DDIGITIZER_SHARED_EP
|
||||
SHARED_EP_ENABLE = yes
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(SHARED_EP_ENABLE)), yes)
|
||||
TMK_COMMON_DEFS += -DSHARED_EP_ENABLE
|
||||
endif
|
||||
@@ -133,3 +134,4 @@ endif
|
||||
# Search Path
|
||||
VPATH += $(TMK_PATH)/$(COMMON_DIR)
|
||||
VPATH += $(TMK_PATH)/$(PLATFORM_COMMON_DIR)
|
||||
VPATH += $(PLATFORM_PATH)/$(PLATFORM_KEY)/$(DRIVER_DIR)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,127 +0,0 @@
|
||||
/*
|
||||
Copyright 2012,2013 Jun Wako <wakojun@gmail.com>
|
||||
|
||||
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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "keyboard.h"
|
||||
#include "keycode.h"
|
||||
#include "action_code.h"
|
||||
#include "action_macro.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Disable macro and function features when LTO is enabled, since they break */
|
||||
#ifdef LTO_ENABLE
|
||||
# ifndef NO_ACTION_MACRO
|
||||
# define NO_ACTION_MACRO
|
||||
# endif
|
||||
# ifndef NO_ACTION_FUNCTION
|
||||
# define NO_ACTION_FUNCTION
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* tapping count and state */
|
||||
typedef struct {
|
||||
bool interrupted : 1;
|
||||
bool reserved2 : 1;
|
||||
bool reserved1 : 1;
|
||||
bool reserved0 : 1;
|
||||
uint8_t count : 4;
|
||||
} tap_t;
|
||||
|
||||
/* Key event container for recording */
|
||||
typedef struct {
|
||||
keyevent_t event;
|
||||
#ifndef NO_ACTION_TAPPING
|
||||
tap_t tap;
|
||||
#endif
|
||||
} keyrecord_t;
|
||||
|
||||
/* Execute action per keyevent */
|
||||
void action_exec(keyevent_t event);
|
||||
|
||||
/* action for key */
|
||||
action_t action_for_key(uint8_t layer, keypos_t key);
|
||||
|
||||
/* macro */
|
||||
const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt);
|
||||
|
||||
/* user defined special function */
|
||||
void action_function(keyrecord_t *record, uint8_t id, uint8_t opt);
|
||||
|
||||
/* keyboard-specific key event (pre)processing */
|
||||
bool process_record_quantum(keyrecord_t *record);
|
||||
|
||||
/* Utilities for actions. */
|
||||
#if !defined(NO_ACTION_LAYER) && !defined(STRICT_LAYER_RELEASE)
|
||||
extern bool disable_action_cache;
|
||||
#endif
|
||||
|
||||
/* Code for handling one-handed key modifiers. */
|
||||
#ifdef SWAP_HANDS_ENABLE
|
||||
extern bool swap_hands;
|
||||
extern const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS];
|
||||
# if (MATRIX_COLS <= 8)
|
||||
typedef uint8_t swap_state_row_t;
|
||||
# elif (MATRIX_COLS <= 16)
|
||||
typedef uint16_t swap_state_row_t;
|
||||
# elif (MATRIX_COLS <= 32)
|
||||
typedef uint32_t swap_state_row_t;
|
||||
# else
|
||||
# error "MATRIX_COLS: invalid value"
|
||||
# endif
|
||||
|
||||
void process_hand_swap(keyevent_t *record);
|
||||
#endif
|
||||
|
||||
void process_record_nocache(keyrecord_t *record);
|
||||
void process_record(keyrecord_t *record);
|
||||
void process_record_handler(keyrecord_t *record);
|
||||
void post_process_record_quantum(keyrecord_t *record);
|
||||
void process_action(keyrecord_t *record, action_t action);
|
||||
void register_code(uint8_t code);
|
||||
void unregister_code(uint8_t code);
|
||||
void tap_code(uint8_t code);
|
||||
void tap_code_delay(uint8_t code, uint16_t delay);
|
||||
void register_mods(uint8_t mods);
|
||||
void unregister_mods(uint8_t mods);
|
||||
void register_weak_mods(uint8_t mods);
|
||||
void unregister_weak_mods(uint8_t mods);
|
||||
// void set_mods(uint8_t mods);
|
||||
void clear_keyboard(void);
|
||||
void clear_keyboard_but_mods(void);
|
||||
void clear_keyboard_but_mods_and_keys(void);
|
||||
void layer_switch(uint8_t new_layer);
|
||||
bool is_tap_key(keypos_t key);
|
||||
bool is_tap_action(action_t action);
|
||||
|
||||
#ifndef NO_ACTION_TAPPING
|
||||
void process_record_tap_hint(keyrecord_t *record);
|
||||
#endif
|
||||
|
||||
/* debug */
|
||||
void debug_event(keyevent_t event);
|
||||
void debug_record(keyrecord_t record);
|
||||
void debug_action(action_t action);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,308 +0,0 @@
|
||||
/*
|
||||
Copyright 2013 Jun Wako <wakojun@gmail.com>
|
||||
|
||||
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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \brief Action codes
|
||||
*
|
||||
* 16bit code: action_kind(4bit) + action_parameter(12bit)
|
||||
*
|
||||
* Key Actions(00xx)
|
||||
* -----------------
|
||||
* ACT_MODS(000r):
|
||||
* 000r|0000|0000 0000 No action code
|
||||
* 000r|0000|0000 0001 Transparent code
|
||||
* 000r|0000| keycode Key
|
||||
* 000r|mods|0000 0000 Modifiers
|
||||
* 000r|mods| keycode Modifiers+Key(Modified key)
|
||||
* r: Left/Right flag(Left:0, Right:1)
|
||||
*
|
||||
* ACT_MODS_TAP(001r):
|
||||
* 001r|mods|0000 0000 Modifiers with OneShot
|
||||
* 001r|mods|0000 0001 Modifiers with tap toggle
|
||||
* 001r|mods|0000 00xx (reserved)
|
||||
* 001r|mods| keycode Modifiers with Tap Key(Dual role)
|
||||
*
|
||||
* Other Keys(01xx)
|
||||
* ----------------
|
||||
* ACT_USAGE(0100): TODO: Not needed?
|
||||
* 0100|00| usage(10) System control(0x80) - General Desktop page(0x01)
|
||||
* 0100|01| usage(10) Consumer control(0x01) - Consumer page(0x0C)
|
||||
* 0100|10| usage(10) (reserved)
|
||||
* 0100|11| usage(10) (reserved)
|
||||
*
|
||||
* ACT_MOUSEKEY(0101): TODO: Merge these two actions to conserve space?
|
||||
* 0101|xxxx| keycode Mouse key
|
||||
*
|
||||
* ACT_SWAP_HANDS(0110):
|
||||
* 0110|xxxx| keycode Swap hands (keycode on tap, or options)
|
||||
*
|
||||
* 0111|xxxx xxxx xxxx (reserved)
|
||||
*
|
||||
* Layer Actions(10xx)
|
||||
* -------------------
|
||||
* ACT_LAYER(1000):
|
||||
* 1000|oo00|pppE BBBB Default Layer Bitwise operation
|
||||
* oo: operation(00:AND, 01:OR, 10:XOR, 11:SET)
|
||||
* ppp: 4-bit chunk part(0-7)
|
||||
* EBBBB: bits and extra bit
|
||||
* 1000|ooee|pppE BBBB Layer Bitwise Operation
|
||||
* oo: operation(00:AND, 01:OR, 10:XOR, 11:SET)
|
||||
* ppp: 4-bit chunk part(0-7)
|
||||
* EBBBB: bits and extra bit
|
||||
* ee: on event(01:press, 10:release, 11:both)
|
||||
*
|
||||
* ACT_LAYER_MODS(1001):
|
||||
* 1001|LLLL| mods Layer with modifiers held
|
||||
*
|
||||
* ACT_LAYER_TAP(101x):
|
||||
* 101E|LLLL| keycode On/Off with tap key (0x00-DF)[TAP]
|
||||
* 101E|LLLL|1110 mods On/Off with modifiers (0xE0-EF)[NOT TAP]
|
||||
* 101E|LLLL|1111 0000 Invert with tap toggle (0xF0) [TAP]
|
||||
* 101E|LLLL|1111 0001 On/Off (0xF1) [NOT TAP]
|
||||
* 101E|LLLL|1111 0010 Off/On (0xF2) [NOT TAP]
|
||||
* 101E|LLLL|1111 0011 Set/Clear (0xF3) [NOT TAP]
|
||||
* 101E|LLLL|1111 0100 One Shot Layer (0xF4) [TAP]
|
||||
* 101E|LLLL|1111 xxxx Reserved (0xF5-FF)
|
||||
* ELLLL: layer 0-31(E: extra bit for layer 16-31)
|
||||
*
|
||||
* Extensions(11xx)
|
||||
* ----------------
|
||||
* ACT_MACRO(1100):
|
||||
* 1100|opt | id(8) Macro play?
|
||||
* 1100|1111| id(8) Macro record?
|
||||
*
|
||||
* 1101|xxxx xxxx xxxx (reserved)
|
||||
* 1110|xxxx xxxx xxxx (reserved)
|
||||
*
|
||||
* ACT_FUNCTION(1111):
|
||||
* 1111| address(12) Function?
|
||||
* 1111|opt | id(8) Function?
|
||||
*/
|
||||
enum action_kind_id {
|
||||
/* Key Actions */
|
||||
ACT_MODS = 0b0000,
|
||||
ACT_LMODS = 0b0000,
|
||||
ACT_RMODS = 0b0001,
|
||||
ACT_MODS_TAP = 0b0010,
|
||||
ACT_LMODS_TAP = 0b0010,
|
||||
ACT_RMODS_TAP = 0b0011,
|
||||
/* Other Keys */
|
||||
ACT_USAGE = 0b0100,
|
||||
ACT_MOUSEKEY = 0b0101,
|
||||
/* One-hand Support */
|
||||
ACT_SWAP_HANDS = 0b0110,
|
||||
/* Layer Actions */
|
||||
ACT_LAYER = 0b1000,
|
||||
ACT_LAYER_MODS = 0b1001,
|
||||
ACT_LAYER_TAP = 0b1010, /* Layer 0-15 */
|
||||
ACT_LAYER_TAP_EXT = 0b1011, /* Layer 16-31 */
|
||||
/* Extensions */
|
||||
ACT_MACRO = 0b1100,
|
||||
ACT_FUNCTION = 0b1111
|
||||
};
|
||||
|
||||
/** \brief Action Code Struct
|
||||
*
|
||||
* NOTE:
|
||||
* In avr-gcc bit field seems to be assigned from LSB(bit0) to MSB(bit15).
|
||||
* AVR looks like a little endian in avr-gcc.
|
||||
* Not portable across compiler/endianness?
|
||||
*
|
||||
* Byte order and bit order of 0x1234:
|
||||
* Big endian: Little endian:
|
||||
* -------------------- --------------------
|
||||
* FEDC BA98 7654 3210 0123 4567 89AB CDEF
|
||||
* 0001 0010 0011 0100 0010 1100 0100 1000
|
||||
* 0x12 0x34 0x34 0x12
|
||||
*/
|
||||
typedef union {
|
||||
uint16_t code;
|
||||
struct action_kind {
|
||||
uint16_t param : 12;
|
||||
uint8_t id : 4;
|
||||
} kind;
|
||||
struct action_key {
|
||||
uint8_t code : 8;
|
||||
uint8_t mods : 4;
|
||||
uint8_t kind : 4;
|
||||
} key;
|
||||
struct action_layer_bitop {
|
||||
uint8_t bits : 4;
|
||||
uint8_t xbit : 1;
|
||||
uint8_t part : 3;
|
||||
uint8_t on : 2;
|
||||
uint8_t op : 2;
|
||||
uint8_t kind : 4;
|
||||
} layer_bitop;
|
||||
struct action_layer_mods {
|
||||
uint8_t mods : 8;
|
||||
uint8_t layer : 4;
|
||||
uint8_t kind : 4;
|
||||
} layer_mods;
|
||||
struct action_layer_tap {
|
||||
uint8_t code : 8;
|
||||
uint8_t val : 5;
|
||||
uint8_t kind : 3;
|
||||
} layer_tap;
|
||||
struct action_usage {
|
||||
uint16_t code : 10;
|
||||
uint8_t page : 2;
|
||||
uint8_t kind : 4;
|
||||
} usage;
|
||||
struct action_function {
|
||||
uint8_t id : 8;
|
||||
uint8_t opt : 4;
|
||||
uint8_t kind : 4;
|
||||
} func;
|
||||
struct action_swap {
|
||||
uint8_t code : 8;
|
||||
uint8_t opt : 4;
|
||||
uint8_t kind : 4;
|
||||
} swap;
|
||||
} action_t;
|
||||
|
||||
/* action utility */
|
||||
#define ACTION_NO 0
|
||||
#define ACTION_TRANSPARENT 1
|
||||
#define ACTION(kind, param) ((kind) << 12 | (param))
|
||||
|
||||
/** \brief Key Actions
|
||||
*
|
||||
* Mod bits: 43210
|
||||
* bit 0 ||||+- Control
|
||||
* bit 1 |||+-- Shift
|
||||
* bit 2 ||+--- Alt
|
||||
* bit 3 |+---- Gui
|
||||
* bit 4 +----- LR flag(Left:0, Right:1)
|
||||
*/
|
||||
enum mods_bit {
|
||||
MOD_LCTL = 0x01,
|
||||
MOD_LSFT = 0x02,
|
||||
MOD_LALT = 0x04,
|
||||
MOD_LGUI = 0x08,
|
||||
MOD_RCTL = 0x11,
|
||||
MOD_RSFT = 0x12,
|
||||
MOD_RALT = 0x14,
|
||||
MOD_RGUI = 0x18,
|
||||
};
|
||||
enum mods_codes {
|
||||
MODS_ONESHOT = 0x00,
|
||||
MODS_TAP_TOGGLE = 0x01,
|
||||
};
|
||||
#define ACTION_KEY(key) ACTION(ACT_MODS, (key))
|
||||
#define ACTION_MODS(mods) ACTION(ACT_MODS, ((mods)&0x1f) << 8 | 0)
|
||||
#define ACTION_MODS_KEY(mods, key) ACTION(ACT_MODS, ((mods)&0x1f) << 8 | (key))
|
||||
#define ACTION_MODS_TAP_KEY(mods, key) ACTION(ACT_MODS_TAP, ((mods)&0x1f) << 8 | (key))
|
||||
#define ACTION_MODS_ONESHOT(mods) ACTION(ACT_MODS_TAP, ((mods)&0x1f) << 8 | MODS_ONESHOT)
|
||||
#define ACTION_MODS_TAP_TOGGLE(mods) ACTION(ACT_MODS_TAP, ((mods)&0x1f) << 8 | MODS_TAP_TOGGLE)
|
||||
|
||||
/** \brief Other Keys
|
||||
*/
|
||||
enum usage_pages { PAGE_SYSTEM, PAGE_CONSUMER };
|
||||
#define ACTION_USAGE_SYSTEM(id) ACTION(ACT_USAGE, PAGE_SYSTEM << 10 | (id))
|
||||
#define ACTION_USAGE_CONSUMER(id) ACTION(ACT_USAGE, PAGE_CONSUMER << 10 | (id))
|
||||
#define ACTION_MOUSEKEY(key) ACTION(ACT_MOUSEKEY, key)
|
||||
|
||||
/** \brief Layer Actions
|
||||
*/
|
||||
enum layer_param_on {
|
||||
ON_PRESS = 1,
|
||||
ON_RELEASE = 2,
|
||||
ON_BOTH = 3,
|
||||
};
|
||||
|
||||
/** \brief Layer Actions
|
||||
*/
|
||||
enum layer_param_bit_op {
|
||||
OP_BIT_AND = 0,
|
||||
OP_BIT_OR = 1,
|
||||
OP_BIT_XOR = 2,
|
||||
OP_BIT_SET = 3,
|
||||
};
|
||||
|
||||
/** \brief Layer Actions
|
||||
*/
|
||||
enum layer_param_tap_op {
|
||||
OP_TAP_TOGGLE = 0xF0,
|
||||
OP_ON_OFF,
|
||||
OP_OFF_ON,
|
||||
OP_SET_CLEAR,
|
||||
OP_ONESHOT,
|
||||
};
|
||||
#define ACTION_LAYER_BITOP(op, part, bits, on) ACTION(ACT_LAYER, (op) << 10 | (on) << 8 | (part) << 5 | ((bits)&0x1f))
|
||||
#define ACTION_LAYER_TAP(layer, key) ACTION(ACT_LAYER_TAP, (layer) << 8 | (key))
|
||||
/* Default Layer */
|
||||
#define ACTION_DEFAULT_LAYER_SET(layer) ACTION_DEFAULT_LAYER_BIT_SET((layer) / 4, 1 << ((layer) % 4))
|
||||
/* Layer Operation */
|
||||
#define ACTION_LAYER_CLEAR(on) ACTION_LAYER_BIT_AND(0, 0, (on))
|
||||
#define ACTION_LAYER_MOMENTARY(layer) ACTION_LAYER_ON_OFF(layer)
|
||||
#define ACTION_LAYER_TOGGLE(layer) ACTION_LAYER_INVERT(layer, ON_RELEASE)
|
||||
#define ACTION_LAYER_INVERT(layer, on) ACTION_LAYER_BIT_XOR((layer) / 4, 1 << ((layer) % 4), (on))
|
||||
#define ACTION_LAYER_ON(layer, on) ACTION_LAYER_BIT_OR((layer) / 4, 1 << ((layer) % 4), (on))
|
||||
#define ACTION_LAYER_OFF(layer, on) ACTION_LAYER_BIT_AND((layer) / 4, ~(1 << ((layer) % 4)), (on))
|
||||
#define ACTION_LAYER_SET(layer, on) ACTION_LAYER_BIT_SET((layer) / 4, 1 << ((layer) % 4), (on))
|
||||
#define ACTION_LAYER_ON_OFF(layer) ACTION_LAYER_TAP((layer), OP_ON_OFF)
|
||||
#define ACTION_LAYER_OFF_ON(layer) ACTION_LAYER_TAP((layer), OP_OFF_ON)
|
||||
#define ACTION_LAYER_SET_CLEAR(layer) ACTION_LAYER_TAP((layer), OP_SET_CLEAR)
|
||||
#define ACTION_LAYER_ONESHOT(layer) ACTION_LAYER_TAP((layer), OP_ONESHOT)
|
||||
#define ACTION_LAYER_MODS(layer, mods) ACTION(ACT_LAYER_MODS, (layer) << 8 | (mods))
|
||||
/* With Tapping */
|
||||
#define ACTION_LAYER_TAP_KEY(layer, key) ACTION_LAYER_TAP((layer), (key))
|
||||
#define ACTION_LAYER_TAP_TOGGLE(layer) ACTION_LAYER_TAP((layer), OP_TAP_TOGGLE)
|
||||
/* Bitwise Operation */
|
||||
#define ACTION_LAYER_BIT_AND(part, bits, on) ACTION_LAYER_BITOP(OP_BIT_AND, (part), (bits), (on))
|
||||
#define ACTION_LAYER_BIT_OR(part, bits, on) ACTION_LAYER_BITOP(OP_BIT_OR, (part), (bits), (on))
|
||||
#define ACTION_LAYER_BIT_XOR(part, bits, on) ACTION_LAYER_BITOP(OP_BIT_XOR, (part), (bits), (on))
|
||||
#define ACTION_LAYER_BIT_SET(part, bits, on) ACTION_LAYER_BITOP(OP_BIT_SET, (part), (bits), (on))
|
||||
/* Default Layer Bitwise Operation */
|
||||
#define ACTION_DEFAULT_LAYER_BIT_AND(part, bits) ACTION_LAYER_BITOP(OP_BIT_AND, (part), (bits), 0)
|
||||
#define ACTION_DEFAULT_LAYER_BIT_OR(part, bits) ACTION_LAYER_BITOP(OP_BIT_OR, (part), (bits), 0)
|
||||
#define ACTION_DEFAULT_LAYER_BIT_XOR(part, bits) ACTION_LAYER_BITOP(OP_BIT_XOR, (part), (bits), 0)
|
||||
#define ACTION_DEFAULT_LAYER_BIT_SET(part, bits) ACTION_LAYER_BITOP(OP_BIT_SET, (part), (bits), 0)
|
||||
|
||||
/* Macro */
|
||||
#define ACTION_MACRO(id) ACTION(ACT_MACRO, (id))
|
||||
#define ACTION_MACRO_TAP(id) ACTION(ACT_MACRO, FUNC_TAP << 8 | (id))
|
||||
#define ACTION_MACRO_OPT(id, opt) ACTION(ACT_MACRO, (opt) << 8 | (id))
|
||||
/* Function */
|
||||
enum function_opts {
|
||||
FUNC_TAP = 0x8, /* indciates function is tappable */
|
||||
};
|
||||
#define ACTION_FUNCTION(id) ACTION(ACT_FUNCTION, (id))
|
||||
#define ACTION_FUNCTION_TAP(id) ACTION(ACT_FUNCTION, FUNC_TAP << 8 | (id))
|
||||
#define ACTION_FUNCTION_OPT(id, opt) ACTION(ACT_FUNCTION, (opt) << 8 | (id))
|
||||
/* OneHand Support */
|
||||
enum swap_hands_param_tap_op {
|
||||
OP_SH_TOGGLE = 0xF0,
|
||||
OP_SH_TAP_TOGGLE,
|
||||
OP_SH_ON_OFF,
|
||||
OP_SH_OFF_ON,
|
||||
OP_SH_OFF,
|
||||
OP_SH_ON,
|
||||
OP_SH_ONESHOT,
|
||||
};
|
||||
|
||||
#define ACTION_SWAP_HANDS() ACTION_SWAP_HANDS_ON_OFF()
|
||||
#define ACTION_SWAP_HANDS_TOGGLE() ACTION(ACT_SWAP_HANDS, OP_SH_TOGGLE)
|
||||
#define ACTION_SWAP_HANDS_TAP_TOGGLE() ACTION(ACT_SWAP_HANDS, OP_SH_TAP_TOGGLE)
|
||||
#define ACTION_SWAP_HANDS_ONESHOT() ACTION(ACT_SWAP_HANDS, OP_SH_ONESHOT)
|
||||
#define ACTION_SWAP_HANDS_TAP_KEY(key) ACTION(ACT_SWAP_HANDS, key)
|
||||
#define ACTION_SWAP_HANDS_ON_OFF() ACTION(ACT_SWAP_HANDS, OP_SH_ON_OFF)
|
||||
#define ACTION_SWAP_HANDS_OFF_ON() ACTION(ACT_SWAP_HANDS, OP_SH_OFF_ON)
|
||||
#define ACTION_SWAP_HANDS_ON() ACTION(ACT_SWAP_HANDS, OP_SH_ON)
|
||||
#define ACTION_SWAP_HANDS_OFF() ACTION(ACT_SWAP_HANDS, OP_SH_OFF)
|
||||
@@ -1,291 +0,0 @@
|
||||
#include <stdint.h>
|
||||
#include "keyboard.h"
|
||||
#include "action.h"
|
||||
#include "util.h"
|
||||
#include "action_layer.h"
|
||||
|
||||
#ifdef DEBUG_ACTION
|
||||
# include "debug.h"
|
||||
#else
|
||||
# include "nodebug.h"
|
||||
#endif
|
||||
|
||||
#ifdef VIAL_ENABLE
|
||||
#include "vial.h"
|
||||
#endif
|
||||
|
||||
/** \brief Default Layer State
|
||||
*/
|
||||
layer_state_t default_layer_state = 0;
|
||||
|
||||
/** \brief Default Layer State Set At user Level
|
||||
*
|
||||
* Run user code on default layer state change
|
||||
*/
|
||||
__attribute__((weak)) layer_state_t default_layer_state_set_user(layer_state_t state) { return state; }
|
||||
|
||||
/** \brief Default Layer State Set At Keyboard Level
|
||||
*
|
||||
* Run keyboard code on default layer state change
|
||||
*/
|
||||
__attribute__((weak)) layer_state_t default_layer_state_set_kb(layer_state_t state) { return default_layer_state_set_user(state); }
|
||||
|
||||
/** \brief Default Layer State Set
|
||||
*
|
||||
* Static function to set the default layer state, prints debug info and clears keys
|
||||
*/
|
||||
static void default_layer_state_set(layer_state_t state) {
|
||||
state = default_layer_state_set_kb(state);
|
||||
debug("default_layer_state: ");
|
||||
default_layer_debug();
|
||||
debug(" to ");
|
||||
default_layer_state = state;
|
||||
default_layer_debug();
|
||||
debug("\n");
|
||||
#ifdef STRICT_LAYER_RELEASE
|
||||
clear_keyboard_but_mods(); // To avoid stuck keys
|
||||
#else
|
||||
clear_keyboard_but_mods_and_keys(); // Don't reset held keys
|
||||
#endif
|
||||
}
|
||||
|
||||
/** \brief Default Layer Print
|
||||
*
|
||||
* Print out the hex value of the 32-bit default layer state, as well as the value of the highest bit.
|
||||
*/
|
||||
void default_layer_debug(void) { dprintf("%08lX(%u)", default_layer_state, get_highest_layer(default_layer_state)); }
|
||||
|
||||
/** \brief Default Layer Set
|
||||
*
|
||||
* Sets the default layer state.
|
||||
*/
|
||||
void default_layer_set(layer_state_t state) { default_layer_state_set(state); }
|
||||
|
||||
#ifndef NO_ACTION_LAYER
|
||||
/** \brief Default Layer Or
|
||||
*
|
||||
* Turns on the default layer based on matching bits between specifed layer and existing layer state
|
||||
*/
|
||||
void default_layer_or(layer_state_t state) { default_layer_state_set(default_layer_state | state); }
|
||||
/** \brief Default Layer And
|
||||
*
|
||||
* Turns on default layer based on matching enabled bits between specifed layer and existing layer state
|
||||
*/
|
||||
void default_layer_and(layer_state_t state) { default_layer_state_set(default_layer_state & state); }
|
||||
/** \brief Default Layer Xor
|
||||
*
|
||||
* Turns on default layer based on non-matching bits between specifed layer and existing layer state
|
||||
*/
|
||||
void default_layer_xor(layer_state_t state) { default_layer_state_set(default_layer_state ^ state); }
|
||||
#endif
|
||||
|
||||
#ifndef NO_ACTION_LAYER
|
||||
/** \brief Keymap Layer State
|
||||
*/
|
||||
layer_state_t layer_state = 0;
|
||||
|
||||
/** \brief Layer state set user
|
||||
*
|
||||
* Runs user code on layer state change
|
||||
*/
|
||||
__attribute__((weak)) layer_state_t layer_state_set_user(layer_state_t state) { return state; }
|
||||
|
||||
/** \brief Layer state set keyboard
|
||||
*
|
||||
* Runs keyboard code on layer state change
|
||||
*/
|
||||
__attribute__((weak)) layer_state_t layer_state_set_kb(layer_state_t state) { return layer_state_set_user(state); }
|
||||
|
||||
/** \brief Layer state set
|
||||
*
|
||||
* Sets the layer to match the specifed state (a bitmask)
|
||||
*/
|
||||
void layer_state_set(layer_state_t state) {
|
||||
state = layer_state_set_kb(state);
|
||||
dprint("layer_state: ");
|
||||
layer_debug();
|
||||
dprint(" to ");
|
||||
layer_state = state;
|
||||
layer_debug();
|
||||
dprintln();
|
||||
# ifdef STRICT_LAYER_RELEASE
|
||||
clear_keyboard_but_mods(); // To avoid stuck keys
|
||||
# else
|
||||
clear_keyboard_but_mods_and_keys(); // Don't reset held keys
|
||||
# endif
|
||||
}
|
||||
|
||||
/** \brief Layer clear
|
||||
*
|
||||
* Turn off all layers
|
||||
*/
|
||||
void layer_clear(void) { layer_state_set(0); }
|
||||
|
||||
/** \brief Layer state is
|
||||
*
|
||||
* Return whether the given state is on (it might still be shadowed by a higher state, though)
|
||||
*/
|
||||
bool layer_state_is(uint8_t layer) { return layer_state_cmp(layer_state, layer); }
|
||||
|
||||
/** \brief Layer state compare
|
||||
*
|
||||
* Used for comparing layers {mostly used for unit testing}
|
||||
*/
|
||||
bool layer_state_cmp(layer_state_t cmp_layer_state, uint8_t layer) {
|
||||
if (!cmp_layer_state) {
|
||||
return layer == 0;
|
||||
}
|
||||
return (cmp_layer_state & (1UL << layer)) != 0;
|
||||
}
|
||||
|
||||
/** \brief Layer move
|
||||
*
|
||||
* Turns on the given layer and turn off all other layers
|
||||
*/
|
||||
void layer_move(uint8_t layer) { layer_state_set(1UL << layer); }
|
||||
|
||||
/** \brief Layer on
|
||||
*
|
||||
* Turns on given layer
|
||||
*/
|
||||
void layer_on(uint8_t layer) { layer_state_set(layer_state | (1UL << layer)); }
|
||||
|
||||
/** \brief Layer off
|
||||
*
|
||||
* Turns off given layer
|
||||
*/
|
||||
void layer_off(uint8_t layer) { layer_state_set(layer_state & ~(1UL << layer)); }
|
||||
|
||||
/** \brief Layer invert
|
||||
*
|
||||
* Toggle the given layer (set it if it's unset, or unset it if it's set)
|
||||
*/
|
||||
void layer_invert(uint8_t layer) { layer_state_set(layer_state ^ (1UL << layer)); }
|
||||
|
||||
/** \brief Layer or
|
||||
*
|
||||
* Turns on layers based on matching bits between specifed layer and existing layer state
|
||||
*/
|
||||
void layer_or(layer_state_t state) { layer_state_set(layer_state | state); }
|
||||
/** \brief Layer and
|
||||
*
|
||||
* Turns on layers based on matching enabled bits between specifed layer and existing layer state
|
||||
*/
|
||||
void layer_and(layer_state_t state) { layer_state_set(layer_state & state); }
|
||||
/** \brief Layer xor
|
||||
*
|
||||
* Turns on layers based on non-matching bits between specifed layer and existing layer state
|
||||
*/
|
||||
void layer_xor(layer_state_t state) { layer_state_set(layer_state ^ state); }
|
||||
|
||||
/** \brief Layer debug printing
|
||||
*
|
||||
* Print out the hex value of the 32-bit layer state, as well as the value of the highest bit.
|
||||
*/
|
||||
void layer_debug(void) { dprintf("%08lX(%u)", layer_state, get_highest_layer(layer_state)); }
|
||||
#endif
|
||||
|
||||
#if !defined(NO_ACTION_LAYER) && !defined(STRICT_LAYER_RELEASE)
|
||||
/** \brief source layer cache
|
||||
*/
|
||||
|
||||
uint8_t source_layers_cache[(MATRIX_ROWS * MATRIX_COLS + 7) / 8][MAX_LAYER_BITS] = {{0}};
|
||||
|
||||
/** \brief update source layers cache
|
||||
*
|
||||
* Updates the cached keys when changing layers
|
||||
*/
|
||||
void update_source_layers_cache(keypos_t key, uint8_t layer) {
|
||||
#ifdef VIAL_ENABLE
|
||||
if (key.row == VIAL_MATRIX_MAGIC) return;
|
||||
#endif
|
||||
|
||||
const uint8_t key_number = key.col + (key.row * MATRIX_COLS);
|
||||
const uint8_t storage_row = key_number / 8;
|
||||
const uint8_t storage_bit = key_number % 8;
|
||||
|
||||
for (uint8_t bit_number = 0; bit_number < MAX_LAYER_BITS; bit_number++) {
|
||||
source_layers_cache[storage_row][bit_number] ^= (-((layer & (1U << bit_number)) != 0) ^ source_layers_cache[storage_row][bit_number]) & (1U << storage_bit);
|
||||
}
|
||||
}
|
||||
|
||||
/** \brief read source layers cache
|
||||
*
|
||||
* reads the cached keys stored when the layer was changed
|
||||
*/
|
||||
uint8_t read_source_layers_cache(keypos_t key) {
|
||||
#ifdef VIAL_ENABLE
|
||||
if (key.row == VIAL_MATRIX_MAGIC) return 0;
|
||||
#endif
|
||||
|
||||
const uint8_t key_number = key.col + (key.row * MATRIX_COLS);
|
||||
const uint8_t storage_row = key_number / 8;
|
||||
const uint8_t storage_bit = key_number % 8;
|
||||
uint8_t layer = 0;
|
||||
|
||||
for (uint8_t bit_number = 0; bit_number < MAX_LAYER_BITS; bit_number++) {
|
||||
layer |= ((source_layers_cache[storage_row][bit_number] & (1U << storage_bit)) != 0) << bit_number;
|
||||
}
|
||||
|
||||
return layer;
|
||||
}
|
||||
#endif
|
||||
|
||||
/** \brief Store or get action (FIXME: Needs better summary)
|
||||
*
|
||||
* Make sure the action triggered when the key is released is the same
|
||||
* one as the one triggered on press. It's important for the mod keys
|
||||
* when the layer is switched after the down event but before the up
|
||||
* event as they may get stuck otherwise.
|
||||
*/
|
||||
action_t store_or_get_action(bool pressed, keypos_t key) {
|
||||
#if !defined(NO_ACTION_LAYER) && !defined(STRICT_LAYER_RELEASE)
|
||||
if (disable_action_cache) {
|
||||
return layer_switch_get_action(key);
|
||||
}
|
||||
|
||||
uint8_t layer;
|
||||
|
||||
if (pressed) {
|
||||
layer = layer_switch_get_layer(key);
|
||||
update_source_layers_cache(key, layer);
|
||||
} else {
|
||||
layer = read_source_layers_cache(key);
|
||||
}
|
||||
return action_for_key(layer, key);
|
||||
#else
|
||||
return layer_switch_get_action(key);
|
||||
#endif
|
||||
}
|
||||
|
||||
/** \brief Layer switch get layer
|
||||
*
|
||||
* Gets the layer based on key info
|
||||
*/
|
||||
uint8_t layer_switch_get_layer(keypos_t key) {
|
||||
#ifndef NO_ACTION_LAYER
|
||||
action_t action;
|
||||
action.code = ACTION_TRANSPARENT;
|
||||
|
||||
layer_state_t layers = layer_state | default_layer_state;
|
||||
/* check top layer first */
|
||||
for (int8_t i = MAX_LAYER - 1; i >= 0; i--) {
|
||||
if (layers & (1UL << i)) {
|
||||
action = action_for_key(i, key);
|
||||
if (action.code != ACTION_TRANSPARENT) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* fall back to layer 0 */
|
||||
return 0;
|
||||
#else
|
||||
return get_highest_layer(default_layer_state);
|
||||
#endif
|
||||
}
|
||||
|
||||
/** \brief Layer switch get layer
|
||||
*
|
||||
* Gets action code based on key position
|
||||
*/
|
||||
action_t layer_switch_get_action(keypos_t key) { return action_for_key(layer_switch_get_layer(key), key); }
|
||||
@@ -1,122 +0,0 @@
|
||||
/*
|
||||
Copyright 2013 Jun Wako <wakojun@gmail.com>
|
||||
|
||||
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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "keyboard.h"
|
||||
#include "action.h"
|
||||
|
||||
#if defined(LAYER_STATE_8BIT)
|
||||
typedef uint8_t layer_state_t;
|
||||
# define MAX_LAYER_BITS 3
|
||||
# ifndef MAX_LAYER
|
||||
# define MAX_LAYER 8
|
||||
# endif
|
||||
# define get_highest_layer(state) biton(state)
|
||||
#elif defined(LAYER_STATE_16BIT)
|
||||
typedef uint16_t layer_state_t;
|
||||
# define MAX_LAYER_BITS 4
|
||||
# ifndef MAX_LAYER
|
||||
# define MAX_LAYER 16
|
||||
# endif
|
||||
# define get_highest_layer(state) biton16(state)
|
||||
#else
|
||||
typedef uint32_t layer_state_t;
|
||||
# define MAX_LAYER_BITS 5
|
||||
# ifndef MAX_LAYER
|
||||
# define MAX_LAYER 32
|
||||
# endif
|
||||
# define get_highest_layer(state) biton32(state)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Default Layer
|
||||
*/
|
||||
extern layer_state_t default_layer_state;
|
||||
void default_layer_debug(void);
|
||||
void default_layer_set(layer_state_t state);
|
||||
|
||||
__attribute__((weak)) layer_state_t default_layer_state_set_kb(layer_state_t state);
|
||||
__attribute__((weak)) layer_state_t default_layer_state_set_user(layer_state_t state);
|
||||
|
||||
#ifndef NO_ACTION_LAYER
|
||||
/* bitwise operation */
|
||||
void default_layer_or(layer_state_t state);
|
||||
void default_layer_and(layer_state_t state);
|
||||
void default_layer_xor(layer_state_t state);
|
||||
#else
|
||||
# define default_layer_or(state)
|
||||
# define default_layer_and(state)
|
||||
# define default_layer_xor(state)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Keymap Layer
|
||||
*/
|
||||
#ifndef NO_ACTION_LAYER
|
||||
extern layer_state_t layer_state;
|
||||
|
||||
void layer_state_set(layer_state_t state);
|
||||
bool layer_state_is(uint8_t layer);
|
||||
bool layer_state_cmp(layer_state_t layer1, uint8_t layer2);
|
||||
|
||||
void layer_debug(void);
|
||||
void layer_clear(void);
|
||||
void layer_move(uint8_t layer);
|
||||
void layer_on(uint8_t layer);
|
||||
void layer_off(uint8_t layer);
|
||||
void layer_invert(uint8_t layer);
|
||||
/* bitwise operation */
|
||||
void layer_or(layer_state_t state);
|
||||
void layer_and(layer_state_t state);
|
||||
void layer_xor(layer_state_t state);
|
||||
layer_state_t layer_state_set_user(layer_state_t state);
|
||||
layer_state_t layer_state_set_kb(layer_state_t state);
|
||||
#else
|
||||
# define layer_state 0
|
||||
|
||||
# define layer_state_set(layer)
|
||||
# define layer_state_is(layer) (layer == 0)
|
||||
# define layer_state_cmp(state, layer) (state == 0 ? layer == 0 : (state & 1UL << layer) != 0)
|
||||
|
||||
# define layer_debug()
|
||||
# define layer_clear()
|
||||
# define layer_move(layer) (void)layer
|
||||
# define layer_on(layer) (void)layer
|
||||
# define layer_off(layer) (void)layer
|
||||
# define layer_invert(layer) (void)layer
|
||||
# define layer_or(state) (void)state
|
||||
# define layer_and(state) (void)state
|
||||
# define layer_xor(state) (void)state
|
||||
# define layer_state_set_kb(state) (void)state
|
||||
# define layer_state_set_user(state) (void)state
|
||||
#endif
|
||||
|
||||
/* pressed actions cache */
|
||||
#if !defined(NO_ACTION_LAYER) && !defined(STRICT_LAYER_RELEASE)
|
||||
|
||||
void update_source_layers_cache(keypos_t key, uint8_t layer);
|
||||
uint8_t read_source_layers_cache(keypos_t key);
|
||||
#endif
|
||||
action_t store_or_get_action(bool pressed, keypos_t key);
|
||||
|
||||
/* return the topmost non-transparent layer currently associated with key */
|
||||
uint8_t layer_switch_get_layer(keypos_t key);
|
||||
|
||||
/* return action depending on current layer status */
|
||||
action_t layer_switch_get_action(keypos_t key);
|
||||
@@ -1,93 +0,0 @@
|
||||
/*
|
||||
Copyright 2013 Jun Wako <wakojun@gmail.com>
|
||||
|
||||
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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "action.h"
|
||||
#include "action_util.h"
|
||||
#include "action_macro.h"
|
||||
#include "wait.h"
|
||||
|
||||
#ifdef DEBUG_ACTION
|
||||
# include "debug.h"
|
||||
#else
|
||||
# include "nodebug.h"
|
||||
#endif
|
||||
|
||||
#ifndef NO_ACTION_MACRO
|
||||
|
||||
# define MACRO_READ() (macro = MACRO_GET(macro_p++))
|
||||
/** \brief Action Macro Play
|
||||
*
|
||||
* FIXME: Needs doc
|
||||
*/
|
||||
void action_macro_play(const macro_t *macro_p) {
|
||||
macro_t macro = END;
|
||||
uint8_t interval = 0;
|
||||
|
||||
if (!macro_p) return;
|
||||
while (true) {
|
||||
switch (MACRO_READ()) {
|
||||
case KEY_DOWN:
|
||||
MACRO_READ();
|
||||
dprintf("KEY_DOWN(%02X)\n", macro);
|
||||
if (IS_MOD(macro)) {
|
||||
add_macro_mods(MOD_BIT(macro));
|
||||
send_keyboard_report();
|
||||
} else {
|
||||
register_code(macro);
|
||||
}
|
||||
break;
|
||||
case KEY_UP:
|
||||
MACRO_READ();
|
||||
dprintf("KEY_UP(%02X)\n", macro);
|
||||
if (IS_MOD(macro)) {
|
||||
del_macro_mods(MOD_BIT(macro));
|
||||
send_keyboard_report();
|
||||
} else {
|
||||
unregister_code(macro);
|
||||
}
|
||||
break;
|
||||
case WAIT:
|
||||
MACRO_READ();
|
||||
dprintf("WAIT(%u)\n", macro);
|
||||
{
|
||||
uint8_t ms = macro;
|
||||
while (ms--) wait_ms(1);
|
||||
}
|
||||
break;
|
||||
case INTERVAL:
|
||||
interval = MACRO_READ();
|
||||
dprintf("INTERVAL(%u)\n", interval);
|
||||
break;
|
||||
case 0x04 ... 0x73:
|
||||
dprintf("DOWN(%02X)\n", macro);
|
||||
register_code(macro);
|
||||
break;
|
||||
case 0x84 ... 0xF3:
|
||||
dprintf("UP(%02X)\n", macro);
|
||||
unregister_code(macro & 0x7F);
|
||||
break;
|
||||
case END:
|
||||
default:
|
||||
return;
|
||||
}
|
||||
// interval
|
||||
{
|
||||
uint8_t ms = interval;
|
||||
while (ms--) wait_ms(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,123 +0,0 @@
|
||||
/*
|
||||
Copyright 2013 Jun Wako <wakojun@gmail.com>
|
||||
|
||||
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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "progmem.h"
|
||||
|
||||
typedef uint8_t macro_t;
|
||||
|
||||
#define MACRO_NONE (macro_t *)0
|
||||
#define MACRO(...) \
|
||||
({ \
|
||||
static const macro_t __m[] PROGMEM = {__VA_ARGS__}; \
|
||||
&__m[0]; \
|
||||
})
|
||||
#define MACRO_GET(p) pgm_read_byte(p)
|
||||
|
||||
// Sends press when the macro key is pressed, release when release, or tap_macro when the key has been tapped
|
||||
#define MACRO_TAP_HOLD(record, press, release, tap_macro) (((record)->event.pressed) ? (((record)->tap.count <= 0 || (record)->tap.interrupted) ? (press) : MACRO_NONE) : (((record)->tap.count > 0 && !((record)->tap.interrupted)) ? (tap_macro) : (release)))
|
||||
|
||||
// Holds down the modifier mod when the macro key is held, or sends macro instead when tapped
|
||||
#define MACRO_TAP_HOLD_MOD(record, macro, mod) MACRO_TAP_HOLD(record, (MACRO(D(mod), END)), MACRO(U(mod), END), macro)
|
||||
|
||||
// Holds down the modifier mod when the macro key is held, or pressed a shifted key when tapped (eg: shift+3 for #)
|
||||
#define MACRO_TAP_SHFT_KEY_HOLD_MOD(record, key, mod) MACRO_TAP_HOLD_MOD(record, (MACRO(I(10), D(LSFT), T(key), U(LSFT), END)), mod)
|
||||
|
||||
// Momentary switch layer when held, sends macro if tapped
|
||||
#define MACRO_TAP_HOLD_LAYER(record, macro, layer) \
|
||||
(((record)->event.pressed) ? (((record)->tap.count <= 0 || (record)->tap.interrupted) ? ({ \
|
||||
layer_on((layer)); \
|
||||
MACRO_NONE; \
|
||||
}) \
|
||||
: MACRO_NONE) \
|
||||
: (((record)->tap.count > 0 && !((record)->tap.interrupted)) ? (macro) : ({ \
|
||||
layer_off((layer)); \
|
||||
MACRO_NONE; \
|
||||
})))
|
||||
|
||||
// Momentary switch layer when held, presses a shifted key when tapped (eg: shift+3 for #)
|
||||
#define MACRO_TAP_SHFT_KEY_HOLD_LAYER(record, key, layer) MACRO_TAP_HOLD_LAYER(record, MACRO(I(10), D(LSFT), T(key), U(LSFT), END), layer)
|
||||
|
||||
#ifndef NO_ACTION_MACRO
|
||||
void action_macro_play(const macro_t *macro_p);
|
||||
#else
|
||||
# define action_macro_play(macro)
|
||||
#endif
|
||||
|
||||
/* Macro commands
|
||||
* code(0x04-73) // key down(1byte)
|
||||
* code(0x04-73) | 0x80 // key up(1byte)
|
||||
* { KEY_DOWN, code(0x04-0xff) } // key down(2bytes)
|
||||
* { KEY_UP, code(0x04-0xff) } // key up(2bytes)
|
||||
* WAIT // wait milli-seconds
|
||||
* INTERVAL // set interval between macro commands
|
||||
* END // stop macro execution
|
||||
*
|
||||
* Ideas(Not implemented):
|
||||
* modifiers
|
||||
* system usage
|
||||
* consumer usage
|
||||
* unicode usage
|
||||
* function call
|
||||
* conditionals
|
||||
* loop
|
||||
*/
|
||||
enum macro_command_id {
|
||||
/* 0x00 - 0x03 */
|
||||
END = 0x00,
|
||||
KEY_DOWN,
|
||||
KEY_UP,
|
||||
|
||||
/* 0x04 - 0x73 (reserved for keycode down) */
|
||||
|
||||
/* 0x74 - 0x83 */
|
||||
WAIT = 0x74,
|
||||
INTERVAL,
|
||||
|
||||
/* 0x84 - 0xf3 (reserved for keycode up) */
|
||||
|
||||
/* 0xf4 - 0xff */
|
||||
};
|
||||
|
||||
/* TODO: keycode:0x04-0x73 can be handled by 1byte command else 2bytes are needed
|
||||
* if keycode between 0x04 and 0x73
|
||||
* keycode / (keycode|0x80)
|
||||
* else
|
||||
* {KEY_DOWN, keycode} / {KEY_UP, keycode}
|
||||
*/
|
||||
#define DOWN(key) KEY_DOWN, (key)
|
||||
#define UP(key) KEY_UP, (key)
|
||||
#define TYPE(key) DOWN(key), UP(key)
|
||||
#define WAIT(ms) WAIT, (ms)
|
||||
#define INTERVAL(ms) INTERVAL, (ms)
|
||||
|
||||
/* key down */
|
||||
#define D(key) DOWN(KC_##key)
|
||||
/* key up */
|
||||
#define U(key) UP(KC_##key)
|
||||
/* key type */
|
||||
#define T(key) TYPE(KC_##key)
|
||||
/* wait */
|
||||
#define W(ms) WAIT(ms)
|
||||
/* interval */
|
||||
#define I(ms) INTERVAL(ms)
|
||||
|
||||
/* for backward comaptibility */
|
||||
#define MD(key) DOWN(KC_##key)
|
||||
#define MU(key) UP(KC_##key)
|
||||
@@ -1,426 +0,0 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "action.h"
|
||||
#include "action_layer.h"
|
||||
#include "action_tapping.h"
|
||||
#include "keycode.h"
|
||||
#include "timer.h"
|
||||
|
||||
#ifdef DEBUG_ACTION
|
||||
# include "debug.h"
|
||||
#else
|
||||
# include "nodebug.h"
|
||||
#endif
|
||||
|
||||
#ifndef NO_ACTION_TAPPING
|
||||
|
||||
# define IS_TAPPING() !IS_NOEVENT(tapping_key.event)
|
||||
# define IS_TAPPING_PRESSED() (IS_TAPPING() && tapping_key.event.pressed)
|
||||
# define IS_TAPPING_RELEASED() (IS_TAPPING() && !tapping_key.event.pressed)
|
||||
# define IS_TAPPING_KEY(k) (IS_TAPPING() && KEYEQ(tapping_key.event.key, (k)))
|
||||
|
||||
__attribute__((weak)) uint16_t get_tapping_term(uint16_t keycode, keyrecord_t *record) { return TAPPING_TERM; }
|
||||
|
||||
# ifdef TAPPING_TERM_PER_KEY
|
||||
# define WITHIN_TAPPING_TERM(e) (TIMER_DIFF_16(e.time, tapping_key.event.time) < get_tapping_term(get_event_keycode(tapping_key.event, false), &tapping_key))
|
||||
# else
|
||||
# define WITHIN_TAPPING_TERM(e) (TIMER_DIFF_16(e.time, tapping_key.event.time) < TAPPING_TERM)
|
||||
# endif
|
||||
|
||||
# ifdef TAPPING_FORCE_HOLD_PER_KEY
|
||||
__attribute__((weak)) bool get_tapping_force_hold(uint16_t keycode, keyrecord_t *record) { return false; }
|
||||
# endif
|
||||
|
||||
# ifdef PERMISSIVE_HOLD_PER_KEY
|
||||
__attribute__((weak)) bool get_permissive_hold(uint16_t keycode, keyrecord_t *record) { return false; }
|
||||
# endif
|
||||
|
||||
static keyrecord_t tapping_key = {};
|
||||
static keyrecord_t waiting_buffer[WAITING_BUFFER_SIZE] = {};
|
||||
static uint8_t waiting_buffer_head = 0;
|
||||
static uint8_t waiting_buffer_tail = 0;
|
||||
|
||||
static bool process_tapping(keyrecord_t *record);
|
||||
static bool waiting_buffer_enq(keyrecord_t record);
|
||||
static void waiting_buffer_clear(void);
|
||||
static bool waiting_buffer_typed(keyevent_t event);
|
||||
static bool waiting_buffer_has_anykey_pressed(void);
|
||||
static void waiting_buffer_scan_tap(void);
|
||||
static void debug_tapping_key(void);
|
||||
static void debug_waiting_buffer(void);
|
||||
|
||||
/** \brief Action Tapping Process
|
||||
*
|
||||
* FIXME: Needs doc
|
||||
*/
|
||||
void action_tapping_process(keyrecord_t record) {
|
||||
if (process_tapping(&record)) {
|
||||
if (!IS_NOEVENT(record.event)) {
|
||||
debug("processed: ");
|
||||
debug_record(record);
|
||||
debug("\n");
|
||||
}
|
||||
} else {
|
||||
if (!waiting_buffer_enq(record)) {
|
||||
// clear all in case of overflow.
|
||||
debug("OVERFLOW: CLEAR ALL STATES\n");
|
||||
clear_keyboard();
|
||||
waiting_buffer_clear();
|
||||
tapping_key = (keyrecord_t){};
|
||||
}
|
||||
}
|
||||
|
||||
// process waiting_buffer
|
||||
if (!IS_NOEVENT(record.event) && waiting_buffer_head != waiting_buffer_tail) {
|
||||
debug("---- action_exec: process waiting_buffer -----\n");
|
||||
}
|
||||
for (; waiting_buffer_tail != waiting_buffer_head; waiting_buffer_tail = (waiting_buffer_tail + 1) % WAITING_BUFFER_SIZE) {
|
||||
if (process_tapping(&waiting_buffer[waiting_buffer_tail])) {
|
||||
debug("processed: waiting_buffer[");
|
||||
debug_dec(waiting_buffer_tail);
|
||||
debug("] = ");
|
||||
debug_record(waiting_buffer[waiting_buffer_tail]);
|
||||
debug("\n\n");
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!IS_NOEVENT(record.event)) {
|
||||
debug("\n");
|
||||
}
|
||||
}
|
||||
|
||||
/** \brief Tapping
|
||||
*
|
||||
* Rule: Tap key is typed(pressed and released) within TAPPING_TERM.
|
||||
* (without interfering by typing other key)
|
||||
*/
|
||||
/* return true when key event is processed or consumed. */
|
||||
bool process_tapping(keyrecord_t *keyp) {
|
||||
keyevent_t event = keyp->event;
|
||||
|
||||
// if tapping
|
||||
if (IS_TAPPING_PRESSED()) {
|
||||
if (WITHIN_TAPPING_TERM(event)) {
|
||||
if (tapping_key.tap.count == 0) {
|
||||
if (IS_TAPPING_KEY(event.key) && !event.pressed) {
|
||||
// first tap!
|
||||
debug("Tapping: First tap(0->1).\n");
|
||||
tapping_key.tap.count = 1;
|
||||
debug_tapping_key();
|
||||
process_record(&tapping_key);
|
||||
|
||||
// copy tapping state
|
||||
keyp->tap = tapping_key.tap;
|
||||
// enqueue
|
||||
return false;
|
||||
}
|
||||
/* Process a key typed within TAPPING_TERM
|
||||
* This can register the key before settlement of tapping,
|
||||
* useful for long TAPPING_TERM but may prevent fast typing.
|
||||
*/
|
||||
# if defined(TAPPING_TERM_PER_KEY) || (TAPPING_TERM >= 500) || defined(PERMISSIVE_HOLD) || defined(PERMISSIVE_HOLD_PER_KEY)
|
||||
else if (((
|
||||
# ifdef TAPPING_TERM_PER_KEY
|
||||
get_tapping_term(get_event_keycode(tapping_key.event, false), keyp)
|
||||
# else
|
||||
TAPPING_TERM
|
||||
# endif
|
||||
>= 500)
|
||||
|
||||
# ifdef PERMISSIVE_HOLD_PER_KEY
|
||||
|| get_permissive_hold(get_event_keycode(tapping_key.event, false), keyp)
|
||||
# elif defined(PERMISSIVE_HOLD)
|
||||
|| true
|
||||
# endif
|
||||
) &&
|
||||
IS_RELEASED(event) && waiting_buffer_typed(event)) {
|
||||
debug("Tapping: End. No tap. Interfered by typing key\n");
|
||||
process_record(&tapping_key);
|
||||
tapping_key = (keyrecord_t){};
|
||||
debug_tapping_key();
|
||||
// enqueue
|
||||
return false;
|
||||
}
|
||||
# endif
|
||||
/* Process release event of a key pressed before tapping starts
|
||||
* Without this unexpected repeating will occur with having fast repeating setting
|
||||
* https://github.com/tmk/tmk_keyboard/issues/60
|
||||
*/
|
||||
else if (IS_RELEASED(event) && !waiting_buffer_typed(event)) {
|
||||
// Modifier should be retained till end of this tapping.
|
||||
action_t action = layer_switch_get_action(event.key);
|
||||
switch (action.kind.id) {
|
||||
case ACT_LMODS:
|
||||
case ACT_RMODS:
|
||||
if (action.key.mods && !action.key.code) return false;
|
||||
if (IS_MOD(action.key.code)) return false;
|
||||
break;
|
||||
case ACT_LMODS_TAP:
|
||||
case ACT_RMODS_TAP:
|
||||
if (action.key.mods && keyp->tap.count == 0) return false;
|
||||
if (IS_MOD(action.key.code)) return false;
|
||||
break;
|
||||
}
|
||||
// Release of key should be process immediately.
|
||||
debug("Tapping: release event of a key pressed before tapping\n");
|
||||
process_record(keyp);
|
||||
return true;
|
||||
} else {
|
||||
// set interrupted flag when other key preesed during tapping
|
||||
if (event.pressed) {
|
||||
tapping_key.tap.interrupted = true;
|
||||
}
|
||||
// enqueue
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// tap_count > 0
|
||||
else {
|
||||
if (IS_TAPPING_KEY(event.key) && !event.pressed) {
|
||||
debug("Tapping: Tap release(");
|
||||
debug_dec(tapping_key.tap.count);
|
||||
debug(")\n");
|
||||
keyp->tap = tapping_key.tap;
|
||||
process_record(keyp);
|
||||
tapping_key = *keyp;
|
||||
debug_tapping_key();
|
||||
return true;
|
||||
} else if (is_tap_key(event.key) && event.pressed) {
|
||||
if (tapping_key.tap.count > 1) {
|
||||
debug("Tapping: Start new tap with releasing last tap(>1).\n");
|
||||
// unregister key
|
||||
process_record(&(keyrecord_t){.tap = tapping_key.tap, .event.key = tapping_key.event.key, .event.time = event.time, .event.pressed = false});
|
||||
} else {
|
||||
debug("Tapping: Start while last tap(1).\n");
|
||||
}
|
||||
tapping_key = *keyp;
|
||||
waiting_buffer_scan_tap();
|
||||
debug_tapping_key();
|
||||
return true;
|
||||
} else {
|
||||
if (!IS_NOEVENT(event)) {
|
||||
debug("Tapping: key event while last tap(>0).\n");
|
||||
}
|
||||
process_record(keyp);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// after TAPPING_TERM
|
||||
else {
|
||||
if (tapping_key.tap.count == 0) {
|
||||
debug("Tapping: End. Timeout. Not tap(0): ");
|
||||
debug_event(event);
|
||||
debug("\n");
|
||||
process_record(&tapping_key);
|
||||
tapping_key = (keyrecord_t){};
|
||||
debug_tapping_key();
|
||||
return false;
|
||||
} else {
|
||||
if (IS_TAPPING_KEY(event.key) && !event.pressed) {
|
||||
debug("Tapping: End. last timeout tap release(>0).");
|
||||
keyp->tap = tapping_key.tap;
|
||||
process_record(keyp);
|
||||
tapping_key = (keyrecord_t){};
|
||||
return true;
|
||||
} else if (is_tap_key(event.key) && event.pressed) {
|
||||
if (tapping_key.tap.count > 1) {
|
||||
debug("Tapping: Start new tap with releasing last timeout tap(>1).\n");
|
||||
// unregister key
|
||||
process_record(&(keyrecord_t){.tap = tapping_key.tap, .event.key = tapping_key.event.key, .event.time = event.time, .event.pressed = false});
|
||||
} else {
|
||||
debug("Tapping: Start while last timeout tap(1).\n");
|
||||
}
|
||||
tapping_key = *keyp;
|
||||
waiting_buffer_scan_tap();
|
||||
debug_tapping_key();
|
||||
return true;
|
||||
} else {
|
||||
if (!IS_NOEVENT(event)) {
|
||||
debug("Tapping: key event while last timeout tap(>0).\n");
|
||||
}
|
||||
process_record(keyp);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (IS_TAPPING_RELEASED()) {
|
||||
if (WITHIN_TAPPING_TERM(event)) {
|
||||
if (event.pressed) {
|
||||
if (IS_TAPPING_KEY(event.key)) {
|
||||
//# ifndef TAPPING_FORCE_HOLD
|
||||
# if !defined(TAPPING_FORCE_HOLD) || defined(TAPPING_FORCE_HOLD_PER_KEY)
|
||||
if (
|
||||
# ifdef TAPPING_FORCE_HOLD_PER_KEY
|
||||
!get_tapping_force_hold(get_event_keycode(tapping_key.event, false), keyp) &&
|
||||
# endif
|
||||
!tapping_key.tap.interrupted && tapping_key.tap.count > 0) {
|
||||
// sequential tap.
|
||||
keyp->tap = tapping_key.tap;
|
||||
if (keyp->tap.count < 15) keyp->tap.count += 1;
|
||||
debug("Tapping: Tap press(");
|
||||
debug_dec(keyp->tap.count);
|
||||
debug(")\n");
|
||||
process_record(keyp);
|
||||
tapping_key = *keyp;
|
||||
debug_tapping_key();
|
||||
return true;
|
||||
}
|
||||
# endif
|
||||
// FIX: start new tap again
|
||||
tapping_key = *keyp;
|
||||
return true;
|
||||
} else if (is_tap_key(event.key)) {
|
||||
// Sequential tap can be interfered with other tap key.
|
||||
debug("Tapping: Start with interfering other tap.\n");
|
||||
tapping_key = *keyp;
|
||||
waiting_buffer_scan_tap();
|
||||
debug_tapping_key();
|
||||
return true;
|
||||
} else {
|
||||
// should none in buffer
|
||||
// FIX: interrupted when other key is pressed
|
||||
tapping_key.tap.interrupted = true;
|
||||
process_record(keyp);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (!IS_NOEVENT(event)) debug("Tapping: other key just after tap.\n");
|
||||
process_record(keyp);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
// FIX: process_action here?
|
||||
// timeout. no sequential tap.
|
||||
debug("Tapping: End(Timeout after releasing last tap): ");
|
||||
debug_event(event);
|
||||
debug("\n");
|
||||
tapping_key = (keyrecord_t){};
|
||||
debug_tapping_key();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// not tapping state
|
||||
else {
|
||||
if (event.pressed && is_tap_key(event.key)) {
|
||||
debug("Tapping: Start(Press tap key).\n");
|
||||
tapping_key = *keyp;
|
||||
process_record_tap_hint(&tapping_key);
|
||||
waiting_buffer_scan_tap();
|
||||
debug_tapping_key();
|
||||
return true;
|
||||
} else {
|
||||
process_record(keyp);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** \brief Waiting buffer enq
|
||||
*
|
||||
* FIXME: Needs docs
|
||||
*/
|
||||
bool waiting_buffer_enq(keyrecord_t record) {
|
||||
if (IS_NOEVENT(record.event)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((waiting_buffer_head + 1) % WAITING_BUFFER_SIZE == waiting_buffer_tail) {
|
||||
debug("waiting_buffer_enq: Over flow.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
waiting_buffer[waiting_buffer_head] = record;
|
||||
waiting_buffer_head = (waiting_buffer_head + 1) % WAITING_BUFFER_SIZE;
|
||||
|
||||
debug("waiting_buffer_enq: ");
|
||||
debug_waiting_buffer();
|
||||
return true;
|
||||
}
|
||||
|
||||
/** \brief Waiting buffer clear
|
||||
*
|
||||
* FIXME: Needs docs
|
||||
*/
|
||||
void waiting_buffer_clear(void) {
|
||||
waiting_buffer_head = 0;
|
||||
waiting_buffer_tail = 0;
|
||||
}
|
||||
|
||||
/** \brief Waiting buffer typed
|
||||
*
|
||||
* FIXME: Needs docs
|
||||
*/
|
||||
bool waiting_buffer_typed(keyevent_t event) {
|
||||
for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) {
|
||||
if (KEYEQ(event.key, waiting_buffer[i].event.key) && event.pressed != waiting_buffer[i].event.pressed) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** \brief Waiting buffer has anykey pressed
|
||||
*
|
||||
* FIXME: Needs docs
|
||||
*/
|
||||
__attribute__((unused)) bool waiting_buffer_has_anykey_pressed(void) {
|
||||
for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) {
|
||||
if (waiting_buffer[i].event.pressed) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** \brief Scan buffer for tapping
|
||||
*
|
||||
* FIXME: Needs docs
|
||||
*/
|
||||
void waiting_buffer_scan_tap(void) {
|
||||
// tapping already is settled
|
||||
if (tapping_key.tap.count > 0) return;
|
||||
// invalid state: tapping_key released && tap.count == 0
|
||||
if (!tapping_key.event.pressed) return;
|
||||
|
||||
for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) {
|
||||
if (IS_TAPPING_KEY(waiting_buffer[i].event.key) && !waiting_buffer[i].event.pressed && WITHIN_TAPPING_TERM(waiting_buffer[i].event)) {
|
||||
tapping_key.tap.count = 1;
|
||||
waiting_buffer[i].tap.count = 1;
|
||||
process_record(&tapping_key);
|
||||
|
||||
debug("waiting_buffer_scan_tap: found at [");
|
||||
debug_dec(i);
|
||||
debug("]\n");
|
||||
debug_waiting_buffer();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** \brief Tapping key debug print
|
||||
*
|
||||
* FIXME: Needs docs
|
||||
*/
|
||||
static void debug_tapping_key(void) {
|
||||
debug("TAPPING_KEY=");
|
||||
debug_record(tapping_key);
|
||||
debug("\n");
|
||||
}
|
||||
|
||||
/** \brief Waiting buffer debug print
|
||||
*
|
||||
* FIXME: Needs docs
|
||||
*/
|
||||
static void debug_waiting_buffer(void) {
|
||||
debug("{ ");
|
||||
for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) {
|
||||
debug("[");
|
||||
debug_dec(i);
|
||||
debug("]=");
|
||||
debug_record(waiting_buffer[i]);
|
||||
debug(" ");
|
||||
}
|
||||
debug("}\n");
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
Copyright 2013 Jun Wako <wakojun@gmail.com>
|
||||
|
||||
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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/* period of tapping(ms) */
|
||||
#ifndef TAPPING_TERM
|
||||
# define TAPPING_TERM 200
|
||||
#endif
|
||||
|
||||
/* tap count needed for toggling a feature */
|
||||
#ifndef TAPPING_TOGGLE
|
||||
# define TAPPING_TOGGLE 5
|
||||
#endif
|
||||
|
||||
#define WAITING_BUFFER_SIZE 8
|
||||
|
||||
#ifndef NO_ACTION_TAPPING
|
||||
uint16_t get_event_keycode(keyevent_t event, bool update_layer_cache);
|
||||
void action_tapping_process(keyrecord_t record);
|
||||
|
||||
uint16_t get_tapping_term(uint16_t keycode, keyrecord_t *record);
|
||||
bool get_permissive_hold(uint16_t keycode, keyrecord_t *record);
|
||||
bool get_ignore_mod_tap_interrupt(uint16_t keycode, keyrecord_t *record);
|
||||
bool get_tapping_force_hold(uint16_t keycode, keyrecord_t *record);
|
||||
bool get_retro_tapping(uint16_t keycode, keyrecord_t *record);
|
||||
#endif
|
||||
@@ -1,404 +0,0 @@
|
||||
/*
|
||||
Copyright 2013 Jun Wako <wakojun@gmail.com>
|
||||
|
||||
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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "host.h"
|
||||
#include "report.h"
|
||||
#include "debug.h"
|
||||
#include "action_util.h"
|
||||
#include "action_layer.h"
|
||||
#include "timer.h"
|
||||
#include "keycode_config.h"
|
||||
#include "qmk_settings.h"
|
||||
|
||||
extern keymap_config_t keymap_config;
|
||||
|
||||
static uint8_t real_mods = 0;
|
||||
static uint8_t weak_mods = 0;
|
||||
static uint8_t macro_mods = 0;
|
||||
|
||||
#ifdef USB_6KRO_ENABLE
|
||||
# define RO_ADD(a, b) ((a + b) % KEYBOARD_REPORT_KEYS)
|
||||
# define RO_SUB(a, b) ((a - b + KEYBOARD_REPORT_KEYS) % KEYBOARD_REPORT_KEYS)
|
||||
# define RO_INC(a) RO_ADD(a, 1)
|
||||
# define RO_DEC(a) RO_SUB(a, 1)
|
||||
static int8_t cb_head = 0;
|
||||
static int8_t cb_tail = 0;
|
||||
static int8_t cb_count = 0;
|
||||
#endif
|
||||
|
||||
// TODO: pointer variable is not needed
|
||||
// report_keyboard_t keyboard_report = {};
|
||||
report_keyboard_t *keyboard_report = &(report_keyboard_t){};
|
||||
|
||||
extern inline void add_key(uint8_t key);
|
||||
extern inline void del_key(uint8_t key);
|
||||
extern inline void clear_keys(void);
|
||||
|
||||
#ifndef NO_ACTION_ONESHOT
|
||||
static uint8_t oneshot_mods = 0;
|
||||
static uint8_t oneshot_locked_mods = 0;
|
||||
uint8_t get_oneshot_locked_mods(void) { return oneshot_locked_mods; }
|
||||
void set_oneshot_locked_mods(uint8_t mods) {
|
||||
if (mods != oneshot_locked_mods) {
|
||||
oneshot_locked_mods = mods;
|
||||
oneshot_locked_mods_changed_kb(oneshot_locked_mods);
|
||||
}
|
||||
}
|
||||
void clear_oneshot_locked_mods(void) {
|
||||
if (oneshot_locked_mods) {
|
||||
oneshot_locked_mods = 0;
|
||||
oneshot_locked_mods_changed_kb(oneshot_locked_mods);
|
||||
}
|
||||
}
|
||||
static uint16_t oneshot_time = 0;
|
||||
bool has_oneshot_mods_timed_out(void) { return QS_oneshot_timeout > 0 && TIMER_DIFF_16(timer_read(), oneshot_time) >= QS_oneshot_timeout; }
|
||||
#endif
|
||||
|
||||
/* oneshot layer */
|
||||
#ifndef NO_ACTION_ONESHOT
|
||||
/** \brief oneshot_layer_data bits
|
||||
* LLLL LSSS
|
||||
* where:
|
||||
* L => are layer bits
|
||||
* S => oneshot state bits
|
||||
*/
|
||||
static int8_t oneshot_layer_data = 0;
|
||||
|
||||
inline uint8_t get_oneshot_layer(void) { return oneshot_layer_data >> 3; }
|
||||
inline uint8_t get_oneshot_layer_state(void) { return oneshot_layer_data & 0b111; }
|
||||
|
||||
# ifdef SWAP_HANDS_ENABLE
|
||||
enum {
|
||||
SHO_OFF,
|
||||
SHO_ACTIVE, // Swap hands button was pressed, and we didn't send any swapped keys yet
|
||||
SHO_PRESSED, // Swap hands button is currently pressed
|
||||
SHO_USED, // Swap hands button is still pressed, and we already sent swapped keys
|
||||
} swap_hands_oneshot = SHO_OFF;
|
||||
# endif
|
||||
|
||||
static uint16_t oneshot_layer_time = 0;
|
||||
inline bool has_oneshot_layer_timed_out() { return TIMER_DIFF_16(timer_read(), oneshot_layer_time) >= QS_oneshot_timeout && !(get_oneshot_layer_state() & ONESHOT_TOGGLED); }
|
||||
# ifdef SWAP_HANDS_ENABLE
|
||||
static uint16_t oneshot_swaphands_time = 0;
|
||||
inline bool has_oneshot_swaphands_timed_out() { return TIMER_DIFF_16(timer_read(), oneshot_swaphands_time) >= QS_oneshot_timeout && (swap_hands_oneshot == SHO_ACTIVE); }
|
||||
# endif
|
||||
|
||||
# ifdef SWAP_HANDS_ENABLE
|
||||
|
||||
void set_oneshot_swaphands(void) {
|
||||
swap_hands_oneshot = SHO_PRESSED;
|
||||
swap_hands = true;
|
||||
oneshot_swaphands_time = timer_read();
|
||||
if (oneshot_layer_time != 0) {
|
||||
oneshot_layer_time = oneshot_swaphands_time;
|
||||
}
|
||||
}
|
||||
|
||||
void release_oneshot_swaphands(void) {
|
||||
if (swap_hands_oneshot == SHO_PRESSED) {
|
||||
swap_hands_oneshot = SHO_ACTIVE;
|
||||
}
|
||||
if (swap_hands_oneshot == SHO_USED) {
|
||||
clear_oneshot_swaphands();
|
||||
}
|
||||
}
|
||||
|
||||
void use_oneshot_swaphands(void) {
|
||||
if (swap_hands_oneshot == SHO_PRESSED) {
|
||||
swap_hands_oneshot = SHO_USED;
|
||||
}
|
||||
if (swap_hands_oneshot == SHO_ACTIVE) {
|
||||
clear_oneshot_swaphands();
|
||||
}
|
||||
}
|
||||
|
||||
void clear_oneshot_swaphands(void) {
|
||||
swap_hands_oneshot = SHO_OFF;
|
||||
swap_hands = false;
|
||||
oneshot_swaphands_time = 0;
|
||||
}
|
||||
|
||||
# endif
|
||||
|
||||
/** \brief Set oneshot layer
|
||||
*
|
||||
* FIXME: needs doc
|
||||
*/
|
||||
void set_oneshot_layer(uint8_t layer, uint8_t state) {
|
||||
if (!keymap_config.oneshot_disable) {
|
||||
oneshot_layer_data = layer << 3 | state;
|
||||
layer_on(layer);
|
||||
oneshot_layer_time = timer_read();
|
||||
oneshot_layer_changed_kb(get_oneshot_layer());
|
||||
} else {
|
||||
layer_on(layer);
|
||||
}
|
||||
}
|
||||
/** \brief Reset oneshot layer
|
||||
*
|
||||
* FIXME: needs doc
|
||||
*/
|
||||
void reset_oneshot_layer(void) {
|
||||
oneshot_layer_data = 0;
|
||||
oneshot_layer_time = 0;
|
||||
oneshot_layer_changed_kb(get_oneshot_layer());
|
||||
}
|
||||
/** \brief Clear oneshot layer
|
||||
*
|
||||
* FIXME: needs doc
|
||||
*/
|
||||
void clear_oneshot_layer_state(oneshot_fullfillment_t state) {
|
||||
uint8_t start_state = oneshot_layer_data;
|
||||
oneshot_layer_data &= ~state;
|
||||
if ((!get_oneshot_layer_state() && start_state != oneshot_layer_data) || keymap_config.oneshot_disable) {
|
||||
layer_off(get_oneshot_layer());
|
||||
reset_oneshot_layer();
|
||||
}
|
||||
}
|
||||
/** \brief Is oneshot layer active
|
||||
*
|
||||
* FIXME: needs doc
|
||||
*/
|
||||
bool is_oneshot_layer_active(void) { return get_oneshot_layer_state(); }
|
||||
|
||||
/** \brief set oneshot
|
||||
*
|
||||
* FIXME: needs doc
|
||||
*/
|
||||
void oneshot_set(bool active) {
|
||||
if (keymap_config.oneshot_disable != active) {
|
||||
keymap_config.oneshot_disable = active;
|
||||
eeconfig_update_keymap(keymap_config.raw);
|
||||
dprintf("Oneshot: active: %d\n", active);
|
||||
}
|
||||
}
|
||||
|
||||
/** \brief toggle oneshot
|
||||
*
|
||||
* FIXME: needs doc
|
||||
*/
|
||||
void oneshot_toggle(void) { oneshot_set(!keymap_config.oneshot_disable); }
|
||||
|
||||
/** \brief enable oneshot
|
||||
*
|
||||
* FIXME: needs doc
|
||||
*/
|
||||
void oneshot_enable(void) { oneshot_set(true); }
|
||||
|
||||
/** \brief disable oneshot
|
||||
*
|
||||
* FIXME: needs doc
|
||||
*/
|
||||
void oneshot_disable(void) { oneshot_set(false); }
|
||||
|
||||
bool is_oneshot_enabled(void) { return keymap_config.oneshot_disable; }
|
||||
|
||||
#endif
|
||||
|
||||
/** \brief Send keyboard report
|
||||
*
|
||||
* FIXME: needs doc
|
||||
*/
|
||||
void send_keyboard_report(void) {
|
||||
keyboard_report->mods = real_mods;
|
||||
keyboard_report->mods |= weak_mods;
|
||||
keyboard_report->mods |= macro_mods;
|
||||
#ifndef NO_ACTION_ONESHOT
|
||||
if (oneshot_mods) {
|
||||
if (QS_oneshot_timeout > 0 && has_oneshot_mods_timed_out()) {
|
||||
dprintf("Oneshot: timeout\n");
|
||||
clear_oneshot_mods();
|
||||
}
|
||||
keyboard_report->mods |= oneshot_mods;
|
||||
if (has_anykey(keyboard_report)) {
|
||||
clear_oneshot_mods();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
host_keyboard_send(keyboard_report);
|
||||
}
|
||||
|
||||
/** \brief Get mods
|
||||
*
|
||||
* FIXME: needs doc
|
||||
*/
|
||||
uint8_t get_mods(void) { return real_mods; }
|
||||
/** \brief add mods
|
||||
*
|
||||
* FIXME: needs doc
|
||||
*/
|
||||
void add_mods(uint8_t mods) { real_mods |= mods; }
|
||||
/** \brief del mods
|
||||
*
|
||||
* FIXME: needs doc
|
||||
*/
|
||||
void del_mods(uint8_t mods) { real_mods &= ~mods; }
|
||||
/** \brief set mods
|
||||
*
|
||||
* FIXME: needs doc
|
||||
*/
|
||||
void set_mods(uint8_t mods) { real_mods = mods; }
|
||||
/** \brief clear mods
|
||||
*
|
||||
* FIXME: needs doc
|
||||
*/
|
||||
void clear_mods(void) { real_mods = 0; }
|
||||
|
||||
/** \brief get weak mods
|
||||
*
|
||||
* FIXME: needs doc
|
||||
*/
|
||||
uint8_t get_weak_mods(void) { return weak_mods; }
|
||||
/** \brief add weak mods
|
||||
*
|
||||
* FIXME: needs doc
|
||||
*/
|
||||
void add_weak_mods(uint8_t mods) { weak_mods |= mods; }
|
||||
/** \brief del weak mods
|
||||
*
|
||||
* FIXME: needs doc
|
||||
*/
|
||||
void del_weak_mods(uint8_t mods) { weak_mods &= ~mods; }
|
||||
/** \brief set weak mods
|
||||
*
|
||||
* FIXME: needs doc
|
||||
*/
|
||||
void set_weak_mods(uint8_t mods) { weak_mods = mods; }
|
||||
/** \brief clear weak mods
|
||||
*
|
||||
* FIXME: needs doc
|
||||
*/
|
||||
void clear_weak_mods(void) { weak_mods = 0; }
|
||||
|
||||
/* macro modifier */
|
||||
/** \brief get macro mods
|
||||
*
|
||||
* FIXME: needs doc
|
||||
*/
|
||||
uint8_t get_macro_mods(void) { return macro_mods; }
|
||||
/** \brief add macro mods
|
||||
*
|
||||
* FIXME: needs doc
|
||||
*/
|
||||
void add_macro_mods(uint8_t mods) { macro_mods |= mods; }
|
||||
/** \brief del macro mods
|
||||
*
|
||||
* FIXME: needs doc
|
||||
*/
|
||||
void del_macro_mods(uint8_t mods) { macro_mods &= ~mods; }
|
||||
/** \brief set macro mods
|
||||
*
|
||||
* FIXME: needs doc
|
||||
*/
|
||||
void set_macro_mods(uint8_t mods) { macro_mods = mods; }
|
||||
/** \brief clear macro mods
|
||||
*
|
||||
* FIXME: needs doc
|
||||
*/
|
||||
void clear_macro_mods(void) { macro_mods = 0; }
|
||||
|
||||
#ifndef NO_ACTION_ONESHOT
|
||||
/** \brief get oneshot mods
|
||||
*
|
||||
* FIXME: needs doc
|
||||
*/
|
||||
uint8_t get_oneshot_mods(void) { return oneshot_mods; }
|
||||
|
||||
void add_oneshot_mods(uint8_t mods) {
|
||||
if ((oneshot_mods & mods) != mods) {
|
||||
oneshot_time = timer_read();
|
||||
oneshot_mods |= mods;
|
||||
oneshot_mods_changed_kb(mods);
|
||||
}
|
||||
}
|
||||
|
||||
void del_oneshot_mods(uint8_t mods) {
|
||||
if (oneshot_mods & mods) {
|
||||
oneshot_mods &= ~mods;
|
||||
oneshot_time = oneshot_mods ? timer_read() : 0;
|
||||
oneshot_mods_changed_kb(oneshot_mods);
|
||||
}
|
||||
}
|
||||
|
||||
/** \brief set oneshot mods
|
||||
*
|
||||
* FIXME: needs doc
|
||||
*/
|
||||
void set_oneshot_mods(uint8_t mods) {
|
||||
if (!keymap_config.oneshot_disable) {
|
||||
if (oneshot_mods != mods) {
|
||||
oneshot_time = timer_read();
|
||||
oneshot_mods = mods;
|
||||
oneshot_mods_changed_kb(mods);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** \brief clear oneshot mods
|
||||
*
|
||||
* FIXME: needs doc
|
||||
*/
|
||||
void clear_oneshot_mods(void) {
|
||||
if (oneshot_mods) {
|
||||
oneshot_mods = 0;
|
||||
oneshot_time = 0;
|
||||
oneshot_mods_changed_kb(oneshot_mods);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/** \brief Called when the one shot modifiers have been changed.
|
||||
*
|
||||
* \param mods Contains the active modifiers active after the change.
|
||||
*/
|
||||
__attribute__((weak)) void oneshot_locked_mods_changed_user(uint8_t mods) {}
|
||||
|
||||
/** \brief Called when the locked one shot modifiers have been changed.
|
||||
*
|
||||
* \param mods Contains the active modifiers active after the change.
|
||||
*/
|
||||
__attribute__((weak)) void oneshot_locked_mods_changed_kb(uint8_t mods) { oneshot_locked_mods_changed_user(mods); }
|
||||
|
||||
/** \brief Called when the one shot modifiers have been changed.
|
||||
*
|
||||
* \param mods Contains the active modifiers active after the change.
|
||||
*/
|
||||
__attribute__((weak)) void oneshot_mods_changed_user(uint8_t mods) {}
|
||||
|
||||
/** \brief Called when the one shot modifiers have been changed.
|
||||
*
|
||||
* \param mods Contains the active modifiers active after the change.
|
||||
*/
|
||||
__attribute__((weak)) void oneshot_mods_changed_kb(uint8_t mods) { oneshot_mods_changed_user(mods); }
|
||||
|
||||
/** \brief Called when the one shot layers have been changed.
|
||||
*
|
||||
* \param layer Contains the layer that is toggled on, or zero when toggled off.
|
||||
*/
|
||||
__attribute__((weak)) void oneshot_layer_changed_user(uint8_t layer) {}
|
||||
|
||||
/** \brief Called when the one shot layers have been changed.
|
||||
*
|
||||
* \param layer Contains the layer that is toggled on, or zero when toggled off.
|
||||
*/
|
||||
__attribute__((weak)) void oneshot_layer_changed_kb(uint8_t layer) { oneshot_layer_changed_user(layer); }
|
||||
|
||||
/** \brief inspect keyboard state
|
||||
*
|
||||
* FIXME: needs doc
|
||||
*/
|
||||
uint8_t has_anymod(void) { return bitpop(real_mods); }
|
||||
@@ -1,105 +0,0 @@
|
||||
/*
|
||||
Copyright 2013 Jun Wako <wakojun@gmail.com>
|
||||
|
||||
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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "report.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern report_keyboard_t *keyboard_report;
|
||||
|
||||
void send_keyboard_report(void);
|
||||
|
||||
/* key */
|
||||
inline void add_key(uint8_t key) { add_key_to_report(keyboard_report, key); }
|
||||
|
||||
inline void del_key(uint8_t key) { del_key_from_report(keyboard_report, key); }
|
||||
|
||||
inline void clear_keys(void) { clear_keys_from_report(keyboard_report); }
|
||||
|
||||
/* modifier */
|
||||
uint8_t get_mods(void);
|
||||
void add_mods(uint8_t mods);
|
||||
void del_mods(uint8_t mods);
|
||||
void set_mods(uint8_t mods);
|
||||
void clear_mods(void);
|
||||
|
||||
/* weak modifier */
|
||||
uint8_t get_weak_mods(void);
|
||||
void add_weak_mods(uint8_t mods);
|
||||
void del_weak_mods(uint8_t mods);
|
||||
void set_weak_mods(uint8_t mods);
|
||||
void clear_weak_mods(void);
|
||||
|
||||
/* macro modifier */
|
||||
uint8_t get_macro_mods(void);
|
||||
void add_macro_mods(uint8_t mods);
|
||||
void del_macro_mods(uint8_t mods);
|
||||
void set_macro_mods(uint8_t mods);
|
||||
void clear_macro_mods(void);
|
||||
|
||||
/* oneshot modifier */
|
||||
uint8_t get_oneshot_mods(void);
|
||||
void add_oneshot_mods(uint8_t mods);
|
||||
void del_oneshot_mods(uint8_t mods);
|
||||
void set_oneshot_mods(uint8_t mods);
|
||||
void clear_oneshot_mods(void);
|
||||
bool has_oneshot_mods_timed_out(void);
|
||||
|
||||
uint8_t get_oneshot_locked_mods(void);
|
||||
void set_oneshot_locked_mods(uint8_t mods);
|
||||
void clear_oneshot_locked_mods(void);
|
||||
|
||||
typedef enum { ONESHOT_PRESSED = 0b01, ONESHOT_OTHER_KEY_PRESSED = 0b10, ONESHOT_START = 0b11, ONESHOT_TOGGLED = 0b100 } oneshot_fullfillment_t;
|
||||
void set_oneshot_layer(uint8_t layer, uint8_t state);
|
||||
uint8_t get_oneshot_layer(void);
|
||||
void clear_oneshot_layer_state(oneshot_fullfillment_t state);
|
||||
void reset_oneshot_layer(void);
|
||||
bool is_oneshot_layer_active(void);
|
||||
uint8_t get_oneshot_layer_state(void);
|
||||
bool has_oneshot_layer_timed_out(void);
|
||||
bool has_oneshot_swaphands_timed_out(void);
|
||||
|
||||
void oneshot_locked_mods_changed_user(uint8_t mods);
|
||||
void oneshot_locked_mods_changed_kb(uint8_t mods);
|
||||
void oneshot_mods_changed_user(uint8_t mods);
|
||||
void oneshot_mods_changed_kb(uint8_t mods);
|
||||
void oneshot_layer_changed_user(uint8_t layer);
|
||||
void oneshot_layer_changed_kb(uint8_t layer);
|
||||
|
||||
void oneshot_toggle(void);
|
||||
void oneshot_enable(void);
|
||||
void oneshot_disable(void);
|
||||
bool is_oneshot_enabled(void);
|
||||
|
||||
/* inspect */
|
||||
uint8_t has_anymod(void);
|
||||
|
||||
#ifdef SWAP_HANDS_ENABLE
|
||||
void set_oneshot_swaphands(void);
|
||||
void release_oneshot_swaphands(void);
|
||||
void use_oneshot_swaphands(void);
|
||||
void clear_oneshot_swaphands(void);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,34 +0,0 @@
|
||||
/* Copyright 2012 Jun Wako <wakojun@gmail.com> */
|
||||
/* Very basic print functions, intended to be used with usb_debug_only.c
|
||||
* http://www.pjrc.com/teensy/
|
||||
* Copyright (c) 2008 PJRC.COM, LLC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "arm_atsam/printf.h"
|
||||
|
||||
// Create user & normal print defines
|
||||
#define xprintf(fmt, ...) __xprintf(fmt, ##__VA_ARGS__)
|
||||
#define print(s) __xprintf(s)
|
||||
#define println(s) __xprintf(s "\r\n")
|
||||
#define uprint(s) __xprintf(s)
|
||||
#define uprintln(s) __xprintf(s "\r\n")
|
||||
#define uprintf(fmt, ...) __xprintf(fmt, ##__VA_ARGS__)
|
||||
@@ -0,0 +1,19 @@
|
||||
/* Copyright 2021 Simon Arlott
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
// The platform is 32-bit, so prefer 32-bit timers to avoid overflow
|
||||
#define FAST_TIMER_T_SIZE 32
|
||||
@@ -0,0 +1,37 @@
|
||||
/* Copyright 2021 QMK
|
||||
*
|
||||
* 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "samd51j18a.h"
|
||||
|
||||
static __inline__ uint8_t __interrupt_disable__(void) {
|
||||
__disable_irq();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static __inline__ void __interrupt_enable__(const uint8_t *__s) {
|
||||
__enable_irq();
|
||||
|
||||
__asm__ volatile("" ::: "memory");
|
||||
(void)__s;
|
||||
}
|
||||
|
||||
#define ATOMIC_BLOCK(type) for (type, __ToDo = __interrupt_disable__(); __ToDo; __ToDo = 0)
|
||||
#define ATOMIC_FORCEON uint8_t sreg_save __attribute__((__cleanup__(__interrupt_enable__))) = 0
|
||||
|
||||
#define ATOMIC_BLOCK_RESTORESTATE _Static_assert(0, "ATOMIC_BLOCK_RESTORESTATE not implemented")
|
||||
#define ATOMIC_BLOCK_FORCEON ATOMIC_BLOCK(ATOMIC_FORCEON)
|
||||
@@ -0,0 +1,71 @@
|
||||
/* Copyright 2021 QMK
|
||||
*
|
||||
* 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "stdint.h"
|
||||
#include "samd51j18a.h"
|
||||
|
||||
#include "pin_defs.h"
|
||||
|
||||
typedef uint8_t pin_t;
|
||||
|
||||
#define SAMD_PORT(pin) ((pin & 0x20) >> 5)
|
||||
#define SAMD_PIN(pin) (pin & 0x1f)
|
||||
#define SAMD_PIN_MASK(pin) (1 << (pin & 0x1f))
|
||||
|
||||
#define setPinInput(pin) \
|
||||
do { \
|
||||
PORT->Group[SAMD_PORT(pin)].PINCFG[SAMD_PIN(pin)].bit.INEN = 1; \
|
||||
PORT->Group[SAMD_PORT(pin)].DIRCLR.reg = SAMD_PIN_MASK(pin); \
|
||||
} while (0)
|
||||
|
||||
#define setPinInputHigh(pin) \
|
||||
do { \
|
||||
PORT->Group[SAMD_PORT(pin)].DIRCLR.reg = SAMD_PIN_MASK(pin); \
|
||||
PORT->Group[SAMD_PORT(pin)].OUTSET.reg = SAMD_PIN_MASK(pin); \
|
||||
PORT->Group[SAMD_PORT(pin)].PINCFG[SAMD_PIN(pin)].bit.INEN = 1; \
|
||||
PORT->Group[SAMD_PORT(pin)].PINCFG[SAMD_PIN(pin)].bit.PULLEN = 1; \
|
||||
} while (0)
|
||||
|
||||
#define setPinInputLow(pin) \
|
||||
do { \
|
||||
PORT->Group[SAMD_PORT(pin)].DIRCLR.reg = SAMD_PIN_MASK(pin); \
|
||||
PORT->Group[SAMD_PORT(pin)].OUTCLR.reg = SAMD_PIN_MASK(pin); \
|
||||
PORT->Group[SAMD_PORT(pin)].PINCFG[SAMD_PIN(pin)].bit.INEN = 1; \
|
||||
PORT->Group[SAMD_PORT(pin)].PINCFG[SAMD_PIN(pin)].bit.PULLEN = 1; \
|
||||
} while (0)
|
||||
|
||||
#define setPinOutput(pin) \
|
||||
do { \
|
||||
PORT->Group[SAMD_PORT(pin)].DIRSET.reg = SAMD_PIN_MASK(pin); \
|
||||
PORT->Group[SAMD_PORT(pin)].OUTCLR.reg = SAMD_PIN_MASK(pin); \
|
||||
} while (0)
|
||||
|
||||
#define writePinHigh(pin) \
|
||||
do { \
|
||||
PORT->Group[SAMD_PORT(pin)].OUTSET.reg = SAMD_PIN_MASK(pin); \
|
||||
} while (0)
|
||||
|
||||
#define writePinLow(pin) \
|
||||
do { \
|
||||
PORT->Group[SAMD_PORT(pin)].OUTCLR.reg = SAMD_PIN_MASK(pin); \
|
||||
} while (0)
|
||||
|
||||
#define writePin(pin, level) ((level) ? (writePinHigh(pin)) : (writePinLow(pin)))
|
||||
|
||||
#define readPin(pin) ((PORT->Group[SAMD_PORT(pin)].IN.reg & SAMD_PIN_MASK(pin)) != 0)
|
||||
|
||||
#define togglePin(pin) (PORT->Group[SAMD_PORT(pin)].OUTTGL.reg = SAMD_PIN_MASK(pin))
|
||||
@@ -0,0 +1,84 @@
|
||||
/* Copyright 2021 QMK
|
||||
*
|
||||
* 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "samd51j18a.h"
|
||||
|
||||
#define A00 PIN_PA00
|
||||
#define A01 PIN_PA01
|
||||
#define A02 PIN_PA02
|
||||
#define A03 PIN_PA03
|
||||
#define A04 PIN_PA04
|
||||
#define A05 PIN_PA05
|
||||
#define A06 PIN_PA06
|
||||
#define A07 PIN_PA07
|
||||
#define A08 PIN_PA08
|
||||
#define A09 PIN_PA09
|
||||
#define A10 PIN_PA10
|
||||
#define A11 PIN_PA11
|
||||
#define A12 PIN_PA12
|
||||
#define A13 PIN_PA13
|
||||
#define A14 PIN_PA14
|
||||
#define A15 PIN_PA15
|
||||
#define A16 PIN_PA16
|
||||
#define A17 PIN_PA17
|
||||
#define A18 PIN_PA18
|
||||
#define A19 PIN_PA19
|
||||
#define A20 PIN_PA20
|
||||
#define A21 PIN_PA21
|
||||
#define A22 PIN_PA22
|
||||
#define A23 PIN_PA23
|
||||
#define A24 PIN_PA24
|
||||
#define A25 PIN_PA25
|
||||
#define A26 PIN_PA26
|
||||
#define A27 PIN_PA27
|
||||
#define A28 PIN_PA28
|
||||
#define A29 PIN_PA29
|
||||
#define A30 PIN_PA30
|
||||
#define A31 PIN_PA31
|
||||
|
||||
#define B00 PIN_PB00
|
||||
#define B01 PIN_PB01
|
||||
#define B02 PIN_PB02
|
||||
#define B03 PIN_PB03
|
||||
#define B04 PIN_PB04
|
||||
#define B05 PIN_PB05
|
||||
#define B06 PIN_PB06
|
||||
#define B07 PIN_PB07
|
||||
#define B08 PIN_PB08
|
||||
#define B09 PIN_PB09
|
||||
#define B10 PIN_PB10
|
||||
#define B11 PIN_PB11
|
||||
#define B12 PIN_PB12
|
||||
#define B13 PIN_PB13
|
||||
#define B14 PIN_PB14
|
||||
#define B15 PIN_PB15
|
||||
#define B16 PIN_PB16
|
||||
#define B17 PIN_PB17
|
||||
#define B18 PIN_PB18
|
||||
#define B19 PIN_PB19
|
||||
#define B20 PIN_PB20
|
||||
#define B21 PIN_PB21
|
||||
#define B22 PIN_PB22
|
||||
#define B23 PIN_PB23
|
||||
#define B24 PIN_PB24
|
||||
#define B25 PIN_PB25
|
||||
#define B26 PIN_PB26
|
||||
#define B27 PIN_PB27
|
||||
#define B28 PIN_PB28
|
||||
#define B29 PIN_PB29
|
||||
#define B30 PIN_PB30
|
||||
#define B31 PIN_PB31
|
||||
@@ -0,0 +1,21 @@
|
||||
/* Copyright 2021 QMK
|
||||
*
|
||||
* 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "platform_deps.h"
|
||||
|
||||
void platform_setup(void) {
|
||||
// do nothing
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
/* Copyright 2021 QMK
|
||||
*
|
||||
* 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
// here just to please the build
|
||||
@@ -1,72 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 Massdrop Inc.
|
||||
|
||||
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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "printf.h"
|
||||
#include "sendchar.h"
|
||||
|
||||
#ifdef CONSOLE_ENABLE
|
||||
|
||||
# include "samd51j18a.h"
|
||||
# include "arm_atsam_protocol.h"
|
||||
# include <string.h>
|
||||
# include <stdarg.h>
|
||||
|
||||
void console_printf(char *fmt, ...) {
|
||||
while (udi_hid_con_b_report_trans_ongoing) {
|
||||
} // Wait for any previous transfers to complete
|
||||
|
||||
static char console_printbuf[CONSOLE_PRINTBUF_SIZE]; // Print and send buffer
|
||||
va_list va;
|
||||
int result;
|
||||
|
||||
va_start(va, fmt);
|
||||
result = vsnprintf(console_printbuf, CONSOLE_PRINTBUF_SIZE, fmt, va);
|
||||
va_end(va);
|
||||
|
||||
uint32_t irqflags;
|
||||
char * pconbuf = console_printbuf; // Pointer to start send from
|
||||
int send_out = CONSOLE_EPSIZE; // Bytes to send per transfer
|
||||
|
||||
while (result > 0) { // While not error and bytes remain
|
||||
while (udi_hid_con_b_report_trans_ongoing) {
|
||||
} // Wait for any previous transfers to complete
|
||||
|
||||
irqflags = __get_PRIMASK();
|
||||
__disable_irq();
|
||||
__DMB();
|
||||
|
||||
if (result < CONSOLE_EPSIZE) { // If remaining bytes are less than console epsize
|
||||
memset(udi_hid_con_report, 0, CONSOLE_EPSIZE); // Clear the buffer
|
||||
send_out = result; // Send remaining size
|
||||
}
|
||||
|
||||
memcpy(udi_hid_con_report, pconbuf, send_out); // Copy data into the send buffer
|
||||
|
||||
udi_hid_con_b_report_valid = 1; // Set report valid
|
||||
udi_hid_con_send_report(); // Send report
|
||||
|
||||
__DMB();
|
||||
__set_PRIMASK(irqflags);
|
||||
|
||||
result -= send_out; // Decrement result by bytes sent
|
||||
pconbuf += send_out; // Increment buffer point by bytes sent
|
||||
}
|
||||
}
|
||||
|
||||
#endif // CONSOLE_ENABLE
|
||||
|
||||
void print_set_sendchar(sendchar_func_t send) {}
|
||||
@@ -1,7 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#define CONSOLE_PRINTBUF_SIZE 512
|
||||
|
||||
void console_printf(char *fmt, ...);
|
||||
|
||||
#define __xprintf console_printf
|
||||
@@ -1 +0,0 @@
|
||||
TMK_COMMON_SRC += $(PLATFORM_COMMON_DIR)/printf.c
|
||||
@@ -0,0 +1,19 @@
|
||||
/* Copyright 2021 Simon Arlott
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
// The platform is 8-bit, so prefer 16-bit timers to reduce code size
|
||||
#define FAST_TIMER_T_SIZE 16
|
||||
@@ -17,8 +17,28 @@
|
||||
|
||||
#include <util/delay.h>
|
||||
|
||||
#define wait_ms(ms) _delay_ms(ms)
|
||||
#define wait_us(us) _delay_us(us)
|
||||
#define wait_ms(ms) \
|
||||
do { \
|
||||
if (__builtin_constant_p(ms)) { \
|
||||
_delay_ms(ms); \
|
||||
} else { \
|
||||
for (uint16_t i = ms; i > 0; i--) { \
|
||||
_delay_ms(1); \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
#define wait_us(us) \
|
||||
do { \
|
||||
if (__builtin_constant_p(us)) { \
|
||||
_delay_us(us); \
|
||||
} else { \
|
||||
for (uint16_t i = us; i > 0; i--) { \
|
||||
_delay_us(1); \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
#define wait_cpuclock(n) __builtin_avr_delay_cycles(n)
|
||||
#define CPU_CLOCK F_CPU
|
||||
|
||||
/* The AVR series GPIOs have a one clock read delay for changes in the digital input signal.
|
||||
* But here's more margin to make it two clocks. */
|
||||
@@ -26,4 +46,4 @@
|
||||
# define GPIO_INPUT_PIN_DELAY 2
|
||||
#endif
|
||||
|
||||
#define waitInputPinDelay() __builtin_avr_delay_cycles(GPIO_INPUT_PIN_DELAY)
|
||||
#define waitInputPinDelay() wait_cpuclock(GPIO_INPUT_PIN_DELAY)
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
|
||||
typedef uint8_t pin_t;
|
||||
|
||||
/* Operation of GPIO by pin. */
|
||||
|
||||
#define setPinInput(pin) (DDRx_ADDRESS(pin) &= ~_BV((pin)&0xF), PORTx_ADDRESS(pin) &= ~_BV((pin)&0xF))
|
||||
#define setPinInputHigh(pin) (DDRx_ADDRESS(pin) &= ~_BV((pin)&0xF), PORTx_ADDRESS(pin) |= _BV((pin)&0xF))
|
||||
#define setPinInputLow(pin) _Static_assert(0, "AVR processors cannot implement an input as pull low")
|
||||
@@ -32,3 +34,16 @@ typedef uint8_t pin_t;
|
||||
#define readPin(pin) ((bool)(PINx_ADDRESS(pin) & _BV((pin)&0xF)))
|
||||
|
||||
#define togglePin(pin) (PORTx_ADDRESS(pin) ^= _BV((pin)&0xF))
|
||||
|
||||
/* Operation of GPIO by port. */
|
||||
|
||||
typedef uint8_t port_data_t;
|
||||
|
||||
#define readPort(port) PINx_ADDRESS(port)
|
||||
|
||||
#define setPortBitInput(port, bit) (DDRx_ADDRESS(port) &= ~_BV((bit)&0xF), PORTx_ADDRESS(port) &= ~_BV((bit)&0xF))
|
||||
#define setPortBitInputHigh(port, bit) (DDRx_ADDRESS(port) &= ~_BV((bit)&0xF), PORTx_ADDRESS(port) |= _BV((bit)&0xF))
|
||||
#define setPortBitOutput(port, bit) (DDRx_ADDRESS(port) |= _BV((bit)&0xF))
|
||||
|
||||
#define writePortBitLow(port, bit) (PORTx_ADDRESS(port) &= ~_BV((bit)&0xF))
|
||||
#define writePortBitHigh(port, bit) (PORTx_ADDRESS(port) |= _BV((bit)&0xF))
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
/* Copyright 2021 QMK
|
||||
*
|
||||
* 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "platform_deps.h"
|
||||
|
||||
void platform_setup(void) {
|
||||
// do nothing
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/* Copyright 2021 QMK
|
||||
*
|
||||
* 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <avr/pgmspace.h>
|
||||
#include <avr/io.h>
|
||||
#include <avr/interrupt.h>
|
||||
@@ -0,0 +1,19 @@
|
||||
/* Copyright 2021 Simon Arlott
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
// The platform is 32-bit, so prefer 32-bit timers to avoid overflow
|
||||
#define FAST_TIMER_T_SIZE 32
|
||||
@@ -0,0 +1,89 @@
|
||||
/* Copyright 2021 QMK
|
||||
*
|
||||
* 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __OPTIMIZE__
|
||||
# pragma message "Compiler optimizations disabled; wait_cpuclock() won't work as designed"
|
||||
#endif
|
||||
|
||||
#define CLOCK_DELAY_NOP8 "nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t"
|
||||
|
||||
__attribute__((always_inline)) static inline void wait_cpuclock(unsigned int n) { /* n: 1..135 */
|
||||
/* The argument n must be a constant expression.
|
||||
* That way, compiler optimization will remove unnecessary code. */
|
||||
if (n < 1) {
|
||||
return;
|
||||
}
|
||||
if (n > 8) {
|
||||
unsigned int n8 = n / 8;
|
||||
n = n - n8 * 8;
|
||||
switch (n8) {
|
||||
case 16:
|
||||
asm volatile(CLOCK_DELAY_NOP8::: "memory");
|
||||
case 15:
|
||||
asm volatile(CLOCK_DELAY_NOP8::: "memory");
|
||||
case 14:
|
||||
asm volatile(CLOCK_DELAY_NOP8::: "memory");
|
||||
case 13:
|
||||
asm volatile(CLOCK_DELAY_NOP8::: "memory");
|
||||
case 12:
|
||||
asm volatile(CLOCK_DELAY_NOP8::: "memory");
|
||||
case 11:
|
||||
asm volatile(CLOCK_DELAY_NOP8::: "memory");
|
||||
case 10:
|
||||
asm volatile(CLOCK_DELAY_NOP8::: "memory");
|
||||
case 9:
|
||||
asm volatile(CLOCK_DELAY_NOP8::: "memory");
|
||||
case 8:
|
||||
asm volatile(CLOCK_DELAY_NOP8::: "memory");
|
||||
case 7:
|
||||
asm volatile(CLOCK_DELAY_NOP8::: "memory");
|
||||
case 6:
|
||||
asm volatile(CLOCK_DELAY_NOP8::: "memory");
|
||||
case 5:
|
||||
asm volatile(CLOCK_DELAY_NOP8::: "memory");
|
||||
case 4:
|
||||
asm volatile(CLOCK_DELAY_NOP8::: "memory");
|
||||
case 3:
|
||||
asm volatile(CLOCK_DELAY_NOP8::: "memory");
|
||||
case 2:
|
||||
asm volatile(CLOCK_DELAY_NOP8::: "memory");
|
||||
case 1:
|
||||
asm volatile(CLOCK_DELAY_NOP8::: "memory");
|
||||
case 0:
|
||||
break;
|
||||
}
|
||||
}
|
||||
switch (n) {
|
||||
case 8:
|
||||
asm volatile("nop" ::: "memory");
|
||||
case 7:
|
||||
asm volatile("nop" ::: "memory");
|
||||
case 6:
|
||||
asm volatile("nop" ::: "memory");
|
||||
case 5:
|
||||
asm volatile("nop" ::: "memory");
|
||||
case 4:
|
||||
asm volatile("nop" ::: "memory");
|
||||
case 3:
|
||||
asm volatile("nop" ::: "memory");
|
||||
case 2:
|
||||
asm volatile("nop" ::: "memory");
|
||||
case 1:
|
||||
asm volatile("nop" ::: "memory");
|
||||
case 0:
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <ch.h>
|
||||
#include <hal.h>
|
||||
|
||||
/* chThdSleepX of zero maps to infinite - so we map to a tiny delay to still yield */
|
||||
#define wait_ms(ms) \
|
||||
@@ -26,14 +27,23 @@
|
||||
chThdSleepMicroseconds(1); \
|
||||
} \
|
||||
} while (0)
|
||||
#define wait_us(us) \
|
||||
do { \
|
||||
if (us != 0) { \
|
||||
chThdSleepMicroseconds(us); \
|
||||
} else { \
|
||||
chThdSleepMicroseconds(1); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#ifdef WAIT_US_TIMER
|
||||
void wait_us(uint16_t duration);
|
||||
#else
|
||||
# define wait_us(us) \
|
||||
do { \
|
||||
if (us != 0) { \
|
||||
chThdSleepMicroseconds(us); \
|
||||
} else { \
|
||||
chThdSleepMicroseconds(1); \
|
||||
} \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#include "_wait.c"
|
||||
|
||||
#define CPU_CLOCK STM32_SYSCLK
|
||||
|
||||
/* For GPIOs on ARM-based MCUs, the input pins are sampled by the clock of the bus
|
||||
* to which the GPIO is connected.
|
||||
@@ -45,11 +55,8 @@
|
||||
* If the GPIO_INPUT_PIN_DELAY macro is not defined, the following default values will be used.
|
||||
* (A fairly large value of 0.25 microseconds is set.)
|
||||
*/
|
||||
|
||||
#include "wait.c"
|
||||
|
||||
#ifndef GPIO_INPUT_PIN_DELAY
|
||||
# define GPIO_INPUT_PIN_DELAY (STM32_SYSCLK / 1000000L / 4)
|
||||
# define GPIO_INPUT_PIN_DELAY (CPU_CLOCK / 1000000L / 4)
|
||||
#endif
|
||||
|
||||
#define waitInputPinDelay() wait_cpuclock(GPIO_INPUT_PIN_DELAY)
|
||||
|
||||
@@ -95,7 +95,7 @@ void enter_bootloader_mode_if_requested(void) {
|
||||
}
|
||||
}
|
||||
|
||||
#elif defined(KL2x) || defined(K20x) || defined(MK66F18) // STM32_BOOTLOADER_DUAL_BANK // STM32_BOOTLOADER_ADDRESS
|
||||
#elif defined(KL2x) || defined(K20x) || defined(MK66F18) || defined(MIMXRT1062) // STM32_BOOTLOADER_DUAL_BANK // STM32_BOOTLOADER_ADDRESS
|
||||
/* Kinetis */
|
||||
|
||||
# if defined(BOOTLOADER_KIIBOHD)
|
||||
@@ -103,7 +103,8 @@ void enter_bootloader_mode_if_requested(void) {
|
||||
# define SCB_AIRCR_VECTKEY_WRITEMAGIC 0x05FA0000
|
||||
const uint8_t sys_reset_to_loader_magic[] = "\xff\x00\x7fRESET TO LOADER\x7f\x00\xff";
|
||||
__attribute__((weak)) void bootloader_jump(void) {
|
||||
__builtin_memcpy((void *)VBAT, (const void *)sys_reset_to_loader_magic, sizeof(sys_reset_to_loader_magic));
|
||||
void *volatile vbat = (void *)VBAT;
|
||||
__builtin_memcpy(vbat, (const void *)sys_reset_to_loader_magic, sizeof(sys_reset_to_loader_magic));
|
||||
// request reset
|
||||
SCB->AIRCR = SCB_AIRCR_VECTKEY_WRITEMAGIC | SCB_AIRCR_SYSRESETREQ_Msk;
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
|
||||
typedef ioline_t pin_t;
|
||||
|
||||
/* Operation of GPIO by pin. */
|
||||
|
||||
#define setPinInput(pin) palSetLineMode(pin, PAL_MODE_INPUT)
|
||||
#define setPinInputHigh(pin) palSetLineMode(pin, PAL_MODE_INPUT_PULLUP)
|
||||
#define setPinInputLow(pin) palSetLineMode(pin, PAL_MODE_INPUT_PULLDOWN)
|
||||
@@ -32,3 +34,17 @@ typedef ioline_t pin_t;
|
||||
#define readPin(pin) palReadLine(pin)
|
||||
|
||||
#define togglePin(pin) palToggleLine(pin)
|
||||
|
||||
/* Operation of GPIO by port. */
|
||||
|
||||
typedef uint16_t port_data_t;
|
||||
|
||||
#define readPort(pin) palReadPort(PAL_PORT(pin))
|
||||
|
||||
#define setPortBitInput(pin, bit) palSetPadMode(PAL_PORT(pin), bit, PAL_MODE_INPUT)
|
||||
#define setPortBitInputHigh(pin, bit) palSetPadMode(PAL_PORT(pin), bit, PAL_MODE_INPUT_PULLUP)
|
||||
#define setPortBitInputLow(pin, bit) palSetPadMode(PAL_PORT(pin), bit, PAL_MODE_INPUT_PULLDOWN)
|
||||
#define setPortBitOutput(pin, bit) palSetPadMode(PAL_PORT(pin), bit, PAL_MODE_OUTPUT_PUSHPULL)
|
||||
|
||||
#define writePortBitLow(pin, bit) palClearLine(PAL_LINE(PAL_PORT(pin), bit))
|
||||
#define writePortBitHigh(pin, bit) palSetLine(PAL_LINE(PAL_PORT(pin), bit))
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
/* Copyright 2021 QMK
|
||||
*
|
||||
* 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "platform_deps.h"
|
||||
|
||||
void platform_setup(void) {
|
||||
halInit();
|
||||
chSysInit();
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/* Copyright 2021 QMK
|
||||
*
|
||||
* 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <hal.h>
|
||||
#include "chibios_config.h"
|
||||
@@ -14,76 +14,28 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __OPTIMIZE__
|
||||
# pragma message "Compiler optimizations disabled; wait_cpuclock() won't work as designed"
|
||||
#include <ch.h>
|
||||
#include <hal.h>
|
||||
|
||||
#include "_wait.h"
|
||||
|
||||
#ifdef WAIT_US_TIMER
|
||||
void wait_us(uint16_t duration) {
|
||||
static const GPTConfig gpt_cfg = {1000000, NULL, 0, 0}; /* 1MHz timer, no callback */
|
||||
|
||||
if (duration == 0) {
|
||||
duration = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Only use this timer on the main thread;
|
||||
* other threads need to use their own timer.
|
||||
*/
|
||||
if (chThdGetSelfX() == &ch.mainthread && duration < (1ULL << (sizeof(gptcnt_t) * 8))) {
|
||||
gptStart(&WAIT_US_TIMER, &gpt_cfg);
|
||||
gptPolledDelay(&WAIT_US_TIMER, duration);
|
||||
} else {
|
||||
chThdSleepMicroseconds(duration);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#define CLOCK_DELAY_NOP8 "nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t"
|
||||
|
||||
__attribute__((always_inline)) static inline void wait_cpuclock(unsigned int n) { /* n: 1..135 */
|
||||
/* The argument n must be a constant expression.
|
||||
* That way, compiler optimization will remove unnecessary code. */
|
||||
if (n < 1) {
|
||||
return;
|
||||
}
|
||||
if (n > 8) {
|
||||
unsigned int n8 = n / 8;
|
||||
n = n - n8 * 8;
|
||||
switch (n8) {
|
||||
case 16:
|
||||
asm volatile(CLOCK_DELAY_NOP8::: "memory");
|
||||
case 15:
|
||||
asm volatile(CLOCK_DELAY_NOP8::: "memory");
|
||||
case 14:
|
||||
asm volatile(CLOCK_DELAY_NOP8::: "memory");
|
||||
case 13:
|
||||
asm volatile(CLOCK_DELAY_NOP8::: "memory");
|
||||
case 12:
|
||||
asm volatile(CLOCK_DELAY_NOP8::: "memory");
|
||||
case 11:
|
||||
asm volatile(CLOCK_DELAY_NOP8::: "memory");
|
||||
case 10:
|
||||
asm volatile(CLOCK_DELAY_NOP8::: "memory");
|
||||
case 9:
|
||||
asm volatile(CLOCK_DELAY_NOP8::: "memory");
|
||||
case 8:
|
||||
asm volatile(CLOCK_DELAY_NOP8::: "memory");
|
||||
case 7:
|
||||
asm volatile(CLOCK_DELAY_NOP8::: "memory");
|
||||
case 6:
|
||||
asm volatile(CLOCK_DELAY_NOP8::: "memory");
|
||||
case 5:
|
||||
asm volatile(CLOCK_DELAY_NOP8::: "memory");
|
||||
case 4:
|
||||
asm volatile(CLOCK_DELAY_NOP8::: "memory");
|
||||
case 3:
|
||||
asm volatile(CLOCK_DELAY_NOP8::: "memory");
|
||||
case 2:
|
||||
asm volatile(CLOCK_DELAY_NOP8::: "memory");
|
||||
case 1:
|
||||
asm volatile(CLOCK_DELAY_NOP8::: "memory");
|
||||
case 0:
|
||||
break;
|
||||
}
|
||||
}
|
||||
switch (n) {
|
||||
case 8:
|
||||
asm volatile("nop" ::: "memory");
|
||||
case 7:
|
||||
asm volatile("nop" ::: "memory");
|
||||
case 6:
|
||||
asm volatile("nop" ::: "memory");
|
||||
case 5:
|
||||
asm volatile("nop" ::: "memory");
|
||||
case 4:
|
||||
asm volatile("nop" ::: "memory");
|
||||
case 3:
|
||||
asm volatile("nop" ::: "memory");
|
||||
case 2:
|
||||
asm volatile("nop" ::: "memory");
|
||||
case 1:
|
||||
asm volatile("nop" ::: "memory");
|
||||
case 0:
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
/*
|
||||
Copyright 2011 Jun Wako <wakojun@gmail.com>
|
||||
|
||||
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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "debug.h"
|
||||
|
||||
debug_config_t debug_config = {
|
||||
.enable = false, //
|
||||
.matrix = false, //
|
||||
.keyboard = false, //
|
||||
.mouse = false, //
|
||||
.reserved = 0 //
|
||||
};
|
||||
@@ -1,169 +0,0 @@
|
||||
/*
|
||||
Copyright 2011 Jun Wako <wakojun@gmail.com>
|
||||
|
||||
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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "print.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Debug output control
|
||||
*/
|
||||
typedef union {
|
||||
struct {
|
||||
bool enable : 1;
|
||||
bool matrix : 1;
|
||||
bool keyboard : 1;
|
||||
bool mouse : 1;
|
||||
uint8_t reserved : 4;
|
||||
};
|
||||
uint8_t raw;
|
||||
} debug_config_t;
|
||||
|
||||
extern debug_config_t debug_config;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/* for backward compatibility */
|
||||
#define debug_enable (debug_config.enable)
|
||||
#define debug_matrix (debug_config.matrix)
|
||||
#define debug_keyboard (debug_config.keyboard)
|
||||
#define debug_mouse (debug_config.mouse)
|
||||
|
||||
/*
|
||||
* Debug print utils
|
||||
*/
|
||||
#ifndef NO_DEBUG
|
||||
|
||||
# define dprint(s) \
|
||||
do { \
|
||||
if (debug_enable) print(s); \
|
||||
} while (0)
|
||||
# define dprintln(s) \
|
||||
do { \
|
||||
if (debug_enable) println(s); \
|
||||
} while (0)
|
||||
# define dprintf(fmt, ...) \
|
||||
do { \
|
||||
if (debug_enable) xprintf(fmt, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
# define dmsg(s) dprintf("%s at %s: %S\n", __FILE__, __LINE__, PSTR(s))
|
||||
|
||||
/* Deprecated. DO NOT USE these anymore, use dprintf instead. */
|
||||
# define debug(s) \
|
||||
do { \
|
||||
if (debug_enable) print(s); \
|
||||
} while (0)
|
||||
# define debugln(s) \
|
||||
do { \
|
||||
if (debug_enable) println(s); \
|
||||
} while (0)
|
||||
# define debug_msg(s) \
|
||||
do { \
|
||||
if (debug_enable) { \
|
||||
print(__FILE__); \
|
||||
print(" at "); \
|
||||
print_dec(__LINE__); \
|
||||
print(" in "); \
|
||||
print(": "); \
|
||||
print(s); \
|
||||
} \
|
||||
} while (0)
|
||||
# define debug_dec(data) \
|
||||
do { \
|
||||
if (debug_enable) print_dec(data); \
|
||||
} while (0)
|
||||
# define debug_decs(data) \
|
||||
do { \
|
||||
if (debug_enable) print_decs(data); \
|
||||
} while (0)
|
||||
# define debug_hex4(data) \
|
||||
do { \
|
||||
if (debug_enable) print_hex4(data); \
|
||||
} while (0)
|
||||
# define debug_hex8(data) \
|
||||
do { \
|
||||
if (debug_enable) print_hex8(data); \
|
||||
} while (0)
|
||||
# define debug_hex16(data) \
|
||||
do { \
|
||||
if (debug_enable) print_hex16(data); \
|
||||
} while (0)
|
||||
# define debug_hex32(data) \
|
||||
do { \
|
||||
if (debug_enable) print_hex32(data); \
|
||||
} while (0)
|
||||
# define debug_bin8(data) \
|
||||
do { \
|
||||
if (debug_enable) print_bin8(data); \
|
||||
} while (0)
|
||||
# define debug_bin16(data) \
|
||||
do { \
|
||||
if (debug_enable) print_bin16(data); \
|
||||
} while (0)
|
||||
# define debug_bin32(data) \
|
||||
do { \
|
||||
if (debug_enable) print_bin32(data); \
|
||||
} while (0)
|
||||
# define debug_bin_reverse8(data) \
|
||||
do { \
|
||||
if (debug_enable) print_bin_reverse8(data); \
|
||||
} while (0)
|
||||
# define debug_bin_reverse16(data) \
|
||||
do { \
|
||||
if (debug_enable) print_bin_reverse16(data); \
|
||||
} while (0)
|
||||
# define debug_bin_reverse32(data) \
|
||||
do { \
|
||||
if (debug_enable) print_bin_reverse32(data); \
|
||||
} while (0)
|
||||
# define debug_hex(data) debug_hex8(data)
|
||||
# define debug_bin(data) debug_bin8(data)
|
||||
# define debug_bin_reverse(data) debug_bin8(data)
|
||||
|
||||
#else /* NO_DEBUG */
|
||||
|
||||
# define dprint(s)
|
||||
# define dprintln(s)
|
||||
# define dprintf(fmt, ...)
|
||||
# define dmsg(s)
|
||||
# define debug(s)
|
||||
# define debugln(s)
|
||||
# define debug_msg(s)
|
||||
# define debug_dec(data)
|
||||
# define debug_decs(data)
|
||||
# define debug_hex4(data)
|
||||
# define debug_hex8(data)
|
||||
# define debug_hex16(data)
|
||||
# define debug_hex32(data)
|
||||
# define debug_bin8(data)
|
||||
# define debug_bin16(data)
|
||||
# define debug_bin32(data)
|
||||
# define debug_bin_reverse8(data)
|
||||
# define debug_bin_reverse16(data)
|
||||
# define debug_bin_reverse32(data)
|
||||
# define debug_hex(data)
|
||||
# define debug_bin(data)
|
||||
# define debug_bin_reverse(data)
|
||||
|
||||
#endif /* NO_DEBUG */
|
||||
@@ -1,211 +0,0 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "eeprom.h"
|
||||
#include "eeconfig.h"
|
||||
#include "action_layer.h"
|
||||
|
||||
#ifdef STM32_EEPROM_ENABLE
|
||||
# include <hal.h>
|
||||
# include "eeprom_stm32.h"
|
||||
#endif
|
||||
|
||||
#if defined(EEPROM_DRIVER)
|
||||
# include "eeprom_driver.h"
|
||||
#endif
|
||||
|
||||
#if defined(HAPTIC_ENABLE)
|
||||
# include "haptic.h"
|
||||
#endif
|
||||
|
||||
/** \brief eeconfig enable
|
||||
*
|
||||
* FIXME: needs doc
|
||||
*/
|
||||
__attribute__((weak)) void eeconfig_init_user(void) {
|
||||
// Reset user EEPROM value to blank, rather than to a set value
|
||||
eeconfig_update_user(0);
|
||||
}
|
||||
|
||||
__attribute__((weak)) void eeconfig_init_kb(void) {
|
||||
// Reset Keyboard EEPROM value to blank, rather than to a set value
|
||||
eeconfig_update_kb(0);
|
||||
|
||||
eeconfig_init_user();
|
||||
}
|
||||
|
||||
/*
|
||||
* FIXME: needs doc
|
||||
*/
|
||||
void eeconfig_init_quantum(void) {
|
||||
#ifdef STM32_EEPROM_ENABLE
|
||||
EEPROM_Erase();
|
||||
#endif
|
||||
#if defined(EEPROM_DRIVER)
|
||||
eeprom_driver_erase();
|
||||
#endif
|
||||
eeprom_update_word(EECONFIG_MAGIC, EECONFIG_MAGIC_NUMBER);
|
||||
eeprom_update_byte(EECONFIG_DEBUG, 0);
|
||||
eeprom_update_byte(EECONFIG_DEFAULT_LAYER, 0);
|
||||
default_layer_state = 0;
|
||||
eeprom_update_byte(EECONFIG_KEYMAP_LOWER_BYTE, 0);
|
||||
eeprom_update_byte(EECONFIG_KEYMAP_UPPER_BYTE, 0);
|
||||
eeprom_update_byte(EECONFIG_MOUSEKEY_ACCEL, 0);
|
||||
eeprom_update_byte(EECONFIG_BACKLIGHT, 0);
|
||||
eeprom_update_byte(EECONFIG_AUDIO, 0xFF); // On by default
|
||||
eeprom_update_dword(EECONFIG_RGBLIGHT, 0);
|
||||
eeprom_update_byte(EECONFIG_STENOMODE, 0);
|
||||
eeprom_update_dword(EECONFIG_HAPTIC, 0);
|
||||
eeprom_update_byte(EECONFIG_VELOCIKEY, 0);
|
||||
eeprom_update_dword(EECONFIG_RGB_MATRIX, 0);
|
||||
eeprom_update_word(EECONFIG_RGB_MATRIX_EXTENDED, 0);
|
||||
|
||||
// TODO: Remove once ARM has a way to configure EECONFIG_HANDEDNESS
|
||||
// within the emulated eeprom via dfu-util or another tool
|
||||
#if defined INIT_EE_HANDS_LEFT
|
||||
# pragma message "Faking EE_HANDS for left hand"
|
||||
eeprom_update_byte(EECONFIG_HANDEDNESS, 1);
|
||||
#elif defined INIT_EE_HANDS_RIGHT
|
||||
# pragma message "Faking EE_HANDS for right hand"
|
||||
eeprom_update_byte(EECONFIG_HANDEDNESS, 0);
|
||||
#endif
|
||||
|
||||
#if defined(HAPTIC_ENABLE)
|
||||
haptic_reset();
|
||||
#else
|
||||
// this is used in case haptic is disabled, but we still want sane defaults
|
||||
// in the haptic configuration eeprom. All zero will trigger a haptic_reset
|
||||
// when a haptic-enabled firmware is loaded onto the keyboard.
|
||||
eeprom_update_dword(EECONFIG_HAPTIC, 0);
|
||||
#endif
|
||||
|
||||
eeconfig_init_kb();
|
||||
}
|
||||
|
||||
/** \brief eeconfig initialization
|
||||
*
|
||||
* FIXME: needs doc
|
||||
*/
|
||||
void eeconfig_init(void) { eeconfig_init_quantum(); }
|
||||
|
||||
/** \brief eeconfig enable
|
||||
*
|
||||
* FIXME: needs doc
|
||||
*/
|
||||
void eeconfig_enable(void) { eeprom_update_word(EECONFIG_MAGIC, EECONFIG_MAGIC_NUMBER); }
|
||||
|
||||
/** \brief eeconfig disable
|
||||
*
|
||||
* FIXME: needs doc
|
||||
*/
|
||||
void eeconfig_disable(void) {
|
||||
#ifdef STM32_EEPROM_ENABLE
|
||||
EEPROM_Erase();
|
||||
#endif
|
||||
#if defined(EEPROM_DRIVER)
|
||||
eeprom_driver_erase();
|
||||
#endif
|
||||
eeprom_update_word(EECONFIG_MAGIC, EECONFIG_MAGIC_NUMBER_OFF);
|
||||
}
|
||||
|
||||
/** \brief eeconfig is enabled
|
||||
*
|
||||
* FIXME: needs doc
|
||||
*/
|
||||
bool eeconfig_is_enabled(void) { return (eeprom_read_word(EECONFIG_MAGIC) == EECONFIG_MAGIC_NUMBER); }
|
||||
|
||||
/** \brief eeconfig is disabled
|
||||
*
|
||||
* FIXME: needs doc
|
||||
*/
|
||||
bool eeconfig_is_disabled(void) { return (eeprom_read_word(EECONFIG_MAGIC) == EECONFIG_MAGIC_NUMBER_OFF); }
|
||||
|
||||
/** \brief eeconfig read debug
|
||||
*
|
||||
* FIXME: needs doc
|
||||
*/
|
||||
uint8_t eeconfig_read_debug(void) { return eeprom_read_byte(EECONFIG_DEBUG); }
|
||||
/** \brief eeconfig update debug
|
||||
*
|
||||
* FIXME: needs doc
|
||||
*/
|
||||
void eeconfig_update_debug(uint8_t val) { eeprom_update_byte(EECONFIG_DEBUG, val); }
|
||||
|
||||
/** \brief eeconfig read default layer
|
||||
*
|
||||
* FIXME: needs doc
|
||||
*/
|
||||
uint8_t eeconfig_read_default_layer(void) { return eeprom_read_byte(EECONFIG_DEFAULT_LAYER); }
|
||||
/** \brief eeconfig update default layer
|
||||
*
|
||||
* FIXME: needs doc
|
||||
*/
|
||||
void eeconfig_update_default_layer(uint8_t val) { eeprom_update_byte(EECONFIG_DEFAULT_LAYER, val); }
|
||||
|
||||
/** \brief eeconfig read keymap
|
||||
*
|
||||
* FIXME: needs doc
|
||||
*/
|
||||
uint16_t eeconfig_read_keymap(void) { return (eeprom_read_byte(EECONFIG_KEYMAP_LOWER_BYTE) | (eeprom_read_byte(EECONFIG_KEYMAP_UPPER_BYTE) << 8)); }
|
||||
/** \brief eeconfig update keymap
|
||||
*
|
||||
* FIXME: needs doc
|
||||
*/
|
||||
void eeconfig_update_keymap(uint16_t val) {
|
||||
eeprom_update_byte(EECONFIG_KEYMAP_LOWER_BYTE, val & 0xFF);
|
||||
eeprom_update_byte(EECONFIG_KEYMAP_UPPER_BYTE, (val >> 8) & 0xFF);
|
||||
}
|
||||
|
||||
/** \brief eeconfig read audio
|
||||
*
|
||||
* FIXME: needs doc
|
||||
*/
|
||||
uint8_t eeconfig_read_audio(void) { return eeprom_read_byte(EECONFIG_AUDIO); }
|
||||
/** \brief eeconfig update audio
|
||||
*
|
||||
* FIXME: needs doc
|
||||
*/
|
||||
void eeconfig_update_audio(uint8_t val) { eeprom_update_byte(EECONFIG_AUDIO, val); }
|
||||
|
||||
/** \brief eeconfig read kb
|
||||
*
|
||||
* FIXME: needs doc
|
||||
*/
|
||||
uint32_t eeconfig_read_kb(void) { return eeprom_read_dword(EECONFIG_KEYBOARD); }
|
||||
/** \brief eeconfig update kb
|
||||
*
|
||||
* FIXME: needs doc
|
||||
*/
|
||||
void eeconfig_update_kb(uint32_t val) { eeprom_update_dword(EECONFIG_KEYBOARD, val); }
|
||||
|
||||
/** \brief eeconfig read user
|
||||
*
|
||||
* FIXME: needs doc
|
||||
*/
|
||||
uint32_t eeconfig_read_user(void) { return eeprom_read_dword(EECONFIG_USER); }
|
||||
/** \brief eeconfig update user
|
||||
*
|
||||
* FIXME: needs doc
|
||||
*/
|
||||
void eeconfig_update_user(uint32_t val) { eeprom_update_dword(EECONFIG_USER, val); }
|
||||
|
||||
/** \brief eeconfig read haptic
|
||||
*
|
||||
* FIXME: needs doc
|
||||
*/
|
||||
uint32_t eeconfig_read_haptic(void) { return eeprom_read_dword(EECONFIG_HAPTIC); }
|
||||
/** \brief eeconfig update haptic
|
||||
*
|
||||
* FIXME: needs doc
|
||||
*/
|
||||
void eeconfig_update_haptic(uint32_t val) { eeprom_update_dword(EECONFIG_HAPTIC, val); }
|
||||
|
||||
/** \brief eeconfig read split handedness
|
||||
*
|
||||
* FIXME: needs doc
|
||||
*/
|
||||
bool eeconfig_read_handedness(void) { return !!eeprom_read_byte(EECONFIG_HANDEDNESS); }
|
||||
/** \brief eeconfig update split handedness
|
||||
*
|
||||
* FIXME: needs doc
|
||||
*/
|
||||
void eeconfig_update_handedness(bool val) { eeprom_update_byte(EECONFIG_HANDEDNESS, !!val); }
|
||||
@@ -1,113 +0,0 @@
|
||||
/*
|
||||
Copyright 2013 Jun Wako <wakojun@gmail.com>
|
||||
|
||||
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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifndef EECONFIG_MAGIC_NUMBER
|
||||
# define EECONFIG_MAGIC_NUMBER (uint16_t)0xFEEA // When changing, decrement this value to avoid future re-init issues
|
||||
#endif
|
||||
#define EECONFIG_MAGIC_NUMBER_OFF (uint16_t)0xFFFF
|
||||
|
||||
/* EEPROM parameter address */
|
||||
#define EECONFIG_MAGIC (uint16_t *)0
|
||||
#define EECONFIG_DEBUG (uint8_t *)2
|
||||
#define EECONFIG_DEFAULT_LAYER (uint8_t *)3
|
||||
#define EECONFIG_KEYMAP (uint8_t *)4
|
||||
#define EECONFIG_MOUSEKEY_ACCEL (uint8_t *)5
|
||||
#define EECONFIG_BACKLIGHT (uint8_t *)6
|
||||
#define EECONFIG_AUDIO (uint8_t *)7
|
||||
#define EECONFIG_RGBLIGHT (uint32_t *)8
|
||||
#define EECONFIG_UNICODEMODE (uint8_t *)12
|
||||
#define EECONFIG_STENOMODE (uint8_t *)13
|
||||
// EEHANDS for two handed boards
|
||||
#define EECONFIG_HANDEDNESS (uint8_t *)14
|
||||
#define EECONFIG_KEYBOARD (uint32_t *)15
|
||||
#define EECONFIG_USER (uint32_t *)19
|
||||
#define EECONFIG_VELOCIKEY (uint8_t *)23
|
||||
|
||||
#define EECONFIG_HAPTIC (uint32_t *)24
|
||||
|
||||
// Mutually exclusive
|
||||
#define EECONFIG_LED_MATRIX (uint32_t *)28
|
||||
#define EECONFIG_RGB_MATRIX (uint32_t *)28
|
||||
// Speed & Flags
|
||||
#define EECONFIG_LED_MATRIX_EXTENDED (uint16_t *)32
|
||||
#define EECONFIG_RGB_MATRIX_EXTENDED (uint16_t *)32
|
||||
|
||||
// TODO: Combine these into a single word and single block of EEPROM
|
||||
#define EECONFIG_KEYMAP_UPPER_BYTE (uint8_t *)34
|
||||
// Size of EEPROM being used, other code can refer to this for available EEPROM
|
||||
#define EECONFIG_SIZE 35
|
||||
/* debug bit */
|
||||
#define EECONFIG_DEBUG_ENABLE (1 << 0)
|
||||
#define EECONFIG_DEBUG_MATRIX (1 << 1)
|
||||
#define EECONFIG_DEBUG_KEYBOARD (1 << 2)
|
||||
#define EECONFIG_DEBUG_MOUSE (1 << 3)
|
||||
|
||||
/* keyconf bit */
|
||||
#define EECONFIG_KEYMAP_SWAP_CONTROL_CAPSLOCK (1 << 0)
|
||||
#define EECONFIG_KEYMAP_CAPSLOCK_TO_CONTROL (1 << 1)
|
||||
#define EECONFIG_KEYMAP_SWAP_LALT_LGUI (1 << 2)
|
||||
#define EECONFIG_KEYMAP_SWAP_RALT_RGUI (1 << 3)
|
||||
#define EECONFIG_KEYMAP_NO_GUI (1 << 4)
|
||||
#define EECONFIG_KEYMAP_SWAP_GRAVE_ESC (1 << 5)
|
||||
#define EECONFIG_KEYMAP_SWAP_BACKSLASH_BACKSPACE (1 << 6)
|
||||
#define EECONFIG_KEYMAP_NKRO (1 << 7)
|
||||
|
||||
#define EECONFIG_KEYMAP_LOWER_BYTE EECONFIG_KEYMAP
|
||||
|
||||
bool eeconfig_is_enabled(void);
|
||||
bool eeconfig_is_disabled(void);
|
||||
|
||||
void eeconfig_init(void);
|
||||
void eeconfig_init_quantum(void);
|
||||
void eeconfig_init_kb(void);
|
||||
void eeconfig_init_user(void);
|
||||
|
||||
void eeconfig_enable(void);
|
||||
|
||||
void eeconfig_disable(void);
|
||||
|
||||
uint8_t eeconfig_read_debug(void);
|
||||
void eeconfig_update_debug(uint8_t val);
|
||||
|
||||
uint8_t eeconfig_read_default_layer(void);
|
||||
void eeconfig_update_default_layer(uint8_t val);
|
||||
|
||||
uint16_t eeconfig_read_keymap(void);
|
||||
void eeconfig_update_keymap(uint16_t val);
|
||||
|
||||
#ifdef AUDIO_ENABLE
|
||||
uint8_t eeconfig_read_audio(void);
|
||||
void eeconfig_update_audio(uint8_t val);
|
||||
#endif
|
||||
|
||||
uint32_t eeconfig_read_kb(void);
|
||||
void eeconfig_update_kb(uint32_t val);
|
||||
uint32_t eeconfig_read_user(void);
|
||||
void eeconfig_update_user(uint32_t val);
|
||||
|
||||
#ifdef HAPTIC_ENABLE
|
||||
uint32_t eeconfig_read_haptic(void);
|
||||
void eeconfig_update_haptic(uint32_t val);
|
||||
#endif
|
||||
|
||||
bool eeconfig_read_handedness(void);
|
||||
void eeconfig_update_handedness(bool val);
|
||||
+29
-4
@@ -17,10 +17,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include <stdint.h>
|
||||
//#include <avr/interrupt.h>
|
||||
#include "keyboard.h"
|
||||
#include "keycode.h"
|
||||
#include "host.h"
|
||||
#include "util.h"
|
||||
#include "debug.h"
|
||||
#include "digitizer.h"
|
||||
|
||||
#ifdef NKRO_ENABLE
|
||||
# include "keycode_config.h"
|
||||
@@ -35,15 +37,20 @@ void host_set_driver(host_driver_t *d) { driver = d; }
|
||||
|
||||
host_driver_t *host_get_driver(void) { return driver; }
|
||||
|
||||
#ifdef SPLIT_KEYBOARD
|
||||
uint8_t split_led_state = 0;
|
||||
void set_split_host_keyboard_leds(uint8_t led_state) { split_led_state = led_state; }
|
||||
#endif
|
||||
|
||||
uint8_t host_keyboard_leds(void) {
|
||||
#ifdef SPLIT_KEYBOARD
|
||||
if (!is_keyboard_master()) return split_led_state;
|
||||
#endif
|
||||
if (!driver) return 0;
|
||||
return (*driver->keyboard_leds)();
|
||||
}
|
||||
|
||||
led_t host_keyboard_led_state(void) {
|
||||
if (!driver) return (led_t){0};
|
||||
return (led_t)((*driver->keyboard_leds)());
|
||||
}
|
||||
led_t host_keyboard_led_state(void) { return (led_t)host_keyboard_leds(); }
|
||||
|
||||
/* send report */
|
||||
void host_keyboard_send(report_keyboard_t *report) {
|
||||
@@ -97,6 +104,24 @@ void host_consumer_send(uint16_t report) {
|
||||
(*driver->send_consumer)(report);
|
||||
}
|
||||
|
||||
void host_digitizer_send(digitizer_t *digitizer) {
|
||||
if (!driver) return;
|
||||
|
||||
report_digitizer_t report = {
|
||||
#ifdef DIGITIZER_SHARED_EP
|
||||
.report_id = REPORT_ID_DIGITIZER,
|
||||
#endif
|
||||
.tip = digitizer->tipswitch & 0x1,
|
||||
.inrange = digitizer->inrange & 0x1,
|
||||
.x = (uint16_t)(digitizer->x * 0x7FFF),
|
||||
.y = (uint16_t)(digitizer->y * 0x7FFF),
|
||||
};
|
||||
|
||||
send_digitizer(&report);
|
||||
}
|
||||
|
||||
__attribute__((weak)) void send_digitizer(report_digitizer_t *report) {}
|
||||
|
||||
uint16_t host_last_system_report(void) { return last_system_report; }
|
||||
|
||||
uint16_t host_last_consumer_report(void) { return last_consumer_report; }
|
||||
|
||||
@@ -30,3 +30,5 @@ typedef struct {
|
||||
void (*send_system)(uint16_t);
|
||||
void (*send_consumer)(uint16_t);
|
||||
} host_driver_t;
|
||||
|
||||
void send_digitizer(report_digitizer_t *report);
|
||||
@@ -1,546 +0,0 @@
|
||||
/*
|
||||
Copyright 2011, 2012, 2013 Jun Wako <wakojun@gmail.com>
|
||||
|
||||
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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include "keyboard.h"
|
||||
#include "matrix.h"
|
||||
#include "keymap.h"
|
||||
#include "host.h"
|
||||
#include "led.h"
|
||||
#include "keycode.h"
|
||||
#include "timer.h"
|
||||
#include "sync_timer.h"
|
||||
#include "print.h"
|
||||
#include "debug.h"
|
||||
#include "command.h"
|
||||
#include "util.h"
|
||||
#include "sendchar.h"
|
||||
#include "eeconfig.h"
|
||||
#include "action_layer.h"
|
||||
#ifdef BACKLIGHT_ENABLE
|
||||
# include "backlight.h"
|
||||
#endif
|
||||
#ifdef MOUSEKEY_ENABLE
|
||||
# include "mousekey.h"
|
||||
#endif
|
||||
#ifdef PS2_MOUSE_ENABLE
|
||||
# include "ps2_mouse.h"
|
||||
#endif
|
||||
#ifdef SERIAL_MOUSE_ENABLE
|
||||
# include "serial_mouse.h"
|
||||
#endif
|
||||
#ifdef ADB_MOUSE_ENABLE
|
||||
# include "adb.h"
|
||||
#endif
|
||||
#ifdef RGBLIGHT_ENABLE
|
||||
# include "rgblight.h"
|
||||
#endif
|
||||
#ifdef LED_MATRIX_ENABLE
|
||||
# include "led_matrix.h"
|
||||
#endif
|
||||
#ifdef RGB_MATRIX_ENABLE
|
||||
# include "rgb_matrix.h"
|
||||
#endif
|
||||
#ifdef ENCODER_ENABLE
|
||||
# include "encoder.h"
|
||||
#endif
|
||||
#ifdef STENO_ENABLE
|
||||
# include "process_steno.h"
|
||||
#endif
|
||||
#ifdef SERIAL_LINK_ENABLE
|
||||
# include "serial_link/system/serial_link.h"
|
||||
#endif
|
||||
#ifdef VISUALIZER_ENABLE
|
||||
# include "visualizer/visualizer.h"
|
||||
#endif
|
||||
#ifdef POINTING_DEVICE_ENABLE
|
||||
# include "pointing_device.h"
|
||||
#endif
|
||||
#ifdef MIDI_ENABLE
|
||||
# include "process_midi.h"
|
||||
#endif
|
||||
#ifdef JOYSTICK_ENABLE
|
||||
# include "process_joystick.h"
|
||||
#endif
|
||||
#ifdef HD44780_ENABLE
|
||||
# include "hd44780.h"
|
||||
#endif
|
||||
#ifdef QWIIC_ENABLE
|
||||
# include "qwiic.h"
|
||||
#endif
|
||||
#ifdef OLED_DRIVER_ENABLE
|
||||
# include "oled_driver.h"
|
||||
#endif
|
||||
#ifdef VELOCIKEY_ENABLE
|
||||
# include "velocikey.h"
|
||||
#endif
|
||||
#ifdef VIA_ENABLE
|
||||
# include "via.h"
|
||||
#endif
|
||||
#ifdef DIP_SWITCH_ENABLE
|
||||
# include "dip_switch.h"
|
||||
#endif
|
||||
#ifdef STM32_EEPROM_ENABLE
|
||||
# include "eeprom_stm32.h"
|
||||
#endif
|
||||
#ifdef EEPROM_DRIVER
|
||||
# include "eeprom_driver.h"
|
||||
#endif
|
||||
#ifdef QMK_SETTINGS
|
||||
# include "qmk_settings.h"
|
||||
#endif
|
||||
#ifdef VIAL_ENABLE
|
||||
# include "vial.h"
|
||||
#endif
|
||||
|
||||
static uint32_t last_input_modification_time = 0;
|
||||
uint32_t last_input_activity_time(void) { return last_input_modification_time; }
|
||||
uint32_t last_input_activity_elapsed(void) { return timer_elapsed32(last_input_modification_time); }
|
||||
|
||||
static uint32_t last_matrix_modification_time = 0;
|
||||
uint32_t last_matrix_activity_time(void) { return last_matrix_modification_time; }
|
||||
uint32_t last_matrix_activity_elapsed(void) { return timer_elapsed32(last_matrix_modification_time); }
|
||||
void last_matrix_activity_trigger(void) { last_matrix_modification_time = last_input_modification_time = timer_read32(); }
|
||||
|
||||
static uint32_t last_encoder_modification_time = 0;
|
||||
uint32_t last_encoder_activity_time(void) { return last_encoder_modification_time; }
|
||||
uint32_t last_encoder_activity_elapsed(void) { return timer_elapsed32(last_encoder_modification_time); }
|
||||
void last_encoder_activity_trigger(void) { last_encoder_modification_time = last_input_modification_time = timer_read32(); }
|
||||
|
||||
// Only enable this if console is enabled to print to
|
||||
#if defined(DEBUG_MATRIX_SCAN_RATE)
|
||||
static uint32_t matrix_timer = 0;
|
||||
static uint32_t matrix_scan_count = 0;
|
||||
static uint32_t last_matrix_scan_count = 0;
|
||||
|
||||
void matrix_scan_perf_task(void) {
|
||||
matrix_scan_count++;
|
||||
|
||||
uint32_t timer_now = timer_read32();
|
||||
if (TIMER_DIFF_32(timer_now, matrix_timer) > 1000) {
|
||||
# if defined(CONSOLE_ENABLE)
|
||||
dprintf("matrix scan frequency: %lu\n", matrix_scan_count);
|
||||
# endif
|
||||
last_matrix_scan_count = matrix_scan_count;
|
||||
matrix_timer = timer_now;
|
||||
matrix_scan_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t get_matrix_scan_rate(void) { return last_matrix_scan_count; }
|
||||
#else
|
||||
# define matrix_scan_perf_task()
|
||||
#endif
|
||||
|
||||
#ifdef MATRIX_HAS_GHOST
|
||||
extern const uint16_t keymaps[][MATRIX_ROWS][MATRIX_COLS];
|
||||
static matrix_row_t get_real_keys(uint8_t row, matrix_row_t rowdata) {
|
||||
matrix_row_t out = 0;
|
||||
for (uint8_t col = 0; col < MATRIX_COLS; col++) {
|
||||
// read each key in the row data and check if the keymap defines it as a real key
|
||||
if (pgm_read_byte(&keymaps[0][row][col]) && (rowdata & (1 << col))) {
|
||||
// this creates new row data, if a key is defined in the keymap, it will be set here
|
||||
out |= 1 << col;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
static inline bool popcount_more_than_one(matrix_row_t rowdata) {
|
||||
rowdata &= rowdata - 1; // if there are less than two bits (keys) set, rowdata will become zero
|
||||
return rowdata;
|
||||
}
|
||||
|
||||
static inline bool has_ghost_in_row(uint8_t row, matrix_row_t rowdata) {
|
||||
/* No ghost exists when less than 2 keys are down on the row.
|
||||
If there are "active" blanks in the matrix, the key can't be pressed by the user,
|
||||
there is no doubt as to which keys are really being pressed.
|
||||
The ghosts will be ignored, they are KC_NO. */
|
||||
rowdata = get_real_keys(row, rowdata);
|
||||
if ((popcount_more_than_one(rowdata)) == 0) {
|
||||
return false;
|
||||
}
|
||||
/* Ghost occurs when the row shares a column line with other row,
|
||||
and two columns are read on each row. Blanks in the matrix don't matter,
|
||||
so they are filtered out.
|
||||
If there are two or more real keys pressed and they match columns with
|
||||
at least two of another row's real keys, the row will be ignored. Keep in mind,
|
||||
we are checking one row at a time, not all of them at once.
|
||||
*/
|
||||
for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
|
||||
if (i != row && popcount_more_than_one(get_real_keys(i, matrix_get_row(i)) & rowdata)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void disable_jtag(void) {
|
||||
// To use PF4-7 (PC2-5 on ATmega32A), disable JTAG by writing JTD bit twice within four cycles.
|
||||
#if (defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__) || defined(__AVR_ATmega16U4__) || defined(__AVR_ATmega32U4__))
|
||||
MCUCR |= _BV(JTD);
|
||||
MCUCR |= _BV(JTD);
|
||||
#elif defined(__AVR_ATmega32A__)
|
||||
MCUCSR |= _BV(JTD);
|
||||
MCUCSR |= _BV(JTD);
|
||||
#endif
|
||||
}
|
||||
|
||||
/** \brief matrix_setup
|
||||
*
|
||||
* FIXME: needs doc
|
||||
*/
|
||||
__attribute__((weak)) void matrix_setup(void) {}
|
||||
|
||||
/** \brief keyboard_pre_init_user
|
||||
*
|
||||
* FIXME: needs doc
|
||||
*/
|
||||
__attribute__((weak)) void keyboard_pre_init_user(void) {}
|
||||
|
||||
/** \brief keyboard_pre_init_kb
|
||||
*
|
||||
* FIXME: needs doc
|
||||
*/
|
||||
__attribute__((weak)) void keyboard_pre_init_kb(void) { keyboard_pre_init_user(); }
|
||||
|
||||
/** \brief keyboard_post_init_user
|
||||
*
|
||||
* FIXME: needs doc
|
||||
*/
|
||||
|
||||
__attribute__((weak)) void keyboard_post_init_user() {}
|
||||
|
||||
/** \brief keyboard_post_init_kb
|
||||
*
|
||||
* FIXME: needs doc
|
||||
*/
|
||||
|
||||
__attribute__((weak)) void keyboard_post_init_kb(void) { keyboard_post_init_user(); }
|
||||
|
||||
/** \brief keyboard_setup
|
||||
*
|
||||
* FIXME: needs doc
|
||||
*/
|
||||
void keyboard_setup(void) {
|
||||
#ifndef NO_JTAG_DISABLE
|
||||
disable_jtag();
|
||||
#endif
|
||||
print_set_sendchar(sendchar);
|
||||
#ifdef STM32_EEPROM_ENABLE
|
||||
EEPROM_Init();
|
||||
#endif
|
||||
#ifdef EEPROM_DRIVER
|
||||
eeprom_driver_init();
|
||||
#endif
|
||||
#ifdef VIAL_ENABLE
|
||||
vial_init();
|
||||
#endif
|
||||
#ifdef QMK_SETTINGS
|
||||
qmk_settings_init();
|
||||
#endif
|
||||
matrix_setup();
|
||||
keyboard_pre_init_kb();
|
||||
}
|
||||
|
||||
/** \brief is_keyboard_master
|
||||
*
|
||||
* FIXME: needs doc
|
||||
*/
|
||||
__attribute__((weak)) bool is_keyboard_master(void) { return true; }
|
||||
|
||||
/** \brief is_keyboard_left
|
||||
*
|
||||
* FIXME: needs doc
|
||||
*/
|
||||
__attribute__((weak)) bool is_keyboard_left(void) { return true; }
|
||||
|
||||
/** \brief should_process_keypress
|
||||
*
|
||||
* Override this function if you have a condition where keypresses processing should change:
|
||||
* - splits where the slave side needs to process for rgb/oled functionality
|
||||
*/
|
||||
__attribute__((weak)) bool should_process_keypress(void) { return is_keyboard_master(); }
|
||||
|
||||
/** \brief housekeeping_task_kb
|
||||
*
|
||||
* Override this function if you have a need to execute code for every keyboard main loop iteration.
|
||||
* This is specific to keyboard-level functionality.
|
||||
*/
|
||||
__attribute__((weak)) void housekeeping_task_kb(void) {}
|
||||
|
||||
/** \brief housekeeping_task_user
|
||||
*
|
||||
* Override this function if you have a need to execute code for every keyboard main loop iteration.
|
||||
* This is specific to user/keymap-level functionality.
|
||||
*/
|
||||
__attribute__((weak)) void housekeeping_task_user(void) {}
|
||||
|
||||
/** \brief housekeeping_task
|
||||
*
|
||||
* Invokes hooks for executing code after QMK is done after each loop iteration.
|
||||
*/
|
||||
void housekeeping_task(void) {
|
||||
housekeeping_task_kb();
|
||||
housekeeping_task_user();
|
||||
}
|
||||
|
||||
/** \brief keyboard_init
|
||||
*
|
||||
* FIXME: needs doc
|
||||
*/
|
||||
void keyboard_init(void) {
|
||||
timer_init();
|
||||
sync_timer_init();
|
||||
matrix_init();
|
||||
#ifdef VIA_ENABLE
|
||||
via_init();
|
||||
#endif
|
||||
#ifdef QWIIC_ENABLE
|
||||
qwiic_init();
|
||||
#endif
|
||||
#ifdef OLED_DRIVER_ENABLE
|
||||
oled_init(OLED_ROTATION_0);
|
||||
#endif
|
||||
#ifdef PS2_MOUSE_ENABLE
|
||||
ps2_mouse_init();
|
||||
#endif
|
||||
#ifdef SERIAL_MOUSE_ENABLE
|
||||
serial_mouse_init();
|
||||
#endif
|
||||
#ifdef ADB_MOUSE_ENABLE
|
||||
adb_mouse_init();
|
||||
#endif
|
||||
#ifdef BACKLIGHT_ENABLE
|
||||
backlight_init();
|
||||
#endif
|
||||
#ifdef RGBLIGHT_ENABLE
|
||||
rgblight_init();
|
||||
#endif
|
||||
#ifdef ENCODER_ENABLE
|
||||
encoder_init();
|
||||
#endif
|
||||
#ifdef STENO_ENABLE
|
||||
steno_init();
|
||||
#endif
|
||||
#ifdef POINTING_DEVICE_ENABLE
|
||||
pointing_device_init();
|
||||
#endif
|
||||
#if defined(NKRO_ENABLE) && defined(FORCE_NKRO)
|
||||
keymap_config.nkro = 1;
|
||||
eeconfig_update_keymap(keymap_config.raw);
|
||||
#endif
|
||||
#ifdef DIP_SWITCH_ENABLE
|
||||
dip_switch_init();
|
||||
#endif
|
||||
|
||||
#if defined(DEBUG_MATRIX_SCAN_RATE) && defined(CONSOLE_ENABLE)
|
||||
debug_enable = true;
|
||||
#endif
|
||||
|
||||
keyboard_post_init_kb(); /* Always keep this last */
|
||||
}
|
||||
|
||||
/** \brief key_event_task
|
||||
*
|
||||
* This function is responsible for calling into other systems when they need to respond to electrical switch press events.
|
||||
* This is differnet than keycode events as no layer processing, or filtering occurs.
|
||||
*/
|
||||
void switch_events(uint8_t row, uint8_t col, bool pressed) {
|
||||
#if defined(LED_MATRIX_ENABLE)
|
||||
process_led_matrix(row, col, pressed);
|
||||
#endif
|
||||
#if defined(RGB_MATRIX_ENABLE)
|
||||
process_rgb_matrix(row, col, pressed);
|
||||
#endif
|
||||
}
|
||||
|
||||
/** \brief Keyboard task: Do keyboard routine jobs
|
||||
*
|
||||
* Do routine keyboard jobs:
|
||||
*
|
||||
* * scan matrix
|
||||
* * handle mouse movements
|
||||
* * run visualizer code
|
||||
* * handle midi commands
|
||||
* * light LEDs
|
||||
*
|
||||
* This is repeatedly called as fast as possible.
|
||||
*/
|
||||
void keyboard_task(void) {
|
||||
static matrix_row_t matrix_prev[MATRIX_ROWS];
|
||||
static uint8_t led_status = 0;
|
||||
matrix_row_t matrix_row = 0;
|
||||
matrix_row_t matrix_change = 0;
|
||||
#ifdef QMK_KEYS_PER_SCAN
|
||||
uint8_t keys_processed = 0;
|
||||
#endif
|
||||
#ifdef ENCODER_ENABLE
|
||||
bool encoders_changed = false;
|
||||
#endif
|
||||
|
||||
uint8_t matrix_changed = matrix_scan();
|
||||
if (matrix_changed) last_matrix_activity_trigger();
|
||||
|
||||
for (uint8_t r = 0; r < MATRIX_ROWS; r++) {
|
||||
matrix_row = matrix_get_row(r);
|
||||
matrix_change = matrix_row ^ matrix_prev[r];
|
||||
if (matrix_change) {
|
||||
#ifdef MATRIX_HAS_GHOST
|
||||
if (has_ghost_in_row(r, matrix_row)) {
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
if (debug_matrix) matrix_print();
|
||||
matrix_row_t col_mask = 1;
|
||||
for (uint8_t c = 0; c < MATRIX_COLS; c++, col_mask <<= 1) {
|
||||
if (matrix_change & col_mask) {
|
||||
if (should_process_keypress()) {
|
||||
action_exec((keyevent_t){
|
||||
.key = (keypos_t){.row = r, .col = c}, .pressed = (matrix_row & col_mask), .time = (timer_read() | 1) /* time should not be 0 */
|
||||
});
|
||||
}
|
||||
// record a processed key
|
||||
matrix_prev[r] ^= col_mask;
|
||||
|
||||
switch_events(r, c, (matrix_row & col_mask));
|
||||
|
||||
#ifdef QMK_KEYS_PER_SCAN
|
||||
// only jump out if we have processed "enough" keys.
|
||||
if (++keys_processed >= QMK_KEYS_PER_SCAN)
|
||||
#endif
|
||||
// process a key per task call
|
||||
goto MATRIX_LOOP_END;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// call with pseudo tick event when no real key event.
|
||||
#ifdef QMK_KEYS_PER_SCAN
|
||||
// we can get here with some keys processed now.
|
||||
if (!keys_processed)
|
||||
#endif
|
||||
action_exec(TICK);
|
||||
|
||||
MATRIX_LOOP_END:
|
||||
|
||||
#ifdef DEBUG_MATRIX_SCAN_RATE
|
||||
matrix_scan_perf_task();
|
||||
#endif
|
||||
|
||||
#if defined(RGBLIGHT_ENABLE)
|
||||
rgblight_task();
|
||||
#endif
|
||||
|
||||
#ifdef LED_MATRIX_ENABLE
|
||||
led_matrix_task();
|
||||
#endif
|
||||
#ifdef RGB_MATRIX_ENABLE
|
||||
rgb_matrix_task();
|
||||
#endif
|
||||
|
||||
#if defined(BACKLIGHT_ENABLE)
|
||||
# if defined(BACKLIGHT_PIN) || defined(BACKLIGHT_PINS)
|
||||
backlight_task();
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef ENCODER_ENABLE
|
||||
encoders_changed = encoder_read();
|
||||
if (encoders_changed) last_encoder_activity_trigger();
|
||||
#endif
|
||||
|
||||
#ifdef QWIIC_ENABLE
|
||||
qwiic_task();
|
||||
#endif
|
||||
|
||||
#ifdef OLED_DRIVER_ENABLE
|
||||
oled_task();
|
||||
# ifndef OLED_DISABLE_TIMEOUT
|
||||
// Wake up oled if user is using those fabulous keys or spinning those encoders!
|
||||
# ifdef ENCODER_ENABLE
|
||||
if (matrix_changed || encoders_changed) oled_on();
|
||||
# else
|
||||
if (matrix_changed) oled_on();
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef MOUSEKEY_ENABLE
|
||||
// mousekey repeat & acceleration
|
||||
mousekey_task();
|
||||
#endif
|
||||
|
||||
#ifdef PS2_MOUSE_ENABLE
|
||||
ps2_mouse_task();
|
||||
#endif
|
||||
|
||||
#ifdef SERIAL_MOUSE_ENABLE
|
||||
serial_mouse_task();
|
||||
#endif
|
||||
|
||||
#ifdef ADB_MOUSE_ENABLE
|
||||
adb_mouse_task();
|
||||
#endif
|
||||
|
||||
#ifdef SERIAL_LINK_ENABLE
|
||||
serial_link_update();
|
||||
#endif
|
||||
|
||||
#ifdef VISUALIZER_ENABLE
|
||||
visualizer_update(default_layer_state, layer_state, visualizer_get_mods(), host_keyboard_leds());
|
||||
#endif
|
||||
|
||||
#ifdef POINTING_DEVICE_ENABLE
|
||||
pointing_device_task();
|
||||
#endif
|
||||
|
||||
#ifdef MIDI_ENABLE
|
||||
midi_task();
|
||||
#endif
|
||||
|
||||
#ifdef VELOCIKEY_ENABLE
|
||||
if (velocikey_enabled()) {
|
||||
velocikey_decelerate();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef JOYSTICK_ENABLE
|
||||
joystick_task();
|
||||
#endif
|
||||
|
||||
// update LED
|
||||
if (led_status != host_keyboard_leds()) {
|
||||
led_status = host_keyboard_leds();
|
||||
keyboard_set_leds(led_status);
|
||||
}
|
||||
}
|
||||
|
||||
/** \brief keyboard set leds
|
||||
*
|
||||
* FIXME: needs doc
|
||||
*/
|
||||
void keyboard_set_leds(uint8_t leds) {
|
||||
if (debug_keyboard) {
|
||||
debug("keyboard_set_led: ");
|
||||
debug_hex8(leds);
|
||||
debug("\n");
|
||||
}
|
||||
led_set(leds);
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
/*
|
||||
Copyright 2011,2012,2013 Jun Wako <wakojun@gmail.com>
|
||||
|
||||
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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* key matrix position */
|
||||
typedef struct {
|
||||
uint8_t col;
|
||||
uint8_t row;
|
||||
} keypos_t;
|
||||
|
||||
/* key event */
|
||||
typedef struct {
|
||||
keypos_t key;
|
||||
bool pressed;
|
||||
uint16_t time;
|
||||
} keyevent_t;
|
||||
|
||||
/* equivalent test of keypos_t */
|
||||
#define KEYEQ(keya, keyb) ((keya).row == (keyb).row && (keya).col == (keyb).col)
|
||||
|
||||
/* Rules for No Event:
|
||||
* 1) (time == 0) to handle (keyevent_t){} as empty event
|
||||
* 2) Matrix(255, 255) to make TICK event available
|
||||
*/
|
||||
static inline bool IS_NOEVENT(keyevent_t event) { return event.time == 0 || (event.key.row == 255 && event.key.col == 255); }
|
||||
static inline bool IS_PRESSED(keyevent_t event) { return (!IS_NOEVENT(event) && event.pressed); }
|
||||
static inline bool IS_RELEASED(keyevent_t event) { return (!IS_NOEVENT(event) && !event.pressed); }
|
||||
|
||||
/* Tick event */
|
||||
#define TICK \
|
||||
(keyevent_t) { .key = (keypos_t){.row = 255, .col = 255}, .pressed = false, .time = (timer_read() | 1) }
|
||||
|
||||
/* it runs once at early stage of startup before keyboard_init. */
|
||||
void keyboard_setup(void);
|
||||
/* it runs once after initializing host side protocol, debug and MCU peripherals. */
|
||||
void keyboard_init(void);
|
||||
/* it runs repeatedly in main loop */
|
||||
void keyboard_task(void);
|
||||
/* it runs when host LED status is updated */
|
||||
void keyboard_set_leds(uint8_t leds);
|
||||
/* it runs whenever code has to behave differently on a slave */
|
||||
bool is_keyboard_master(void);
|
||||
/* it runs whenever code has to behave differently on left vs right split */
|
||||
bool is_keyboard_left(void);
|
||||
|
||||
void keyboard_pre_init_kb(void);
|
||||
void keyboard_pre_init_user(void);
|
||||
void keyboard_post_init_kb(void);
|
||||
void keyboard_post_init_user(void);
|
||||
|
||||
void housekeeping_task(void); // To be executed by the main loop in each backend TMK protocol
|
||||
void housekeeping_task_kb(void); // To be overridden by keyboard-level code
|
||||
void housekeeping_task_user(void); // To be overridden by user/keymap-level code
|
||||
|
||||
uint32_t last_input_activity_time(void); // Timestamp of the last matrix or encoder activity
|
||||
uint32_t last_input_activity_elapsed(void); // Number of milliseconds since the last matrix or encoder activity
|
||||
|
||||
uint32_t last_matrix_activity_time(void); // Timestamp of the last matrix activity
|
||||
uint32_t last_matrix_activity_elapsed(void); // Number of milliseconds since the last matrix activity
|
||||
|
||||
uint32_t last_encoder_activity_time(void); // Timestamp of the last encoder activity
|
||||
uint32_t last_encoder_activity_elapsed(void); // Number of milliseconds since the last encoder activity
|
||||
|
||||
uint32_t get_matrix_scan_rate(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,560 +0,0 @@
|
||||
/*
|
||||
Copyright 2011,2012 Jun Wako <wakojun@gmail.com>
|
||||
|
||||
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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Keycodes based on HID Keyboard/Keypad Usage Page (0x07) plus media keys from Generic Desktop Page (0x01) and Consumer Page (0x0C)
|
||||
*
|
||||
* See https://web.archive.org/web/20060218214400/http://www.usb.org/developers/devclass_docs/Hut1_12.pdf
|
||||
* or http://www.usb.org/developers/hidpage/Hut1_12v2.pdf (older)
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/* FIXME: Add doxygen comments here */
|
||||
|
||||
#define IS_ERROR(code) (KC_ROLL_OVER <= (code) && (code) <= KC_UNDEFINED)
|
||||
#define IS_ANY(code) (KC_A <= (code) && (code) <= 0xFF)
|
||||
#define IS_KEY(code) (KC_A <= (code) && (code) <= KC_EXSEL)
|
||||
#define IS_MOD(code) (KC_LCTRL <= (code) && (code) <= KC_RGUI)
|
||||
|
||||
#define IS_SPECIAL(code) ((0xA5 <= (code) && (code) <= 0xDF) || (0xE8 <= (code) && (code) <= 0xFF))
|
||||
#define IS_SYSTEM(code) (KC_PWR <= (code) && (code) <= KC_WAKE)
|
||||
#define IS_CONSUMER(code) (KC_MUTE <= (code) && (code) <= KC_BRID)
|
||||
|
||||
#define IS_FN(code) (KC_FN0 <= (code) && (code) <= KC_FN31)
|
||||
|
||||
#define IS_MOUSEKEY(code) (KC_MS_UP <= (code) && (code) <= KC_MS_ACCEL2)
|
||||
#define IS_MOUSEKEY_MOVE(code) (KC_MS_UP <= (code) && (code) <= KC_MS_RIGHT)
|
||||
#define IS_MOUSEKEY_BUTTON(code) (KC_MS_BTN1 <= (code) && (code) <= KC_MS_BTN8)
|
||||
#define IS_MOUSEKEY_WHEEL(code) (KC_MS_WH_UP <= (code) && (code) <= KC_MS_WH_RIGHT)
|
||||
#define IS_MOUSEKEY_ACCEL(code) (KC_MS_ACCEL0 <= (code) && (code) <= KC_MS_ACCEL2)
|
||||
|
||||
#define MOD_BIT(code) (1 << MOD_INDEX(code))
|
||||
#define MOD_INDEX(code) ((code)&0x07)
|
||||
|
||||
#define MOD_MASK_CTRL (MOD_BIT(KC_LCTRL) | MOD_BIT(KC_RCTRL))
|
||||
#define MOD_MASK_SHIFT (MOD_BIT(KC_LSHIFT) | MOD_BIT(KC_RSHIFT))
|
||||
#define MOD_MASK_ALT (MOD_BIT(KC_LALT) | MOD_BIT(KC_RALT))
|
||||
#define MOD_MASK_GUI (MOD_BIT(KC_LGUI) | MOD_BIT(KC_RGUI))
|
||||
#define MOD_MASK_CS (MOD_MASK_CTRL | MOD_MASK_SHIFT)
|
||||
#define MOD_MASK_CA (MOD_MASK_CTRL | MOD_MASK_ALT)
|
||||
#define MOD_MASK_CG (MOD_MASK_CTRL | MOD_MASK_GUI)
|
||||
#define MOD_MASK_SA (MOD_MASK_SHIFT | MOD_MASK_ALT)
|
||||
#define MOD_MASK_SG (MOD_MASK_SHIFT | MOD_MASK_GUI)
|
||||
#define MOD_MASK_AG (MOD_MASK_ALT | MOD_MASK_GUI)
|
||||
#define MOD_MASK_CSA (MOD_MASK_CTRL | MOD_MASK_SHIFT | MOD_MASK_ALT)
|
||||
#define MOD_MASK_CSG (MOD_MASK_CTRL | MOD_MASK_SHIFT | MOD_MASK_GUI)
|
||||
#define MOD_MASK_CAG (MOD_MASK_CTRL | MOD_MASK_ALT | MOD_MASK_GUI)
|
||||
#define MOD_MASK_SAG (MOD_MASK_SHIFT | MOD_MASK_ALT | MOD_MASK_GUI)
|
||||
#define MOD_MASK_CSAG (MOD_MASK_CTRL | MOD_MASK_SHIFT | MOD_MASK_ALT | MOD_MASK_GUI)
|
||||
|
||||
#define FN_BIT(code) (1 << FN_INDEX(code))
|
||||
#define FN_INDEX(code) ((code)-KC_FN0)
|
||||
#define FN_MIN KC_FN0
|
||||
#define FN_MAX KC_FN31
|
||||
|
||||
/*
|
||||
* Short names for ease of definition of keymap
|
||||
*/
|
||||
/* Transparent */
|
||||
#define KC_TRANSPARENT 0x01
|
||||
#define KC_TRNS KC_TRANSPARENT
|
||||
|
||||
/* Punctuation */
|
||||
#define KC_ENT KC_ENTER
|
||||
#define KC_ESC KC_ESCAPE
|
||||
#define KC_BSPC KC_BSPACE
|
||||
#define KC_SPC KC_SPACE
|
||||
#define KC_MINS KC_MINUS
|
||||
#define KC_EQL KC_EQUAL
|
||||
#define KC_LBRC KC_LBRACKET
|
||||
#define KC_RBRC KC_RBRACKET
|
||||
#define KC_BSLS KC_BSLASH
|
||||
#define KC_NUHS KC_NONUS_HASH
|
||||
#define KC_SCLN KC_SCOLON
|
||||
#define KC_QUOT KC_QUOTE
|
||||
#define KC_GRV KC_GRAVE
|
||||
#define KC_COMM KC_COMMA
|
||||
#define KC_SLSH KC_SLASH
|
||||
#define KC_NUBS KC_NONUS_BSLASH
|
||||
|
||||
/* Lock Keys */
|
||||
#define KC_CLCK KC_CAPSLOCK
|
||||
#define KC_CAPS KC_CAPSLOCK
|
||||
#define KC_SLCK KC_SCROLLLOCK
|
||||
#define KC_NLCK KC_NUMLOCK
|
||||
#define KC_LCAP KC_LOCKING_CAPS
|
||||
#define KC_LNUM KC_LOCKING_NUM
|
||||
#define KC_LSCR KC_LOCKING_SCROLL
|
||||
|
||||
/* Commands */
|
||||
#define KC_PSCR KC_PSCREEN
|
||||
#define KC_PAUS KC_PAUSE
|
||||
#define KC_BRK KC_PAUSE
|
||||
#define KC_INS KC_INSERT
|
||||
#define KC_DEL KC_DELETE
|
||||
#define KC_PGDN KC_PGDOWN
|
||||
#define KC_RGHT KC_RIGHT
|
||||
#define KC_APP KC_APPLICATION
|
||||
#define KC_EXEC KC_EXECUTE
|
||||
#define KC_SLCT KC_SELECT
|
||||
#define KC_AGIN KC_AGAIN
|
||||
#define KC_PSTE KC_PASTE
|
||||
#define KC_ERAS KC_ALT_ERASE
|
||||
#define KC_CLR KC_CLEAR
|
||||
|
||||
/* Keypad */
|
||||
#define KC_PSLS KC_KP_SLASH
|
||||
#define KC_PAST KC_KP_ASTERISK
|
||||
#define KC_PMNS KC_KP_MINUS
|
||||
#define KC_PPLS KC_KP_PLUS
|
||||
#define KC_PENT KC_KP_ENTER
|
||||
#define KC_P1 KC_KP_1
|
||||
#define KC_P2 KC_KP_2
|
||||
#define KC_P3 KC_KP_3
|
||||
#define KC_P4 KC_KP_4
|
||||
#define KC_P5 KC_KP_5
|
||||
#define KC_P6 KC_KP_6
|
||||
#define KC_P7 KC_KP_7
|
||||
#define KC_P8 KC_KP_8
|
||||
#define KC_P9 KC_KP_9
|
||||
#define KC_P0 KC_KP_0
|
||||
#define KC_PDOT KC_KP_DOT
|
||||
#define KC_PEQL KC_KP_EQUAL
|
||||
#define KC_PCMM KC_KP_COMMA
|
||||
|
||||
/* Japanese specific */
|
||||
#define KC_ZKHK KC_GRAVE
|
||||
#define KC_RO KC_INT1
|
||||
#define KC_KANA KC_INT2
|
||||
#define KC_JYEN KC_INT3
|
||||
#define KC_HENK KC_INT4
|
||||
#define KC_MHEN KC_INT5
|
||||
|
||||
/* Korean specific */
|
||||
#define KC_HAEN KC_LANG1
|
||||
#define KC_HANJ KC_LANG2
|
||||
|
||||
/* Modifiers */
|
||||
#define KC_LCTL KC_LCTRL
|
||||
#define KC_LSFT KC_LSHIFT
|
||||
#define KC_LOPT KC_LALT
|
||||
#define KC_LCMD KC_LGUI
|
||||
#define KC_LWIN KC_LGUI
|
||||
#define KC_RCTL KC_RCTRL
|
||||
#define KC_RSFT KC_RSHIFT
|
||||
#define KC_ALGR KC_RALT
|
||||
#define KC_ROPT KC_RALT
|
||||
#define KC_RCMD KC_RGUI
|
||||
#define KC_RWIN KC_RGUI
|
||||
|
||||
/* Generic Desktop Page (0x01) */
|
||||
#define KC_PWR KC_SYSTEM_POWER
|
||||
#define KC_SLEP KC_SYSTEM_SLEEP
|
||||
#define KC_WAKE KC_SYSTEM_WAKE
|
||||
|
||||
/* Consumer Page (0x0C) */
|
||||
#define KC_MUTE KC_AUDIO_MUTE
|
||||
#define KC_VOLU KC_AUDIO_VOL_UP
|
||||
#define KC_VOLD KC_AUDIO_VOL_DOWN
|
||||
#define KC_MNXT KC_MEDIA_NEXT_TRACK
|
||||
#define KC_MPRV KC_MEDIA_PREV_TRACK
|
||||
#define KC_MSTP KC_MEDIA_STOP
|
||||
#define KC_MPLY KC_MEDIA_PLAY_PAUSE
|
||||
#define KC_MSEL KC_MEDIA_SELECT
|
||||
#define KC_EJCT KC_MEDIA_EJECT
|
||||
#define KC_CALC KC_CALCULATOR
|
||||
#define KC_MYCM KC_MY_COMPUTER
|
||||
#define KC_WSCH KC_WWW_SEARCH
|
||||
#define KC_WHOM KC_WWW_HOME
|
||||
#define KC_WBAK KC_WWW_BACK
|
||||
#define KC_WFWD KC_WWW_FORWARD
|
||||
#define KC_WSTP KC_WWW_STOP
|
||||
#define KC_WREF KC_WWW_REFRESH
|
||||
#define KC_WFAV KC_WWW_FAVORITES
|
||||
#define KC_MFFD KC_MEDIA_FAST_FORWARD
|
||||
#define KC_MRWD KC_MEDIA_REWIND
|
||||
#define KC_BRIU KC_BRIGHTNESS_UP
|
||||
#define KC_BRID KC_BRIGHTNESS_DOWN
|
||||
|
||||
/* System Specific */
|
||||
#define KC_BRMU KC_PAUSE
|
||||
#define KC_BRMD KC_SCROLLLOCK
|
||||
|
||||
/* Mouse Keys */
|
||||
#define KC_MS_U KC_MS_UP
|
||||
#define KC_MS_D KC_MS_DOWN
|
||||
#define KC_MS_L KC_MS_LEFT
|
||||
#define KC_MS_R KC_MS_RIGHT
|
||||
#define KC_BTN1 KC_MS_BTN1
|
||||
#define KC_BTN2 KC_MS_BTN2
|
||||
#define KC_BTN3 KC_MS_BTN3
|
||||
#define KC_BTN4 KC_MS_BTN4
|
||||
#define KC_BTN5 KC_MS_BTN5
|
||||
#define KC_BTN6 KC_MS_BTN6
|
||||
#define KC_BTN7 KC_MS_BTN7
|
||||
#define KC_BTN8 KC_MS_BTN8
|
||||
#define KC_WH_U KC_MS_WH_UP
|
||||
#define KC_WH_D KC_MS_WH_DOWN
|
||||
#define KC_WH_L KC_MS_WH_LEFT
|
||||
#define KC_WH_R KC_MS_WH_RIGHT
|
||||
#define KC_ACL0 KC_MS_ACCEL0
|
||||
#define KC_ACL1 KC_MS_ACCEL1
|
||||
#define KC_ACL2 KC_MS_ACCEL2
|
||||
|
||||
/* Keyboard/Keypad Page (0x07) */
|
||||
enum hid_keyboard_keypad_usage {
|
||||
KC_NO = 0x00,
|
||||
KC_ROLL_OVER,
|
||||
KC_POST_FAIL,
|
||||
KC_UNDEFINED,
|
||||
KC_A,
|
||||
KC_B,
|
||||
KC_C,
|
||||
KC_D,
|
||||
KC_E,
|
||||
KC_F,
|
||||
KC_G,
|
||||
KC_H,
|
||||
KC_I,
|
||||
KC_J,
|
||||
KC_K,
|
||||
KC_L,
|
||||
KC_M, // 0x10
|
||||
KC_N,
|
||||
KC_O,
|
||||
KC_P,
|
||||
KC_Q,
|
||||
KC_R,
|
||||
KC_S,
|
||||
KC_T,
|
||||
KC_U,
|
||||
KC_V,
|
||||
KC_W,
|
||||
KC_X,
|
||||
KC_Y,
|
||||
KC_Z,
|
||||
KC_1,
|
||||
KC_2,
|
||||
KC_3, // 0x20
|
||||
KC_4,
|
||||
KC_5,
|
||||
KC_6,
|
||||
KC_7,
|
||||
KC_8,
|
||||
KC_9,
|
||||
KC_0,
|
||||
KC_ENTER,
|
||||
KC_ESCAPE,
|
||||
KC_BSPACE,
|
||||
KC_TAB,
|
||||
KC_SPACE,
|
||||
KC_MINUS,
|
||||
KC_EQUAL,
|
||||
KC_LBRACKET,
|
||||
KC_RBRACKET, // 0x30
|
||||
KC_BSLASH,
|
||||
KC_NONUS_HASH,
|
||||
KC_SCOLON,
|
||||
KC_QUOTE,
|
||||
KC_GRAVE,
|
||||
KC_COMMA,
|
||||
KC_DOT,
|
||||
KC_SLASH,
|
||||
KC_CAPSLOCK,
|
||||
KC_F1,
|
||||
KC_F2,
|
||||
KC_F3,
|
||||
KC_F4,
|
||||
KC_F5,
|
||||
KC_F6,
|
||||
KC_F7, // 0x40
|
||||
KC_F8,
|
||||
KC_F9,
|
||||
KC_F10,
|
||||
KC_F11,
|
||||
KC_F12,
|
||||
KC_PSCREEN,
|
||||
KC_SCROLLLOCK,
|
||||
KC_PAUSE,
|
||||
KC_INSERT,
|
||||
KC_HOME,
|
||||
KC_PGUP,
|
||||
KC_DELETE,
|
||||
KC_END,
|
||||
KC_PGDOWN,
|
||||
KC_RIGHT,
|
||||
KC_LEFT, // 0x50
|
||||
KC_DOWN,
|
||||
KC_UP,
|
||||
KC_NUMLOCK,
|
||||
KC_KP_SLASH,
|
||||
KC_KP_ASTERISK,
|
||||
KC_KP_MINUS,
|
||||
KC_KP_PLUS,
|
||||
KC_KP_ENTER,
|
||||
KC_KP_1,
|
||||
KC_KP_2,
|
||||
KC_KP_3,
|
||||
KC_KP_4,
|
||||
KC_KP_5,
|
||||
KC_KP_6,
|
||||
KC_KP_7,
|
||||
KC_KP_8, // 0x60
|
||||
KC_KP_9,
|
||||
KC_KP_0,
|
||||
KC_KP_DOT,
|
||||
KC_NONUS_BSLASH,
|
||||
KC_APPLICATION,
|
||||
KC_POWER,
|
||||
KC_KP_EQUAL,
|
||||
KC_F13,
|
||||
KC_F14,
|
||||
KC_F15,
|
||||
KC_F16,
|
||||
KC_F17,
|
||||
KC_F18,
|
||||
KC_F19,
|
||||
KC_F20,
|
||||
KC_F21, // 0x70
|
||||
KC_F22,
|
||||
KC_F23,
|
||||
KC_F24,
|
||||
KC_EXECUTE,
|
||||
KC_HELP,
|
||||
KC_MENU,
|
||||
KC_SELECT,
|
||||
KC_STOP,
|
||||
KC_AGAIN,
|
||||
KC_UNDO,
|
||||
KC_CUT,
|
||||
KC_COPY,
|
||||
KC_PASTE,
|
||||
KC_FIND,
|
||||
KC__MUTE,
|
||||
KC__VOLUP, // 0x80
|
||||
KC__VOLDOWN,
|
||||
KC_LOCKING_CAPS,
|
||||
KC_LOCKING_NUM,
|
||||
KC_LOCKING_SCROLL,
|
||||
KC_KP_COMMA,
|
||||
KC_KP_EQUAL_AS400,
|
||||
KC_INT1,
|
||||
KC_INT2,
|
||||
KC_INT3,
|
||||
KC_INT4,
|
||||
KC_INT5,
|
||||
KC_INT6,
|
||||
KC_INT7,
|
||||
KC_INT8,
|
||||
KC_INT9,
|
||||
KC_LANG1, // 0x90
|
||||
KC_LANG2,
|
||||
KC_LANG3,
|
||||
KC_LANG4,
|
||||
KC_LANG5,
|
||||
KC_LANG6,
|
||||
KC_LANG7,
|
||||
KC_LANG8,
|
||||
KC_LANG9,
|
||||
KC_ALT_ERASE,
|
||||
KC_SYSREQ,
|
||||
KC_CANCEL,
|
||||
KC_CLEAR,
|
||||
KC_PRIOR,
|
||||
KC_RETURN,
|
||||
KC_SEPARATOR,
|
||||
KC_OUT, // 0xA0
|
||||
KC_OPER,
|
||||
KC_CLEAR_AGAIN,
|
||||
KC_CRSEL,
|
||||
KC_EXSEL,
|
||||
|
||||
#if 0
|
||||
// ***************************************************************
|
||||
// These keycodes are present in the HID spec, but are *
|
||||
// nonfunctional on modern OSes. QMK uses this range (0xA5-0xDF) *
|
||||
// for the media and function keys instead - see below. *
|
||||
// ***************************************************************
|
||||
|
||||
KC_KP_00 = 0xB0,
|
||||
KC_KP_000,
|
||||
KC_THOUSANDS_SEPARATOR,
|
||||
KC_DECIMAL_SEPARATOR,
|
||||
KC_CURRENCY_UNIT,
|
||||
KC_CURRENCY_SUB_UNIT,
|
||||
KC_KP_LPAREN,
|
||||
KC_KP_RPAREN,
|
||||
KC_KP_LCBRACKET,
|
||||
KC_KP_RCBRACKET,
|
||||
KC_KP_TAB,
|
||||
KC_KP_BSPACE,
|
||||
KC_KP_A,
|
||||
KC_KP_B,
|
||||
KC_KP_C,
|
||||
KC_KP_D,
|
||||
KC_KP_E, //0xC0
|
||||
KC_KP_F,
|
||||
KC_KP_XOR,
|
||||
KC_KP_HAT,
|
||||
KC_KP_PERC,
|
||||
KC_KP_LT,
|
||||
KC_KP_GT,
|
||||
KC_KP_AND,
|
||||
KC_KP_LAZYAND,
|
||||
KC_KP_OR,
|
||||
KC_KP_LAZYOR,
|
||||
KC_KP_COLON,
|
||||
KC_KP_HASH,
|
||||
KC_KP_SPACE,
|
||||
KC_KP_ATMARK,
|
||||
KC_KP_EXCLAMATION,
|
||||
KC_KP_MEM_STORE, //0xD0
|
||||
KC_KP_MEM_RECALL,
|
||||
KC_KP_MEM_CLEAR,
|
||||
KC_KP_MEM_ADD,
|
||||
KC_KP_MEM_SUB,
|
||||
KC_KP_MEM_MUL,
|
||||
KC_KP_MEM_DIV,
|
||||
KC_KP_PLUS_MINUS,
|
||||
KC_KP_CLEAR,
|
||||
KC_KP_CLEAR_ENTRY,
|
||||
KC_KP_BINARY,
|
||||
KC_KP_OCTAL,
|
||||
KC_KP_DECIMAL,
|
||||
KC_KP_HEXADECIMAL,
|
||||
#endif
|
||||
|
||||
/* Modifiers */
|
||||
KC_LCTRL = 0xE0,
|
||||
KC_LSHIFT,
|
||||
KC_LALT,
|
||||
KC_LGUI,
|
||||
KC_RCTRL,
|
||||
KC_RSHIFT,
|
||||
KC_RALT,
|
||||
KC_RGUI
|
||||
|
||||
// **********************************************
|
||||
// * 0xF0-0xFF are unallocated in the HID spec. *
|
||||
// * QMK uses these for Mouse Keys - see below. *
|
||||
// **********************************************
|
||||
};
|
||||
|
||||
/* Media and Function keys */
|
||||
enum internal_special_keycodes {
|
||||
/* Generic Desktop Page (0x01) */
|
||||
KC_SYSTEM_POWER = 0xA5,
|
||||
KC_SYSTEM_SLEEP,
|
||||
KC_SYSTEM_WAKE,
|
||||
|
||||
/* Consumer Page (0x0C) */
|
||||
KC_AUDIO_MUTE,
|
||||
KC_AUDIO_VOL_UP,
|
||||
KC_AUDIO_VOL_DOWN,
|
||||
KC_MEDIA_NEXT_TRACK,
|
||||
KC_MEDIA_PREV_TRACK,
|
||||
KC_MEDIA_STOP,
|
||||
KC_MEDIA_PLAY_PAUSE,
|
||||
KC_MEDIA_SELECT,
|
||||
KC_MEDIA_EJECT, // 0xB0
|
||||
KC_MAIL,
|
||||
KC_CALCULATOR,
|
||||
KC_MY_COMPUTER,
|
||||
KC_WWW_SEARCH,
|
||||
KC_WWW_HOME,
|
||||
KC_WWW_BACK,
|
||||
KC_WWW_FORWARD,
|
||||
KC_WWW_STOP,
|
||||
KC_WWW_REFRESH,
|
||||
KC_WWW_FAVORITES,
|
||||
KC_MEDIA_FAST_FORWARD,
|
||||
KC_MEDIA_REWIND,
|
||||
KC_BRIGHTNESS_UP,
|
||||
KC_BRIGHTNESS_DOWN,
|
||||
|
||||
/* Fn keys */
|
||||
KC_FN0 = 0xC0,
|
||||
KC_FN1,
|
||||
KC_FN2,
|
||||
KC_FN3,
|
||||
KC_FN4,
|
||||
KC_FN5,
|
||||
KC_FN6,
|
||||
KC_FN7,
|
||||
KC_FN8,
|
||||
KC_FN9,
|
||||
KC_FN10,
|
||||
KC_FN11,
|
||||
KC_FN12,
|
||||
KC_FN13,
|
||||
KC_FN14,
|
||||
KC_FN15,
|
||||
KC_FN16, // 0xD0
|
||||
KC_FN17,
|
||||
KC_FN18,
|
||||
KC_FN19,
|
||||
KC_FN20,
|
||||
KC_FN21,
|
||||
KC_FN22,
|
||||
KC_FN23,
|
||||
KC_FN24,
|
||||
KC_FN25,
|
||||
KC_FN26,
|
||||
KC_FN27,
|
||||
KC_FN28,
|
||||
KC_FN29,
|
||||
KC_FN30,
|
||||
KC_FN31
|
||||
};
|
||||
|
||||
enum mouse_keys {
|
||||
/* Mouse Buttons */
|
||||
#ifdef VIA_ENABLE
|
||||
KC_MS_UP = 0xF0,
|
||||
#else
|
||||
KC_MS_UP = 0xED,
|
||||
#endif
|
||||
KC_MS_DOWN,
|
||||
KC_MS_LEFT,
|
||||
KC_MS_RIGHT, // 0xF0
|
||||
KC_MS_BTN1,
|
||||
KC_MS_BTN2,
|
||||
KC_MS_BTN3,
|
||||
KC_MS_BTN4,
|
||||
KC_MS_BTN5,
|
||||
#ifdef VIA_ENABLE
|
||||
KC_MS_BTN6 = KC_MS_BTN5,
|
||||
KC_MS_BTN7 = KC_MS_BTN5,
|
||||
KC_MS_BTN8 = KC_MS_BTN5,
|
||||
#else
|
||||
KC_MS_BTN6,
|
||||
KC_MS_BTN7,
|
||||
KC_MS_BTN8,
|
||||
#endif
|
||||
|
||||
/* Mouse Wheel */
|
||||
KC_MS_WH_UP,
|
||||
KC_MS_WH_DOWN,
|
||||
KC_MS_WH_LEFT,
|
||||
KC_MS_WH_RIGHT,
|
||||
|
||||
/* Acceleration */
|
||||
KC_MS_ACCEL0,
|
||||
KC_MS_ACCEL1,
|
||||
KC_MS_ACCEL2 // 0xFF
|
||||
};
|
||||
@@ -1,9 +0,0 @@
|
||||
PRINTF_PATH = $(LIB_PATH)/printf
|
||||
|
||||
TMK_COMMON_SRC += $(PRINTF_PATH)/printf.c
|
||||
TMK_COMMON_SRC += $(COMMON_DIR)/printf.c
|
||||
TMK_COMMON_DEFS += -DPRINTF_DISABLE_SUPPORT_FLOAT
|
||||
TMK_COMMON_DEFS += -DPRINTF_DISABLE_SUPPORT_EXPONENTIAL
|
||||
TMK_COMMON_DEFS += -DPRINTF_DISABLE_SUPPORT_LONG_LONG
|
||||
TMK_COMMON_DEFS += -DPRINTF_DISABLE_SUPPORT_PTRDIFF_T
|
||||
VPATH += $(PRINTF_PATH)
|
||||
@@ -1,26 +0,0 @@
|
||||
/*
|
||||
Copyright 2013 Jun Wako <wakojun@gmail.com>
|
||||
|
||||
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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef NO_DEBUG
|
||||
# define NO_DEBUG
|
||||
# include "debug.h"
|
||||
# undef NO_DEBUG
|
||||
#else
|
||||
# include "debug.h"
|
||||
#endif
|
||||
@@ -1,135 +0,0 @@
|
||||
/* Copyright 2012 Jun Wako <wakojun@gmail.com> */
|
||||
/* Very basic print functions, intended to be used with usb_debug_only.c
|
||||
* http://www.pjrc.com/teensy/
|
||||
* Copyright (c) 2008 PJRC.COM, LLC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "util.h"
|
||||
#include "sendchar.h"
|
||||
#include "progmem.h"
|
||||
|
||||
void print_set_sendchar(sendchar_func_t func);
|
||||
|
||||
#ifndef NO_PRINT
|
||||
# if __has_include_next("_print.h")
|
||||
# include_next "_print.h" /* Include the platforms print.h */
|
||||
# else
|
||||
// Fall back to lib/printf
|
||||
# include "printf.h" // lib/printf/printf.h
|
||||
|
||||
// Create user & normal print defines
|
||||
# define print(s) printf(s)
|
||||
# define println(s) printf(s "\r\n")
|
||||
# define xprintf printf
|
||||
# define uprint(s) printf(s)
|
||||
# define uprintln(s) printf(s "\r\n")
|
||||
# define uprintf printf
|
||||
|
||||
# endif /* __AVR__ / PROTOCOL_CHIBIOS / PROTOCOL_ARM_ATSAM */
|
||||
#else /* NO_PRINT */
|
||||
# undef xprintf
|
||||
// Remove print defines
|
||||
# define print(s)
|
||||
# define println(s)
|
||||
# define xprintf(fmt, ...)
|
||||
# define uprintf(fmt, ...)
|
||||
# define uprint(s)
|
||||
# define uprintln(s)
|
||||
|
||||
#endif /* NO_PRINT */
|
||||
|
||||
#ifdef USER_PRINT
|
||||
// Remove normal print defines
|
||||
# undef print
|
||||
# undef println
|
||||
# undef xprintf
|
||||
# define print(s)
|
||||
# define println(s)
|
||||
# define xprintf(fmt, ...)
|
||||
#endif
|
||||
|
||||
#define print_dec(i) xprintf("%u", i)
|
||||
#define print_decs(i) xprintf("%d", i)
|
||||
/* hex */
|
||||
#define print_hex4(i) xprintf("%X", i)
|
||||
#define print_hex8(i) xprintf("%02X", i)
|
||||
#define print_hex16(i) xprintf("%04X", i)
|
||||
#define print_hex32(i) xprintf("%08lX", i)
|
||||
/* binary */
|
||||
#define print_bin4(i) xprintf("%04b", i)
|
||||
#define print_bin8(i) xprintf("%08b", i)
|
||||
#define print_bin16(i) xprintf("%016b", i)
|
||||
#define print_bin32(i) xprintf("%032lb", i)
|
||||
#define print_bin_reverse8(i) xprintf("%08b", bitrev(i))
|
||||
#define print_bin_reverse16(i) xprintf("%016b", bitrev16(i))
|
||||
#define print_bin_reverse32(i) xprintf("%032lb", bitrev32(i))
|
||||
/* print value utility */
|
||||
#define print_val_dec(v) xprintf(#v ": %u\n", v)
|
||||
#define print_val_decs(v) xprintf(#v ": %d\n", v)
|
||||
#define print_val_hex8(v) xprintf(#v ": %X\n", v)
|
||||
#define print_val_hex16(v) xprintf(#v ": %02X\n", v)
|
||||
#define print_val_hex32(v) xprintf(#v ": %04lX\n", v)
|
||||
#define print_val_bin8(v) xprintf(#v ": %08b\n", v)
|
||||
#define print_val_bin16(v) xprintf(#v ": %016b\n", v)
|
||||
#define print_val_bin32(v) xprintf(#v ": %032lb\n", v)
|
||||
#define print_val_bin_reverse8(v) xprintf(#v ": %08b\n", bitrev(v))
|
||||
#define print_val_bin_reverse16(v) xprintf(#v ": %016b\n", bitrev16(v))
|
||||
#define print_val_bin_reverse32(v) xprintf(#v ": %032lb\n", bitrev32(v))
|
||||
|
||||
// User print disables the normal print messages in the body of QMK/TMK code and
|
||||
// is meant as a lightweight alternative to NOPRINT. Use it when you only want to do
|
||||
// a spot of debugging but lack flash resources for allowing all of the codebase to
|
||||
// print (and store their wasteful strings).
|
||||
//
|
||||
// !!! DO NOT USE USER PRINT CALLS IN THE BODY OF QMK/TMK !!!
|
||||
|
||||
/* decimal */
|
||||
#define uprint_dec(i) uprintf("%u", i)
|
||||
#define uprint_decs(i) uprintf("%d", i)
|
||||
/* hex */
|
||||
#define uprint_hex4(i) uprintf("%X", i)
|
||||
#define uprint_hex8(i) uprintf("%02X", i)
|
||||
#define uprint_hex16(i) uprintf("%04X", i)
|
||||
#define uprint_hex32(i) uprintf("%08lX", i)
|
||||
/* binary */
|
||||
#define uprint_bin4(i) uprintf("%04b", i)
|
||||
#define uprint_bin8(i) uprintf("%08b", i)
|
||||
#define uprint_bin16(i) uprintf("%016b", i)
|
||||
#define uprint_bin32(i) uprintf("%032lb", i)
|
||||
#define uprint_bin_reverse8(i) uprintf("%08b", bitrev(i))
|
||||
#define uprint_bin_reverse16(i) uprintf("%016b", bitrev16(i))
|
||||
#define uprint_bin_reverse32(i) uprintf("%032lb", bitrev32(i))
|
||||
/* print value utility */
|
||||
#define uprint_val_dec(v) uprintf(#v ": %u\n", v)
|
||||
#define uprint_val_decs(v) uprintf(#v ": %d\n", v)
|
||||
#define uprint_val_hex8(v) uprintf(#v ": %X\n", v)
|
||||
#define uprint_val_hex16(v) uprintf(#v ": %02X\n", v)
|
||||
#define uprint_val_hex32(v) uprintf(#v ": %04lX\n", v)
|
||||
#define uprint_val_bin8(v) uprintf(#v ": %08b\n", v)
|
||||
#define uprint_val_bin16(v) uprintf(#v ": %016b\n", v)
|
||||
#define uprint_val_bin32(v) uprintf(#v ": %032lb\n", v)
|
||||
#define uprint_val_bin_reverse8(v) uprintf(#v ": %08b\n", bitrev(v))
|
||||
#define uprint_val_bin_reverse16(v) uprintf(#v ": %016b\n", bitrev16(v))
|
||||
#define uprint_val_bin_reverse32(v) uprintf(#v ": %032lb\n", bitrev32(v))
|
||||
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
Copyright 2011 Jun Wako <wakojun@gmail.com>
|
||||
|
||||
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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stddef.h>
|
||||
#include "sendchar.h"
|
||||
|
||||
// bind lib/printf to console interface - sendchar
|
||||
|
||||
static int8_t null_sendchar_func(uint8_t c) { return 0; }
|
||||
static sendchar_func_t func = null_sendchar_func;
|
||||
|
||||
void print_set_sendchar(sendchar_func_t send) { func = send; }
|
||||
|
||||
void _putchar(char character) { func(character); }
|
||||
@@ -3,7 +3,9 @@
|
||||
#if defined(__AVR__)
|
||||
# include <avr/pgmspace.h>
|
||||
#else
|
||||
# include <string.h>
|
||||
# define PROGMEM
|
||||
# define __flash
|
||||
# define PSTR(x) x
|
||||
# define PGM_P const char*
|
||||
# define memcpy_P(dest, src, n) memcpy(dest, src, n)
|
||||
|
||||
@@ -30,7 +30,8 @@ enum hid_report_ids {
|
||||
REPORT_ID_SYSTEM,
|
||||
REPORT_ID_CONSUMER,
|
||||
REPORT_ID_NKRO,
|
||||
REPORT_ID_JOYSTICK
|
||||
REPORT_ID_JOYSTICK,
|
||||
REPORT_ID_DIGITIZER
|
||||
};
|
||||
|
||||
/* Mouse buttons */
|
||||
@@ -202,6 +203,17 @@ typedef struct {
|
||||
int8_t h;
|
||||
} __attribute__((packed)) report_mouse_t;
|
||||
|
||||
typedef struct {
|
||||
#ifdef DIGITIZER_SHARED_EP
|
||||
uint8_t report_id;
|
||||
#endif
|
||||
uint8_t tip : 1;
|
||||
uint8_t inrange : 1;
|
||||
uint8_t pad2 : 6;
|
||||
uint16_t x;
|
||||
uint16_t y;
|
||||
} __attribute__((packed)) report_digitizer_t;
|
||||
|
||||
typedef struct {
|
||||
#if JOYSTICK_AXES_COUNT > 0
|
||||
# if JOYSTICK_AXES_RESOLUTION > 8
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
Copyright 2011 Jun Wako <wakojun@gmail.com>
|
||||
|
||||
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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef int8_t (*sendchar_func_t)(uint8_t c);
|
||||
|
||||
/* transmit a character. return 0 on success, -1 on error. */
|
||||
int8_t sendchar(uint8_t c);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,19 +0,0 @@
|
||||
/*
|
||||
Copyright 2011 Jun Wako <wakojun@gmail.com>
|
||||
|
||||
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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "sendchar.h"
|
||||
|
||||
__attribute__((weak)) int8_t sendchar(uint8_t c) { return 0; }
|
||||
@@ -1,23 +0,0 @@
|
||||
/*
|
||||
Copyright 2011 Jun Wako <wakojun@gmail.com>
|
||||
|
||||
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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "uart.h"
|
||||
#include "sendchar.h"
|
||||
|
||||
int8_t sendchar(uint8_t c) {
|
||||
uart_putchar(c);
|
||||
return 0;
|
||||
}
|
||||
@@ -26,7 +26,7 @@ SOFTWARE.
|
||||
#include "sync_timer.h"
|
||||
#include "keyboard.h"
|
||||
|
||||
#if defined(SPLIT_KEYBOARD) && !defined(DISABLE_SYNC_TIMER)
|
||||
#if (defined(SPLIT_KEYBOARD) || defined(SERIAL_LINK_ENABLE)) && !defined(DISABLE_SYNC_TIMER)
|
||||
volatile int32_t sync_timer_ms;
|
||||
|
||||
void sync_timer_init(void) { sync_timer_ms = 0; }
|
||||
|
||||
@@ -32,7 +32,7 @@ SOFTWARE.
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if defined(SPLIT_KEYBOARD) && !defined(DISABLE_SYNC_TIMER)
|
||||
#if (defined(SPLIT_KEYBOARD) || defined(SERIAL_LINK_ENABLE)) && !defined(DISABLE_SYNC_TIMER)
|
||||
void sync_timer_init(void);
|
||||
void sync_timer_update(uint32_t time);
|
||||
uint16_t sync_timer_read(void);
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
/* Copyright 2021 QMK
|
||||
*
|
||||
* 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "platform_deps.h"
|
||||
|
||||
void platform_setup(void) {
|
||||
// do nothing
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
/* Copyright 2021 QMK
|
||||
*
|
||||
* 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
// here just to please the build
|
||||
@@ -0,0 +1,18 @@
|
||||
/* Copyright 2021 QMK
|
||||
*
|
||||
* 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
// here just to please the build
|
||||
+20
-5
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
Copyright 2011 Jun Wako <wakojun@gmail.com>
|
||||
Copyright 2021 Simon Arlott
|
||||
|
||||
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
|
||||
@@ -17,13 +18,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#if defined(__AVR__)
|
||||
# include "avr/timer_avr.h"
|
||||
#if __has_include_next("_timer.h")
|
||||
# include_next "_timer.h" /* Include the platform's _timer.h */
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define TIMER_DIFF(a, b, max) ((max == UINT8_MAX) ? ((uint8_t)((a) - (b))) : ((max == UINT16_MAX) ? ((uint16_t)((a) - (b))) : ((max == UINT32_MAX) ? ((uint32_t)((a) - (b))) : ((a) >= (b) ? (a) - (b) : (max) + 1 - (b) + (a)))))
|
||||
#define TIMER_DIFF_8(a, b) TIMER_DIFF(a, b, UINT8_MAX)
|
||||
#define TIMER_DIFF_16(a, b) TIMER_DIFF(a, b, UINT16_MAX)
|
||||
@@ -47,6 +47,21 @@ uint32_t timer_elapsed32(uint32_t last);
|
||||
#define timer_expired(current, future) ((uint16_t)(current - future) < UINT16_MAX / 2)
|
||||
#define timer_expired32(current, future) ((uint32_t)(current - future) < UINT32_MAX / 2)
|
||||
|
||||
// Use an appropriate timer integer size based on architecture (16-bit will overflow sooner)
|
||||
#if FAST_TIMER_T_SIZE < 32
|
||||
# define TIMER_DIFF_FAST(a, b) TIMER_DIFF_16(a, b)
|
||||
# define timer_expired_fast(current, future) timer_expired(current, future)
|
||||
typedef uint16_t fast_timer_t;
|
||||
fast_timer_t inline timer_read_fast(void) { return timer_read(); }
|
||||
fast_timer_t inline timer_elapsed_fast(fast_timer_t last) { return timer_elapsed(last); }
|
||||
#else
|
||||
# define TIMER_DIFF_FAST(a, b) TIMER_DIFF_32(a, b)
|
||||
# define timer_expired_fast(current, future) timer_expired32(current, future)
|
||||
typedef uint32_t fast_timer_t;
|
||||
fast_timer_t inline timer_read_fast(void) { return timer_read32(); }
|
||||
fast_timer_t inline timer_elapsed_fast(fast_timer_t last) { return timer_elapsed32(last); }
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
#include "quantum.h"
|
||||
#include "usb_util.h"
|
||||
|
||||
__attribute__((weak)) void usb_disable(void) {}
|
||||
__attribute__((weak)) void usb_disconnect(void) {}
|
||||
__attribute__((weak)) bool usb_connected_state(void) { return true; }
|
||||
__attribute__((weak)) bool usb_vbus_state(void) {
|
||||
#ifdef USB_VBUS_PIN
|
||||
|
||||
@@ -17,6 +17,6 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
void usb_disable(void);
|
||||
void usb_disconnect(void);
|
||||
bool usb_connected_state(void);
|
||||
bool usb_vbus_state(void);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
SYSTEM_TYPE := $(shell gcc -dumpmachine)
|
||||
GCC_VERSION := $(shell gcc --version 2>/dev/null)
|
||||
|
||||
CC = gcc
|
||||
OBJCOPY =
|
||||
@@ -12,7 +13,9 @@ BIN =
|
||||
|
||||
|
||||
COMPILEFLAGS += -funsigned-char
|
||||
ifeq ($(findstring clang, ${GCC_VERSION}),)
|
||||
COMPILEFLAGS += -funsigned-bitfields
|
||||
endif
|
||||
COMPILEFLAGS += -ffunction-sections
|
||||
COMPILEFLAGS += -fdata-sections
|
||||
COMPILEFLAGS += -fshort-enums
|
||||
@@ -21,7 +24,9 @@ COMPILEFLAGS += -mno-ms-bitfields
|
||||
endif
|
||||
|
||||
CFLAGS += $(COMPILEFLAGS)
|
||||
ifeq ($(findstring clang, ${GCC_VERSION}),)
|
||||
CFLAGS += -fno-inline-small-functions
|
||||
endif
|
||||
CFLAGS += -fno-strict-aliasing
|
||||
|
||||
CXXFLAGS += $(COMPILEFLAGS)
|
||||
|
||||
@@ -14,13 +14,13 @@ endif
|
||||
|
||||
ifeq ($(strip $(PS2_USE_INT)), yes)
|
||||
SRC += protocol/ps2_interrupt.c
|
||||
SRC += protocol/ps2_io_avr.c
|
||||
SRC += protocol/ps2_io_$(PLATFORM_KEY).c
|
||||
OPT_DEFS += -DPS2_USE_INT
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(PS2_USE_USART)), yes)
|
||||
SRC += protocol/ps2_usart.c
|
||||
SRC += protocol/ps2_io_avr.c
|
||||
SRC += protocol/ps2_io_$(PLATFORM_KEY).c
|
||||
OPT_DEFS += -DPS2_USE_USART
|
||||
endif
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "samd51j18a.h"
|
||||
#include "tmk_core/common/keyboard.h"
|
||||
#include "keyboard.h"
|
||||
|
||||
#include "report.h"
|
||||
#include "host.h"
|
||||
@@ -140,6 +140,57 @@ void send_consumer(uint16_t data) {
|
||||
#endif // EXTRAKEY_ENABLE
|
||||
}
|
||||
|
||||
#ifdef CONSOLE_ENABLE
|
||||
# define CONSOLE_PRINTBUF_SIZE 512
|
||||
static char console_printbuf[CONSOLE_PRINTBUF_SIZE];
|
||||
static uint16_t console_printbuf_len = 0;
|
||||
|
||||
int8_t sendchar(uint8_t c) {
|
||||
if (console_printbuf_len >= CONSOLE_PRINTBUF_SIZE) return -1;
|
||||
|
||||
console_printbuf[console_printbuf_len++] = c;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void main_subtask_console_flush(void) {
|
||||
while (udi_hid_con_b_report_trans_ongoing) {
|
||||
} // Wait for any previous transfers to complete
|
||||
|
||||
uint16_t result = console_printbuf_len;
|
||||
uint32_t irqflags;
|
||||
char * pconbuf = console_printbuf; // Pointer to start send from
|
||||
int send_out = CONSOLE_EPSIZE; // Bytes to send per transfer
|
||||
|
||||
while (result > 0) { // While not error and bytes remain
|
||||
while (udi_hid_con_b_report_trans_ongoing) {
|
||||
} // Wait for any previous transfers to complete
|
||||
|
||||
irqflags = __get_PRIMASK();
|
||||
__disable_irq();
|
||||
__DMB();
|
||||
|
||||
if (result < CONSOLE_EPSIZE) { // If remaining bytes are less than console epsize
|
||||
memset(udi_hid_con_report, 0, CONSOLE_EPSIZE); // Clear the buffer
|
||||
send_out = result; // Send remaining size
|
||||
}
|
||||
|
||||
memcpy(udi_hid_con_report, pconbuf, send_out); // Copy data into the send buffer
|
||||
|
||||
udi_hid_con_b_report_valid = 1; // Set report valid
|
||||
udi_hid_con_send_report(); // Send report
|
||||
|
||||
__DMB();
|
||||
__set_PRIMASK(irqflags);
|
||||
|
||||
result -= send_out; // Decrement result by bytes sent
|
||||
pconbuf += send_out; // Increment buffer point by bytes sent
|
||||
}
|
||||
|
||||
console_printbuf_len = 0;
|
||||
}
|
||||
|
||||
#endif // CONSOLE_ENABLE
|
||||
|
||||
void main_subtask_usb_state(void) {
|
||||
static uint64_t fsmstate_on_delay = 0; // Delay timer to be sure USB is actually operating before bringing up hardware
|
||||
uint8_t fsmstate_now = USB->DEVICE.FSMSTATUS.reg; // Current state from hardware register
|
||||
@@ -214,6 +265,9 @@ void main_subtasks(void) {
|
||||
main_subtask_usb_state();
|
||||
main_subtask_power_check();
|
||||
main_subtask_usb_extra_device();
|
||||
#ifdef CONSOLE_ENABLE
|
||||
main_subtask_console_flush();
|
||||
#endif
|
||||
#ifdef RAW_ENABLE
|
||||
main_subtask_raw();
|
||||
#endif
|
||||
|
||||
@@ -291,10 +291,10 @@ static void flush(void) {
|
||||
i2c_led_q_run();
|
||||
}
|
||||
|
||||
void md_rgb_matrix_indicators(void) {
|
||||
void md_rgb_matrix_indicators_advanced(uint8_t led_min, uint8_t led_max) {
|
||||
uint8_t kbled = keyboard_leds();
|
||||
if (kbled && rgb_matrix_config.enable) {
|
||||
for (uint8_t i = 0; i < ISSI3733_LED_COUNT; i++) {
|
||||
for (uint8_t i = led_min; i < led_max; i++) {
|
||||
if (
|
||||
# if USB_LED_NUM_LOCK_SCANCODE != 255
|
||||
(led_map[i].scan == USB_LED_NUM_LOCK_SCANCODE && (kbled & (1 << USB_LED_NUM_LOCK))) ||
|
||||
|
||||
@@ -86,7 +86,7 @@ extern uint8_t gcr_actual_last;
|
||||
|
||||
void gcr_compute(void);
|
||||
|
||||
void md_rgb_matrix_indicators(void);
|
||||
void md_rgb_matrix_indicators_advanced(uint8_t led_min, uint8_t led_max);
|
||||
|
||||
/*------------------------- Legacy Lighting Support ------------------------*/
|
||||
|
||||
|
||||
@@ -23,6 +23,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#include "compiler.h"
|
||||
#include "usb_protocol_hid.h"
|
||||
|
||||
#ifndef USB_POLLING_INTERVAL_MS
|
||||
# define USB_POLLING_INTERVAL_MS 10
|
||||
#endif
|
||||
|
||||
#ifdef VIRTSER_ENABLE
|
||||
// because CDC uses IAD (interface association descriptor
|
||||
// per USB Interface Association Descriptor Device Class Code and Use Model 7/23/2003 Rev 1.0)
|
||||
@@ -118,7 +122,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#define UDI_HID_KBD_EP_IN KEYBOARD_IN_EPNUM
|
||||
#define NEXT_IN_EPNUM_1 (KEYBOARD_IN_EPNUM + 1)
|
||||
#define UDI_HID_KBD_EP_SIZE KEYBOARD_EPSIZE
|
||||
#define KBD_POLLING_INTERVAL 10
|
||||
#define KBD_POLLING_INTERVAL USB_POLLING_INTERVAL_MS
|
||||
#ifndef UDI_HID_KBD_STRING_ID
|
||||
# define UDI_HID_KBD_STRING_ID 0
|
||||
#endif
|
||||
@@ -128,7 +132,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
# define NEXT_IN_EPNUM_2 (MOUSE_IN_EPNUM + 1)
|
||||
# define UDI_HID_MOU_EP_IN MOUSE_IN_EPNUM
|
||||
# define UDI_HID_MOU_EP_SIZE MOUSE_EPSIZE
|
||||
# define MOU_POLLING_INTERVAL 10
|
||||
# define MOU_POLLING_INTERVAL USB_POLLING_INTERVAL_MS
|
||||
# ifndef UDI_HID_MOU_STRING_ID
|
||||
# define UDI_HID_MOU_STRING_ID 0
|
||||
# endif
|
||||
@@ -141,7 +145,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
# define UDI_HID_EXK_EP_IN EXTRAKEY_IN_EPNUM
|
||||
# define NEXT_IN_EPNUM_3 (EXTRAKEY_IN_EPNUM + 1)
|
||||
# define UDI_HID_EXK_EP_SIZE EXTRAKEY_EPSIZE
|
||||
# define EXTRAKEY_POLLING_INTERVAL 10
|
||||
# define EXTRAKEY_POLLING_INTERVAL USB_POLLING_INTERVAL_MS
|
||||
# ifndef UDI_HID_EXK_STRING_ID
|
||||
# define UDI_HID_EXK_STRING_ID 0
|
||||
# endif
|
||||
|
||||
@@ -3,7 +3,7 @@ CHIBIOS_DIR = $(PROTOCOL_DIR)/chibios
|
||||
|
||||
|
||||
SRC += $(CHIBIOS_DIR)/usb_main.c
|
||||
SRC += $(CHIBIOS_DIR)/main.c
|
||||
SRC += $(CHIBIOS_DIR)/chibios.c
|
||||
SRC += usb_descriptor.c
|
||||
SRC += $(CHIBIOS_DIR)/usb_driver.c
|
||||
SRC += $(CHIBIOS_DIR)/usb_util.c
|
||||
|
||||
@@ -65,6 +65,7 @@ void send_keyboard(report_keyboard_t *report);
|
||||
void send_mouse(report_mouse_t *report);
|
||||
void send_system(uint16_t data);
|
||||
void send_consumer(uint16_t data);
|
||||
void send_digitizer(report_digitizer_t *report);
|
||||
|
||||
/* host struct */
|
||||
host_driver_t chibios_driver = {keyboard_leds, send_keyboard, send_mouse, send_system, send_consumer};
|
||||
@@ -137,18 +138,14 @@ void boardInit(void) {
|
||||
board_init();
|
||||
}
|
||||
|
||||
/* Main thread
|
||||
*/
|
||||
int main(void) {
|
||||
/* ChibiOS/RT init */
|
||||
halInit();
|
||||
chSysInit();
|
||||
|
||||
void protocol_setup(void) {
|
||||
// TESTING
|
||||
// chThdCreateStatic(waThread1, sizeof(waThread1), NORMALPRIO, Thread1, NULL);
|
||||
|
||||
keyboard_setup();
|
||||
}
|
||||
|
||||
void protocol_init(void) {
|
||||
/* Init USB */
|
||||
usb_event_queue_init();
|
||||
init_usb_driver(&USB_DRIVER);
|
||||
@@ -206,57 +203,53 @@ int main(void) {
|
||||
#endif
|
||||
|
||||
print("Keyboard start.\n");
|
||||
}
|
||||
|
||||
/* Main loop */
|
||||
while (true) {
|
||||
usb_event_queue_task();
|
||||
void protocol_task(void) {
|
||||
usb_event_queue_task();
|
||||
|
||||
#if !defined(NO_USB_STARTUP_CHECK)
|
||||
if (USB_DRIVER.state == USB_SUSPENDED) {
|
||||
print("[s]");
|
||||
if (USB_DRIVER.state == USB_SUSPENDED) {
|
||||
print("[s]");
|
||||
# ifdef VISUALIZER_ENABLE
|
||||
visualizer_suspend();
|
||||
visualizer_suspend();
|
||||
# endif
|
||||
while (USB_DRIVER.state == USB_SUSPENDED) {
|
||||
/* Do this in the suspended state */
|
||||
while (USB_DRIVER.state == USB_SUSPENDED) {
|
||||
/* Do this in the suspended state */
|
||||
# ifdef SERIAL_LINK_ENABLE
|
||||
serial_link_update();
|
||||
serial_link_update();
|
||||
# endif
|
||||
suspend_power_down(); // on AVR this deep sleeps for 15ms
|
||||
/* Remote wakeup */
|
||||
if (suspend_wakeup_condition()) {
|
||||
usbWakeupHost(&USB_DRIVER);
|
||||
restart_usb_driver(&USB_DRIVER);
|
||||
}
|
||||
suspend_power_down(); // on AVR this deep sleeps for 15ms
|
||||
/* Remote wakeup */
|
||||
if (suspend_wakeup_condition()) {
|
||||
usbWakeupHost(&USB_DRIVER);
|
||||
restart_usb_driver(&USB_DRIVER);
|
||||
}
|
||||
/* Woken up */
|
||||
// variables has been already cleared by the wakeup hook
|
||||
send_keyboard_report();
|
||||
}
|
||||
/* Woken up */
|
||||
// variables has been already cleared by the wakeup hook
|
||||
send_keyboard_report();
|
||||
# ifdef MOUSEKEY_ENABLE
|
||||
mousekey_send();
|
||||
mousekey_send();
|
||||
# endif /* MOUSEKEY_ENABLE */
|
||||
|
||||
# ifdef VISUALIZER_ENABLE
|
||||
visualizer_resume();
|
||||
visualizer_resume();
|
||||
# endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
keyboard_task();
|
||||
keyboard_task();
|
||||
#ifdef CONSOLE_ENABLE
|
||||
console_task();
|
||||
console_task();
|
||||
#endif
|
||||
#ifdef MIDI_ENABLE
|
||||
midi_ep_task();
|
||||
midi_ep_task();
|
||||
#endif
|
||||
#ifdef VIRTSER_ENABLE
|
||||
virtser_task();
|
||||
virtser_task();
|
||||
#endif
|
||||
#ifdef RAW_ENABLE
|
||||
raw_hid_task();
|
||||
raw_hid_task();
|
||||
#endif
|
||||
|
||||
// Run housekeeping
|
||||
housekeeping_task();
|
||||
}
|
||||
}
|
||||
@@ -315,6 +315,9 @@ typedef struct {
|
||||
#endif
|
||||
#ifdef JOYSTICK_ENABLE
|
||||
usb_driver_config_t joystick_driver;
|
||||
#endif
|
||||
#if defined(DIGITIZER_ENABLE) && !defined(DIGITIZER_SHARED_EP)
|
||||
usb_driver_config_t digitizer_driver;
|
||||
#endif
|
||||
};
|
||||
usb_driver_config_t array[0];
|
||||
@@ -360,6 +363,14 @@ static usb_driver_configs_t drivers = {
|
||||
# define JOYSTICK_OUT_MODE USB_EP_MODE_TYPE_BULK
|
||||
.joystick_driver = QMK_USB_DRIVER_CONFIG(JOYSTICK, 0, false),
|
||||
#endif
|
||||
|
||||
#if defined(DIGITIZER_ENABLE) && !defined(DIGITIZER_SHARED_EP)
|
||||
# define DIGITIZER_IN_CAPACITY 4
|
||||
# define DIGITIZER_OUT_CAPACITY 4
|
||||
# define DIGITIZER_IN_MODE USB_EP_MODE_TYPE_BULK
|
||||
# define DIGITIZER_OUT_MODE USB_EP_MODE_TYPE_BULK
|
||||
.digitizer_driver = QMK_USB_DRIVER_CONFIG(DIGITIZER, 0, false),
|
||||
#endif
|
||||
};
|
||||
|
||||
#define NUM_USB_DRIVERS (sizeof(drivers) / sizeof(usb_driver_config_t))
|
||||
@@ -415,14 +426,18 @@ static inline void usb_event_wakeup_handler(void) {
|
||||
#endif /* SLEEP_LED_ENABLE */
|
||||
}
|
||||
|
||||
bool last_suspend_state = false;
|
||||
|
||||
void usb_event_queue_task(void) {
|
||||
usbevent_t event;
|
||||
while (usb_event_queue_dequeue(&event)) {
|
||||
switch (event) {
|
||||
case USB_EVENT_SUSPEND:
|
||||
last_suspend_state = true;
|
||||
usb_event_suspend_handler();
|
||||
break;
|
||||
case USB_EVENT_WAKEUP:
|
||||
last_suspend_state = false;
|
||||
usb_event_wakeup_handler();
|
||||
break;
|
||||
default:
|
||||
@@ -464,6 +479,9 @@ static void usb_event_cb(USBDriver *usbp, usbevent_t event) {
|
||||
qmkusbConfigureHookI(&drivers.array[i].driver);
|
||||
}
|
||||
osalSysUnlockFromISR();
|
||||
if (last_suspend_state) {
|
||||
usb_event_queue_enqueue(USB_EVENT_WAKEUP);
|
||||
}
|
||||
return;
|
||||
case USB_EVENT_SUSPEND:
|
||||
usb_event_queue_enqueue(USB_EVENT_SUSPEND);
|
||||
@@ -518,7 +536,7 @@ static uint16_t get_hword(uint8_t *p) {
|
||||
* Other Device Required Optional Optional Optional Optional Optional
|
||||
*/
|
||||
|
||||
static uint8_t set_report_buf[2] __attribute__((aligned(2)));
|
||||
static uint8_t set_report_buf[2] __attribute__((aligned(4)));
|
||||
static void set_led_transfer_cb(USBDriver *usbp) {
|
||||
if (usbp->setup[6] == 2) { /* LSB(wLength) */
|
||||
uint8_t report_id = set_report_buf[0];
|
||||
@@ -705,7 +723,7 @@ void init_usb_driver(USBDriver *usbp) {
|
||||
chVTObjectInit(&keyboard_idle_timer);
|
||||
}
|
||||
|
||||
void restart_usb_driver(USBDriver *usbp) {
|
||||
__attribute__((weak)) void restart_usb_driver(USBDriver *usbp) {
|
||||
usbStop(usbp);
|
||||
usbDisconnectBus(usbp);
|
||||
|
||||
@@ -903,7 +921,8 @@ static void send_extra(uint8_t report_id, uint16_t data) {
|
||||
return;
|
||||
}
|
||||
|
||||
report_extra_t report = {.report_id = report_id, .usage = data};
|
||||
static report_extra_t report;
|
||||
report = (report_extra_t){.report_id = report_id, .usage = data};
|
||||
|
||||
usbStartTransmitI(&USB_DRIVER, SHARED_IN_EPNUM, (uint8_t *)&report, sizeof(report_extra_t));
|
||||
osalSysUnlock();
|
||||
@@ -922,6 +941,23 @@ void send_consumer(uint16_t data) {
|
||||
#endif
|
||||
}
|
||||
|
||||
void send_digitizer(report_digitizer_t *report) {
|
||||
#ifdef DIGITIZER_ENABLE
|
||||
# ifdef DIGITIZER_SHARED_EP
|
||||
osalSysLock();
|
||||
if (usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) {
|
||||
osalSysUnlock();
|
||||
return;
|
||||
}
|
||||
|
||||
usbStartTransmitI(&USB_DRIVER, DIGITIZER_IN_EPNUM, (uint8_t *)report, sizeof(report_digitizer_t));
|
||||
osalSysUnlock();
|
||||
# else
|
||||
chnWrite(&drivers.digitizer_driver.driver, (uint8_t *)report, sizeof(report_digitizer_t));
|
||||
# endif
|
||||
#endif
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------
|
||||
* Console functions
|
||||
* ---------------------------------------------------------
|
||||
@@ -1051,45 +1087,44 @@ void virtser_task(void) {
|
||||
#ifdef JOYSTICK_ENABLE
|
||||
|
||||
void send_joystick_packet(joystick_t *joystick) {
|
||||
joystick_report_t rep = {
|
||||
static joystick_report_t rep;
|
||||
rep = (joystick_report_t) {
|
||||
# if JOYSTICK_AXES_COUNT > 0
|
||||
.axes =
|
||||
{
|
||||
joystick->axes[0],
|
||||
{ joystick->axes[0],
|
||||
|
||||
# if JOYSTICK_AXES_COUNT >= 2
|
||||
joystick->axes[1],
|
||||
joystick->axes[1],
|
||||
# endif
|
||||
# if JOYSTICK_AXES_COUNT >= 3
|
||||
joystick->axes[2],
|
||||
joystick->axes[2],
|
||||
# endif
|
||||
# if JOYSTICK_AXES_COUNT >= 4
|
||||
joystick->axes[3],
|
||||
joystick->axes[3],
|
||||
# endif
|
||||
# if JOYSTICK_AXES_COUNT >= 5
|
||||
joystick->axes[4],
|
||||
joystick->axes[4],
|
||||
# endif
|
||||
# if JOYSTICK_AXES_COUNT >= 6
|
||||
joystick->axes[5],
|
||||
joystick->axes[5],
|
||||
# endif
|
||||
},
|
||||
},
|
||||
# endif // JOYSTICK_AXES_COUNT>0
|
||||
|
||||
# if JOYSTICK_BUTTON_COUNT > 0
|
||||
.buttons =
|
||||
{
|
||||
joystick->buttons[0],
|
||||
.buttons = {
|
||||
joystick->buttons[0],
|
||||
|
||||
# if JOYSTICK_BUTTON_COUNT > 8
|
||||
joystick->buttons[1],
|
||||
joystick->buttons[1],
|
||||
# endif
|
||||
# if JOYSTICK_BUTTON_COUNT > 16
|
||||
joystick->buttons[2],
|
||||
joystick->buttons[2],
|
||||
# endif
|
||||
# if JOYSTICK_BUTTON_COUNT > 24
|
||||
joystick->buttons[3],
|
||||
joystick->buttons[3],
|
||||
# endif
|
||||
}
|
||||
}
|
||||
# endif // JOYSTICK_BUTTON_COUNT>0
|
||||
};
|
||||
|
||||
|
||||
@@ -16,6 +16,6 @@
|
||||
#include <hal.h>
|
||||
#include "usb_util.h"
|
||||
|
||||
void usb_disable(void) { usbStop(&USBD1); }
|
||||
void usb_disconnect(void) { usbStop(&USBD1); }
|
||||
|
||||
bool usb_connected_state(void) { return usbGetDriverStateI(&USBD1) == USB_ACTIVE; }
|
||||
|
||||
@@ -49,7 +49,6 @@ SRC += $(LUFA_DIR)/usb_util.c
|
||||
# Search Path
|
||||
VPATH += $(TMK_PATH)/$(LUFA_DIR)
|
||||
VPATH += $(LUFA_PATH)
|
||||
VPATH += $(DRIVER_PATH)/avr
|
||||
|
||||
# Option modules
|
||||
#ifdef $(or MOUSEKEY_ENABLE, PS2_MOUSE_ENABLE)
|
||||
|
||||
@@ -142,9 +142,7 @@ static void send_keyboard(report_keyboard_t *report);
|
||||
static void send_mouse(report_mouse_t *report);
|
||||
static void send_system(uint16_t data);
|
||||
static void send_consumer(uint16_t data);
|
||||
host_driver_t lufa_driver = {
|
||||
keyboard_leds, send_keyboard, send_mouse, send_system, send_consumer,
|
||||
};
|
||||
host_driver_t lufa_driver = {keyboard_leds, send_keyboard, send_mouse, send_system, send_consumer};
|
||||
|
||||
#ifdef VIRTSER_ENABLE
|
||||
// clang-format off
|
||||
@@ -314,45 +312,44 @@ static void Console_Task(void) {
|
||||
void send_joystick_packet(joystick_t *joystick) {
|
||||
uint8_t timeout = 255;
|
||||
|
||||
joystick_report_t r = {
|
||||
static joystick_report_t r;
|
||||
r = (joystick_report_t) {
|
||||
# if JOYSTICK_AXES_COUNT > 0
|
||||
.axes =
|
||||
{
|
||||
joystick->axes[0],
|
||||
{ joystick->axes[0],
|
||||
|
||||
# if JOYSTICK_AXES_COUNT >= 2
|
||||
joystick->axes[1],
|
||||
joystick->axes[1],
|
||||
# endif
|
||||
# if JOYSTICK_AXES_COUNT >= 3
|
||||
joystick->axes[2],
|
||||
joystick->axes[2],
|
||||
# endif
|
||||
# if JOYSTICK_AXES_COUNT >= 4
|
||||
joystick->axes[3],
|
||||
joystick->axes[3],
|
||||
# endif
|
||||
# if JOYSTICK_AXES_COUNT >= 5
|
||||
joystick->axes[4],
|
||||
joystick->axes[4],
|
||||
# endif
|
||||
# if JOYSTICK_AXES_COUNT >= 6
|
||||
joystick->axes[5],
|
||||
joystick->axes[5],
|
||||
# endif
|
||||
},
|
||||
},
|
||||
# endif // JOYSTICK_AXES_COUNT>0
|
||||
|
||||
# if JOYSTICK_BUTTON_COUNT > 0
|
||||
.buttons =
|
||||
{
|
||||
joystick->buttons[0],
|
||||
.buttons = {
|
||||
joystick->buttons[0],
|
||||
|
||||
# if JOYSTICK_BUTTON_COUNT > 8
|
||||
joystick->buttons[1],
|
||||
joystick->buttons[1],
|
||||
# endif
|
||||
# if JOYSTICK_BUTTON_COUNT > 16
|
||||
joystick->buttons[2],
|
||||
joystick->buttons[2],
|
||||
# endif
|
||||
# if JOYSTICK_BUTTON_COUNT > 24
|
||||
joystick->buttons[3],
|
||||
joystick->buttons[3],
|
||||
# endif
|
||||
}
|
||||
}
|
||||
# endif // JOYSTICK_BUTTON_COUNT>0
|
||||
};
|
||||
|
||||
@@ -526,6 +523,11 @@ void EVENT_USB_Device_ConfigurationChanged(void) {
|
||||
/* Setup joystick endpoint */
|
||||
ConfigSuccess &= Endpoint_ConfigureEndpoint((JOYSTICK_IN_EPNUM | ENDPOINT_DIR_IN), EP_TYPE_INTERRUPT, JOYSTICK_EPSIZE, 1);
|
||||
#endif
|
||||
|
||||
#if defined(DIGITIZER_ENABLE) && !defined(DIGITIZER_SHARED_EP)
|
||||
/* Setup digitizer endpoint */
|
||||
ConfigSuccess &= Endpoint_ConfigureEndpoint((DIGITIZER_IN_EPNUM | ENDPOINT_DIR_IN), EP_TYPE_INTERRUPT, DIGITIZER_EPSIZE, 1);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* FIXME: Expose this table in the docs somehow
|
||||
@@ -768,7 +770,8 @@ static void send_extra(uint8_t report_id, uint16_t data) {
|
||||
|
||||
if (USB_DeviceState != DEVICE_STATE_Configured) return;
|
||||
|
||||
report_extra_t r = {.report_id = report_id, .usage = data};
|
||||
static report_extra_t r;
|
||||
r = (report_extra_t){.report_id = report_id, .usage = data};
|
||||
Endpoint_SelectEndpoint(SHARED_IN_EPNUM);
|
||||
|
||||
/* Check if write ready for a polling interval around 10ms */
|
||||
@@ -983,6 +986,23 @@ void virtser_send(const uint8_t byte) {
|
||||
}
|
||||
#endif
|
||||
|
||||
void send_digitizer(report_digitizer_t *report) {
|
||||
#ifdef DIGITIZER_ENABLE
|
||||
uint8_t timeout = 255;
|
||||
|
||||
if (USB_DeviceState != DEVICE_STATE_Configured) return;
|
||||
|
||||
Endpoint_SelectEndpoint(DIGITIZER_IN_EPNUM);
|
||||
|
||||
/* Check if write ready for a polling interval around 10ms */
|
||||
while (timeout-- && !Endpoint_IsReadWriteAllowed()) _delay_us(40);
|
||||
if (!Endpoint_IsReadWriteAllowed()) return;
|
||||
|
||||
Endpoint_Write_Stream_LE(report, sizeof(report_digitizer_t), NULL);
|
||||
Endpoint_ClearIN();
|
||||
#endif
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* main
|
||||
******************************************************************************/
|
||||
@@ -995,8 +1015,13 @@ static void setup_mcu(void) {
|
||||
MCUSR &= ~_BV(WDRF);
|
||||
wdt_disable();
|
||||
|
||||
/* Disable clock division */
|
||||
// For boards running at 3.3V and crystal at 16 MHz
|
||||
#if (F_CPU == 8000000 && F_USB == 16000000)
|
||||
/* Divide clock by 2 */
|
||||
clock_prescale_set(clock_div_2);
|
||||
#else /* Disable clock division */
|
||||
clock_prescale_set(clock_div_1);
|
||||
#endif
|
||||
}
|
||||
|
||||
/** \brief Setup USB
|
||||
@@ -1013,18 +1038,16 @@ static void setup_usb(void) {
|
||||
USB_Device_EnableSOFEvents();
|
||||
}
|
||||
|
||||
/** \brief Main
|
||||
*
|
||||
* FIXME: Needs doc
|
||||
*/
|
||||
int main(void) __attribute__((weak));
|
||||
int main(void) {
|
||||
void protocol_setup(void) {
|
||||
#ifdef MIDI_ENABLE
|
||||
setup_midi();
|
||||
#endif
|
||||
|
||||
setup_mcu();
|
||||
keyboard_setup();
|
||||
}
|
||||
|
||||
void protocol_init(void) {
|
||||
setup_usb();
|
||||
sei();
|
||||
|
||||
@@ -1058,57 +1081,55 @@ int main(void) {
|
||||
#endif
|
||||
|
||||
print("Keyboard start.\n");
|
||||
while (1) {
|
||||
}
|
||||
|
||||
void protocol_task(void) {
|
||||
#if !defined(NO_USB_STARTUP_CHECK)
|
||||
if (USB_DeviceState == DEVICE_STATE_Suspended) {
|
||||
print("[s]");
|
||||
while (USB_DeviceState == DEVICE_STATE_Suspended) {
|
||||
suspend_power_down();
|
||||
if (USB_Device_RemoteWakeupEnabled && suspend_wakeup_condition()) {
|
||||
USB_Device_SendRemoteWakeup();
|
||||
clear_keyboard();
|
||||
if (USB_DeviceState == DEVICE_STATE_Suspended) {
|
||||
print("[s]");
|
||||
while (USB_DeviceState == DEVICE_STATE_Suspended) {
|
||||
suspend_power_down();
|
||||
if (USB_Device_RemoteWakeupEnabled && suspend_wakeup_condition()) {
|
||||
USB_Device_SendRemoteWakeup();
|
||||
clear_keyboard();
|
||||
|
||||
# if USB_SUSPEND_WAKEUP_DELAY > 0
|
||||
// Some hubs, kvm switches, and monitors do
|
||||
// weird things, with USB device state bouncing
|
||||
// around wildly on wakeup, yielding race
|
||||
// conditions that can corrupt the keyboard state.
|
||||
//
|
||||
// Pause for a while to let things settle...
|
||||
wait_ms(USB_SUSPEND_WAKEUP_DELAY);
|
||||
// Some hubs, kvm switches, and monitors do
|
||||
// weird things, with USB device state bouncing
|
||||
// around wildly on wakeup, yielding race
|
||||
// conditions that can corrupt the keyboard state.
|
||||
//
|
||||
// Pause for a while to let things settle...
|
||||
wait_ms(USB_SUSPEND_WAKEUP_DELAY);
|
||||
# endif
|
||||
}
|
||||
}
|
||||
suspend_wakeup_init();
|
||||
}
|
||||
suspend_wakeup_init();
|
||||
}
|
||||
#endif
|
||||
|
||||
keyboard_task();
|
||||
keyboard_task();
|
||||
|
||||
#ifdef MIDI_ENABLE
|
||||
MIDI_Device_USBTask(&USB_MIDI_Interface);
|
||||
MIDI_Device_USBTask(&USB_MIDI_Interface);
|
||||
#endif
|
||||
|
||||
#ifdef MODULE_ADAFRUIT_BLE
|
||||
adafruit_ble_task();
|
||||
adafruit_ble_task();
|
||||
#endif
|
||||
|
||||
#ifdef VIRTSER_ENABLE
|
||||
virtser_task();
|
||||
CDC_Device_USBTask(&cdc_device);
|
||||
virtser_task();
|
||||
CDC_Device_USBTask(&cdc_device);
|
||||
#endif
|
||||
|
||||
#ifdef RAW_ENABLE
|
||||
raw_hid_task();
|
||||
raw_hid_task();
|
||||
#endif
|
||||
|
||||
#if !defined(INTERRUPT_CONTROL_ENDPOINT)
|
||||
USB_USBTask();
|
||||
USB_USBTask();
|
||||
#endif
|
||||
|
||||
// Run housekeeping
|
||||
housekeeping_task();
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t CALLBACK_USB_GetDescriptor(const uint16_t wValue, const uint16_t wIndex, const void **const DescriptorAddress) { return get_usb_descriptor(wValue, wIndex, DescriptorAddress); }
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
#include "usb_util.h"
|
||||
#include "wait.h"
|
||||
|
||||
void usb_disable(void) {
|
||||
void usb_disconnect(void) {
|
||||
USB_Disable();
|
||||
USB_DeviceState = DEVICE_STATE_Unattached;
|
||||
}
|
||||
|
||||
@@ -40,11 +40,19 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <avr/interrupt.h>
|
||||
#include <util/delay.h>
|
||||
|
||||
#if defined(__AVR__)
|
||||
# include <avr/interrupt.h>
|
||||
#elif defined(PROTOCOL_CHIBIOS) // TODO: or STM32 ?
|
||||
// chibiOS headers
|
||||
# include "ch.h"
|
||||
# include "hal.h"
|
||||
#endif
|
||||
|
||||
#include "ps2.h"
|
||||
#include "ps2_io.h"
|
||||
#include "print.h"
|
||||
#include "wait.h"
|
||||
|
||||
#define WAIT(stat, us, err) \
|
||||
do { \
|
||||
@@ -61,12 +69,30 @@ static inline void pbuf_enqueue(uint8_t data);
|
||||
static inline bool pbuf_has_data(void);
|
||||
static inline void pbuf_clear(void);
|
||||
|
||||
#if defined(PROTOCOL_CHIBIOS)
|
||||
void ps2_interrupt_service_routine(void);
|
||||
void palCallback(void *arg) { ps2_interrupt_service_routine(); }
|
||||
|
||||
# define PS2_INT_INIT() \
|
||||
{ palSetLineMode(PS2_CLOCK, PAL_MODE_INPUT); } \
|
||||
while (0)
|
||||
# define PS2_INT_ON() \
|
||||
{ \
|
||||
palEnableLineEvent(PS2_CLOCK, PAL_EVENT_MODE_FALLING_EDGE); \
|
||||
palSetLineCallback(PS2_CLOCK, palCallback, NULL); \
|
||||
} \
|
||||
while (0)
|
||||
# define PS2_INT_OFF() \
|
||||
{ palDisableLineEvent(PS2_CLOCK); } \
|
||||
while (0)
|
||||
#endif // PROTOCOL_CHIBIOS
|
||||
|
||||
void ps2_host_init(void) {
|
||||
idle();
|
||||
PS2_INT_INIT();
|
||||
PS2_INT_ON();
|
||||
// POR(150-2000ms) plus BAT(300-500ms) may take 2.5sec([3]p.20)
|
||||
//_delay_ms(2500);
|
||||
// wait_ms(2500);
|
||||
}
|
||||
|
||||
uint8_t ps2_host_send(uint8_t data) {
|
||||
@@ -77,7 +103,7 @@ uint8_t ps2_host_send(uint8_t data) {
|
||||
|
||||
/* terminate a transmission if we have */
|
||||
inhibit();
|
||||
_delay_us(100); // 100us [4]p.13, [5]p.50
|
||||
wait_us(100); // 100us [4]p.13, [5]p.50
|
||||
|
||||
/* 'Request to Send' and Start bit */
|
||||
data_lo();
|
||||
@@ -86,7 +112,6 @@ uint8_t ps2_host_send(uint8_t data) {
|
||||
|
||||
/* Data bit[2-9] */
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
_delay_us(15);
|
||||
if (data & (1 << i)) {
|
||||
parity = !parity;
|
||||
data_hi();
|
||||
@@ -98,7 +123,7 @@ uint8_t ps2_host_send(uint8_t data) {
|
||||
}
|
||||
|
||||
/* Parity bit */
|
||||
_delay_us(15);
|
||||
wait_us(15);
|
||||
if (parity) {
|
||||
data_hi();
|
||||
} else {
|
||||
@@ -108,7 +133,7 @@ uint8_t ps2_host_send(uint8_t data) {
|
||||
WAIT(clock_lo, 50, 5);
|
||||
|
||||
/* Stop bit */
|
||||
_delay_us(15);
|
||||
wait_us(15);
|
||||
data_hi();
|
||||
|
||||
/* Ack */
|
||||
@@ -132,7 +157,7 @@ uint8_t ps2_host_recv_response(void) {
|
||||
// Command may take 25ms/20ms at most([5]p.46, [3]p.21)
|
||||
uint8_t retry = 25;
|
||||
while (retry-- && !pbuf_has_data()) {
|
||||
_delay_ms(1);
|
||||
wait_ms(1);
|
||||
}
|
||||
return pbuf_dequeue();
|
||||
}
|
||||
@@ -148,7 +173,7 @@ uint8_t ps2_host_recv(void) {
|
||||
}
|
||||
}
|
||||
|
||||
ISR(PS2_INT_VECT) {
|
||||
void ps2_interrupt_service_routine(void) {
|
||||
static enum {
|
||||
INIT,
|
||||
START,
|
||||
@@ -218,6 +243,10 @@ RETURN:
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(__AVR__)
|
||||
ISR(PS2_INT_VECT) { ps2_interrupt_service_routine(); }
|
||||
#endif
|
||||
|
||||
/* send LED state to keyboard */
|
||||
void ps2_host_set_led(uint8_t led) {
|
||||
ps2_host_send(0xED);
|
||||
@@ -232,8 +261,13 @@ static uint8_t pbuf[PBUF_SIZE];
|
||||
static uint8_t pbuf_head = 0;
|
||||
static uint8_t pbuf_tail = 0;
|
||||
static inline void pbuf_enqueue(uint8_t data) {
|
||||
#if defined(__AVR__)
|
||||
uint8_t sreg = SREG;
|
||||
cli();
|
||||
#elif defined(PROTOCOL_CHIBIOS)
|
||||
chSysLockFromISR();
|
||||
#endif
|
||||
|
||||
uint8_t next = (pbuf_head + 1) % PBUF_SIZE;
|
||||
if (next != pbuf_tail) {
|
||||
pbuf[pbuf_head] = data;
|
||||
@@ -241,31 +275,66 @@ static inline void pbuf_enqueue(uint8_t data) {
|
||||
} else {
|
||||
print("pbuf: full\n");
|
||||
}
|
||||
|
||||
#if defined(__AVR__)
|
||||
SREG = sreg;
|
||||
#elif defined(PROTOCOL_CHIBIOS)
|
||||
chSysUnlockFromISR();
|
||||
#endif
|
||||
}
|
||||
static inline uint8_t pbuf_dequeue(void) {
|
||||
uint8_t val = 0;
|
||||
|
||||
#if defined(__AVR__)
|
||||
uint8_t sreg = SREG;
|
||||
cli();
|
||||
#elif defined(PROTOCOL_CHIBIOS)
|
||||
chSysLock();
|
||||
#endif
|
||||
|
||||
if (pbuf_head != pbuf_tail) {
|
||||
val = pbuf[pbuf_tail];
|
||||
pbuf_tail = (pbuf_tail + 1) % PBUF_SIZE;
|
||||
}
|
||||
|
||||
#if defined(__AVR__)
|
||||
SREG = sreg;
|
||||
#elif defined(PROTOCOL_CHIBIOS)
|
||||
chSysUnlock();
|
||||
#endif
|
||||
|
||||
return val;
|
||||
}
|
||||
static inline bool pbuf_has_data(void) {
|
||||
#if defined(__AVR__)
|
||||
uint8_t sreg = SREG;
|
||||
cli();
|
||||
#elif defined(PROTOCOL_CHIBIOS)
|
||||
chSysLock();
|
||||
#endif
|
||||
|
||||
bool has_data = (pbuf_head != pbuf_tail);
|
||||
SREG = sreg;
|
||||
|
||||
#if defined(__AVR__)
|
||||
SREG = sreg;
|
||||
#elif defined(PROTOCOL_CHIBIOS)
|
||||
chSysUnlock();
|
||||
#endif
|
||||
return has_data;
|
||||
}
|
||||
static inline void pbuf_clear(void) {
|
||||
#if defined(__AVR__)
|
||||
uint8_t sreg = SREG;
|
||||
cli();
|
||||
#elif defined(PROTOCOL_CHIBIOS)
|
||||
chSysLock();
|
||||
#endif
|
||||
|
||||
pbuf_head = pbuf_tail = 0;
|
||||
SREG = sreg;
|
||||
|
||||
#if defined(__AVR__)
|
||||
SREG = sreg;
|
||||
#elif defined(PROTOCOL_CHIBIOS)
|
||||
chSysUnlock();
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
#include <stdbool.h>
|
||||
#include "ps2_io.h"
|
||||
|
||||
// chibiOS headers
|
||||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
|
||||
/* Check port settings for clock and data line */
|
||||
#if !(defined(PS2_CLOCK))
|
||||
# error "PS/2 clock setting is required in config.h"
|
||||
#endif
|
||||
|
||||
#if !(defined(PS2_DATA))
|
||||
# error "PS/2 data setting is required in config.h"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Clock
|
||||
*/
|
||||
void clock_init(void) {}
|
||||
|
||||
void clock_lo(void) {
|
||||
palSetLineMode(PS2_CLOCK, PAL_MODE_OUTPUT_OPENDRAIN);
|
||||
palWriteLine(PS2_CLOCK, PAL_LOW);
|
||||
}
|
||||
|
||||
void clock_hi(void) {
|
||||
palSetLineMode(PS2_CLOCK, PAL_MODE_OUTPUT_OPENDRAIN);
|
||||
palWriteLine(PS2_CLOCK, PAL_HIGH);
|
||||
}
|
||||
|
||||
bool clock_in(void) {
|
||||
palSetLineMode(PS2_CLOCK, PAL_MODE_INPUT);
|
||||
return palReadLine(PS2_CLOCK);
|
||||
}
|
||||
|
||||
/*
|
||||
* Data
|
||||
*/
|
||||
void data_init(void) {}
|
||||
|
||||
void data_lo(void) {
|
||||
palSetLineMode(PS2_DATA, PAL_MODE_OUTPUT_OPENDRAIN);
|
||||
palWriteLine(PS2_DATA, PAL_LOW);
|
||||
}
|
||||
|
||||
void data_hi(void) {
|
||||
palSetLineMode(PS2_DATA, PAL_MODE_OUTPUT_OPENDRAIN);
|
||||
palWriteLine(PS2_DATA, PAL_HIGH);
|
||||
}
|
||||
|
||||
bool data_in(void) {
|
||||
palSetLineMode(PS2_DATA, PAL_MODE_INPUT);
|
||||
return palReadLine(PS2_DATA);
|
||||
}
|
||||
@@ -16,9 +16,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <avr/io.h>
|
||||
#include <util/delay.h>
|
||||
|
||||
#if defined(__AVR__)
|
||||
# include <avr/io.h>
|
||||
#endif
|
||||
|
||||
#include "ps2_mouse.h"
|
||||
#include "wait.h"
|
||||
#include "host.h"
|
||||
#include "timer.h"
|
||||
#include "print.h"
|
||||
@@ -42,7 +46,7 @@ static inline void ps2_mouse_scroll_button_task(report_mouse_t *mouse_report);
|
||||
void ps2_mouse_init(void) {
|
||||
ps2_host_init();
|
||||
|
||||
_delay_ms(PS2_MOUSE_INIT_DELAY); // wait for powering up
|
||||
wait_ms(PS2_MOUSE_INIT_DELAY); // wait for powering up
|
||||
|
||||
PS2_MOUSE_SEND(PS2_MOUSE_RESET, "ps2_mouse_init: sending reset");
|
||||
|
||||
@@ -152,8 +156,15 @@ static inline void ps2_mouse_convert_report_to_hid(report_mouse_t *mouse_report)
|
||||
mouse_report->x = X_IS_NEG ? ((!X_IS_OVF && -127 <= mouse_report->x && mouse_report->x <= -1) ? mouse_report->x : -127) : ((!X_IS_OVF && 0 <= mouse_report->x && mouse_report->x <= 127) ? mouse_report->x : 127);
|
||||
mouse_report->y = Y_IS_NEG ? ((!Y_IS_OVF && -127 <= mouse_report->y && mouse_report->y <= -1) ? mouse_report->y : -127) : ((!Y_IS_OVF && 0 <= mouse_report->y && mouse_report->y <= 127) ? mouse_report->y : 127);
|
||||
|
||||
#ifdef PS2_MOUSE_INVERT_BUTTONS
|
||||
// swap left & right buttons
|
||||
uint8_t needs_left = mouse_report->buttons & PS2_MOUSE_BTN_RIGHT;
|
||||
uint8_t needs_right = mouse_report->buttons & PS2_MOUSE_BTN_LEFT;
|
||||
mouse_report->buttons = (mouse_report->buttons & ~(PS2_MOUSE_BTN_MASK)) | (needs_left ? PS2_MOUSE_BTN_LEFT : 0) | (needs_right ? PS2_MOUSE_BTN_RIGHT : 0);
|
||||
#else
|
||||
// remove sign and overflow flags
|
||||
mouse_report->buttons &= PS2_MOUSE_BTN_MASK;
|
||||
#endif
|
||||
|
||||
#ifdef PS2_MOUSE_INVERT_X
|
||||
mouse_report->x = -mouse_report->x;
|
||||
@@ -210,7 +221,7 @@ static inline void ps2_mouse_enable_scrolling(void) {
|
||||
PS2_MOUSE_SEND(PS2_MOUSE_SET_SAMPLE_RATE, "Set sample rate");
|
||||
PS2_MOUSE_SEND(80, "80");
|
||||
PS2_MOUSE_SEND(PS2_MOUSE_GET_DEVICE_ID, "Finished enabling scroll wheel");
|
||||
_delay_ms(20);
|
||||
wait_ms(20);
|
||||
}
|
||||
|
||||
#define PRESS_SCROLL_BUTTONS mouse_report->buttons |= (PS2_MOUSE_SCROLL_BTN_MASK)
|
||||
@@ -252,7 +263,7 @@ static inline void ps2_mouse_scroll_button_task(report_mouse_t *mouse_report) {
|
||||
if (scroll_state == SCROLL_BTN && timer_elapsed(scroll_button_time) < PS2_MOUSE_SCROLL_BTN_SEND) {
|
||||
PRESS_SCROLL_BUTTONS;
|
||||
host_mouse_send(mouse_report);
|
||||
_delay_ms(100);
|
||||
wait_ms(100);
|
||||
RELEASE_SCROLL_BUTTONS;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -162,6 +162,53 @@ const USB_Descriptor_HIDReport_Datatype_t PROGMEM SharedReport[] = {
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef DIGITIZER_ENABLE
|
||||
# ifndef DIGITIZER_SHARED_EP
|
||||
const USB_Descriptor_HIDReport_Datatype_t PROGMEM DigitizerReport[] = {
|
||||
# elif !defined(SHARED_REPORT_STARTED)
|
||||
const USB_Descriptor_HIDReport_Datatype_t PROGMEM SharedReport[] = {
|
||||
# define SHARED_REPORT_STARTED
|
||||
# endif
|
||||
HID_RI_USAGE_PAGE(8, 0x0D), // Digitizers
|
||||
HID_RI_USAGE(8, 0x01), // Digitizer
|
||||
HID_RI_COLLECTION(8, 0x01), // Application
|
||||
# ifdef DIGITIZER_SHARED_EP
|
||||
HID_RI_REPORT_ID(8, REPORT_ID_DIGITIZER),
|
||||
# endif
|
||||
HID_RI_USAGE(8, 0x20), // Stylus
|
||||
HID_RI_COLLECTION(8, 0x00), // Physical
|
||||
// Tip Switch (1 bit)
|
||||
HID_RI_USAGE(8, 0x42), // Tip Switch
|
||||
HID_RI_LOGICAL_MINIMUM(8, 0x00),
|
||||
HID_RI_LOGICAL_MAXIMUM(8, 0x01),
|
||||
HID_RI_REPORT_SIZE(8, 0x01),
|
||||
HID_RI_REPORT_COUNT(8, 0x01),
|
||||
HID_RI_INPUT(8, HID_IOF_VARIABLE),
|
||||
// In Range (1 bit)
|
||||
HID_RI_USAGE(8, 0x32), // In Range
|
||||
HID_RI_INPUT(8, HID_IOF_VARIABLE),
|
||||
// Padding (6 bits)
|
||||
HID_RI_REPORT_COUNT(8, 0x06),
|
||||
HID_RI_INPUT(8, HID_IOF_CONSTANT | HID_IOF_VARIABLE),
|
||||
|
||||
// X/Y Position (4 bytes)
|
||||
HID_RI_USAGE_PAGE(8, 0x01), // Generic Desktop
|
||||
HID_RI_LOGICAL_MAXIMUM(16, 0x7FFF),
|
||||
HID_RI_REPORT_SIZE(8, 0x10),
|
||||
HID_RI_REPORT_COUNT(8, 0x01),
|
||||
HID_RI_UNIT(8, 0x33), // Inch, English Linear
|
||||
HID_RI_UNIT_EXPONENT(8, 0x0E), // -2
|
||||
HID_RI_USAGE(8, 0x30), // X
|
||||
HID_RI_INPUT(8, HID_IOF_VARIABLE),
|
||||
HID_RI_USAGE(8, 0x31), // Y
|
||||
HID_RI_INPUT(8, HID_IOF_VARIABLE),
|
||||
HID_RI_END_COLLECTION(0),
|
||||
HID_RI_END_COLLECTION(0),
|
||||
# ifndef DIGITIZER_SHARED_EP
|
||||
};
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined(SHARED_EP_ENABLE) && !defined(SHARED_REPORT_STARTED)
|
||||
const USB_Descriptor_HIDReport_Datatype_t PROGMEM SharedReport[] = {
|
||||
#endif
|
||||
@@ -231,6 +278,7 @@ const USB_Descriptor_HIDReport_Datatype_t PROGMEM SharedReport[] = {
|
||||
HID_RI_OUTPUT(8, HID_IOF_CONSTANT),
|
||||
HID_RI_END_COLLECTION(0),
|
||||
#endif
|
||||
|
||||
#ifdef SHARED_EP_ENABLE
|
||||
};
|
||||
#endif
|
||||
@@ -347,14 +395,6 @@ const USB_Descriptor_HIDReport_Datatype_t PROGMEM JoystickReport[] = {
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifndef SERIAL_NUMBER
|
||||
#ifdef VIAL_ENABLE
|
||||
# define SERIAL_NUMBER vial:f64c2b3c
|
||||
#else
|
||||
# define SERIAL_NUMBER 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Device descriptor
|
||||
*/
|
||||
@@ -363,8 +403,8 @@ const USB_Descriptor_Device_t PROGMEM DeviceDescriptor = {
|
||||
.Size = sizeof(USB_Descriptor_Device_t),
|
||||
.Type = DTYPE_Device
|
||||
},
|
||||
.USBSpecification = VERSION_BCD(1, 1, 0),
|
||||
|
||||
.USBSpecification = VERSION_BCD(2, 0, 0),
|
||||
|
||||
#if VIRTSER_ENABLE
|
||||
.Class = USB_CSCP_IADDeviceClass,
|
||||
.SubClass = USB_CSCP_IADDeviceSubclass,
|
||||
@@ -933,6 +973,46 @@ const USB_Descriptor_Configuration_t PROGMEM ConfigurationDescriptor = {
|
||||
.PollingIntervalMS = USB_POLLING_INTERVAL_MS
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(DIGITIZER_ENABLE) && !defined(DIGITIZER_SHARED_EP)
|
||||
/*
|
||||
* Digitizer
|
||||
*/
|
||||
.Digitizer_Interface = {
|
||||
.Header = {
|
||||
.Size = sizeof(USB_Descriptor_Interface_t),
|
||||
.Type = DTYPE_Interface
|
||||
},
|
||||
.InterfaceNumber = DIGITIZER_INTERFACE,
|
||||
.AlternateSetting = 0x00,
|
||||
.TotalEndpoints = 1,
|
||||
.Class = HID_CSCP_HIDClass,
|
||||
.SubClass = HID_CSCP_NonBootSubclass,
|
||||
.Protocol = HID_CSCP_NonBootProtocol,
|
||||
.InterfaceStrIndex = NO_DESCRIPTOR
|
||||
},
|
||||
.Digitizer_HID = {
|
||||
.Header = {
|
||||
.Size = sizeof(USB_HID_Descriptor_HID_t),
|
||||
.Type = HID_DTYPE_HID
|
||||
},
|
||||
.HIDSpec = VERSION_BCD(1, 1, 1),
|
||||
.CountryCode = 0x00,
|
||||
.TotalReportDescriptors = 1,
|
||||
.HIDReportType = HID_DTYPE_Report,
|
||||
.HIDReportLength = sizeof(DigitizerReport)
|
||||
},
|
||||
.Digitizer_INEndpoint = {
|
||||
.Header = {
|
||||
.Size = sizeof(USB_Descriptor_Endpoint_t),
|
||||
.Type = DTYPE_Endpoint
|
||||
},
|
||||
.EndpointAddress = (ENDPOINT_DIR_IN | DIGITIZER_IN_EPNUM),
|
||||
.Attributes = (EP_TYPE_INTERRUPT | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA),
|
||||
.EndpointSize = DIGITIZER_EPSIZE,
|
||||
.PollingIntervalMS = USB_POLLING_INTERVAL_MS
|
||||
},
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -965,10 +1045,10 @@ const USB_Descriptor_String_t PROGMEM ProductString = {
|
||||
#if defined(SERIAL_NUMBER)
|
||||
const USB_Descriptor_String_t PROGMEM SerialNumberString = {
|
||||
.Header = {
|
||||
.Size = USB_STRING_LEN(sizeof(STR(SERIAL_NUMBER)) - 1), // Subtract 1 for null terminator
|
||||
.Size = USB_STRING_LEN(sizeof(SERIAL_NUMBER) - 1), // Subtract 1 for null terminator
|
||||
.Type = DTYPE_String
|
||||
},
|
||||
.UnicodeString = LSTR(SERIAL_NUMBER)
|
||||
.UnicodeString = USBSTR(SERIAL_NUMBER)
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -1071,6 +1151,13 @@ uint16_t get_usb_descriptor(const uint16_t wValue, const uint16_t wIndex, const
|
||||
Size = sizeof(USB_HID_Descriptor_HID_t);
|
||||
break;
|
||||
#endif
|
||||
#if defined(DIGITIZER_ENABLE) && !defined(DIGITIZER_SHARED_EP)
|
||||
case DIGITIZER_INTERFACE:
|
||||
Address = &ConfigurationDescriptor.Digitizer_HID;
|
||||
Size = sizeof(USB_HID_Descriptor_HID_t);
|
||||
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -1120,6 +1207,12 @@ uint16_t get_usb_descriptor(const uint16_t wValue, const uint16_t wIndex, const
|
||||
Address = &JoystickReport;
|
||||
Size = sizeof(JoystickReport);
|
||||
break;
|
||||
#endif
|
||||
#if defined(DIGITIZER_ENABLE) && !defined(DIGITIZER_SHARED_EP)
|
||||
case DIGITIZER_INTERFACE:
|
||||
Address = &DigitizerReport;
|
||||
Size = sizeof(DigitizerReport);
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -135,6 +135,13 @@ typedef struct {
|
||||
USB_HID_Descriptor_HID_t Joystick_HID;
|
||||
USB_Descriptor_Endpoint_t Joystick_INEndpoint;
|
||||
#endif
|
||||
|
||||
#if defined(DIGITIZER_ENABLE) && !defined(DIGITIZER_SHARED_EP)
|
||||
// Digitizer HID Interface
|
||||
USB_Descriptor_Interface_t Digitizer_Interface;
|
||||
USB_HID_Descriptor_HID_t Digitizer_HID;
|
||||
USB_Descriptor_Endpoint_t Digitizer_INEndpoint;
|
||||
#endif
|
||||
} USB_Descriptor_Configuration_t;
|
||||
|
||||
/*
|
||||
@@ -180,6 +187,10 @@ enum usb_interfaces {
|
||||
#if defined(JOYSTICK_ENABLE)
|
||||
JOYSTICK_INTERFACE,
|
||||
#endif
|
||||
|
||||
#if defined(DIGITIZER_ENABLE) && !defined(DIGITIZER_SHARED_EP)
|
||||
DIGITIZER_INTERFACE,
|
||||
#endif
|
||||
TOTAL_INTERFACES
|
||||
};
|
||||
|
||||
@@ -226,7 +237,7 @@ enum usb_endpoints {
|
||||
# if STM32_USB_USE_OTG1
|
||||
# define CONSOLE_OUT_EPNUM CONSOLE_IN_EPNUM
|
||||
# else
|
||||
CONSOLE_OUT_EPNUM = NEXT_EPNUM,
|
||||
CONSOLE_OUT_EPNUM = NEXT_EPNUM,
|
||||
# endif
|
||||
# else
|
||||
# define CONSOLE_OUT_EPNUM CONSOLE_IN_EPNUM
|
||||
@@ -259,6 +270,19 @@ enum usb_endpoints {
|
||||
JOYSTICK_OUT_EPNUM = NEXT_EPNUM,
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef DIGITIZER_ENABLE
|
||||
# if !defined(DIGITIZER_SHARED_EP)
|
||||
DIGITIZER_IN_EPNUM = NEXT_EPNUM,
|
||||
# if STM32_USB_USE_OTG1
|
||||
DIGITIZER_OUT_EPNUM = DIGITIZER_IN_EPNUM,
|
||||
# else
|
||||
DIGITIZER_OUT_EPNUM = NEXT_EPNUM,
|
||||
# endif
|
||||
# else
|
||||
# define DIGITIZER_IN_EPNUM SHARED_IN_EPNUM
|
||||
# endif
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef PROTOCOL_LUFA
|
||||
@@ -284,5 +308,6 @@ enum usb_endpoints {
|
||||
#define CDC_NOTIFICATION_EPSIZE 8
|
||||
#define CDC_EPSIZE 16
|
||||
#define JOYSTICK_EPSIZE 8
|
||||
#define DIGITIZER_EPSIZE 8
|
||||
|
||||
uint16_t get_usb_descriptor(const uint16_t wValue, const uint16_t wIndex, const void** const DescriptorAddress);
|
||||
|
||||
@@ -16,6 +16,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
// Prefix string literal with L for descriptors
|
||||
#define USBCONCAT(a, b) a##b
|
||||
#define USBSTR(s) USBCONCAT(L, s)
|
||||
|
||||
/////////////////////
|
||||
// RAW Usage page and ID configuration
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
USB_HID_DIR = protocol/usb_hid
|
||||
|
||||
USB_HOST_LIB_DIR = $(LIB_PATH)/usbhost
|
||||
|
||||
#
|
||||
# USB Host Shield
|
||||
#
|
||||
USB_HOST_SHIELD_DIR = $(USB_HID_DIR)/USB_Host_Shield_2.0
|
||||
USB_HOST_SHIELD_DIR = $(USB_HOST_LIB_DIR)/USB_Host_Shield_2.0
|
||||
USB_HOST_SHIELD_SRC = \
|
||||
$(USB_HOST_SHIELD_DIR)/Usb.cpp \
|
||||
$(USB_HOST_SHIELD_DIR)/hid.cpp \
|
||||
@@ -17,7 +17,7 @@ USB_HOST_SHIELD_SRC = \
|
||||
#
|
||||
# Arduino
|
||||
#
|
||||
ARDUINO_DIR = $(USB_HID_DIR)/arduino-1.0.1
|
||||
ARDUINO_DIR = $(USB_HOST_LIB_DIR)/arduino-1.0.1
|
||||
ARDUINO_CORES_DIR = $(ARDUINO_DIR)/cores/arduino
|
||||
ARDUINO_CORES_SRC = \
|
||||
$(ARDUINO_CORES_DIR)/Print.cpp \
|
||||
@@ -58,13 +58,13 @@ OPT_DEFS += -DARDUINO=101
|
||||
# Search Path
|
||||
#
|
||||
VPATH += $(TMK_DIR)/$(USB_HID_DIR)
|
||||
VPATH += $(TMK_DIR)/$(USB_HOST_SHIELD_DIR)
|
||||
VPATH += $(USB_HOST_SHIELD_DIR)
|
||||
|
||||
# for #include "Arduino.h"
|
||||
VPATH += $(TMK_DIR)/$(ARDUINO_CORES_DIR)
|
||||
VPATH += $(ARDUINO_CORES_DIR)
|
||||
|
||||
# for #include "pins_arduino.h"
|
||||
VPATH += $(TMK_DIR)/$(ARDUINO_DIR)/variants/leonardo
|
||||
VPATH += $(ARDUINO_DIR)/variants/leonardo
|
||||
|
||||
# ad hoc workaround for compile problem on Windows:
|
||||
# Windows doesn't know difference between common/print.h and arduino/Print.h.
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
# Auto detect text files and perform LF normalization
|
||||
* text=auto
|
||||
* text eol=lf
|
||||
|
||||
# Custom for Visual Studio
|
||||
*.cs diff=csharp
|
||||
*.sln merge=union
|
||||
*.csproj merge=union
|
||||
*.vbproj merge=union
|
||||
*.fsproj merge=union
|
||||
*.dbproj merge=union
|
||||
|
||||
# Standard to msysgit
|
||||
*.doc diff=astextplain
|
||||
*.DOC diff=astextplain
|
||||
*.docx diff=astextplain
|
||||
*.DOCX diff=astextplain
|
||||
*.dot diff=astextplain
|
||||
*.DOT diff=astextplain
|
||||
*.pdf diff=astextplain
|
||||
*.PDF diff=astextplain
|
||||
*.rtf diff=astextplain
|
||||
*.RTF diff=astextplain
|
||||
@@ -1,4 +0,0 @@
|
||||
*.bak
|
||||
*.zip
|
||||
*.rar
|
||||
build/
|
||||
@@ -1,12 +0,0 @@
|
||||
[submodule "examples/testusbhostFAT/generic_storage"]
|
||||
path = examples/testusbhostFAT/generic_storage
|
||||
url = https://github.com/xxxajk/generic_storage
|
||||
[submodule "examples/testusbhostFAT/xmem2"]
|
||||
path = examples/testusbhostFAT/xmem2
|
||||
url = https://github.com/xxxajk/xmem2
|
||||
[submodule "examples/testusbhostFAT/Arduino_Makefile_master"]
|
||||
path = examples/testusbhostFAT/Arduino_Makefile_master
|
||||
url = https://github.com/xxxajk/Arduino_Makefile_master
|
||||
[submodule "examples/testusbhostFAT/RTClib"]
|
||||
path = examples/testusbhostFAT/RTClib
|
||||
url = https://github.com/xxxajk/RTClib
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,620 +0,0 @@
|
||||
/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved.
|
||||
|
||||
This software may be distributed and modified under the terms of the GNU
|
||||
General Public License version 2 (GPL2) as published by the Free Software
|
||||
Foundation and appearing in the file GPL2.TXT included in the packaging of
|
||||
this file. Please note that GPL2 Section 2[b] requires that all works based
|
||||
on this software must also be made publicly available under the terms of
|
||||
the GPL2 ("Copyleft").
|
||||
|
||||
Contact information
|
||||
-------------------
|
||||
|
||||
Kristian Lauszus, TKJ Electronics
|
||||
Web : http://www.tkjelectronics.com
|
||||
e-mail : kristianl@tkjelectronics.com
|
||||
*/
|
||||
|
||||
#ifndef _btd_h_
|
||||
#define _btd_h_
|
||||
|
||||
#include "Usb.h"
|
||||
#include "hid.h"
|
||||
|
||||
//PID and VID of the Sony PS3 devices
|
||||
#define PS3_VID 0x054C // Sony Corporation
|
||||
#define PS3_PID 0x0268 // PS3 Controller DualShock 3
|
||||
#define PS3NAVIGATION_PID 0x042F // Navigation controller
|
||||
#define PS3MOVE_PID 0x03D5 // Motion controller
|
||||
|
||||
#define IOGEAR_GBU521_VID 0x0A5C // The IOGEAR GBU521 dongle does not presents itself correctly, so we have to check for it manually
|
||||
#define IOGEAR_GBU521_PID 0x21E8
|
||||
|
||||
/* Bluetooth dongle data taken from descriptors */
|
||||
#define BULK_MAXPKTSIZE 64 // Max size for ACL data
|
||||
|
||||
// Used in control endpoint header for HCI Commands
|
||||
#define bmREQ_HCI_OUT USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_DEVICE
|
||||
|
||||
/* Bluetooth HCI states for hci_task() */
|
||||
#define HCI_INIT_STATE 0
|
||||
#define HCI_RESET_STATE 1
|
||||
#define HCI_CLASS_STATE 2
|
||||
#define HCI_BDADDR_STATE 3
|
||||
#define HCI_LOCAL_VERSION_STATE 4
|
||||
#define HCI_SET_NAME_STATE 5
|
||||
#define HCI_CHECK_DEVICE_SERVICE 6
|
||||
|
||||
#define HCI_INQUIRY_STATE 7 // These three states are only used if it should pair and connect to a device
|
||||
#define HCI_CONNECT_DEVICE_STATE 8
|
||||
#define HCI_CONNECTED_DEVICE_STATE 9
|
||||
|
||||
#define HCI_SCANNING_STATE 10
|
||||
#define HCI_CONNECT_IN_STATE 11
|
||||
#define HCI_REMOTE_NAME_STATE 12
|
||||
#define HCI_CONNECTED_STATE 13
|
||||
#define HCI_DISABLE_SCAN_STATE 14
|
||||
#define HCI_DONE_STATE 15
|
||||
#define HCI_DISCONNECT_STATE 16
|
||||
|
||||
/* HCI event flags*/
|
||||
#define HCI_FLAG_CMD_COMPLETE (1UL << 0)
|
||||
#define HCI_FLAG_CONNECT_COMPLETE (1UL << 1)
|
||||
#define HCI_FLAG_DISCONNECT_COMPLETE (1UL << 2)
|
||||
#define HCI_FLAG_REMOTE_NAME_COMPLETE (1UL << 3)
|
||||
#define HCI_FLAG_INCOMING_REQUEST (1UL << 4)
|
||||
#define HCI_FLAG_READ_BDADDR (1UL << 5)
|
||||
#define HCI_FLAG_READ_VERSION (1UL << 6)
|
||||
#define HCI_FLAG_DEVICE_FOUND (1UL << 7)
|
||||
#define HCI_FLAG_CONNECT_EVENT (1UL << 8)
|
||||
|
||||
/* Macros for HCI event flag tests */
|
||||
#define hci_check_flag(flag) (hci_event_flag & (flag))
|
||||
#define hci_set_flag(flag) (hci_event_flag |= (flag))
|
||||
#define hci_clear_flag(flag) (hci_event_flag &= ~(flag))
|
||||
|
||||
/* HCI Events managed */
|
||||
#define EV_INQUIRY_COMPLETE 0x01
|
||||
#define EV_INQUIRY_RESULT 0x02
|
||||
#define EV_CONNECT_COMPLETE 0x03
|
||||
#define EV_INCOMING_CONNECT 0x04
|
||||
#define EV_DISCONNECT_COMPLETE 0x05
|
||||
#define EV_AUTHENTICATION_COMPLETE 0x06
|
||||
#define EV_REMOTE_NAME_COMPLETE 0x07
|
||||
#define EV_ENCRYPTION_CHANGE 0x08
|
||||
#define EV_CHANGE_CONNECTION_LINK 0x09
|
||||
#define EV_ROLE_CHANGED 0x12
|
||||
#define EV_NUM_COMPLETE_PKT 0x13
|
||||
#define EV_PIN_CODE_REQUEST 0x16
|
||||
#define EV_LINK_KEY_REQUEST 0x17
|
||||
#define EV_LINK_KEY_NOTIFICATION 0x18
|
||||
#define EV_DATA_BUFFER_OVERFLOW 0x1A
|
||||
#define EV_MAX_SLOTS_CHANGE 0x1B
|
||||
#define EV_READ_REMOTE_VERSION_INFORMATION_COMPLETE 0x0C
|
||||
#define EV_QOS_SETUP_COMPLETE 0x0D
|
||||
#define EV_COMMAND_COMPLETE 0x0E
|
||||
#define EV_COMMAND_STATUS 0x0F
|
||||
#define EV_LOOPBACK_COMMAND 0x19
|
||||
#define EV_PAGE_SCAN_REP_MODE 0x20
|
||||
|
||||
/* Bluetooth states for the different Bluetooth drivers */
|
||||
#define L2CAP_WAIT 0
|
||||
#define L2CAP_DONE 1
|
||||
|
||||
/* Used for HID Control channel */
|
||||
#define L2CAP_CONTROL_CONNECT_REQUEST 2
|
||||
#define L2CAP_CONTROL_CONFIG_REQUEST 3
|
||||
#define L2CAP_CONTROL_SUCCESS 4
|
||||
#define L2CAP_CONTROL_DISCONNECT 5
|
||||
|
||||
/* Used for HID Interrupt channel */
|
||||
#define L2CAP_INTERRUPT_SETUP 6
|
||||
#define L2CAP_INTERRUPT_CONNECT_REQUEST 7
|
||||
#define L2CAP_INTERRUPT_CONFIG_REQUEST 8
|
||||
#define L2CAP_INTERRUPT_DISCONNECT 9
|
||||
|
||||
/* Used for SDP channel */
|
||||
#define L2CAP_SDP_WAIT 10
|
||||
#define L2CAP_SDP_SUCCESS 11
|
||||
|
||||
/* Used for RFCOMM channel */
|
||||
#define L2CAP_RFCOMM_WAIT 12
|
||||
#define L2CAP_RFCOMM_SUCCESS 13
|
||||
|
||||
#define L2CAP_DISCONNECT_RESPONSE 14 // Used for both SDP and RFCOMM channel
|
||||
|
||||
/* Bluetooth states used by some drivers */
|
||||
#define TURN_ON_LED 17
|
||||
#define PS3_ENABLE_SIXAXIS 18
|
||||
#define WII_CHECK_MOTION_PLUS_STATE 19
|
||||
#define WII_CHECK_EXTENSION_STATE 20
|
||||
#define WII_INIT_MOTION_PLUS_STATE 21
|
||||
|
||||
/* L2CAP event flags for HID Control channel */
|
||||
#define L2CAP_FLAG_CONNECTION_CONTROL_REQUEST (1UL << 0)
|
||||
#define L2CAP_FLAG_CONFIG_CONTROL_SUCCESS (1UL << 1)
|
||||
#define L2CAP_FLAG_CONTROL_CONNECTED (1UL << 2)
|
||||
#define L2CAP_FLAG_DISCONNECT_CONTROL_RESPONSE (1UL << 3)
|
||||
|
||||
/* L2CAP event flags for HID Interrupt channel */
|
||||
#define L2CAP_FLAG_CONNECTION_INTERRUPT_REQUEST (1UL << 4)
|
||||
#define L2CAP_FLAG_CONFIG_INTERRUPT_SUCCESS (1UL << 5)
|
||||
#define L2CAP_FLAG_INTERRUPT_CONNECTED (1UL << 6)
|
||||
#define L2CAP_FLAG_DISCONNECT_INTERRUPT_RESPONSE (1UL << 7)
|
||||
|
||||
/* L2CAP event flags for SDP channel */
|
||||
#define L2CAP_FLAG_CONNECTION_SDP_REQUEST (1UL << 8)
|
||||
#define L2CAP_FLAG_CONFIG_SDP_SUCCESS (1UL << 9)
|
||||
#define L2CAP_FLAG_DISCONNECT_SDP_REQUEST (1UL << 10)
|
||||
|
||||
/* L2CAP event flags for RFCOMM channel */
|
||||
#define L2CAP_FLAG_CONNECTION_RFCOMM_REQUEST (1UL << 11)
|
||||
#define L2CAP_FLAG_CONFIG_RFCOMM_SUCCESS (1UL << 12)
|
||||
#define L2CAP_FLAG_DISCONNECT_RFCOMM_REQUEST (1UL << 13)
|
||||
|
||||
#define L2CAP_FLAG_DISCONNECT_RESPONSE (1UL << 14)
|
||||
|
||||
/* Macros for L2CAP event flag tests */
|
||||
#define l2cap_check_flag(flag) (l2cap_event_flag & (flag))
|
||||
#define l2cap_set_flag(flag) (l2cap_event_flag |= (flag))
|
||||
#define l2cap_clear_flag(flag) (l2cap_event_flag &= ~(flag))
|
||||
|
||||
/* L2CAP signaling commands */
|
||||
#define L2CAP_CMD_COMMAND_REJECT 0x01
|
||||
#define L2CAP_CMD_CONNECTION_REQUEST 0x02
|
||||
#define L2CAP_CMD_CONNECTION_RESPONSE 0x03
|
||||
#define L2CAP_CMD_CONFIG_REQUEST 0x04
|
||||
#define L2CAP_CMD_CONFIG_RESPONSE 0x05
|
||||
#define L2CAP_CMD_DISCONNECT_REQUEST 0x06
|
||||
#define L2CAP_CMD_DISCONNECT_RESPONSE 0x07
|
||||
#define L2CAP_CMD_INFORMATION_REQUEST 0x0A
|
||||
#define L2CAP_CMD_INFORMATION_RESPONSE 0x0B
|
||||
|
||||
// Used For Connection Response - Remember to Include High Byte
|
||||
#define PENDING 0x01
|
||||
#define SUCCESSFUL 0x00
|
||||
|
||||
/* Bluetooth L2CAP PSM - see http://www.bluetooth.org/Technical/AssignedNumbers/logical_link.htm */
|
||||
#define SDP_PSM 0x01 // Service Discovery Protocol PSM Value
|
||||
#define RFCOMM_PSM 0x03 // RFCOMM PSM Value
|
||||
#define HID_CTRL_PSM 0x11 // HID_Control PSM Value
|
||||
#define HID_INTR_PSM 0x13 // HID_Interrupt PSM Value
|
||||
|
||||
// Used to determine if it is a Bluetooth dongle
|
||||
#define WI_SUBCLASS_RF 0x01 // RF Controller
|
||||
#define WI_PROTOCOL_BT 0x01 // Bluetooth Programming Interface
|
||||
|
||||
#define BTD_MAX_ENDPOINTS 4
|
||||
#define BTD_NUM_SERVICES 4 // Max number of Bluetooth services - if you need more than 4 simply increase this number
|
||||
|
||||
#define PAIR 1
|
||||
|
||||
class BluetoothService;
|
||||
|
||||
/**
|
||||
* The Bluetooth Dongle class will take care of all the USB communication
|
||||
* and then pass the data to the BluetoothService classes.
|
||||
*/
|
||||
class BTD : public USBDeviceConfig, public UsbConfigXtracter {
|
||||
public:
|
||||
/**
|
||||
* Constructor for the BTD class.
|
||||
* @param p Pointer to USB class instance.
|
||||
*/
|
||||
BTD(USB *p);
|
||||
|
||||
/** @name USBDeviceConfig implementation */
|
||||
/**
|
||||
* Address assignment and basic initialization is done here.
|
||||
* @param parent Hub number.
|
||||
* @param port Port number on the hub.
|
||||
* @param lowspeed Speed of the device.
|
||||
* @return 0 on success.
|
||||
*/
|
||||
uint8_t ConfigureDevice(uint8_t parent, uint8_t port, bool lowspeed);
|
||||
/**
|
||||
* Initialize the Bluetooth dongle.
|
||||
* @param parent Hub number.
|
||||
* @param port Port number on the hub.
|
||||
* @param lowspeed Speed of the device.
|
||||
* @return 0 on success.
|
||||
*/
|
||||
uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed);
|
||||
/**
|
||||
* Release the USB device.
|
||||
* @return 0 on success.
|
||||
*/
|
||||
uint8_t Release();
|
||||
/**
|
||||
* Poll the USB Input endpoints and run the state machines.
|
||||
* @return 0 on success.
|
||||
*/
|
||||
uint8_t Poll();
|
||||
|
||||
/**
|
||||
* Get the device address.
|
||||
* @return The device address.
|
||||
*/
|
||||
virtual uint8_t GetAddress() {
|
||||
return bAddress;
|
||||
};
|
||||
|
||||
/**
|
||||
* Used to check if the dongle has been initialized.
|
||||
* @return True if it's ready.
|
||||
*/
|
||||
virtual bool isReady() {
|
||||
return bPollEnable;
|
||||
};
|
||||
|
||||
/**
|
||||
* Used by the USB core to check what this driver support.
|
||||
* @param klass The device's USB class.
|
||||
* @return Returns true if the device's USB class matches this driver.
|
||||
*/
|
||||
virtual bool DEVCLASSOK(uint8_t klass) {
|
||||
return (klass == USB_CLASS_WIRELESS_CTRL);
|
||||
};
|
||||
|
||||
/**
|
||||
* Used by the USB core to check what this driver support.
|
||||
* Used to set the Bluetooth address into the PS3 controllers.
|
||||
* @param vid The device's VID.
|
||||
* @param pid The device's PID.
|
||||
* @return Returns true if the device's VID and PID matches this driver.
|
||||
*/
|
||||
virtual bool VIDPIDOK(uint16_t vid, uint16_t pid) {
|
||||
if(vid == IOGEAR_GBU521_VID && pid == IOGEAR_GBU521_PID)
|
||||
return true;
|
||||
if(my_bdaddr[0] != 0x00 || my_bdaddr[1] != 0x00 || my_bdaddr[2] != 0x00 || my_bdaddr[3] != 0x00 || my_bdaddr[4] != 0x00 || my_bdaddr[5] != 0x00) { // Check if Bluetooth address is set
|
||||
if(vid == PS3_VID && (pid == PS3_PID || pid == PS3NAVIGATION_PID || pid == PS3MOVE_PID))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
/**@}*/
|
||||
|
||||
/** @name UsbConfigXtracter implementation */
|
||||
/**
|
||||
* UsbConfigXtracter implementation, used to extract endpoint information.
|
||||
* @param conf Configuration value.
|
||||
* @param iface Interface number.
|
||||
* @param alt Alternate setting.
|
||||
* @param proto Interface Protocol.
|
||||
* @param ep Endpoint Descriptor.
|
||||
*/
|
||||
void EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR *ep);
|
||||
/**@}*/
|
||||
|
||||
/** Disconnects both the L2CAP Channel and the HCI Connection for all Bluetooth services. */
|
||||
void disconnect();
|
||||
|
||||
/**
|
||||
* Register Bluetooth dongle members/services.
|
||||
* @param pService Pointer to BluetoothService class instance.
|
||||
* @return The service ID on success or -1 on fail.
|
||||
*/
|
||||
int8_t registerBluetoothService(BluetoothService *pService) {
|
||||
for(uint8_t i = 0; i < BTD_NUM_SERVICES; i++) {
|
||||
if(!btService[i]) {
|
||||
btService[i] = pService;
|
||||
return i; // Return ID
|
||||
}
|
||||
}
|
||||
return -1; // Error registering BluetoothService
|
||||
};
|
||||
|
||||
/** @name HCI Commands */
|
||||
/**
|
||||
* Used to send a HCI Command.
|
||||
* @param data Data to send.
|
||||
* @param nbytes Number of bytes to send.
|
||||
*/
|
||||
void HCI_Command(uint8_t* data, uint16_t nbytes);
|
||||
/** Reset the Bluetooth dongle. */
|
||||
void hci_reset();
|
||||
/** Read the Bluetooth address of the dongle. */
|
||||
void hci_read_bdaddr();
|
||||
/** Read the HCI Version of the Bluetooth dongle. */
|
||||
void hci_read_local_version_information();
|
||||
/**
|
||||
* Set the local name of the Bluetooth dongle.
|
||||
* @param name Desired name.
|
||||
*/
|
||||
void hci_set_local_name(const char* name);
|
||||
/** Enable visibility to other Bluetooth devices. */
|
||||
void hci_write_scan_enable();
|
||||
/** Disable visibility to other Bluetooth devices. */
|
||||
void hci_write_scan_disable();
|
||||
/** Read the remote devices name. */
|
||||
void hci_remote_name();
|
||||
/** Accept the connection with the Bluetooth device. */
|
||||
void hci_accept_connection();
|
||||
/**
|
||||
* Disconnect the HCI connection.
|
||||
* @param handle The HCI Handle for the connection.
|
||||
*/
|
||||
void hci_disconnect(uint16_t handle);
|
||||
/**
|
||||
* Respond with the pin for the connection.
|
||||
* The pin is automatically set for the Wii library,
|
||||
* but can be customized for the SPP library.
|
||||
*/
|
||||
void hci_pin_code_request_reply();
|
||||
/** Respons when no pin was set. */
|
||||
void hci_pin_code_negative_request_reply();
|
||||
/**
|
||||
* Command is used to reply to a Link Key Request event from the BR/EDR Controller
|
||||
* if the Host does not have a stored Link Key for the connection.
|
||||
*/
|
||||
void hci_link_key_request_negative_reply();
|
||||
/** Used to try to authenticate with the remote device. */
|
||||
void hci_authentication_request();
|
||||
/** Start a HCI inquiry. */
|
||||
void hci_inquiry();
|
||||
/** Cancel a HCI inquiry. */
|
||||
void hci_inquiry_cancel();
|
||||
/** Connect to last device communicated with. */
|
||||
void hci_connect();
|
||||
/**
|
||||
* Connect to device.
|
||||
* @param bdaddr Bluetooth address of the device.
|
||||
*/
|
||||
void hci_connect(uint8_t *bdaddr);
|
||||
/** Used to a set the class of the device. */
|
||||
void hci_write_class_of_device();
|
||||
/**@}*/
|
||||
|
||||
/** @name L2CAP Commands */
|
||||
/**
|
||||
* Used to send L2CAP Commands.
|
||||
* @param handle HCI Handle.
|
||||
* @param data Data to send.
|
||||
* @param nbytes Number of bytes to send.
|
||||
* @param channelLow,channelHigh Low and high byte of channel to send to.
|
||||
* If argument is omitted then the Standard L2CAP header: Channel ID (0x01) for ACL-U will be used.
|
||||
*/
|
||||
void L2CAP_Command(uint16_t handle, uint8_t* data, uint8_t nbytes, uint8_t channelLow = 0x01, uint8_t channelHigh = 0x00);
|
||||
/**
|
||||
* L2CAP Connection Request.
|
||||
* @param handle HCI handle.
|
||||
* @param rxid Identifier.
|
||||
* @param scid Source Channel Identifier.
|
||||
* @param psm Protocol/Service Multiplexer - see: https://www.bluetooth.org/Technical/AssignedNumbers/logical_link.htm.
|
||||
*/
|
||||
void l2cap_connection_request(uint16_t handle, uint8_t rxid, uint8_t* scid, uint16_t psm);
|
||||
/**
|
||||
* L2CAP Connection Response.
|
||||
* @param handle HCI handle.
|
||||
* @param rxid Identifier.
|
||||
* @param dcid Destination Channel Identifier.
|
||||
* @param scid Source Channel Identifier.
|
||||
* @param result Result - First send ::PENDING and then ::SUCCESSFUL.
|
||||
*/
|
||||
void l2cap_connection_response(uint16_t handle, uint8_t rxid, uint8_t* dcid, uint8_t* scid, uint8_t result);
|
||||
/**
|
||||
* L2CAP Config Request.
|
||||
* @param handle HCI Handle.
|
||||
* @param rxid Identifier.
|
||||
* @param dcid Destination Channel Identifier.
|
||||
*/
|
||||
void l2cap_config_request(uint16_t handle, uint8_t rxid, uint8_t* dcid);
|
||||
/**
|
||||
* L2CAP Config Response.
|
||||
* @param handle HCI Handle.
|
||||
* @param rxid Identifier.
|
||||
* @param scid Source Channel Identifier.
|
||||
*/
|
||||
void l2cap_config_response(uint16_t handle, uint8_t rxid, uint8_t* scid);
|
||||
/**
|
||||
* L2CAP Disconnection Request.
|
||||
* @param handle HCI Handle.
|
||||
* @param rxid Identifier.
|
||||
* @param dcid Device Channel Identifier.
|
||||
* @param scid Source Channel Identifier.
|
||||
*/
|
||||
void l2cap_disconnection_request(uint16_t handle, uint8_t rxid, uint8_t* dcid, uint8_t* scid);
|
||||
/**
|
||||
* L2CAP Disconnection Response.
|
||||
* @param handle HCI Handle.
|
||||
* @param rxid Identifier.
|
||||
* @param dcid Device Channel Identifier.
|
||||
* @param scid Source Channel Identifier.
|
||||
*/
|
||||
void l2cap_disconnection_response(uint16_t handle, uint8_t rxid, uint8_t* dcid, uint8_t* scid);
|
||||
/**
|
||||
* L2CAP Information Response.
|
||||
* @param handle HCI Handle.
|
||||
* @param rxid Identifier.
|
||||
* @param infoTypeLow,infoTypeHigh Infotype.
|
||||
*/
|
||||
void l2cap_information_response(uint16_t handle, uint8_t rxid, uint8_t infoTypeLow, uint8_t infoTypeHigh);
|
||||
/**@}*/
|
||||
|
||||
/** Use this to see if it is waiting for a incoming connection. */
|
||||
bool watingForConnection;
|
||||
/** This is used by the service to know when to store the device information. */
|
||||
bool l2capConnectionClaimed;
|
||||
/** This is used by the SPP library to claim the current SDP incoming request. */
|
||||
bool sdpConnectionClaimed;
|
||||
/** This is used by the SPP library to claim the current RFCOMM incoming request. */
|
||||
bool rfcommConnectionClaimed;
|
||||
|
||||
/** The name you wish to make the dongle show up as. It is set automatically by the SPP library. */
|
||||
const char* btdName;
|
||||
/** The pin you wish to make the dongle use for authentication. It is set automatically by the SPP and BTHID library. */
|
||||
const char* btdPin;
|
||||
|
||||
/** The bluetooth dongles Bluetooth address. */
|
||||
uint8_t my_bdaddr[6];
|
||||
/** HCI handle for the last connection. */
|
||||
uint16_t hci_handle;
|
||||
/** Last incoming devices Bluetooth address. */
|
||||
uint8_t disc_bdaddr[6];
|
||||
/** First 30 chars of last remote name. */
|
||||
char remote_name[30];
|
||||
/**
|
||||
* The supported HCI Version read from the Bluetooth dongle.
|
||||
* Used by the PS3BT library to check the HCI Version of the Bluetooth dongle,
|
||||
* it should be at least 3 to work properly with the library.
|
||||
*/
|
||||
uint8_t hci_version;
|
||||
|
||||
/** Call this function to pair with a Wiimote */
|
||||
void pairWithWiimote() {
|
||||
pairWithWii = true;
|
||||
hci_state = HCI_CHECK_DEVICE_SERVICE;
|
||||
};
|
||||
/** Used to only send the ACL data to the Wiimote. */
|
||||
bool connectToWii;
|
||||
/** True if a Wiimote is connecting. */
|
||||
bool incomingWii;
|
||||
/** True when it should pair with a Wiimote. */
|
||||
bool pairWithWii;
|
||||
/** True if it's the new Wiimote with the Motion Plus Inside or a Wii U Pro Controller. */
|
||||
bool motionPlusInside;
|
||||
/** True if it's a Wii U Pro Controller. */
|
||||
bool wiiUProController;
|
||||
|
||||
/** Call this function to pair with a Wiimote */
|
||||
void pairWithHID() {
|
||||
pairWithHIDDevice = true;
|
||||
hci_state = HCI_CHECK_DEVICE_SERVICE;
|
||||
};
|
||||
/** Used to only send the ACL data to the Wiimote. */
|
||||
bool connectToHIDDevice;
|
||||
/** True if a Wiimote is connecting. */
|
||||
bool incomingHIDDevice;
|
||||
/** True when it should pair with a device like a mouse or keyboard. */
|
||||
bool pairWithHIDDevice;
|
||||
|
||||
/**
|
||||
* Read the poll interval taken from the endpoint descriptors.
|
||||
* @return The poll interval in ms.
|
||||
*/
|
||||
uint8_t readPollInterval() {
|
||||
return pollInterval;
|
||||
};
|
||||
|
||||
protected:
|
||||
/** Pointer to USB class instance. */
|
||||
USB *pUsb;
|
||||
/** Device address. */
|
||||
uint8_t bAddress;
|
||||
/** Endpoint info structure. */
|
||||
EpInfo epInfo[BTD_MAX_ENDPOINTS];
|
||||
|
||||
/** Configuration number. */
|
||||
uint8_t bConfNum;
|
||||
/** Total number of endpoints in the configuration. */
|
||||
uint8_t bNumEP;
|
||||
/** Next poll time based on poll interval taken from the USB descriptor. */
|
||||
uint32_t qNextPollTime;
|
||||
|
||||
/** Bluetooth dongle control endpoint. */
|
||||
static const uint8_t BTD_CONTROL_PIPE;
|
||||
/** HCI event endpoint index. */
|
||||
static const uint8_t BTD_EVENT_PIPE;
|
||||
/** ACL In endpoint index. */
|
||||
static const uint8_t BTD_DATAIN_PIPE;
|
||||
/** ACL Out endpoint index. */
|
||||
static const uint8_t BTD_DATAOUT_PIPE;
|
||||
|
||||
/**
|
||||
* Used to print the USB Endpoint Descriptor.
|
||||
* @param ep_ptr Pointer to USB Endpoint Descriptor.
|
||||
*/
|
||||
void PrintEndpointDescriptor(const USB_ENDPOINT_DESCRIPTOR* ep_ptr);
|
||||
|
||||
private:
|
||||
void Initialize(); // Set all variables, endpoint structs etc. to default values
|
||||
BluetoothService *btService[BTD_NUM_SERVICES];
|
||||
|
||||
uint16_t PID, VID; // PID and VID of device connected
|
||||
|
||||
uint8_t pollInterval;
|
||||
bool bPollEnable;
|
||||
|
||||
bool pairWiiUsingSync; // True if paring was done using the Wii SYNC button.
|
||||
bool checkRemoteName; // Used to check remote device's name before connecting.
|
||||
bool incomingPS4; // True if a PS4 controller is connecting
|
||||
uint8_t classOfDevice[3]; // Class of device of last device
|
||||
|
||||
/* Variables used by high level HCI task */
|
||||
uint8_t hci_state; // Current state of Bluetooth HCI connection
|
||||
uint16_t hci_counter; // Counter used for Bluetooth HCI reset loops
|
||||
uint16_t hci_num_reset_loops; // This value indicate how many times it should read before trying to reset
|
||||
uint16_t hci_event_flag; // HCI flags of received Bluetooth events
|
||||
uint8_t inquiry_counter;
|
||||
|
||||
uint8_t hcibuf[BULK_MAXPKTSIZE]; // General purpose buffer for HCI data
|
||||
uint8_t l2capinbuf[BULK_MAXPKTSIZE]; // General purpose buffer for L2CAP in data
|
||||
uint8_t l2capoutbuf[14]; // General purpose buffer for L2CAP out data
|
||||
|
||||
/* State machines */
|
||||
void HCI_event_task(); // Poll the HCI event pipe
|
||||
void HCI_task(); // HCI state machine
|
||||
void ACL_event_task(); // ACL input pipe
|
||||
|
||||
/* Used to set the Bluetooth Address internally to the PS3 Controllers */
|
||||
void setBdaddr(uint8_t* BDADDR);
|
||||
void setMoveBdaddr(uint8_t* BDADDR);
|
||||
};
|
||||
|
||||
/** All Bluetooth services should inherit this class. */
|
||||
class BluetoothService {
|
||||
public:
|
||||
BluetoothService(BTD *p) : pBtd(p) {
|
||||
if(pBtd)
|
||||
pBtd->registerBluetoothService(this); // Register it as a Bluetooth service
|
||||
};
|
||||
/**
|
||||
* Used to pass acldata to the Bluetooth service.
|
||||
* @param ACLData Pointer to the incoming acldata.
|
||||
*/
|
||||
virtual void ACLData(uint8_t* ACLData) = 0;
|
||||
/** Used to run the different state machines in the Bluetooth service. */
|
||||
virtual void Run() = 0;
|
||||
/** Used to reset the Bluetooth service. */
|
||||
virtual void Reset() = 0;
|
||||
/** Used to disconnect both the L2CAP Channel and the HCI Connection for the Bluetooth service. */
|
||||
virtual void disconnect() = 0;
|
||||
|
||||
/**
|
||||
* Used to call your own function when the device is successfully initialized.
|
||||
* @param funcOnInit Function to call.
|
||||
*/
|
||||
void attachOnInit(void (*funcOnInit)(void)) {
|
||||
pFuncOnInit = funcOnInit; // TODO: This really belong in a class of it's own as it is repeated several times
|
||||
};
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Called when a device is successfully initialized.
|
||||
* Use attachOnInit(void (*funcOnInit)(void)) to call your own function.
|
||||
* This is useful for instance if you want to set the LEDs in a specific way.
|
||||
*/
|
||||
virtual void onInit() = 0;
|
||||
|
||||
/** Used to check if the incoming L2CAP data matches the HCI Handle */
|
||||
bool checkHciHandle(uint8_t *buf, uint16_t handle) {
|
||||
return (buf[0] == (handle & 0xFF)) && (buf[1] == ((handle >> 8) | 0x20));
|
||||
}
|
||||
|
||||
/** Pointer to function called in onInit(). */
|
||||
void (*pFuncOnInit)(void);
|
||||
|
||||
/** Pointer to BTD instance. */
|
||||
BTD *pBtd;
|
||||
|
||||
/** The HCI Handle for the connection. */
|
||||
uint16_t hci_handle;
|
||||
|
||||
/** L2CAP flags of received Bluetooth events. */
|
||||
uint32_t l2cap_event_flag;
|
||||
|
||||
/** Identifier for L2CAP commands. */
|
||||
uint8_t identifier;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,399 +0,0 @@
|
||||
/* Copyright (C) 2013 Kristian Lauszus, TKJ Electronics. All rights reserved.
|
||||
|
||||
This software may be distributed and modified under the terms of the GNU
|
||||
General Public License version 2 (GPL2) as published by the Free Software
|
||||
Foundation and appearing in the file GPL2.TXT included in the packaging of
|
||||
this file. Please note that GPL2 Section 2[b] requires that all works based
|
||||
on this software must also be made publicly available under the terms of
|
||||
the GPL2 ("Copyleft").
|
||||
|
||||
Contact information
|
||||
-------------------
|
||||
|
||||
Kristian Lauszus, TKJ Electronics
|
||||
Web : http://www.tkjelectronics.com
|
||||
e-mail : kristianl@tkjelectronics.com
|
||||
*/
|
||||
|
||||
#include "BTHID.h"
|
||||
// To enable serial debugging see "settings.h"
|
||||
//#define EXTRADEBUG // Uncomment to get even more debugging data
|
||||
//#define PRINTREPORT // Uncomment to print the report send by the HID device
|
||||
|
||||
BTHID::BTHID(BTD *p, bool pair, const char *pin) :
|
||||
BluetoothService(p), // Pointer to USB class instance - mandatory
|
||||
protocolMode(HID_BOOT_PROTOCOL) {
|
||||
for(uint8_t i = 0; i < NUM_PARSERS; i++)
|
||||
pRptParser[i] = NULL;
|
||||
|
||||
pBtd->pairWithHIDDevice = pair;
|
||||
pBtd->btdPin = pin;
|
||||
|
||||
/* Set device cid for the control and intterrupt channelse - LSB */
|
||||
control_dcid[0] = 0x70; // 0x0070
|
||||
control_dcid[1] = 0x00;
|
||||
interrupt_dcid[0] = 0x71; // 0x0071
|
||||
interrupt_dcid[1] = 0x00;
|
||||
|
||||
Reset();
|
||||
}
|
||||
|
||||
void BTHID::Reset() {
|
||||
connected = false;
|
||||
activeConnection = false;
|
||||
l2cap_event_flag = 0; // Reset flags
|
||||
l2cap_state = L2CAP_WAIT;
|
||||
ResetBTHID();
|
||||
}
|
||||
|
||||
void BTHID::disconnect() { // Use this void to disconnect the device
|
||||
// First the HID interrupt channel has to be disconnected, then the HID control channel and finally the HCI connection
|
||||
pBtd->l2cap_disconnection_request(hci_handle, ++identifier, interrupt_scid, interrupt_dcid);
|
||||
Reset();
|
||||
l2cap_state = L2CAP_INTERRUPT_DISCONNECT;
|
||||
}
|
||||
|
||||
void BTHID::ACLData(uint8_t* l2capinbuf) {
|
||||
if(!pBtd->l2capConnectionClaimed && pBtd->incomingHIDDevice && !connected && !activeConnection) {
|
||||
if(l2capinbuf[8] == L2CAP_CMD_CONNECTION_REQUEST) {
|
||||
if((l2capinbuf[12] | (l2capinbuf[13] << 8)) == HID_CTRL_PSM) {
|
||||
pBtd->incomingHIDDevice = false;
|
||||
pBtd->l2capConnectionClaimed = true; // Claim that the incoming connection belongs to this service
|
||||
activeConnection = true;
|
||||
hci_handle = pBtd->hci_handle; // Store the HCI Handle for the connection
|
||||
l2cap_state = L2CAP_WAIT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(checkHciHandle(l2capinbuf, hci_handle)) { // acl_handle_ok
|
||||
if((l2capinbuf[6] | (l2capinbuf[7] << 8)) == 0x0001U) { // l2cap_control - Channel ID for ACL-U
|
||||
if(l2capinbuf[8] == L2CAP_CMD_COMMAND_REJECT) {
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nL2CAP Command Rejected - Reason: "), 0x80);
|
||||
D_PrintHex<uint8_t > (l2capinbuf[13], 0x80);
|
||||
Notify(PSTR(" "), 0x80);
|
||||
D_PrintHex<uint8_t > (l2capinbuf[12], 0x80);
|
||||
Notify(PSTR(" "), 0x80);
|
||||
D_PrintHex<uint8_t > (l2capinbuf[17], 0x80);
|
||||
Notify(PSTR(" "), 0x80);
|
||||
D_PrintHex<uint8_t > (l2capinbuf[16], 0x80);
|
||||
Notify(PSTR(" "), 0x80);
|
||||
D_PrintHex<uint8_t > (l2capinbuf[15], 0x80);
|
||||
Notify(PSTR(" "), 0x80);
|
||||
D_PrintHex<uint8_t > (l2capinbuf[14], 0x80);
|
||||
#endif
|
||||
} else if(l2capinbuf[8] == L2CAP_CMD_CONNECTION_RESPONSE) {
|
||||
if(((l2capinbuf[16] | (l2capinbuf[17] << 8)) == 0x0000) && ((l2capinbuf[18] | (l2capinbuf[19] << 8)) == SUCCESSFUL)) { // Success
|
||||
if(l2capinbuf[14] == control_dcid[0] && l2capinbuf[15] == control_dcid[1]) {
|
||||
//Notify(PSTR("\r\nHID Control Connection Complete"), 0x80);
|
||||
identifier = l2capinbuf[9];
|
||||
control_scid[0] = l2capinbuf[12];
|
||||
control_scid[1] = l2capinbuf[13];
|
||||
l2cap_set_flag(L2CAP_FLAG_CONTROL_CONNECTED);
|
||||
} else if(l2capinbuf[14] == interrupt_dcid[0] && l2capinbuf[15] == interrupt_dcid[1]) {
|
||||
//Notify(PSTR("\r\nHID Interrupt Connection Complete"), 0x80);
|
||||
identifier = l2capinbuf[9];
|
||||
interrupt_scid[0] = l2capinbuf[12];
|
||||
interrupt_scid[1] = l2capinbuf[13];
|
||||
l2cap_set_flag(L2CAP_FLAG_INTERRUPT_CONNECTED);
|
||||
}
|
||||
}
|
||||
} else if(l2capinbuf[8] == L2CAP_CMD_CONNECTION_REQUEST) {
|
||||
#ifdef EXTRADEBUG
|
||||
Notify(PSTR("\r\nL2CAP Connection Request - PSM: "), 0x80);
|
||||
D_PrintHex<uint8_t > (l2capinbuf[13], 0x80);
|
||||
Notify(PSTR(" "), 0x80);
|
||||
D_PrintHex<uint8_t > (l2capinbuf[12], 0x80);
|
||||
Notify(PSTR(" SCID: "), 0x80);
|
||||
D_PrintHex<uint8_t > (l2capinbuf[15], 0x80);
|
||||
Notify(PSTR(" "), 0x80);
|
||||
D_PrintHex<uint8_t > (l2capinbuf[14], 0x80);
|
||||
Notify(PSTR(" Identifier: "), 0x80);
|
||||
D_PrintHex<uint8_t > (l2capinbuf[9], 0x80);
|
||||
#endif
|
||||
if((l2capinbuf[12] | (l2capinbuf[13] << 8)) == HID_CTRL_PSM) {
|
||||
identifier = l2capinbuf[9];
|
||||
control_scid[0] = l2capinbuf[14];
|
||||
control_scid[1] = l2capinbuf[15];
|
||||
l2cap_set_flag(L2CAP_FLAG_CONNECTION_CONTROL_REQUEST);
|
||||
} else if((l2capinbuf[12] | (l2capinbuf[13] << 8)) == HID_INTR_PSM) {
|
||||
identifier = l2capinbuf[9];
|
||||
interrupt_scid[0] = l2capinbuf[14];
|
||||
interrupt_scid[1] = l2capinbuf[15];
|
||||
l2cap_set_flag(L2CAP_FLAG_CONNECTION_INTERRUPT_REQUEST);
|
||||
}
|
||||
} else if(l2capinbuf[8] == L2CAP_CMD_CONFIG_RESPONSE) {
|
||||
if((l2capinbuf[16] | (l2capinbuf[17] << 8)) == 0x0000) { // Success
|
||||
if(l2capinbuf[12] == control_dcid[0] && l2capinbuf[13] == control_dcid[1]) {
|
||||
//Notify(PSTR("\r\nHID Control Configuration Complete"), 0x80);
|
||||
identifier = l2capinbuf[9];
|
||||
l2cap_set_flag(L2CAP_FLAG_CONFIG_CONTROL_SUCCESS);
|
||||
} else if(l2capinbuf[12] == interrupt_dcid[0] && l2capinbuf[13] == interrupt_dcid[1]) {
|
||||
//Notify(PSTR("\r\nHID Interrupt Configuration Complete"), 0x80);
|
||||
identifier = l2capinbuf[9];
|
||||
l2cap_set_flag(L2CAP_FLAG_CONFIG_INTERRUPT_SUCCESS);
|
||||
}
|
||||
}
|
||||
} else if(l2capinbuf[8] == L2CAP_CMD_CONFIG_REQUEST) {
|
||||
if(l2capinbuf[12] == control_dcid[0] && l2capinbuf[13] == control_dcid[1]) {
|
||||
//Notify(PSTR("\r\nHID Control Configuration Request"), 0x80);
|
||||
pBtd->l2cap_config_response(hci_handle, l2capinbuf[9], control_scid);
|
||||
} else if(l2capinbuf[12] == interrupt_dcid[0] && l2capinbuf[13] == interrupt_dcid[1]) {
|
||||
//Notify(PSTR("\r\nHID Interrupt Configuration Request"), 0x80);
|
||||
pBtd->l2cap_config_response(hci_handle, l2capinbuf[9], interrupt_scid);
|
||||
}
|
||||
} else if(l2capinbuf[8] == L2CAP_CMD_DISCONNECT_REQUEST) {
|
||||
if(l2capinbuf[12] == control_dcid[0] && l2capinbuf[13] == control_dcid[1]) {
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nDisconnect Request: Control Channel"), 0x80);
|
||||
#endif
|
||||
identifier = l2capinbuf[9];
|
||||
pBtd->l2cap_disconnection_response(hci_handle, identifier, control_dcid, control_scid);
|
||||
Reset();
|
||||
} else if(l2capinbuf[12] == interrupt_dcid[0] && l2capinbuf[13] == interrupt_dcid[1]) {
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nDisconnect Request: Interrupt Channel"), 0x80);
|
||||
#endif
|
||||
identifier = l2capinbuf[9];
|
||||
pBtd->l2cap_disconnection_response(hci_handle, identifier, interrupt_dcid, interrupt_scid);
|
||||
Reset();
|
||||
}
|
||||
} else if(l2capinbuf[8] == L2CAP_CMD_DISCONNECT_RESPONSE) {
|
||||
if(l2capinbuf[12] == control_scid[0] && l2capinbuf[13] == control_scid[1]) {
|
||||
//Notify(PSTR("\r\nDisconnect Response: Control Channel"), 0x80);
|
||||
identifier = l2capinbuf[9];
|
||||
l2cap_set_flag(L2CAP_FLAG_DISCONNECT_CONTROL_RESPONSE);
|
||||
} else if(l2capinbuf[12] == interrupt_scid[0] && l2capinbuf[13] == interrupt_scid[1]) {
|
||||
//Notify(PSTR("\r\nDisconnect Response: Interrupt Channel"), 0x80);
|
||||
identifier = l2capinbuf[9];
|
||||
l2cap_set_flag(L2CAP_FLAG_DISCONNECT_INTERRUPT_RESPONSE);
|
||||
}
|
||||
}
|
||||
#ifdef EXTRADEBUG
|
||||
else {
|
||||
identifier = l2capinbuf[9];
|
||||
Notify(PSTR("\r\nL2CAP Unknown Signaling Command: "), 0x80);
|
||||
D_PrintHex<uint8_t > (l2capinbuf[8], 0x80);
|
||||
}
|
||||
#endif
|
||||
} else if(l2capinbuf[6] == interrupt_dcid[0] && l2capinbuf[7] == interrupt_dcid[1]) { // l2cap_interrupt
|
||||
#ifdef PRINTREPORT
|
||||
Notify(PSTR("\r\nL2CAP Interrupt: "), 0x80);
|
||||
for(uint16_t i = 0; i < ((uint16_t)l2capinbuf[5] << 8 | l2capinbuf[4]); i++) {
|
||||
D_PrintHex<uint8_t > (l2capinbuf[i + 8], 0x80);
|
||||
Notify(PSTR(" "), 0x80);
|
||||
}
|
||||
#endif
|
||||
if(l2capinbuf[8] == 0xA1) { // HID_THDR_DATA_INPUT
|
||||
uint16_t length = ((uint16_t)l2capinbuf[5] << 8 | l2capinbuf[4]);
|
||||
ParseBTHIDData((uint8_t)(length - 1), &l2capinbuf[9]);
|
||||
|
||||
switch(l2capinbuf[9]) {
|
||||
case 0x01: // Keyboard or Joystick events
|
||||
if(pRptParser[KEYBOARD_PARSER_ID])
|
||||
pRptParser[KEYBOARD_PARSER_ID]->Parse(reinterpret_cast<HID *>(this), 0, (uint8_t)(length - 2), &l2capinbuf[10]); // Use reinterpret_cast again to extract the instance
|
||||
break;
|
||||
|
||||
case 0x02: // Mouse events
|
||||
if(pRptParser[MOUSE_PARSER_ID])
|
||||
pRptParser[MOUSE_PARSER_ID]->Parse(reinterpret_cast<HID *>(this), 0, (uint8_t)(length - 2), &l2capinbuf[10]); // Use reinterpret_cast again to extract the instance
|
||||
break;
|
||||
#ifdef EXTRADEBUG
|
||||
default:
|
||||
Notify(PSTR("\r\nUnknown Report type: "), 0x80);
|
||||
D_PrintHex<uint8_t > (l2capinbuf[9], 0x80);
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
} else if(l2capinbuf[6] == control_dcid[0] && l2capinbuf[7] == control_dcid[1]) { // l2cap_control
|
||||
#ifdef PRINTREPORT
|
||||
Notify(PSTR("\r\nL2CAP Control: "), 0x80);
|
||||
for(uint16_t i = 0; i < ((uint16_t)l2capinbuf[5] << 8 | l2capinbuf[4]); i++) {
|
||||
D_PrintHex<uint8_t > (l2capinbuf[i + 8], 0x80);
|
||||
Notify(PSTR(" "), 0x80);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#ifdef EXTRADEBUG
|
||||
else {
|
||||
Notify(PSTR("\r\nUnsupported L2CAP Data - Channel ID: "), 0x80);
|
||||
D_PrintHex<uint8_t > (l2capinbuf[7], 0x80);
|
||||
Notify(PSTR(" "), 0x80);
|
||||
D_PrintHex<uint8_t > (l2capinbuf[6], 0x80);
|
||||
|
||||
Notify(PSTR("\r\nData: "), 0x80);
|
||||
Notify(PSTR("\r\n"), 0x80);
|
||||
for(uint16_t i = 0; i < ((uint16_t)l2capinbuf[5] << 8 | l2capinbuf[4]); i++) {
|
||||
D_PrintHex<uint8_t > (l2capinbuf[i + 8], 0x80);
|
||||
Notify(PSTR(" "), 0x80);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
L2CAP_task();
|
||||
}
|
||||
}
|
||||
|
||||
void BTHID::L2CAP_task() {
|
||||
switch(l2cap_state) {
|
||||
/* These states are used if the HID device is the host */
|
||||
case L2CAP_CONTROL_SUCCESS:
|
||||
if(l2cap_check_flag(L2CAP_FLAG_CONFIG_CONTROL_SUCCESS)) {
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nHID Control Successfully Configured"), 0x80);
|
||||
#endif
|
||||
setProtocol(); // Set protocol before establishing HID interrupt channel
|
||||
l2cap_state = L2CAP_INTERRUPT_SETUP;
|
||||
}
|
||||
break;
|
||||
|
||||
case L2CAP_INTERRUPT_SETUP:
|
||||
if(l2cap_check_flag(L2CAP_FLAG_CONNECTION_INTERRUPT_REQUEST)) {
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nHID Interrupt Incoming Connection Request"), 0x80);
|
||||
#endif
|
||||
pBtd->l2cap_connection_response(hci_handle, identifier, interrupt_dcid, interrupt_scid, PENDING);
|
||||
delay(1);
|
||||
pBtd->l2cap_connection_response(hci_handle, identifier, interrupt_dcid, interrupt_scid, SUCCESSFUL);
|
||||
identifier++;
|
||||
delay(1);
|
||||
pBtd->l2cap_config_request(hci_handle, identifier, interrupt_scid);
|
||||
|
||||
l2cap_state = L2CAP_INTERRUPT_CONFIG_REQUEST;
|
||||
}
|
||||
break;
|
||||
|
||||
/* These states are used if the Arduino is the host */
|
||||
case L2CAP_CONTROL_CONNECT_REQUEST:
|
||||
if(l2cap_check_flag(L2CAP_FLAG_CONTROL_CONNECTED)) {
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nSend HID Control Config Request"), 0x80);
|
||||
#endif
|
||||
identifier++;
|
||||
pBtd->l2cap_config_request(hci_handle, identifier, control_scid);
|
||||
l2cap_state = L2CAP_CONTROL_CONFIG_REQUEST;
|
||||
}
|
||||
break;
|
||||
|
||||
case L2CAP_CONTROL_CONFIG_REQUEST:
|
||||
if(l2cap_check_flag(L2CAP_FLAG_CONFIG_CONTROL_SUCCESS)) {
|
||||
setProtocol(); // Set protocol before establishing HID interrupt channel
|
||||
delay(1); // Short delay between commands - just to be sure
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nSend HID Interrupt Connection Request"), 0x80);
|
||||
#endif
|
||||
identifier++;
|
||||
pBtd->l2cap_connection_request(hci_handle, identifier, interrupt_dcid, HID_INTR_PSM);
|
||||
l2cap_state = L2CAP_INTERRUPT_CONNECT_REQUEST;
|
||||
}
|
||||
break;
|
||||
|
||||
case L2CAP_INTERRUPT_CONNECT_REQUEST:
|
||||
if(l2cap_check_flag(L2CAP_FLAG_INTERRUPT_CONNECTED)) {
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nSend HID Interrupt Config Request"), 0x80);
|
||||
#endif
|
||||
identifier++;
|
||||
pBtd->l2cap_config_request(hci_handle, identifier, interrupt_scid);
|
||||
l2cap_state = L2CAP_INTERRUPT_CONFIG_REQUEST;
|
||||
}
|
||||
break;
|
||||
|
||||
case L2CAP_INTERRUPT_CONFIG_REQUEST:
|
||||
if(l2cap_check_flag(L2CAP_FLAG_CONFIG_INTERRUPT_SUCCESS)) { // Now the HID channels is established
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nHID Channels Established"), 0x80);
|
||||
#endif
|
||||
pBtd->connectToHIDDevice = false;
|
||||
pBtd->pairWithHIDDevice = false;
|
||||
connected = true;
|
||||
onInit();
|
||||
l2cap_state = L2CAP_DONE;
|
||||
}
|
||||
break;
|
||||
|
||||
case L2CAP_DONE:
|
||||
break;
|
||||
|
||||
case L2CAP_INTERRUPT_DISCONNECT:
|
||||
if(l2cap_check_flag(L2CAP_FLAG_DISCONNECT_INTERRUPT_RESPONSE)) {
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nDisconnected Interrupt Channel"), 0x80);
|
||||
#endif
|
||||
identifier++;
|
||||
pBtd->l2cap_disconnection_request(hci_handle, identifier, control_scid, control_dcid);
|
||||
l2cap_state = L2CAP_CONTROL_DISCONNECT;
|
||||
}
|
||||
break;
|
||||
|
||||
case L2CAP_CONTROL_DISCONNECT:
|
||||
if(l2cap_check_flag(L2CAP_FLAG_DISCONNECT_CONTROL_RESPONSE)) {
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nDisconnected Control Channel"), 0x80);
|
||||
#endif
|
||||
pBtd->hci_disconnect(hci_handle);
|
||||
hci_handle = -1; // Reset handle
|
||||
l2cap_event_flag = 0; // Reset flags
|
||||
l2cap_state = L2CAP_WAIT;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void BTHID::Run() {
|
||||
switch(l2cap_state) {
|
||||
case L2CAP_WAIT:
|
||||
if(pBtd->connectToHIDDevice && !pBtd->l2capConnectionClaimed && !connected && !activeConnection) {
|
||||
pBtd->l2capConnectionClaimed = true;
|
||||
activeConnection = true;
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nSend HID Control Connection Request"), 0x80);
|
||||
#endif
|
||||
hci_handle = pBtd->hci_handle; // Store the HCI Handle for the connection
|
||||
l2cap_event_flag = 0; // Reset flags
|
||||
identifier = 0;
|
||||
pBtd->l2cap_connection_request(hci_handle, identifier, control_dcid, HID_CTRL_PSM);
|
||||
l2cap_state = L2CAP_CONTROL_CONNECT_REQUEST;
|
||||
} else if(l2cap_check_flag(L2CAP_FLAG_CONNECTION_CONTROL_REQUEST)) {
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nHID Control Incoming Connection Request"), 0x80);
|
||||
#endif
|
||||
pBtd->l2cap_connection_response(hci_handle, identifier, control_dcid, control_scid, PENDING);
|
||||
delay(1);
|
||||
pBtd->l2cap_connection_response(hci_handle, identifier, control_dcid, control_scid, SUCCESSFUL);
|
||||
identifier++;
|
||||
delay(1);
|
||||
pBtd->l2cap_config_request(hci_handle, identifier, control_scid);
|
||||
l2cap_state = L2CAP_CONTROL_SUCCESS;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************/
|
||||
/* HID Commands */
|
||||
|
||||
/************************************************************/
|
||||
void BTHID::setProtocol() {
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nSet protocol mode: "), 0x80);
|
||||
D_PrintHex<uint8_t > (protocolMode, 0x80);
|
||||
#endif
|
||||
if (protocolMode != HID_BOOT_PROTOCOL && protocolMode != HID_RPT_PROTOCOL) {
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nNot a valid protocol mode. Using Boot protocol instead."), 0x80);
|
||||
#endif
|
||||
protocolMode = HID_BOOT_PROTOCOL; // Use Boot Protocol by default
|
||||
}
|
||||
uint8_t command = 0x70 | protocolMode; // Set Protocol, see Bluetooth HID specs page 33
|
||||
pBtd->L2CAP_Command(hci_handle, &command, 1, control_scid[0], control_scid[1]);
|
||||
}
|
||||
|
||||
void BTHID::setLeds(uint8_t data) {
|
||||
uint8_t buf[3];
|
||||
buf[0] = 0xA2; // HID BT DATA_request (0xA0) | Report Type (Output 0x02)
|
||||
buf[1] = 0x01; // Report ID
|
||||
buf[2] = data;
|
||||
pBtd->L2CAP_Command(hci_handle, buf, 3, interrupt_scid[0], interrupt_scid[1]);
|
||||
}
|
||||
@@ -1,155 +0,0 @@
|
||||
/* Copyright (C) 2013 Kristian Lauszus, TKJ Electronics. All rights reserved.
|
||||
|
||||
This software may be distributed and modified under the terms of the GNU
|
||||
General Public License version 2 (GPL2) as published by the Free Software
|
||||
Foundation and appearing in the file GPL2.TXT included in the packaging of
|
||||
this file. Please note that GPL2 Section 2[b] requires that all works based
|
||||
on this software must also be made publicly available under the terms of
|
||||
the GPL2 ("Copyleft").
|
||||
|
||||
Contact information
|
||||
-------------------
|
||||
|
||||
Kristian Lauszus, TKJ Electronics
|
||||
Web : http://www.tkjelectronics.com
|
||||
e-mail : kristianl@tkjelectronics.com
|
||||
*/
|
||||
|
||||
#ifndef _bthid_h_
|
||||
#define _bthid_h_
|
||||
|
||||
#include "BTD.h"
|
||||
#include "hidboot.h"
|
||||
|
||||
#define KEYBOARD_PARSER_ID 0
|
||||
#define MOUSE_PARSER_ID 1
|
||||
#define NUM_PARSERS 2
|
||||
|
||||
/** This BluetoothService class implements support for Bluetooth HID devices. */
|
||||
class BTHID : public BluetoothService {
|
||||
public:
|
||||
/**
|
||||
* Constructor for the BTHID class.
|
||||
* @param p Pointer to the BTD class instance.
|
||||
* @param pair Set this to true in order to pair with the device. If the argument is omitted then it will not pair with it. One can use ::PAIR to set it to true.
|
||||
* @param pin Write the pin to BTD#btdPin. If argument is omitted, then "0000" will be used.
|
||||
*/
|
||||
BTHID(BTD *p, bool pair = false, const char *pin = "0000");
|
||||
|
||||
/** @name BluetoothService implementation */
|
||||
/** Used this to disconnect the devices. */
|
||||
void disconnect();
|
||||
/**@}*/
|
||||
|
||||
/**
|
||||
* Get HIDReportParser.
|
||||
* @param id ID of parser.
|
||||
* @return Returns the corresponding HIDReportParser. Returns NULL if id is not valid.
|
||||
*/
|
||||
HIDReportParser *GetReportParser(uint8_t id) {
|
||||
if (id >= NUM_PARSERS)
|
||||
return NULL;
|
||||
return pRptParser[id];
|
||||
};
|
||||
|
||||
/**
|
||||
* Set HIDReportParser to be used.
|
||||
* @param id Id of parser.
|
||||
* @param prs Pointer to HIDReportParser.
|
||||
* @return Returns true if the HIDReportParser is set. False otherwise.
|
||||
*/
|
||||
bool SetReportParser(uint8_t id, HIDReportParser *prs) {
|
||||
if (id >= NUM_PARSERS)
|
||||
return false;
|
||||
pRptParser[id] = prs;
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set HID protocol mode.
|
||||
* @param mode HID protocol to use. Either HID_BOOT_PROTOCOL or HID_RPT_PROTOCOL.
|
||||
*/
|
||||
void setProtocolMode(uint8_t mode) {
|
||||
protocolMode = mode;
|
||||
};
|
||||
|
||||
/**
|
||||
* Used to set the leds on a keyboard.
|
||||
* @param data See KBDLEDS in hidboot.h
|
||||
*/
|
||||
void setLeds(uint8_t data);
|
||||
|
||||
/** True if a device is connected */
|
||||
bool connected;
|
||||
|
||||
/** Call this to start the paring sequence with a device */
|
||||
void pair(void) {
|
||||
if(pBtd)
|
||||
pBtd->pairWithHID();
|
||||
};
|
||||
|
||||
protected:
|
||||
/** @name BluetoothService implementation */
|
||||
/**
|
||||
* Used to pass acldata to the services.
|
||||
* @param ACLData Incoming acldata.
|
||||
*/
|
||||
void ACLData(uint8_t* ACLData);
|
||||
/** Used to run part of the state machine. */
|
||||
void Run();
|
||||
/** Use this to reset the service. */
|
||||
void Reset();
|
||||
/**
|
||||
* Called when a device is successfully initialized.
|
||||
* Use attachOnInit(void (*funcOnInit)(void)) to call your own function.
|
||||
* This is useful for instance if you want to set the LEDs in a specific way.
|
||||
*/
|
||||
void onInit() {
|
||||
if(pFuncOnInit)
|
||||
pFuncOnInit(); // Call the user function
|
||||
OnInitBTHID();
|
||||
};
|
||||
/**@}*/
|
||||
|
||||
/** @name Overridable functions */
|
||||
/**
|
||||
* Used to parse Bluetooth HID data to any class that inherits this class.
|
||||
* @param len The length of the incoming data.
|
||||
* @param buf Pointer to the data buffer.
|
||||
*/
|
||||
virtual void ParseBTHIDData(uint8_t len, uint8_t *buf) {
|
||||
return;
|
||||
};
|
||||
/** Called when a device is connected */
|
||||
virtual void OnInitBTHID() {
|
||||
return;
|
||||
};
|
||||
/** Used to reset any buffers in the class that inherits this */
|
||||
virtual void ResetBTHID() {
|
||||
return;
|
||||
}
|
||||
/**@}*/
|
||||
|
||||
/** L2CAP source CID for HID_Control */
|
||||
uint8_t control_scid[2];
|
||||
|
||||
/** L2CAP source CID for HID_Interrupt */
|
||||
uint8_t interrupt_scid[2];
|
||||
|
||||
private:
|
||||
HIDReportParser *pRptParser[NUM_PARSERS]; // Pointer to HIDReportParsers.
|
||||
|
||||
/** Set report protocol. */
|
||||
void setProtocol();
|
||||
uint8_t protocolMode;
|
||||
|
||||
void L2CAP_task(); // L2CAP state machine
|
||||
|
||||
bool activeConnection; // Used to indicate if it already has established a connection
|
||||
|
||||
/* Variables used for L2CAP communication */
|
||||
uint8_t control_dcid[2]; // L2CAP device CID for HID_Control - Always 0x0070
|
||||
uint8_t interrupt_dcid[2]; // L2CAP device CID for HID_Interrupt - Always 0x0071
|
||||
uint8_t l2cap_state;
|
||||
};
|
||||
#endif
|
||||
@@ -1,634 +0,0 @@
|
||||
/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved.
|
||||
|
||||
This software may be distributed and modified under the terms of the GNU
|
||||
General Public License version 2 (GPL2) as published by the Free Software
|
||||
Foundation and appearing in the file GPL2.TXT included in the packaging of
|
||||
this file. Please note that GPL2 Section 2[b] requires that all works based
|
||||
on this software must also be made publicly available under the terms of
|
||||
the GPL2 ("Copyleft").
|
||||
|
||||
Contact information
|
||||
-------------------
|
||||
|
||||
Kristian Lauszus, TKJ Electronics
|
||||
Web : http://www.tkjelectronics.com
|
||||
e-mail : kristianl@tkjelectronics.com
|
||||
*/
|
||||
|
||||
#include "PS3BT.h"
|
||||
// To enable serial debugging see "settings.h"
|
||||
//#define EXTRADEBUG // Uncomment to get even more debugging data
|
||||
//#define PRINTREPORT // Uncomment to print the report send by the PS3 Controllers
|
||||
|
||||
PS3BT::PS3BT(BTD *p, uint8_t btadr5, uint8_t btadr4, uint8_t btadr3, uint8_t btadr2, uint8_t btadr1, uint8_t btadr0) :
|
||||
BluetoothService(p) // Pointer to USB class instance - mandatory
|
||||
{
|
||||
pBtd->my_bdaddr[5] = btadr5; // Change to your dongle's Bluetooth address instead
|
||||
pBtd->my_bdaddr[4] = btadr4;
|
||||
pBtd->my_bdaddr[3] = btadr3;
|
||||
pBtd->my_bdaddr[2] = btadr2;
|
||||
pBtd->my_bdaddr[1] = btadr1;
|
||||
pBtd->my_bdaddr[0] = btadr0;
|
||||
|
||||
HIDBuffer[0] = 0x52; // HID BT Set_report (0x50) | Report Type (Output 0x02)
|
||||
HIDBuffer[1] = 0x01; // Report ID
|
||||
|
||||
// Needed for PS3 Move Controller commands to work via bluetooth
|
||||
HIDMoveBuffer[0] = 0xA2; // HID BT DATA_request (0xA0) | Report Type (Output 0x02)
|
||||
HIDMoveBuffer[1] = 0x02; // Report ID
|
||||
|
||||
/* Set device cid for the control and intterrupt channelse - LSB */
|
||||
control_dcid[0] = 0x40; // 0x0040
|
||||
control_dcid[1] = 0x00;
|
||||
interrupt_dcid[0] = 0x41; // 0x0041
|
||||
interrupt_dcid[1] = 0x00;
|
||||
|
||||
Reset();
|
||||
}
|
||||
|
||||
bool PS3BT::getButtonPress(ButtonEnum b) {
|
||||
return (ButtonState & pgm_read_dword(&PS3_BUTTONS[(uint8_t)b]));
|
||||
}
|
||||
|
||||
bool PS3BT::getButtonClick(ButtonEnum b) {
|
||||
uint32_t button = pgm_read_dword(&PS3_BUTTONS[(uint8_t)b]);
|
||||
bool click = (ButtonClickState & button);
|
||||
ButtonClickState &= ~button; // Clear "click" event
|
||||
return click;
|
||||
}
|
||||
|
||||
uint8_t PS3BT::getAnalogButton(ButtonEnum a) {
|
||||
return (uint8_t)(l2capinbuf[pgm_read_byte(&PS3_ANALOG_BUTTONS[(uint8_t)a])]);
|
||||
}
|
||||
|
||||
uint8_t PS3BT::getAnalogHat(AnalogHatEnum a) {
|
||||
return (uint8_t)(l2capinbuf[(uint8_t)a + 15]);
|
||||
}
|
||||
|
||||
int16_t PS3BT::getSensor(SensorEnum a) {
|
||||
if(PS3Connected) {
|
||||
if(a == aX || a == aY || a == aZ || a == gZ)
|
||||
return ((l2capinbuf[(uint16_t)a] << 8) | l2capinbuf[(uint16_t)a + 1]);
|
||||
else
|
||||
return 0;
|
||||
} else if(PS3MoveConnected) {
|
||||
if(a == mXmove || a == mYmove) // These are all 12-bits long
|
||||
return (((l2capinbuf[(uint16_t)a] & 0x0F) << 8) | (l2capinbuf[(uint16_t)a + 1]));
|
||||
else if(a == mZmove || a == tempMove) // The tempearature is also 12 bits long
|
||||
return ((l2capinbuf[(uint16_t)a] << 4) | ((l2capinbuf[(uint16_t)a + 1] & 0xF0) >> 4));
|
||||
else // aXmove, aYmove, aZmove, gXmove, gYmove and gZmove
|
||||
return (l2capinbuf[(uint16_t)a] | (l2capinbuf[(uint16_t)a + 1] << 8));
|
||||
} else
|
||||
return 0;
|
||||
}
|
||||
|
||||
double PS3BT::getAngle(AngleEnum a) {
|
||||
double accXval, accYval, accZval;
|
||||
|
||||
if(PS3Connected) {
|
||||
// Data for the Kionix KXPC4 used in the DualShock 3
|
||||
const double zeroG = 511.5; // 1.65/3.3*1023 (1.65V)
|
||||
accXval = -((double)getSensor(aX) - zeroG);
|
||||
accYval = -((double)getSensor(aY) - zeroG);
|
||||
accZval = -((double)getSensor(aZ) - zeroG);
|
||||
} else if(PS3MoveConnected) {
|
||||
// It's a Kionix KXSC4 inside the Motion controller
|
||||
const uint16_t zeroG = 0x8000;
|
||||
accXval = -(int16_t)(getSensor(aXmove) - zeroG);
|
||||
accYval = (int16_t)(getSensor(aYmove) - zeroG);
|
||||
accZval = (int16_t)(getSensor(aZmove) - zeroG);
|
||||
} else
|
||||
return 0;
|
||||
|
||||
// Convert to 360 degrees resolution
|
||||
// atan2 outputs the value of -π to π (radians)
|
||||
// We are then converting it to 0 to 2π and then to degrees
|
||||
if(a == Pitch)
|
||||
return (atan2(accYval, accZval) + PI) * RAD_TO_DEG;
|
||||
else
|
||||
return (atan2(accXval, accZval) + PI) * RAD_TO_DEG;
|
||||
}
|
||||
|
||||
double PS3BT::get9DOFValues(SensorEnum a) { // Thanks to Manfred Piendl
|
||||
if(!PS3MoveConnected)
|
||||
return 0;
|
||||
int16_t value = getSensor(a);
|
||||
if(a == mXmove || a == mYmove || a == mZmove) {
|
||||
if(value > 2047)
|
||||
value -= 0x1000;
|
||||
return (double)value / 3.2; // unit: muT = 10^(-6) Tesla
|
||||
} else if(a == aXmove || a == aYmove || a == aZmove) {
|
||||
if(value < 0)
|
||||
value += 0x8000;
|
||||
else
|
||||
value -= 0x8000;
|
||||
return (double)value / 442.0; // unit: m/(s^2)
|
||||
} else if(a == gXmove || a == gYmove || a == gZmove) {
|
||||
if(value < 0)
|
||||
value += 0x8000;
|
||||
else
|
||||
value -= 0x8000;
|
||||
if(a == gXmove)
|
||||
return (double)value / 11.6; // unit: deg/s
|
||||
else if(a == gYmove)
|
||||
return (double)value / 11.2; // unit: deg/s
|
||||
else // gZmove
|
||||
return (double)value / 9.6; // unit: deg/s
|
||||
} else
|
||||
return 0;
|
||||
}
|
||||
|
||||
String PS3BT::getTemperature() {
|
||||
if(PS3MoveConnected) {
|
||||
int16_t input = getSensor(tempMove);
|
||||
|
||||
String output = String(input / 100);
|
||||
output += ".";
|
||||
if(input % 100 < 10)
|
||||
output += "0";
|
||||
output += String(input % 100);
|
||||
|
||||
return output;
|
||||
} else
|
||||
return "Error";
|
||||
}
|
||||
|
||||
bool PS3BT::getStatus(StatusEnum c) {
|
||||
return (l2capinbuf[(uint16_t)c >> 8] == ((uint8_t)c & 0xff));
|
||||
}
|
||||
|
||||
void PS3BT::printStatusString() {
|
||||
char statusOutput[100]; // Max string length plus null character
|
||||
if(PS3Connected || PS3NavigationConnected) {
|
||||
strcpy_P(statusOutput, PSTR("ConnectionStatus: "));
|
||||
|
||||
if(getStatus(Plugged)) strcat_P(statusOutput, PSTR("Plugged"));
|
||||
else if(getStatus(Unplugged)) strcat_P(statusOutput, PSTR("Unplugged"));
|
||||
else strcat_P(statusOutput, PSTR("Error"));
|
||||
|
||||
strcat_P(statusOutput, PSTR(" - PowerRating: "));
|
||||
|
||||
if(getStatus(Charging)) strcat_P(statusOutput, PSTR("Charging"));
|
||||
else if(getStatus(NotCharging)) strcat_P(statusOutput, PSTR("Not Charging"));
|
||||
else if(getStatus(Shutdown)) strcat_P(statusOutput, PSTR("Shutdown"));
|
||||
else if(getStatus(Dying)) strcat_P(statusOutput, PSTR("Dying"));
|
||||
else if(getStatus(Low)) strcat_P(statusOutput, PSTR("Low"));
|
||||
else if(getStatus(High)) strcat_P(statusOutput, PSTR("High"));
|
||||
else if(getStatus(Full)) strcat_P(statusOutput, PSTR("Full"));
|
||||
else strcat_P(statusOutput, PSTR("Error"));
|
||||
|
||||
strcat_P(statusOutput, PSTR(" - WirelessStatus: "));
|
||||
|
||||
if(getStatus(CableRumble)) strcat_P(statusOutput, PSTR("Cable - Rumble is on"));
|
||||
else if(getStatus(Cable)) strcat_P(statusOutput, PSTR("Cable - Rumble is off"));
|
||||
else if(getStatus(BluetoothRumble)) strcat_P(statusOutput, PSTR("Bluetooth - Rumble is on"));
|
||||
else if(getStatus(Bluetooth)) strcat_P(statusOutput, PSTR("Bluetooth - Rumble is off"));
|
||||
else strcat_P(statusOutput, PSTR("Error"));
|
||||
} else if(PS3MoveConnected) {
|
||||
strcpy_P(statusOutput, PSTR("PowerRating: "));
|
||||
|
||||
if(getStatus(MoveCharging)) strcat_P(statusOutput, PSTR("Charging"));
|
||||
else if(getStatus(MoveNotCharging)) strcat_P(statusOutput, PSTR("Not Charging"));
|
||||
else if(getStatus(MoveShutdown)) strcat_P(statusOutput, PSTR("Shutdown"));
|
||||
else if(getStatus(MoveDying)) strcat_P(statusOutput, PSTR("Dying"));
|
||||
else if(getStatus(MoveLow)) strcat_P(statusOutput, PSTR("Low"));
|
||||
else if(getStatus(MoveHigh)) strcat_P(statusOutput, PSTR("High"));
|
||||
else if(getStatus(MoveFull)) strcat_P(statusOutput, PSTR("Full"));
|
||||
else strcat_P(statusOutput, PSTR("Error"));
|
||||
} else
|
||||
strcpy_P(statusOutput, PSTR("Error"));
|
||||
|
||||
USB_HOST_SERIAL.write(statusOutput);
|
||||
}
|
||||
|
||||
void PS3BT::Reset() {
|
||||
PS3Connected = false;
|
||||
PS3MoveConnected = false;
|
||||
PS3NavigationConnected = false;
|
||||
activeConnection = false;
|
||||
l2cap_event_flag = 0; // Reset flags
|
||||
l2cap_state = L2CAP_WAIT;
|
||||
|
||||
// Needed for PS3 Dualshock Controller commands to work via Bluetooth
|
||||
for(uint8_t i = 0; i < PS3_REPORT_BUFFER_SIZE; i++)
|
||||
HIDBuffer[i + 2] = pgm_read_byte(&PS3_REPORT_BUFFER[i]); // First two bytes reserved for report type and ID
|
||||
}
|
||||
|
||||
void PS3BT::disconnect() { // Use this void to disconnect any of the controllers
|
||||
// First the HID interrupt channel has to be disconnected, then the HID control channel and finally the HCI connection
|
||||
pBtd->l2cap_disconnection_request(hci_handle, ++identifier, interrupt_scid, interrupt_dcid);
|
||||
Reset();
|
||||
l2cap_state = L2CAP_INTERRUPT_DISCONNECT;
|
||||
}
|
||||
|
||||
void PS3BT::ACLData(uint8_t* ACLData) {
|
||||
if(!pBtd->l2capConnectionClaimed && !PS3Connected && !PS3MoveConnected && !PS3NavigationConnected && !activeConnection && !pBtd->connectToWii && !pBtd->incomingWii && !pBtd->pairWithWii) {
|
||||
if(ACLData[8] == L2CAP_CMD_CONNECTION_REQUEST) {
|
||||
if((ACLData[12] | (ACLData[13] << 8)) == HID_CTRL_PSM) {
|
||||
pBtd->l2capConnectionClaimed = true; // Claim that the incoming connection belongs to this service
|
||||
activeConnection = true;
|
||||
hci_handle = pBtd->hci_handle; // Store the HCI Handle for the connection
|
||||
l2cap_state = L2CAP_WAIT;
|
||||
remote_name_first = pBtd->remote_name[0]; // Store the first letter in remote name for the connection
|
||||
#ifdef DEBUG_USB_HOST
|
||||
if(pBtd->hci_version < 3) { // Check the HCI Version of the Bluetooth dongle
|
||||
Notify(PSTR("\r\nYour dongle may not support reading the analog buttons, sensors and status\r\nYour HCI Version is: "), 0x80);
|
||||
Notify(pBtd->hci_version, 0x80);
|
||||
Notify(PSTR("\r\nBut should be at least 3\r\nThis means that it doesn't support Bluetooth Version 2.0+EDR"), 0x80);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(checkHciHandle(ACLData, hci_handle)) { // acl_handle_ok
|
||||
memcpy(l2capinbuf, ACLData, BULK_MAXPKTSIZE);
|
||||
if((l2capinbuf[6] | (l2capinbuf[7] << 8)) == 0x0001U) { // l2cap_control - Channel ID for ACL-U
|
||||
if(l2capinbuf[8] == L2CAP_CMD_COMMAND_REJECT) {
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nL2CAP Command Rejected - Reason: "), 0x80);
|
||||
D_PrintHex<uint8_t > (l2capinbuf[13], 0x80);
|
||||
Notify(PSTR(" "), 0x80);
|
||||
D_PrintHex<uint8_t > (l2capinbuf[12], 0x80);
|
||||
Notify(PSTR(" Data: "), 0x80);
|
||||
D_PrintHex<uint8_t > (l2capinbuf[17], 0x80);
|
||||
Notify(PSTR(" "), 0x80);
|
||||
D_PrintHex<uint8_t > (l2capinbuf[16], 0x80);
|
||||
Notify(PSTR(" "), 0x80);
|
||||
D_PrintHex<uint8_t > (l2capinbuf[15], 0x80);
|
||||
Notify(PSTR(" "), 0x80);
|
||||
D_PrintHex<uint8_t > (l2capinbuf[14], 0x80);
|
||||
#endif
|
||||
} else if(l2capinbuf[8] == L2CAP_CMD_CONNECTION_REQUEST) {
|
||||
#ifdef EXTRADEBUG
|
||||
Notify(PSTR("\r\nL2CAP Connection Request - PSM: "), 0x80);
|
||||
D_PrintHex<uint8_t > (l2capinbuf[13], 0x80);
|
||||
Notify(PSTR(" "), 0x80);
|
||||
D_PrintHex<uint8_t > (l2capinbuf[12], 0x80);
|
||||
Notify(PSTR(" SCID: "), 0x80);
|
||||
D_PrintHex<uint8_t > (l2capinbuf[15], 0x80);
|
||||
Notify(PSTR(" "), 0x80);
|
||||
D_PrintHex<uint8_t > (l2capinbuf[14], 0x80);
|
||||
Notify(PSTR(" Identifier: "), 0x80);
|
||||
D_PrintHex<uint8_t > (l2capinbuf[9], 0x80);
|
||||
#endif
|
||||
if((l2capinbuf[12] | (l2capinbuf[13] << 8)) == HID_CTRL_PSM) {
|
||||
identifier = l2capinbuf[9];
|
||||
control_scid[0] = l2capinbuf[14];
|
||||
control_scid[1] = l2capinbuf[15];
|
||||
l2cap_set_flag(L2CAP_FLAG_CONNECTION_CONTROL_REQUEST);
|
||||
} else if((l2capinbuf[12] | (l2capinbuf[13] << 8)) == HID_INTR_PSM) {
|
||||
identifier = l2capinbuf[9];
|
||||
interrupt_scid[0] = l2capinbuf[14];
|
||||
interrupt_scid[1] = l2capinbuf[15];
|
||||
l2cap_set_flag(L2CAP_FLAG_CONNECTION_INTERRUPT_REQUEST);
|
||||
}
|
||||
} else if(l2capinbuf[8] == L2CAP_CMD_CONFIG_RESPONSE) {
|
||||
if((l2capinbuf[16] | (l2capinbuf[17] << 8)) == 0x0000) { // Success
|
||||
if(l2capinbuf[12] == control_dcid[0] && l2capinbuf[13] == control_dcid[1]) {
|
||||
//Notify(PSTR("\r\nHID Control Configuration Complete"), 0x80);
|
||||
l2cap_set_flag(L2CAP_FLAG_CONFIG_CONTROL_SUCCESS);
|
||||
} else if(l2capinbuf[12] == interrupt_dcid[0] && l2capinbuf[13] == interrupt_dcid[1]) {
|
||||
//Notify(PSTR("\r\nHID Interrupt Configuration Complete"), 0x80);
|
||||
l2cap_set_flag(L2CAP_FLAG_CONFIG_INTERRUPT_SUCCESS);
|
||||
}
|
||||
}
|
||||
} else if(l2capinbuf[8] == L2CAP_CMD_CONFIG_REQUEST) {
|
||||
if(l2capinbuf[12] == control_dcid[0] && l2capinbuf[13] == control_dcid[1]) {
|
||||
//Notify(PSTR("\r\nHID Control Configuration Request"), 0x80);
|
||||
pBtd->l2cap_config_response(hci_handle, l2capinbuf[9], control_scid);
|
||||
} else if(l2capinbuf[12] == interrupt_dcid[0] && l2capinbuf[13] == interrupt_dcid[1]) {
|
||||
//Notify(PSTR("\r\nHID Interrupt Configuration Request"), 0x80);
|
||||
pBtd->l2cap_config_response(hci_handle, l2capinbuf[9], interrupt_scid);
|
||||
}
|
||||
} else if(l2capinbuf[8] == L2CAP_CMD_DISCONNECT_REQUEST) {
|
||||
if(l2capinbuf[12] == control_dcid[0] && l2capinbuf[13] == control_dcid[1]) {
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nDisconnect Request: Control Channel"), 0x80);
|
||||
#endif
|
||||
identifier = l2capinbuf[9];
|
||||
pBtd->l2cap_disconnection_response(hci_handle, identifier, control_dcid, control_scid);
|
||||
Reset();
|
||||
} else if(l2capinbuf[12] == interrupt_dcid[0] && l2capinbuf[13] == interrupt_dcid[1]) {
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nDisconnect Request: Interrupt Channel"), 0x80);
|
||||
#endif
|
||||
identifier = l2capinbuf[9];
|
||||
pBtd->l2cap_disconnection_response(hci_handle, identifier, interrupt_dcid, interrupt_scid);
|
||||
Reset();
|
||||
}
|
||||
} else if(l2capinbuf[8] == L2CAP_CMD_DISCONNECT_RESPONSE) {
|
||||
if(l2capinbuf[12] == control_scid[0] && l2capinbuf[13] == control_scid[1]) {
|
||||
//Notify(PSTR("\r\nDisconnect Response: Control Channel"), 0x80);
|
||||
identifier = l2capinbuf[9];
|
||||
l2cap_set_flag(L2CAP_FLAG_DISCONNECT_CONTROL_RESPONSE);
|
||||
} else if(l2capinbuf[12] == interrupt_scid[0] && l2capinbuf[13] == interrupt_scid[1]) {
|
||||
//Notify(PSTR("\r\nDisconnect Response: Interrupt Channel"), 0x80);
|
||||
identifier = l2capinbuf[9];
|
||||
l2cap_set_flag(L2CAP_FLAG_DISCONNECT_INTERRUPT_RESPONSE);
|
||||
}
|
||||
}
|
||||
#ifdef EXTRADEBUG
|
||||
else {
|
||||
Notify(PSTR("\r\nL2CAP Unknown Signaling Command: "), 0x80);
|
||||
D_PrintHex<uint8_t > (l2capinbuf[8], 0x80);
|
||||
}
|
||||
#endif
|
||||
} else if(l2capinbuf[6] == interrupt_dcid[0] && l2capinbuf[7] == interrupt_dcid[1]) { // l2cap_interrupt
|
||||
//Notify(PSTR("\r\nL2CAP Interrupt"), 0x80);
|
||||
if(PS3Connected || PS3MoveConnected || PS3NavigationConnected) {
|
||||
/* Read Report */
|
||||
if(l2capinbuf[8] == 0xA1) { // HID_THDR_DATA_INPUT
|
||||
lastMessageTime = millis(); // Store the last message time
|
||||
|
||||
if(PS3Connected || PS3NavigationConnected)
|
||||
ButtonState = (uint32_t)(l2capinbuf[11] | ((uint16_t)l2capinbuf[12] << 8) | ((uint32_t)l2capinbuf[13] << 16));
|
||||
else if(PS3MoveConnected)
|
||||
ButtonState = (uint32_t)(l2capinbuf[10] | ((uint16_t)l2capinbuf[11] << 8) | ((uint32_t)l2capinbuf[12] << 16));
|
||||
|
||||
//Notify(PSTR("\r\nButtonState", 0x80);
|
||||
//PrintHex<uint32_t>(ButtonState, 0x80);
|
||||
|
||||
if(ButtonState != OldButtonState) {
|
||||
ButtonClickState = ButtonState & ~OldButtonState; // Update click state variable
|
||||
OldButtonState = ButtonState;
|
||||
}
|
||||
|
||||
#ifdef PRINTREPORT // Uncomment "#define PRINTREPORT" to print the report send by the PS3 Controllers
|
||||
for(uint8_t i = 10; i < 58; i++) {
|
||||
D_PrintHex<uint8_t > (l2capinbuf[i], 0x80);
|
||||
Notify(PSTR(" "), 0x80);
|
||||
}
|
||||
Notify(PSTR("\r\n"), 0x80);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
L2CAP_task();
|
||||
}
|
||||
}
|
||||
|
||||
void PS3BT::L2CAP_task() {
|
||||
switch(l2cap_state) {
|
||||
case L2CAP_WAIT:
|
||||
if(l2cap_check_flag(L2CAP_FLAG_CONNECTION_CONTROL_REQUEST)) {
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nHID Control Incoming Connection Request"), 0x80);
|
||||
#endif
|
||||
pBtd->l2cap_connection_response(hci_handle, identifier, control_dcid, control_scid, PENDING);
|
||||
delay(1);
|
||||
pBtd->l2cap_connection_response(hci_handle, identifier, control_dcid, control_scid, SUCCESSFUL);
|
||||
identifier++;
|
||||
delay(1);
|
||||
pBtd->l2cap_config_request(hci_handle, identifier, control_scid);
|
||||
l2cap_state = L2CAP_CONTROL_SUCCESS;
|
||||
}
|
||||
break;
|
||||
|
||||
case L2CAP_CONTROL_SUCCESS:
|
||||
if(l2cap_check_flag(L2CAP_FLAG_CONFIG_CONTROL_SUCCESS)) {
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nHID Control Successfully Configured"), 0x80);
|
||||
#endif
|
||||
l2cap_state = L2CAP_INTERRUPT_SETUP;
|
||||
}
|
||||
break;
|
||||
|
||||
case L2CAP_INTERRUPT_SETUP:
|
||||
if(l2cap_check_flag(L2CAP_FLAG_CONNECTION_INTERRUPT_REQUEST)) {
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nHID Interrupt Incoming Connection Request"), 0x80);
|
||||
#endif
|
||||
pBtd->l2cap_connection_response(hci_handle, identifier, interrupt_dcid, interrupt_scid, PENDING);
|
||||
delay(1);
|
||||
pBtd->l2cap_connection_response(hci_handle, identifier, interrupt_dcid, interrupt_scid, SUCCESSFUL);
|
||||
identifier++;
|
||||
delay(1);
|
||||
pBtd->l2cap_config_request(hci_handle, identifier, interrupt_scid);
|
||||
|
||||
l2cap_state = L2CAP_INTERRUPT_CONFIG_REQUEST;
|
||||
}
|
||||
break;
|
||||
|
||||
case L2CAP_INTERRUPT_CONFIG_REQUEST:
|
||||
if(l2cap_check_flag(L2CAP_FLAG_CONFIG_INTERRUPT_SUCCESS)) { // Now the HID channels is established
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nHID Interrupt Successfully Configured"), 0x80);
|
||||
#endif
|
||||
if(remote_name_first == 'M') { // First letter in Motion Controller ('M')
|
||||
memset(l2capinbuf, 0, BULK_MAXPKTSIZE); // Reset l2cap in buffer as it sometimes read it as a button has been pressed
|
||||
l2cap_state = TURN_ON_LED;
|
||||
} else
|
||||
l2cap_state = PS3_ENABLE_SIXAXIS;
|
||||
timer = millis();
|
||||
}
|
||||
break;
|
||||
|
||||
/* These states are handled in Run() */
|
||||
|
||||
case L2CAP_INTERRUPT_DISCONNECT:
|
||||
if(l2cap_check_flag(L2CAP_FLAG_DISCONNECT_INTERRUPT_RESPONSE)) {
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nDisconnected Interrupt Channel"), 0x80);
|
||||
#endif
|
||||
identifier++;
|
||||
pBtd->l2cap_disconnection_request(hci_handle, identifier, control_scid, control_dcid);
|
||||
l2cap_state = L2CAP_CONTROL_DISCONNECT;
|
||||
}
|
||||
break;
|
||||
|
||||
case L2CAP_CONTROL_DISCONNECT:
|
||||
if(l2cap_check_flag(L2CAP_FLAG_DISCONNECT_CONTROL_RESPONSE)) {
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nDisconnected Control Channel"), 0x80);
|
||||
#endif
|
||||
pBtd->hci_disconnect(hci_handle);
|
||||
hci_handle = -1; // Reset handle
|
||||
l2cap_event_flag = 0; // Reset flags
|
||||
l2cap_state = L2CAP_WAIT;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void PS3BT::Run() {
|
||||
switch(l2cap_state) {
|
||||
case PS3_ENABLE_SIXAXIS:
|
||||
if(millis() - timer > 1000) { // loop 1 second before sending the command
|
||||
memset(l2capinbuf, 0, BULK_MAXPKTSIZE); // Reset l2cap in buffer as it sometimes read it as a button has been pressed
|
||||
for(uint8_t i = 15; i < 19; i++)
|
||||
l2capinbuf[i] = 0x7F; // Set the analog joystick values to center position
|
||||
enable_sixaxis();
|
||||
l2cap_state = TURN_ON_LED;
|
||||
timer = millis();
|
||||
}
|
||||
break;
|
||||
|
||||
case TURN_ON_LED:
|
||||
if(millis() - timer > 1000) { // loop 1 second before sending the command
|
||||
if(remote_name_first == 'P') { // First letter in PLAYSTATION(R)3 Controller ('P')
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nDualshock 3 Controller Enabled\r\n"), 0x80);
|
||||
#endif
|
||||
PS3Connected = true;
|
||||
} else if(remote_name_first == 'N') { // First letter in Navigation Controller ('N')
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nNavigation Controller Enabled\r\n"), 0x80);
|
||||
#endif
|
||||
PS3NavigationConnected = true;
|
||||
} else if(remote_name_first == 'M') { // First letter in Motion Controller ('M')
|
||||
timer = millis();
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nMotion Controller Enabled\r\n"), 0x80);
|
||||
#endif
|
||||
PS3MoveConnected = true;
|
||||
}
|
||||
ButtonState = 0; // Clear all values
|
||||
OldButtonState = 0;
|
||||
ButtonClickState = 0;
|
||||
|
||||
onInit(); // Turn on the LED on the controller
|
||||
l2cap_state = L2CAP_DONE;
|
||||
}
|
||||
break;
|
||||
|
||||
case L2CAP_DONE:
|
||||
if(PS3MoveConnected) { // The Bulb and rumble values, has to be send at approximately every 5th second for it to stay on
|
||||
if(millis() - timer > 4000) { // Send at least every 4th second
|
||||
HIDMove_Command(HIDMoveBuffer, HID_BUFFERSIZE); // The Bulb and rumble values, has to be written again and again, for it to stay turned on
|
||||
timer = millis();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************/
|
||||
/* HID Commands */
|
||||
/************************************************************/
|
||||
|
||||
// Playstation Sixaxis Dualshock and Navigation Controller commands
|
||||
|
||||
void PS3BT::HID_Command(uint8_t* data, uint8_t nbytes) {
|
||||
if(millis() - timerHID <= 150) // Check if is has been more than 150ms since last command
|
||||
delay((uint32_t)(150 - (millis() - timerHID))); // There have to be a delay between commands
|
||||
pBtd->L2CAP_Command(hci_handle, data, nbytes, control_scid[0], control_scid[1]); // Both the Navigation and Dualshock controller sends data via the control channel
|
||||
timerHID = millis();
|
||||
}
|
||||
|
||||
void PS3BT::setAllOff() {
|
||||
HIDBuffer[3] = 0x00; // Rumble bytes
|
||||
HIDBuffer[4] = 0x00;
|
||||
HIDBuffer[5] = 0x00;
|
||||
HIDBuffer[6] = 0x00;
|
||||
|
||||
HIDBuffer[11] = 0x00; // LED byte
|
||||
|
||||
HID_Command(HIDBuffer, HID_BUFFERSIZE);
|
||||
}
|
||||
|
||||
void PS3BT::setRumbleOff() {
|
||||
HIDBuffer[3] = 0x00;
|
||||
HIDBuffer[4] = 0x00;
|
||||
HIDBuffer[5] = 0x00;
|
||||
HIDBuffer[6] = 0x00;
|
||||
|
||||
HID_Command(HIDBuffer, HID_BUFFERSIZE);
|
||||
}
|
||||
|
||||
void PS3BT::setRumbleOn(RumbleEnum mode) {
|
||||
uint8_t power[2] = {0xff, 0x00}; // Defaults to RumbleLow
|
||||
if(mode == RumbleHigh) {
|
||||
power[0] = 0x00;
|
||||
power[1] = 0xff;
|
||||
}
|
||||
setRumbleOn(0xfe, power[0], 0xfe, power[1]);
|
||||
}
|
||||
|
||||
void PS3BT::setRumbleOn(uint8_t rightDuration, uint8_t rightPower, uint8_t leftDuration, uint8_t leftPower) {
|
||||
HIDBuffer[3] = rightDuration;
|
||||
HIDBuffer[4] = rightPower;
|
||||
HIDBuffer[5] = leftDuration;
|
||||
HIDBuffer[6] = leftPower;
|
||||
HID_Command(HIDBuffer, HID_BUFFERSIZE);
|
||||
}
|
||||
|
||||
void PS3BT::setLedRaw(uint8_t value) {
|
||||
HIDBuffer[11] = value << 1;
|
||||
HID_Command(HIDBuffer, HID_BUFFERSIZE);
|
||||
}
|
||||
|
||||
void PS3BT::setLedOff(LEDEnum a) {
|
||||
HIDBuffer[11] &= ~((uint8_t)((pgm_read_byte(&PS3_LEDS[(uint8_t)a]) & 0x0f) << 1));
|
||||
HID_Command(HIDBuffer, HID_BUFFERSIZE);
|
||||
}
|
||||
|
||||
void PS3BT::setLedOn(LEDEnum a) {
|
||||
if(a == OFF)
|
||||
setLedRaw(0);
|
||||
else {
|
||||
HIDBuffer[11] |= (uint8_t)((pgm_read_byte(&PS3_LEDS[(uint8_t)a]) & 0x0f) << 1);
|
||||
HID_Command(HIDBuffer, HID_BUFFERSIZE);
|
||||
}
|
||||
}
|
||||
|
||||
void PS3BT::setLedToggle(LEDEnum a) {
|
||||
HIDBuffer[11] ^= (uint8_t)((pgm_read_byte(&PS3_LEDS[(uint8_t)a]) & 0x0f) << 1);
|
||||
HID_Command(HIDBuffer, HID_BUFFERSIZE);
|
||||
}
|
||||
|
||||
void PS3BT::enable_sixaxis() { // Command used to enable the Dualshock 3 and Navigation controller to send data via Bluetooth
|
||||
uint8_t cmd_buf[6];
|
||||
cmd_buf[0] = 0x53; // HID BT Set_report (0x50) | Report Type (Feature 0x03)
|
||||
cmd_buf[1] = 0xF4; // Report ID
|
||||
cmd_buf[2] = 0x42; // Special PS3 Controller enable commands
|
||||
cmd_buf[3] = 0x03;
|
||||
cmd_buf[4] = 0x00;
|
||||
cmd_buf[5] = 0x00;
|
||||
|
||||
HID_Command(cmd_buf, 6);
|
||||
}
|
||||
|
||||
// Playstation Move Controller commands
|
||||
|
||||
void PS3BT::HIDMove_Command(uint8_t* data, uint8_t nbytes) {
|
||||
if(millis() - timerHID <= 150)// Check if is has been less than 150ms since last command
|
||||
delay((uint32_t)(150 - (millis() - timerHID))); // There have to be a delay between commands
|
||||
pBtd->L2CAP_Command(hci_handle, data, nbytes, interrupt_scid[0], interrupt_scid[1]); // The Move controller sends it's data via the intterrupt channel
|
||||
timerHID = millis();
|
||||
}
|
||||
|
||||
void PS3BT::moveSetBulb(uint8_t r, uint8_t g, uint8_t b) { // Use this to set the Color using RGB values
|
||||
// Set the Bulb's values into the write buffer
|
||||
HIDMoveBuffer[3] = r;
|
||||
HIDMoveBuffer[4] = g;
|
||||
HIDMoveBuffer[5] = b;
|
||||
|
||||
HIDMove_Command(HIDMoveBuffer, HID_BUFFERSIZE);
|
||||
}
|
||||
|
||||
void PS3BT::moveSetBulb(ColorsEnum color) { // Use this to set the Color using the predefined colors in enum
|
||||
moveSetBulb((uint8_t)(color >> 16), (uint8_t)(color >> 8), (uint8_t)(color));
|
||||
}
|
||||
|
||||
void PS3BT::moveSetRumble(uint8_t rumble) {
|
||||
#ifdef DEBUG_USB_HOST
|
||||
if(rumble < 64 && rumble != 0) // The rumble value has to at least 64, or approximately 25% (64/255*100)
|
||||
Notify(PSTR("\r\nThe rumble value has to at least 64, or approximately 25%"), 0x80);
|
||||
#endif
|
||||
// Set the rumble value into the write buffer
|
||||
HIDMoveBuffer[7] = rumble;
|
||||
|
||||
HIDMove_Command(HIDMoveBuffer, HID_BUFFERSIZE);
|
||||
}
|
||||
|
||||
void PS3BT::onInit() {
|
||||
if(pFuncOnInit)
|
||||
pFuncOnInit(); // Call the user function
|
||||
else {
|
||||
if(PS3MoveConnected)
|
||||
moveSetBulb(Red);
|
||||
else // Dualshock 3 or Navigation controller
|
||||
setLedOn(LED1);
|
||||
}
|
||||
}
|
||||
@@ -1,240 +0,0 @@
|
||||
/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved.
|
||||
|
||||
This software may be distributed and modified under the terms of the GNU
|
||||
General Public License version 2 (GPL2) as published by the Free Software
|
||||
Foundation and appearing in the file GPL2.TXT included in the packaging of
|
||||
this file. Please note that GPL2 Section 2[b] requires that all works based
|
||||
on this software must also be made publicly available under the terms of
|
||||
the GPL2 ("Copyleft").
|
||||
|
||||
Contact information
|
||||
-------------------
|
||||
|
||||
Kristian Lauszus, TKJ Electronics
|
||||
Web : http://www.tkjelectronics.com
|
||||
e-mail : kristianl@tkjelectronics.com
|
||||
*/
|
||||
|
||||
#ifndef _ps3bt_h_
|
||||
#define _ps3bt_h_
|
||||
|
||||
#include "BTD.h"
|
||||
#include "PS3Enums.h"
|
||||
|
||||
#define HID_BUFFERSIZE 50 // Size of the buffer for the Playstation Motion Controller
|
||||
|
||||
/**
|
||||
* This BluetoothService class implements support for all the official PS3 Controllers:
|
||||
* Dualshock 3, Navigation or a Motion controller via Bluetooth.
|
||||
*
|
||||
* Information about the protocol can be found at the wiki: https://github.com/felis/USB_Host_Shield_2.0/wiki/PS3-Information.
|
||||
*/
|
||||
class PS3BT : public BluetoothService {
|
||||
public:
|
||||
/**
|
||||
* Constructor for the PS3BT class.
|
||||
* @param pBtd Pointer to BTD class instance.
|
||||
* @param btadr5,btadr4,btadr3,btadr2,btadr1,btadr0
|
||||
* Pass your dongles Bluetooth address into the constructor,
|
||||
* This will set BTD#my_bdaddr, so you don't have to plug in the dongle before pairing with your controller.
|
||||
*/
|
||||
PS3BT(BTD *pBtd, uint8_t btadr5 = 0, uint8_t btadr4 = 0, uint8_t btadr3 = 0, uint8_t btadr2 = 0, uint8_t btadr1 = 0, uint8_t btadr0 = 0);
|
||||
|
||||
/** @name BluetoothService implementation */
|
||||
/** Used this to disconnect any of the controllers. */
|
||||
void disconnect();
|
||||
/**@}*/
|
||||
|
||||
/** @name PS3 Controller functions */
|
||||
/**
|
||||
* getButtonPress(ButtonEnum b) will return true as long as the button is held down.
|
||||
*
|
||||
* While getButtonClick(ButtonEnum b) will only return it once.
|
||||
*
|
||||
* So you instance if you need to increase a variable once you would use getButtonClick(ButtonEnum b),
|
||||
* but if you need to drive a robot forward you would use getButtonPress(ButtonEnum b).
|
||||
* @param b ::ButtonEnum to read.
|
||||
* @return getButtonPress(ButtonEnum b) will return a true as long as a button is held down, while getButtonClick(ButtonEnum b) will return true once for each button press.
|
||||
*/
|
||||
bool getButtonPress(ButtonEnum b);
|
||||
bool getButtonClick(ButtonEnum b);
|
||||
/**@}*/
|
||||
/** @name PS3 Controller functions */
|
||||
/**
|
||||
* Used to get the analog value from button presses.
|
||||
* @param a The ::ButtonEnum to read.
|
||||
* The supported buttons are:
|
||||
* ::UP, ::RIGHT, ::DOWN, ::LEFT, ::L1, ::L2, ::R1, ::R2,
|
||||
* ::TRIANGLE, ::CIRCLE, ::CROSS, ::SQUARE, and ::T.
|
||||
* @return Analog value in the range of 0-255.
|
||||
*/
|
||||
uint8_t getAnalogButton(ButtonEnum a);
|
||||
/**
|
||||
* Used to read the analog joystick.
|
||||
* @param a ::LeftHatX, ::LeftHatY, ::RightHatX, and ::RightHatY.
|
||||
* @return Return the analog value in the range of 0-255.
|
||||
*/
|
||||
uint8_t getAnalogHat(AnalogHatEnum a);
|
||||
/**
|
||||
* Used to read the sensors inside the Dualshock 3 and Move controller.
|
||||
* @param a
|
||||
* The Dualshock 3 has a 3-axis accelerometer and a 1-axis gyro inside.
|
||||
* The Move controller has a 3-axis accelerometer, a 3-axis gyro, a 3-axis magnetometer
|
||||
* and a temperature sensor inside.
|
||||
* @return Return the raw sensor value.
|
||||
*/
|
||||
int16_t getSensor(SensorEnum a);
|
||||
/**
|
||||
* Use this to get ::Pitch and ::Roll calculated using the accelerometer.
|
||||
* @param a Either ::Pitch or ::Roll.
|
||||
* @return Return the angle in the range of 0-360.
|
||||
*/
|
||||
double getAngle(AngleEnum a);
|
||||
/**
|
||||
* Read the sensors inside the Move controller.
|
||||
* @param a ::aXmove, ::aYmove, ::aZmove, ::gXmove, ::gYmove, ::gZmove, ::mXmove, ::mYmove, and ::mXmove.
|
||||
* @return The value in SI units.
|
||||
*/
|
||||
double get9DOFValues(SensorEnum a);
|
||||
/**
|
||||
* Get the status from the controller.
|
||||
* @param c The ::StatusEnum you want to read.
|
||||
* @return True if correct and false if not.
|
||||
*/
|
||||
bool getStatus(StatusEnum c);
|
||||
/** Read all the available statuses from the controller and prints it as a nice formated string. */
|
||||
void printStatusString();
|
||||
/**
|
||||
* Read the temperature from the Move controller.
|
||||
* @return The temperature in degrees Celsius.
|
||||
*/
|
||||
String getTemperature();
|
||||
|
||||
/** Used to set all LEDs and rumble off. */
|
||||
void setAllOff();
|
||||
/** Turn off rumble. */
|
||||
void setRumbleOff();
|
||||
/**
|
||||
* Turn on rumble.
|
||||
* @param mode Either ::RumbleHigh or ::RumbleLow.
|
||||
*/
|
||||
void setRumbleOn(RumbleEnum mode);
|
||||
/**
|
||||
* Turn on rumble using custom duration and power.
|
||||
* @param rightDuration The duration of the right/low rumble effect.
|
||||
* @param rightPower The intensity of the right/low rumble effect.
|
||||
* @param leftDuration The duration of the left/high rumble effect.
|
||||
* @param leftPower The intensity of the left/high rumble effect.
|
||||
*/
|
||||
void setRumbleOn(uint8_t rightDuration, uint8_t rightPower, uint8_t leftDuration, uint8_t leftPower);
|
||||
|
||||
/**
|
||||
* Set LED value without using ::LEDEnum.
|
||||
* @param value See: ::LEDEnum.
|
||||
*/
|
||||
void setLedRaw(uint8_t value);
|
||||
|
||||
/** Turn all LEDs off. */
|
||||
void setLedOff() {
|
||||
setLedRaw(0);
|
||||
};
|
||||
/**
|
||||
* Turn the specific LED off.
|
||||
* @param a The ::LEDEnum to turn off.
|
||||
*/
|
||||
void setLedOff(LEDEnum a);
|
||||
/**
|
||||
* Turn the specific LED on.
|
||||
* @param a The ::LEDEnum to turn on.
|
||||
*/
|
||||
void setLedOn(LEDEnum a);
|
||||
/**
|
||||
* Toggle the specific LED.
|
||||
* @param a The ::LEDEnum to toggle.
|
||||
*/
|
||||
void setLedToggle(LEDEnum a);
|
||||
|
||||
/**
|
||||
* Use this to set the Color using RGB values.
|
||||
* @param r,g,b RGB value.
|
||||
*/
|
||||
void moveSetBulb(uint8_t r, uint8_t g, uint8_t b);
|
||||
/**
|
||||
* Use this to set the color using the predefined colors in ::ColorsEnum.
|
||||
* @param color The desired color.
|
||||
*/
|
||||
void moveSetBulb(ColorsEnum color);
|
||||
/**
|
||||
* Set the rumble value inside the Move controller.
|
||||
* @param rumble The desired value in the range from 64-255.
|
||||
*/
|
||||
void moveSetRumble(uint8_t rumble);
|
||||
|
||||
/** Used to get the millis() of the last message */
|
||||
uint32_t getLastMessageTime() {
|
||||
return lastMessageTime;
|
||||
};
|
||||
/**@}*/
|
||||
|
||||
/** Variable used to indicate if the normal Playstation controller is successfully connected. */
|
||||
bool PS3Connected;
|
||||
/** Variable used to indicate if the Move controller is successfully connected. */
|
||||
bool PS3MoveConnected;
|
||||
/** Variable used to indicate if the Navigation controller is successfully connected. */
|
||||
bool PS3NavigationConnected;
|
||||
|
||||
protected:
|
||||
/** @name BluetoothService implementation */
|
||||
/**
|
||||
* Used to pass acldata to the services.
|
||||
* @param ACLData Incoming acldata.
|
||||
*/
|
||||
void ACLData(uint8_t* ACLData);
|
||||
/** Used to run part of the state machine. */
|
||||
void Run();
|
||||
/** Use this to reset the service. */
|
||||
void Reset();
|
||||
/**
|
||||
* Called when the controller is successfully initialized.
|
||||
* Use attachOnInit(void (*funcOnInit)(void)) to call your own function.
|
||||
* This is useful for instance if you want to set the LEDs in a specific way.
|
||||
*/
|
||||
void onInit();
|
||||
/**@}*/
|
||||
|
||||
private:
|
||||
|
||||
void L2CAP_task(); // L2CAP state machine
|
||||
|
||||
/* Variables filled from HCI event management */
|
||||
char remote_name_first; // First letter in remote name
|
||||
bool activeConnection; // Used to indicate if it's already has established a connection
|
||||
|
||||
/* Variables used by high level L2CAP task */
|
||||
uint8_t l2cap_state;
|
||||
|
||||
uint32_t lastMessageTime; // Variable used to store the millis value of the last message.
|
||||
|
||||
uint32_t ButtonState;
|
||||
uint32_t OldButtonState;
|
||||
uint32_t ButtonClickState;
|
||||
|
||||
uint32_t timer; // Timer used to limit time between messages and also used to continuously set PS3 Move controller Bulb and rumble values
|
||||
uint32_t timerHID; // Timer used see if there has to be a delay before a new HID command
|
||||
|
||||
uint8_t l2capinbuf[BULK_MAXPKTSIZE]; // General purpose buffer for L2CAP in data
|
||||
uint8_t HIDBuffer[HID_BUFFERSIZE]; // Used to store HID commands
|
||||
uint8_t HIDMoveBuffer[HID_BUFFERSIZE]; // Used to store HID commands for the Move controller
|
||||
|
||||
/* L2CAP Channels */
|
||||
uint8_t control_scid[2]; // L2CAP source CID for HID_Control
|
||||
uint8_t control_dcid[2]; // 0x0040
|
||||
uint8_t interrupt_scid[2]; // L2CAP source CID for HID_Interrupt
|
||||
uint8_t interrupt_dcid[2]; // 0x0041
|
||||
|
||||
/* HID Commands */
|
||||
void HID_Command(uint8_t* data, uint8_t nbytes);
|
||||
void HIDMove_Command(uint8_t* data, uint8_t nbytes);
|
||||
void enable_sixaxis(); // Command used to enable the Dualshock 3 and Navigation controller to send data via Bluetooth
|
||||
};
|
||||
#endif
|
||||
@@ -1,141 +0,0 @@
|
||||
/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved.
|
||||
|
||||
This software may be distributed and modified under the terms of the GNU
|
||||
General Public License version 2 (GPL2) as published by the Free Software
|
||||
Foundation and appearing in the file GPL2.TXT included in the packaging of
|
||||
this file. Please note that GPL2 Section 2[b] requires that all works based
|
||||
on this software must also be made publicly available under the terms of
|
||||
the GPL2 ("Copyleft").
|
||||
|
||||
Contact information
|
||||
-------------------
|
||||
|
||||
Kristian Lauszus, TKJ Electronics
|
||||
Web : http://www.tkjelectronics.com
|
||||
e-mail : kristianl@tkjelectronics.com
|
||||
*/
|
||||
|
||||
#ifndef _ps3enums_h
|
||||
#define _ps3enums_h
|
||||
|
||||
#include "controllerEnums.h"
|
||||
|
||||
/** Size of the output report buffer for the Dualshock and Navigation controllers */
|
||||
#define PS3_REPORT_BUFFER_SIZE 48
|
||||
|
||||
/** Report buffer for all PS3 commands */
|
||||
const uint8_t PS3_REPORT_BUFFER[PS3_REPORT_BUFFER_SIZE] PROGMEM = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xff, 0x27, 0x10, 0x00, 0x32,
|
||||
0xff, 0x27, 0x10, 0x00, 0x32,
|
||||
0xff, 0x27, 0x10, 0x00, 0x32,
|
||||
0xff, 0x27, 0x10, 0x00, 0x32,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
/** Size of the output report buffer for the Move Controller */
|
||||
#define MOVE_REPORT_BUFFER_SIZE 7
|
||||
|
||||
/** Used to set the LEDs on the controllers */
|
||||
const uint8_t PS3_LEDS[] PROGMEM = {
|
||||
0x00, // OFF
|
||||
0x01, // LED1
|
||||
0x02, // LED2
|
||||
0x04, // LED3
|
||||
0x08, // LED4
|
||||
|
||||
0x09, // LED5
|
||||
0x0A, // LED6
|
||||
0x0C, // LED7
|
||||
0x0D, // LED8
|
||||
0x0E, // LED9
|
||||
0x0F, // LED10
|
||||
};
|
||||
|
||||
/**
|
||||
* Buttons on the controllers.
|
||||
* <B>Note:</B> that the location is shifted 9 when it's connected via USB.
|
||||
*/
|
||||
const uint32_t PS3_BUTTONS[] PROGMEM = {
|
||||
0x10, // UP
|
||||
0x20, // RIGHT
|
||||
0x40, // DOWN
|
||||
0x80, // LEFT
|
||||
|
||||
0x01, // SELECT
|
||||
0x08, // START
|
||||
0x02, // L3
|
||||
0x04, // R3
|
||||
|
||||
0x0100, // L2
|
||||
0x0200, // R2
|
||||
0x0400, // L1
|
||||
0x0800, // R1
|
||||
|
||||
0x1000, // TRIANGLE
|
||||
0x2000, // CIRCLE
|
||||
0x4000, // CROSS
|
||||
0x8000, // SQUARE
|
||||
|
||||
0x010000, // PS
|
||||
0x080000, // MOVE - covers 12 bits - we only need to read the top 8
|
||||
0x100000, // T - covers 12 bits - we only need to read the top 8
|
||||
};
|
||||
|
||||
/**
|
||||
* Analog buttons on the controllers.
|
||||
* <B>Note:</B> that the location is shifted 9 when it's connected via USB.
|
||||
*/
|
||||
const uint8_t PS3_ANALOG_BUTTONS[] PROGMEM = {
|
||||
23, // UP_ANALOG
|
||||
24, // RIGHT_ANALOG
|
||||
25, // DOWN_ANALOG
|
||||
26, // LEFT_ANALOG
|
||||
0, 0, 0, 0, // Skip SELECT, L3, R3 and START
|
||||
|
||||
27, // L2_ANALOG
|
||||
28, // R2_ANALOG
|
||||
29, // L1_ANALOG
|
||||
30, // R1_ANALOG
|
||||
31, // TRIANGLE_ANALOG
|
||||
32, // CIRCLE_ANALOG
|
||||
33, // CROSS_ANALOG
|
||||
34, // SQUARE_ANALOG
|
||||
0, 0, // Skip PS and MOVE
|
||||
|
||||
// Playstation Move Controller
|
||||
15, // T_ANALOG - Both at byte 14 (last reading) and byte 15 (current reading)
|
||||
};
|
||||
|
||||
enum StatusEnum {
|
||||
// Note that the location is shifted 9 when it's connected via USB
|
||||
// Byte location | bit location
|
||||
Plugged = (38 << 8) | 0x02,
|
||||
Unplugged = (38 << 8) | 0x03,
|
||||
|
||||
Charging = (39 << 8) | 0xEE,
|
||||
NotCharging = (39 << 8) | 0xF1,
|
||||
Shutdown = (39 << 8) | 0x01,
|
||||
Dying = (39 << 8) | 0x02,
|
||||
Low = (39 << 8) | 0x03,
|
||||
High = (39 << 8) | 0x04,
|
||||
Full = (39 << 8) | 0x05,
|
||||
|
||||
MoveCharging = (21 << 8) | 0xEE,
|
||||
MoveNotCharging = (21 << 8) | 0xF1,
|
||||
MoveShutdown = (21 << 8) | 0x01,
|
||||
MoveDying = (21 << 8) | 0x02,
|
||||
MoveLow = (21 << 8) | 0x03,
|
||||
MoveHigh = (21 << 8) | 0x04,
|
||||
MoveFull = (21 << 8) | 0x05,
|
||||
|
||||
CableRumble = (40 << 8) | 0x10, // Operating by USB and rumble is turned on
|
||||
Cable = (40 << 8) | 0x12, // Operating by USB and rumble is turned off
|
||||
BluetoothRumble = (40 << 8) | 0x14, // Operating by Bluetooth and rumble is turned on
|
||||
Bluetooth = (40 << 8) | 0x16, // Operating by Bluetooth and rumble is turned off
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,572 +0,0 @@
|
||||
/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved.
|
||||
|
||||
This software may be distributed and modified under the terms of the GNU
|
||||
General Public License version 2 (GPL2) as published by the Free Software
|
||||
Foundation and appearing in the file GPL2.TXT included in the packaging of
|
||||
this file. Please note that GPL2 Section 2[b] requires that all works based
|
||||
on this software must also be made publicly available under the terms of
|
||||
the GPL2 ("Copyleft").
|
||||
|
||||
Contact information
|
||||
-------------------
|
||||
|
||||
Kristian Lauszus, TKJ Electronics
|
||||
Web : http://www.tkjelectronics.com
|
||||
e-mail : kristianl@tkjelectronics.com
|
||||
*/
|
||||
|
||||
#include "PS3USB.h"
|
||||
// To enable serial debugging see "settings.h"
|
||||
//#define EXTRADEBUG // Uncomment to get even more debugging data
|
||||
//#define PRINTREPORT // Uncomment to print the report send by the PS3 Controllers
|
||||
|
||||
PS3USB::PS3USB(USB *p, uint8_t btadr5, uint8_t btadr4, uint8_t btadr3, uint8_t btadr2, uint8_t btadr1, uint8_t btadr0) :
|
||||
pUsb(p), // pointer to USB class instance - mandatory
|
||||
bAddress(0), // device address - mandatory
|
||||
bPollEnable(false) // don't start polling before dongle is connected
|
||||
{
|
||||
for(uint8_t i = 0; i < PS3_MAX_ENDPOINTS; i++) {
|
||||
epInfo[i].epAddr = 0;
|
||||
epInfo[i].maxPktSize = (i) ? 0 : 8;
|
||||
epInfo[i].epAttribs = 0;
|
||||
epInfo[i].bmNakPower = (i) ? USB_NAK_NOWAIT : USB_NAK_MAX_POWER;
|
||||
}
|
||||
|
||||
if(pUsb) // register in USB subsystem
|
||||
pUsb->RegisterDeviceClass(this); //set devConfig[] entry
|
||||
|
||||
my_bdaddr[5] = btadr5; // Change to your dongle's Bluetooth address instead
|
||||
my_bdaddr[4] = btadr4;
|
||||
my_bdaddr[3] = btadr3;
|
||||
my_bdaddr[2] = btadr2;
|
||||
my_bdaddr[1] = btadr1;
|
||||
my_bdaddr[0] = btadr0;
|
||||
}
|
||||
|
||||
uint8_t PS3USB::Init(uint8_t parent, uint8_t port, bool lowspeed) {
|
||||
uint8_t buf[sizeof (USB_DEVICE_DESCRIPTOR)];
|
||||
USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast<USB_DEVICE_DESCRIPTOR*>(buf);
|
||||
uint8_t rcode;
|
||||
UsbDevice *p = NULL;
|
||||
EpInfo *oldep_ptr = NULL;
|
||||
uint16_t PID;
|
||||
uint16_t VID;
|
||||
|
||||
// get memory address of USB device address pool
|
||||
AddressPool &addrPool = pUsb->GetAddressPool();
|
||||
#ifdef EXTRADEBUG
|
||||
Notify(PSTR("\r\nPS3USB Init"), 0x80);
|
||||
#endif
|
||||
// check if address has already been assigned to an instance
|
||||
if(bAddress) {
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nAddress in use"), 0x80);
|
||||
#endif
|
||||
return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE;
|
||||
}
|
||||
|
||||
// Get pointer to pseudo device with address 0 assigned
|
||||
p = addrPool.GetUsbDevicePtr(0);
|
||||
|
||||
if(!p) {
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nAddress not found"), 0x80);
|
||||
#endif
|
||||
return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
|
||||
}
|
||||
|
||||
if(!p->epinfo) {
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nepinfo is null"), 0x80);
|
||||
#endif
|
||||
return USB_ERROR_EPINFO_IS_NULL;
|
||||
}
|
||||
|
||||
// Save old pointer to EP_RECORD of address 0
|
||||
oldep_ptr = p->epinfo;
|
||||
|
||||
// Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence
|
||||
p->epinfo = epInfo;
|
||||
|
||||
p->lowspeed = lowspeed;
|
||||
|
||||
// Get device descriptor
|
||||
rcode = pUsb->getDevDescr(0, 0, sizeof (USB_DEVICE_DESCRIPTOR), (uint8_t*)buf); // Get device descriptor - addr, ep, nbytes, data
|
||||
// Restore p->epinfo
|
||||
p->epinfo = oldep_ptr;
|
||||
|
||||
if(rcode)
|
||||
goto FailGetDevDescr;
|
||||
|
||||
VID = udd->idVendor;
|
||||
PID = udd->idProduct;
|
||||
|
||||
if(VID != PS3_VID || (PID != PS3_PID && PID != PS3NAVIGATION_PID && PID != PS3MOVE_PID))
|
||||
goto FailUnknownDevice;
|
||||
|
||||
// Allocate new address according to device class
|
||||
bAddress = addrPool.AllocAddress(parent, false, port);
|
||||
|
||||
if(!bAddress)
|
||||
return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL;
|
||||
|
||||
// Extract Max Packet Size from device descriptor
|
||||
epInfo[0].maxPktSize = udd->bMaxPacketSize0;
|
||||
|
||||
// Assign new address to the device
|
||||
rcode = pUsb->setAddr(0, 0, bAddress);
|
||||
if(rcode) {
|
||||
p->lowspeed = false;
|
||||
addrPool.FreeAddress(bAddress);
|
||||
bAddress = 0;
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nsetAddr: "), 0x80);
|
||||
D_PrintHex<uint8_t > (rcode, 0x80);
|
||||
#endif
|
||||
return rcode;
|
||||
}
|
||||
#ifdef EXTRADEBUG
|
||||
Notify(PSTR("\r\nAddr: "), 0x80);
|
||||
D_PrintHex<uint8_t > (bAddress, 0x80);
|
||||
#endif
|
||||
//delay(300); // Spec says you should wait at least 200ms
|
||||
|
||||
p->lowspeed = false;
|
||||
|
||||
//get pointer to assigned address record
|
||||
p = addrPool.GetUsbDevicePtr(bAddress);
|
||||
if(!p)
|
||||
return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
|
||||
|
||||
p->lowspeed = lowspeed;
|
||||
|
||||
// Assign epInfo to epinfo pointer - only EP0 is known
|
||||
rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo);
|
||||
if(rcode)
|
||||
goto FailSetDevTblEntry;
|
||||
|
||||
|
||||
/* The application will work in reduced host mode, so we can save program and data
|
||||
memory space. After verifying the PID and VID we will use known values for the
|
||||
configuration values for device, interface, endpoints and HID for the PS3 Controllers */
|
||||
|
||||
/* Initialize data structures for endpoints of device */
|
||||
epInfo[ PS3_OUTPUT_PIPE ].epAddr = 0x02; // PS3 output endpoint
|
||||
epInfo[ PS3_OUTPUT_PIPE ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT;
|
||||
epInfo[ PS3_OUTPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
|
||||
epInfo[ PS3_OUTPUT_PIPE ].maxPktSize = EP_MAXPKTSIZE;
|
||||
epInfo[ PS3_OUTPUT_PIPE ].bmSndToggle = 0;
|
||||
epInfo[ PS3_OUTPUT_PIPE ].bmRcvToggle = 0;
|
||||
epInfo[ PS3_INPUT_PIPE ].epAddr = 0x01; // PS3 report endpoint
|
||||
epInfo[ PS3_INPUT_PIPE ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT;
|
||||
epInfo[ PS3_INPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
|
||||
epInfo[ PS3_INPUT_PIPE ].maxPktSize = EP_MAXPKTSIZE;
|
||||
epInfo[ PS3_INPUT_PIPE ].bmSndToggle = 0;
|
||||
epInfo[ PS3_INPUT_PIPE ].bmRcvToggle = 0;
|
||||
|
||||
rcode = pUsb->setEpInfoEntry(bAddress, 3, epInfo);
|
||||
if(rcode)
|
||||
goto FailSetDevTblEntry;
|
||||
|
||||
delay(200); //Give time for address change
|
||||
|
||||
rcode = pUsb->setConf(bAddress, epInfo[ PS3_CONTROL_PIPE ].epAddr, 1);
|
||||
if(rcode)
|
||||
goto FailSetConfDescr;
|
||||
|
||||
if(PID == PS3_PID || PID == PS3NAVIGATION_PID) {
|
||||
if(PID == PS3_PID) {
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nDualshock 3 Controller Connected"), 0x80);
|
||||
#endif
|
||||
PS3Connected = true;
|
||||
} else { // must be a navigation controller
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nNavigation Controller Connected"), 0x80);
|
||||
#endif
|
||||
PS3NavigationConnected = true;
|
||||
}
|
||||
enable_sixaxis(); // The PS3 controller needs a special command before it starts sending data
|
||||
|
||||
// Needed for PS3 Dualshock and Navigation commands to work
|
||||
for(uint8_t i = 0; i < PS3_REPORT_BUFFER_SIZE; i++)
|
||||
writeBuf[i] = pgm_read_byte(&PS3_REPORT_BUFFER[i]);
|
||||
|
||||
for(uint8_t i = 6; i < 10; i++)
|
||||
readBuf[i] = 0x7F; // Set the analog joystick values to center position
|
||||
} else { // must be a Motion controller
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nMotion Controller Connected"), 0x80);
|
||||
#endif
|
||||
PS3MoveConnected = true;
|
||||
writeBuf[0] = 0x02; // Set report ID, this is needed for Move commands to work
|
||||
}
|
||||
if(my_bdaddr[0] != 0x00 || my_bdaddr[1] != 0x00 || my_bdaddr[2] != 0x00 || my_bdaddr[3] != 0x00 || my_bdaddr[4] != 0x00 || my_bdaddr[5] != 0x00) {
|
||||
if(PS3MoveConnected)
|
||||
setMoveBdaddr(my_bdaddr); // Set internal Bluetooth address
|
||||
else
|
||||
setBdaddr(my_bdaddr); // Set internal Bluetooth address
|
||||
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nBluetooth Address was set to: "), 0x80);
|
||||
for(int8_t i = 5; i > 0; i--) {
|
||||
D_PrintHex<uint8_t > (my_bdaddr[i], 0x80);
|
||||
Notify(PSTR(":"), 0x80);
|
||||
}
|
||||
D_PrintHex<uint8_t > (my_bdaddr[0], 0x80);
|
||||
#endif
|
||||
}
|
||||
onInit();
|
||||
|
||||
bPollEnable = true;
|
||||
Notify(PSTR("\r\n"), 0x80);
|
||||
timer = millis();
|
||||
return 0; // Successful configuration
|
||||
|
||||
/* Diagnostic messages */
|
||||
FailGetDevDescr:
|
||||
#ifdef DEBUG_USB_HOST
|
||||
NotifyFailGetDevDescr();
|
||||
goto Fail;
|
||||
#endif
|
||||
|
||||
FailSetDevTblEntry:
|
||||
#ifdef DEBUG_USB_HOST
|
||||
NotifyFailSetDevTblEntry();
|
||||
goto Fail;
|
||||
#endif
|
||||
|
||||
FailSetConfDescr:
|
||||
#ifdef DEBUG_USB_HOST
|
||||
NotifyFailSetConfDescr();
|
||||
#endif
|
||||
goto Fail;
|
||||
|
||||
FailUnknownDevice:
|
||||
#ifdef DEBUG_USB_HOST
|
||||
NotifyFailUnknownDevice(VID, PID);
|
||||
#endif
|
||||
rcode = USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED;
|
||||
|
||||
Fail:
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nPS3 Init Failed, error code: "), 0x80);
|
||||
NotifyFail(rcode);
|
||||
#endif
|
||||
Release();
|
||||
return rcode;
|
||||
}
|
||||
|
||||
/* Performs a cleanup after failed Init() attempt */
|
||||
uint8_t PS3USB::Release() {
|
||||
PS3Connected = false;
|
||||
PS3MoveConnected = false;
|
||||
PS3NavigationConnected = false;
|
||||
pUsb->GetAddressPool().FreeAddress(bAddress);
|
||||
bAddress = 0;
|
||||
bPollEnable = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t PS3USB::Poll() {
|
||||
if(!bPollEnable)
|
||||
return 0;
|
||||
|
||||
if(PS3Connected || PS3NavigationConnected) {
|
||||
uint16_t BUFFER_SIZE = EP_MAXPKTSIZE;
|
||||
pUsb->inTransfer(bAddress, epInfo[ PS3_INPUT_PIPE ].epAddr, &BUFFER_SIZE, readBuf); // input on endpoint 1
|
||||
if(millis() - timer > 100) { // Loop 100ms before processing data
|
||||
readReport();
|
||||
#ifdef PRINTREPORT
|
||||
printReport(); // Uncomment "#define PRINTREPORT" to print the report send by the PS3 Controllers
|
||||
#endif
|
||||
}
|
||||
} else if(PS3MoveConnected) { // One can only set the color of the bulb, set the rumble, set and get the bluetooth address and calibrate the magnetometer via USB
|
||||
if(millis() - timer > 4000) { // Send at least every 4th second
|
||||
Move_Command(writeBuf, MOVE_REPORT_BUFFER_SIZE); // The Bulb and rumble values, has to be written again and again, for it to stay turned on
|
||||
timer = millis();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void PS3USB::readReport() {
|
||||
ButtonState = (uint32_t)(readBuf[2] | ((uint16_t)readBuf[3] << 8) | ((uint32_t)readBuf[4] << 16));
|
||||
|
||||
//Notify(PSTR("\r\nButtonState", 0x80);
|
||||
//PrintHex<uint32_t>(ButtonState, 0x80);
|
||||
|
||||
if(ButtonState != OldButtonState) {
|
||||
ButtonClickState = ButtonState & ~OldButtonState; // Update click state variable
|
||||
OldButtonState = ButtonState;
|
||||
}
|
||||
}
|
||||
|
||||
void PS3USB::printReport() { // Uncomment "#define PRINTREPORT" to print the report send by the PS3 Controllers
|
||||
#ifdef PRINTREPORT
|
||||
for(uint8_t i = 0; i < PS3_REPORT_BUFFER_SIZE; i++) {
|
||||
D_PrintHex<uint8_t > (readBuf[i], 0x80);
|
||||
Notify(PSTR(" "), 0x80);
|
||||
}
|
||||
Notify(PSTR("\r\n"), 0x80);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool PS3USB::getButtonPress(ButtonEnum b) {
|
||||
return (ButtonState & pgm_read_dword(&PS3_BUTTONS[(uint8_t)b]));
|
||||
}
|
||||
|
||||
bool PS3USB::getButtonClick(ButtonEnum b) {
|
||||
uint32_t button = pgm_read_dword(&PS3_BUTTONS[(uint8_t)b]);
|
||||
bool click = (ButtonClickState & button);
|
||||
ButtonClickState &= ~button; // Clear "click" event
|
||||
return click;
|
||||
}
|
||||
|
||||
uint8_t PS3USB::getAnalogButton(ButtonEnum a) {
|
||||
return (uint8_t)(readBuf[(pgm_read_byte(&PS3_ANALOG_BUTTONS[(uint8_t)a])) - 9]);
|
||||
}
|
||||
|
||||
uint8_t PS3USB::getAnalogHat(AnalogHatEnum a) {
|
||||
return (uint8_t)(readBuf[((uint8_t)a + 6)]);
|
||||
}
|
||||
|
||||
uint16_t PS3USB::getSensor(SensorEnum a) {
|
||||
return ((readBuf[((uint16_t)a) - 9] << 8) | readBuf[((uint16_t)a + 1) - 9]);
|
||||
}
|
||||
|
||||
double PS3USB::getAngle(AngleEnum a) {
|
||||
if(PS3Connected) {
|
||||
double accXval;
|
||||
double accYval;
|
||||
double accZval;
|
||||
|
||||
// Data for the Kionix KXPC4 used in the DualShock 3
|
||||
const double zeroG = 511.5; // 1.65/3.3*1023 (1,65V)
|
||||
accXval = -((double)getSensor(aX) - zeroG);
|
||||
accYval = -((double)getSensor(aY) - zeroG);
|
||||
accZval = -((double)getSensor(aZ) - zeroG);
|
||||
|
||||
// Convert to 360 degrees resolution
|
||||
// atan2 outputs the value of -π to π (radians)
|
||||
// We are then converting it to 0 to 2π and then to degrees
|
||||
if(a == Pitch)
|
||||
return (atan2(accYval, accZval) + PI) * RAD_TO_DEG;
|
||||
else
|
||||
return (atan2(accXval, accZval) + PI) * RAD_TO_DEG;
|
||||
} else
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool PS3USB::getStatus(StatusEnum c) {
|
||||
return (readBuf[((uint16_t)c >> 8) - 9] == ((uint8_t)c & 0xff));
|
||||
}
|
||||
|
||||
void PS3USB::printStatusString() {
|
||||
char statusOutput[100]; // Max string length plus null character
|
||||
if(PS3Connected || PS3NavigationConnected) {
|
||||
strcpy_P(statusOutput, PSTR("ConnectionStatus: "));
|
||||
|
||||
if(getStatus(Plugged)) strcat_P(statusOutput, PSTR("Plugged"));
|
||||
else if(getStatus(Unplugged)) strcat_P(statusOutput, PSTR("Unplugged"));
|
||||
else strcat_P(statusOutput, PSTR("Error"));
|
||||
|
||||
strcat_P(statusOutput, PSTR(" - PowerRating: "));
|
||||
|
||||
if(getStatus(Charging)) strcat_P(statusOutput, PSTR("Charging"));
|
||||
else if(getStatus(NotCharging)) strcat_P(statusOutput, PSTR("Not Charging"));
|
||||
else if(getStatus(Shutdown)) strcat_P(statusOutput, PSTR("Shutdown"));
|
||||
else if(getStatus(Dying)) strcat_P(statusOutput, PSTR("Dying"));
|
||||
else if(getStatus(Low)) strcat_P(statusOutput, PSTR("Low"));
|
||||
else if(getStatus(High)) strcat_P(statusOutput, PSTR("High"));
|
||||
else if(getStatus(Full)) strcat_P(statusOutput, PSTR("Full"));
|
||||
else strcat_P(statusOutput, PSTR("Error"));
|
||||
|
||||
strcat_P(statusOutput, PSTR(" - WirelessStatus: "));
|
||||
|
||||
if(getStatus(CableRumble)) strcat_P(statusOutput, PSTR("Cable - Rumble is on"));
|
||||
else if(getStatus(Cable)) strcat_P(statusOutput, PSTR("Cable - Rumble is off"));
|
||||
else if(getStatus(BluetoothRumble)) strcat_P(statusOutput, PSTR("Bluetooth - Rumble is on"));
|
||||
else if(getStatus(Bluetooth)) strcat_P(statusOutput, PSTR("Bluetooth - Rumble is off"));
|
||||
else strcat_P(statusOutput, PSTR("Error"));
|
||||
} else
|
||||
strcpy_P(statusOutput, PSTR("Error"));
|
||||
|
||||
USB_HOST_SERIAL.write(statusOutput);
|
||||
}
|
||||
|
||||
/* Playstation Sixaxis Dualshock and Navigation Controller commands */
|
||||
void PS3USB::PS3_Command(uint8_t *data, uint16_t nbytes) {
|
||||
// bmRequest = Host to device (0x00) | Class (0x20) | Interface (0x01) = 0x21, bRequest = Set Report (0x09), Report ID (0x01), Report Type (Output 0x02), interface (0x00), datalength, datalength, data)
|
||||
pUsb->ctrlReq(bAddress, epInfo[PS3_CONTROL_PIPE].epAddr, bmREQ_HID_OUT, HID_REQUEST_SET_REPORT, 0x01, 0x02, 0x00, nbytes, nbytes, data, NULL);
|
||||
}
|
||||
|
||||
void PS3USB::setAllOff() {
|
||||
for(uint8_t i = 0; i < PS3_REPORT_BUFFER_SIZE; i++)
|
||||
writeBuf[i] = pgm_read_byte(&PS3_REPORT_BUFFER[i]); // Reset buffer
|
||||
|
||||
PS3_Command(writeBuf, PS3_REPORT_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
void PS3USB::setRumbleOff() {
|
||||
writeBuf[1] = 0x00;
|
||||
writeBuf[2] = 0x00; // Low mode off
|
||||
writeBuf[3] = 0x00;
|
||||
writeBuf[4] = 0x00; // High mode off
|
||||
|
||||
PS3_Command(writeBuf, PS3_REPORT_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
void PS3USB::setRumbleOn(RumbleEnum mode) {
|
||||
if((mode & 0x30) > 0x00) {
|
||||
uint8_t power[2] = {0xff, 0x00}; // Defaults to RumbleLow
|
||||
if(mode == RumbleHigh) {
|
||||
power[0] = 0x00;
|
||||
power[1] = 0xff;
|
||||
}
|
||||
setRumbleOn(0xfe, power[0], 0xfe, power[1]);
|
||||
}
|
||||
}
|
||||
|
||||
void PS3USB::setRumbleOn(uint8_t rightDuration, uint8_t rightPower, uint8_t leftDuration, uint8_t leftPower) {
|
||||
writeBuf[1] = rightDuration;
|
||||
writeBuf[2] = rightPower;
|
||||
writeBuf[3] = leftDuration;
|
||||
writeBuf[4] = leftPower;
|
||||
PS3_Command(writeBuf, PS3_REPORT_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
void PS3USB::setLedRaw(uint8_t value) {
|
||||
writeBuf[9] = value << 1;
|
||||
PS3_Command(writeBuf, PS3_REPORT_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
void PS3USB::setLedOff(LEDEnum a) {
|
||||
writeBuf[9] &= ~((uint8_t)((pgm_read_byte(&PS3_LEDS[(uint8_t)a]) & 0x0f) << 1));
|
||||
PS3_Command(writeBuf, PS3_REPORT_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
void PS3USB::setLedOn(LEDEnum a) {
|
||||
if(a == OFF)
|
||||
setLedRaw(0);
|
||||
else {
|
||||
writeBuf[9] |= (uint8_t)((pgm_read_byte(&PS3_LEDS[(uint8_t)a]) & 0x0f) << 1);
|
||||
PS3_Command(writeBuf, PS3_REPORT_BUFFER_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
void PS3USB::setLedToggle(LEDEnum a) {
|
||||
writeBuf[9] ^= (uint8_t)((pgm_read_byte(&PS3_LEDS[(uint8_t)a]) & 0x0f) << 1);
|
||||
PS3_Command(writeBuf, PS3_REPORT_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
void PS3USB::setBdaddr(uint8_t *bdaddr) {
|
||||
/* Set the internal Bluetooth address */
|
||||
uint8_t buf[8];
|
||||
buf[0] = 0x01;
|
||||
buf[1] = 0x00;
|
||||
|
||||
for(uint8_t i = 0; i < 6; i++)
|
||||
buf[i + 2] = bdaddr[5 - i]; // Copy into buffer, has to be written reversed, so it is MSB first
|
||||
|
||||
// bmRequest = Host to device (0x00) | Class (0x20) | Interface (0x01) = 0x21, bRequest = Set Report (0x09), Report ID (0xF5), Report Type (Feature 0x03), interface (0x00), datalength, datalength, data
|
||||
pUsb->ctrlReq(bAddress, epInfo[PS3_CONTROL_PIPE].epAddr, bmREQ_HID_OUT, HID_REQUEST_SET_REPORT, 0xF5, 0x03, 0x00, 8, 8, buf, NULL);
|
||||
}
|
||||
|
||||
void PS3USB::getBdaddr(uint8_t *bdaddr) {
|
||||
uint8_t buf[8];
|
||||
|
||||
// bmRequest = Device to host (0x80) | Class (0x20) | Interface (0x01) = 0xA1, bRequest = Get Report (0x01), Report ID (0xF5), Report Type (Feature 0x03), interface (0x00), datalength, datalength, data
|
||||
pUsb->ctrlReq(bAddress, epInfo[PS3_CONTROL_PIPE].epAddr, bmREQ_HID_IN, HID_REQUEST_GET_REPORT, 0xF5, 0x03, 0x00, 8, 8, buf, NULL);
|
||||
|
||||
for(uint8_t i = 0; i < 6; i++)
|
||||
bdaddr[5 - i] = buf[i + 2]; // Copy into buffer reversed, so it is LSB first
|
||||
}
|
||||
|
||||
void PS3USB::enable_sixaxis() { // Command used to enable the Dualshock 3 and Navigation controller to send data via USB
|
||||
uint8_t cmd_buf[4];
|
||||
cmd_buf[0] = 0x42; // Special PS3 Controller enable commands
|
||||
cmd_buf[1] = 0x0c;
|
||||
cmd_buf[2] = 0x00;
|
||||
cmd_buf[3] = 0x00;
|
||||
|
||||
// bmRequest = Host to device (0x00) | Class (0x20) | Interface (0x01) = 0x21, bRequest = Set Report (0x09), Report ID (0xF4), Report Type (Feature 0x03), interface (0x00), datalength, datalength, data)
|
||||
pUsb->ctrlReq(bAddress, epInfo[PS3_CONTROL_PIPE].epAddr, bmREQ_HID_OUT, HID_REQUEST_SET_REPORT, 0xF4, 0x03, 0x00, 4, 4, cmd_buf, NULL);
|
||||
}
|
||||
|
||||
/* Playstation Move Controller commands */
|
||||
void PS3USB::Move_Command(uint8_t *data, uint16_t nbytes) {
|
||||
pUsb->outTransfer(bAddress, epInfo[ PS3_OUTPUT_PIPE ].epAddr, nbytes, data);
|
||||
}
|
||||
|
||||
void PS3USB::moveSetBulb(uint8_t r, uint8_t g, uint8_t b) { // Use this to set the Color using RGB values
|
||||
// Set the Bulb's values into the write buffer
|
||||
writeBuf[2] = r;
|
||||
writeBuf[3] = g;
|
||||
writeBuf[4] = b;
|
||||
|
||||
Move_Command(writeBuf, MOVE_REPORT_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
void PS3USB::moveSetBulb(ColorsEnum color) { // Use this to set the Color using the predefined colors in "enums.h"
|
||||
moveSetBulb((uint8_t)(color >> 16), (uint8_t)(color >> 8), (uint8_t)(color));
|
||||
}
|
||||
|
||||
void PS3USB::moveSetRumble(uint8_t rumble) {
|
||||
#ifdef DEBUG_USB_HOST
|
||||
if(rumble < 64 && rumble != 0) // The rumble value has to at least 64, or approximately 25% (64/255*100)
|
||||
Notify(PSTR("\r\nThe rumble value has to at least 64, or approximately 25%"), 0x80);
|
||||
#endif
|
||||
writeBuf[6] = rumble; // Set the rumble value into the write buffer
|
||||
|
||||
Move_Command(writeBuf, MOVE_REPORT_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
void PS3USB::setMoveBdaddr(uint8_t *bdaddr) {
|
||||
/* Set the internal Bluetooth address */
|
||||
uint8_t buf[11];
|
||||
buf[0] = 0x05;
|
||||
buf[7] = 0x10;
|
||||
buf[8] = 0x01;
|
||||
buf[9] = 0x02;
|
||||
buf[10] = 0x12;
|
||||
|
||||
for(uint8_t i = 0; i < 6; i++)
|
||||
buf[i + 1] = bdaddr[i];
|
||||
|
||||
// bmRequest = Host to device (0x00) | Class (0x20) | Interface (0x01) = 0x21, bRequest = Set Report (0x09), Report ID (0x05), Report Type (Feature 0x03), interface (0x00), datalength, datalength, data
|
||||
pUsb->ctrlReq(bAddress, epInfo[PS3_CONTROL_PIPE].epAddr, bmREQ_HID_OUT, HID_REQUEST_SET_REPORT, 0x05, 0x03, 0x00, 11, 11, buf, NULL);
|
||||
}
|
||||
|
||||
void PS3USB::getMoveBdaddr(uint8_t *bdaddr) {
|
||||
uint8_t buf[16];
|
||||
|
||||
// bmRequest = Device to host (0x80) | Class (0x20) | Interface (0x01) = 0xA1, bRequest = Get Report (0x01), Report ID (0x04), Report Type (Feature 0x03), interface (0x00), datalength, datalength, data
|
||||
pUsb->ctrlReq(bAddress, epInfo[PS3_CONTROL_PIPE].epAddr, bmREQ_HID_IN, HID_REQUEST_GET_REPORT, 0x04, 0x03, 0x00, 16, 16, buf, NULL);
|
||||
|
||||
for(uint8_t i = 0; i < 6; i++)
|
||||
bdaddr[i] = buf[10 + i];
|
||||
}
|
||||
|
||||
void PS3USB::getMoveCalibration(uint8_t *data) {
|
||||
uint8_t buf[49];
|
||||
|
||||
for(uint8_t i = 0; i < 3; i++) {
|
||||
// bmRequest = Device to host (0x80) | Class (0x20) | Interface (0x01) = 0xA1, bRequest = Get Report (0x01), Report ID (0x10), Report Type (Feature 0x03), interface (0x00), datalength, datalength, data
|
||||
pUsb->ctrlReq(bAddress, epInfo[PS3_CONTROL_PIPE].epAddr, bmREQ_HID_IN, HID_REQUEST_GET_REPORT, 0x10, 0x03, 0x00, 49, 49, buf, NULL);
|
||||
|
||||
for(byte j = 0; j < 49; j++)
|
||||
data[49 * i + j] = buf[j];
|
||||
}
|
||||
}
|
||||
|
||||
void PS3USB::onInit() {
|
||||
if(pFuncOnInit)
|
||||
pFuncOnInit(); // Call the user function
|
||||
else {
|
||||
if(PS3MoveConnected)
|
||||
moveSetBulb(Red);
|
||||
else // Dualshock 3 or Navigation controller
|
||||
setLedOn(LED1);
|
||||
}
|
||||
}
|
||||
@@ -1,303 +0,0 @@
|
||||
/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved.
|
||||
|
||||
This software may be distributed and modified under the terms of the GNU
|
||||
General Public License version 2 (GPL2) as published by the Free Software
|
||||
Foundation and appearing in the file GPL2.TXT included in the packaging of
|
||||
this file. Please note that GPL2 Section 2[b] requires that all works based
|
||||
on this software must also be made publicly available under the terms of
|
||||
the GPL2 ("Copyleft").
|
||||
|
||||
Contact information
|
||||
-------------------
|
||||
|
||||
Kristian Lauszus, TKJ Electronics
|
||||
Web : http://www.tkjelectronics.com
|
||||
e-mail : kristianl@tkjelectronics.com
|
||||
*/
|
||||
|
||||
#ifndef _ps3usb_h_
|
||||
#define _ps3usb_h_
|
||||
|
||||
#include "Usb.h"
|
||||
#include "hid.h"
|
||||
#include "PS3Enums.h"
|
||||
|
||||
/* PS3 data taken from descriptors */
|
||||
#define EP_MAXPKTSIZE 64 // max size for data via USB
|
||||
|
||||
/* Names we give to the 3 ps3 pipes - this is only used for setting the bluetooth address into the ps3 controllers */
|
||||
#define PS3_CONTROL_PIPE 0
|
||||
#define PS3_OUTPUT_PIPE 1
|
||||
#define PS3_INPUT_PIPE 2
|
||||
|
||||
//PID and VID of the different devices
|
||||
#define PS3_VID 0x054C // Sony Corporation
|
||||
#define PS3_PID 0x0268 // PS3 Controller DualShock 3
|
||||
#define PS3NAVIGATION_PID 0x042F // Navigation controller
|
||||
#define PS3MOVE_PID 0x03D5 // Motion controller
|
||||
|
||||
#define PS3_MAX_ENDPOINTS 3
|
||||
|
||||
/**
|
||||
* This class implements support for all the official PS3 Controllers:
|
||||
* Dualshock 3, Navigation or a Motion controller via USB.
|
||||
*
|
||||
* One can only set the color of the bulb, set the rumble, set and get the bluetooth address and calibrate the magnetometer via USB on the Move controller.
|
||||
*
|
||||
* Information about the protocol can be found at the wiki: https://github.com/felis/USB_Host_Shield_2.0/wiki/PS3-Information.
|
||||
*/
|
||||
class PS3USB : public USBDeviceConfig {
|
||||
public:
|
||||
/**
|
||||
* Constructor for the PS3USB class.
|
||||
* @param pUsb Pointer to USB class instance.
|
||||
* @param btadr5,btadr4,btadr3,btadr2,btadr1,btadr0
|
||||
* Pass your dongles Bluetooth address into the constructor,
|
||||
* so you are able to pair the controller with a Bluetooth dongle.
|
||||
*/
|
||||
PS3USB(USB *pUsb, uint8_t btadr5 = 0, uint8_t btadr4 = 0, uint8_t btadr3 = 0, uint8_t btadr2 = 0, uint8_t btadr1 = 0, uint8_t btadr0 = 0);
|
||||
|
||||
/** @name USBDeviceConfig implementation */
|
||||
/**
|
||||
* Initialize the PS3 Controller.
|
||||
* @param parent Hub number.
|
||||
* @param port Port number on the hub.
|
||||
* @param lowspeed Speed of the device.
|
||||
* @return 0 on success.
|
||||
*/
|
||||
uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed);
|
||||
/**
|
||||
* Release the USB device.
|
||||
* @return 0 on success.
|
||||
*/
|
||||
uint8_t Release();
|
||||
/**
|
||||
* Poll the USB Input endpoins and run the state machines.
|
||||
* @return 0 on success.
|
||||
*/
|
||||
uint8_t Poll();
|
||||
|
||||
/**
|
||||
* Get the device address.
|
||||
* @return The device address.
|
||||
*/
|
||||
virtual uint8_t GetAddress() {
|
||||
return bAddress;
|
||||
};
|
||||
|
||||
/**
|
||||
* Used to check if the controller has been initialized.
|
||||
* @return True if it's ready.
|
||||
*/
|
||||
virtual bool isReady() {
|
||||
return bPollEnable;
|
||||
};
|
||||
|
||||
/**
|
||||
* Used by the USB core to check what this driver support.
|
||||
* @param vid The device's VID.
|
||||
* @param pid The device's PID.
|
||||
* @return Returns true if the device's VID and PID matches this driver.
|
||||
*/
|
||||
virtual bool VIDPIDOK(uint16_t vid, uint16_t pid) {
|
||||
return (vid == PS3_VID && (pid == PS3_PID || pid == PS3NAVIGATION_PID || pid == PS3MOVE_PID));
|
||||
};
|
||||
/**@}*/
|
||||
|
||||
/**
|
||||
* Used to set the Bluetooth address inside the Dualshock 3 and Navigation controller.
|
||||
* Set using LSB first.
|
||||
* @param bdaddr Your dongles Bluetooth address.
|
||||
*/
|
||||
void setBdaddr(uint8_t *bdaddr);
|
||||
/**
|
||||
* Used to get the Bluetooth address inside the Dualshock 3 and Navigation controller.
|
||||
* Will return LSB first.
|
||||
* @param bdaddr Your dongles Bluetooth address.
|
||||
*/
|
||||
void getBdaddr(uint8_t *bdaddr);
|
||||
|
||||
/**
|
||||
* Used to set the Bluetooth address inside the Move controller.
|
||||
* Set using LSB first.
|
||||
* @param bdaddr Your dongles Bluetooth address.
|
||||
*/
|
||||
void setMoveBdaddr(uint8_t *bdaddr);
|
||||
/**
|
||||
* Used to get the Bluetooth address inside the Move controller.
|
||||
* Will return LSB first.
|
||||
* @param bdaddr Your dongles Bluetooth address.
|
||||
*/
|
||||
void getMoveBdaddr(uint8_t *bdaddr);
|
||||
/**
|
||||
* Used to get the calibration data inside the Move controller.
|
||||
* @param data Buffer to store data in. Must be at least 147 bytes
|
||||
*/
|
||||
void getMoveCalibration(uint8_t *data);
|
||||
|
||||
/** @name PS3 Controller functions */
|
||||
/**
|
||||
* getButtonPress(ButtonEnum b) will return true as long as the button is held down.
|
||||
*
|
||||
* While getButtonClick(ButtonEnum b) will only return it once.
|
||||
*
|
||||
* So you instance if you need to increase a variable once you would use getButtonClick(ButtonEnum b),
|
||||
* but if you need to drive a robot forward you would use getButtonPress(ButtonEnum b).
|
||||
* @param b ::ButtonEnum to read.
|
||||
* @return getButtonPress(ButtonEnum b) will return a true as long as a button is held down, while getButtonClick(ButtonEnum b) will return true once for each button press.
|
||||
*/
|
||||
bool getButtonPress(ButtonEnum b);
|
||||
bool getButtonClick(ButtonEnum b);
|
||||
/**@}*/
|
||||
/** @name PS3 Controller functions */
|
||||
/**
|
||||
* Used to get the analog value from button presses.
|
||||
* @param a The ::ButtonEnum to read.
|
||||
* The supported buttons are:
|
||||
* ::UP, ::RIGHT, ::DOWN, ::LEFT, ::L1, ::L2, ::R1, ::R2,
|
||||
* ::TRIANGLE, ::CIRCLE, ::CROSS, ::SQUARE, and ::T.
|
||||
* @return Analog value in the range of 0-255.
|
||||
*/
|
||||
uint8_t getAnalogButton(ButtonEnum a);
|
||||
/**
|
||||
* Used to read the analog joystick.
|
||||
* @param a ::LeftHatX, ::LeftHatY, ::RightHatX, and ::RightHatY.
|
||||
* @return Return the analog value in the range of 0-255.
|
||||
*/
|
||||
uint8_t getAnalogHat(AnalogHatEnum a);
|
||||
/**
|
||||
* Used to read the sensors inside the Dualshock 3 controller.
|
||||
* @param a
|
||||
* The Dualshock 3 has a 3-axis accelerometer and a 1-axis gyro inside.
|
||||
* @return Return the raw sensor value.
|
||||
*/
|
||||
uint16_t getSensor(SensorEnum a);
|
||||
/**
|
||||
* Use this to get ::Pitch and ::Roll calculated using the accelerometer.
|
||||
* @param a Either ::Pitch or ::Roll.
|
||||
* @return Return the angle in the range of 0-360.
|
||||
*/
|
||||
double getAngle(AngleEnum a);
|
||||
/**
|
||||
* Get the ::StatusEnum from the controller.
|
||||
* @param c The ::StatusEnum you want to read.
|
||||
* @return True if correct and false if not.
|
||||
*/
|
||||
bool getStatus(StatusEnum c);
|
||||
/** Read all the available statuses from the controller and prints it as a nice formated string. */
|
||||
void printStatusString();
|
||||
|
||||
/** Used to set all LEDs and rumble off. */
|
||||
void setAllOff();
|
||||
/** Turn off rumble. */
|
||||
void setRumbleOff();
|
||||
/**
|
||||
* Turn on rumble.
|
||||
* @param mode Either ::RumbleHigh or ::RumbleLow.
|
||||
*/
|
||||
void setRumbleOn(RumbleEnum mode);
|
||||
/**
|
||||
* Turn on rumble using custom duration and power.
|
||||
* @param rightDuration The duration of the right/low rumble effect.
|
||||
* @param rightPower The intensity of the right/low rumble effect.
|
||||
* @param leftDuration The duration of the left/high rumble effect.
|
||||
* @param leftPower The intensity of the left/high rumble effect.
|
||||
*/
|
||||
void setRumbleOn(uint8_t rightDuration, uint8_t rightPower, uint8_t leftDuration, uint8_t leftPower);
|
||||
|
||||
/**
|
||||
* Set LED value without using the ::LEDEnum.
|
||||
* @param value See: ::LEDEnum.
|
||||
*/
|
||||
void setLedRaw(uint8_t value);
|
||||
|
||||
/** Turn all LEDs off. */
|
||||
void setLedOff() {
|
||||
setLedRaw(0);
|
||||
}
|
||||
/**
|
||||
* Turn the specific ::LEDEnum off.
|
||||
* @param a The ::LEDEnum to turn off.
|
||||
*/
|
||||
void setLedOff(LEDEnum a);
|
||||
/**
|
||||
* Turn the specific ::LEDEnum on.
|
||||
* @param a The ::LEDEnum to turn on.
|
||||
*/
|
||||
void setLedOn(LEDEnum a);
|
||||
/**
|
||||
* Toggle the specific ::LEDEnum.
|
||||
* @param a The ::LEDEnum to toggle.
|
||||
*/
|
||||
void setLedToggle(LEDEnum a);
|
||||
|
||||
/**
|
||||
* Use this to set the Color using RGB values.
|
||||
* @param r,g,b RGB value.
|
||||
*/
|
||||
void moveSetBulb(uint8_t r, uint8_t g, uint8_t b);
|
||||
/**
|
||||
* Use this to set the color using the predefined colors in ::ColorsEnum.
|
||||
* @param color The desired color.
|
||||
*/
|
||||
void moveSetBulb(ColorsEnum color);
|
||||
/**
|
||||
* Set the rumble value inside the Move controller.
|
||||
* @param rumble The desired value in the range from 64-255.
|
||||
*/
|
||||
void moveSetRumble(uint8_t rumble);
|
||||
|
||||
/**
|
||||
* Used to call your own function when the controller is successfully initialized.
|
||||
* @param funcOnInit Function to call.
|
||||
*/
|
||||
void attachOnInit(void (*funcOnInit)(void)) {
|
||||
pFuncOnInit = funcOnInit;
|
||||
};
|
||||
/**@}*/
|
||||
|
||||
/** Variable used to indicate if the normal playstation controller is successfully connected. */
|
||||
bool PS3Connected;
|
||||
/** Variable used to indicate if the move controller is successfully connected. */
|
||||
bool PS3MoveConnected;
|
||||
/** Variable used to indicate if the navigation controller is successfully connected. */
|
||||
bool PS3NavigationConnected;
|
||||
|
||||
protected:
|
||||
/** Pointer to USB class instance. */
|
||||
USB *pUsb;
|
||||
/** Device address. */
|
||||
uint8_t bAddress;
|
||||
/** Endpoint info structure. */
|
||||
EpInfo epInfo[PS3_MAX_ENDPOINTS];
|
||||
|
||||
private:
|
||||
/**
|
||||
* Called when the controller is successfully initialized.
|
||||
* Use attachOnInit(void (*funcOnInit)(void)) to call your own function.
|
||||
* This is useful for instance if you want to set the LEDs in a specific way.
|
||||
*/
|
||||
void onInit();
|
||||
void (*pFuncOnInit)(void); // Pointer to function called in onInit()
|
||||
|
||||
bool bPollEnable;
|
||||
|
||||
uint32_t timer; // used to continuously set PS3 Move controller Bulb and rumble values
|
||||
|
||||
uint32_t ButtonState;
|
||||
uint32_t OldButtonState;
|
||||
uint32_t ButtonClickState;
|
||||
|
||||
uint8_t my_bdaddr[6]; // Change to your dongles Bluetooth address in the constructor
|
||||
uint8_t readBuf[EP_MAXPKTSIZE]; // General purpose buffer for input data
|
||||
uint8_t writeBuf[EP_MAXPKTSIZE]; // General purpose buffer for output data
|
||||
|
||||
void readReport(); // read incoming data
|
||||
void printReport(); // print incoming date - Uncomment for debugging
|
||||
|
||||
/* Private commands */
|
||||
void PS3_Command(uint8_t *data, uint16_t nbytes);
|
||||
void enable_sixaxis(); // Command used to enable the Dualshock 3 and Navigation controller to send data via USB
|
||||
void Move_Command(uint8_t *data, uint16_t nbytes);
|
||||
};
|
||||
#endif
|
||||
@@ -1,121 +0,0 @@
|
||||
/* Copyright (C) 2014 Kristian Lauszus, TKJ Electronics. All rights reserved.
|
||||
|
||||
This software may be distributed and modified under the terms of the GNU
|
||||
General Public License version 2 (GPL2) as published by the Free Software
|
||||
Foundation and appearing in the file GPL2.TXT included in the packaging of
|
||||
this file. Please note that GPL2 Section 2[b] requires that all works based
|
||||
on this software must also be made publicly available under the terms of
|
||||
the GPL2 ("Copyleft").
|
||||
|
||||
Contact information
|
||||
-------------------
|
||||
|
||||
Kristian Lauszus, TKJ Electronics
|
||||
Web : http://www.tkjelectronics.com
|
||||
e-mail : kristianl@tkjelectronics.com
|
||||
*/
|
||||
|
||||
#ifndef _ps4bt_h_
|
||||
#define _ps4bt_h_
|
||||
|
||||
#include "BTHID.h"
|
||||
#include "PS4Parser.h"
|
||||
|
||||
/**
|
||||
* This class implements support for the PS4 controller via Bluetooth.
|
||||
* It uses the BTHID class for all the Bluetooth communication.
|
||||
*/
|
||||
class PS4BT : public BTHID, public PS4Parser {
|
||||
public:
|
||||
/**
|
||||
* Constructor for the PS4BT class.
|
||||
* @param p Pointer to the BTD class instance.
|
||||
* @param pair Set this to true in order to pair with the device. If the argument is omitted then it will not pair with it. One can use ::PAIR to set it to true.
|
||||
* @param pin Write the pin to BTD#btdPin. If argument is omitted, then "0000" will be used.
|
||||
*/
|
||||
PS4BT(BTD *p, bool pair = false, const char *pin = "0000") :
|
||||
BTHID(p, pair, pin) {
|
||||
PS4Parser::Reset();
|
||||
};
|
||||
|
||||
/**
|
||||
* Used to check if a PS4 controller is connected.
|
||||
* @return Returns true if it is connected.
|
||||
*/
|
||||
bool connected() {
|
||||
return BTHID::connected;
|
||||
};
|
||||
|
||||
protected:
|
||||
/** @name BTHID implementation */
|
||||
/**
|
||||
* Used to parse Bluetooth HID data.
|
||||
* @param len The length of the incoming data.
|
||||
* @param buf Pointer to the data buffer.
|
||||
*/
|
||||
virtual void ParseBTHIDData(uint8_t len, uint8_t *buf) {
|
||||
PS4Parser::Parse(len, buf);
|
||||
};
|
||||
|
||||
/**
|
||||
* Called when a device is successfully initialized.
|
||||
* Use attachOnInit(void (*funcOnInit)(void)) to call your own function.
|
||||
* This is useful for instance if you want to set the LEDs in a specific way.
|
||||
*/
|
||||
virtual void OnInitBTHID() {
|
||||
PS4Parser::Reset();
|
||||
enable_sixaxis(); // Make the controller send out the entire output report
|
||||
if (pFuncOnInit)
|
||||
pFuncOnInit(); // Call the user function
|
||||
else
|
||||
setLed(Blue);
|
||||
};
|
||||
|
||||
/** Used to reset the different buffers to there default values */
|
||||
virtual void ResetBTHID() {
|
||||
PS4Parser::Reset();
|
||||
};
|
||||
/**@}*/
|
||||
|
||||
/** @name PS4Parser implementation */
|
||||
virtual void sendOutputReport(PS4Output *output) { // Source: https://github.com/chrippa/ds4drv
|
||||
uint8_t buf[79];
|
||||
memset(buf, 0, sizeof(buf));
|
||||
|
||||
buf[0] = 0x52; // HID BT Set_report (0x50) | Report Type (Output 0x02)
|
||||
buf[1] = 0x11; // Report ID
|
||||
buf[2] = 0x80;
|
||||
buf[4]= 0xFF;
|
||||
|
||||
buf[7] = output->smallRumble; // Small Rumble
|
||||
buf[8] = output->bigRumble; // Big rumble
|
||||
|
||||
buf[9] = output->r; // Red
|
||||
buf[10] = output->g; // Green
|
||||
buf[11] = output->b; // Blue
|
||||
|
||||
buf[12] = output->flashOn; // Time to flash bright (255 = 2.5 seconds)
|
||||
buf[13] = output->flashOff; // Time to flash dark (255 = 2.5 seconds)
|
||||
|
||||
output->reportChanged = false;
|
||||
|
||||
// The PS4 console actually set the four last bytes to a CRC32 checksum, but it seems like it is actually not needed
|
||||
|
||||
HID_Command(buf, sizeof(buf));
|
||||
};
|
||||
/**@}*/
|
||||
|
||||
private:
|
||||
void enable_sixaxis() { // Command used to make the PS4 controller send out the entire output report
|
||||
uint8_t buf[2];
|
||||
buf[0] = 0x43; // HID BT Get_report (0x40) | Report Type (Feature 0x03)
|
||||
buf[1] = 0x02; // Report ID
|
||||
|
||||
HID_Command(buf, 2);
|
||||
};
|
||||
|
||||
void HID_Command(uint8_t *data, uint8_t nbytes) {
|
||||
pBtd->L2CAP_Command(hci_handle, data, nbytes, control_scid[0], control_scid[1]);
|
||||
};
|
||||
};
|
||||
#endif
|
||||
@@ -1,116 +0,0 @@
|
||||
/* Copyright (C) 2014 Kristian Lauszus, TKJ Electronics. All rights reserved.
|
||||
|
||||
This software may be distributed and modified under the terms of the GNU
|
||||
General Public License version 2 (GPL2) as published by the Free Software
|
||||
Foundation and appearing in the file GPL2.TXT included in the packaging of
|
||||
this file. Please note that GPL2 Section 2[b] requires that all works based
|
||||
on this software must also be made publicly available under the terms of
|
||||
the GPL2 ("Copyleft").
|
||||
|
||||
Contact information
|
||||
-------------------
|
||||
|
||||
Kristian Lauszus, TKJ Electronics
|
||||
Web : http://www.tkjelectronics.com
|
||||
e-mail : kristianl@tkjelectronics.com
|
||||
*/
|
||||
|
||||
#include "PS4Parser.h"
|
||||
|
||||
// To enable serial debugging see "settings.h"
|
||||
//#define PRINTREPORT // Uncomment to print the report send by the PS4 Controller
|
||||
|
||||
bool PS4Parser::checkDpad(ButtonEnum b) {
|
||||
switch (b) {
|
||||
case UP:
|
||||
return ps4Data.btn.dpad == DPAD_LEFT_UP || ps4Data.btn.dpad == DPAD_UP || ps4Data.btn.dpad == DPAD_UP_RIGHT;
|
||||
case RIGHT:
|
||||
return ps4Data.btn.dpad == DPAD_UP_RIGHT || ps4Data.btn.dpad == DPAD_RIGHT || ps4Data.btn.dpad == DPAD_RIGHT_DOWN;
|
||||
case DOWN:
|
||||
return ps4Data.btn.dpad == DPAD_RIGHT_DOWN || ps4Data.btn.dpad == DPAD_DOWN || ps4Data.btn.dpad == DPAD_DOWN_LEFT;
|
||||
case LEFT:
|
||||
return ps4Data.btn.dpad == DPAD_DOWN_LEFT || ps4Data.btn.dpad == DPAD_LEFT || ps4Data.btn.dpad == DPAD_LEFT_UP;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool PS4Parser::getButtonPress(ButtonEnum b) {
|
||||
if (b <= LEFT) // Dpad
|
||||
return checkDpad(b);
|
||||
else
|
||||
return ps4Data.btn.val & (1UL << pgm_read_byte(&PS4_BUTTONS[(uint8_t)b]));
|
||||
}
|
||||
|
||||
bool PS4Parser::getButtonClick(ButtonEnum b) {
|
||||
uint32_t mask = 1UL << pgm_read_byte(&PS4_BUTTONS[(uint8_t)b]);
|
||||
bool click = buttonClickState.val & mask;
|
||||
buttonClickState.val &= ~mask; // Clear "click" event
|
||||
return click;
|
||||
}
|
||||
|
||||
uint8_t PS4Parser::getAnalogButton(ButtonEnum b) {
|
||||
if (b == L2) // These are the only analog buttons on the controller
|
||||
return ps4Data.trigger[0];
|
||||
else if (b == R2)
|
||||
return ps4Data.trigger[1];
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t PS4Parser::getAnalogHat(AnalogHatEnum a) {
|
||||
return ps4Data.hatValue[(uint8_t)a];
|
||||
}
|
||||
|
||||
void PS4Parser::Parse(uint8_t len, uint8_t *buf) {
|
||||
if (len > 1 && buf) {
|
||||
#ifdef PRINTREPORT
|
||||
Notify(PSTR("\r\n"), 0x80);
|
||||
for (uint8_t i = 0; i < len; i++) {
|
||||
D_PrintHex<uint8_t > (buf[i], 0x80);
|
||||
Notify(PSTR(" "), 0x80);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (buf[0] == 0x01) // Check report ID
|
||||
memcpy(&ps4Data, buf + 1, min((uint8_t)(len - 1), sizeof(ps4Data)));
|
||||
else if (buf[0] == 0x11) { // This report is send via Bluetooth, it has an offset of 2 compared to the USB data
|
||||
if (len < 4) {
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nReport is too short: "), 0x80);
|
||||
D_PrintHex<uint8_t > (len, 0x80);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
memcpy(&ps4Data, buf + 3, min((uint8_t)(len - 3), sizeof(ps4Data)));
|
||||
} else {
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nUnknown report id: "), 0x80);
|
||||
D_PrintHex<uint8_t > (buf[0], 0x80);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
if (ps4Data.btn.val != oldButtonState.val) { // Check if anything has changed
|
||||
buttonClickState.val = ps4Data.btn.val & ~oldButtonState.val; // Update click state variable
|
||||
oldButtonState.val = ps4Data.btn.val;
|
||||
|
||||
// The DPAD buttons does not set the different bits, but set a value corresponding to the buttons pressed, we will simply set the bits ourself
|
||||
uint8_t newDpad = 0;
|
||||
if (checkDpad(UP))
|
||||
newDpad |= 1 << UP;
|
||||
if (checkDpad(RIGHT))
|
||||
newDpad |= 1 << RIGHT;
|
||||
if (checkDpad(DOWN))
|
||||
newDpad |= 1 << DOWN;
|
||||
if (checkDpad(LEFT))
|
||||
newDpad |= 1 << LEFT;
|
||||
if (newDpad != oldDpad) {
|
||||
buttonClickState.dpad = newDpad & ~oldDpad; // Override values
|
||||
oldDpad = newDpad;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ps4Output.reportChanged)
|
||||
sendOutputReport(&ps4Output); // Send output report
|
||||
}
|
||||
@@ -1,407 +0,0 @@
|
||||
/* Copyright (C) 2014 Kristian Lauszus, TKJ Electronics. All rights reserved.
|
||||
|
||||
This software may be distributed and modified under the terms of the GNU
|
||||
General Public License version 2 (GPL2) as published by the Free Software
|
||||
Foundation and appearing in the file GPL2.TXT included in the packaging of
|
||||
this file. Please note that GPL2 Section 2[b] requires that all works based
|
||||
on this software must also be made publicly available under the terms of
|
||||
the GPL2 ("Copyleft").
|
||||
|
||||
Contact information
|
||||
-------------------
|
||||
|
||||
Kristian Lauszus, TKJ Electronics
|
||||
Web : http://www.tkjelectronics.com
|
||||
e-mail : kristianl@tkjelectronics.com
|
||||
*/
|
||||
|
||||
#ifndef _ps4parser_h_
|
||||
#define _ps4parser_h_
|
||||
|
||||
#include "Usb.h"
|
||||
#include "controllerEnums.h"
|
||||
|
||||
/** Buttons on the controller */
|
||||
const uint8_t PS4_BUTTONS[] PROGMEM = {
|
||||
UP, // UP
|
||||
RIGHT, // RIGHT
|
||||
DOWN, // DOWN
|
||||
LEFT, // LEFT
|
||||
|
||||
0x0C, // SHARE
|
||||
0x0D, // OPTIONS
|
||||
0x0E, // L3
|
||||
0x0F, // R3
|
||||
|
||||
0x0A, // L2
|
||||
0x0B, // R2
|
||||
0x08, // L1
|
||||
0x09, // R1
|
||||
|
||||
0x07, // TRIANGLE
|
||||
0x06, // CIRCLE
|
||||
0x05, // CROSS
|
||||
0x04, // SQUARE
|
||||
|
||||
0x10, // PS
|
||||
0x11, // TOUCHPAD
|
||||
};
|
||||
|
||||
union PS4Buttons {
|
||||
struct {
|
||||
uint8_t dpad : 4;
|
||||
uint8_t square : 1;
|
||||
uint8_t cross : 1;
|
||||
uint8_t circle : 1;
|
||||
uint8_t triangle : 1;
|
||||
|
||||
uint8_t l1 : 1;
|
||||
uint8_t r1 : 1;
|
||||
uint8_t l2 : 1;
|
||||
uint8_t r2 : 1;
|
||||
uint8_t share : 1;
|
||||
uint8_t options : 1;
|
||||
uint8_t l3 : 1;
|
||||
uint8_t r3 : 1;
|
||||
|
||||
uint8_t ps : 1;
|
||||
uint8_t touchpad : 1;
|
||||
uint8_t reportCounter : 6;
|
||||
} __attribute__((packed));
|
||||
uint32_t val : 24;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct touchpadXY {
|
||||
uint8_t dummy; // I can not figure out what this data is for, it seems to change randomly, maybe a timestamp?
|
||||
struct {
|
||||
uint8_t counter : 7; // Increments every time a finger is touching the touchpad
|
||||
uint8_t touching : 1; // The top bit is cleared if the finger is touching the touchpad
|
||||
uint16_t x : 12;
|
||||
uint16_t y : 12;
|
||||
} __attribute__((packed)) finger[2]; // 0 = first finger, 1 = second finger
|
||||
} __attribute__((packed));
|
||||
|
||||
struct PS4Status {
|
||||
uint8_t battery : 4;
|
||||
uint8_t usb : 1;
|
||||
uint8_t audio : 1;
|
||||
uint8_t mic : 1;
|
||||
uint8_t unknown : 1; // Extension port?
|
||||
} __attribute__((packed));
|
||||
|
||||
struct PS4Data {
|
||||
/* Button and joystick values */
|
||||
uint8_t hatValue[4];
|
||||
PS4Buttons btn;
|
||||
uint8_t trigger[2];
|
||||
|
||||
/* Gyro and accelerometer values */
|
||||
uint8_t dummy[3]; // First two looks random, while the third one might be some kind of status - it increments once in a while
|
||||
int16_t gyroY, gyroZ, gyroX;
|
||||
int16_t accX, accZ, accY;
|
||||
|
||||
uint8_t dummy2[5];
|
||||
PS4Status status;
|
||||
uint8_t dummy3[3];
|
||||
|
||||
/* The rest is data for the touchpad */
|
||||
touchpadXY xy[3]; // It looks like it sends out three coordinates each time, this might be because the microcontroller inside the PS4 controller is much faster than the Bluetooth connection.
|
||||
// The last data is read from the last position in the array while the oldest measurement is from the first position.
|
||||
// The first position will also keep it's value after the finger is released, while the other two will set them to zero.
|
||||
// Note that if you read fast enough from the device, then only the first one will contain any data.
|
||||
|
||||
// The last three bytes are always: 0x00, 0x80, 0x00
|
||||
} __attribute__((packed));
|
||||
|
||||
struct PS4Output {
|
||||
uint8_t bigRumble, smallRumble; // Rumble
|
||||
uint8_t r, g, b; // RGB
|
||||
uint8_t flashOn, flashOff; // Time to flash bright/dark (255 = 2.5 seconds)
|
||||
bool reportChanged; // The data is send when data is received from the controller
|
||||
} __attribute__((packed));
|
||||
|
||||
enum DPADEnum {
|
||||
DPAD_UP = 0x0,
|
||||
DPAD_UP_RIGHT = 0x1,
|
||||
DPAD_RIGHT = 0x2,
|
||||
DPAD_RIGHT_DOWN = 0x3,
|
||||
DPAD_DOWN = 0x4,
|
||||
DPAD_DOWN_LEFT = 0x5,
|
||||
DPAD_LEFT = 0x6,
|
||||
DPAD_LEFT_UP = 0x7,
|
||||
DPAD_OFF = 0x8,
|
||||
};
|
||||
|
||||
/** This class parses all the data sent by the PS4 controller */
|
||||
class PS4Parser {
|
||||
public:
|
||||
/** Constructor for the PS4Parser class. */
|
||||
PS4Parser() {
|
||||
Reset();
|
||||
};
|
||||
|
||||
/** @name PS4 Controller functions */
|
||||
/**
|
||||
* getButtonPress(ButtonEnum b) will return true as long as the button is held down.
|
||||
*
|
||||
* While getButtonClick(ButtonEnum b) will only return it once.
|
||||
*
|
||||
* So you instance if you need to increase a variable once you would use getButtonClick(ButtonEnum b),
|
||||
* but if you need to drive a robot forward you would use getButtonPress(ButtonEnum b).
|
||||
* @param b ::ButtonEnum to read.
|
||||
* @return getButtonPress(ButtonEnum b) will return a true as long as a button is held down, while getButtonClick(ButtonEnum b) will return true once for each button press.
|
||||
*/
|
||||
bool getButtonPress(ButtonEnum b);
|
||||
bool getButtonClick(ButtonEnum b);
|
||||
/**@}*/
|
||||
/** @name PS4 Controller functions */
|
||||
/**
|
||||
* Used to get the analog value from button presses.
|
||||
* @param b The ::ButtonEnum to read.
|
||||
* The supported buttons are:
|
||||
* ::UP, ::RIGHT, ::DOWN, ::LEFT, ::L1, ::L2, ::R1, ::R2,
|
||||
* ::TRIANGLE, ::CIRCLE, ::CROSS, ::SQUARE, and ::T.
|
||||
* @return Analog value in the range of 0-255.
|
||||
*/
|
||||
uint8_t getAnalogButton(ButtonEnum b);
|
||||
|
||||
/**
|
||||
* Used to read the analog joystick.
|
||||
* @param a ::LeftHatX, ::LeftHatY, ::RightHatX, and ::RightHatY.
|
||||
* @return Return the analog value in the range of 0-255.
|
||||
*/
|
||||
uint8_t getAnalogHat(AnalogHatEnum a);
|
||||
|
||||
/**
|
||||
* Get the x-coordinate of the touchpad. Position 0 is in the top left.
|
||||
* @param finger 0 = first finger, 1 = second finger. If omitted, then 0 will be used.
|
||||
* @param xyId The controller sends out three packets with the same structure.
|
||||
* The third one will contain the last measure, but if you read from the controller then there is only be data in the first one.
|
||||
* For that reason it will be set to 0 if the argument is omitted.
|
||||
* @return Returns the x-coordinate of the finger.
|
||||
*/
|
||||
uint16_t getX(uint8_t finger = 0, uint8_t xyId = 0) {
|
||||
return ps4Data.xy[xyId].finger[finger].x;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the y-coordinate of the touchpad. Position 0 is in the top left.
|
||||
* @param finger 0 = first finger, 1 = second finger. If omitted, then 0 will be used.
|
||||
* @param xyId The controller sends out three packets with the same structure.
|
||||
* The third one will contain the last measure, but if you read from the controller then there is only be data in the first one.
|
||||
* For that reason it will be set to 0 if the argument is omitted.
|
||||
* @return Returns the y-coordinate of the finger.
|
||||
*/
|
||||
uint16_t getY(uint8_t finger = 0, uint8_t xyId = 0) {
|
||||
return ps4Data.xy[xyId].finger[finger].y;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns whenever the user is toucing the touchpad.
|
||||
* @param finger 0 = first finger, 1 = second finger. If omitted, then 0 will be used.
|
||||
* @param xyId The controller sends out three packets with the same structure.
|
||||
* The third one will contain the last measure, but if you read from the controller then there is only be data in the first one.
|
||||
* For that reason it will be set to 0 if the argument is omitted.
|
||||
* @return Returns true if the specific finger is touching the touchpad.
|
||||
*/
|
||||
bool isTouching(uint8_t finger = 0, uint8_t xyId = 0) {
|
||||
return !(ps4Data.xy[xyId].finger[finger].touching); // The bit is cleared when a finger is touching the touchpad
|
||||
};
|
||||
|
||||
/**
|
||||
* This counter increments every time a finger touches the touchpad.
|
||||
* @param finger 0 = first finger, 1 = second finger. If omitted, then 0 will be used.
|
||||
* @param xyId The controller sends out three packets with the same structure.
|
||||
* The third one will contain the last measure, but if you read from the controller then there is only be data in the first one.
|
||||
* For that reason it will be set to 0 if the argument is omitted.
|
||||
* @return Return the value of the counter, note that it is only a 7-bit value.
|
||||
*/
|
||||
uint8_t getTouchCounter(uint8_t finger = 0, uint8_t xyId = 0) {
|
||||
return ps4Data.xy[xyId].finger[finger].counter;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the angle of the controller calculated using the accelerometer.
|
||||
* @param a Either ::Pitch or ::Roll.
|
||||
* @return Return the angle in the range of 0-360.
|
||||
*/
|
||||
double getAngle(AngleEnum a) {
|
||||
if (a == Pitch)
|
||||
return (atan2(ps4Data.accY, ps4Data.accZ) + PI) * RAD_TO_DEG;
|
||||
else
|
||||
return (atan2(ps4Data.accX, ps4Data.accZ) + PI) * RAD_TO_DEG;
|
||||
};
|
||||
|
||||
/**
|
||||
* Used to get the raw values from the 3-axis gyroscope and 3-axis accelerometer inside the PS4 controller.
|
||||
* @param s The sensor to read.
|
||||
* @return Returns the raw sensor reading.
|
||||
*/
|
||||
int16_t getSensor(SensorEnum s) {
|
||||
switch(s) {
|
||||
case gX:
|
||||
return ps4Data.gyroX;
|
||||
case gY:
|
||||
return ps4Data.gyroY;
|
||||
case gZ:
|
||||
return ps4Data.gyroZ;
|
||||
case aX:
|
||||
return ps4Data.accX;
|
||||
case aY:
|
||||
return ps4Data.accY;
|
||||
case aZ:
|
||||
return ps4Data.accZ;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the battery level of the PS4 controller.
|
||||
* @return The battery level in the range 0-15.
|
||||
*/
|
||||
uint8_t getBatteryLevel() {
|
||||
return ps4Data.status.battery;
|
||||
};
|
||||
|
||||
/**
|
||||
* Use this to check if an USB cable is connected to the PS4 controller.
|
||||
* @return Returns true if an USB cable is connected.
|
||||
*/
|
||||
bool getUsbStatus() {
|
||||
return ps4Data.status.usb;
|
||||
};
|
||||
|
||||
/**
|
||||
* Use this to check if an audio jack cable is connected to the PS4 controller.
|
||||
* @return Returns true if an audio jack cable is connected.
|
||||
*/
|
||||
bool getAudioStatus() {
|
||||
return ps4Data.status.audio;
|
||||
};
|
||||
|
||||
/**
|
||||
* Use this to check if a microphone is connected to the PS4 controller.
|
||||
* @return Returns true if a microphone is connected.
|
||||
*/
|
||||
bool getMicStatus() {
|
||||
return ps4Data.status.mic;
|
||||
};
|
||||
|
||||
/** Turn both rumble and the LEDs off. */
|
||||
void setAllOff() {
|
||||
setRumbleOff();
|
||||
setLedOff();
|
||||
};
|
||||
|
||||
/** Set rumble off. */
|
||||
void setRumbleOff() {
|
||||
setRumbleOn(0, 0);
|
||||
};
|
||||
|
||||
/**
|
||||
* Turn on rumble.
|
||||
* @param mode Either ::RumbleHigh or ::RumbleLow.
|
||||
*/
|
||||
void setRumbleOn(RumbleEnum mode) {
|
||||
if (mode == RumbleLow)
|
||||
setRumbleOn(0x00, 0xFF);
|
||||
else
|
||||
setRumbleOn(0xFF, 0x00);
|
||||
};
|
||||
|
||||
/**
|
||||
* Turn on rumble.
|
||||
* @param bigRumble Value for big motor.
|
||||
* @param smallRumble Value for small motor.
|
||||
*/
|
||||
void setRumbleOn(uint8_t bigRumble, uint8_t smallRumble) {
|
||||
ps4Output.bigRumble = bigRumble;
|
||||
ps4Output.smallRumble = smallRumble;
|
||||
ps4Output.reportChanged = true;
|
||||
};
|
||||
|
||||
/** Turn all LEDs off. */
|
||||
void setLedOff() {
|
||||
setLed(0, 0, 0);
|
||||
};
|
||||
|
||||
/**
|
||||
* Use this to set the color using RGB values.
|
||||
* @param r,g,b RGB value.
|
||||
*/
|
||||
void setLed(uint8_t r, uint8_t g, uint8_t b) {
|
||||
ps4Output.r = r;
|
||||
ps4Output.g = g;
|
||||
ps4Output.b = b;
|
||||
ps4Output.reportChanged = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Use this to set the color using the predefined colors in ::ColorsEnum.
|
||||
* @param color The desired color.
|
||||
*/
|
||||
void setLed(ColorsEnum color) {
|
||||
setLed((uint8_t)(color >> 16), (uint8_t)(color >> 8), (uint8_t)(color));
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the LEDs flash time.
|
||||
* @param flashOn Time to flash bright (255 = 2.5 seconds).
|
||||
* @param flashOff Time to flash dark (255 = 2.5 seconds).
|
||||
*/
|
||||
void setLedFlash(uint8_t flashOn, uint8_t flashOff) {
|
||||
ps4Output.flashOn = flashOn;
|
||||
ps4Output.flashOff = flashOff;
|
||||
ps4Output.reportChanged = true;
|
||||
};
|
||||
/**@}*/
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Used to parse data sent from the PS4 controller.
|
||||
* @param len Length of the data.
|
||||
* @param buf Pointer to the data buffer.
|
||||
*/
|
||||
void Parse(uint8_t len, uint8_t *buf);
|
||||
|
||||
/** Used to reset the different buffers to their default values */
|
||||
void Reset() {
|
||||
uint8_t i;
|
||||
for (i = 0; i < sizeof(ps4Data.hatValue); i++)
|
||||
ps4Data.hatValue[i] = 127; // Center value
|
||||
ps4Data.btn.val = 0;
|
||||
oldButtonState.val = 0;
|
||||
for (i = 0; i < sizeof(ps4Data.trigger); i++)
|
||||
ps4Data.trigger[i] = 0;
|
||||
for (i = 0; i < sizeof(ps4Data.xy)/sizeof(ps4Data.xy[0]); i++) {
|
||||
for (uint8_t j = 0; j < sizeof(ps4Data.xy[0].finger)/sizeof(ps4Data.xy[0].finger[0]); j++)
|
||||
ps4Data.xy[i].finger[j].touching = 1; // The bit is cleared if the finger is touching the touchpad
|
||||
}
|
||||
|
||||
ps4Data.btn.dpad = DPAD_OFF;
|
||||
oldButtonState.dpad = DPAD_OFF;
|
||||
buttonClickState.dpad = 0;
|
||||
oldDpad = 0;
|
||||
|
||||
ps4Output.bigRumble = ps4Output.smallRumble = 0;
|
||||
ps4Output.r = ps4Output.g = ps4Output.b = 0;
|
||||
ps4Output.flashOn = ps4Output.flashOff = 0;
|
||||
ps4Output.reportChanged = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Send the output to the PS4 controller. This is implemented in PS4BT.h and PS4USB.h.
|
||||
* @param output Pointer to PS4Output buffer;
|
||||
*/
|
||||
virtual void sendOutputReport(PS4Output *output) = 0;
|
||||
|
||||
private:
|
||||
bool checkDpad(ButtonEnum b); // Used to check PS4 DPAD buttons
|
||||
|
||||
PS4Data ps4Data;
|
||||
PS4Buttons oldButtonState, buttonClickState;
|
||||
PS4Output ps4Output;
|
||||
uint8_t oldDpad;
|
||||
};
|
||||
#endif
|
||||
@@ -1,130 +0,0 @@
|
||||
/* Copyright (C) 2014 Kristian Lauszus, TKJ Electronics. All rights reserved.
|
||||
|
||||
This software may be distributed and modified under the terms of the GNU
|
||||
General Public License version 2 (GPL2) as published by the Free Software
|
||||
Foundation and appearing in the file GPL2.TXT included in the packaging of
|
||||
this file. Please note that GPL2 Section 2[b] requires that all works based
|
||||
on this software must also be made publicly available under the terms of
|
||||
the GPL2 ("Copyleft").
|
||||
|
||||
Contact information
|
||||
-------------------
|
||||
|
||||
Kristian Lauszus, TKJ Electronics
|
||||
Web : http://www.tkjelectronics.com
|
||||
e-mail : kristianl@tkjelectronics.com
|
||||
*/
|
||||
|
||||
#ifndef _ps4usb_h_
|
||||
#define _ps4usb_h_
|
||||
|
||||
#include "hiduniversal.h"
|
||||
#include "PS4Parser.h"
|
||||
|
||||
#define PS4_VID 0x054C // Sony Corporation
|
||||
#define PS4_PID 0x05C4 // PS4 Controller
|
||||
|
||||
/**
|
||||
* This class implements support for the PS4 controller via USB.
|
||||
* It uses the HIDUniversal class for all the USB communication.
|
||||
*/
|
||||
class PS4USB : public HIDUniversal, public PS4Parser {
|
||||
public:
|
||||
/**
|
||||
* Constructor for the PS4USB class.
|
||||
* @param p Pointer to the USB class instance.
|
||||
*/
|
||||
PS4USB(USB *p) :
|
||||
HIDUniversal(p) {
|
||||
PS4Parser::Reset();
|
||||
};
|
||||
|
||||
/**
|
||||
* Used to check if a PS4 controller is connected.
|
||||
* @return Returns true if it is connected.
|
||||
*/
|
||||
bool connected() {
|
||||
return HIDUniversal::isReady() && HIDUniversal::VID == PS4_VID && HIDUniversal::PID == PS4_PID;
|
||||
};
|
||||
|
||||
/**
|
||||
* Used to call your own function when the device is successfully initialized.
|
||||
* @param funcOnInit Function to call.
|
||||
*/
|
||||
void attachOnInit(void (*funcOnInit)(void)) {
|
||||
pFuncOnInit = funcOnInit;
|
||||
};
|
||||
|
||||
protected:
|
||||
/** @name HIDUniversal implementation */
|
||||
/**
|
||||
* Used to parse USB HID data.
|
||||
* @param hid Pointer to the HID class.
|
||||
* @param is_rpt_id Only used for Hubs.
|
||||
* @param len The length of the incoming data.
|
||||
* @param buf Pointer to the data buffer.
|
||||
*/
|
||||
virtual void ParseHIDData(HID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf) {
|
||||
if (HIDUniversal::VID == PS4_VID && HIDUniversal::PID == PS4_PID)
|
||||
PS4Parser::Parse(len, buf);
|
||||
};
|
||||
|
||||
/**
|
||||
* Called when a device is successfully initialized.
|
||||
* Use attachOnInit(void (*funcOnInit)(void)) to call your own function.
|
||||
* This is useful for instance if you want to set the LEDs in a specific way.
|
||||
*/
|
||||
virtual uint8_t OnInitSuccessful() {
|
||||
if (HIDUniversal::VID == PS4_VID && HIDUniversal::PID == PS4_PID) {
|
||||
PS4Parser::Reset();
|
||||
if (pFuncOnInit)
|
||||
pFuncOnInit(); // Call the user function
|
||||
else
|
||||
setLed(Blue);
|
||||
};
|
||||
return 0;
|
||||
};
|
||||
/**@}*/
|
||||
|
||||
/** @name PS4Parser implementation */
|
||||
virtual void sendOutputReport(PS4Output *output) { // Source: https://github.com/chrippa/ds4drv
|
||||
uint8_t buf[32];
|
||||
memset(buf, 0, sizeof(buf));
|
||||
|
||||
buf[0] = 0x05; // Report ID
|
||||
buf[1]= 0xFF;
|
||||
|
||||
buf[4] = output->smallRumble; // Small Rumble
|
||||
buf[5] = output->bigRumble; // Big rumble
|
||||
|
||||
buf[6] = output->r; // Red
|
||||
buf[7] = output->g; // Green
|
||||
buf[8] = output->b; // Blue
|
||||
|
||||
buf[9] = output->flashOn; // Time to flash bright (255 = 2.5 seconds)
|
||||
buf[10] = output->flashOff; // Time to flash dark (255 = 2.5 seconds)
|
||||
|
||||
output->reportChanged = false;
|
||||
|
||||
// The PS4 console actually set the four last bytes to a CRC32 checksum, but it seems like it is actually not needed
|
||||
|
||||
pUsb->outTransfer(bAddress, epInfo[ hidInterfaces[0].epIndex[epInterruptOutIndex] ].epAddr, sizeof(buf), buf);
|
||||
};
|
||||
/**@}*/
|
||||
|
||||
/** @name USBDeviceConfig implementation */
|
||||
/**
|
||||
* Used by the USB core to check what this driver support.
|
||||
* @param vid The device's VID.
|
||||
* @param pid The device's PID.
|
||||
* @return Returns true if the device's VID and PID matches this driver.
|
||||
*/
|
||||
virtual bool VIDPIDOK(uint16_t vid, uint16_t pid) {
|
||||
return (vid == PS4_VID && pid == PS4_PID);
|
||||
};
|
||||
/**@}*/
|
||||
|
||||
private:
|
||||
void (*pFuncOnInit)(void); // Pointer to function called in onInit()
|
||||
};
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user