5.3.1.2.1. sys_init, initializing devices¶
Devices declaration, initialization and configuration is done with the help of
the sys_init()
syscall family.
Contents
5.3.1.2.1.1. sys_init(INIT_GETTASKID)¶
If a task A wants to communicate with another task B, task A needs to retrieve task’s B identifier.
Note
- Each running task is identified by a unique identifier: its task id. A task have also a name, given by the implementor, to ease its identification.
- If IPC domains are supported by the kernel, only tasks in the same domain can identify each other.
Getting a task id is done with sys_init(INIT_GETTASKID)
syscall:
uint8_t peer_id;
e_syscall_ret ret;
ret = sys_init(INIT_GETTASKID, "task_b", &peer_id);
if (ret != SYS_E_DONE) {
...
}
In the example above, if the call is succesful, the peer_id
parameter is
updated with the task id.
5.3.1.2.1.2. sys_init(INIT_DEVACCESS)¶
If a task wants to use a device, it must request it to the kernel
using the sys_init(INIT_DEVACCESS)
syscall.
Warning
DMA streams are not initialized with sys_init(INIT_DEVACCESS)
but with sys_init(INIT_DMA)
To make that request, a device_t
structure,
whose prototype is defined in kernel/src/C/exported/devices.h
, must be
filled. That structure describes the requested device.
Its content is:
typedef struct {
char name[16];
physaddr_t address;
uint32_t size;
uint8_t irq_num;
uint8_t gpio_num;
dev_map_mode_t map_mode;
dev_irq_info_t irqs[MAX_IRQS];
dev_gpio_info_t gpios[MAX_GPIOS];
} device_t;
The fields of the device_t
structure are explained here:
name
contains a name, useful for debugging purposesaddress
is the base address of the device in memory (0 if the device is not mapped in memory)size
is the size of the mapping in memory (0 if the device is not mapped in memory)irq_num
is the number of configured IRQs in theirqs[]
arraygpio_num
is the number of configured GPIOs in thegpios[]
arraymap_mode
tell if the device must be automatically mapped in task’s address space.- 0 up to 4 IRQ lines are defined in
irqs[]
array- 0 up to 16 GPIOs are defined in
gpios[]
array
Below is an example, excerpt from the Blinky demo demo:
device_t leds;
int desc_leds;
memset (&leds, 0, sizeof (leds));
strncpy (leds.name, "LEDs", sizeof (leds.name));
leds.gpio_num = 4; /* Number of configured GPIO */
leds.gpios[0].kref.port = GPIO_PD;
leds.gpios[0].kref.pin = 12;
leds.gpios[0].mask = GPIO_MASK_SET_MODE | GPIO_MASK_SET_PUPD |
GPIO_MASK_SET_TYPE | GPIO_MASK_SET_SPEED;
leds.gpios[0].mode = GPIO_PIN_OUTPUT_MODE;
leds.gpios[0].pupd = GPIO_PULLDOWN;
leds.gpios[0].type = GPIO_PIN_OTYPER_PP;
leds.gpios[0].speed = GPIO_PIN_HIGH_SPEED;
leds.gpios[1].kref.port = GPIO_PD;
leds.gpios[1].kref.pin = 13;
leds.gpios[1].mask = GPIO_MASK_SET_MODE | GPIO_MASK_SET_PUPD |
GPIO_MASK_SET_TYPE | GPIO_MASK_SET_SPEED;
...
ret = sys_init(INIT_DEVACCESS, &leds, &desc_leds);
In this example:
leds
parameter is adevice_t
structure that describes the requested device. Here, the leds on the stm32f407 board.desc_leds
is a device id returned by the syscall. It’s not very useful here as the device id is used only in some few syscalls (sys_cfg(CFG_DEV_MAP)
andsys_cfg(CFG_DEV_UNMAP)
)
5.3.1.2.1.2.1. Mapping devices in memory¶
Due to MPU constraints on Cortex-M, a task can not map simultaneously more than 4 devices in memory.
If a task needs to manage more than 4 devices, it should use the
syscalls sys_cfg(CFG_DEV_MAP)
and sys_cfg(CFG_DEV_UNMAP)
to voluntary
map and unmap the desire devices. Those syscalls can be used only if:
map_mode
field of thedevice_t
structure is set toDEV_MAP_VOLUNTARY
- the task is granted with a specific permission to do this (set in the menuconfig kernel menu).
5.3.1.2.1.2.2. Using GPIOs¶
Each GPIO port/pin pair is identified by a kref
value. That value
must be filled in when using the sys_cfg(CFG_GPIO_GET)
and
sys_cfg(CFG_GPIO_GET)
syscalls (see sys_cfg, configuring devices).
5.3.1.2.1.2.3. IRQ handler¶
For each IRQ, an Interrupt Service Routine (ISR) should be declared. Here is an example excerpt from the Demo examples :
memset (&button, 0, sizeof (button));
strncpy (button.name, "BUTTON", sizeof (button.name));
button.gpio_num = 1;
button.gpios[0].kref.port = button_dev_infos.gpios[BUTTON].port;
button.gpios[0].kref.pin = button_dev_infos.gpios[BUTTON].pin;
button.gpios[0].mask = GPIO_MASK_SET_MODE | GPIO_MASK_SET_PUPD |
GPIO_MASK_SET_TYPE | GPIO_MASK_SET_SPEED |
GPIO_MASK_SET_EXTI;
button.gpios[0].mode = GPIO_PIN_INPUT_MODE;
button.gpios[0].pupd = GPIO_PULLDOWN;
button.gpios[0].type = GPIO_PIN_OTYPER_PP;
button.gpios[0].speed = GPIO_PIN_LOW_SPEED;
button.gpios[0].exti_trigger = GPIO_EXTI_TRIGGER_RISE;
button.gpios[0].exti_lock = GPIO_EXTI_UNLOCKED;
button.gpios[0].exti_handler = (user_handler_t) exti_button_handler;
/* Now that the button device structure is filled, use sys_init to
* initialize it */
ret = sys_init(INIT_DEVACCESS, &button, &desc_button);
5.3.1.2.1.3. sys_init(INIT_DMA)¶
If a task wants to use a DMA stream, it must request it to the kernel
using the sys_init(INIT_DMA)
syscall.
To make that request, a dma_t
structure,
whose prototype is defined in kernel/src/C/exported/dma.h
, must be
filled. That structure describes the requested DMA stream.
Its content is:
typedef struct {
uint8_t dma; /* DMA controler identifier (1 for DMA1, 2 for DMA2, etc.) */
uint8_t stream;
uint8_t channel;
uint16_t size; /* Transfering size in bytes */
physaddr_t in_addr; /* Input base address */
dma_prio_t in_prio; /* Priority */
user_dma_handler_t in_handler; /* ISR with one argument (irqnum), see types.h */
physaddr_t out_addr; /* Output base address */
dma_prio_t out_prio; /* Priority */
user_dma_handler_t out_handler; /* ISR with one argument (irqnum), see types.h */
dma_flowctrl_t flow_control; /* Flow controller */
dma_dir_t dir; /* Transfert direction */
dma_mode_t mode; /* DMA mode */
dma_datasize_t datasize; /* Data unit size (byte, half-word or word) */
bool mem_inc; /* Increment for memory */
bool dev_inc; /* Increment for device */
dma_burst_t mem_burst; /* Memory burst size */
dma_burst_t dev_burst; /* Device burst size */
} dma_t;
Example:
dma.dma = DMA2;
dma.stream = DMA2_STREAM_SDIO_FD;
dma.channel = DMA2_CHANNEL_SDIO;
dma.size = 0; /* Set later with DMA_RECONF */
dma.in_addr = (physaddr_t) 0; /* Set later with DMA_RECONF */
dma.in_prio = DMA_PRI_HIGH;
dma.in_handler = (user_dma_handler_t) sdio_dmacallback;
dma.out_addr = (volatile physaddr_t)sdio_get_data_addr();
dma.out_handler = (user_dma_handler_t) sdio_dmacallback;
dma.flow_control = DMA_FLOWCTRL_DEV;
dma.dir = MEMORY_TO_PERIPHERAL;
dma.mode = DMA_FIFO_MODE;
dma.datasize = DMA_DS_WORD;
dma.mem_inc = 1;
dma.dev_inc = 0;
dma.mem_burst = DMA_BURST_INC4;
dma.dev_burst = DMA_BURST_INC4;
ret = sys_init(INIT_DMA, &dma, &dma_descriptor);
In this example, the dma_descriptor
is an identifier returned by the
syscall and used by the sys_cfg(CFG_DMA_RECONF)
and
sys_cfg(CFG_DMA_RELOAD)
syscalls.
Note
For the sake of security, the EwoK DMA implementation denies memory-to-memory transfers.
5.3.1.2.1.4. sys_init(INIT_DMA_SHM)¶
When multiple tasks take part in a complex data flow with multiple DMA copies from one device to another (e.g. from a USB High Speed device to the SDIO interface), it may be efficient to support pipelined DMA transfers with low latency between tasks.
As tasks have no rights to request a DMA transfer from another task’s buffer toward a device they own, this syscall allows to explicitly declare this right, based on the Ewok permission model.
Using such a mechanism, the task can initiate a DMA transfer from a foreign memory buffer without any direct access to it, but only toward a given peripheral (e.g. a CRYP device or an SDIO device).
Sharing a DMA buffer with another task is done with the following API:
e_syscall_ret sys_init(INIT_DMA_SHM, dma_shm_t *dma_shm);
Declaring a DMA SHM does not create a mapping of the other task’s buffer in the current task memory map. Only the DMA controller is able to access the other task’s buffer, as a source or destination of the transaction. The current task is not able to read or write directly into the buffer. As the MEMORY_TO_MEMORY DMA transaction is also forbidden, the task is not able to use the DMA to get back its content from the DMA controller by requesting a copy into its own memory map.
5.3.1.2.1.5. sys_init(INIT_DONE)¶
As previously described, this syscall locks the initialization phase and starts
the nominal phase of the task. From now on, the task can execute all syscalls
but the sys_init()
one under its own permission condition.
Finalizing the initialization phase is done with the following API:
e_syscall_ret sys_init(INIT_DONE);