9.11. Creating a new Application

9.11.1. What is a WooKey application?

An application is a standalone software embedding all the needed content (apart from the application loader) and built on top of the EwoK microkernel. Applications interact with EwoK through syscalls.

Usually, libraries (such as the standard library) wrap such syscalls exposing higher level helpers. Applications can also interact with the hardware if they are allowed to (see the permissions model), and must initialize devices using dedicated syscalls. Higher level helpers for hardware interactions are also provided to the application in the form of userland drivers libraries (such as the USART library).

An application can use any of the SoCs, boards, cores and drivers sources to be able to run on the target.

9.11.2. The application source directory structure

A typical application named myapp is hosted in apps/myapp.

The application directory structure is composed of four main elements:

  • Its Makefile
  • Its configuration file (named Kconfig)
  • Its main.c source file (usually hosted in src/ relative directory)
  • a README.md file, describing the purpose of the application

And that should be all!

The myapp directory should look like this:

./src/main.c ./Kconfig ./Makefile ./README.md

9.11.3. Writing your first basic application

9.11.3.1. The application Makefile

The application Makefile allows to specify which library is needed by the application. The Makefile is also the link between the SDK and the application.

All applications Makefiles are nearly identical. Only the application’s dependencies (libs, drivers) and paths varies.

Here is a sample Makefile for a given application:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
###################################################################
# About the application name and path
###################################################################

# Application name, can be suffixed by the SDK
APP_NAME ?= crypto
# application build directory name
DIR_NAME := crypto

# project root directory, relative to app dir
PROJ_FILES = ../../

# binary, hex and elf file names
BIN_NAME = $(APP_NAME).bin
HEX_NAME = $(APP_NAME).hex
ELF_NAME = $(APP_NAME).elf

# SDK helper Makefiles inclusion
-include $(PROJ_FILES)/m_config.mk
-include $(PROJ_FILES)/m_generic.mk

# application build directory, relative to the SDK BUILD_DIR environment
# variable.
APP_BUILD_DIR = $(BUILD_DIR)/apps/$(DIR_NAME)

###################################################################
# About the compilation flags
###################################################################

# Application CFLAGS, first yours... Remember no use '+=' as the SDK
# create an initial CLFAGS value for application
CFLAGS += -Isrc/ -Iinc/ -MMD -MP
# and the SDK ones
CFLAGS += $(APPS_CFLAGS)

###################################################################
# About the link step
###################################################################

# the layout file, must be named 'myapp.fw1.ld' for default. is overriden by
# the SDK in multi-bank case.
# this is an option of the linker.
EXTRA_LDFLAGS ?= -Tpin.fw1.ld

# linker options to add the layout file
LDFLAGS += $(EXTRA_LDFLAGS) -L$(APP_BUILD_DIR)

# generic linker options
LDFLAGS += $(AFLAGS) -fno-builtin -nostdlib -nostartfiles

# project's library/drivers you whish to use..., dont forget to add your application
# build directory as library source path (-L argument)
LD_LIBS += -lcryp -laes -lstd -L$(APP_BUILD_DIR)

###################################################################
# okay let's list our source files and generated files now
###################################################################

CSRC_DIR = src
SRC = $(wildcard $(CSRC_DIR)/*.c)
OBJ = $(patsubst %.c,$(APP_BUILD_DIR)/%.o,$(SRC))
DEP = $(OBJ:.o=.d)

# the output directories, that will be deleted by the distclean target
OUT_DIRS = $(dir $(OBJ))

# the ldcript file generated by the SDK
LDSCRIPT_NAME = $(APP_BUILD_DIR)/$(APP_NAME).ld

# file to (dist)clean

# first, objects and compilation related
TODEL_CLEAN += $(OBJ) $(DEP) $(LDSCRIPT_NAME)

# the overall target content
TODEL_DISTCLEAN += $(APP_BUILD_DIR)

.PHONY: app

#############################################################
# Informations about the applications
#############################################################

show:
	@echo
	@echo "\t\tAPP_BUILD_DIR\t=> " $(APP_BUILD_DIR)
	@echo
	@echo "C sources files:"
	@echo "\t\tSRC\t=> " $(SRC)
	@echo "\t\tASM\t=> " $(ASM)
	@echo "\t\tOBJ\t=> " $(OBJ)
	@echo "\t\tDEP\t=> " $(DEP)
	@echo
	@echo "\t\tCFLAGS\t=> " $(CFLAGS)
	@echo "\t\tLDLAGS\t=> " $(LDLAGS)



###################################################################
# build targets (driver, core, SoC, Board... and local)
###################################################################

# all (default) build the app
all: $(APP_BUILD_DIR) alldeps app

############################################################
# eplicit dependency on the application libs and drivers
# compiling the application requires the compilation of its
# dependencies
# This permit to compile all the application dependencies
# when requesting the application compilation
############################################################

# library dependencies, here we have 2 libs
LIBDEP := $(BUILD_DIR)/libs/libstd/libstd.a \
          $(BUILD_DIR)/libs/libaes/libaes.a

libdep: $(LIBDEP)

$(LIBDEP):
	$(Q)$(MAKE) -C $(PROJ_FILES)libs/$(patsubst lib%.a,%,$(notdir $@))


# drivers dependencies, here only one
SOCDRVDEP := $(BUILD_DIR)/drivers/libcryp/libcryp.a

socdrvdep: $(SOCDRVDEP)

$(SOCDRVDEP):
	$(Q)$(MAKE) -C $(PROJ_FILES)drivers/socs/$(SOC)/$(patsubst lib%.a,%,$(notdir $@))

# board drivers dependencies. If none, leave the variable empty
BRDDRVDEP    :=

brddrvdep: $(BRDDRVDEP)

$(BRDDRVDEP):
	$(Q)$(MAKE) -C $(PROJ_FILES)drivers/boards/$(BOARD)/$(patsubst lib%.a,%,$(notdir $@))

# external dependencies
EXTDEP    :=

extdep: $(EXTDEP)

$(EXTDEP):
	$(Q)$(MAKE) -C $(PROJ_FILES)externals


# alldeps, including all dependencies (see 'all' target, which depends on it)
alldeps: libdep socdrvdep brddrvdep extdep


###################################################################
# build targets (driver, core, SoC, Board... and local)
###################################################################

# app build the hex and elf binaries
app: $(APP_BUILD_DIR)/$(ELF_NAME) $(APP_BUILD_DIR)/$(HEX_NAME)

# objet files and dependencies
$(APP_BUILD_DIR)/%.o: %.c
	$(call if_changed,cc_o_c)

# ELF file dependencies. libs are build separately and before.
# Be sure to add the libs to your config file!
$(APP_BUILD_DIR)/$(ELF_NAME): $(OBJ)
	$(call if_changed,link_o_target)

# same for hex
$(APP_BUILD_DIR)/$(HEX_NAME): $(APP_BUILD_DIR)/$(ELF_NAME)
	$(call if_changed,objcopy_ihex)

# same for bin. bin is not build but you can add it if you whish
$(APP_BUILD_DIR)/$(BIN_NAME): $(APP_BUILD_DIR)/$(ELF_NAME)
	$(call if_changed,objcopy_bin)

# special target to create the application build directory
$(APP_BUILD_DIR):
	$(call cmd,mkdir)

# including automaticaly calculated headers/sources dependencies
-include $(DEP)

Inclusion path of libraries and drivers is implicitly added by Tataouine through the APPS_CFLAGS variable.

As a consequence, including a library or a driver in the C code can be done by using the following syntax:

#include <api/headername.h>

The application layout is automatically generated.

It is important to respect the following variable names, as they are used by the SDK and overloaded as input variables for advanced mechanisms such as multi-bank support:

  • APP_NAME
  • EXTRA_LDFLAGS

Some variables are set by the SDK (through the included m_config.mk file):

  • BUILD_DIR : the build directory of the project
  • AFLAGS : the architecture-specific compilation flags
  • APPS_CFLAGS : libs and drivers inclusion flags and other arch-specific and warning flags
  • PROJ_FILES : the project root directory path

Caution

Please do not set these variables using :=, use instead ?= for paths and += for flags to support overloading, as shown in the sample Makefile

9.11.3.2. The application Kconfig

Like all applications, yours must be configured. A typical example is the permissions configuration, but also the required memory or stack. All these information can be set using a Kconfig file (same syntax as the Linux kernel) which allows the usage of ncurses or GTK-based configuration.

Like for Makefiles, applications Kconfig are nearly the same. Only the application name varies.

Hint

You can use the application Kconfig file to add other application-specific configuration elements if needed

80% of the Kconfig file is dedicated to the permissions.

Here is a sample Kconfig file.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
# starting PIN application configuration
menu "PIN custom app"

config APP_PIN
  bool "Pin App"
  depends on STM32F4
  default y
  ---help---
    Say y if you want to embbed USER custom application.

# start of (de)actication PIN option list
# all the config entry here depends on the activation of the application
if APP_PIN

config APP_PIN_FW
  bool "Pin App for firmware mode"
  depends on STM32F4
  default y
  ---help---
    Say y if you want to embbed Pin custom application in firmware.

config APP_PIN_DFU
  bool "Pin App for DFU mode"
  depends on FIRMWARE_DFU
  depends on STM32F4
  default y
  ---help---
    Say y if you want to embbed Pin custom application in DFU.


config APP_PIN_NUMSLOTS
  int "Number of required slots"
  default 1
  ---help---
    specify the number of slots required by the application text and data/bss.
    remember that the number of total slots is limited by the hardware.
    For example, there is 8 32kb sized slots in STM32F4, reduced to 7 when using SHM for
    .text + .data in flash, and 8 (reduced to 7 with SHM) slots of 16kb in RAM.
    This choice has an impact on the total number of applications that can be loaded
    in the firmware.

config APP_PIN_STACKSIZE
  int "Stack size"
  default 8192
  ---help---
    Specify the application stack size, in bytes. By default, set to 8192
    (i.e. 8k). Depending on the number of slots required, and the usage,
    the stack can be bigger or smaller.


# start of PIN permission configuration
menu "Permissions"

menu "Devices"

config APP_PIN_PERM_DEV_DMA
    bool "App has capacity to register DMA streams"
    default n
    ---help---
    if no, the application can't declare a DMA. If y, the application
    is able to require one or more secure DMA stream(s).

config APP_PIN_PERM_DEV_CRYPTO
    int "App can interact with HW cryptographic module"
    default 0
    range 0 3
    ---help---
    If 0, the application has no access to any HW cryp module.
    If 1, the application is able to use a HW cryp module configured
    by another application.
    If 2, the application is able to configure (inject keys) but can't
    use the crypt module (no IRQ handler).
    If 3, the application as a full, autonomous access to the HW cryp
    module

config APP_PIN_PERM_DEV_BUSES
    bool "App has capacity to use buses (SPI, I2C, USART...)"
    default n
    ---help---
    If n, the application can't access any buses (I2C, SPI, USART).
    If y, the application can require a bus mapping.

config APP_PIN_PERM_DEV_EXTI
    bool "App can use EXTI or GPIO driven external interrupts"
    default n

config APP_PIN_PERM_DEV_TIM
    bool "App can register a hardware timer"
    default n
    ---help---
    If n, the application can't require a timer from the kernel (using
    the timer API). If y, the application can require one or more HW
    timer(s).

endmenu

menu "Time"

config APP_PIN_PERM_TIM_GETCYCLES
    int "App has capacity to get current timestamp from kernel"
    default 0
    range 0 3
    ---help---
    If 0, the application has no access to timestamping.
    If 1, the application is able to get tick accurate timestamping.
    If 2, the application is able to get microsecond accurate timestamping.
    If 3, the application is able to get cycle accurate timestamping.

endmenu

menu "Tasking"

config APP_PIN_PERM_TSK_FISR
    bool "App is allowed to request main thread execution after ISR"
    default n
    ---help---
    If y, the application is able to request its main thread execution
    just after specific ISRs. This is done using the dev_irq_mode_t
    structure in device_t struct, using the IRQ_ISR_FORCE_MAINTHREAD value.
    If n, this field can't be set to this value.

config APP_PIN_PERM_TSK_FIPC
    bool "App is allowed to request peer execution on syncrhnous send IPC"
    default n
    ---help---
    If y, the application is able to request peer thread execution
    when sending syncrhonous IPC.

config APP_PIN_PERM_TSK_RESET
    bool "App is allowed to request a board software reset"
    default n
    ---help---
    If y, the application is able to request a MCU reset. This is usefull
    only if the application is handling events that require urgent reactivity
    implying board reset.

config APP_PIN_PERM_TSK_UPGRADE
    bool "App is allowed to upgrade the firmware"
    default n
    ---help---
    If y, the application is able to map the embedded flash device in
    order to upgrade the overall firmware. This permission should
    be hosted by a task with no external access (USB, USART...)



endmenu

menu "Memory management"

config APP_PIN_PERM_MEM_DYNAMIC_MAP
    bool "App is allow to declare MAP_VOLUNTARY devices"
    default n
    ---help---
    If y, the task is allowed to declare devices that are not automatically
    mapped, but that are mapped/unmapped voluntary using sys_cfg() syscalls.
    This permission does not give access to any devices not already declared
    but allow dynamic (un)mapping of previously declared ones.

endmenu


endmenu
# end of PIN permission configuration

# Here we can add one (or more) menu, submenu and so on, which
# are specific to the application behavior.
# The menu structure is free, but the option prefix should respect
# the global application prefix (here APP_PIN) to avoid any collision
# or confusion with other part of the configuration
#
# start of PIN specific option configuration
menu "Application specific options"

choice
  prompt "PIN human interaction mode"
  default APP_PIN_INPUT_USART
    config APP_PIN_INPUT_USART
      bool "PIN is asked through USART"
      ---help---
      User interaction for PIN ask is done using dedicated
      USART interface
    config APP_PIN_INPUT_SCREEN
      bool "PIN is asked through graphical interface"
      ---help---
      User interaction for PIN ask is done using dedicated
      screen/touchscreen couple
endchoice

if APP_PIN_INPUT_USART
  config APP_PIN_INPUT_USART_ID
  int "PIN USART interface identifier"
  range 1 6
endif

endmenu
# end of PIN specific option configuration

# end of (de)actication PIN option list
endif

endmenu
# end of PIN application configuration

9.11.4. Integrating your application to the Tataouine SDK

This is done by updating the manifest file to add your application repository. The SDK automatically detects that your application is added to the apps/ subdirectory and integrates it to the configuration subsystem.

Now, you only have to activate it using menuconfig, in the same way you configure the Linux kernel, by executing:

make menuconfig

Go to Userspace drivers and features, Applications. You should see your application and should be able to activate it. Until your configuration is saved, you can now directly compile and flash the new version of the firmware with your application integrated in it.

Warning

Please beware of the following limitations when adding new applications:
  • The number of available slots for applications is limited. Too much applications will induce a compilation error
  • The user is responsible of the memory ressource management of new applications (stack usage and so on). Some configuration errors here (for instance a stack too small for the app memory needs) will not be caught at compile time, and will lead to crashes (due to memory errors caught by the MPU) that can be hard to debug
  • Mistakes and inconsistencies when setting the applications permissions could lead to runtime hazardous errors!
  • The user must check the consistency of device drivers usage when adding new applications. For instance, if two applications both try to use USART4 concurrently, one of it will have its request refused by the kernel. As a general rule of thumb, sharing the same piece of hardware in two or more applications (i.e. registers handling the same peripheral) is not permitted and must be resolved by using a userspace proxy