--- /dev/null
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright 2019, Olivier MATZ <zer0@droids-corp.org>
+#
+
+ROOTDIR := $(abspath .)
+ARCH := avr
+CROSS := avr-
+
+include $(ROOTDIR)/mk/pre.mk
+
+MCU = atmega328p
+F_CPU = 8000000UL
+
+#AVRDUDE_OPTS = -P /dev/ttyUSB0 -c arduino -b 57600
+#AVRDUDE_OPTS = -P usb -c dragon_isp -B 100 -v -v -v -v -i 100
+#AVRDUDE_OPTS = -P usb -c avrispmkii -B 100 -v -v -v -v -i 100
+#AVRDUDE_OPTS = -P usb -c avrispmkii
+AVRDUDE_OPTS = -P usb -c usbasp-clone -B 100 -i 100
+
+O ?= $(CURDIR)/build
+PROG = $(O)/red-alert
+
+CFLAGS := -g -Werror -Isrc
+CFLAGS += -Wall -W
+CFLAGS += -Os
+CFLAGS += -ffunction-sections
+CFLAGS += -DF_CPU=$(F_CPU)
+CFLAGS += -mmcu=$(MCU)
+
+LDFLAGS := -Wl,--gc-sections
+LDFLAGS += -mmcu=$(MCU)
+LDFLAGS += -lm
+
+exe-y-$(PROG) += src/cirbuf.c
+exe-y-$(PROG) += src/uart.c
+exe-y-$(PROG) += src/callout.c
+exe-y-$(PROG) += src/led.c
+exe-y-$(PROG) += src/rtc.c
+exe-y-$(PROG) += src/commands.c
+exe-y-$(PROG) += src/buzzer.c
+exe-y-$(PROG) += src/main.c
+
+ldflags-$(PROG) += -Wl,-Map=$(PROG).map,--cref
+
+objcopy-hex-y-$(PROG).hex := $(PROG)
+objcopy-bin-y-$(PROG).bin := $(PROG)
+
+BOOTLOADER = $(O)/bootloader
+exe-y-$(BOOTLOADER) += bootloader/bootloader.c
+
+# 0x7800 bytes == 0x3c00 words -> BOOTSZ1=0, BOOTSZ0=1
+ldflags-$(BOOTLOADER) += -Wl,--section-start=.text=7800
+ldflags-$(BOOTLOADER) += -Wl,-Map=$(BOOTLOADER).map,--cref
+
+objcopy-hex-y-$(BOOTLOADER).hex := $(BOOTLOADER)
+objcopy-bin-y-$(BOOTLOADER).bin := $(BOOTLOADER)
+
+
+.PHONY: all
+all:
+ $(CROSS)size $(PROG)
+ $(CROSS)size $(BOOTLOADER)
+
+include $(ROOTDIR)/mk/post.mk
+
+all: $(all-targets)
+
+.PHONY: clean
+clean: __clean
+
+.PHONY: program
+program: all
+# avrdude -e -p $(MCU) $(AVRDUDE_OPTS) -U flash:w:$(PROG):e
+ avrdude -e -p $(MCU) $(AVRDUDE_OPTS) -U flash:w:$(BOOTLOADER):e
+
+.PHONY: reset
+reset:
+ avrdude -p $(MCU) $(AVRDUDE_OPTS)
+
+# defaults + CKDIV8=1, BOOTSZ0=1, BOOTRST=0
+.PHONY: fuse
+fuse:
+ avrdude -p $(MCU) $(AVRDUDE_OPTS) -U lfuse:w:0xE2:m -U hfuse:w:0xDA:m -U efuse:w:0xFF:m
--- /dev/null
+#
+# Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# * Neither the name of the University of California, Berkeley nor the
+# names of its contributors may be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+# dump some infos if debug is enabled
+ifeq ($(D),1)
+$(call disp_list,------ all-ar,$(all-ar))
+$(foreach ar,$(all-ar),\
+ $(info,out-$(ar): $(out-$(ar))) \
+ $(call disp_list,pre-$(ar),$(pre-$(ar))) \
+)
+endif
+
+# include dependencies and commands files if they exist
+$(foreach ar,$(all-ar),\
+ $(eval -include $(call depfile,$(ar))) \
+ $(eval -include $(call cmdfile,$(ar))) \
+)
+
+# remove duplicates
+filtered-all-ar := $(sort $(all-ar))
+
+# link several objects files into one shared object
+$(filtered-all-ar): $$(pre-$$@) $$(wildcard $$(dep-$$@)) FORCE
+ @[ -d $(dir $@) ] || mkdir -p $(dir $@)
+ @$(call display_deps,$(pre-$(@)),$@,\
+ $(call ar_cmd,$(pre-$(@)),$@),$?)
+ @$(if $(call check_deps,$@,$(call ar_cmd,$(pre-$(@)),$@),$?),\
+ $(call ar_print_cmd,$(pre-$(@)),$@) && \
+ $(call ar_cmd,$(pre-$(@)),$@) && \
+ $(call save_cmd,$(call ar_cmd,$(pre-$(@)),$@),$@) && \
+ $(call create_empty_depfile,$@))
--- /dev/null
+#
+# Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# * Neither the name of the University of California, Berkeley nor the
+# names of its contributors may be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+# ar-y-$(ar) is provided by the user
+# $(ar) is the path of the static library, and the variable contains
+# the list of sources. Several ar-y-$(ar) can be present.
+
+# list all ar builds requested by user
+all-ar := $(patsubst ar-y-%,%,$(filter ar-y-%,$(.VARIABLES)))
+
+# add them to the list of targets
+all-targets += $(all-ar)
+
+# for each ar, create the following variables:
+# out-$(ar) = output path of the arcutable
+# pre-$(ar) = list of prerequisites for this arcutable
+# Some source files need intermediate objects, we define these variables
+# for them too, and add them in a list: $(all-iobj).
+# Last, we add the generated files in $(all-clean-file).
+$(foreach ar,$(all-ar),\
+ $(eval out-$(ar) := $(dir $(ar))) \
+ $(eval pre-$(ar) := ) \
+ $(foreach src,$(ar-y-$(ar)), \
+ $(if $(call is_cc_source,$(src)), \
+ $(eval iobj := $(call src2iobj,$(src),$(out-$(ar)))) \
+ $(eval pre-$(iobj) := $(src)) \
+ $(eval all-iobj += $(iobj)) \
+ $(eval all-clean-file += $(iobj)) \
+ $(eval pre-$(ar) += $(iobj)) \
+ , \
+ $(if $(call is_obj_source,$(src)),\
+ $(eval pre-$(ar) += $(src)) \
+ , \
+ $(error "unsupported source format: $(src)"))) \
+ )\
+ $(eval all-clean-file += $(ar)) \
+)
+
+# link several *.o files into a static libary
+# $1: sources (*.o)
+# $2: dst (xyz.a)
+ar_cmd = ar crsD $(2) $(1)
+
+# print line used to ar object files
+ifeq ($(V),1)
+ar_print_cmd = echo $(call protect_quote,$(call ar_cmd,$1,$2))
+else
+ar_print_cmd = echo " AR $(2)"
+endif
+
+all-clean-file += $(all-ar)
--- /dev/null
+#
+# Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# * Neither the name of the University of California, Berkeley nor the
+# names of its contributors may be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+ARCH_CFLAGS += -Wall -W
+ARCH_CFLAGS += -Os
+ARCH_CFLAGS += -ffunction-sections
+
+ARCH_LDFLAGS += -Wl,--gc-sections
+
+ARCH_CROSS := avr-
--- /dev/null
+#
+# Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# * Neither the name of the University of California, Berkeley nor the
+# names of its contributors may be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+.PHONY: __clean
+__clean: $(all-clean-target) FORCE
+ @$(call clean_print_cmd,$(all-clean-file) $(call depfile,$(all-clean-file)) \
+ $(call cmdfile,$(all-clean-file))) && \
+ $(call clean_cmd,$(all-clean-file) $(call depfile,$(all-clean-file)) \
+ $(call cmdfile,$(all-clean-file)))
--- /dev/null
+#
+# Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# * Neither the name of the University of California, Berkeley nor the
+# names of its contributors may be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+# remove files
+# $1: files
+clean_cmd = rm -rf $(1)
+
+# print line used to clean files
+ifeq ($(V),1)
+clean_print_cmd = echo $(call protect_quote,$(call clean_cmd,$1))
+else
+clean_print_cmd = echo " CLEAN $(CURDIR)"
+endif
--- /dev/null
+#
+# Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# * Neither the name of the University of California, Berkeley nor the
+# names of its contributors may be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+# dump some infos if debug is enabled
+ifeq ($(D),1)
+$(call disp_list,------ all-copy,$(all-copy))
+$(foreach copy,$(all-copy),\
+ $(info,out-$(copy): $(out-$(copy))) \
+ $(call disp_list,pre-$(copy),$(pre-$(copy))) \
+)
+endif
+
+# include dependencies and commands files if they exist
+$(foreach copy,$(all-copy),\
+ $(eval -include $(call depfile,$(copy))) \
+ $(eval -include $(call cmdfile,$(copy))) \
+)
+
+# remove duplicates
+filtered-all-copy := $(sort $(all-copy))
+
+# convert format of executable
+$(filtered-all-copy): $$(pre-$$@) $$(wildcard $$(dep-$$@)) FORCE
+ @[ -d $(dir $@) ] || mkdir -p $(dir $@)
+ @$(call display_deps,$(pre-$(@)),$@,\
+ $(call copy_cmd,$(pre-$(@)),$@),$?)
+ @$(if $(call check_deps,$@,$(call copy_cmd,$(pre-$(@)),$@),$?),\
+ $(call copy_print_cmd,$(pre-$(@)),$@) && \
+ $(call copy_cmd,$(pre-$(@)),$@) && \
+ $(call save_cmd,$(call copy_cmd,$(pre-$(@)),$@),$@) && \
+ $(call create_empty_depfile,$@))
--- /dev/null
+#
+# Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# * Neither the name of the University of California, Berkeley nor the
+# names of its contributors may be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+# copy a file
+# copy-y-$(copy) is provided by the user
+# $(copy) is the path of the directory containing the destination
+# files, and the variable contains the path of the files to copy. Several
+# copy-y-$(copy) can be present.
+
+# list all path requested by user
+_all-copy := $(patsubst copy-y-%,%,$(filter copy-y-%,$(.VARIABLES)))
+all-copy :=
+
+# for each copy, create the following variables:
+# out-$(copy) = output path of the executable
+# pre-$(copy) = list of prerequisites for this executable
+# We also add the files in $(all-copy).
+$(foreach copy,$(_all-copy),\
+ $(if $(notdir $(copy)), \
+ $(if $(call compare,$(words $(copy-y-$(copy))),1), \
+ $(error "only one source file is allowed in copy-y-$(copy)")) \
+ $(eval dst := $(dir $(copy))$(notdir $(copy-y-$(copy)))) \
+ $(eval out-$(copy) := $(dir $(copy))) \
+ $(eval pre-$(copy) := $(copy-y-$(copy))) \
+ $(eval all-copy += $(dst)) \
+ , \
+ $(foreach src,$(copy-y-$(copy)),\
+ $(eval dst := $(copy)$(notdir $(src))) \
+ $(eval out-$(copy) := $(copy)) \
+ $(eval pre-$(dst) := $(src)) \
+ $(eval all-copy += $(dst)) \
+ ) \
+ ) \
+)
+
+# add them to the list of targets and clean
+all-targets += $(all-copy)
+all-clean-file += $(all-copy)
+
+# convert format of executable from elf to ihex
+# $1: source executable (elf)
+# $2: destination file
+copy_cmd = $(CP) $(1) $(2)
+
+# print line used to convert executable format
+ifeq ($(V),1)
+copy_print_cmd = echo $(call protect_quote,$(call copy_cmd,$1,$2))
+else
+copy_print_cmd = echo " COPY $(2)"
+endif
--- /dev/null
+#
+# Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# * Neither the name of the University of California, Berkeley nor the
+# names of its contributors may be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+# dump some infos if debug is enabled
+ifeq ($(D),1)
+$(call disp_list,------ all-exe,$(all-exe))
+$(foreach exe,$(all-exe),\
+ $(info,out-$(exe): $(out-$(exe))) \
+ $(call disp_list,pre-$(exe),$(pre-$(exe))) \
+)
+endif
+
+# include dependencies and commands files if they exist
+$(foreach exe,$(all-exe),\
+ $(eval -include $(call depfile,$(exe))) \
+ $(eval -include $(call cmdfile,$(exe))) \
+)
+
+# remove duplicates
+filtered-all-exe := $(sort $(all-exe))
+
+# link several objects files into one executable
+$(filtered-all-exe): $$(pre-$$@) $$(wildcard $$(dep-$$@)) FORCE
+ @[ -d $(dir $@) ] || mkdir -p $(dir $@)
+ @$(call display_deps,$(pre-$(@)),$@,\
+ $(call link_cmd,$(pre-$(@)),$@),$?)
+ @$(if $(call check_deps,$@,$(call link_cmd,$(pre-$(@)),$@),$?),\
+ $(call link_print_cmd,$(pre-$(@)),$@) && \
+ $(call link_cmd,$(pre-$(@)),$@) && \
+ $(call save_cmd,$(call link_cmd,$(pre-$(@)),$@),$@) && \
+ $(call create_empty_depfile,$@))
--- /dev/null
+#
+# Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# * Neither the name of the University of California, Berkeley nor the
+# names of its contributors may be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+# exe-y-$(exe) is provided by the user
+# $(exe) is the path of the binary, and the variable contains
+# the list of sources. Several exe-y-$(exe) can be present.
+
+# list all exe builds requested by user
+all-exe := $(patsubst exe-y-%,%,$(filter exe-y-%,$(.VARIABLES)))
+
+# add them to the list of targets
+all-targets += $(all-exe)
+
+# for each exe, create the following variables:
+# out-$(exe) = output path of the executable
+# pre-$(exe) = list of prerequisites for this executable
+# Some source files need intermediate objects, we define these variables
+# for them too, and add them in a list: $(all-iobj).
+# Last, we add the generated files in $(all-clean-file).
+$(foreach exe,$(all-exe),\
+ $(eval out-$(exe) := $(dir $(exe))) \
+ $(eval pre-$(exe) := ) \
+ $(foreach src,$(exe-y-$(exe)), \
+ $(if $(call is_cc_source,$(src)), \
+ $(eval iobj := $(call src2iobj,$(src),$(out-$(exe)))) \
+ $(eval pre-$(iobj) := $(src)) \
+ $(eval all-iobj += $(iobj)) \
+ $(eval all-clean-file += $(iobj)) \
+ $(eval pre-$(exe) += $(iobj)) \
+ , \
+ $(if $(call is_obj_source,$(src)),\
+ $(eval pre-$(exe) += $(src)) \
+ , \
+ $(if $(call is_alib_source,$(src)),\
+ $(eval pre-$(exe) += $(src)) \
+ , \
+ $(error "unsupported source format: $(src)")))) \
+ )\
+ $(eval all-clean-file += $(exe)) \
+)
+
+# link several *.o files into a exeary
+# $1: sources (*.o) (*.a)
+# $2: dst (xyz.o too)
+link_cmd = $(CC) $(LDFLAGS) $(ldflags-$(2)) -o $(2) $(filter %.o,$(1)) \
+ $(filter %.a,$(1)) $(LDLIBS) $(ldlibs-$(2))
+
+# print line used to link object files
+ifeq ($(V),1)
+link_print_cmd = echo $(call protect_quote,$(call link_cmd,$1,$2))
+else
+link_print_cmd = echo " EXE $(2)"
+endif
+
+all-clean-file += $(all-exe)
--- /dev/null
+#
+# Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# * Neither the name of the University of California, Berkeley nor the
+# names of its contributors may be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+# dump some infos if debug is enabled
+ifeq ($(D),1)
+$(call disp_list,------ all-obj,$(all-obj))
+$(foreach obj,$(all-obj),\
+ $(info,out-$(obj): $(out-$(obj))) \
+ $(call disp_list,pre-$(obj),$(pre-$(obj))) \
+)
+$(call disp_list,------ all-iobj,$(all-iobj))
+$(foreach iobj,$(all-iobj),\
+ $(call disp_list,pre-$(iobj),$(pre-$(iobj))) \
+)
+endif
+
+# if a generated file has the same name than a user target,
+# generate an error
+conflicts := $(filter $(all-iobj),$(all-targets))
+$(if $(conflicts), \
+ $(error Intermediate file has the same names than user targets:\
+ $(conflicts)))
+
+# include dependencies and commands files if they exist
+$(foreach obj,$(all-obj),\
+ $(eval -include $(call depfile,$(obj))) \
+ $(eval -include $(call cmdfile,$(obj))) \
+)
+$(foreach iobj,$(all-iobj),\
+ $(eval -include $(call depfile,$(iobj))) \
+ $(eval -include $(call cmdfile,$(iobj))) \
+)
+
+# remove duplicates
+filtered-all-iobj := $(sort $(all-iobj))
+
+# convert source files to intermediate object file
+$(filtered-all-iobj): $$(pre-$$@) $$(wildcard $$(dep-$$@)) FORCE
+ @[ -d $(dir $@) ] || mkdir -p $(dir $@)
+ @$(call display_deps,$(pre-$(@)),$@,$(call compile_cmd,$(pre-$(@)),$@),$?)
+ @$(if $(call check_deps,$@,$(call compile_cmd,$(pre-$(@)),$@),$?),\
+ $(call compile_print_cmd,$(pre-$(@)),$@) && \
+ $(call compile_cmd,$(pre-$(@)),$@) && \
+ $(call save_cmd,$(call compile_cmd,$(pre-$(@)),$@),$@) && \
+ $(call obj-fixdep,$@))
+
+# remove duplicates
+filtered-all-obj := $(sort $(all-obj))
+
+# combine several objects files to one
+$(filtered-all-obj): $$(pre-$$@) $$(wildcard $$(dep-$$@)) FORCE
+ @[ -d $(dir $@) ] || mkdir -p $(dir $@)
+ @$(call display_deps,$(pre-$(@)),$@,\
+ $(call combine_cmd,$(pre-$(@)),$@),$?)
+ @$(if $(call check_deps,$@,$(call combine_cmd,$(pre-$(@)),$@),$?),\
+ $(call combine_print_cmd,$(pre-$(@)),$@) && \
+ $(call combine_cmd,$(pre-$(@)),$@) && \
+ $(call save_cmd,$(call combine_cmd,$(pre-$(@)),$@),$@) && \
+ $(call create_empty_depfile,$@))
--- /dev/null
+#
+# Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# * Neither the name of the University of California, Berkeley nor the
+# names of its contributors may be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+# obj-y-$(obj) is provided by the user
+# $(obj) is the path of the object, and the variable contains
+# the list of sources. Several obj-y-$(obj) can be present.
+
+# list all object builds requested by user
+all-obj := $(patsubst obj-y-%,%,$(filter obj-y-%,$(.VARIABLES)))
+
+# add them to the list of targets
+all-targets += $(all-obj)
+
+# convert source path to intermediate object path, and filter
+# objects from sources
+# $1: list of source paths
+# $2: output directory (including trailing slash)
+# return: list of intermediate object paths
+src2iobj = $(addprefix $(filter-out ./,$(2)),$(notdir $(strip \
+ $(patsubst %.c,%.o,\
+ $(patsubst %.s,%.o,\
+ $(filter-out %.o,$(1)))))))
+
+# return the file if it matches a extension that is built with cc
+# $1: source file
+is_cc_source = $(filter %.c %.s %S,$(1))
+
+# return the file if it's already an object file: in this case no
+# intermediate object is needed
+# $1: source file
+is_obj_source = $(filter %.o,$(1))
+
+# return the file if it's a static library
+# $1: source file
+is_alib_source = $(filter %.a,$(1))
+
+# for each obj, create the following variables:
+# out-$(obj) = output path of the object
+# pre-$(obj) = list of prerequisites for this object
+# Some source files need intermediate objects, we define these variables
+# for them too, and add them in a list: $(all-iobj).
+# Last, we add the generated files in $(all-clean-file).
+$(foreach obj,$(all-obj),\
+ $(eval out-$(obj) := $(dir $(obj))) \
+ $(eval pre-$(obj) := ) \
+ $(foreach src,$(obj-y-$(obj)), \
+ $(if $(call is_cc_source,$(src)), \
+ $(eval iobj := $(call src2iobj,$(src),$(out-$(obj)))) \
+ $(eval pre-$(iobj) := $(src)) \
+ $(eval all-iobj += $(iobj)) \
+ $(eval all-clean-file += $(iobj)) \
+ $(eval pre-$(obj) += $(iobj)) \
+ , \
+ $(if $(call is_obj_source,$(src)),\
+ $(eval pre-$(obj) += $(src)) \
+ , \
+ $(error "unsupported source format: $(src)"))) \
+ )\
+ $(eval all-clean-file += $(obj)) \
+)
+
+# fix the format of .o.d.tmp (generated by gcc) to a .o.d that defines
+# dependencies as makefile variables
+# $1: object file (.o)
+obj-fixdep = if [ -f $(call file2tmpdep,$(1)) ]; then\
+ echo -n "dep-$(1) = " > $(call depfile,$(1)) && \
+ sed 's,^[^ ][^:]*: ,,' $(call file2tmpdep,$(1)) >> $(call depfile,$(1)) && \
+ rm -f $(call file2tmpdep,$(1)); \
+ else \
+ $(call create_empty_depfile,$(1)); \
+ fi
+
+# compile a file
+# $1: sources
+# $2: dst
+compile_cmd = $(CC) -Wp,-MD,$(call file2tmpdep,$(2)) \
+ $(CPPFLAGS) $(cppflags-$(2)) \
+ $(CFLAGS) $(cflags-$(2)) \
+ -c -o $2 $1
+
+# print line used to compile a file
+ifeq ($(V),1)
+compile_print_cmd = echo $(call protect_quote,$(call compile_cmd,$1,$2))
+else
+compile_print_cmd = echo " CC $(2)"
+endif
+
+# combine several *.o files into one
+# $1: sources (*.o)
+# $2: dst (xyz.o too)
+combine_cmd = $(LD) -r $(1) -o $(2)
+
+# print line used to combine object files
+ifeq ($(V),1)
+combine_print_cmd = echo $(call protect_quote,$(call combine_cmd,$1,$2))
+else
+combine_print_cmd = echo " LD $(2)"
+endif
+
+all-clean-file += $(all-obj)
--- /dev/null
+#
+# Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# * Neither the name of the University of California, Berkeley nor the
+# names of its contributors may be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+# dump some infos if debug is enabled
+ifeq ($(D),1)
+$(call disp_list,------ all-objcopy-hex,$(all-objcopy-hex))
+$(foreach objcopy,$(all-objcopy-hex),\
+ $(info,out-$(objcopy): $(out-$(objcopy))) \
+ $(call disp_list,pre-$(objcopy),$(pre-$(objcopy))) \
+)
+$(call disp_list,------ all-objcopy-bin,$(all-objcopy-bin))
+$(foreach objcopy,$(all-objcopy-bin),\
+ $(info,out-$(objcopy): $(out-$(objcopy))) \
+ $(call disp_list,pre-$(objcopy),$(pre-$(objcopy))) \
+)
+endif
+
+# include dependencies and commands files if they exist
+$(foreach objcopy,$(all-objcopy-hex) $(all-objcopy-bin),\
+ $(eval -include $(call depfile,$(objcopy))) \
+ $(eval -include $(call cmdfile,$(objcopy))) \
+)
+
+# remove duplicates
+filtered-all-objcopy-hex := $(sort $(all-objcopy-hex))
+
+# convert format of executable
+$(filtered-all-objcopy-hex): $$(pre-$$@) $$(wildcard $$(dep-$$@)) FORCE
+ @[ -d $(dir $@) ] || mkdir -p $(dir $@)
+ @$(call display_deps,$(pre-$(@)),$@,\
+ $(call objcopy_hex_cmd,$(pre-$(@)),$@),$?)
+ @$(if $(call check_deps,$@,$(call objcopy_hex_cmd,$(pre-$(@)),$@),$?),\
+ $(call objcopy_print_cmd,$(pre-$(@)),$@) && \
+ $(call objcopy_hex_cmd,$(pre-$(@)),$@) && \
+ $(call save_cmd,$(call objcopy_hex_cmd,$(pre-$(@)),$@),$@) && \
+ $(call create_empty_depfile,$@))
+
+# remove duplicates
+filtered-all-objcopy-bin := $(sort $(all-objcopy-bin))
+
+# convert format of executable
+$(filtered-all-objcopy-bin): $$(pre-$$@) $$(wildcard $$(dep-$$@)) FORCE
+ @[ -d $(dir $@) ] || mkdir -p $(dir $@)
+ @$(call display_deps,$(pre-$(@)),$@,\
+ $(call objcopy_bin_cmd,$(pre-$(@)),$@),$?)
+ @$(if $(call check_deps,$@,$(call objcopy_bin_cmd,$(pre-$(@)),$@),$?),\
+ $(call objcopy_print_cmd,$(pre-$(@)),$@) && \
+ $(call objcopy_bin_cmd,$(pre-$(@)),$@) && \
+ $(call save_cmd,$(call objcopy_bin_cmd,$(pre-$(@)),$@),$@) && \
+ $(call create_empty_depfile,$@))
--- /dev/null
+#
+# Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# * Neither the name of the University of California, Berkeley nor the
+# names of its contributors may be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+# objcopy changes the format of a binary
+# objcopy-hex-y-$(objcopy), objcopy-bin-y-$(objcopy) are provided by the user
+# $(objcopy) is the path of the binary, and the variable contains
+# the path to the elf. Several objcopy-y-$(objcopy) can be present.
+
+# list all executable builds requested by user
+all-objcopy-hex := $(patsubst objcopy-hex-y-%,%,$(filter objcopy-hex-y-%,$(.VARIABLES)))
+all-objcopy-bin := $(patsubst objcopy-bin-y-%,%,$(filter objcopy-bin-y-%,$(.VARIABLES)))
+
+# add them to the list of targets
+all-targets += $(all-objcopy-hex) $(all-objcopy-bin)
+
+# for each objcopy, create the following variables:
+# out-$(objcopy) = output path of the executable
+# pre-$(objcopy) = list of prerequisites for this executable
+# We also add the generated files in $(all-clean-file).
+$(foreach objcopy,$(all-objcopy-hex),\
+ $(if $(call compare,$(words $(objcopy-hex-y-$(objcopy))),1),\
+ $(error "only one source file is allowed in objcopy-hex-y-$(objcopy)")) \
+ $(eval out-$(objcopy) := $(dir $(objcopy))) \
+ $(eval pre-$(objcopy) := $(objcopy-hex-y-$(objcopy))) \
+ $(eval all-clean-file += $(objcopy)) \
+)
+
+# for each objcopy, create the following variables:
+# out-$(objcopy) = output path of the executable
+# pre-$(objcopy) = list of prerequisites for this executable
+# We also add the generated files in $(all-clean-file).
+$(foreach objcopy,$(all-objcopy-bin),\
+ $(if $(call compare,$(words $(objcopy-bin-y-$(objcopy))),1),\
+ $(error "only one source file is allowed in objcopy-bin-y-$(objcopy)")) \
+ $(eval out-$(objcopy) := $(dir $(objcopy))) \
+ $(eval pre-$(objcopy) := $(objcopy-bin-y-$(objcopy))) \
+ $(eval all-clean-file += $(objcopy)) \
+)
+
+# convert format of executable from elf to ihex
+# $1: source executable (elf)
+# $2: destination file
+objcopy_hex_cmd = $(OBJCOPY) -O ihex $(1) $(2)
+
+# print line used to convert executable format
+ifeq ($(V),1)
+objcopy_print_cmd = echo $(call protect_quote,$(call objcopy_hex_cmd,$1,$2))
+else
+objcopy_print_cmd = echo " OBJCOPY $(2)"
+endif
+
+# convert format of executable from elf to binary
+# $1: source executable (elf)
+# $2: destination file
+objcopy_bin_cmd = $(OBJCOPY) -O binary $(1) $(2)
+
+# print line used to convert executable format
+ifeq ($(V),1)
+objcopy_print_cmd = echo $(call protect_quote,$(call objcopy_bin_cmd,$1,$2))
+else
+objcopy_print_cmd = echo " OBJCOPY $(2)"
+endif
+
+# XXX dup ?
+all-clean-file += $(all-objcopy-hex) $(all-objcopy-bin)
--- /dev/null
+#
+# Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# * Neither the name of the University of California, Berkeley nor the
+# names of its contributors may be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+# ---- variables that must be defined:
+#
+# ROOTDIR: path to project root directory
+#
+# ---- variable that can be defined anywhere
+#
+# CROSS: prefix of the toolchain
+# CP, LN, GAWK, GREP: coreutils tools
+# CC, CPP, AR, LD, OBJCOPY, OBJDUMP, STRIP: compilers/binutils
+#
+# ---- variable that can be defined by Makefile:
+#
+# obj-y-$(path)
+# exe-y-$(path)
+# ar-y-$(path)
+# shlib-y-$(path)
+# copy-y-$(path)
+# slink-y-$(path)
+# objcopy-y-$(path)
+# subdir-y
+#
+# CPPFLAGS, CFLAGS, LDFLAGS, LDLIBS: global flags
+# cflags-$(path), cppflags-$(path), ldflags-$(path), ldlibs-$(path): per
+# file flags
+# mkflags-$(path): flags for subdirectories
+#
+# ---- variables that can be defined on the command line:
+#
+# EXTRA_CPPFLAGS, EXTRA_CFLAGS, EXTRA_LDFLAGS, EXTRA_LDLIBS: global
+# extra flags
+# extra-cflags-$(path), extra-cppflags-$(path): per object extra flags
+
+ifeq ($(ROOTDIR),)
+$(error ROOTDIR environment variable is not defined)
+endif
+
+# list of targets asked by user
+all-targets :=
+# list of files generated
+all-clean-file :=
+
+# usual internal variables:
+# out-$(file) = output path of a generated file
+# pre-$(file) = list of files needed to generate $(file)
+# all-type = list of targets for this type
+
+include $(ROOTDIR)/mk/obj-vars.mk
+include $(ROOTDIR)/mk/exe-vars.mk
+include $(ROOTDIR)/mk/ar-vars.mk
+include $(ROOTDIR)/mk/shlib-vars.mk
+include $(ROOTDIR)/mk/copy-vars.mk
+include $(ROOTDIR)/mk/slink-vars.mk
+include $(ROOTDIR)/mk/objcopy-vars.mk
+include $(ROOTDIR)/mk/subdir-vars.mk
+# must stay at the end
+include $(ROOTDIR)/mk/clean-vars.mk
+
+# dump the list of targets
+ifeq ($(D),1)
+$(call disp_list,------ all-targets,$(all-targets))
+endif
+
+# first rule (default)
+.PHONY: __all
+__all: $(all-targets)
+
+# the includes below require second expansion
+.SECONDEXPANSION:
+
+include $(ROOTDIR)/mk/obj-rules.mk
+include $(ROOTDIR)/mk/exe-rules.mk
+include $(ROOTDIR)/mk/ar-rules.mk
+include $(ROOTDIR)/mk/shlib-rules.mk
+include $(ROOTDIR)/mk/copy-rules.mk
+include $(ROOTDIR)/mk/slink-rules.mk
+include $(ROOTDIR)/mk/objcopy-rules.mk
+include $(ROOTDIR)/mk/subdir-rules.mk
+include $(ROOTDIR)/mk/clean-rules.mk
+
+.PHONY: FORCE
+FORCE:
--- /dev/null
+#
+# Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# * Neither the name of the University of California, Berkeley nor the
+# names of its contributors may be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+# ---- variables that must be defined:
+#
+# ROOTDIR: path to project root dir
+# ARCH: architecture (ex: avr, stm32, ...)
+#
+
+ifeq ($(ROOTDIR),)
+$(error ROOTDIR environment variable is not defined)
+endif
+
+ifeq ($(ARCH),)
+$(error ARCH environment variable is not defined)
+endif
+
+MAKEFLAGS += --no-print-directory
+
+include $(ROOTDIR)/mk/tools.mk
+
+include $(ROOTDIR)/mk/arch-$(ARCH).mk
+
+include $(ROOTDIR)/mk/vars.mk
--- /dev/null
+#
+# Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# * Neither the name of the University of California, Berkeley nor the
+# names of its contributors may be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+# dump some infos if debug is enabled
+ifeq ($(D),1)
+$(call disp_list,------ all-shlib,$(all-shlib))
+$(foreach shlib,$(all-shlib),\
+ $(info,out-$(shlib): $(out-$(shlib))) \
+ $(call disp_list,pre-$(shlib),$(pre-$(shlib))) \
+)
+endif
+
+# include dependencies and commands files if they exist
+$(foreach shlib,$(all-shlib),\
+ $(eval -include $(call depfile,$(shlib))) \
+ $(eval -include $(call cmdfile,$(shlib))) \
+)
+
+# remove duplicates
+filtered-all-shlib := $(sort $(all-shlib))
+
+# link several objects files into one shared object
+$(filtered-all-shlib): $$(pre-$$@) $$(wildcard $$(dep-$$@)) FORCE
+ @[ -d $(dir $@) ] || mkdir -p $(dir $@)
+ @$(call display_deps,$(pre-$(@)),$@,\
+ $(call shlib_cmd,$(pre-$(@)),$@),$?)
+ @$(if $(call check_deps,$@,$(call shlib_cmd,$(pre-$(@)),$@),$?),\
+ $(call shlib_print_cmd,$(pre-$(@)),$@) && \
+ $(call shlib_cmd,$(pre-$(@)),$@) && \
+ $(call save_cmd,$(call shlib_cmd,$(pre-$(@)),$@),$@) && \
+ $(call create_empty_depfile,$@))
--- /dev/null
+#
+# Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# * Neither the name of the University of California, Berkeley nor the
+# names of its contributors may be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+# shlib-y-$(shlib) is provided by the user
+# $(shlib) is the path of the shared library, and the variable
+# contains the list of sources. Several shlib-y-$(shlib) can be
+# present.
+
+# list all shlib builds requested by user
+all-shlib := $(patsubst shlib-y-%,%,$(filter shlib-y-%,$(.VARIABLES)))
+
+# add them to the list of targets
+all-targets += $(all-shlib)
+
+# for each shlib, create the following variables:
+# out-$(shlib) = output path of the shlibcutable
+# pre-$(shlib) = list of prerequisites for this shlibcutable
+# Some source files need intermediate objects, we define these variables
+# for them too, and add them in a list: $(all-iobj).
+# Last, we add the generated files in $(all-clean-file).
+$(foreach shlib,$(all-shlib),\
+ $(eval out-$(shlib) := $(dir $(shlib))) \
+ $(eval pre-$(shlib) := ) \
+ $(foreach src,$(shlib-y-$(shlib)), \
+ $(if $(call is_cc_source,$(src)), \
+ $(eval iobj := $(call src2iobj,$(src),$(out-$(shlib)))) \
+ $(eval pre-$(iobj) := $(src)) \
+ $(eval all-iobj += $(iobj)) \
+ $(eval all-clean-file += $(iobj)) \
+ $(eval pre-$(shlib) += $(iobj)) \
+ , \
+ $(if $(call is_obj_source,$(src)),\
+ $(eval pre-$(shlib) += $(src)) \
+ , \
+ $(error "unsupported source format: $(src)"))) \
+ )\
+ $(eval all-clean-file += $(shlib)) \
+)
+
+# link several *.o files into a shared libary
+# $1: sources (*.o)
+# $2: dst (xyz.so)
+shlib_cmd = $(CC) $(LDFLAGS) $(ldflags-$(2)) -shared -o $(2) $(1)
+
+# print line used to shlib object files
+ifeq ($(V),1)
+shlib_print_cmd = echo $(call protect_quote,$(call shlib_cmd,$1,$2))
+else
+shlib_print_cmd = echo " SHLIB $(2)"
+endif
+
+all-clean-file += $(all-shlib)
--- /dev/null
+#
+# Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# * Neither the name of the University of California, Berkeley nor the
+# names of its contributors may be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+# dump some infos if debug is enabled
+ifeq ($(D),1)
+$(call disp_list,------ all-slink,$(all-slink))
+$(foreach slink,$(all-slink),\
+ $(info,out-$(slink): $(out-$(slink))) \
+ $(call disp_list,pre-$(slink),$(pre-$(slink))) \
+)
+endif
+
+# include dependencies and commands files if they exist
+$(foreach slink,$(all-slink),\
+ $(eval -include $(call depfile,$(slink))) \
+ $(eval -include $(call cmdfile,$(slink))) \
+)
+
+# remove duplicates
+filtered-all-slink := $(sort $(all-slink))
+
+# convert format of executable
+$(filtered-all-slink): $$(pre-$$@) $$(wildcard $$(dep-$$@)) FORCE
+ @[ -d $(dir $@) ] || mkdir -p $(dir $@)
+ @$(call display_deps,$(pre-$(@)),$@,\
+ $(call slink_cmd,$(pre-$(@)),$@),$?)
+ @$(if $(call check_deps,$@,$(call slink_cmd,$(pre-$(@)),$@),$?),\
+ $(call slink_print_cmd,$(pre-$(@)),$@) && \
+ $(call slink_cmd,$(pre-$(@)),$@) && \
+ $(call save_cmd,$(call slink_cmd,$(pre-$(@)),$@),$@) && \
+ $(call create_empty_depfile,$@))
--- /dev/null
+#
+# Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# * Neither the name of the University of California, Berkeley nor the
+# names of its contributors may be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+# create a symbolic link of a file
+# slink-y-$(slink) is provided by the user
+# $(slink) is the path of the directory containing the destination
+# files, and the variable contains the path of the files to linked. Several
+# slink-y-$(slink) can be present.
+
+# list all path requested by user
+_all-slink := $(patsubst slink-y-%,%,$(filter slink-y-%,$(.VARIABLES)))
+all-slink :=
+
+# for each slink, create the following variables:
+# out-$(slink) = output path of the executable
+# pre-$(slink) = list of prerequisites for this executable
+# We also add the files in $(all-slink).
+$(foreach slink,$(_all-slink),\
+ $(if $(notdir $(slink)), \
+ $(if $(call compare,$(words $(slink-y-$(slink))),1), \
+ $(error "only one source file is allowed in slink-y-$(slink)")) \
+ $(eval dst := $(dir $(slink))$(notdir $(slink-y-$(slink)))) \
+ $(eval out-$(slink) := $(dir $(slink))) \
+ $(eval pre-$(slink) := $(slink-y-$(slink))) \
+ $(eval all-slink += $(dst)) \
+ , \
+ $(foreach src,$(slink-y-$(slink)),\
+ $(eval dst := $(slink)$(notdir $(src))) \
+ $(eval out-$(slink) := $(slink)) \
+ $(eval pre-$(dst) := $(src)) \
+ $(eval all-slink += $(dst)) \
+ ) \
+ ) \
+)
+
+# add them to the list of targets and clean
+all-targets += $(all-slink)
+all-clean-file += $(all-slink)
+
+# convert format of executable from elf to ihex
+# $1: source executable (elf)
+# $2: destination file
+slink_cmd = $(LN) -nsf $(abspath $(1)) $(2)
+
+# print line used to convert executable format
+ifeq ($(V),1)
+slink_print_cmd = echo $(call protect_quote,$(call slink_cmd,$1,$2))
+else
+slink_print_cmd = echo " SLINK $(2)"
+endif
--- /dev/null
+#
+# Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# * Neither the name of the University of California, Berkeley nor the
+# names of its contributors may be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+.PHONY: $(subdir-y)
+$(subdir-y): FORCE
+ $(Q)$(MAKE) -C $(@) $(mkflags-$(@)) $(MAKECMDGOALS)
--- /dev/null
+#
+# Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# * Neither the name of the University of California, Berkeley nor the
+# names of its contributors may be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+# subdir-y is provided by the user
+# it contains the list of directory to build
+
+# add them to the list of targets
+all-targets += $(subdir-y)
+all-clean-target += $(subdir-y)
--- /dev/null
+#
+# Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# * Neither the name of the University of California, Berkeley nor the
+# names of its contributors may be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+empty:=
+space:= $(empty) $(empty)
+indent:= $(space)$(space)
+
+# define a newline char, useful for debugging with $(info)
+define newline
+
+
+endef
+
+# $(prefix shell commands with $(Q) to silent them, except if V=1
+Q=@
+ifeq ("$(V)-$(origin V)", "1-command line")
+Q=
+endif
+
+# set variable $1 to $2 if the variable has an implicit value or
+# is not defined
+# $1 variable name
+# $2 new variable content
+set_default = $(if \
+ $(call not,$(or \
+ $(compare $(origin $(1)),default), \
+ $(compare $(origin $(1)),undefined) \
+ )),\
+ $(eval $(1) = $(2)) \
+)
+
+# display a list
+# $1 title
+# $2 list
+disp_list = $(info $(1)$(newline)\
+ $(addsuffix $(newline),$(addprefix $(space),$(2))))
+
+# add a dot in front of the file name
+# $1 list of paths
+# return: full paths with files prefixed by a dot
+dotfile = $(strip $(foreach f,$(1),\
+ $(join $(dir $f),.$(notdir $f))))
+
+# convert source/obj files into dot-dep filename
+# $1 list of paths
+# return: full paths with files prefixed by a dot and suffixed with .d
+depfile = $(strip $(call dotfile,$(addsuffix .d,$(1))))
+
+# convert source/obj files into dot-dep filename
+# $1 list of paths
+# return: full paths with files prefixed by a dot and suffixed with .d.tmp
+file2tmpdep = $(strip $(call dotfile,$(addsuffix .d.tmp,$(1))))
+
+# convert source/obj files into dot-cmd filename
+# $1 list of paths
+# return: full paths with files prefixed by a dot and suffixed with .cmd
+cmdfile = $(strip $(call dotfile,$(addsuffix .cmd,$(1))))
+
+# add a \ before each quote
+protect_quote = $(subst ','\'',$(1))
+#'# editor syntax highlight fix
+
+# return an non-empty string if $1 is empty, and vice versa
+# $1 a string
+not = $(if $1,,true)
+
+# return 1 if parameter is a non-empty string, else 0
+boolean = $(if $1,1,0)
+
+# return an empty string if string are equal
+compare = $(strip $(subst $(1),,$(2)) $(subst $(2),,$(1)))
+
+# return a non-empty string if a file does not exist
+# $1: file
+file_missing = $(call compare,$(wildcard $1),$1)
+
+# return a non-empty string if cmdline changed
+# $1: file to be built
+# $2: the command to build it
+cmdline_changed = $(call compare,$(strip $(cmd-$(1))),$(strip $(2)))
+
+# return an non-empty string if the .d file does not exist
+# $1: the dep file (.d)
+depfile_missing = $(call compare,$(wildcard $(1)),$(1))
+
+# return a non-empty string if, according to dep-xyz variable, a file
+# needed to build $1 does not exist. In this case we need to rebuild
+# the file and the .d file.
+# $1: file to be built
+dep-missing = $(call compare,$(wildcard $(dep-$(1))),$(dep-$(1)))
+
+# return an empty string if no prereq is newer than target
+# $1: list of prerequisites newer than target ($?)
+dep-newer = $(strip $(filter-out FORCE,$(1)))
+
+# display why a file should be re-built
+# $1: source files
+# $2: dst file
+# $3: build command
+# $4: all prerequisites newer than target ($?)
+ifeq ($(D),1)
+display_deps = \
+ echo -n "$1 -> $2 " ; \
+ echo -n "file_missing=$(call boolean,$(call file_missing,$(2))) " ; \
+ echo -n "cmdline_changed=$(call boolean,$(call cmdline_changed,$(2),$(3))) " ; \
+ echo -n "depfile_missing=$(call boolean,$(call depfile_missing,$(call depfile,$(2)))) " ; \
+ echo -n "dep-missing=$(call boolean,$(call dep-missing,$(2))) " ; \
+ echo "dep-newer=$(call boolean,$(call dep-newer,$(4)))"
+else
+display_deps=
+endif
+
+# return an empty string if a file should be rebuilt
+# $1: dst file
+# $2: build command
+# $3: all prerequisites newer than target ($?)
+check_deps = \
+ $(or $(call file_missing,$(1)),\
+ $(call cmdline_changed,$(1),$(2)),\
+ $(call depfile_missing,$(call depfile,$(1))),\
+ $(call dep-missing,$(1)),\
+ $(call dep-newer,$(3)))
+
+# create a depfile (.d) with no additional deps
+# $1: object file (.o)
+create_empty_depfile = echo "dep-$(1) =" > $(call depfile,$(1))
+
+# save a command in a file
+# $1: command to build the file
+# $2: name of the file
+save_cmd = echo "cmd-$(2) = $(call protect_quote,$(1))" > $(call cmdfile,$(2))
+
+# remove the FORCE target from the list of all prerequisites $+
+# no arguments, use $+
+prereq = $(filter-out FORCE,$(+))
--- /dev/null
+#
+# Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# * Neither the name of the University of California, Berkeley nor the
+# names of its contributors may be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+CROSS ?= $(ARCH_CROSS)
+
+# core tools
+CP ?= cp
+LN ?= ln
+GAWK ?= gawk
+GREP ?= grep
+# compiler and binutils, set_default overrides mk implicit value
+# but not command line or standard variables
+$(call set_default,CC,$(CROSS)gcc)
+$(call set_default,CPP,$(CROSS)cpp)
+$(call set_default,AR,$(CROSS)ar)
+$(call set_default,LD,$(CROSS)ld)
+$(call set_default,OBJCOPY,$(CROSS)objcopy)
+$(call set_default,OBJDUMP,$(CROSS)objdump)
+$(call set_default,STRIP,$(CROSS)strip)
+HOSTCC ?= cc
+
+CFLAGS += $(EXTRA_CFLAGS)
+CPPFLAGS += $(EXTRA_CPPFLAGS)
+LDFLAGS += $(EXTRA_LDFLAGS)
+LDLIBS += $(EXTRA_LDLIBS)
--- /dev/null
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright 2019, Olivier MATZ <zer0@droids-corp.org>
+ */
+
+#include <avr/io.h>
+
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+
+#include "callout.h"
+#include "main.h"
+#include "buzzer.h"
+
+#define BUZZER_RESOLUTION_MS 8
+
+struct buzzer_state {
+ uint16_t ms; /* current time in ms */
+ uint16_t duration_ms;
+
+ uint16_t vibrato_ms; /* time in ms, modulo vibrato period */
+ uint16_t vibrato_period_ms;
+ uint8_t type; /* saw, sin, cos, ... */
+
+ float p1; /* start tone period */
+ float k; /* coef to scale to end tone period */
+};
+
+static struct buzzer_state buzzer_state;
+static struct callout buzzer_timer;
+
+/* reminder: period_us is approximately between 100 and 10000.
+ * period_us -> freq
+ * 100 us -> 10000Hz
+ * 1000 us -> 1000Hz
+ * 10000 us -> 100Hz
+ */
+static void buzzer_set(uint16_t period_us)
+{
+ BUILD_BUG_ON(F_CPU != 8000000UL);
+ TCCR1B |= (1 << CS10); /* count at F_CPU */
+
+ /* duty cycle is 50%, but we could control it in the future */
+ OCR1B = period_us * 2;
+ OCR1A = period_us * 4;
+}
+
+void buzzer_stop(void)
+{
+ /* just stop timer that controls the PWM */
+ TCCR1B &= ~(1 << CS10);
+}
+
+static void buzzer_cb(struct callout_mgr *cm,
+ struct callout *tim, void *arg)
+{
+ struct buzzer_state *st = &buzzer_state;
+ float t, x, period_us;
+
+ (void)arg;
+
+ st->ms += BUZZER_RESOLUTION_MS;
+ if (st->duration_ms != 0 && st->ms >= st->duration_ms) {
+ buzzer_stop();
+ return;
+ }
+
+ if (st->type == VIBRATO_TYPE_NONE)
+ goto reschedule;
+
+ st->vibrato_ms += BUZZER_RESOLUTION_MS;
+ if (st->vibrato_ms >= st->vibrato_period_ms)
+ st->vibrato_ms -= st->vibrato_period_ms;
+
+ /* t is the time, normalized to be between 0 and 1 */
+ t = (float)st->vibrato_ms / (float)st->vibrato_period_ms;
+
+ /* x determines the normalized vibrato level, between 0 and 1 */
+ switch (st->type) {
+ case VIBRATO_TYPE_SIN:
+ x = (sin(2 * M_PI * t) + 1) / 2.;
+ break;
+ case VIBRATO_TYPE_COS:
+ x = (-cos(2 * M_PI * t) + 1) / 2.;
+ break;
+ case VIBRATO_TYPE_TRIANGLE:
+ if (t < 0.5)
+ x = t * 2;
+ else
+ x = 2 - (t * 2);
+ break;
+ case VIBRATO_TYPE_SQUARE:
+ if (t < 0.5)
+ x = 0;
+ else
+ x = 1;
+ break;
+ case VIBRATO_TYPE_SAW:
+ default:
+ x = t;
+ break;
+ }
+
+ /* scale x between period1 and period2 */
+ period_us = st->p1 * exp(st->k * x);
+ buzzer_set(period_us);
+
+reschedule:
+ callout_reschedule(cm, tim, BUZZER_RESOLUTION_MS);
+}
+
+void buzzer_init(void)
+{
+ DDRB |= (1 << 2); /* buzzer pin as output */
+
+ /* Fast PWM mode: OCR1A for TOP, OCR1B for half period */
+ TCCR1A = (1 << WGM11) | (1 << WGM10) | (1 << COM1B1);
+ TCCR1B = (1 << WGM13) | (1 << WGM12);
+
+ callout_init(&buzzer_timer, buzzer_cb, NULL, 128);
+}
+
+void buzzer(uint16_t period_us, uint16_t duration_ms)
+{
+ callout_stop(&callout_mgr, &buzzer_timer);
+ buzzer_stop();
+
+ if (period_us == 0)
+ return;
+
+ memset(&buzzer_state, 0, sizeof(buzzer_state));
+ buzzer_state.type = VIBRATO_TYPE_NONE;
+ buzzer_state.duration_ms = duration_ms;
+ buzzer_set(period_us);
+
+ callout_schedule(&callout_mgr, &buzzer_timer, 0);
+}
+
+void buzzer_vibrato(uint16_t period1_us, uint16_t period2_us,
+ uint16_t duration_ms, uint16_t vibrato_period_ms,
+ uint8_t vibrato_type)
+{
+ callout_stop(&callout_mgr, &buzzer_timer);
+ buzzer_stop();
+
+ if (vibrato_period_ms == 0 || period1_us == 0 || period2_us == 0)
+ return;
+
+ memset(&buzzer_state, 0, sizeof(buzzer_state));
+ buzzer_state.duration_ms = duration_ms;
+ buzzer_state.vibrato_period_ms = vibrato_period_ms;
+ buzzer_state.type = vibrato_type;
+ buzzer_state.p1 = (float)period1_us;
+ buzzer_state.k = log((float)period2_us / (float)period1_us);
+
+ callout_schedule(&callout_mgr, &buzzer_timer, 0);
+}
--- /dev/null
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright 2019, Olivier MATZ <zer0@droids-corp.org>
+ */
+
+#ifndef BUZZER_H_
+#define BUZZER_H_
+
+/* initialize timer1 for buzzer */
+void buzzer_init(void);
+
+/* stop buzzer */
+void buzzer_stop(void);
+
+/* buzz with specified period during specified time
+ * period_us is between 100 and 10000, duration_ms max is 32767 */
+void buzzer(uint16_t period_us, uint16_t duration_ms);
+
+#define VIBRATO_TYPE_NONE 0
+#define VIBRATO_TYPE_SAW 1
+#define VIBRATO_TYPE_TRIANGLE 2
+#define VIBRATO_TYPE_SIN 3
+#define VIBRATO_TYPE_COS 4
+#define VIBRATO_TYPE_SQUARE 5
+
+/* buzz with a vibrato effect */
+void buzzer_vibrato(uint16_t period1_us, uint16_t period2_us,
+ uint16_t duration_ms, uint16_t vibrato_period_ms,
+ uint8_t vibrato_type);
+
+#endif
--- /dev/null
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright 2014-2019, Olivier MATZ <zer0@droids-corp.org>
+ * Copyright 2010 Intel Corporation
+ * (Inspired by Intel DPDK rte_timer library)
+ */
+
+#include <avr/pgmspace.h>
+
+#include <queue.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <inttypes.h>
+
+#include <irq.h>
+#include <callout.h>
+
+/* allow to browse a list while modifying the current element */
+#define _LIST_FOREACH_SAFE(cur, next, head, field) \
+ for ((cur) = LIST_FIRST((head)), \
+ (next) = ((cur) ? LIST_NEXT((cur), field) : NULL); \
+ (cur); \
+ (cur) = (next), \
+ (next) = ((cur) ? LIST_NEXT((cur), field) : NULL))
+
+#ifdef CALLOUT_STATS
+/* called with irq locked */
+#define CALLOUT_STAT_ADD(cm, field, x) do { \
+ cm->stats.field += x; \
+ } while(0)
+#else
+#define CALLOUT_STAT_ADD(cm, field, x) do { } while(0)
+#endif
+
+#ifdef CALLOUT_DEBUG
+#define callout_dprintf(fmt, ...) \
+ printf_P(PSTR("%s(): " fmt), __FUNCTION__, __VA_ARGS__)
+#else
+#define callout_dprintf(...) do { } while (0)
+#endif
+
+/* Initialize a callout manager */
+void
+callout_mgr_init(struct callout_mgr *cm,
+ callout_get_time_t *get_time)
+{
+ memset(cm, 0, sizeof(*cm));
+ cm->get_time = get_time;
+ LIST_INIT(&cm->sched_list);
+}
+
+/* Initialize the timer handle tim for use */
+void
+callout_init(struct callout *tim, callout_cb_t f, void *arg,
+ uint8_t priority)
+{
+ memset(tim, 0, sizeof(*tim));
+ tim->f = f;
+ tim->arg = arg;
+ tim->priority = priority;
+}
+
+/*
+ * Add a timer in the scheduled list (timer must not already be in a list). The
+ * timers are sorted in the list according the expire time (the closer timers
+ * first).
+ *
+ * called with irq locked
+ */
+static void
+callout_add_in_sched_list(struct callout_mgr *cm, struct callout *tim)
+{
+ struct callout *t, *prev_t;
+
+ callout_dprintf("cm=%p tim=%p\r\n", cm, tim);
+ tim->state = CALLOUT_STATE_SCHEDULED;
+
+ /* list is empty */
+ if (LIST_EMPTY(&cm->sched_list)) {
+ LIST_INSERT_HEAD(&cm->sched_list, tim, next);
+ return;
+ }
+
+ /* 'tim' expires before first entry */
+ t = LIST_FIRST(&cm->sched_list);
+ if ((int16_t)(tim->expire - t->expire) <= 0) {
+ LIST_INSERT_HEAD(&cm->sched_list, tim, next);
+ return;
+ }
+
+ /* find an element that will expire after 'tim' */
+ LIST_FOREACH(t, &cm->sched_list, next) {
+ if ((int16_t)(tim->expire - t->expire) <= 0) {
+ LIST_INSERT_BEFORE(t, tim, next);
+ return;
+ }
+ prev_t = t;
+ }
+
+ /* not found, insert at the end of the list */
+ LIST_INSERT_AFTER(prev_t, tim, next);
+}
+
+/*
+ * Add a timer in the local expired list (timer must not already be in a
+ * list). The timers are sorted in the list according to the priority (high
+ * priority first).
+ *
+ * called with irq locked
+ */
+static void
+callout_add_in_expired_list(struct callout_mgr *cm,
+ struct callout_list *expired_list, struct callout *tim)
+{
+ struct callout *t, *prev_t;
+
+ (void)cm; /* avoid warning if debug is disabled */
+
+ callout_dprintf("cm=%p tim=%p\r\n", cm, tim);
+ tim->state = CALLOUT_STATE_EXPIRED;
+
+ /* list is empty */
+ if (LIST_EMPTY(expired_list)) {
+ LIST_INSERT_HEAD(expired_list, tim, next);
+ return;
+ }
+
+ /* 'tim' has a higher prio */
+ t = LIST_FIRST(expired_list);
+ if (tim->priority >= t->priority) {
+ LIST_INSERT_HEAD(expired_list, tim, next);
+ return;
+ }
+
+ /* find an element that will expire after 'tim' */
+ LIST_FOREACH(t, expired_list, next) {
+ if (tim->priority >= t->priority) {
+ LIST_INSERT_BEFORE(t, tim, next);
+ return;
+ }
+ prev_t = t;
+ }
+
+ /* not found, insert at the end of the list */
+ LIST_INSERT_AFTER(prev_t, tim, next);
+}
+
+/*
+ * del from list (timer must be in a list)
+ */
+static void
+callout_del(struct callout_mgr *cm, struct callout *tim)
+{
+ (void)cm; /* avoid warning if debug is disabled */
+ callout_dprintf("cm=%p tim=%p\r\n", cm, tim);
+ LIST_REMOVE(tim, next);
+}
+
+/* Reset and start the timer associated with the timer handle tim */
+static int
+__callout_schedule(struct callout_mgr *cm, struct callout *tim,
+ uint16_t expire)
+{
+ irqflags_t flags;
+
+ callout_dprintf("cm=%p tim=%p expire=%d\r\n",
+ cm, tim, expire);
+
+ flags = irq_lock_save();
+ CALLOUT_STAT_ADD(cm, schedule, 1);
+
+ /* remove it from list */
+ if (tim->state != CALLOUT_STATE_STOPPED) {
+ /* stats */
+ if (tim->state == CALLOUT_STATE_SCHEDULED)
+ CALLOUT_STAT_ADD(cm, cur_scheduled, -1);
+ else if (tim->state == CALLOUT_STATE_EXPIRED)
+ CALLOUT_STAT_ADD(cm, cur_expired, -1);
+ if (tim->state == CALLOUT_STATE_RUNNING)
+ CALLOUT_STAT_ADD(cm, cur_running, -1);
+
+ callout_del(cm, tim);
+ }
+
+ tim->expire = expire;
+ CALLOUT_STAT_ADD(cm, cur_scheduled, 1);
+ callout_add_in_sched_list(cm, tim);
+ irq_unlock_restore(flags);
+
+ return 0;
+}
+
+/* Reset and start the timer associated with the timer handle tim */
+int
+callout_schedule(struct callout_mgr *cm, struct callout *tim,
+ uint16_t ticks)
+{
+ return __callout_schedule(cm, tim, cm->get_time() + ticks);
+}
+
+/* Reset and start the timer associated with the timer handle tim */
+int
+callout_reschedule(struct callout_mgr *cm, struct callout *tim,
+ uint16_t ticks)
+{
+ return __callout_schedule(cm, tim, tim->expire + ticks);
+}
+
+/* Stop the timer associated with the timer handle tim */
+void
+callout_stop(struct callout_mgr *cm, struct callout *tim)
+{
+ irqflags_t flags;
+
+ callout_dprintf("cm=%p tim=%p\r\n", cm, tim);
+
+ flags = irq_lock_save();
+ if (tim->state != CALLOUT_STATE_STOPPED) {
+
+ /* stats */
+ if (tim->state == CALLOUT_STATE_SCHEDULED)
+ CALLOUT_STAT_ADD(cm, cur_scheduled, -1);
+ else if (tim->state == CALLOUT_STATE_EXPIRED)
+ CALLOUT_STAT_ADD(cm, cur_expired, -1);
+ if (tim->state == CALLOUT_STATE_RUNNING)
+ CALLOUT_STAT_ADD(cm, cur_running, -1);
+ CALLOUT_STAT_ADD(cm, stop, 1);
+
+ /* remove it from list */
+ callout_del(cm, tim);
+ tim->state = CALLOUT_STATE_STOPPED;
+ }
+ irq_unlock_restore(flags);
+}
+
+/* must be called periodically, run all timer that expired */
+void callout_manage(struct callout_mgr *cm)
+{
+ struct callout_list expired_list;
+ struct callout_list reschedule_list;
+ struct callout *tim, *tim_next;
+ uint16_t cur_time;
+ uint8_t old_prio;
+ int16_t diff;
+
+ CALLOUT_STAT_ADD(cm, manage, 1);
+ callout_dprintf("cm=%p\r\n", cm);
+
+ /* maximize the number of self-recursions */
+ if (cm->nb_recursion >= CALLOUT_MAX_RECURSION) {
+ CALLOUT_STAT_ADD(cm, max_recursion, 1);
+ return;
+ }
+
+ irq_lock();
+ cm->nb_recursion++;
+ LIST_INIT(&expired_list);
+ LIST_INIT(&reschedule_list);
+ cur_time = cm->get_time();
+ old_prio = cm->cur_priority;
+
+ /* move all expired timers in a local list */
+ _LIST_FOREACH_SAFE(tim, tim_next, &cm->sched_list, next) {
+
+ diff = cur_time - tim->expire;
+
+ /* check the expiration time (tasks are sorted) */
+ if (diff < 0)
+ break;
+
+ callout_dprintf("cm=%p diff=%d\r\n", cm, diff);
+
+ /* check the priority, if it's too low, inc stats */
+ if (tim->priority <= cm->cur_priority) {
+ if (diff < 16484)
+ CALLOUT_STAT_ADD(cm, delayed, 1);
+ else {
+ /* reschedule to avoid an overflow */
+ CALLOUT_STAT_ADD(cm, hard_delayed, 1);
+ LIST_REMOVE(tim, next);
+ tim->expire = cur_time;
+ LIST_INSERT_HEAD(&reschedule_list, tim, next);
+ }
+ continue;
+ }
+
+ LIST_REMOVE(tim, next);
+ callout_add_in_expired_list(cm, &expired_list, tim);
+ CALLOUT_STAT_ADD(cm, cur_scheduled, -1);
+ CALLOUT_STAT_ADD(cm, cur_expired, 1);
+ }
+
+ /* reschedule hard_delayed timers, this does not happen usually */
+ while (!LIST_EMPTY(&reschedule_list)) {
+ tim = LIST_FIRST(&reschedule_list);
+ LIST_REMOVE(tim, next);
+ callout_add_in_sched_list(cm, tim);
+ }
+
+ /* for each timer of 'expired' list, execute callback */
+ while (!LIST_EMPTY(&expired_list)) {
+ tim = LIST_FIRST(&expired_list);
+ LIST_REMOVE(tim, next);
+
+ /* execute callback function */
+ CALLOUT_STAT_ADD(cm, cur_expired, -1);
+ CALLOUT_STAT_ADD(cm, cur_running, 1);
+ tim->state = CALLOUT_STATE_RUNNING;
+ cm->cur_priority = tim->priority;
+ irq_unlock();
+ tim->f(cm, tim, tim->arg);
+ irq_lock();
+ }
+
+ cm->cur_priority = old_prio;
+ cm->nb_recursion--;
+ irq_unlock();
+}
+
+/* set the current priority level */
+uint8_t callout_mgr_set_prio(struct callout_mgr *cm, uint8_t new_prio)
+{
+ uint8_t old_prio;
+
+ old_prio = cm->cur_priority;
+ if (new_prio <= old_prio)
+ return old_prio;
+
+ cm->cur_priority = new_prio;
+ return old_prio;
+}
+
+/* restore the current priority level */
+void callout_mgr_restore_prio(struct callout_mgr *cm, uint8_t old_prio)
+{
+ cm->cur_priority = old_prio;
+}
+
+/* dump statistics about timers */
+void callout_dump_stats(struct callout_mgr *cm)
+{
+#ifdef CALLOUT_STATS
+ printf_P(PSTR("Timer statistics:\r\n"));
+ printf_P(PSTR(" schedule = %"PRIu32"\r\n"), cm->stats.schedule);
+ printf_P(PSTR(" stop = %"PRIu32"\r\n"), cm->stats.stop);
+ printf_P(PSTR(" manage = %"PRIu32"\r\n"), cm->stats.manage);
+ printf_P(PSTR(" max_recursion = %"PRIu32"\r\n"),
+ cm->stats.max_recursion);
+ printf_P(PSTR(" delayed = %"PRIu32"\r\n"), cm->stats.delayed);
+ printf_P(PSTR(" hard_delayed = %"PRIu32"\r\n"),
+ cm->stats.hard_delayed);
+
+ printf_P(PSTR(" cur_scheduled = %u\r\n"), cm->stats.cur_scheduled);
+ printf_P(PSTR(" cur_expired = %u\r\n"), cm->stats.cur_expired);
+ printf_P(PSTR(" cur_running = %u\r\n"), cm->stats.cur_running);
+#else
+ printf_P(PSTR("No timer statistics, CALLOUT_STATS is disabled\r\n"));
+#endif
+}
--- /dev/null
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright 2014-2015, Olivier MATZ <zer0@droids-corp.org>
+ * Copyright 2010 Intel Corporation
+ * (Inspired by Intel DPDK rte_timer library)
+ */
+
+#ifndef CALLOUT_H_
+#define CALLOUT_H_
+
+#include <queue.h>
+
+#define CALLOUT_STATS
+/* #define CALLOUT_DEBUG */
+
+/**
+ * This module provides a timer service. The manager function
+ * callout_manage() can be called from an interrupt or from a
+ * standard function (usually a main-loop). In the latter case, no
+ * preemption is possible.
+ *
+ * Each timer has a priority: the timers with higher priorities are
+ * scheduled before the others. This feature is mostly useful when the
+ * manager is called from an interrupt. Indeed, the callback function of
+ * a timer with a high priority cannot be preempted by a timer with a
+ * lower priority.
+ *
+ * The module locks interrupts when doing critical operations, ensuring that
+ * critical data are accessed atomically.
+ *
+ * State of timers:
+ * - stopped: initial state after callout_init()
+ * - scheduled: after a call to callout_schedule(), the timer is in the
+ * scheduled list of the callout manager
+ * - expired: after a call to callout_manage(), if the expire time of a
+ * timer is reached, it is moved in a local list and its state is
+ * changed to "expired".
+ * - before starting the callback, the timer goes in state "running".
+ *
+ * Once running, the associated timer is not touched anymore by
+ * callout_manage(). As a result, the timer MUST be either reloaded
+ * or stopped (and potentially freed).
+ */
+
+/**
+ * Maximum number of nested preemptions.
+ */
+#define CALLOUT_MAX_RECURSION 5
+
+#ifdef CALLOUT_STATS
+/**
+ * The structure that stores the timer statistics, mostly useful for debug
+ * purposes.
+ */
+struct callout_debug_stats {
+ uint32_t schedule; /**< nb of calls to callout_(re)schedule() */
+ uint32_t stop; /**< nb of calls to callout_stop() */
+ uint32_t manage; /**< nb of calls to callout_manage() */
+ uint32_t max_recursion; /** manage() skipped due to max recursion */
+ uint32_t delayed; /** task delayed a bit due to low prio */
+ uint32_t hard_delayed; /** task recheduled later due to low priority */
+
+ uint8_t cur_scheduled; /**< current number of scheduled timers */
+ uint8_t cur_expired; /**< current number of expired timers */
+ uint8_t cur_running; /**< current number of running timers */
+};
+#endif
+
+struct callout;
+struct callout_mgr;
+
+/**
+ * The type of a callout callback function.
+ */
+typedef void (callout_cb_t)(struct callout_mgr *cm,
+ struct callout *tim, void *arg);
+
+/**
+ * A callout structure, storing all data associated to a timer.
+ */
+struct callout {
+ LIST_ENTRY(callout) next; /**< next/prev in list */
+
+#define CALLOUT_STATE_STOPPED 0 /**< not scheduled */
+#define CALLOUT_STATE_SCHEDULED 1 /**< in the scheduled list */
+#define CALLOUT_STATE_EXPIRED 2 /**< expired, will be executed soon */
+#define CALLOUT_STATE_RUNNING 3 /**< being executed */
+ uint8_t state; /**< stopped, scheduled, expired */
+ uint8_t priority; /**< the priority of the timer */
+ uint16_t expire; /**< time when timer should expire */
+
+ callout_cb_t *f; /**< callback function pointer */
+ void *arg; /**< argument given to the cb function. */
+};
+
+/* define the callout list */
+LIST_HEAD(callout_list, callout);
+
+/* static initializer for a timer structure */
+#define CALLOUT_INITIALIZER { }
+
+/**
+ * Type of the function used by a callout manager to get a time reference
+ */
+typedef uint16_t (callout_get_time_t)(void);
+
+/**
+ * An instance of callout manager. It is possible to have several
+ * managers. A callout is attached to one manager at a time.
+ */
+struct callout_mgr {
+ callout_get_time_t *get_time; /**< func to get the time reference */
+ uint16_t prev_time; /**< time of previous call */
+ uint8_t cur_priority; /** priority of running event */
+ uint8_t nb_recursion; /** number of recursion */
+ struct callout_list sched_list; /**< list of scheduled timers */
+
+#ifdef CALLOUT_STATS
+ struct callout_debug_stats stats; /**< stats */
+#endif
+};
+
+/**
+ * Initialize a callout manager
+ *
+ * The callout manager must be initialized before callout_add() or
+ * callout_manage() can be called.
+ *
+ * @param cm
+ * Pointer to the uninitialized callout manager structure.
+ * @param get_time
+ * Pointer to a function that returns a time reference (unsigned 16 bits).
+ */
+void callout_mgr_init(struct callout_mgr *cm,
+ callout_get_time_t *get_time);
+
+/**
+ * Initialize a callout structure and set callback function
+ *
+ * Before doing any operation on the callout structure, it has to be
+ * initialized with this function. It is possible to reinitialize a
+ * timer that has been previously scheduled, but it must be stopped.
+ *
+ * @param tim
+ * The timer to initialize.
+ * @param priority
+ * The priority of the callout (high value means higher priority)
+ * @param f
+ * The callback function of the timer.
+ * @param arg
+ * The user argument of the callback function.
+ */
+void callout_init(struct callout *tim, callout_cb_t f, void *arg,
+ uint8_t priority);
+
+/**
+ * Schedule a callout
+ *
+ * The callout_schedule() function adds the timer in the scheduled
+ * list. After the specified amount of ticks are elapsed, the callback
+ * function of the timer previously given to callout_init() will be
+ * invoked with its argument.
+ *
+ * The given "tick" value is relative to the current time, and is 16 bits
+ * wide. As it internally uses signed 16 bits comparison, the max value for
+ * ticks is 32767.
+ *
+ * @param cm
+ * The callout manager where the timer should be scheduled
+ * @param tim
+ * The timer handle
+ * @param ticks
+ * The number of ticks before the callback function is called, relative to now
+ * (the reference is given by the get_time() function of the callout manager).
+ * @return
+ * 0 on success, negative on error
+ */
+int callout_schedule(struct callout_mgr *cm, struct callout *tim,
+ uint16_t ticks);
+
+/**
+ * Reschedule a callout
+ *
+ * This function does exactly the same than callout_schedule()
+ * except that the given time "ticks" is not relative to the current
+ * time but to the "expire" field of the timer.
+ *
+ * Using this function is advised to avoid drift if you want to have periodic
+ * timers.
+ *
+ * This function should preferably be called from the callback function of
+ * the timer. Indeed, if the "expire" field should be a known value or it
+ * can result in an undefined behavior
+ *
+ * The given "tick" value is relative to the "expire" field of the
+ * timer, and is 16 bits wide. As it internally uses signed 16 bits
+ * comparison, the max value for ticks is 32767.
+ *
+ * @param cm
+ * The callout manager where the timer should be scheduled
+ * @param tim
+ * The timer handle
+ * @param ticks
+ * The number of ticks before the callback function is called, relative to
+ * the "expire" value of the timer
+ * @return
+ * 0 on success, negative on error
+ */
+int callout_reschedule(struct callout_mgr *cm, struct callout *tim,
+ uint16_t ticks);
+
+/**
+ * Stop a timer.
+ *
+ * The callout_stop() function stops a timer associated with the
+ * timer handle tim.
+ *
+ * If the timer is scheduled or expired, it is removed from the list:
+ * the callback function won't be invoked. If the timer is stopped or
+ * running the function does nothing.
+ *
+ * If a timer structure is dynamically allocated, invoking
+ * callout_stop() is needed before freeing the structure, even if
+ * the freeing occurs in a callback. Indeed, this function can be called
+ * safely from a timer callback. If it succeeds, the timer is not
+ * referenced anymore by the callout manager.
+ *
+ * @param cm
+ * The callout manager where the timer is or was scheduled
+ * @param tim
+ * The timer
+ * @return
+ * 0 on success, negative on error
+ */
+void callout_stop(struct callout_mgr *cm, struct callout *tim);
+
+/**
+ * Return the state of a timer
+ *
+ * @param tim
+ * The timer
+ * @return
+ * - CALLOUT_STATE_STOPPED: the timer is stopped
+ * - CALLOUT_STATE_SCHEDULED: the timer is scheduled
+ * - CALLOUT_STATE_EXPIRED: the timer was moved in a local list before
+ * execution
+ */
+static inline uint8_t callout_state(struct callout *tim)
+{
+ return tim->state;
+}
+
+/**
+ * Manage the timer list and execute callback functions.
+ *
+ * This function must be called periodically, either from a loop of from
+ * an interrupt. It browses the list of scheduled timers and runs all
+ * timers that are expired.
+ *
+ * This function must be called at least every 16384 reference ticks of
+ * cm->get_time(), but calling it more often is recommanded to avoid
+ * delaying task abusively.
+ *
+ * The function must be called with IRQ allowed.
+ */
+void callout_manage(struct callout_mgr *cm);
+
+/**
+ * Dump statistics about timers.
+ */
+void callout_dump_stats(struct callout_mgr *cm);
+
+/**
+ * Set the current priority level
+ *
+ * Prevent callout with a priority lower than "new_prio" to be executed.
+ * If the current priority of the callout manager is already lower higher
+ * than "new_prio", the function won't change the running priority.
+ *
+ * The returned value should be stored by the caller and restored with
+ * callout_mgr_restore_prio(), preferably in the same function.
+ *
+ * @param cm
+ * The callout manager
+ * @param new_prio
+ * The new running priority
+ *
+ * @return
+ * The value of the running priority before the call og this function
+ */
+uint8_t callout_mgr_set_prio(struct callout_mgr *cm, uint8_t new_prio);
+
+/**
+ * Restore the current priority level
+ *
+ * Used after a call to callout_mgr_set_prio().
+ *
+ * @param cm
+ * The callout manager
+ * @param old_prio
+ * The old running priority
+ */
+void callout_mgr_restore_prio(struct callout_mgr *cm, uint8_t old_prio);
+
+#endif /* CALLOUT_H_ */
--- /dev/null
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright 2009-2015, Olivier MATZ <zer0@droids-corp.org>
+ */
+
+#include <string.h>
+#include <errno.h>
+
+#include <cirbuf.h>
+
+/* init a circular buffer */
+void
+cirbuf_init(struct cirbuf *cbuf, char *buf, unsigned start,
+ unsigned maxlen)
+{
+ cbuf->maxlen = maxlen;
+ cbuf->len = 0;
+ cbuf->start = start;
+ cbuf->buf = buf;
+}
+
+/* return the index of the tail (contains an empty element) */
+static unsigned
+cirbuf_get_end(const struct cirbuf *cbuf)
+{
+ unsigned end;
+
+ end = cbuf->start + cbuf->len;
+ if (end >= cbuf->maxlen)
+ end -= cbuf->maxlen;
+
+ return end;
+}
+
+/* multiple add at head */
+int
+cirbuf_add_buf_head(struct cirbuf *cbuf, const char *buf, unsigned n)
+{
+ unsigned remain = n;
+ int copy_start;
+ unsigned copy_len;
+
+ if (remain == 0 || remain > cirbuf_get_freelen(cbuf))
+ return -EINVAL;
+
+ copy_start = cbuf->start - remain;
+ if (copy_start < 0)
+ copy_start += cbuf->maxlen;
+ cbuf->start = copy_start;
+ cbuf->len += remain;
+ copy_len = remain;
+ if ((unsigned)copy_start + copy_len >= cbuf->maxlen)
+ copy_len = cbuf->maxlen - copy_start;
+ if (copy_len > 0) {
+ memcpy(cbuf->buf + copy_start, buf, copy_len);
+ buf += copy_len;
+ remain -= copy_len;
+ }
+ if (remain == 0)
+ return n;
+
+ copy_len = remain;
+ memcpy(cbuf->buf, buf, copy_len);
+
+ return n;
+}
+
+/* multiple add at tail */
+int
+cirbuf_add_buf_tail(struct cirbuf *cbuf, const char *buf, unsigned n)
+{
+ unsigned remain = n;
+ unsigned copy_start, copy_len;
+
+ if (remain == 0 || remain > cirbuf_get_freelen(cbuf))
+ return -EINVAL;
+
+ copy_start = cirbuf_get_end(cbuf);
+ cbuf->len += remain;
+ copy_len = remain;
+ if (copy_start + copy_len >= cbuf->maxlen)
+ copy_len = cbuf->maxlen - copy_start;
+ if (copy_len > 0) {
+ memcpy(cbuf->buf + copy_start, buf, copy_len);
+ buf += copy_len;
+ remain -= copy_len;
+ }
+ if (remain == 0)
+ return n;
+
+ copy_len = remain;
+ memcpy(cbuf->buf, buf, copy_len);
+
+ return n;
+}
+
+/* single add at head */
+static inline void
+__cirbuf_add_head(struct cirbuf *cbuf, char c)
+{
+ unsigned start = cbuf->start;
+
+ if (start == 0)
+ start = cbuf->maxlen - 1;
+ else
+ start = start - 1;
+ cbuf->buf[start] = c;
+ cbuf->start = start;
+ cbuf->len++;
+}
+
+/* single add at head, checking if full first */
+int
+cirbuf_add_head_safe(struct cirbuf *cbuf, char c)
+{
+ if (!cirbuf_is_full(cbuf)) {
+ __cirbuf_add_head(cbuf, c);
+ return 0;
+ }
+ return -EINVAL;
+}
+
+/* single add at head */
+void
+cirbuf_add_head(struct cirbuf *cbuf, char c)
+{
+ __cirbuf_add_head(cbuf, c);
+}
+
+/* single add at tail */
+static inline void
+__cirbuf_add_tail(struct cirbuf *cbuf, char c)
+{
+ unsigned end = cirbuf_get_end(cbuf);
+
+ cbuf->buf[end] = c;
+ cbuf->len++;
+}
+
+/* single add at tail, checking if full first */
+int
+cirbuf_add_tail_safe(struct cirbuf *cbuf, char c)
+{
+ if (!cirbuf_is_full(cbuf)) {
+ __cirbuf_add_tail(cbuf, c);
+ return 0;
+ }
+ return -EINVAL;
+}
+
+/* single add at tail */
+void
+cirbuf_add_tail(struct cirbuf *cbuf, char c)
+{
+ __cirbuf_add_tail(cbuf, c);
+}
+
+/* multiple delete at head */
+int
+cirbuf_del_buf_head(struct cirbuf *cbuf, unsigned size)
+{
+ if (size == 0 || size > cirbuf_get_len(cbuf))
+ return -EINVAL;
+
+ cbuf->len -= size;
+ if (cirbuf_is_empty(cbuf)) {
+ cbuf->start += size - 1;
+ cbuf->start %= cbuf->maxlen;
+ }
+ else {
+ cbuf->start += size;
+ cbuf->start %= cbuf->maxlen;
+ }
+ return 0;
+}
+
+/* multiple delete at tail */
+int
+cirbuf_del_buf_tail(struct cirbuf *cbuf, unsigned size)
+{
+ if (size == 0 || size > cirbuf_get_len(cbuf))
+ return -EINVAL;
+
+ cbuf->len -= size;
+ return 0;
+}
+
+/* single del at head */
+static inline void
+__cirbuf_del_head(struct cirbuf *cbuf)
+{
+ cbuf->len --;
+ if (!cirbuf_is_empty(cbuf)) {
+ cbuf->start ++;
+ cbuf->start %= cbuf->maxlen;
+ }
+}
+
+/* single del at head, checking if empty first */
+int
+cirbuf_del_head_safe(struct cirbuf *cbuf)
+{
+ if (cbuf && !cirbuf_is_empty(cbuf)) {
+ __cirbuf_del_head(cbuf);
+ return 0;
+ }
+ return -EINVAL;
+}
+
+/* single del at head */
+void
+cirbuf_del_head(struct cirbuf *cbuf)
+{
+ __cirbuf_del_head(cbuf);
+}
+
+/* single del at tail */
+static inline void
+__cirbuf_del_tail(struct cirbuf *cbuf)
+{
+ cbuf->len--;
+}
+
+/* single del at tail, checking if empty first */
+int
+cirbuf_del_tail_safe(struct cirbuf *cbuf)
+{
+ if (cbuf && !cirbuf_is_empty(cbuf)) {
+ __cirbuf_del_tail(cbuf);
+ return 0;
+ }
+ return -EINVAL;
+}
+
+/* single del at tail */
+void
+cirbuf_del_tail(struct cirbuf *cbuf)
+{
+ __cirbuf_del_tail(cbuf);
+}
+
+/* convert to buffer */
+int
+cirbuf_get_buf_head(const struct cirbuf *cbuf, char *buf, unsigned n)
+{
+ unsigned remain = n;
+ unsigned cirbuf_len, copy_start, copy_len;
+
+ cirbuf_len = cirbuf_get_len(cbuf);
+ if (remain >= cirbuf_len)
+ remain = cirbuf_len;
+
+ if (remain == 0)
+ return 0;
+
+ copy_start = cbuf->start;
+ copy_len = remain;
+ if (copy_start + copy_len >= cbuf->maxlen)
+ copy_len = cbuf->maxlen - copy_start;
+ if (copy_len > 0) {
+ memcpy(buf, cbuf->buf + copy_start, copy_len);
+ buf += copy_len;
+ remain -= copy_len;
+ }
+ if (remain == 0)
+ return n;
+
+ copy_len = remain;
+ memcpy(buf, cbuf->buf, copy_len);
+
+ return n;
+}
+
+/* convert to buffer */
+int
+cirbuf_get_buf_tail(const struct cirbuf *cbuf, char *buf, unsigned n)
+{
+ unsigned remain = n;
+ int copy_start;
+ unsigned cirbuf_len, copy_len;
+
+ cirbuf_len = cirbuf_get_len(cbuf);
+ if (remain >= cirbuf_len)
+ remain = cirbuf_len;
+
+ if (remain == 0)
+ return 0;
+
+ copy_start = cirbuf_get_end(cbuf) - remain;
+ if (copy_start < 0)
+ copy_start += cbuf->maxlen;
+ copy_len = remain;
+ if ((unsigned)copy_start + copy_len >= cbuf->maxlen)
+ copy_len = cbuf->maxlen - copy_start;
+ if (copy_len > 0) {
+ memcpy(buf, cbuf->buf + copy_start, copy_len);
+ buf += copy_len;
+ remain -= copy_len;
+ }
+ if (remain == 0)
+ return n;
+
+ copy_len = remain;
+ memcpy(buf, cbuf->buf, copy_len);
+
+ return n;
+}
+
+/* get head */
+char
+cirbuf_get_head(const struct cirbuf *cbuf)
+{
+ return cbuf->buf[cbuf->start];
+}
+
+/* get tail */
+char
+cirbuf_get_tail(const struct cirbuf *cbuf)
+{
+ unsigned end;
+
+ /* should not happen */
+ if (cbuf->len == 0)
+ return 0;
+
+ end = cbuf->start + cbuf->len - 1;
+ if (end >= cbuf->maxlen)
+ end -= cbuf->maxlen;
+ return cbuf->buf[end];
+}
+
+static void
+__cirbuf_shift(struct cirbuf *cbuf, unsigned n)
+{
+ char tmp, tmp2;
+ unsigned start, cur, min;
+
+ start = 0;
+ cur = 0;
+ tmp = cbuf->buf[0];
+ min = cbuf->maxlen;
+
+ while (1) {
+ cur = cur + n;
+ if (cur >= cbuf->maxlen)
+ cur -= cbuf->maxlen;
+ tmp2 = cbuf->buf[cur];
+ cbuf->buf[cur] = tmp;
+ tmp = tmp2;
+ if (cur == start) {
+ if ((cur + 1) == min)
+ break;
+ cur++;
+ tmp = cbuf->buf[cur];
+ start = cur;
+ } else if (cur < min) {
+ min = cur;
+ }
+ }
+
+ cbuf->start += n;
+ if (cbuf->start >= cbuf->maxlen)
+ cbuf->start -= cbuf->maxlen;
+}
+
+void cirbuf_align_left(struct cirbuf *cbuf)
+{
+ __cirbuf_shift(cbuf, cbuf->maxlen - cbuf->start);
+}
+
+void cirbuf_align_right(struct cirbuf *cbuf)
+{
+ unsigned end = cirbuf_get_end(cbuf);
+ __cirbuf_shift(cbuf, cbuf->maxlen - end);
+}
--- /dev/null
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright 2009-2015, Olivier MATZ <zer0@droids-corp.org>
+ */
+
+#ifndef CIRBUF_H_
+#define CIRBUF_H_
+
+#include <stdio.h>
+
+/**
+ * A circular buffer.
+ */
+struct cirbuf {
+ unsigned maxlen; /**< Total length of the fifo (number of elements). */
+ unsigned start; /**< Index of the first element. */
+ unsigned len; /**< Current len of fifo. */
+ char *buf; /**< Pointer to the data buffer. */
+};
+
+/**
+ * Initialize a circular buffer.
+ *
+ * @param cbuf
+ * A pointer to an uninitialized circular buffer structure.
+ * @param buf
+ * The buffer used to store the data.
+ * @param start
+ * The index of head at initialization.
+ * @param maxlen
+ * The size of the buffer.
+ */
+void cirbuf_init(struct cirbuf *cbuf, char *buf, unsigned start,
+ unsigned maxlen);
+
+/**
+ * Check if the circular buffer is full.
+ *
+ * @param cbuf
+ * The circular buffer pointer.
+ * @return
+ * 1 if the circular buffer is full, else 0.
+ */
+static inline int cirbuf_is_full(const struct cirbuf *cbuf)
+{
+ return cbuf->len == cbuf->maxlen;
+}
+
+/**
+ * Check if the circular buffer is empty.
+ *
+ * @param cbuf
+ * The circular buffer pointer.
+ * @return
+ * 1 if the circular buffer is empty, else 0.
+ */
+static inline int cirbuf_is_empty(const struct cirbuf *cbuf)
+{
+ return cbuf->len == 0;
+}
+
+/**
+ * Get the length of data in the circular buffer.
+ *
+ * @param cbuf
+ * The circular buffer pointer.
+ * @return
+ * The current length of data in the circular buffer.
+ */
+static inline unsigned cirbuf_get_len(const struct cirbuf *cbuf)
+{
+ return cbuf->len;
+}
+
+/**
+ * Get the size of the circular buffer.
+ *
+ * @param cbuf
+ * The circular buffer pointer.
+ * @return
+ * Return the maximum size of the circular buffer (used + free elements)
+ */
+static inline unsigned cirbuf_get_maxlen(const struct cirbuf *cbuf)
+{
+ return cbuf->maxlen;
+}
+
+/**
+ * Get the lenght of free space in the circular buffer.
+ *
+ * @param cbuf
+ * The circular buffer pointer.
+ * @return
+ * Return the length of free space.
+ */
+static inline unsigned cirbuf_get_freelen(const struct cirbuf *cbuf)
+{
+ return cbuf->maxlen - cbuf->len;
+}
+
+/**
+ * Iterator for a circular buffer
+ *
+ * cirbuf: struct cirbuf pointer
+ * i: an integer internally used in the macro
+ * elt: char that takes the value for each iteration
+ */
+#define CIRBUF_FOREACH(cirbuf, i, elt) \
+ for (i = 0, elt = (cirbuf)->buf[(cirbuf)->start]; \
+ i < ((cirbuf)->len); \
+ i ++, elt = (cirbuf)->buf[((cirbuf)->start + i) % \
+ ((cirbuf)->maxlen)])
+
+/**
+ * Add a character at the head of the circular buffer.
+ *
+ * @param cbuf
+ * The circular buffer pointer.
+ * @param c
+ * The character to add.
+ * @return
+ * Return 0 on success, or a negative value on error.
+ */
+int cirbuf_add_head_safe(struct cirbuf *cbuf, char c);
+
+/**
+ * Add a character at the head of the circular buffer.
+ *
+ * The function does not check that there is enough free space
+ * in the buffer, so it has to be done by the caller. If it's
+ * not the case, undefined behavior will occur.
+ *
+ * @param cbuf
+ * The circular buffer pointer.
+ * @param c
+ * The character to add.
+ */
+void cirbuf_add_head(struct cirbuf *cbuf, char c);
+
+/**
+ * Add a character at the tail of the circular buffer.
+ *
+ * @param cbuf
+ * The circular buffer pointer.
+ * @param c
+ * The character to add.
+ * @return
+ * Return 0 on success, or a negative value on error.
+ */
+int cirbuf_add_tail_safe(struct cirbuf *cbuf, char c);
+
+/**
+ * Add a character at the tail of the circular buffer.
+ *
+ * The function does not check that there is enough free space
+ * in the buffer, so it has to be done by the caller. If it's
+ * not the case, undefined behavior will occur.
+ *
+ * @param cbuf
+ * The circular buffer pointer.
+ * @param c
+ * The character to add.
+ */
+void cirbuf_add_tail(struct cirbuf *cbuf, char c);
+
+/**
+ * Remove a char at the head of the circular buffer.
+ *
+ * @param cbuf
+ * The circular buffer pointer.
+ * @return
+ * Return 0 on success, or a negative value on error.
+ */
+int cirbuf_del_head_safe(struct cirbuf *cbuf);
+
+/**
+ * Remove a char at the head of the circular buffer.
+ *
+ * The function does not check that there is enough elements
+ * in the buffer, so it has to be done by the caller. If it's
+ * not the case, undefined behavior will occur.
+ *
+ * @param cbuf
+ * The circular buffer pointer.
+ */
+void cirbuf_del_head(struct cirbuf *cbuf);
+
+/**
+ * Remove a char at the tail of the circular buffer.
+ *
+ * @param cbuf
+ * The circular buffer pointer.
+ * @return
+ * Return 0 on success, or a negative value on error.
+ */
+int cirbuf_del_tail_safe(struct cirbuf *cbuf);
+
+/**
+ * Remove a char at the tail of the circular buffer.
+ *
+ * The function does not check that there is enough elements
+ * in the buffer, so it has to be done by the caller. If it's
+ * not the case, undefined behavior will occur.
+ *
+ * @param cbuf
+ * The circular buffer pointer.
+ */
+void cirbuf_del_tail(struct cirbuf *cbuf);
+
+/**
+ * Return the element at the tail of the circular buffer.
+ *
+ * The circular buffer must not be empty or an undefined character
+ * will be returned.
+ *
+ * @param cbuf
+ * The circular buffer pointer.
+ * @return
+ * The character at the tail of the circular buffer.
+ */
+char cirbuf_get_head(const struct cirbuf *cbuf);
+
+/**
+ * Return the element at the tail of the circular buffer.
+ *
+ * The circular buffer must not be empty or an undefined character
+ * will be returned.
+ *
+ * @param cbuf
+ * The circular buffer pointer.
+ * @return
+ * The character at the tail of the circular buffer.
+ */
+char cirbuf_get_tail(const struct cirbuf *cbuf);
+
+/**
+ * Add a buffer at the head of the circular buffer.
+ *
+ * Add 'n' bytes of buffer pointed by 'buf' ad the head of th
+ * circular buffer.
+ *
+ * @param cbuf
+ * The circular buffer pointer.
+ * @param buf
+ * The pointer to the buffer.
+ * @param n
+ * Number of bytes to add.
+ * @return
+ * Return 0 on success, or a negative value on error.
+ */
+int cirbuf_add_buf_head(struct cirbuf *cbuf, const char *buf,
+ unsigned n);
+
+/**
+ * Add a buffer at the tail of the circular buffer.
+ *
+ * Add 'n' bytes of buffer pointed by 'buf' ad the tail of th
+ * circular buffer.
+ *
+ * @param cbuf
+ * The circular buffer pointer.
+ * @param buf
+ * The pointer to the buffer.
+ * @param n
+ * Number of bytes to add.
+ * @return
+ * Return 0 on success, or a negative value on error.
+ */
+int cirbuf_add_buf_tail(struct cirbuf *cbuf, const char *buf,
+ unsigned n);
+
+/**
+ * Remove chars at the head of the circular buffer.
+ *
+ * @param cbuf
+ * The circular buffer pointer.
+ * @param n
+ * Number of bytes to remove.
+ * @return
+ * Return 0 on success, or a negative value on error.
+ */
+int cirbuf_del_buf_head(struct cirbuf *cbuf, unsigned n);
+
+/**
+ * Remove chars at the tail of the circular buffer.
+ *
+ * @param cbuf
+ * The circular buffer pointer.
+ * @param n
+ * Number of bytes to remove.
+ * @return
+ * Return 0 on success, or a negative value on error.
+ */
+int cirbuf_del_buf_tail(struct cirbuf *cbuf, unsigned n);
+
+/**
+ * Copy multiple bytes from the head of the circular buffer.
+ *
+ * Copy a maximum of 'n' characters from the head of the circular buffer
+ * into a flat buffer pointed by 'buf'. If the circular buffer is
+ * smaller than n, less bytes are copied.
+ *
+ * @param cbuf
+ * The circular buffer pointer.
+ * @param buf
+ * The pointer to the buffer.
+ * @param n
+ * Maximum number of bytes to copy.
+ * @return
+ * Return the number of copied chars.
+ */
+int cirbuf_get_buf_head(const struct cirbuf *cbuf, char *buf,
+ unsigned n);
+
+/**
+ * Copy multiple bytes from the tail of the circular buffer.
+ *
+ * Copy a maximum of 'n' characters from the tail of the circular buffer
+ * into a flat buffer pointed by 'buf'. If the circular buffer is
+ * smaller than n, less bytes are copied.
+ *
+ * @param cbuf
+ * The circular buffer pointer.
+ * @param buf
+ * The pointer to the buffer.
+ * @param n
+ * Maximum number of bytes to copy.
+ * @return
+ * Return the number of copied chars.
+ */
+int cirbuf_get_buf_tail(const struct cirbuf *cbuf, char *buf,
+ unsigned n);
+
+/**
+ * Set the start of the data to the index 0 of the internal buffer.
+ *
+ * After a call to this function, it is possible for the caller to
+ * use cbuf->buf as a linear (read-only) buffer.
+ *
+ * @param cbuf
+ * The circular buffer pointer.
+ */
+void cirbuf_align_left(struct cirbuf *cbuf);
+
+/**
+ * Set the end of the data to the last index of the internal buffer.
+ *
+ * After a call to this function, it is possible for the caller to
+ * use cbuf->buf as a linear (read-only) buffer.
+ *
+ * @param cbuf
+ * The circular buffer pointer.
+ */
+void cirbuf_align_right(struct cirbuf *cbuf);
+
+#endif /* CIRBUF_H_ */
--- /dev/null
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright 2019, Olivier MATZ <zer0@droids-corp.org>
+ */
+
+#include <avr/wdt.h>
+#include <avr/pgmspace.h>
+
+#include "irq.h"
+#include "uart.h"
+#include "led.h"
+#include "buzzer.h"
+#include "main.h"
+#include "commands.h"
+
+#define ntohs __builtin_bswap16
+#define htons __builtin_bswap16
+
+#define CMD_LED_DEBUG 0x00
+struct cmd_led_debug {
+ uint8_t on;
+};
+
+#define CMD_BUZZER 0x01
+struct cmd_buzzer {
+ uint16_t period_us;
+ uint16_t duration_ms;
+};
+
+#define CMD_LED_GYRO_ALL 0x02
+struct cmd_led_gyro_all {
+ uint16_t intensity;
+ uint16_t blink_on_ms;
+ uint16_t blink_off_ms;
+ uint16_t stop_ms;
+};
+
+#define CMD_LED_GYRO_RAY 0x03
+struct cmd_led_gyro_ray {
+ uint16_t angle;
+ uint16_t intensity;
+ uint16_t width;
+ int16_t rot_speed;
+ uint16_t stop_ms;
+};
+
+#define CMD_LOG_DEBUG 0x04
+struct cmd_log_debug {
+ uint8_t mask;
+};
+
+#define CMD_RESET 0x05
+
+#define CMD_BUZZER_VIBRATO 0x06
+struct cmd_buzzer_vibrato {
+ uint16_t period1_us;
+ uint16_t period2_us;
+ uint16_t duration_ms;
+ uint16_t vibrato_period_ms;
+ uint8_t vibrato_type;
+};
+
+#define CMD_MAX 0x07
+
+#define CMD_INVALID 0xFF
+
+struct cmd_buf {
+ uint8_t cmd;
+ uint8_t len;
+ union {
+ struct cmd_led_debug led_debug;
+ struct cmd_buzzer buzzer;
+ struct cmd_led_gyro_all led_gyro_all;
+ struct cmd_led_gyro_ray led_gyro_ray;
+ struct cmd_log_debug log_debug;
+ struct cmd_buzzer_vibrato buzzer_vibrato;
+ };
+ uint8_t csum;
+};
+
+/* current command buffer */
+static struct cmd_buf cmd_buf;
+static uint8_t cmd_len;
+static uint8_t cmd_csum;
+
+static void clear_cmd(void)
+{
+ cmd_len = 0;
+ cmd_csum = 0;
+}
+
+static uint8_t csum_add(uint8_t x, uint8_t y)
+{
+ uint16_t res = (uint16_t)x + (uint16_t)y;
+ res = (res >> 8) + (res & 0xff);
+ return res;
+}
+
+static void cmd_led_debug(void)
+{
+ if (cmd_buf.len != sizeof(struct cmd_led_debug) + 3)
+ return;
+ if (cmd_buf.led_debug.on)
+ led_debug_on();
+ else
+ led_debug_off();
+}
+
+static void cmd_buzzer(void)
+{
+ if (cmd_buf.len != sizeof(struct cmd_buzzer) + 3)
+ return;
+ buzzer(ntohs(cmd_buf.buzzer.period_us),
+ ntohs(cmd_buf.buzzer.duration_ms));
+}
+
+static void cmd_led_gyro_all(void)
+{
+ if (cmd_buf.len != sizeof(struct cmd_led_gyro_all) + 3)
+ return;
+ led_gyro_all(ntohs(cmd_buf.led_gyro_all.intensity),
+ ntohs(cmd_buf.led_gyro_all.blink_on_ms),
+ ntohs(cmd_buf.led_gyro_all.blink_off_ms),
+ ntohs(cmd_buf.led_gyro_all.stop_ms));
+}
+
+static void cmd_led_gyro_ray(void)
+{
+ if (cmd_buf.len != sizeof(struct cmd_led_gyro_ray) + 3)
+ return;
+ led_gyro_ray(
+ ntohs(cmd_buf.led_gyro_ray.angle),
+ ntohs(cmd_buf.led_gyro_ray.intensity),
+ ntohs(cmd_buf.led_gyro_ray.width),
+ ntohs(cmd_buf.led_gyro_ray.rot_speed),
+ ntohs(cmd_buf.led_gyro_ray.stop_ms));
+}
+
+static void cmd_log_debug(void)
+{
+ if (cmd_buf.len != sizeof(struct cmd_log_debug) + 3)
+ return;
+ log_debug = 1;
+}
+
+static void cmd_reset(void)
+{
+ wdt_enable(WDTO_15MS);
+ while(1);
+}
+
+static void cmd_buzzer_vibrato(void)
+{
+ if (cmd_buf.len != sizeof(struct cmd_buzzer_vibrato) + 3)
+ return;
+ buzzer_vibrato(ntohs(cmd_buf.buzzer_vibrato.period1_us),
+ ntohs(cmd_buf.buzzer_vibrato.period2_us),
+ ntohs(cmd_buf.buzzer_vibrato.duration_ms),
+ ntohs(cmd_buf.buzzer_vibrato.vibrato_period_ms),
+ cmd_buf.buzzer_vibrato.vibrato_type);
+}
+
+static void cmd_exec(void)
+{
+ if (cmd_csum != 0xff) {
+ clear_cmd();
+ return;
+ }
+
+ if (log_debug)
+ printf_P(PSTR("command %d received\n"), cmd_buf.cmd);
+
+ switch (cmd_buf.cmd) {
+ case CMD_LED_DEBUG: cmd_led_debug(); break;
+ case CMD_BUZZER: cmd_buzzer(); break;
+ case CMD_LED_GYRO_ALL: cmd_led_gyro_all(); break;
+ case CMD_LED_GYRO_RAY: cmd_led_gyro_ray(); break;
+ case CMD_LOG_DEBUG: cmd_log_debug(); break;
+ case CMD_RESET: cmd_reset(); break;
+ case CMD_BUZZER_VIBRATO: cmd_buzzer_vibrato(); break;
+ default: break;
+ }
+
+}
+
+void cmd_input(uint8_t c)
+{
+ if (cmd_len >= sizeof(cmd_buf))
+ clear_cmd();
+
+ if (cmd_len == 0) {
+ if (c >= CMD_MAX || c == CMD_INVALID) {
+ clear_cmd();
+ return;
+ }
+ } else if (cmd_len == 1) {
+ if (c < 3 || c > sizeof(cmd_buf)) {
+ clear_cmd();
+ return;
+ }
+ }
+
+ ((uint8_t *)&cmd_buf)[cmd_len] = c;
+ cmd_len++;
+ cmd_csum = csum_add(cmd_csum, c);
+
+ if (cmd_len >= 3 && cmd_len == cmd_buf.len) {
+ cmd_exec();
+ clear_cmd();
+ }
+}
--- /dev/null
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright 2019, Olivier MATZ <zer0@droids-corp.org>
+ */
+
+#ifndef _COMMANDS_H
+#define _COMMANDS_H
+
+#include <stdint.h>
+
+void cmd_input(uint8_t c);
+
+#endif
--- /dev/null
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 1982, 1986, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ */
+
+#ifndef ERRNO_H_
+#define ERRNO_H_
+
+#define EPERM 1 /* Operation not permitted */
+#define ENOENT 2 /* No such file or directory */
+#define ESRCH 3 /* No such process */
+#define EINTR 4 /* Interrupted system call */
+#define EIO 5 /* Input/output error */
+#define ENXIO 6 /* Device not configured */
+#define E2BIG 7 /* Argument list too long */
+#define ENOEXEC 8 /* Exec format error */
+#define EBADF 9 /* Bad file descriptor */
+#define ECHILD 10 /* No child processes */
+#define EDEADLK 11 /* Resource deadlock avoided */
+ /* 11 was EAGAIN */
+#define ENOMEM 12 /* Cannot allocate memory */
+#define EACCES 13 /* Permission denied */
+#define EFAULT 14 /* Bad address */
+#define ENOTBLK 15 /* Block device required */
+#define EBUSY 16 /* Device busy */
+#define EEXIST 17 /* File exists */
+#define EXDEV 18 /* Cross-device link */
+#define ENODEV 19 /* Operation not supported by device */
+#define ENOTDIR 20 /* Not a directory */
+#define EISDIR 21 /* Is a directory */
+#define EINVAL 22 /* Invalid argument */
+#define ENFILE 23 /* Too many open files in system */
+#define EMFILE 24 /* Too many open files */
+#define ENOTTY 25 /* Inappropriate ioctl for device */
+#define ETXTBSY 26 /* Text file busy */
+#define EFBIG 27 /* File too large */
+#define ENOSPC 28 /* No space left on device */
+#define ESPIPE 29 /* Illegal seek */
+#define EROFS 30 /* Read-only file system */
+#define EMLINK 31 /* Too many links */
+#define EPIPE 32 /* Broken pipe */
+
+/* math software */
+#define EDOM 33 /* Numerical argument out of domain */
+#define ERANGE 34 /* Result too large or too small */
+
+/* non-blocking and interrupt i/o */
+#define EAGAIN 35 /* Resource temporarily unavailable */
+#define EWOULDBLOCK EAGAIN /* Operation would block */
+#define EINPROGRESS 36 /* Operation now in progress */
+#define EALREADY 37 /* Operation already in progress */
+
+/* ipc/network software -- argument errors */
+#define ENOTSOCK 38 /* Socket operation on non-socket */
+#define EDESTADDRREQ 39 /* Destination address required */
+#define EMSGSIZE 40 /* Message too long */
+#define EPROTOTYPE 41 /* Protocol wrong type for socket */
+#define ENOPROTOOPT 42 /* Protocol option not available */
+#define EPROTONOSUPPORT 43 /* Protocol not supported */
+#define ESOCKTNOSUPPORT 44 /* Socket type not supported */
+#define EOPNOTSUPP 45 /* Operation not supported */
+#define EPFNOSUPPORT 46 /* Protocol family not supported */
+#define EAFNOSUPPORT 47 /* Address family not supported by protocol family */
+#define EADDRINUSE 48 /* Address already in use */
+#define EADDRNOTAVAIL 49 /* Can't assign requested address */
+
+/* ipc/network software -- operational errors */
+#define ENETDOWN 50 /* Network is down */
+#define ENETUNREACH 51 /* Network is unreachable */
+#define ENETRESET 52 /* Network dropped connection on reset */
+#define ECONNABORTED 53 /* Software caused connection abort */
+#define ECONNRESET 54 /* Connection reset by peer */
+#define ENOBUFS 55 /* No buffer space available */
+#define EISCONN 56 /* Socket is already connected */
+#define ENOTCONN 57 /* Socket is not connected */
+#define ESHUTDOWN 58 /* Can't send after socket shutdown */
+#define ETOOMANYREFS 59 /* Too many references: can't splice */
+#define ETIMEDOUT 60 /* Operation timed out */
+#define ECONNREFUSED 61 /* Connection refused */
+
+#define ELOOP 62 /* Too many levels of symbolic links */
+#define ENAMETOOLONG 63 /* File name too long */
+
+/* should be rearranged */
+#define EHOSTDOWN 64 /* Host is down */
+#define EHOSTUNREACH 65 /* No route to host */
+#define ENOTEMPTY 66 /* Directory not empty */
+
+/* quotas & mush */
+#define EPROCLIM 67 /* Too many processes */
+#define EUSERS 68 /* Too many users */
+#define EDQUOT 69 /* Disc quota exceeded */
+
+/* Network File System */
+#define ESTALE 70 /* Stale NFS file handle */
+#define EREMOTE 71 /* Too many levels of remote in path */
+#define EBADRPC 72 /* RPC struct is bad */
+#define ERPCMISMATCH 73 /* RPC version wrong */
+#define EPROGUNAVAIL 74 /* RPC prog. not avail */
+#define EPROGMISMATCH 75 /* Program version wrong */
+#define EPROCUNAVAIL 76 /* Bad procedure for program */
+
+#define ENOLCK 77 /* No locks available */
+#define ENOSYS 78 /* Function not implemented */
+
+#define EFTYPE 79 /* Inappropriate file type or format */
+#define EAUTH 80 /* Authentication error */
+#define ENEEDAUTH 81 /* Need authenticator */
+
+/* SystemV IPC */
+#define EIDRM 82 /* Identifier removed */
+#define ENOMSG 83 /* No message of desired type */
+#define EOVERFLOW 84 /* Value too large to be stored in data type */
+
+/* Wide/multibyte-character handling, ISO/IEC 9899/AMD1:1995 */
+#define EILSEQ 85 /* Illegal byte sequence */
+
+/* From IEEE Std 1003.1-2001 */
+/* Base, Realtime, Threads or Thread Priority Scheduling option errors */
+#define ENOTSUP 86 /* Not supported */
+
+/* Realtime option errors */
+#define ECANCELED 87 /* Operation canceled */
+
+/* Realtime, XSI STREAMS option errors */
+#define EBADMSG 88 /* Bad or Corrupt message */
+
+/* XSI STREAMS option errors */
+#define ENODATA 89 /* No message available */
+#define ENOSR 90 /* No STREAM resources */
+#define ENOSTR 91 /* Not a STREAM */
+#define ETIME 92 /* STREAM ioctl timeout */
+
+/* File system extended attribute errors */
+#define ENOATTR 93 /* Attribute not found */
+
+/* Realtime, XSI STREAMS option errors */
+#define EMULTIHOP 94 /* Multihop attempted */
+#define ENOLINK 95 /* Link has been severed */
+#define EPROTO 96 /* Protocol error */
+
+#define ELAST 96 /* Must equal largest errno */
+
+#include_next <errno.h>
+
+#endif /* !ERRNO_H_ */
--- /dev/null
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
+ */
+
+#ifndef IRQ_H_
+#define IRQ_H_
+
+#include <avr/interrupt.h>
+
+typedef uint8_t irqflags_t;
+
+static inline void irq_lock(void)
+{
+ cli();
+}
+
+static inline void irq_unlock(void)
+{
+ sei();
+}
+
+static inline irqflags_t irq_lock_save(void)
+{
+ irqflags_t flags;
+
+ flags = SREG;
+ cli();
+ return flags;
+}
+
+static inline void irq_unlock_restore(irqflags_t flags)
+{
+ SREG = flags;
+}
+
+static inline int irq_locked(void)
+{
+ return !(bit_is_set(SREG,7));
+}
+
+#endif /* IRQ_H_ */
--- /dev/null
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright 2019, Olivier MATZ <zer0@droids-corp.org>
+ */
+
+#include <avr/io.h>
+#include <util/delay.h>
+#include <avr/pgmspace.h>
+
+#include <stddef.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "main.h"
+#include "callout.h"
+#include "led.h"
+
+#define GYRO_UPDATE_MS 10
+#define GYRO_N_LEDS 12
+
+#define GYRO_TYPE_STATIC 0
+#define GYRO_TYPE_BLINK 1
+#define GYRO_TYPE_ROTATE 2
+
+#define LED1642_PORT PORTD
+#define LED1642_PIN PIND
+#define LED1642_DDR DDRD
+#define SDI_BIT 6
+#define SDO_BIT 2
+#define LE_BIT 3
+#define CLK_BIT 4
+#define PWCLK_BIT 5
+
+#define LED1642_CMD_DATA_SWITCH 1
+#define LED1642_CMD_DATA_LATCH 3
+#define LED1642_CMD_GLOBAL_LATCH 5
+#define LED1642_CMD_WRITE_CONFIG 7
+#define LED1642_CMD_READ_CONFIG 8
+
+
+#define LED1642_CFG_CURRENT 0 /* 6 bits */
+#define LED1642_CFG_CURRENT_RANGE 6
+#define LED1642_CFG_ERROR_DETECT 7
+#define LED1642_CFG_SHORTED_LEDS 8 /* 2 bits */
+#define LED1642_CFG_AUTO_OFF 10
+#define LED1642_CFG_TURN_ON_TIME 11 /* 2 bits */
+#define LED1642_CFG_SDO_DELAY 13
+#define LED1642_CFG_OUT_DELAY 14
+#define LED1642_CFG_PWM12 15
+
+struct gyro_state {
+ uint8_t type; /* none, blink, rotate */
+ uint16_t ms;
+ uint16_t stop_ms;
+
+ union {
+ struct {
+ uint16_t period_ms;
+ uint16_t intensity;
+ uint16_t on_ms;
+ uint16_t off_ms;
+ } blink;
+ struct {
+ uint16_t angle;
+ uint16_t intensity;
+ uint16_t width;
+ int16_t speed;
+ } rot;
+ };
+};
+
+/* LEDs status. Intensity is stored within 12 bits. */
+static uint16_t gyro_leds[GYRO_N_LEDS];
+static struct gyro_state gyro_state;
+static struct callout gyro_timer;
+
+void __led_gyro_ray(uint16_t angle, uint16_t intensity, uint16_t width)
+{
+#define LED_WIDTH ((uint16_t)(65536 / GYRO_N_LEDS))
+#define LED_HALF ((uint16_t)(65536 / (GYRO_N_LEDS * 2)))
+
+ uint32_t ray_half;
+ uint16_t led_angle;
+ int16_t diff;
+ uint8_t i;
+
+ ray_half = width / 2;
+
+ for (i = 0; i < GYRO_N_LEDS; i++) {
+ gyro_leds[i] = 0;
+
+ led_angle = (uint16_t)(((uint32_t)i * 65536) / GYRO_N_LEDS);
+
+ /* get angle diff */
+ diff = led_angle - angle; /* modulo 16 bits */
+ if (diff == -32768)
+ diff = 32767;
+ else if (diff < 0)
+ diff = -diff;
+
+ /* calculate led intensity */
+ if ((uint16_t)diff >= ray_half + LED_HALF)
+ gyro_leds[i] = 0;
+ else if (diff <= (int32_t)(ray_half - LED_HALF))
+ gyro_leds[i] = intensity;
+ else
+ gyro_leds[i] = ((int32_t)(ray_half + LED_HALF - diff) *
+ intensity) / LED_WIDTH;
+ }
+}
+
+static void
+led1642_write(uint8_t cmd, uint16_t data)
+{
+ uint8_t i;
+
+ LED1642_PORT = 0;
+ for (i = 0; i < 16; i++, data <<= 1) {
+ if (data & 0x8000)
+ LED1642_PORT |= (1 << SDI_BIT);
+ else
+ LED1642_PORT &= ~(1 << SDI_BIT);
+ if (i + cmd >= 16)
+ LED1642_PORT |= (1 << LE_BIT);
+
+ LED1642_PORT |= (1 << CLK_BIT);
+ LED1642_PORT &= ~(1 << CLK_BIT);
+ }
+ LED1642_PORT = 0;
+}
+
+static uint16_t
+led1642_read(uint8_t cmd)
+{
+ uint8_t i;
+ uint16_t data = 0;
+
+ led1642_write(cmd, 0);
+ for (i = 0; i < 16; i++) {
+ LED1642_PORT |= (1 << CLK_BIT);
+ LED1642_PORT &= ~(1 << CLK_BIT);
+ data <<= 1;
+ if (LED1642_PIN & (1 << SDO_BIT))
+ data |= 1;
+ }
+ LED1642_PORT = 0;
+ return data;
+}
+
+/* send gyro_leds[] to led1642 */
+static void
+gyro_update_leds(void)
+{
+ uint16_t data, cmd;
+ uint8_t i;
+
+ for (i = 0; i < 16; i++) {
+ if (i >= 4)
+ data = (gyro_leds[i - 4]) >> 4;
+ else
+ data = 0;
+ if (i != 15)
+ cmd = LED1642_CMD_DATA_LATCH;
+ else
+ cmd = LED1642_CMD_GLOBAL_LATCH;
+
+ led1642_write(cmd, data);
+ }
+}
+
+void led_gyro_off(void)
+{
+ callout_stop(&callout_mgr, &gyro_timer);
+ memset(gyro_leds, 0, sizeof(gyro_leds));
+ gyro_update_leds();
+}
+
+static void gyro_update_cb(struct callout_mgr *cm,
+ struct callout *tim, void *arg)
+{
+ uint16_t intensity;
+ uint8_t i, update = 0;
+
+ (void)arg;
+
+ gyro_state.ms += GYRO_UPDATE_MS;
+ if (gyro_state.ms >= gyro_state.stop_ms) {
+ led_gyro_off();
+ return;
+ }
+
+ switch (gyro_state.type) {
+ case GYRO_TYPE_STATIC:
+ break;
+ case GYRO_TYPE_BLINK:
+ gyro_state.blink.period_ms += GYRO_UPDATE_MS;
+ if (gyro_state.blink.period_ms > gyro_state.blink.on_ms +
+ gyro_state.blink.off_ms) {
+ intensity = gyro_state.blink.intensity;
+ gyro_state.blink.period_ms -= gyro_state.blink.on_ms +
+ gyro_state.blink.off_ms;
+ update = 1;
+ } else if (gyro_state.blink.period_ms > gyro_state.blink.on_ms) {
+ intensity = 0;
+ update = 1;
+ }
+ if (update) {
+ for (i = 0; i < GYRO_N_LEDS; i++)
+ gyro_leds[i] = intensity;
+ }
+ break;
+ case GYRO_TYPE_ROTATE:
+ gyro_state.rot.angle += gyro_state.rot.speed;
+ __led_gyro_ray(gyro_state.rot.angle, gyro_state.rot.intensity,
+ gyro_state.rot.width);
+ update = 1;
+ break;
+ default:
+ break;
+ }
+
+ if (update)
+ gyro_update_leds();
+
+ callout_reschedule(cm, tim, GYRO_UPDATE_MS);
+}
+
+void led_debug_init(void)
+{
+ DDRB |= (1 << 6); /* debug led as output */
+}
+
+void led_debug_on(void)
+{
+ PORTB |= (1 << 6);
+}
+
+void led_debug_off(void)
+{
+ PORTB &= (~(1 << 6));
+}
+
+void led_debug_toggle(void)
+{
+ PORTB ^= (1 << 6);
+}
+
+void led_gyro_all(uint16_t intensity, uint16_t blink_on_ms,
+ uint16_t blink_off_ms, uint16_t stop_ms)
+{
+ uint8_t i;
+
+ callout_stop(&callout_mgr, &gyro_timer);
+ printf_P(PSTR("%u %u %u %u\n"),
+ intensity, blink_on_ms, blink_off_ms, stop_ms);
+ for (i = 0; i < GYRO_N_LEDS; i++)
+ gyro_leds[i] = intensity;
+ gyro_update_leds();
+
+ if (!blink_on_ms || !blink_off_ms) {
+ gyro_state.type = GYRO_TYPE_STATIC;
+ return;
+ }
+
+ gyro_state.type = GYRO_TYPE_BLINK;
+ gyro_state.ms = 0;
+ gyro_state.stop_ms = stop_ms;
+ gyro_state.blink.on_ms = blink_on_ms;
+ gyro_state.blink.off_ms = blink_off_ms;
+ gyro_state.blink.intensity = intensity;
+ gyro_state.blink.period_ms = 0;
+ callout_schedule(&callout_mgr, &gyro_timer, GYRO_UPDATE_MS);
+}
+
+void led_gyro_ray(uint16_t angle, uint16_t intensity, uint16_t width,
+ int16_t rot_speed, uint16_t stop_ms)
+{
+ callout_stop(&callout_mgr, &gyro_timer);
+
+ printf_P(PSTR("led_gyro_ray %u %u %u %u %u\n"),
+ angle, intensity, width, rot_speed, stop_ms);
+ __led_gyro_ray(angle, intensity, width);
+ gyro_update_leds();
+
+ gyro_state.ms = 0;
+ gyro_state.stop_ms = stop_ms;
+ if (rot_speed == 0) {
+ gyro_state.type = GYRO_TYPE_STATIC;
+ } else {
+ gyro_state.type = GYRO_TYPE_ROTATE;
+ gyro_state.rot.angle = angle;
+ gyro_state.rot.speed = rot_speed;
+ gyro_state.rot.intensity = intensity;
+ gyro_state.rot.width = width;
+ }
+ callout_schedule(&callout_mgr, &gyro_timer, GYRO_UPDATE_MS);
+}
+
+void
+led1642_init(void)
+{
+ /* 0 = 9.8mA, 31 = 39.3mA (with current range = 1)*/
+ const uint16_t current = 0;
+ const uint16_t config = (
+ (current << LED1642_CFG_CURRENT) |
+ (1U << LED1642_CFG_CURRENT_RANGE) |
+ (1U << LED1642_CFG_OUT_DELAY) |
+ (1U << LED1642_CFG_PWM12));
+ uint16_t data;
+
+ LED1642_DDR |= (1 << SDI_BIT) | (1 << LE_BIT) | (1 << CLK_BIT) |
+ (1 << PWCLK_BIT);
+
+ /* Fast PWM, reset timer at OCRA, set OC0B at bottom, clear
+ * OC0B at OCR0B */
+ TCCR0A = (1 << COM0B1) | (1 << WGM01) | (1 << WGM00);
+ TCCR0B = (1 << WGM02) | (1 << CS00); /* div = 1 */
+ OCR0B = 2;
+ OCR0A = 4;
+
+ LED1642_PORT = 0;
+
+ led1642_write(LED1642_CMD_DATA_SWITCH, 0);
+
+ led1642_write(LED1642_CMD_WRITE_CONFIG, config);
+ data = led1642_read(LED1642_CMD_READ_CONFIG);
+ if (data != config)
+ printf_P(PSTR("data (0x%x) != config (0x%x)\n"),
+ data, config);
+
+ led1642_write(LED1642_CMD_DATA_SWITCH, 0xffff);
+
+ _delay_ms(1000);
+
+ led_gyro_off();
+
+ /* timer that updates gyro LEDs */
+ callout_init(&gyro_timer, gyro_update_cb, NULL, 128);
+}
--- /dev/null
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright 2019, Olivier MATZ <zer0@droids-corp.org>
+ */
+
+#ifndef _LED_H
+#define _LED_H
+
+void led_debug_init(void);
+void led1642_init(void);
+
+void led_debug_on(void);
+void led_debug_off(void);
+void led_debug_toggle(void);
+
+/**
+ * Control all leds at the same time with specified intensity.
+ *
+ * @param intensity
+ * Led intensity between 0 and 4095
+ * @param blink_on_ms
+ * Time during which leds are on when blinking. If 0, don't blink.
+ * @param blink_off_ms
+ * Time during which leds are off when blinking. If 0, don't blink.
+ * Note that on + off times must be < 65536.
+ * @param stop_ms
+ * Time after which all the leds are switched off. If 0, never stop.
+ */
+void led_gyro_all(uint16_t intensity, uint16_t blink_on_ms,
+ uint16_t blink_off_ms, uint16_t stop_ms);
+
+/**
+ * Switch on the leds at specified intensity in one direction.
+ *
+ * @param angle
+ * Between 0 and 65535.
+ * @param intensity
+ * Led intensity, between 0 and 4095.
+ * @param width
+ * The width represents the angle of the enabled zone, between 0
+ * and 65535 (one led = 6553).
+ * @param rot_speed
+ * The rotation speed is angle per 10ms (so 655 is ~1 round/sec).
+ * @param stop_ms
+ * Time after which all the leds are switched off. If 0, never stop.
+ */
+void led_gyro_ray(uint16_t angle, uint16_t intensity, uint16_t width,
+ int16_t rot_speed, uint16_t stop_ms);
+
+#endif
--- /dev/null
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright 2019, Olivier MATZ <zer0@droids-corp.org>
+ */
+
+#include <avr/io.h>
+#include <util/delay.h>
+#include <avr/wdt.h>
+#include <avr/pgmspace.h>
+
+#include <stdio.h>
+#include <inttypes.h>
+#include <string.h>
+
+#include "irq.h"
+#include "uart.h"
+#include "callout.h"
+#include "commands.h"
+#include "led.h"
+#include "rtc.h"
+#include "buzzer.h"
+
+struct callout_mgr callout_mgr;
+uint8_t log_debug = 0;
+
+/* disable watchdog early at init */
+__attribute__((naked)) __attribute__((section(".init3")))
+void wdt_init(void)
+{
+ MCUSR = 0;
+ wdt_disable();
+}
+
+/* called every ms to manage callouts */
+static void callout_cb(void)
+{
+ callout_manage(&callout_mgr);
+}
+
+int main(void)
+{
+ uint8_t i;
+ char c;
+
+ /* LEDs as output pin */
+ led_debug_init();
+
+ /* toggle the pin */
+ for (i = 0; i < 3; i++) {
+ led_debug_on();
+ _delay_ms(100);
+ led_debug_off();
+ _delay_ms(100);
+ }
+
+ /* uart and stdout initialization */
+ uart_init();
+
+ /* timer interrupt and callout manager */
+ callout_mgr_init(&callout_mgr, rtc_get_ms_u16);
+ rtc_init(&callout_cb);
+
+ /* LEDs as output pin */
+ led1642_init();
+
+ /* timer 1 and buzzer */
+ buzzer_init();
+
+ /* enable interrupts globally */
+ irq_unlock();
+
+ //buzzer(100, 100);
+ //buzzer_vibrato(800, 600, 10000, 800, VIBRATO_TYPE_TRIANGLE);
+
+ printf_P(PSTR("ready\r\n"));
+
+ /* mainloop */
+ while (1) {
+ if (uart_recv(&c) == 0)
+ cmd_input(c);
+ }
+
+ return 0;
+}
--- /dev/null
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright 2019, Olivier MATZ <zer0@droids-corp.org>
+ */
+
+#ifndef MAIN_H_
+#define MAIN_H_
+
+#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)]))
+
+extern struct callout_mgr callout_mgr;
+extern uint8_t log_debug;
+
+#endif
--- /dev/null
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)queue.h 8.5 (Berkeley) 8/20/94
+ */
+
+#ifndef _SYS_QUEUE_H_
+#define _SYS_QUEUE_H_
+
+/*
+ * This file defines five types of data structures: singly-linked lists,
+ * lists, simple queues, tail queues, and circular queues.
+ *
+ * A singly-linked list is headed by a single forward pointer. The
+ * elements are singly linked for minimum space and pointer manipulation
+ * overhead at the expense of O(n) removal for arbitrary elements. New
+ * elements can be added to the list after an existing element or at the
+ * head of the list. Elements being removed from the head of the list
+ * should use the explicit macro for this purpose for optimum
+ * efficiency. A singly-linked list may only be traversed in the forward
+ * direction. Singly-linked lists are ideal for applications with large
+ * datasets and few or no removals or for implementing a LIFO queue.
+ *
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before
+ * or after an existing element or at the head of the list. A list
+ * may only be traversed in the forward direction.
+ *
+ * A simple queue is headed by a pair of pointers, one the head of the
+ * list and the other to the tail of the list. The elements are singly
+ * linked to save space, so elements can only be removed from the
+ * head of the list. New elements can be added to the list after
+ * an existing element, at the head of the list, or at the end of the
+ * list. A simple queue may only be traversed in the forward direction.
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or
+ * after an existing element, at the head of the list, or at the end of
+ * the list. A tail queue may be traversed in either direction.
+ *
+ * A circle queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the list.
+ * A circle queue may be traversed in either direction, but has a more
+ * complex end of list detection.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ */
+
+/*
+ * Include the definition of NULL only on NetBSD because sys/null.h
+ * is not available elsewhere. This conditional makes the header
+ * portable and it can simply be dropped verbatim into any system.
+ * The caveat is that on other systems some other header
+ * must provide NULL before the macros can be used.
+ */
+#ifdef __NetBSD__
+#include <sys/null.h>
+#endif
+
+#if defined(QUEUEDEBUG)
+# if defined(_KERNEL)
+# define QUEUEDEBUG_ABORT(...) panic(__VA_ARGS__)
+# else
+# include <err.h>
+# define QUEUEDEBUG_ABORT(...) err(1, __VA_ARGS__)
+# endif
+#endif
+
+/*
+ * Singly-linked List definitions.
+ */
+#define SLIST_HEAD(name, type) \
+struct name { \
+ struct type *slh_first; /* first element */ \
+}
+
+#define SLIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#define SLIST_ENTRY(type) \
+struct { \
+ struct type *sle_next; /* next element */ \
+}
+
+/*
+ * Singly-linked List access methods.
+ */
+#define SLIST_FIRST(head) ((head)->slh_first)
+#define SLIST_END(head) NULL
+#define SLIST_EMPTY(head) ((head)->slh_first == NULL)
+#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
+
+#define SLIST_FOREACH(var, head, field) \
+ for((var) = (head)->slh_first; \
+ (var) != SLIST_END(head); \
+ (var) = (var)->field.sle_next)
+
+#define SLIST_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = SLIST_FIRST((head)); \
+ (var) != SLIST_END(head) && \
+ ((tvar) = SLIST_NEXT((var), field), 1); \
+ (var) = (tvar))
+
+/*
+ * Singly-linked List functions.
+ */
+#define SLIST_INIT(head) do { \
+ (head)->slh_first = SLIST_END(head); \
+} while (/*CONSTCOND*/0)
+
+#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
+ (elm)->field.sle_next = (slistelm)->field.sle_next; \
+ (slistelm)->field.sle_next = (elm); \
+} while (/*CONSTCOND*/0)
+
+#define SLIST_INSERT_HEAD(head, elm, field) do { \
+ (elm)->field.sle_next = (head)->slh_first; \
+ (head)->slh_first = (elm); \
+} while (/*CONSTCOND*/0)
+
+#define SLIST_REMOVE_AFTER(slistelm, field) do { \
+ (slistelm)->field.sle_next = \
+ SLIST_NEXT(SLIST_NEXT((slistelm), field), field); \
+} while (/*CONSTCOND*/0)
+
+#define SLIST_REMOVE_HEAD(head, field) do { \
+ (head)->slh_first = (head)->slh_first->field.sle_next; \
+} while (/*CONSTCOND*/0)
+
+#define SLIST_REMOVE(head, elm, type, field) do { \
+ if ((head)->slh_first == (elm)) { \
+ SLIST_REMOVE_HEAD((head), field); \
+ } \
+ else { \
+ struct type *curelm = (head)->slh_first; \
+ while(curelm->field.sle_next != (elm)) \
+ curelm = curelm->field.sle_next; \
+ curelm->field.sle_next = \
+ curelm->field.sle_next->field.sle_next; \
+ } \
+} while (/*CONSTCOND*/0)
+
+
+/*
+ * List definitions.
+ */
+#define LIST_HEAD(name, type) \
+struct name { \
+ struct type *lh_first; /* first element */ \
+}
+
+#define LIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#define LIST_ENTRY(type) \
+struct { \
+ struct type *le_next; /* next element */ \
+ struct type **le_prev; /* address of previous next element */ \
+}
+
+/*
+ * List access methods.
+ */
+#define LIST_FIRST(head) ((head)->lh_first)
+#define LIST_END(head) NULL
+#define LIST_EMPTY(head) ((head)->lh_first == LIST_END(head))
+#define LIST_NEXT(elm, field) ((elm)->field.le_next)
+
+#define LIST_FOREACH(var, head, field) \
+ for ((var) = ((head)->lh_first); \
+ (var) != LIST_END(head); \
+ (var) = ((var)->field.le_next))
+
+#define LIST_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = LIST_FIRST((head)); \
+ (var) != LIST_END(head) && \
+ ((tvar) = LIST_NEXT((var), field), 1); \
+ (var) = (tvar))
+
+#define LIST_MOVE(head1, head2) do { \
+ LIST_INIT((head2)); \
+ if (!LIST_EMPTY((head1))) { \
+ (head2)->lh_first = (head1)->lh_first; \
+ LIST_INIT((head1)); \
+ } \
+} while (/*CONSTCOND*/0)
+
+/*
+ * List functions.
+ */
+#if defined(QUEUEDEBUG)
+#define QUEUEDEBUG_LIST_INSERT_HEAD(head, elm, field) \
+ if ((head)->lh_first && \
+ (head)->lh_first->field.le_prev != &(head)->lh_first) \
+ QUEUEDEBUG_ABORT("LIST_INSERT_HEAD %p %s:%d", (head), \
+ __FILE__, __LINE__);
+#define QUEUEDEBUG_LIST_OP(elm, field) \
+ if ((elm)->field.le_next && \
+ (elm)->field.le_next->field.le_prev != \
+ &(elm)->field.le_next) \
+ QUEUEDEBUG_ABORT("LIST_* forw %p %s:%d", (elm), \
+ __FILE__, __LINE__); \
+ if (*(elm)->field.le_prev != (elm)) \
+ QUEUEDEBUG_ABORT("LIST_* back %p %s:%d", (elm), \
+ __FILE__, __LINE__);
+#define QUEUEDEBUG_LIST_POSTREMOVE(elm, field) \
+ (elm)->field.le_next = (void *)1L; \
+ (elm)->field.le_prev = (void *)1L;
+#else
+#define QUEUEDEBUG_LIST_INSERT_HEAD(head, elm, field)
+#define QUEUEDEBUG_LIST_OP(elm, field)
+#define QUEUEDEBUG_LIST_POSTREMOVE(elm, field)
+#endif
+
+#define LIST_INIT(head) do { \
+ (head)->lh_first = LIST_END(head); \
+} while (/*CONSTCOND*/0)
+
+#define LIST_INSERT_AFTER(listelm, elm, field) do { \
+ QUEUEDEBUG_LIST_OP((listelm), field) \
+ if (((elm)->field.le_next = (listelm)->field.le_next) != \
+ LIST_END(head)) \
+ (listelm)->field.le_next->field.le_prev = \
+ &(elm)->field.le_next; \
+ (listelm)->field.le_next = (elm); \
+ (elm)->field.le_prev = &(listelm)->field.le_next; \
+} while (/*CONSTCOND*/0)
+
+#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
+ QUEUEDEBUG_LIST_OP((listelm), field) \
+ (elm)->field.le_prev = (listelm)->field.le_prev; \
+ (elm)->field.le_next = (listelm); \
+ *(listelm)->field.le_prev = (elm); \
+ (listelm)->field.le_prev = &(elm)->field.le_next; \
+} while (/*CONSTCOND*/0)
+
+#define LIST_INSERT_HEAD(head, elm, field) do { \
+ QUEUEDEBUG_LIST_INSERT_HEAD((head), (elm), field) \
+ if (((elm)->field.le_next = (head)->lh_first) != LIST_END(head))\
+ (head)->lh_first->field.le_prev = &(elm)->field.le_next;\
+ (head)->lh_first = (elm); \
+ (elm)->field.le_prev = &(head)->lh_first; \
+} while (/*CONSTCOND*/0)
+
+#define LIST_REMOVE(elm, field) do { \
+ QUEUEDEBUG_LIST_OP((elm), field) \
+ if ((elm)->field.le_next != NULL) \
+ (elm)->field.le_next->field.le_prev = \
+ (elm)->field.le_prev; \
+ *(elm)->field.le_prev = (elm)->field.le_next; \
+ QUEUEDEBUG_LIST_POSTREMOVE((elm), field) \
+} while (/*CONSTCOND*/0)
+
+#define LIST_REPLACE(elm, elm2, field) do { \
+ if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \
+ (elm2)->field.le_next->field.le_prev = \
+ &(elm2)->field.le_next; \
+ (elm2)->field.le_prev = (elm)->field.le_prev; \
+ *(elm2)->field.le_prev = (elm2); \
+ QUEUEDEBUG_LIST_POSTREMOVE((elm), field) \
+} while (/*CONSTCOND*/0)
+
+/*
+ * Simple queue definitions.
+ */
+#define SIMPLEQ_HEAD(name, type) \
+struct name { \
+ struct type *sqh_first; /* first element */ \
+ struct type **sqh_last; /* addr of last next element */ \
+}
+
+#define SIMPLEQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).sqh_first }
+
+#define SIMPLEQ_ENTRY(type) \
+struct { \
+ struct type *sqe_next; /* next element */ \
+}
+
+/*
+ * Simple queue access methods.
+ */
+#define SIMPLEQ_FIRST(head) ((head)->sqh_first)
+#define SIMPLEQ_END(head) NULL
+#define SIMPLEQ_EMPTY(head) ((head)->sqh_first == SIMPLEQ_END(head))
+#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next)
+
+#define SIMPLEQ_FOREACH(var, head, field) \
+ for ((var) = ((head)->sqh_first); \
+ (var) != SIMPLEQ_END(head); \
+ (var) = ((var)->field.sqe_next))
+
+#define SIMPLEQ_FOREACH_SAFE(var, head, field, next) \
+ for ((var) = ((head)->sqh_first); \
+ (var) != SIMPLEQ_END(head) && \
+ ((next = ((var)->field.sqe_next)), 1); \
+ (var) = (next))
+
+/*
+ * Simple queue functions.
+ */
+#define SIMPLEQ_INIT(head) do { \
+ (head)->sqh_first = NULL; \
+ (head)->sqh_last = &(head)->sqh_first; \
+} while (/*CONSTCOND*/0)
+
+#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+ (head)->sqh_first = (elm); \
+} while (/*CONSTCOND*/0)
+
+#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.sqe_next = NULL; \
+ *(head)->sqh_last = (elm); \
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+} while (/*CONSTCOND*/0)
+
+#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+ (listelm)->field.sqe_next = (elm); \
+} while (/*CONSTCOND*/0)
+
+#define SIMPLEQ_REMOVE_HEAD(head, field) do { \
+ if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \
+ (head)->sqh_last = &(head)->sqh_first; \
+} while (/*CONSTCOND*/0)
+
+#define SIMPLEQ_REMOVE_AFTER(head, elm, field) do { \
+ if (((elm)->field.sqe_next = (elm)->field.sqe_next->field.sqe_next) \
+ == NULL) \
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+} while (/*CONSTCOND*/0)
+
+#define SIMPLEQ_REMOVE(head, elm, type, field) do { \
+ if ((head)->sqh_first == (elm)) { \
+ SIMPLEQ_REMOVE_HEAD((head), field); \
+ } else { \
+ struct type *curelm = (head)->sqh_first; \
+ while (curelm->field.sqe_next != (elm)) \
+ curelm = curelm->field.sqe_next; \
+ if ((curelm->field.sqe_next = \
+ curelm->field.sqe_next->field.sqe_next) == NULL) \
+ (head)->sqh_last = &(curelm)->field.sqe_next; \
+ } \
+} while (/*CONSTCOND*/0)
+
+#define SIMPLEQ_CONCAT(head1, head2) do { \
+ if (!SIMPLEQ_EMPTY((head2))) { \
+ *(head1)->sqh_last = (head2)->sqh_first; \
+ (head1)->sqh_last = (head2)->sqh_last; \
+ SIMPLEQ_INIT((head2)); \
+ } \
+} while (/*CONSTCOND*/0)
+
+#define SIMPLEQ_LAST(head, type, field) \
+ (SIMPLEQ_EMPTY((head)) ? \
+ NULL : \
+ ((struct type *)(void *) \
+ ((char *)((head)->sqh_last) - offsetof(struct type, field))))
+
+/*
+ * Tail queue definitions.
+ */
+#define _TAILQ_HEAD(name, type, qual) \
+struct name { \
+ qual type *tqh_first; /* first element */ \
+ qual type *qual *tqh_last; /* addr of last next element */ \
+}
+#define TAILQ_HEAD(name, type) _TAILQ_HEAD(name, struct type,)
+
+#define TAILQ_HEAD_INITIALIZER(head) \
+ { TAILQ_END(head), &(head).tqh_first }
+
+#define _TAILQ_ENTRY(type, qual) \
+struct { \
+ qual type *tqe_next; /* next element */ \
+ qual type *qual *tqe_prev; /* address of previous next element */\
+}
+#define TAILQ_ENTRY(type) _TAILQ_ENTRY(struct type,)
+
+/*
+ * Tail queue access methods.
+ */
+#define TAILQ_FIRST(head) ((head)->tqh_first)
+#define TAILQ_END(head) (NULL)
+#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
+#define TAILQ_LAST(head, headname) \
+ (*(((struct headname *)((head)->tqh_last))->tqh_last))
+#define TAILQ_PREV(elm, headname, field) \
+ (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+#define TAILQ_EMPTY(head) (TAILQ_FIRST(head) == TAILQ_END(head))
+
+
+#define TAILQ_FOREACH(var, head, field) \
+ for ((var) = ((head)->tqh_first); \
+ (var) != TAILQ_END(head); \
+ (var) = ((var)->field.tqe_next))
+
+#define TAILQ_FOREACH_SAFE(var, head, field, next) \
+ for ((var) = ((head)->tqh_first); \
+ (var) != TAILQ_END(head) && \
+ ((next) = TAILQ_NEXT(var, field), 1); (var) = (next))
+
+#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
+ for ((var) = (*(((struct headname *)((head)->tqh_last))->tqh_last));\
+ (var) != TAILQ_END(head); \
+ (var) = (*(((struct headname *)((var)->field.tqe_prev))->tqh_last)))
+
+#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, prev) \
+ for ((var) = TAILQ_LAST((head), headname); \
+ (var) != TAILQ_END(head) && \
+ ((prev) = TAILQ_PREV((var), headname, field), 1); (var) = (prev))
+
+/*
+ * Tail queue functions.
+ */
+#if defined(QUEUEDEBUG)
+#define QUEUEDEBUG_TAILQ_INSERT_HEAD(head, elm, field) \
+ if ((head)->tqh_first && \
+ (head)->tqh_first->field.tqe_prev != &(head)->tqh_first) \
+ QUEUEDEBUG_ABORT("TAILQ_INSERT_HEAD %p %s:%d", (head), \
+ __FILE__, __LINE__);
+#define QUEUEDEBUG_TAILQ_INSERT_TAIL(head, elm, field) \
+ if (*(head)->tqh_last != NULL) \
+ QUEUEDEBUG_ABORT("TAILQ_INSERT_TAIL %p %s:%d", (head), \
+ __FILE__, __LINE__);
+#define QUEUEDEBUG_TAILQ_OP(elm, field) \
+ if ((elm)->field.tqe_next && \
+ (elm)->field.tqe_next->field.tqe_prev != \
+ &(elm)->field.tqe_next) \
+ QUEUEDEBUG_ABORT("TAILQ_* forw %p %s:%d", (elm), \
+ __FILE__, __LINE__); \
+ if (*(elm)->field.tqe_prev != (elm)) \
+ QUEUEDEBUG_ABORT("TAILQ_* back %p %s:%d", (elm), \
+ __FILE__, __LINE__);
+#define QUEUEDEBUG_TAILQ_PREREMOVE(head, elm, field) \
+ if ((elm)->field.tqe_next == NULL && \
+ (head)->tqh_last != &(elm)->field.tqe_next) \
+ QUEUEDEBUG_ABORT("TAILQ_PREREMOVE head %p elm %p %s:%d",\
+ (head), (elm), __FILE__, __LINE__);
+#define QUEUEDEBUG_TAILQ_POSTREMOVE(elm, field) \
+ (elm)->field.tqe_next = (void *)1L; \
+ (elm)->field.tqe_prev = (void *)1L;
+#else
+#define QUEUEDEBUG_TAILQ_INSERT_HEAD(head, elm, field)
+#define QUEUEDEBUG_TAILQ_INSERT_TAIL(head, elm, field)
+#define QUEUEDEBUG_TAILQ_OP(elm, field)
+#define QUEUEDEBUG_TAILQ_PREREMOVE(head, elm, field)
+#define QUEUEDEBUG_TAILQ_POSTREMOVE(elm, field)
+#endif
+
+#define TAILQ_INIT(head) do { \
+ (head)->tqh_first = TAILQ_END(head); \
+ (head)->tqh_last = &(head)->tqh_first; \
+} while (/*CONSTCOND*/0)
+
+#define TAILQ_INSERT_HEAD(head, elm, field) do { \
+ QUEUEDEBUG_TAILQ_INSERT_HEAD((head), (elm), field) \
+ if (((elm)->field.tqe_next = (head)->tqh_first) != TAILQ_END(head))\
+ (head)->tqh_first->field.tqe_prev = \
+ &(elm)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ (head)->tqh_first = (elm); \
+ (elm)->field.tqe_prev = &(head)->tqh_first; \
+} while (/*CONSTCOND*/0)
+
+#define TAILQ_INSERT_TAIL(head, elm, field) do { \
+ QUEUEDEBUG_TAILQ_INSERT_TAIL((head), (elm), field) \
+ (elm)->field.tqe_next = TAILQ_END(head); \
+ (elm)->field.tqe_prev = (head)->tqh_last; \
+ *(head)->tqh_last = (elm); \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+} while (/*CONSTCOND*/0)
+
+#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ QUEUEDEBUG_TAILQ_OP((listelm), field) \
+ if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != \
+ TAILQ_END(head)) \
+ (elm)->field.tqe_next->field.tqe_prev = \
+ &(elm)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ (listelm)->field.tqe_next = (elm); \
+ (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
+} while (/*CONSTCOND*/0)
+
+#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
+ QUEUEDEBUG_TAILQ_OP((listelm), field) \
+ (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
+ (elm)->field.tqe_next = (listelm); \
+ *(listelm)->field.tqe_prev = (elm); \
+ (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
+} while (/*CONSTCOND*/0)
+
+#define TAILQ_REMOVE(head, elm, field) do { \
+ QUEUEDEBUG_TAILQ_PREREMOVE((head), (elm), field) \
+ QUEUEDEBUG_TAILQ_OP((elm), field) \
+ if (((elm)->field.tqe_next) != TAILQ_END(head)) \
+ (elm)->field.tqe_next->field.tqe_prev = \
+ (elm)->field.tqe_prev; \
+ else \
+ (head)->tqh_last = (elm)->field.tqe_prev; \
+ *(elm)->field.tqe_prev = (elm)->field.tqe_next; \
+ QUEUEDEBUG_TAILQ_POSTREMOVE((elm), field); \
+} while (/*CONSTCOND*/0)
+
+#define TAILQ_REPLACE(head, elm, elm2, field) do { \
+ if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != \
+ TAILQ_END(head)) \
+ (elm2)->field.tqe_next->field.tqe_prev = \
+ &(elm2)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm2)->field.tqe_next; \
+ (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \
+ *(elm2)->field.tqe_prev = (elm2); \
+ QUEUEDEBUG_TAILQ_POSTREMOVE((elm), field); \
+} while (/*CONSTCOND*/0)
+
+#define TAILQ_CONCAT(head1, head2, field) do { \
+ if (!TAILQ_EMPTY(head2)) { \
+ *(head1)->tqh_last = (head2)->tqh_first; \
+ (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \
+ (head1)->tqh_last = (head2)->tqh_last; \
+ TAILQ_INIT((head2)); \
+ } \
+} while (/*CONSTCOND*/0)
+
+/*
+ * Singly-linked Tail queue declarations.
+ */
+#define STAILQ_HEAD(name, type) \
+struct name { \
+ struct type *stqh_first; /* first element */ \
+ struct type **stqh_last; /* addr of last next element */ \
+}
+
+#define STAILQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).stqh_first }
+
+#define STAILQ_ENTRY(type) \
+struct { \
+ struct type *stqe_next; /* next element */ \
+}
+
+/*
+ * Singly-linked Tail queue access methods.
+ */
+#define STAILQ_FIRST(head) ((head)->stqh_first)
+#define STAILQ_END(head) NULL
+#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next)
+#define STAILQ_EMPTY(head) (STAILQ_FIRST(head) == STAILQ_END(head))
+
+/*
+ * Singly-linked Tail queue functions.
+ */
+#define STAILQ_INIT(head) do { \
+ (head)->stqh_first = NULL; \
+ (head)->stqh_last = &(head)->stqh_first; \
+} while (/*CONSTCOND*/0)
+
+#define STAILQ_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.stqe_next = (head)->stqh_first) == NULL) \
+ (head)->stqh_last = &(elm)->field.stqe_next; \
+ (head)->stqh_first = (elm); \
+} while (/*CONSTCOND*/0)
+
+#define STAILQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.stqe_next = NULL; \
+ *(head)->stqh_last = (elm); \
+ (head)->stqh_last = &(elm)->field.stqe_next; \
+} while (/*CONSTCOND*/0)
+
+#define STAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ if (((elm)->field.stqe_next = (listelm)->field.stqe_next) == NULL)\
+ (head)->stqh_last = &(elm)->field.stqe_next; \
+ (listelm)->field.stqe_next = (elm); \
+} while (/*CONSTCOND*/0)
+
+#define STAILQ_REMOVE_HEAD(head, field) do { \
+ if (((head)->stqh_first = (head)->stqh_first->field.stqe_next) == NULL) \
+ (head)->stqh_last = &(head)->stqh_first; \
+} while (/*CONSTCOND*/0)
+
+#define STAILQ_REMOVE(head, elm, type, field) do { \
+ if ((head)->stqh_first == (elm)) { \
+ STAILQ_REMOVE_HEAD((head), field); \
+ } else { \
+ struct type *curelm = (head)->stqh_first; \
+ while (curelm->field.stqe_next != (elm)) \
+ curelm = curelm->field.stqe_next; \
+ if ((curelm->field.stqe_next = \
+ curelm->field.stqe_next->field.stqe_next) == NULL) \
+ (head)->stqh_last = &(curelm)->field.stqe_next; \
+ } \
+} while (/*CONSTCOND*/0)
+
+#define STAILQ_FOREACH(var, head, field) \
+ for ((var) = ((head)->stqh_first); \
+ (var); \
+ (var) = ((var)->field.stqe_next))
+
+#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = STAILQ_FIRST((head)); \
+ (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \
+ (var) = (tvar))
+
+#define STAILQ_CONCAT(head1, head2) do { \
+ if (!STAILQ_EMPTY((head2))) { \
+ *(head1)->stqh_last = (head2)->stqh_first; \
+ (head1)->stqh_last = (head2)->stqh_last; \
+ STAILQ_INIT((head2)); \
+ } \
+} while (/*CONSTCOND*/0)
+
+#define STAILQ_LAST(head, type, field) \
+ (STAILQ_EMPTY((head)) ? \
+ NULL : \
+ ((struct type *)(void *) \
+ ((char *)((head)->stqh_last) - offsetof(struct type, field))))
+
+
+#ifndef _KERNEL
+/*
+ * Circular queue definitions. Do not use. We still keep the macros
+ * for compatibility but because of pointer aliasing issues their use
+ * is discouraged!
+ */
+
+/*
+ * __launder_type(): We use this ugly hack to work around the the compiler
+ * noticing that two types may not alias each other and elide tests in code.
+ * We hit this in the CIRCLEQ macros when comparing 'struct name *' and
+ * 'struct type *' (see CIRCLEQ_HEAD()). Modern compilers (such as GCC
+ * 4.8) declare these comparisons as always false, causing the code to
+ * not run as designed.
+ *
+ * This hack is only to be used for comparisons and thus can be fully const.
+ * Do not use for assignment.
+ *
+ * If we ever choose to change the ABI of the CIRCLEQ macros, we could fix
+ * this by changing the head/tail sentinal values, but see the note above
+ * this one.
+ */
+static __inline const void * __launder_type(const void *);
+static __inline const void *
+__launder_type(const void *__x)
+{
+ __asm __volatile("" : "+r" (__x));
+ return __x;
+}
+
+#if defined(QUEUEDEBUG)
+#define QUEUEDEBUG_CIRCLEQ_HEAD(head, field) \
+ if ((head)->cqh_first != CIRCLEQ_ENDC(head) && \
+ (head)->cqh_first->field.cqe_prev != CIRCLEQ_ENDC(head)) \
+ QUEUEDEBUG_ABORT("CIRCLEQ head forw %p %s:%d", (head), \
+ __FILE__, __LINE__); \
+ if ((head)->cqh_last != CIRCLEQ_ENDC(head) && \
+ (head)->cqh_last->field.cqe_next != CIRCLEQ_ENDC(head)) \
+ QUEUEDEBUG_ABORT("CIRCLEQ head back %p %s:%d", (head), \
+ __FILE__, __LINE__);
+#define QUEUEDEBUG_CIRCLEQ_ELM(head, elm, field) \
+ if ((elm)->field.cqe_next == CIRCLEQ_ENDC(head)) { \
+ if ((head)->cqh_last != (elm)) \
+ QUEUEDEBUG_ABORT("CIRCLEQ elm last %p %s:%d", \
+ (elm), __FILE__, __LINE__); \
+ } else { \
+ if ((elm)->field.cqe_next->field.cqe_prev != (elm)) \
+ QUEUEDEBUG_ABORT("CIRCLEQ elm forw %p %s:%d", \
+ (elm), __FILE__, __LINE__); \
+ } \
+ if ((elm)->field.cqe_prev == CIRCLEQ_ENDC(head)) { \
+ if ((head)->cqh_first != (elm)) \
+ QUEUEDEBUG_ABORT("CIRCLEQ elm first %p %s:%d", \
+ (elm), __FILE__, __LINE__); \
+ } else { \
+ if ((elm)->field.cqe_prev->field.cqe_next != (elm)) \
+ QUEUEDEBUG_ABORT("CIRCLEQ elm prev %p %s:%d", \
+ (elm), __FILE__, __LINE__); \
+ }
+#define QUEUEDEBUG_CIRCLEQ_POSTREMOVE(elm, field) \
+ (elm)->field.cqe_next = (void *)1L; \
+ (elm)->field.cqe_prev = (void *)1L;
+#else
+#define QUEUEDEBUG_CIRCLEQ_HEAD(head, field)
+#define QUEUEDEBUG_CIRCLEQ_ELM(head, elm, field)
+#define QUEUEDEBUG_CIRCLEQ_POSTREMOVE(elm, field)
+#endif
+
+#define CIRCLEQ_HEAD(name, type) \
+struct name { \
+ struct type *cqh_first; /* first element */ \
+ struct type *cqh_last; /* last element */ \
+}
+
+#define CIRCLEQ_HEAD_INITIALIZER(head) \
+ { CIRCLEQ_END(&head), CIRCLEQ_END(&head) }
+
+#define CIRCLEQ_ENTRY(type) \
+struct { \
+ struct type *cqe_next; /* next element */ \
+ struct type *cqe_prev; /* previous element */ \
+}
+
+/*
+ * Circular queue functions.
+ */
+#define CIRCLEQ_INIT(head) do { \
+ (head)->cqh_first = CIRCLEQ_END(head); \
+ (head)->cqh_last = CIRCLEQ_END(head); \
+} while (/*CONSTCOND*/0)
+
+#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ QUEUEDEBUG_CIRCLEQ_HEAD((head), field) \
+ QUEUEDEBUG_CIRCLEQ_ELM((head), (listelm), field) \
+ (elm)->field.cqe_next = (listelm)->field.cqe_next; \
+ (elm)->field.cqe_prev = (listelm); \
+ if ((listelm)->field.cqe_next == CIRCLEQ_ENDC(head)) \
+ (head)->cqh_last = (elm); \
+ else \
+ (listelm)->field.cqe_next->field.cqe_prev = (elm); \
+ (listelm)->field.cqe_next = (elm); \
+} while (/*CONSTCOND*/0)
+
+#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \
+ QUEUEDEBUG_CIRCLEQ_HEAD((head), field) \
+ QUEUEDEBUG_CIRCLEQ_ELM((head), (listelm), field) \
+ (elm)->field.cqe_next = (listelm); \
+ (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \
+ if ((listelm)->field.cqe_prev == CIRCLEQ_ENDC(head)) \
+ (head)->cqh_first = (elm); \
+ else \
+ (listelm)->field.cqe_prev->field.cqe_next = (elm); \
+ (listelm)->field.cqe_prev = (elm); \
+} while (/*CONSTCOND*/0)
+
+#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \
+ QUEUEDEBUG_CIRCLEQ_HEAD((head), field) \
+ (elm)->field.cqe_next = (head)->cqh_first; \
+ (elm)->field.cqe_prev = CIRCLEQ_END(head); \
+ if ((head)->cqh_last == CIRCLEQ_ENDC(head)) \
+ (head)->cqh_last = (elm); \
+ else \
+ (head)->cqh_first->field.cqe_prev = (elm); \
+ (head)->cqh_first = (elm); \
+} while (/*CONSTCOND*/0)
+
+#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \
+ QUEUEDEBUG_CIRCLEQ_HEAD((head), field) \
+ (elm)->field.cqe_next = CIRCLEQ_END(head); \
+ (elm)->field.cqe_prev = (head)->cqh_last; \
+ if ((head)->cqh_first == CIRCLEQ_ENDC(head)) \
+ (head)->cqh_first = (elm); \
+ else \
+ (head)->cqh_last->field.cqe_next = (elm); \
+ (head)->cqh_last = (elm); \
+} while (/*CONSTCOND*/0)
+
+#define CIRCLEQ_REMOVE(head, elm, field) do { \
+ QUEUEDEBUG_CIRCLEQ_HEAD((head), field) \
+ QUEUEDEBUG_CIRCLEQ_ELM((head), (elm), field) \
+ if ((elm)->field.cqe_next == CIRCLEQ_ENDC(head)) \
+ (head)->cqh_last = (elm)->field.cqe_prev; \
+ else \
+ (elm)->field.cqe_next->field.cqe_prev = \
+ (elm)->field.cqe_prev; \
+ if ((elm)->field.cqe_prev == CIRCLEQ_ENDC(head)) \
+ (head)->cqh_first = (elm)->field.cqe_next; \
+ else \
+ (elm)->field.cqe_prev->field.cqe_next = \
+ (elm)->field.cqe_next; \
+ QUEUEDEBUG_CIRCLEQ_POSTREMOVE((elm), field) \
+} while (/*CONSTCOND*/0)
+
+#define CIRCLEQ_FOREACH(var, head, field) \
+ for ((var) = ((head)->cqh_first); \
+ (var) != CIRCLEQ_ENDC(head); \
+ (var) = ((var)->field.cqe_next))
+
+#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \
+ for ((var) = ((head)->cqh_last); \
+ (var) != CIRCLEQ_ENDC(head); \
+ (var) = ((var)->field.cqe_prev))
+
+/*
+ * Circular queue access methods.
+ */
+#define CIRCLEQ_FIRST(head) ((head)->cqh_first)
+#define CIRCLEQ_LAST(head) ((head)->cqh_last)
+/* For comparisons */
+#define CIRCLEQ_ENDC(head) (__launder_type(head))
+/* For assignments */
+#define CIRCLEQ_END(head) ((void *)(head))
+#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next)
+#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev)
+#define CIRCLEQ_EMPTY(head) \
+ (CIRCLEQ_FIRST(head) == CIRCLEQ_ENDC(head))
+
+#define CIRCLEQ_LOOP_NEXT(head, elm, field) \
+ (((elm)->field.cqe_next == CIRCLEQ_ENDC(head)) \
+ ? ((head)->cqh_first) \
+ : (elm->field.cqe_next))
+#define CIRCLEQ_LOOP_PREV(head, elm, field) \
+ (((elm)->field.cqe_prev == CIRCLEQ_ENDC(head)) \
+ ? ((head)->cqh_last) \
+ : (elm->field.cqe_prev))
+#endif /* !_KERNEL */
+
+#endif /* !_SYS_QUEUE_H_ */
--- /dev/null
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright 2019, Olivier MATZ <zer0@droids-corp.org>
+ */
+
+#include <avr/io.h>
+
+#include <stddef.h>
+
+#include "irq.h"
+#include "main.h"
+#include "callout.h"
+
+static volatile uint32_t global_ms;
+static void (*rtc_cb)(void);
+
+void rtc_init(void (*cb)(void))
+{
+ /* timer2 interrupt */
+ BUILD_BUG_ON(F_CPU != 8000000UL);
+ TCCR2B = (1 << CS21); /* div = 8 */
+ TIMSK2 |= (1 << TOIE2); /* enable intr */
+ rtc_cb = cb;
+}
+
+/* interrupt every 256us */
+SIGNAL(TIMER2_OVF_vect)
+{
+ static uint16_t i = 0;
+
+ i++;
+ if ((i & 0x3) != 0)
+ return;
+
+ global_ms++;
+ if (rtc_cb != NULL)
+ rtc_cb();
+}
+
+/* return the absolute time in ms */
+uint32_t rtc_get_ms_u32(void)
+{
+ irqflags_t flags;
+ uint32_t ms;
+
+ flags = irq_lock_save();
+ ms = global_ms;
+ irq_unlock_restore(flags);
+ return ms;
+}
+
+uint16_t rtc_get_ms_u16(void)
+{
+ return (uint16_t)rtc_get_ms_u32();
+}
--- /dev/null
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright 2019, Olivier MATZ <zer0@droids-corp.org>
+ */
+
+#ifndef _RTC_H
+#define _RTC_H
+
+/* Rtc initialization. The cb is executed every ms. */
+void rtc_init(void (*cb)(void));
+
+/* return the absolute time in ms */
+uint16_t rtc_get_ms_u16(void);
+
+/* return the absolute time in ms */
+uint32_t rtc_get_ms_u32(void);
+
+#endif
--- /dev/null
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright 2019, Olivier MATZ <zer0@droids-corp.org>
+ */
+
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include <avr/pgmspace.h>
+#include <util/delay.h>
+
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include <irq.h>
+
+#include "cirbuf.h"
+#include "main.h"
+#include "uart.h"
+
+#define UART_RXQ_LEN 16
+struct cirbuf uart_rxq;
+static char uart_buf[UART_RXQ_LEN];
+
+SIGNAL(USART_RX_vect)
+{
+ char c = UDR0;
+ cirbuf_add_tail_safe(&uart_rxq, c);
+}
+
+void uart_send(char c)
+{
+ while (!(UCSR0A & (1 << UDRE0)))
+ ;
+ UDR0 = c;
+}
+
+/* used by printf */
+static int stdout_send(char c, FILE *f)
+{
+ (void)f;
+ uart_send(c);
+ return 0;
+}
+
+int8_t uart_recv(char *c)
+{
+ int8_t ret = -1;
+ irqflags_t flags;
+
+ flags = irq_lock_save();
+ if (!cirbuf_is_empty(&uart_rxq)) {
+ *c = cirbuf_get_head(&uart_rxq);
+ cirbuf_del_head(&uart_rxq);
+ ret = 0;
+ }
+ irq_unlock_restore(flags);
+
+ return ret;
+}
+
+void uart_init(void)
+{
+ cirbuf_init(&uart_rxq, uart_buf, 0, UART_RXQ_LEN);
+
+ /* 57600 bauds @ 8Mhz */
+ BUILD_BUG_ON(F_CPU != 8000000UL);
+ UBRR0H = 0;
+ UBRR0L = 16;
+ UCSR0A = (1 << U2X0);
+ UCSR0C = (1 << UCSZ01) | (1 << UCSZ00);
+ UCSR0B = (1 << RXEN0) | (1 << TXEN0) | (1 << RXCIE0);
+
+ fdevopen(stdout_send, NULL);
+}
--- /dev/null
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright 2019, Olivier MATZ <zer0@droids-corp.org>
+ */
+
+#ifndef UART_H_
+#define UART_H_
+
+#include <stdio.h>
+
+#include "cirbuf.h"
+
+extern struct cirbuf uart_rxq;
+
+/* Initialise uart hardware. */
+void uart_init(void);
+
+/* Synchronous uart send. */
+void uart_send(char c);
+
+/* Receive a character from the rxq.
+ * Return 0 on success, -1 if queue is empty. */
+int8_t uart_recv(char *c);
+
+#endif