Skip to content

Commit

Permalink
Merge #17091 #18148
Browse files Browse the repository at this point in the history
17091: USBUS: Add URB support r=benpicco a=bergzand

### Contribution description

This commit adds support for URBs (USB Request/Response Blocks). These
allow for submitting multi-transfer sized buffers with USBUS handling
the individual usbdev xmits. Multiple URBs can be queued at once for a
single endpoint and USBUS will handle them in the order of submission.

OUT endpoint URBs must always consist of a whole number of full-sized
transfers (N x MaxEndpointSize). They will automatically finish after
the endpoint received a transfer less than the endpoint size.

IN endpoints can be arbitrary-sized and do not have to consist of a
whole number of full-sized transmissions. They support a flag to
indicate that the last transfer in the sequence must be less than a full
sized transfer (USBUS_URB_FLAG_AUTO_ZLP) and this adds a zero length
transfer at the end of the transmissions if the last transfer was equal
to the maximum transfer size.

URBs can be cancelled, but if the URB is already being processed it will
be cancelled after the current transmission within the URB is finished.
If it is still in the queue it will immediately be removed from the
queue.

### Testing procedure

- `tests/usbus_cdc_ecm` should still work. Testing one of the usbdev-supported platform should be sufficient here.

### Issues/PRs references

Needs #17064 


18148: sys/flash_utils: helpers to store data in flash r=benpicco a=maribu

### Contribution description

This helpers that allow storing, accessing, and working with data in flash that works for both classical Harvard architectures (which do not map flash also into the address space) as well as modern Harvard architectures and von-Neumann architectures.

With this, `examples/default` again runs on the Arduino Uno / Nano. Since this board is still the "entry kit" for many people to embedded hardware, it would be nice to support it with our default example.

### Testing procedure

`examples/default` should run and work on ATmega boards (especially ATmega328P and ATmega32U4 based boards) as well on all other boards now.

### Issues/PRs references

None

Co-authored-by: Koen Zandberg <koen@bergzand.net>
Co-authored-by: Marian Buschsieweke <marian.buschsieweke@ovgu.de>
  • Loading branch information
3 people authored Feb 27, 2023
3 parents e9333fe + ba88608 + 7e58bea commit f0b60d5
Show file tree
Hide file tree
Showing 201 changed files with 1,356 additions and 879 deletions.
4 changes: 4 additions & 0 deletions cpu/atmega_common/include/cpu_conf.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ extern "C" {
*/
#define IRQ_API_INLINED (1)

#ifndef DOXYGEN
#define HAS_FLASH_UTILS_ARCH 1
#endif

#ifdef __cplusplus
}
#endif
Expand Down
2 changes: 1 addition & 1 deletion cpu/atmega_common/periph/timer.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
/**
* @brief Possible prescaler values, encoded as 2 ^ val
*/
static const uint8_t prescalers[] = { 0, 3, 6, 8, 10 };
static const __flash uint8_t prescalers[] = { 0, 3, 6, 8, 10 };

/**
* @brief Timer state context
Expand Down
60 changes: 60 additions & 0 deletions cpu/avr8_common/include/flash_utils_arch.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Copyright (C) 2022 Otto-von-Guericke-Universität Magdeburg
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/

/**
* @ingroup cpu_avr8_common
* @{
*
* @file
* @brief Implementation of flash_utils
*
* @author Marian Buschsieweke <marian.buschsieweke@ovgu.de>
*
*/

#ifndef FLASH_UTILS_ARCH_H
#define FLASH_UTILS_ARCH_H

#include <stdio.h>
#include <string.h>
#include <avr/pgmspace.h>

#ifdef __cplusplus
extern "C" {
#endif

/* this is documented in sys/include/flash_utils.h - let's not confuse
* Doxygen */
#ifndef DOXYGEN

#define FLASH_ATTR __flash
#define PRIsflash "S"
#define TO_FLASH(x) __extension__({static FLASH_ATTR const char __c[] = (x); &__c[0];})
#define flash_strcmp strcmp_P
#define flash_strncmp strncmp_P
#define flash_strlen strlen_P
#define flash_strcpy strcpy_P
#define flash_strncpy strncpy_P
#define flash_printf printf_P
/* avrlibc seemingly forgot to provide fprintf(), but vfprintf() is there */
#define flash_vprintf(fmt, arglist) vfprintf_P(stdout, fmt, arglist)
#define flash_fprintf fprintf_P
#define flash_vfprintf vfprintf_P
#define flash_snprintf snprintf_P
#define flash_vsnprintf vsnprintf_P
#define flash_puts puts_P
#define flash_memcpy memcpy_P

#endif /* Doxygen */

#ifdef __cplusplus
}
#endif

/** @} */
#endif /* FLASH_UTILS_ARCH_H */
145 changes: 145 additions & 0 deletions cpu/avr8_common/include/stdio.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
/*
* Copyright (C) 2023 Otto-von-Guericke-Universität Magdeburg
*
* This file is subject to the terms and conditions of the GNU Lesser General
* Public License v2.1. See the file LICENSE in the top level directory for more
* details.
*/

/**
* @defgroup cpu_avr8_common_stdio_wrapper stdio wrapper for AVR8
* @ingroup cpu_avr8_common
*
* This module a wrapper for the stdio.h header intended to make use of
* flash_utils.h in printf() automatically
*
* @{
*
* @file
* @brief stdio wrapper to extend the C libs stdio
*
* @author Marian Buschsieweke <marian.buschsieweke@ovgu.de>
*/

#ifndef STDIO_H
#define STDIO_H
#include_next "stdio.h"

/* C++ does not support __flash. Hence, only wrap printf() and friends for
* C and let C++ use them unmodified. */
#ifdef __cplusplus
extern "C" {
}
#else

#include "flash_utils.h"

#ifdef DOXYGEN
/**
* @brief A wrapper for the `printf()` function that passes arguments through
* unmodified, but fails to compile if the first argument is not a
* string literal.
*
* See e.g. `man 3 printf` or https://linux.die.net/man/3/printf for
* documentation the printf function. This applies fully here, as it passes
* through the arguments unmodified.
*
* The motivation for enforcing the first argument to be a string literal is
* three-fold:
*
* 1. It prevents security issues due format strings controlled by adversaries.
* 2. It makes sure that modern C compilers that do understand format
* specifiers have knowledge of the format string and can verify that the
* other arguments match what is given via format string specifiers
* 3. It allows to force the format string to flash even for Harvard
* architectures transparently
*
* Similar wrappers are also in place for `vprintf()`, `fprintf()`,
* `vfprintf()`, `snprintf()`, `vsnprintf()`.
*/
#define printf(...) /* implementation details */
#else
/* this helper function-like macro takes at least 65 arguments and will
* "return" the 65 argument unmodified. It is not useful by itself, but
* needed to implement _SINGLEARG_OR_MULTIARG(). */
#define _TAKE_65TH_TOKEN( _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, N, ...) N

#define _EXPAND_HELPER(x) x
/* this function-like macro expands its argument */
#define _EXPAND(x) _EXPAND_HELPER(x)

/* This function-like macro will expand to `SINGLEARG` if called with one
* argument and `MULTIARG` if called with more than one.
*
* (Implementation detail: It will not work with more than 64 arguments. But
* 64 arguments to one printf call ought to be enough for everyone...)
*/
#define _SINGLEARG_OR_MULTIARG(...) \
_TAKE_65TH_TOKEN(__VA_ARGS__, \
MULTIARG, MULTIARG, MULTIARG, MULTIARG, \
MULTIARG, MULTIARG, MULTIARG, MULTIARG, \
MULTIARG, MULTIARG, MULTIARG, MULTIARG, \
MULTIARG, MULTIARG, MULTIARG, MULTIARG, \
MULTIARG, MULTIARG, MULTIARG, MULTIARG, \
MULTIARG, MULTIARG, MULTIARG, MULTIARG, \
MULTIARG, MULTIARG, MULTIARG, MULTIARG, \
MULTIARG, MULTIARG, MULTIARG, MULTIARG, \
MULTIARG, MULTIARG, MULTIARG, MULTIARG, \
MULTIARG, MULTIARG, MULTIARG, MULTIARG, \
MULTIARG, MULTIARG, MULTIARG, MULTIARG, \
MULTIARG, MULTIARG, MULTIARG, MULTIARG, \
MULTIARG, MULTIARG, MULTIARG, MULTIARG, \
MULTIARG, MULTIARG, MULTIARG, MULTIARG, \
MULTIARG, MULTIARG, MULTIARG, MULTIARG, \
MULTIARG, MULTIARG, MULTIARG, SINGLEARG)
#define _CONCAT_HELPER(a, b) a ## b
#define _CONCAT(a, b) _CONCAT_HELPER(a, b)

/* Implementation for `printf(fmt)` */
#define _PRINTF_SINGLEARG(x) \
flash_printf(TO_FLASH(x))
/* Implementation for `printf(fmt, ...)` */
#define _PRINTF_MULTIARG(x, ...) \
flash_printf(TO_FLASH(x), __VA_ARGS__)
/* Dispatch to _PRINTF_SINGLEARG() and _PRINTF_MULTIARG() depending on the
* number of arguments. Special handling for `printf(fmt)` compared to
* `printf(fmt, ...)` is needed because the `__VA_ARGS__` part must contain
* at least one argument, which in case of `printf(fmt)` is not the case. */
#define printf(...) \
_EXPAND(_CONCAT(_PRINTF_, _SINGLEARG_OR_MULTIARG(__VA_ARGS__))(__VA_ARGS__))

/* And now all other printf variants. For the v*printf() versions we do not
* need to differentiate, because they have always the same number of arguments
* (with the last being va_list). For the other printf variants, we again need
* to dispatch to a _SINGLEARG and a _MULTIARG version. */
#define vprintf(fmt, args) flash_vprintf(TO_FLASH(fmt), args)

#define _FPRINTF_SINGLEARG(stream, x) \
flash_fprintf(stream, TO_FLASH(x))
#define _FPRINTF_MULTIARG(stream, x, ...) \
flash_fprintf(stream, TO_FLASH(x), __VA_ARGS__)
#define fprintf(stream, ...) \
_EXPAND(_CONCAT(_FPRINTF_, _SINGLEARG_OR_MULTIARG(__VA_ARGS__))(stream, __VA_ARGS__))

#define vfprintf(stream, fmt, args) flash_vfprintf(stream, TO_FLASH(fmt), args)

#define _SNPRINTF_SINGLEARG(buf, buf_len, fmt) \
flash_snprintf(buf, buf_len, TO_FLASH(fmt))
#define _SNPRINTF_MULTIARG(buf, buf_len, fmt, ...) \
flash_snprintf(buf, buf_len, TO_FLASH(fmt), __VA_ARGS__)
#define snprintf(buf, buf_len, ...) \
_EXPAND(_CONCAT(_SNPRINTF_, _SINGLEARG_OR_MULTIARG(__VA_ARGS__))(buf, buf_len, __VA_ARGS__))

#define vsnprintf(buf, buf_len, fmt, args) flash_vsnprintf(buf, buf_len, TO_FLASH(fmt), args)
#endif

#endif

#endif /* STDIO_H */
/** @} */
38 changes: 35 additions & 3 deletions drivers/include/saul.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,9 @@
#ifndef SAUL_H
#define SAUL_H

#include <stdint.h>
#include <errno.h>
#include <stdint.h>
#include <sys/types.h>

#include "phydat.h"

Expand Down Expand Up @@ -315,11 +316,42 @@ int saul_read_notsup(const void *dev, phydat_t *dat);
*
* @param[in] class_id device class ID
*
* @return string representation of the device class
* @return NULL if class ID is not known
* @return string representation of the device class
* @retval NULL class ID is not known
*
* @deprecated Use @ref saul_class_print or @ref saul_class_write instead
*
* @warning For classic Harvard architectures a small buffer is used to store
* the string, so that subsequent (or concurrent!) calls will
* overwrite the output.
*/
const char *saul_class_to_str(const uint8_t class_id);

/**
* @brief Prints the class string of the given class ID
*
* @param[in] class_id ID of the device class to print
*/
void saul_class_print(uint8_t class_id);

/**
* @brief Write the string representation of the given device class to the
* given buffer
*
* @param[out] dest destination buffer to write to
* @param[in] max_size size of the buffer at @p dest
* @param[in] class_id ID of the device class to write
*
* @return Number of bytes written
* @retval -EOVERFLOW buffer at @p dest is too small
* @retval -EINVAL invalid unit in @p unit
*
* @warning The function will never write a terminating zero byte
* @note If you pass `NULL` for @p dest, it will return the number of bytes
* it would write (regardless of @p max_size)
*/
ssize_t saul_class_write(char *dest, size_t max_size, uint8_t class_id);

#ifdef __cplusplus
}
#endif
Expand Down
Loading

0 comments on commit f0b60d5

Please sign in to comment.