Compare commits
42 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6ed0a3557f | ||
|
|
a405f8b6b0 | ||
|
|
3683fe625b | ||
|
|
e9fcfa14e6 | ||
|
|
1d16757282 | ||
|
|
eeefd07f68 | ||
|
|
d013132f55 | ||
|
|
25398f36da | ||
|
|
d3c2a2457d | ||
|
|
10086910f5 | ||
|
|
01ad11252c | ||
|
|
652b7f6f83 | ||
|
|
32748c69b8 | ||
|
|
65104bc3cc | ||
|
|
ce4c30fc21 | ||
|
|
5d382fa560 | ||
|
|
e677cfd61d | ||
|
|
db1ed25fad | ||
|
|
e6f70e97c8 | ||
|
|
f89298cf0e | ||
|
|
2e98b1fd8c | ||
|
|
13072b4c7b | ||
|
|
43c936a5dd | ||
|
|
acc7cfaadf | ||
|
|
f9a56bcfd4 | ||
|
|
4a3b59d47b | ||
|
|
414d6c91a1 | ||
|
|
2daec8c70c | ||
|
|
229ca396aa | ||
|
|
06ae95e45c | ||
|
|
f72d8ffe80 | ||
|
|
f2eeeda113 | ||
|
|
916d9ad13b | ||
|
|
a90a3cc493 | ||
|
|
fb5845a072 | ||
|
|
c0d6fad199 | ||
|
|
60ae4f9351 | ||
|
|
6ebce3643e | ||
|
|
80ab098d64 | ||
|
|
d00c98d585 | ||
|
|
f290106952 | ||
|
|
2e0e5e27e8 |
@@ -162,6 +162,13 @@ zephyr_compile_options(${OPTIMIZATION_FLAG})
|
||||
# @Intent: Obtain compiler specific flags related to C++ that are not influenced by kconfig
|
||||
zephyr_compile_options($<$<COMPILE_LANGUAGE:CXX>:$<TARGET_PROPERTY:compiler-cpp,required>>)
|
||||
|
||||
# Extra warnings options for twister run
|
||||
if (CONFIG_COMPILER_WARNINGS_AS_ERRORS)
|
||||
zephyr_compile_options($<$<COMPILE_LANGUAGE:C>:$<TARGET_PROPERTY:compiler,warnings_as_errors>>)
|
||||
zephyr_compile_options($<$<COMPILE_LANGUAGE:ASM>:$<TARGET_PROPERTY:asm,warnings_as_errors>>)
|
||||
zephyr_link_libraries($<TARGET_PROPERTY:linker,warnings_as_errors>)
|
||||
endif()
|
||||
|
||||
# @Intent: Obtain compiler specific flags for compiling under different ISO standards of C++
|
||||
if(CONFIG_CPLUSPLUS)
|
||||
# From kconfig choice, pick a single dialect.
|
||||
|
||||
@@ -305,9 +305,13 @@ config NO_OPTIMIZATIONS
|
||||
help
|
||||
Compiler optimizations will be set to -O0 independently of other
|
||||
options.
|
||||
|
||||
endchoice
|
||||
|
||||
config COMPILER_WARNINGS_AS_ERRORS
|
||||
bool "Treat warnings as errors"
|
||||
help
|
||||
Turn on "warning as error" toolchain flags
|
||||
|
||||
config COMPILER_COLOR_DIAGNOSTICS
|
||||
bool "Enable colored diganostics"
|
||||
default y
|
||||
|
||||
2
VERSION
2
VERSION
@@ -1,5 +1,5 @@
|
||||
VERSION_MAJOR = 2
|
||||
VERSION_MINOR = 7
|
||||
PATCHLEVEL = 5
|
||||
PATCHLEVEL = 6
|
||||
VERSION_TWEAK = 0
|
||||
EXTRAVERSION =
|
||||
|
||||
@@ -132,6 +132,10 @@ set_property(TARGET compiler-cpp PROPERTY dialect_cpp2a "")
|
||||
set_property(TARGET compiler-cpp PROPERTY dialect_cpp20 "")
|
||||
set_property(TARGET compiler-cpp PROPERTY dialect_cpp2b "")
|
||||
|
||||
# Flags for set extra warnigs (ARCMWDT asm can't recognize --fatal-warnings. Skip it)
|
||||
set_property(TARGET compiler PROPERTY warnings_as_errors -Werror)
|
||||
set_property(TARGET asm PROPERTY warnings_as_errors -Werror)
|
||||
|
||||
# Disable exeptions flag in C++
|
||||
set_property(TARGET compiler-cpp PROPERTY no_exceptions "-fno-exceptions")
|
||||
|
||||
|
||||
@@ -65,6 +65,10 @@ set_property(TARGET compiler-cpp PROPERTY dialect_cpp2a)
|
||||
set_property(TARGET compiler-cpp PROPERTY dialect_cpp20)
|
||||
set_property(TARGET compiler-cpp PROPERTY dialect_cpp2b)
|
||||
|
||||
# Extra warnings options for twister run
|
||||
set_property(TARGET compiler PROPERTY warnings_as_errors)
|
||||
set_property(TARGET asm PROPERTY warnings_as_errors)
|
||||
|
||||
# Flag for disabling exeptions in C++
|
||||
set_property(TARGET compiler-cpp PROPERTY no_exceptions)
|
||||
|
||||
|
||||
@@ -137,6 +137,10 @@ set_property(TARGET compiler-cpp PROPERTY dialect_cpp20 "-std=c++20"
|
||||
set_property(TARGET compiler-cpp PROPERTY dialect_cpp2b "-std=c++2b"
|
||||
"-Wno-register" "-Wno-volatile")
|
||||
|
||||
# Flags for set extra warnigs (ARCMWDT asm can't recognize --fatal-warnings. Skip it)
|
||||
set_property(TARGET compiler PROPERTY warnings_as_errors -Werror)
|
||||
set_property(TARGET asm PROPERTY warnings_as_errors -Werror)
|
||||
|
||||
# Disable exeptions flag in C++
|
||||
set_property(TARGET compiler-cpp PROPERTY no_exceptions "-fno-exceptions")
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ list(REMOVE_DUPLICATES
|
||||
# Drop support for NOT CONFIG_HAS_DTS perhaps?
|
||||
if(EXISTS ${DTS_SOURCE})
|
||||
set(SUPPORTS_DTS 1)
|
||||
if(BOARD_REVISION AND EXISTS ${BOARD_DIR}/${BOARD}_${BOARD_REVISION_STRING}.overlay)
|
||||
if(DEFINED BOARD_REVISION AND EXISTS ${BOARD_DIR}/${BOARD}_${BOARD_REVISION_STRING}.overlay)
|
||||
list(APPEND DTS_SOURCE ${BOARD_DIR}/${BOARD}_${BOARD_REVISION_STRING}.overlay)
|
||||
endif()
|
||||
else()
|
||||
|
||||
4
cmake/linker/arcmwdt/linker_flags.cmake
Normal file
4
cmake/linker/arcmwdt/linker_flags.cmake
Normal file
@@ -0,0 +1,4 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# Extra warnings options for twister run
|
||||
set_property(TARGET linker PROPERTY warnings_as_errors -Wl,--fatal-warnings)
|
||||
@@ -3,6 +3,9 @@ if (NOT CONFIG_COVERAGE_GCOV)
|
||||
set_property(TARGET linker PROPERTY coverage --coverage)
|
||||
endif()
|
||||
|
||||
# Extra warnings options for twister run
|
||||
set_property(TARGET linker PROPERTY ld_extra_warning_options -Wl,--fatal-warnings)
|
||||
|
||||
# ld/clang linker flags for sanitizing.
|
||||
check_set_linker_property(TARGET linker APPEND PROPERTY sanitize_address -fsanitize=address)
|
||||
|
||||
|
||||
@@ -7,6 +7,9 @@ if (NOT CONFIG_COVERAGE_GCOV)
|
||||
set_property(TARGET linker PROPERTY coverage -lgcov)
|
||||
endif()
|
||||
|
||||
# Extra warnings options for twister run
|
||||
set_property(TARGET linker PROPERTY warnings_as_errors -Wl,--fatal-warnings)
|
||||
|
||||
# ld/gcc linker flags for sanitizing.
|
||||
check_set_linker_property(TARGET linker APPEND PROPERTY sanitize_address -lasan)
|
||||
check_set_linker_property(TARGET linker APPEND PROPERTY sanitize_address -fsanitize=address)
|
||||
|
||||
@@ -14,3 +14,6 @@ check_set_linker_property(TARGET linker APPEND PROPERTY sanitize_undefined)
|
||||
# If memory reporting is a post build command, please use
|
||||
# cmake/bintools/bintools.cmake insted.
|
||||
check_set_linker_property(TARGET linker PROPERTY memusage)
|
||||
|
||||
# Extra warnings options for twister run
|
||||
set_property(TARGET linker PROPERTY warnings_as_errors)
|
||||
|
||||
@@ -253,8 +253,8 @@ graphviz_dot_args = [
|
||||
# -- Linkcheck options ----------------------------------------------------
|
||||
|
||||
extlinks = {
|
||||
"jira": ("https://jira.zephyrproject.org/browse/%s", ""),
|
||||
"github": ("https://github.com/zephyrproject-rtos/zephyr/issues/%s", ""),
|
||||
"jira": ("https://jira.zephyrproject.org/browse/%s", "JIRA %s"),
|
||||
"github": ("https://github.com/zephyrproject-rtos/zephyr/issues/%s", "GitHub #%s"),
|
||||
}
|
||||
|
||||
linkcheck_timeout = 30
|
||||
|
||||
@@ -2,6 +2,70 @@
|
||||
|
||||
.. _zephyr_2.7:
|
||||
|
||||
.. _zephyr_2.7.6:
|
||||
|
||||
Zephyr 2.7.6
|
||||
####################
|
||||
|
||||
This is an LTS maintenance release with fixes.
|
||||
|
||||
Issues Fixed
|
||||
************
|
||||
|
||||
These GitHub issues were addressed since the previous 2.7.5 tagged
|
||||
release:
|
||||
|
||||
.. comment List derived from GitHub Issue query: ...
|
||||
* :github:`issuenumber` - issue title
|
||||
|
||||
* :github:`32145` - use ``k_thread_foreach_unlocked()`` with shell callbacks
|
||||
* :github:`56604` - drivers: nrf: rtc: make uptime consistent for app booted from v3.x mcuboot
|
||||
* :github:`25917` - bluetooth: fix deadlock with tx of acl data and hci commands
|
||||
* :github:`47649` - bluetooth: release att notification buffer after reconnection
|
||||
* :github:`43718` - bluetooth: bt_conn: ensure tx buffers can be allocated within timeout
|
||||
* :github:`60707` - canbus: isotp: seal context buffer memory leaks
|
||||
* :github:`60904` - drivers: spi_nor: make erase operation more opportunistic
|
||||
* :github:`61451` - drivers: can: stm32: correct timing_max parameters
|
||||
* :github:`61501` - canbus: isotp: convert SF length check from ``ASSERT`` to runtime check
|
||||
* :github:`61544` - drivers: ieee802154_nrf5: add payload length check on TX
|
||||
* :github:`61784` - bluetooth: controller: check minmum sizes of adv PDUs
|
||||
* :github:`62003` - drivers: dma: sam: implement xdmac ``get_status()`` API
|
||||
* :github:`62701` - can: rework the table lookup code in ``can_dlc_to_bytes()``
|
||||
* :github:`63544` - drivers: can: mcan: move RF0L and RF1L to line 1
|
||||
* :github:`63835` - net_mgmt: return ``EMSGSIZE`` if buffer passed to ``recvfrom()`` is too small
|
||||
* :github:`63965` - logging: fix handling of ``CONFIG_LOG_BLOCK_IN_THREAD_TIMEOUT_MS``
|
||||
* :github:`64398` - drivers: can: be consistent in ``filter_id`` checks when removing rx filters
|
||||
* :github:`65548` - cmake: modules: dts: fix board revision 0 overlay
|
||||
* :github:`66500` - toolchain: support ``CONFIG_COMPILER_WARNINGS_AS_ERRORS``
|
||||
* :github:`66888` - net: ipv6: drop received packets sent by the same interface
|
||||
* :github:`67692` - i2c: dw: fix integer overflow in ``i2c_dw_data_ask()``
|
||||
* :github:`69167` - fs: fuse: avoid possible buffer overflow
|
||||
* :github:`69637` - userspace: additional checks in ``K_SYSCALL_MEMORY``
|
||||
|
||||
Security Vulnerability Related
|
||||
******************************
|
||||
|
||||
The following security vulnerabilities (CVEs) were addressed in this
|
||||
release:
|
||||
|
||||
* CVE-2023-4263 `Zephyr project bug tracker GHSA-rf6q-rhhp-pqhf
|
||||
<https://github.com/zephyrproject-rtos/zephyr/security/advisories/GHSA-rf6q-rhhp-pqhf>`_
|
||||
|
||||
* CVE-2023-4424: `Zephyr project bug tracker GHSA-j4qm-xgpf-qjw3
|
||||
<https://github.com/zephyrproject-rtos/zephyr/security/advisories/GHSA-j4qm-xgpf-qjw3>`_
|
||||
|
||||
* CVE-2023-5779 `Zephyr project bug tracker GHSA-7cmj-963q-jj47
|
||||
<https://github.com/zephyrproject-rtos/zephyr/security/advisories/GHSA-7cmj-963q-jj47>`_
|
||||
|
||||
* CVE-2023-6249 `Zephyr project bug tracker GHSA-32f5-3p9h-2rqc
|
||||
<https://github.com/zephyrproject-rtos/zephyr/security/advisories/GHSA-32f5-3p9h-2rqc>`_
|
||||
|
||||
* CVE-2023-6881 `Zephyr project bug tracker GHSA-mh67-4h3q-p437
|
||||
<https://github.com/zephyrproject-rtos/zephyr/security/advisories/GHSA-mh67-4h3q-p437>`_
|
||||
|
||||
More detailed information can be found in:
|
||||
https://docs.zephyrproject.org/latest/security/vulnerabilities.html
|
||||
|
||||
.. _zephyr_2.7.5:
|
||||
|
||||
Zephyr 2.7.5
|
||||
|
||||
@@ -171,6 +171,11 @@ void can_loopback_detach(const struct device *dev, int filter_id)
|
||||
{
|
||||
struct can_loopback_data *data = DEV_DATA(dev);
|
||||
|
||||
if (filter_id < 0 || filter_id >= ARRAY_SIZE(data->filters)) {
|
||||
LOG_ERR("filter ID %d out of bounds", filter_id);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_DBG("Detach filter ID: %d", filter_id);
|
||||
k_mutex_lock(&data->mtx, K_FOREVER);
|
||||
data->filters[filter_id].rx_cb = NULL;
|
||||
|
||||
@@ -404,7 +404,8 @@ int can_mcan_init(const struct device *dev, const struct can_mcan_config *cfg,
|
||||
#ifdef CONFIG_CAN_STM32FD
|
||||
can->ils = CAN_MCAN_ILS_RXFIFO0 | CAN_MCAN_ILS_RXFIFO1;
|
||||
#else
|
||||
can->ils = CAN_MCAN_ILS_RF0N | CAN_MCAN_ILS_RF1N;
|
||||
can->ils = CAN_MCAN_ILS_RF0N | CAN_MCAN_ILS_RF1N |
|
||||
CAN_MCAN_ILS_RF0L | CAN_MCAN_ILS_RF1L;
|
||||
#endif
|
||||
can->ile = CAN_MCAN_ILE_EINT0 | CAN_MCAN_ILE_EINT1;
|
||||
/* Interrupt on every TX fifo element*/
|
||||
@@ -894,11 +895,16 @@ int can_mcan_attach_isr(struct can_mcan_data *data,
|
||||
void can_mcan_detach(struct can_mcan_data *data,
|
||||
struct can_mcan_msg_sram *msg_ram, int filter_nr)
|
||||
{
|
||||
if (filter_nr < 0) {
|
||||
LOG_ERR("filter ID %d out of bounds", filter_nr);
|
||||
return;
|
||||
}
|
||||
|
||||
k_mutex_lock(&data->inst_mutex, K_FOREVER);
|
||||
if (filter_nr >= NUM_STD_FILTER_DATA) {
|
||||
filter_nr -= NUM_STD_FILTER_DATA;
|
||||
if (filter_nr >= NUM_STD_FILTER_DATA) {
|
||||
LOG_ERR("Wrong filter id");
|
||||
LOG_ERR("filter ID %d out of bounds", filter_nr);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -551,6 +551,11 @@ static void mcp2515_detach(const struct device *dev, int filter_nr)
|
||||
{
|
||||
struct mcp2515_data *dev_data = DEV_DATA(dev);
|
||||
|
||||
if (filter_nr < 0 || filter_nr >= CONFIG_CAN_MAX_FILTER) {
|
||||
LOG_ERR("filter ID %d out of bounds", filter_nr);
|
||||
return;
|
||||
}
|
||||
|
||||
k_mutex_lock(&dev_data->mutex, K_FOREVER);
|
||||
dev_data->filter_usage &= ~BIT(filter_nr);
|
||||
k_mutex_unlock(&dev_data->mutex);
|
||||
|
||||
@@ -480,9 +480,8 @@ static void mcux_flexcan_detach(const struct device *dev, int filter_id)
|
||||
const struct mcux_flexcan_config *config = dev->config;
|
||||
struct mcux_flexcan_data *data = dev->data;
|
||||
|
||||
if (filter_id >= MCUX_FLEXCAN_MAX_RX) {
|
||||
LOG_ERR("Detach: Filter id >= MAX_RX (%d >= %d)", filter_id,
|
||||
MCUX_FLEXCAN_MAX_RX);
|
||||
if (filter_id < 0 || filter_id >= MCUX_FLEXCAN_MAX_RX) {
|
||||
LOG_ERR("filter ID %d out of bounds", filter_id);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -829,7 +829,8 @@ void can_rcar_detach(const struct device *dev, int filter_nr)
|
||||
{
|
||||
struct can_rcar_data *data = DEV_CAN_DATA(dev);
|
||||
|
||||
if (filter_nr >= CONFIG_CAN_RCAR_MAX_FILTER) {
|
||||
if (filter_nr < 0 || filter_nr >= CONFIG_CAN_RCAR_MAX_FILTER) {
|
||||
LOG_ERR("filter ID %d out of bounds", filter_nr);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1113,10 +1113,10 @@ static const struct can_driver_api can_api_funcs = {
|
||||
.prescaler = 0x01
|
||||
},
|
||||
.timing_max = {
|
||||
.sjw = 0x07,
|
||||
.sjw = 0x04,
|
||||
.prop_seg = 0x00,
|
||||
.phase_seg1 = 0x0F,
|
||||
.phase_seg2 = 0x07,
|
||||
.phase_seg1 = 0x10,
|
||||
.phase_seg2 = 0x08,
|
||||
.prescaler = 0x400
|
||||
}
|
||||
};
|
||||
|
||||
@@ -354,11 +354,36 @@ static int sam_xdmac_initialize(const struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sam_xdmac_get_status(const struct device *dev, uint32_t channel,
|
||||
struct dma_status *status)
|
||||
{
|
||||
const struct sam_xdmac_dev_cfg *const dev_cfg = dev->config;
|
||||
|
||||
Xdmac * const xdmac = dev_cfg->regs;
|
||||
uint32_t chan_cfg = xdmac->XDMAC_CHID[channel].XDMAC_CC;
|
||||
uint32_t ublen = xdmac->XDMAC_CHID[channel].XDMAC_CUBC;
|
||||
|
||||
/* we need to check some of the XDMAC_CC registers to determine the DMA direction */
|
||||
if ((chan_cfg & XDMAC_CC_TYPE_Msk) == 0) {
|
||||
status->dir = MEMORY_TO_MEMORY;
|
||||
} else if ((chan_cfg & XDMAC_CC_DSYNC_Msk) == XDMAC_CC_DSYNC_MEM2PER) {
|
||||
status->dir = MEMORY_TO_PERIPHERAL;
|
||||
} else {
|
||||
status->dir = PERIPHERAL_TO_MEMORY;
|
||||
}
|
||||
|
||||
status->busy = ((chan_cfg & XDMAC_CC_INITD_Msk) != 0) || (ublen > 0);
|
||||
status->pending_length = ublen;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dma_driver_api sam_xdmac_driver_api = {
|
||||
.config = sam_xdmac_config,
|
||||
.reload = sam_xdmac_transfer_reload,
|
||||
.start = sam_xdmac_transfer_start,
|
||||
.stop = sam_xdmac_transfer_stop,
|
||||
.get_status = sam_xdmac_get_status,
|
||||
};
|
||||
|
||||
/* DMA0 */
|
||||
|
||||
@@ -658,7 +658,7 @@ static int spi_nor_erase(const struct device *dev, off_t addr, size_t size)
|
||||
|
||||
if ((etp->exp != 0)
|
||||
&& SPI_NOR_IS_ALIGNED(addr, etp->exp)
|
||||
&& SPI_NOR_IS_ALIGNED(size, etp->exp)
|
||||
&& (size >= BIT(etp->exp))
|
||||
&& ((bet == NULL)
|
||||
|| (etp->exp > bet->exp))) {
|
||||
bet = etp;
|
||||
|
||||
@@ -43,10 +43,10 @@ static inline void i2c_dw_data_ask(const struct device *dev)
|
||||
{
|
||||
struct i2c_dw_dev_config * const dw = dev->data;
|
||||
uint32_t data;
|
||||
uint8_t tx_empty;
|
||||
int8_t rx_empty;
|
||||
uint8_t cnt;
|
||||
uint8_t rx_buffer_depth, tx_buffer_depth;
|
||||
int tx_empty;
|
||||
int rx_empty;
|
||||
int cnt;
|
||||
int rx_buffer_depth, tx_buffer_depth;
|
||||
union ic_comp_param_1_register ic_comp_param_1;
|
||||
uint32_t reg_base = get_regs(dev);
|
||||
|
||||
|
||||
@@ -494,6 +494,11 @@ static int nrf5_tx(const struct device *dev,
|
||||
uint8_t *payload = frag->data;
|
||||
bool ret = true;
|
||||
|
||||
if (payload_len > NRF5_PSDU_LENGTH) {
|
||||
LOG_ERR("Payload too large: %d", payload_len);
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
LOG_DBG("%p (%u)", payload, payload_len);
|
||||
|
||||
nrf5_radio->tx_psdu[0] = payload_len + NRF5_FCS_LENGTH;
|
||||
|
||||
@@ -341,10 +341,11 @@ int sys_clock_driver_init(const struct device *dev)
|
||||
alloc_mask = BIT_MASK(EXT_CHAN_COUNT) << 1;
|
||||
}
|
||||
|
||||
if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
|
||||
compare_set(0, counter() + CYC_PER_TICK,
|
||||
sys_clock_timeout_handler, NULL);
|
||||
}
|
||||
uint32_t initial_timeout = IS_ENABLED(CONFIG_TICKLESS_KERNEL) ?
|
||||
MAX_CYCLES : CYC_PER_TICK;
|
||||
|
||||
compare_set(0, counter() + initial_timeout,
|
||||
sys_clock_timeout_handler, NULL);
|
||||
|
||||
z_nrf_clock_control_lf_on(mode);
|
||||
|
||||
|
||||
@@ -266,6 +266,19 @@ struct bt_l2cap_chan_ops {
|
||||
*/
|
||||
void (*encrypt_change)(struct bt_l2cap_chan *chan, uint8_t hci_status);
|
||||
|
||||
/** @brief Channel alloc_seg callback
|
||||
*
|
||||
* If this callback is provided the channel will use it to allocate
|
||||
* buffers to store segments. This avoids wasting big SDU buffers with
|
||||
* potentially much smaller PDUs. If this callback is supplied, it must
|
||||
* return a valid buffer.
|
||||
*
|
||||
* @param chan The channel requesting a buffer.
|
||||
*
|
||||
* @return Allocated buffer.
|
||||
*/
|
||||
struct net_buf *(*alloc_seg)(struct bt_l2cap_chan *chan);
|
||||
|
||||
/** @brief Channel alloc_buf callback
|
||||
*
|
||||
* If this callback is provided the channel will use it to allocate
|
||||
|
||||
@@ -420,7 +420,7 @@ static inline uint8_t can_dlc_to_bytes(uint8_t dlc)
|
||||
static const uint8_t dlc_table[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 12,
|
||||
16, 20, 24, 32, 48, 64};
|
||||
|
||||
return dlc > 0x0F ? 64 : dlc_table[dlc];
|
||||
return dlc_table[MIN(dlc, ARRAY_SIZE(dlc_table) - 1)];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -889,15 +889,6 @@ static inline void net_buf_simple_restore(struct net_buf_simple *buf,
|
||||
buf->len = state->len;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flag indicating that the buffer has associated fragments. Only used
|
||||
* internally by the buffer handling code while the buffer is inside a
|
||||
* FIFO, meaning this never needs to be explicitly set or unset by the
|
||||
* net_buf API user. As long as the buffer is outside of a FIFO, i.e.
|
||||
* in practice always for the user for this API, the buf->frags pointer
|
||||
* should be used instead.
|
||||
*/
|
||||
#define NET_BUF_FRAGS BIT(0)
|
||||
/**
|
||||
* Flag indicating that the buffer's associated data pointer, points to
|
||||
* externally allocated memory. Therefore once ref goes down to zero, the
|
||||
@@ -907,7 +898,7 @@ static inline void net_buf_simple_restore(struct net_buf_simple *buf,
|
||||
* Reference count mechanism however will behave the same way, and ref
|
||||
* count going to 0 will free the net_buf but no the data pointer in it.
|
||||
*/
|
||||
#define NET_BUF_EXTERNAL_DATA BIT(1)
|
||||
#define NET_BUF_EXTERNAL_DATA BIT(0)
|
||||
|
||||
/**
|
||||
* @brief Network buffer representation.
|
||||
@@ -917,13 +908,11 @@ static inline void net_buf_simple_restore(struct net_buf_simple *buf,
|
||||
* using the net_buf_alloc() API.
|
||||
*/
|
||||
struct net_buf {
|
||||
union {
|
||||
/** Allow placing the buffer into sys_slist_t */
|
||||
sys_snode_t node;
|
||||
/** Allow placing the buffer into sys_slist_t */
|
||||
sys_snode_t node;
|
||||
|
||||
/** Fragments associated with this buffer. */
|
||||
struct net_buf *frags;
|
||||
};
|
||||
/** Fragments associated with this buffer. */
|
||||
struct net_buf *frags;
|
||||
|
||||
/** Reference count. */
|
||||
uint8_t ref;
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
#include <zephyr/types.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -61,6 +62,23 @@ extern "C" {
|
||||
/** @brief 0 if @p cond is true-ish; causes a compile error otherwise. */
|
||||
#define ZERO_OR_COMPILE_ERROR(cond) ((int) sizeof(char[1 - 2 * !(cond)]) - 1)
|
||||
|
||||
/**
|
||||
* @brief Determine if a buffer exceeds highest address
|
||||
*
|
||||
* This macro determines if a buffer identified by a starting address @a addr
|
||||
* and length @a buflen spans a region of memory that goes beond the highest
|
||||
* possible address (thereby resulting in a pointer overflow).
|
||||
*
|
||||
* @param addr Buffer starting address
|
||||
* @param buflen Length of the buffer
|
||||
*
|
||||
* @return true if pointer overflow detected, false otherwise
|
||||
*/
|
||||
#define Z_DETECT_POINTER_OVERFLOW(addr, buflen) \
|
||||
(((buflen) != 0) && \
|
||||
((UINTPTR_MAX - (uintptr_t)(addr)) <= ((uintptr_t)((buflen) - 1))))
|
||||
|
||||
|
||||
#if defined(__cplusplus)
|
||||
|
||||
/* The built-in function used below for type checking in C is not
|
||||
|
||||
@@ -329,6 +329,22 @@ extern int z_user_string_copy(char *dst, const char *src, size_t maxlen);
|
||||
*/
|
||||
#define Z_SYSCALL_VERIFY(expr) Z_SYSCALL_VERIFY_MSG(expr, #expr)
|
||||
|
||||
/**
|
||||
* @brief Macro to check if size is negative
|
||||
*
|
||||
* Z_SYSCALL_MEMORY can be called with signed/unsigned types
|
||||
* and because of that if we check if size is greater or equal to
|
||||
* zero, many static analyzers complain about no effect expression.
|
||||
*
|
||||
* @param ptr Memory area to examine
|
||||
* @param size Size of the memory area
|
||||
* @return true if size is valid, false otherwise
|
||||
* @note This is an internal API. Do not use unless you are extending
|
||||
* functionality in the Zephyr tree.
|
||||
*/
|
||||
#define Z_SYSCALL_MEMORY_SIZE_CHECK(ptr, size) \
|
||||
(((uintptr_t)ptr + size) >= (uintptr_t)ptr)
|
||||
|
||||
/**
|
||||
* @brief Runtime check that a user thread has read and/or write permission to
|
||||
* a memory area
|
||||
@@ -346,8 +362,10 @@ extern int z_user_string_copy(char *dst, const char *src, size_t maxlen);
|
||||
* @return 0 on success, nonzero on failure
|
||||
*/
|
||||
#define Z_SYSCALL_MEMORY(ptr, size, write) \
|
||||
Z_SYSCALL_VERIFY_MSG(arch_buffer_validate((void *)ptr, size, write) \
|
||||
== 0, \
|
||||
Z_SYSCALL_VERIFY_MSG(Z_SYSCALL_MEMORY_SIZE_CHECK(ptr, size) \
|
||||
&& !Z_DETECT_POINTER_OVERFLOW(ptr, size) \
|
||||
&& (arch_buffer_validate((void *)ptr, size, write) \
|
||||
== 0), \
|
||||
"Memory region %p (size %zu) %s access denied", \
|
||||
(void *)(ptr), (size_t)(size), \
|
||||
write ? "write" : "read")
|
||||
|
||||
@@ -48,8 +48,9 @@
|
||||
#endif
|
||||
|
||||
|
||||
#undef BUILD_ASSERT /* clear out common version */
|
||||
/* C++11 has static_assert built in */
|
||||
#ifdef __cplusplus
|
||||
#if defined(__cplusplus) && (__cplusplus >= 201103L)
|
||||
#define BUILD_ASSERT(EXPR, MSG...) static_assert(EXPR, "" MSG)
|
||||
|
||||
/*
|
||||
|
||||
@@ -2017,21 +2017,17 @@ class CMake():
|
||||
def run_cmake(self, args=[]):
|
||||
|
||||
if self.warnings_as_errors:
|
||||
ldflags = "-Wl,--fatal-warnings"
|
||||
cflags = "-Werror"
|
||||
aflags = "-Wa,--fatal-warnings"
|
||||
warnings_as_errors = 'y'
|
||||
gen_defines_args = "--edtlib-Werror"
|
||||
else:
|
||||
ldflags = cflags = aflags = ""
|
||||
warnings_as_errors = 'n'
|
||||
gen_defines_args = ""
|
||||
|
||||
logger.debug("Running cmake on %s for %s" % (self.source_dir, self.platform.name))
|
||||
cmake_args = [
|
||||
f'-B{self.build_dir}',
|
||||
f'-S{self.source_dir}',
|
||||
f'-DEXTRA_CFLAGS="{cflags}"',
|
||||
f'-DEXTRA_AFLAGS="{aflags}',
|
||||
f'-DEXTRA_LDFLAGS="{ldflags}"',
|
||||
f'-DCONFIG_COMPILER_WARNINGS_AS_ERRORS={warnings_as_errors}',
|
||||
f'-DEXTRA_GEN_DEFINES_ARGS={gen_defines_args}',
|
||||
f'-G{self.generator}'
|
||||
]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# DOC: used to generate docs
|
||||
|
||||
breathe>=4.30
|
||||
sphinx~=4.0
|
||||
sphinx~=5.0.2
|
||||
sphinx_rtd_theme~=1.0
|
||||
sphinx-tabs
|
||||
sphinxcontrib-svg2pdfconverter
|
||||
|
||||
@@ -1181,6 +1181,7 @@ static inline int isr_rx_pdu(struct lll_scan *lll, struct pdu_adv *pdu_adv_rx,
|
||||
/* Active scanner */
|
||||
} else if (((pdu_adv_rx->type == PDU_ADV_TYPE_ADV_IND) ||
|
||||
(pdu_adv_rx->type == PDU_ADV_TYPE_SCAN_IND)) &&
|
||||
(pdu_adv_rx->len >= offsetof(struct pdu_adv_adv_ind, data)) &&
|
||||
(pdu_adv_rx->len <= sizeof(struct pdu_adv_adv_ind)) &&
|
||||
lll->type &&
|
||||
#if defined(CONFIG_BT_CENTRAL)
|
||||
@@ -1274,6 +1275,7 @@ static inline int isr_rx_pdu(struct lll_scan *lll, struct pdu_adv *pdu_adv_rx,
|
||||
else if (((((pdu_adv_rx->type == PDU_ADV_TYPE_ADV_IND) ||
|
||||
(pdu_adv_rx->type == PDU_ADV_TYPE_NONCONN_IND) ||
|
||||
(pdu_adv_rx->type == PDU_ADV_TYPE_SCAN_IND)) &&
|
||||
(pdu_adv_rx->len >= offsetof(struct pdu_adv_adv_ind, data)) &&
|
||||
(pdu_adv_rx->len <= sizeof(struct pdu_adv_adv_ind))) ||
|
||||
((pdu_adv_rx->type == PDU_ADV_TYPE_DIRECT_IND) &&
|
||||
(pdu_adv_rx->len == sizeof(struct pdu_adv_direct_ind)) &&
|
||||
@@ -1287,6 +1289,7 @@ static inline int isr_rx_pdu(struct lll_scan *lll, struct pdu_adv *pdu_adv_rx,
|
||||
pdu_adv_rx, rl_idx)) ||
|
||||
#endif /* CONFIG_BT_CTLR_ADV_EXT */
|
||||
((pdu_adv_rx->type == PDU_ADV_TYPE_SCAN_RSP) &&
|
||||
(pdu_adv_rx->len >= offsetof(struct pdu_adv_scan_rsp, data)) &&
|
||||
(pdu_adv_rx->len <= sizeof(struct pdu_adv_scan_rsp)) &&
|
||||
(lll->state != 0U) &&
|
||||
isr_scan_rsp_adva_matches(pdu_adv_rx))) &&
|
||||
@@ -1334,6 +1337,7 @@ static inline bool isr_scan_init_check(struct lll_scan *lll,
|
||||
lll_scan_adva_check(lll, pdu->tx_addr, pdu->adv_ind.addr,
|
||||
rl_idx)) &&
|
||||
(((pdu->type == PDU_ADV_TYPE_ADV_IND) &&
|
||||
(pdu->len >= offsetof(struct pdu_adv_adv_ind, data)) &&
|
||||
(pdu->len <= sizeof(struct pdu_adv_adv_ind))) ||
|
||||
((pdu->type == PDU_ADV_TYPE_DIRECT_IND) &&
|
||||
(pdu->len == sizeof(struct pdu_adv_direct_ind)) &&
|
||||
|
||||
@@ -41,6 +41,11 @@
|
||||
|
||||
struct tx_meta {
|
||||
struct bt_conn_tx *tx;
|
||||
/* This flag indicates if the current buffer has already been partially
|
||||
* sent to the controller (ie, the next fragments should be sent as
|
||||
* continuations).
|
||||
*/
|
||||
bool is_cont;
|
||||
};
|
||||
|
||||
#define tx_data(buf) ((struct tx_meta *)net_buf_user_data(buf))
|
||||
@@ -396,6 +401,8 @@ int bt_conn_send_cb(struct bt_conn *conn, struct net_buf *buf,
|
||||
tx_data(buf)->tx = NULL;
|
||||
}
|
||||
|
||||
tx_data(buf)->is_cont = false;
|
||||
|
||||
net_buf_put(&conn->tx_queue, buf);
|
||||
return 0;
|
||||
}
|
||||
@@ -464,25 +471,41 @@ static int send_iso(struct bt_conn *conn, struct net_buf *buf, uint8_t flags)
|
||||
return bt_send(buf);
|
||||
}
|
||||
|
||||
static bool send_frag(struct bt_conn *conn, struct net_buf *buf, uint8_t flags,
|
||||
bool always_consume)
|
||||
static inline uint16_t conn_mtu(struct bt_conn *conn)
|
||||
{
|
||||
#if defined(CONFIG_BT_BREDR)
|
||||
if (conn->type == BT_CONN_TYPE_BR || !bt_dev.le.acl_mtu) {
|
||||
return bt_dev.br.mtu;
|
||||
}
|
||||
#endif /* CONFIG_BT_BREDR */
|
||||
#if defined(CONFIG_BT_ISO)
|
||||
if (conn->type == BT_CONN_TYPE_ISO && bt_dev.le.iso_mtu) {
|
||||
return bt_dev.le.iso_mtu;
|
||||
}
|
||||
#endif /* CONFIG_BT_ISO */
|
||||
#if defined(CONFIG_BT_CONN)
|
||||
return bt_dev.le.acl_mtu;
|
||||
#else
|
||||
return 0;
|
||||
#endif /* CONFIG_BT_CONN */
|
||||
}
|
||||
|
||||
static int do_send_frag(struct bt_conn *conn, struct net_buf *buf, uint8_t flags)
|
||||
{
|
||||
struct bt_conn_tx *tx = tx_data(buf)->tx;
|
||||
uint32_t *pending_no_cb;
|
||||
uint32_t *pending_no_cb = NULL;
|
||||
unsigned int key;
|
||||
int err = 0;
|
||||
|
||||
BT_DBG("conn %p buf %p len %u flags 0x%02x", conn, buf, buf->len,
|
||||
flags);
|
||||
|
||||
/* Wait until the controller can accept ACL packets */
|
||||
k_sem_take(bt_conn_get_pkts(conn), K_FOREVER);
|
||||
|
||||
/* Check for disconnection while waiting for pkts_sem */
|
||||
if (conn->state != BT_CONN_CONNECTED) {
|
||||
err = -ENOTCONN;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
BT_DBG("conn %p buf %p len %u flags 0x%02x", conn, buf, buf->len,
|
||||
flags);
|
||||
|
||||
/* Add to pending, it must be done before bt_buf_set_type */
|
||||
key = irq_lock();
|
||||
if (tx) {
|
||||
@@ -520,46 +543,61 @@ static bool send_frag(struct bt_conn *conn, struct net_buf *buf, uint8_t flags,
|
||||
(*pending_no_cb)--;
|
||||
}
|
||||
irq_unlock(key);
|
||||
|
||||
/* We don't want to end up in a situation where send_acl/iso
|
||||
* returns the same error code as when we don't get a buffer in
|
||||
* time.
|
||||
*/
|
||||
err = -EIO;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return true;
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
/* If we get here, something has seriously gone wrong:
|
||||
* We also need to destroy the `parent` buf.
|
||||
*/
|
||||
k_sem_give(bt_conn_get_pkts(conn));
|
||||
if (tx) {
|
||||
tx_free(tx);
|
||||
}
|
||||
|
||||
if (always_consume) {
|
||||
net_buf_unref(buf);
|
||||
}
|
||||
return false;
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline uint16_t conn_mtu(struct bt_conn *conn)
|
||||
static int send_frag(struct bt_conn *conn,
|
||||
struct net_buf *buf, struct net_buf *frag,
|
||||
uint8_t flags)
|
||||
{
|
||||
#if defined(CONFIG_BT_BREDR)
|
||||
if (conn->type == BT_CONN_TYPE_BR || !bt_dev.le.acl_mtu) {
|
||||
return bt_dev.br.mtu;
|
||||
/* Check if the controller can accept ACL packets */
|
||||
if (k_sem_take(bt_conn_get_pkts(conn), K_NO_WAIT)) {
|
||||
BT_DBG("no controller bufs");
|
||||
return -ENOBUFS;
|
||||
}
|
||||
#endif /* CONFIG_BT_BREDR */
|
||||
#if defined(CONFIG_BT_ISO)
|
||||
if (conn->type == BT_CONN_TYPE_ISO && bt_dev.le.iso_mtu) {
|
||||
return bt_dev.le.iso_mtu;
|
||||
|
||||
/* Add the data to the buffer */
|
||||
if (frag) {
|
||||
uint16_t frag_len = MIN(conn_mtu(conn), net_buf_tailroom(frag));
|
||||
|
||||
net_buf_add_mem(frag, buf->data, frag_len);
|
||||
net_buf_pull(buf, frag_len);
|
||||
} else {
|
||||
/* De-queue the buffer now that we know we can send it.
|
||||
* Only applies if the buffer to be sent is the original buffer,
|
||||
* and not one of its fragments.
|
||||
* This buffer was fetched from the FIFO using a peek operation.
|
||||
*/
|
||||
buf = net_buf_get(&conn->tx_queue, K_NO_WAIT);
|
||||
frag = buf;
|
||||
}
|
||||
#endif /* CONFIG_BT_ISO */
|
||||
#if defined(CONFIG_BT_CONN)
|
||||
return bt_dev.le.acl_mtu;
|
||||
#else
|
||||
return 0;
|
||||
#endif /* CONFIG_BT_CONN */
|
||||
|
||||
return do_send_frag(conn, frag, flags);
|
||||
}
|
||||
|
||||
static struct net_buf *create_frag(struct bt_conn *conn, struct net_buf *buf)
|
||||
{
|
||||
struct net_buf *frag;
|
||||
uint16_t frag_len;
|
||||
|
||||
switch (conn->type) {
|
||||
#if defined(CONFIG_BT_ISO)
|
||||
@@ -583,52 +621,55 @@ static struct net_buf *create_frag(struct bt_conn *conn, struct net_buf *buf)
|
||||
|
||||
/* Fragments never have a TX completion callback */
|
||||
tx_data(frag)->tx = NULL;
|
||||
|
||||
frag_len = MIN(conn_mtu(conn), net_buf_tailroom(frag));
|
||||
|
||||
net_buf_add_mem(frag, buf->data, frag_len);
|
||||
net_buf_pull(buf, frag_len);
|
||||
tx_data(frag)->is_cont = false;
|
||||
|
||||
return frag;
|
||||
}
|
||||
|
||||
static bool send_buf(struct bt_conn *conn, struct net_buf *buf)
|
||||
static int send_buf(struct bt_conn *conn, struct net_buf *buf)
|
||||
{
|
||||
struct net_buf *frag;
|
||||
uint8_t flags;
|
||||
int err;
|
||||
|
||||
BT_DBG("conn %p buf %p len %u", conn, buf, buf->len);
|
||||
|
||||
/* Send directly if the packet fits the ACL MTU */
|
||||
if (buf->len <= conn_mtu(conn)) {
|
||||
return send_frag(conn, buf, FRAG_SINGLE, false);
|
||||
}
|
||||
|
||||
/* Create & enqueue first fragment */
|
||||
frag = create_frag(conn, buf);
|
||||
if (!frag) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!send_frag(conn, frag, FRAG_START, true)) {
|
||||
return false;
|
||||
if (buf->len <= conn_mtu(conn) && !tx_data(buf)->is_cont) {
|
||||
BT_DBG("send single");
|
||||
return send_frag(conn, buf, NULL, FRAG_SINGLE);
|
||||
}
|
||||
|
||||
BT_DBG("start fragmenting");
|
||||
/*
|
||||
* Send the fragments. For the last one simply use the original
|
||||
* buffer (which works since we've used net_buf_pull on it.
|
||||
* buffer (which works since we've used net_buf_pull on it).
|
||||
*/
|
||||
flags = FRAG_START;
|
||||
if (tx_data(buf)->is_cont) {
|
||||
flags = FRAG_CONT;
|
||||
}
|
||||
|
||||
while (buf->len > conn_mtu(conn)) {
|
||||
frag = create_frag(conn, buf);
|
||||
if (!frag) {
|
||||
return false;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (!send_frag(conn, frag, FRAG_CONT, true)) {
|
||||
return false;
|
||||
err = send_frag(conn, buf, frag, flags);
|
||||
if (err) {
|
||||
BT_DBG("%p failed, mark as existing frag", buf);
|
||||
tx_data(buf)->is_cont = flags != FRAG_START;
|
||||
net_buf_unref(frag);
|
||||
return err;
|
||||
}
|
||||
|
||||
flags = FRAG_CONT;
|
||||
}
|
||||
|
||||
return send_frag(conn, buf, FRAG_END, false);
|
||||
BT_DBG("last frag");
|
||||
tx_data(buf)->is_cont = true;
|
||||
return send_frag(conn, buf, NULL, FRAG_END);
|
||||
}
|
||||
|
||||
static struct k_poll_signal conn_change =
|
||||
@@ -674,10 +715,26 @@ static int conn_prepare_events(struct bt_conn *conn,
|
||||
|
||||
BT_DBG("Adding conn %p to poll list", conn);
|
||||
|
||||
k_poll_event_init(&events[0],
|
||||
K_POLL_TYPE_FIFO_DATA_AVAILABLE,
|
||||
K_POLL_MODE_NOTIFY_ONLY,
|
||||
&conn->tx_queue);
|
||||
bool buffers_available = k_sem_count_get(bt_conn_get_pkts(conn)) > 0;
|
||||
bool packets_waiting = !k_fifo_is_empty(&conn->tx_queue);
|
||||
|
||||
if (packets_waiting && !buffers_available) {
|
||||
/* Only resume sending when the controller has buffer space
|
||||
* available for this connection.
|
||||
*/
|
||||
BT_DBG("wait on ctlr buffers");
|
||||
k_poll_event_init(&events[0],
|
||||
K_POLL_TYPE_SEM_AVAILABLE,
|
||||
K_POLL_MODE_NOTIFY_ONLY,
|
||||
bt_conn_get_pkts(conn));
|
||||
} else {
|
||||
/* Wait until there is more data to send. */
|
||||
BT_DBG("wait on host fifo");
|
||||
k_poll_event_init(&events[0],
|
||||
K_POLL_TYPE_FIFO_DATA_AVAILABLE,
|
||||
K_POLL_MODE_NOTIFY_ONLY,
|
||||
&conn->tx_queue);
|
||||
}
|
||||
events[0].tag = BT_EVENT_CONN_TX_QUEUE;
|
||||
|
||||
return 0;
|
||||
@@ -720,6 +777,7 @@ int bt_conn_prepare_events(struct k_poll_event events[])
|
||||
void bt_conn_process_tx(struct bt_conn *conn)
|
||||
{
|
||||
struct net_buf *buf;
|
||||
int err;
|
||||
|
||||
BT_DBG("conn %p", conn);
|
||||
|
||||
@@ -730,10 +788,26 @@ void bt_conn_process_tx(struct bt_conn *conn)
|
||||
return;
|
||||
}
|
||||
|
||||
/* Get next ACL packet for connection */
|
||||
buf = net_buf_get(&conn->tx_queue, K_NO_WAIT);
|
||||
/* Get next ACL packet for connection. The buffer will only get dequeued
|
||||
* if there is a free controller buffer to put it in.
|
||||
*
|
||||
* Important: no operations should be done on `buf` until it is properly
|
||||
* dequeued from the FIFO, using the `net_buf_get()` API.
|
||||
*/
|
||||
buf = k_fifo_peek_head(&conn->tx_queue);
|
||||
BT_ASSERT(buf);
|
||||
if (!send_buf(conn, buf)) {
|
||||
|
||||
/* Since we used `peek`, the queue still owns the reference to the
|
||||
* buffer, so we need to take an explicit additional reference here.
|
||||
*/
|
||||
buf = net_buf_ref(buf);
|
||||
err = send_buf(conn, buf);
|
||||
net_buf_unref(buf);
|
||||
|
||||
if (err == -EIO) {
|
||||
tx_data(buf)->tx = NULL;
|
||||
|
||||
/* destroy the buffer */
|
||||
net_buf_unref(buf);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2372,6 +2372,13 @@ static void process_events(struct k_poll_event *ev, int count)
|
||||
switch (ev->state) {
|
||||
case K_POLL_STATE_SIGNALED:
|
||||
break;
|
||||
case K_POLL_STATE_SEM_AVAILABLE:
|
||||
/* After this fn is exec'd, `bt_conn_prepare_events()`
|
||||
* will be called once again, and this time buffers will
|
||||
* be available, so the FIFO will be added to the poll
|
||||
* list instead of the ctlr buffers semaphore.
|
||||
*/
|
||||
break;
|
||||
case K_POLL_STATE_FIFO_DATA_AVAILABLE:
|
||||
if (ev->tag == BT_EVENT_CMD_TX) {
|
||||
send_cmd();
|
||||
@@ -2431,6 +2438,7 @@ static void hci_tx_thread(void *p1, void *p2, void *p3)
|
||||
events[0].state = K_POLL_STATE_NOT_READY;
|
||||
ev_count = 1;
|
||||
|
||||
/* This adds the FIFO per-connection */
|
||||
if (IS_ENABLED(CONFIG_BT_CONN) || IS_ENABLED(CONFIG_BT_ISO)) {
|
||||
ev_count += bt_conn_prepare_events(&events[1]);
|
||||
}
|
||||
|
||||
@@ -873,6 +873,12 @@ static void l2cap_chan_tx_process(struct k_work *work)
|
||||
if (sent < 0) {
|
||||
if (sent == -EAGAIN) {
|
||||
ch->tx_buf = buf;
|
||||
/* If we don't reschedule, and the app doesn't nudge l2cap (e.g. by
|
||||
* sending another SDU), the channel will be stuck in limbo. To
|
||||
* prevent this, we attempt to re-schedule the work item for every
|
||||
* channel on every connection when an SDU has successfully been
|
||||
* sent.
|
||||
*/
|
||||
} else {
|
||||
net_buf_unref(buf);
|
||||
}
|
||||
@@ -1693,13 +1699,20 @@ static void le_disconn_rsp(struct bt_l2cap *l2cap, uint8_t ident,
|
||||
bt_l2cap_chan_del(&chan->chan);
|
||||
}
|
||||
|
||||
static inline struct net_buf *l2cap_alloc_seg(struct net_buf *buf)
|
||||
static inline struct net_buf *l2cap_alloc_seg(struct net_buf *buf, struct bt_l2cap_le_chan *ch)
|
||||
{
|
||||
struct net_buf_pool *pool = net_buf_pool_get(buf->pool_id);
|
||||
struct net_buf *seg;
|
||||
|
||||
/* Try to use original pool if possible */
|
||||
seg = net_buf_alloc(pool, K_NO_WAIT);
|
||||
/* Use the dedicated segment callback if registered */
|
||||
if (ch->chan.ops->alloc_seg) {
|
||||
seg = ch->chan.ops->alloc_seg(&ch->chan);
|
||||
__ASSERT_NO_MSG(seg);
|
||||
} else {
|
||||
/* Try to use original pool if possible */
|
||||
seg = net_buf_alloc(pool, K_NO_WAIT);
|
||||
}
|
||||
|
||||
if (seg) {
|
||||
net_buf_reserve(seg, BT_L2CAP_CHAN_SEND_RESERVE);
|
||||
return seg;
|
||||
@@ -1736,7 +1749,8 @@ static struct net_buf *l2cap_chan_create_seg(struct bt_l2cap_le_chan *ch,
|
||||
}
|
||||
|
||||
segment:
|
||||
seg = l2cap_alloc_seg(buf);
|
||||
seg = l2cap_alloc_seg(buf, ch);
|
||||
|
||||
if (!seg) {
|
||||
return NULL;
|
||||
}
|
||||
@@ -1767,6 +1781,17 @@ static void l2cap_chan_tx_resume(struct bt_l2cap_le_chan *ch)
|
||||
k_work_submit(&ch->tx_work);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL)
|
||||
static void resume_all_channels(struct bt_conn *conn, void *data)
|
||||
{
|
||||
struct bt_l2cap_chan *chan;
|
||||
|
||||
SYS_SLIST_FOR_EACH_CONTAINER(&conn->channels, chan, node) {
|
||||
l2cap_chan_tx_resume(BT_L2CAP_LE_CHAN(chan));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void l2cap_chan_sdu_sent(struct bt_conn *conn, void *user_data)
|
||||
{
|
||||
uint16_t cid = POINTER_TO_UINT(user_data);
|
||||
@@ -1784,7 +1809,15 @@ static void l2cap_chan_sdu_sent(struct bt_conn *conn, void *user_data)
|
||||
chan->ops->sent(chan);
|
||||
}
|
||||
|
||||
/* Resume the current channel */
|
||||
l2cap_chan_tx_resume(BT_L2CAP_LE_CHAN(chan));
|
||||
|
||||
if (IS_ENABLED(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL)) {
|
||||
/* Resume all other channels in case one might be stuck.
|
||||
* The current channel has already been given priority.
|
||||
*/
|
||||
bt_conn_foreach(BT_CONN_TYPE_LE, resume_all_channels, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void l2cap_chan_seg_sent(struct bt_conn *conn, void *user_data)
|
||||
@@ -1872,12 +1905,12 @@ static int l2cap_chan_le_send(struct bt_l2cap_le_chan *ch,
|
||||
BT_WARN("Unable to send seg %d", err);
|
||||
atomic_inc(&ch->tx.credits);
|
||||
|
||||
/* If the segment is not the original buffer release it since it
|
||||
* won't be needed anymore.
|
||||
/* The host takes ownership of the reference in seg when
|
||||
* bt_l2cap_send_cb is successful. The call returned an error,
|
||||
* so we must get rid of the reference that was taken in
|
||||
* l2cap_chan_create_seg.
|
||||
*/
|
||||
if (seg != buf) {
|
||||
net_buf_unref(seg);
|
||||
}
|
||||
net_buf_unref(seg);
|
||||
|
||||
if (err == -ENOBUFS) {
|
||||
/* Restore state since segment could not be sent */
|
||||
@@ -2142,12 +2175,19 @@ static void l2cap_chan_send_credits(struct bt_l2cap_le_chan *chan,
|
||||
struct net_buf *buf, uint16_t credits)
|
||||
{
|
||||
struct bt_l2cap_le_credits *ev;
|
||||
uint16_t old_credits;
|
||||
|
||||
/* Cap the number of credits given */
|
||||
if (credits > chan->rx.init_credits) {
|
||||
credits = chan->rx.init_credits;
|
||||
}
|
||||
|
||||
/* Don't send back more than the initial amount. */
|
||||
old_credits = atomic_get(&chan->rx.credits);
|
||||
if (credits + old_credits > chan->rx.init_credits) {
|
||||
credits = chan->rx.init_credits - old_credits;
|
||||
}
|
||||
|
||||
buf = l2cap_create_le_sig_pdu(buf, BT_L2CAP_LE_CREDITS, get_ident(),
|
||||
sizeof(*ev));
|
||||
if (!buf) {
|
||||
@@ -2180,6 +2220,8 @@ static void l2cap_chan_update_credits(struct bt_l2cap_le_chan *chan,
|
||||
credits = ((chan->_sdu_len - net_buf_frags_len(buf)) +
|
||||
(chan->rx.mps - 1)) / chan->rx.mps;
|
||||
|
||||
BT_DBG("cred %d old %d", credits, (int)old_credits);
|
||||
|
||||
if (credits < old_credits) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -124,13 +124,22 @@ static void purge_buffers(sys_slist_t *list)
|
||||
|
||||
buf = (void *)sys_slist_get_not_empty(list);
|
||||
|
||||
buf->frags = NULL;
|
||||
buf->flags &= ~NET_BUF_FRAGS;
|
||||
|
||||
net_buf_unref(buf);
|
||||
}
|
||||
}
|
||||
|
||||
static void purge_seg_buffers(struct net_buf *buf)
|
||||
{
|
||||
/* Fragments head has always 2 references: one when allocated, one when becomes
|
||||
* fragments head.
|
||||
*/
|
||||
net_buf_unref(buf);
|
||||
|
||||
do {
|
||||
buf = net_buf_frag_del(NULL, buf);
|
||||
} while (buf != NULL);
|
||||
}
|
||||
|
||||
/* Intentionally start a little bit late into the ReceiveWindow when
|
||||
* it's large enough. This may improve reliability with some platforms,
|
||||
* like the PTS, where the receiver might not have sufficiently compensated
|
||||
@@ -166,8 +175,10 @@ static void friend_clear(struct bt_mesh_friend *frnd)
|
||||
for (i = 0; i < ARRAY_SIZE(frnd->seg); i++) {
|
||||
struct bt_mesh_friend_seg *seg = &frnd->seg[i];
|
||||
|
||||
purge_buffers(&seg->queue);
|
||||
seg->seg_count = 0U;
|
||||
if (seg->buf) {
|
||||
purge_seg_buffers(seg->buf);
|
||||
seg->seg_count = 0U;
|
||||
}
|
||||
}
|
||||
|
||||
STRUCT_SECTION_FOREACH(bt_mesh_friend_cb, cb) {
|
||||
@@ -1053,7 +1064,7 @@ init_friend:
|
||||
|
||||
static bool is_seg(struct bt_mesh_friend_seg *seg, uint16_t src, uint16_t seq_zero)
|
||||
{
|
||||
struct net_buf *buf = (void *)sys_slist_peek_head(&seg->queue);
|
||||
struct net_buf *buf = seg->buf;
|
||||
struct net_buf_simple_state state;
|
||||
uint16_t buf_seq_zero;
|
||||
uint16_t buf_src;
|
||||
@@ -1086,7 +1097,7 @@ static struct bt_mesh_friend_seg *get_seg(struct bt_mesh_friend *frnd,
|
||||
return seg;
|
||||
}
|
||||
|
||||
if (!unassigned && !sys_slist_peek_head(&seg->queue)) {
|
||||
if (!unassigned && !seg->buf) {
|
||||
unassigned = seg;
|
||||
}
|
||||
}
|
||||
@@ -1121,16 +1132,13 @@ static void enqueue_friend_pdu(struct bt_mesh_friend *frnd,
|
||||
return;
|
||||
}
|
||||
|
||||
net_buf_slist_put(&seg->queue, buf);
|
||||
seg->buf = net_buf_frag_add(seg->buf, buf);
|
||||
|
||||
if (type == BT_MESH_FRIEND_PDU_COMPLETE) {
|
||||
sys_slist_merge_slist(&frnd->queue, &seg->queue);
|
||||
net_buf_slist_put(&frnd->queue, seg->buf);
|
||||
|
||||
frnd->queue_size += seg->seg_count;
|
||||
seg->seg_count = 0U;
|
||||
} else {
|
||||
/* Mark the buffer as having more to come after it */
|
||||
buf->flags |= NET_BUF_FRAGS;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1244,6 +1252,15 @@ static void friend_timeout(struct k_work *work)
|
||||
return;
|
||||
}
|
||||
|
||||
/* Put next segment to the friend queue. */
|
||||
if (frnd->last != net_buf_frag_last(frnd->last)) {
|
||||
struct net_buf *next;
|
||||
|
||||
next = net_buf_frag_del(NULL, frnd->last);
|
||||
net_buf_frag_add(NULL, next);
|
||||
sys_slist_prepend(&frnd->queue, &next->node);
|
||||
}
|
||||
|
||||
md = (uint8_t)(sys_slist_peek_head(&frnd->queue) != NULL);
|
||||
|
||||
update_overwrite(frnd->last, md);
|
||||
@@ -1252,10 +1269,6 @@ static void friend_timeout(struct k_work *work)
|
||||
return;
|
||||
}
|
||||
|
||||
/* Clear the flag we use for segment tracking */
|
||||
frnd->last->flags &= ~NET_BUF_FRAGS;
|
||||
frnd->last->frags = NULL;
|
||||
|
||||
BT_DBG("Sending buf %p from Friend Queue of LPN 0x%04x",
|
||||
frnd->last, frnd->lpn);
|
||||
frnd->queue_size--;
|
||||
@@ -1330,16 +1343,11 @@ int bt_mesh_friend_init(void)
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) {
|
||||
struct bt_mesh_friend *frnd = &bt_mesh.frnd[i];
|
||||
int j;
|
||||
|
||||
sys_slist_init(&frnd->queue);
|
||||
|
||||
k_work_init_delayable(&frnd->timer, friend_timeout);
|
||||
k_work_init_delayable(&frnd->clear.timer, clear_timeout);
|
||||
|
||||
for (j = 0; j < ARRAY_SIZE(frnd->seg); j++) {
|
||||
sys_slist_init(&frnd->seg[j].queue);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -1635,11 +1643,16 @@ static bool friend_queue_prepare_space(struct bt_mesh_friend *frnd, uint16_t add
|
||||
frnd->queue_size--;
|
||||
avail_space++;
|
||||
|
||||
pending_segments = (buf->flags & NET_BUF_FRAGS);
|
||||
if (buf != net_buf_frag_last(buf)) {
|
||||
struct net_buf *next;
|
||||
|
||||
/* Make sure old slist entry state doesn't remain */
|
||||
buf->frags = NULL;
|
||||
buf->flags &= ~NET_BUF_FRAGS;
|
||||
next = net_buf_frag_del(NULL, buf);
|
||||
|
||||
net_buf_frag_add(NULL, next);
|
||||
sys_slist_prepend(&frnd->queue, &next->node);
|
||||
|
||||
pending_segments = true;
|
||||
}
|
||||
|
||||
net_buf_unref(buf);
|
||||
}
|
||||
@@ -1762,7 +1775,7 @@ void bt_mesh_friend_clear_incomplete(struct bt_mesh_subnet *sub, uint16_t src,
|
||||
|
||||
BT_WARN("Clearing incomplete segments for 0x%04x", src);
|
||||
|
||||
purge_buffers(&seg->queue);
|
||||
purge_seg_buffers(seg->buf);
|
||||
seg->seg_count = 0U;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -67,7 +67,10 @@ struct bt_mesh_friend {
|
||||
struct k_work_delayable timer;
|
||||
|
||||
struct bt_mesh_friend_seg {
|
||||
sys_slist_t queue;
|
||||
/* First received segment of a segmented message. Rest
|
||||
* segments are added as net_buf fragments.
|
||||
*/
|
||||
struct net_buf *buf;
|
||||
|
||||
/* The target number of segments, i.e. not necessarily
|
||||
* the current number of segments, in the queue. This is
|
||||
|
||||
@@ -907,7 +907,11 @@ static inline int send_sf(struct isotp_send_ctx *ctx)
|
||||
|
||||
frame.data[index++] = ISOTP_PCI_TYPE_SF | len;
|
||||
|
||||
__ASSERT_NO_MSG(len <= ISOTP_CAN_DL - index);
|
||||
if (len > ISOTP_CAN_DL - index) {
|
||||
LOG_ERR("SF len does not fit DL");
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
memcpy(&frame.data[index], data, len);
|
||||
|
||||
#ifdef CONFIG_ISOTP_ENABLE_TX_PADDING
|
||||
@@ -1202,6 +1206,7 @@ static int send(struct isotp_send_ctx *ctx, const struct device *can_dev,
|
||||
ret = attach_fc_filter(ctx);
|
||||
if (ret) {
|
||||
LOG_ERR("Can't attach fc filter: %d", ret);
|
||||
free_send_ctx(&ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1214,6 +1219,7 @@ static int send(struct isotp_send_ctx *ctx, const struct device *can_dev,
|
||||
ret = send_sf(ctx);
|
||||
ctx->state = ISOTP_TX_WAIT_FIN;
|
||||
if (ret) {
|
||||
free_send_ctx(&ctx);
|
||||
return ret == CAN_TIMEOUT ?
|
||||
ISOTP_N_TIMEOUT_A : ISOTP_N_ERROR;
|
||||
}
|
||||
|
||||
@@ -65,8 +65,15 @@ static void release_file_handle(size_t handle)
|
||||
static bool is_mount_point(const char *path)
|
||||
{
|
||||
char dir_path[PATH_MAX];
|
||||
size_t len;
|
||||
|
||||
sprintf(dir_path, "%s", path);
|
||||
len = strlen(path);
|
||||
if (len >= sizeof(dir_path)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(dir_path, path, len);
|
||||
dir_path[len] = '\0';
|
||||
return strcmp(dirname(dir_path), "/") == 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -1190,8 +1190,11 @@ void z_log_msg2_init(void)
|
||||
|
||||
struct log_msg2 *z_log_msg2_alloc(uint32_t wlen)
|
||||
{
|
||||
return (struct log_msg2 *)mpsc_pbuf_alloc(&log_buffer, wlen,
|
||||
K_MSEC(CONFIG_LOG_BLOCK_IN_THREAD_TIMEOUT_MS));
|
||||
return (struct log_msg2 *)mpsc_pbuf_alloc(
|
||||
&log_buffer, wlen,
|
||||
(CONFIG_LOG_BLOCK_IN_THREAD_TIMEOUT_MS == -1)
|
||||
? K_FOREVER
|
||||
: K_MSEC(CONFIG_LOG_BLOCK_IN_THREAD_TIMEOUT_MS));
|
||||
}
|
||||
|
||||
void z_log_msg2_commit(struct log_msg2 *msg)
|
||||
|
||||
@@ -17,7 +17,7 @@ config NET_BUF_USER_DATA_SIZE
|
||||
int "Size of user_data available in every network buffer"
|
||||
default 24 if MCUMGR_SMP_UDP && MCUMGR_SMP_UDP_IPV6
|
||||
default 8 if MCUMGR_SMP_UDP && MCUMGR_SMP_UDP_IPV4
|
||||
default 8 if ((BT || NET_TCP2) && 64BIT) || BT_ISO || MCUMGR_SMP_BT
|
||||
default 8 if ((BT || NET_TCP2) && 64BIT) || BT_CONN || BT_ISO
|
||||
default 4
|
||||
range 4 65535 if BT || NET_TCP2
|
||||
range 0 65535
|
||||
|
||||
@@ -405,7 +405,7 @@ struct net_buf *net_buf_get_debug(struct k_fifo *fifo, k_timeout_t timeout,
|
||||
struct net_buf *net_buf_get(struct k_fifo *fifo, k_timeout_t timeout)
|
||||
#endif
|
||||
{
|
||||
struct net_buf *buf, *frag;
|
||||
struct net_buf *buf;
|
||||
|
||||
NET_BUF_DBG("%s():%d: fifo %p", func, line, fifo);
|
||||
|
||||
@@ -416,18 +416,6 @@ struct net_buf *net_buf_get(struct k_fifo *fifo, k_timeout_t timeout)
|
||||
|
||||
NET_BUF_DBG("%s():%d: buf %p fifo %p", func, line, buf, fifo);
|
||||
|
||||
/* Get any fragments belonging to this buffer */
|
||||
for (frag = buf; (frag->flags & NET_BUF_FRAGS); frag = frag->frags) {
|
||||
frag->frags = k_fifo_get(fifo, K_NO_WAIT);
|
||||
__ASSERT_NO_MSG(frag->frags);
|
||||
|
||||
/* The fragments flag is only for FIFO-internal usage */
|
||||
frag->flags &= ~NET_BUF_FRAGS;
|
||||
}
|
||||
|
||||
/* Mark the end of the fragment list */
|
||||
frag->frags = NULL;
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
@@ -451,24 +439,19 @@ void net_buf_simple_reserve(struct net_buf_simple *buf, size_t reserve)
|
||||
|
||||
void net_buf_slist_put(sys_slist_t *list, struct net_buf *buf)
|
||||
{
|
||||
struct net_buf *tail;
|
||||
unsigned int key;
|
||||
|
||||
__ASSERT_NO_MSG(list);
|
||||
__ASSERT_NO_MSG(buf);
|
||||
|
||||
for (tail = buf; tail->frags; tail = tail->frags) {
|
||||
tail->flags |= NET_BUF_FRAGS;
|
||||
}
|
||||
|
||||
key = irq_lock();
|
||||
sys_slist_append_list(list, &buf->node, &tail->node);
|
||||
sys_slist_append(list, &buf->node);
|
||||
irq_unlock(key);
|
||||
}
|
||||
|
||||
struct net_buf *net_buf_slist_get(sys_slist_t *list)
|
||||
{
|
||||
struct net_buf *buf, *frag;
|
||||
struct net_buf *buf;
|
||||
unsigned int key;
|
||||
|
||||
__ASSERT_NO_MSG(list);
|
||||
@@ -477,40 +460,15 @@ struct net_buf *net_buf_slist_get(sys_slist_t *list)
|
||||
buf = (void *)sys_slist_get(list);
|
||||
irq_unlock(key);
|
||||
|
||||
if (!buf) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Get any fragments belonging to this buffer */
|
||||
for (frag = buf; (frag->flags & NET_BUF_FRAGS); frag = frag->frags) {
|
||||
key = irq_lock();
|
||||
frag->frags = (void *)sys_slist_get(list);
|
||||
irq_unlock(key);
|
||||
|
||||
__ASSERT_NO_MSG(frag->frags);
|
||||
|
||||
/* The fragments flag is only for list-internal usage */
|
||||
frag->flags &= ~NET_BUF_FRAGS;
|
||||
}
|
||||
|
||||
/* Mark the end of the fragment list */
|
||||
frag->frags = NULL;
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
void net_buf_put(struct k_fifo *fifo, struct net_buf *buf)
|
||||
{
|
||||
struct net_buf *tail;
|
||||
|
||||
__ASSERT_NO_MSG(fifo);
|
||||
__ASSERT_NO_MSG(buf);
|
||||
|
||||
for (tail = buf; tail->frags; tail = tail->frags) {
|
||||
tail->flags |= NET_BUF_FRAGS;
|
||||
}
|
||||
|
||||
k_fifo_put_list(fifo, buf, tail);
|
||||
k_fifo_put(fifo, buf);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_NET_BUF_LOG)
|
||||
|
||||
@@ -210,7 +210,7 @@ int net_ipv4_parse_hdr_options(struct net_pkt *pkt,
|
||||
}
|
||||
#endif
|
||||
|
||||
enum net_verdict net_ipv4_input(struct net_pkt *pkt)
|
||||
enum net_verdict net_ipv4_input(struct net_pkt *pkt, bool is_loopback)
|
||||
{
|
||||
NET_PKT_DATA_ACCESS_CONTIGUOUS_DEFINE(ipv4_access, struct net_ipv4_hdr);
|
||||
NET_PKT_DATA_ACCESS_DEFINE(udp_access, struct net_udp_hdr);
|
||||
@@ -266,6 +266,19 @@ enum net_verdict net_ipv4_input(struct net_pkt *pkt)
|
||||
net_pkt_update_length(pkt, pkt_len);
|
||||
}
|
||||
|
||||
if (!is_loopback) {
|
||||
if (net_ipv4_is_addr_loopback(&hdr->dst) ||
|
||||
net_ipv4_is_addr_loopback(&hdr->src)) {
|
||||
NET_DBG("DROP: localhost packet");
|
||||
goto drop;
|
||||
}
|
||||
|
||||
if (net_ipv4_is_my_addr(&hdr->src)) {
|
||||
NET_DBG("DROP: src addr is %s", "mine");
|
||||
goto drop;
|
||||
}
|
||||
}
|
||||
|
||||
if (net_ipv4_is_addr_mcast(&hdr->src)) {
|
||||
NET_DBG("DROP: src addr is %s", "mcast");
|
||||
goto drop;
|
||||
|
||||
@@ -488,6 +488,11 @@ enum net_verdict net_ipv6_input(struct net_pkt *pkt, bool is_loopback)
|
||||
NET_DBG("DROP: invalid scope multicast packet");
|
||||
goto drop;
|
||||
}
|
||||
|
||||
if (net_ipv6_is_my_addr(&hdr->src)) {
|
||||
NET_DBG("DROP: src addr is %s", "mine");
|
||||
goto drop;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check extension headers */
|
||||
|
||||
@@ -123,7 +123,7 @@ static inline enum net_verdict process_data(struct net_pkt *pkt,
|
||||
#endif
|
||||
#if defined(CONFIG_NET_IPV4)
|
||||
case 0x40:
|
||||
return net_ipv4_input(pkt);
|
||||
return net_ipv4_input(pkt, is_loopback);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -69,12 +69,14 @@ static inline const char *net_context_state(struct net_context *context)
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_NET_NATIVE)
|
||||
enum net_verdict net_ipv4_input(struct net_pkt *pkt);
|
||||
enum net_verdict net_ipv4_input(struct net_pkt *pkt, bool is_loopback);
|
||||
enum net_verdict net_ipv6_input(struct net_pkt *pkt, bool is_loopback);
|
||||
#else
|
||||
static inline enum net_verdict net_ipv4_input(struct net_pkt *pkt)
|
||||
static inline enum net_verdict net_ipv4_input(struct net_pkt *pkt,
|
||||
bool is_loopback)
|
||||
{
|
||||
ARG_UNUSED(pkt);
|
||||
ARG_UNUSED(is_loopback);
|
||||
|
||||
return NET_CONTINUE;
|
||||
}
|
||||
|
||||
@@ -205,8 +205,12 @@ again:
|
||||
|
||||
if (info) {
|
||||
ret = info_len + sizeof(hdr);
|
||||
ret = MIN(max_len, ret);
|
||||
memcpy(©_to[sizeof(hdr)], info, ret);
|
||||
if (ret > max_len) {
|
||||
errno = EMSGSIZE;
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(©_to[sizeof(hdr)], info, info_len);
|
||||
} else {
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
@@ -140,7 +140,13 @@ static int cmd_kernel_threads(const struct shell *shell,
|
||||
|
||||
shell_print(shell, "Scheduler: %u since last call", sys_clock_elapsed());
|
||||
shell_print(shell, "Threads:");
|
||||
k_thread_foreach(shell_tdata_dump, (void *)shell);
|
||||
|
||||
/*
|
||||
* Use the unlocked version as the callback itself might call
|
||||
* arch_irq_unlock.
|
||||
*/
|
||||
k_thread_foreach_unlocked(shell_tdata_dump, (void *)shell);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -184,7 +190,12 @@ static int cmd_kernel_stacks(const struct shell *shell,
|
||||
|
||||
ARG_UNUSED(argc);
|
||||
ARG_UNUSED(argv);
|
||||
k_thread_foreach(shell_stack_dump, (void *)shell);
|
||||
|
||||
/*
|
||||
* Use the unlocked version as the callback itself might call
|
||||
* arch_irq_unlock.
|
||||
*/
|
||||
k_thread_foreach_unlocked(shell_stack_dump, (void *)shell);
|
||||
|
||||
/* Placeholder logic for interrupt stack until we have better
|
||||
* kernel support, including dumping arch-specific exception-related
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
cmake_minimum_required(VERSION 3.20.0)
|
||||
|
||||
if (NOT DEFINED ENV{BSIM_COMPONENTS_PATH})
|
||||
message(FATAL_ERROR "This test requires the BabbleSim simulator. Please set\
|
||||
the environment variable BSIM_COMPONENTS_PATH to point to its components \
|
||||
folder. More information can be found in\
|
||||
https://babblesim.github.io/folder_structure_and_env.html")
|
||||
endif()
|
||||
|
||||
find_package(Zephyr HINTS $ENV{ZEPHYR_BASE})
|
||||
project(bsim_test_l2cap_stress)
|
||||
|
||||
FILE(GLOB app_sources src/*.c)
|
||||
target_sources(app PRIVATE ${app_sources} )
|
||||
|
||||
zephyr_include_directories(
|
||||
$ENV{BSIM_COMPONENTS_PATH}/libUtilv1/src/
|
||||
$ENV{BSIM_COMPONENTS_PATH}/libPhyComv1/src/
|
||||
)
|
||||
49
tests/bluetooth/bsim_bt/bsim_test_l2cap_stress/prj.conf
Normal file
49
tests/bluetooth/bsim_bt/bsim_test_l2cap_stress/prj.conf
Normal file
@@ -0,0 +1,49 @@
|
||||
CONFIG_BT=y
|
||||
CONFIG_BT_CENTRAL=y
|
||||
CONFIG_BT_PERIPHERAL=y
|
||||
CONFIG_BT_DEVICE_NAME="L2CAP stress test"
|
||||
|
||||
CONFIG_BT_EATT=n
|
||||
CONFIG_BT_L2CAP_ECRED=n
|
||||
|
||||
CONFIG_BT_SMP=y # Next config depends on it
|
||||
CONFIG_BT_L2CAP_DYNAMIC_CHANNEL=y
|
||||
|
||||
# Disable auto-initiated procedures so they don't
|
||||
# mess with the test's execution.
|
||||
CONFIG_BT_AUTO_PHY_UPDATE=n
|
||||
CONFIG_BT_AUTO_DATA_LEN_UPDATE=n
|
||||
CONFIG_BT_GAP_AUTO_UPDATE_CONN_PARAMS=n
|
||||
|
||||
# L2CAP MPS
|
||||
# 23+27+27=77 makes exactly three full packets
|
||||
CONFIG_BT_L2CAP_TX_MTU=77
|
||||
|
||||
# Use this to send L2CAP PDUs without any fragmentation.
|
||||
# In this particular case, we prefer fragmenting to test that code path.
|
||||
# CONFIG_BT_BUF_ACL_TX_SIZE=81
|
||||
|
||||
# L2CAP PDUs will be fragmented in 3 ACL packets.
|
||||
CONFIG_BT_BUF_ACL_TX_SIZE=27
|
||||
|
||||
CONFIG_BT_BUF_ACL_TX_COUNT=4
|
||||
|
||||
# The minimum value for this is
|
||||
# L2AP MPS + L2CAP header (4)
|
||||
CONFIG_BT_BUF_ACL_RX_SIZE=81
|
||||
|
||||
# Governs BT_CONN_TX_MAX, and so must be >= than the max number of
|
||||
# peers, since we attempt to send one SDU per peer. The test execution
|
||||
# is a bit slowed down by having this at the very minimum, but we want
|
||||
# to keep it that way as to stress the stack as much as possible.
|
||||
CONFIG_BT_L2CAP_TX_BUF_COUNT=6
|
||||
|
||||
CONFIG_BT_CTLR_DATA_LENGTH_MAX=27
|
||||
CONFIG_BT_CTLR_RX_BUFFERS=10
|
||||
|
||||
CONFIG_BT_MAX_CONN=10
|
||||
|
||||
CONFIG_LOG=y
|
||||
CONFIG_ASSERT=y
|
||||
CONFIG_BT_DEBUG_LOG=y
|
||||
CONFIG_NET_BUF_POOL_USAGE=y
|
||||
22
tests/bluetooth/bsim_bt/bsim_test_l2cap_stress/src/common.c
Normal file
22
tests/bluetooth/bsim_bt/bsim_test_l2cap_stress/src/common.c
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
|
||||
extern enum bst_result_t bst_result;
|
||||
|
||||
void test_init(void)
|
||||
{
|
||||
bst_ticker_set_next_tick_absolute(WAIT_TIME);
|
||||
bst_result = In_progress;
|
||||
}
|
||||
|
||||
void test_tick(bs_time_t HW_device_time)
|
||||
{
|
||||
if (bst_result != Passed) {
|
||||
FAIL("test failed (not passed after %i seconds)\n", WAIT_SECONDS);
|
||||
}
|
||||
}
|
||||
57
tests/bluetooth/bsim_bt/bsim_test_l2cap_stress/src/common.h
Normal file
57
tests/bluetooth/bsim_bt/bsim_test_l2cap_stress/src/common.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Common functions and helpers for L2CAP tests
|
||||
*
|
||||
* Copyright (c) 2022 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <zephyr/types.h>
|
||||
#include <sys/util.h>
|
||||
#include <sys/byteorder.h>
|
||||
|
||||
#include <bluetooth/bluetooth.h>
|
||||
#include <bluetooth/hci.h>
|
||||
#include <bluetooth/l2cap.h>
|
||||
#include "bs_types.h"
|
||||
#include "bs_tracing.h"
|
||||
#include "bstests.h"
|
||||
#include "bs_pc_backchannel.h"
|
||||
|
||||
extern enum bst_result_t bst_result;
|
||||
|
||||
#define CREATE_FLAG(flag) static atomic_t flag = (atomic_t)false
|
||||
#define SET_FLAG(flag) (void)atomic_set(&flag, (atomic_t)true)
|
||||
#define UNSET_FLAG(flag) (void)atomic_set(&flag, (atomic_t)false)
|
||||
#define TEST_FLAG(flag) (atomic_get(&flag) == (atomic_t)true)
|
||||
#define WAIT_FOR_FLAG_SET(flag) \
|
||||
while (!(bool)atomic_get(&flag)) { \
|
||||
(void)k_sleep(K_MSEC(1)); \
|
||||
}
|
||||
#define WAIT_FOR_FLAG_UNSET(flag) \
|
||||
while ((bool)atomic_get(&flag)) { \
|
||||
(void)k_sleep(K_MSEC(1)); \
|
||||
}
|
||||
|
||||
|
||||
#define WAIT_SECONDS 270 /* seconds */
|
||||
#define WAIT_TIME (WAIT_SECONDS * USEC_PER_SEC) /* microseconds*/
|
||||
|
||||
#define FAIL(...) \
|
||||
do { \
|
||||
bst_result = Failed; \
|
||||
bs_trace_error_time_line(__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#define PASS(...) \
|
||||
do { \
|
||||
bst_result = Passed; \
|
||||
bs_trace_info_time(1, __VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#define ASSERT(expr, ...) if (!(expr)) {FAIL(__VA_ARGS__); }
|
||||
|
||||
void test_init(void);
|
||||
void test_tick(bs_time_t HW_device_time);
|
||||
462
tests/bluetooth/bsim_bt/bsim_test_l2cap_stress/src/main.c
Normal file
462
tests/bluetooth/bsim_bt/bsim_test_l2cap_stress/src/main.c
Normal file
@@ -0,0 +1,462 @@
|
||||
/* main_l2cap_stress.c - Application main entry point */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2022 Nordic Semiconductor
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "bstests.h"
|
||||
#include "common.h"
|
||||
|
||||
#define LOG_MODULE_NAME main
|
||||
#include <logging/log.h>
|
||||
LOG_MODULE_REGISTER(LOG_MODULE_NAME, LOG_LEVEL_DBG);
|
||||
|
||||
CREATE_FLAG(is_connected);
|
||||
CREATE_FLAG(flag_l2cap_connected);
|
||||
|
||||
#define NUM_PERIPHERALS 6
|
||||
#define L2CAP_CHANS NUM_PERIPHERALS
|
||||
#define INIT_CREDITS 10
|
||||
#define SDU_NUM 20
|
||||
#define SDU_LEN 1230
|
||||
#define NUM_SEGMENTS 10
|
||||
|
||||
/* Only one SDU per link will be transmitted at a time */
|
||||
NET_BUF_POOL_DEFINE(sdu_tx_pool,
|
||||
CONFIG_BT_MAX_CONN, BT_L2CAP_SDU_BUF_SIZE(SDU_LEN),
|
||||
8, NULL);
|
||||
|
||||
NET_BUF_POOL_DEFINE(segment_pool,
|
||||
/* MTU + 4 l2cap hdr + 4 ACL hdr */
|
||||
NUM_SEGMENTS, BT_L2CAP_BUF_SIZE(CONFIG_BT_L2CAP_TX_MTU),
|
||||
8, NULL);
|
||||
|
||||
/* Only one SDU per link will be received at a time */
|
||||
NET_BUF_POOL_DEFINE(sdu_rx_pool,
|
||||
CONFIG_BT_MAX_CONN, BT_L2CAP_SDU_BUF_SIZE(SDU_LEN),
|
||||
8, NULL);
|
||||
|
||||
static struct bt_l2cap_le_chan l2cap_channels[L2CAP_CHANS];
|
||||
static struct bt_l2cap_chan *l2cap_chans[L2CAP_CHANS];
|
||||
|
||||
static uint8_t tx_data[SDU_LEN];
|
||||
static uint8_t tx_left[L2CAP_CHANS];
|
||||
static uint16_t rx_cnt;
|
||||
static uint8_t disconnect_counter;
|
||||
|
||||
int l2cap_chan_send(struct bt_l2cap_chan *chan, uint8_t *data, size_t len)
|
||||
{
|
||||
LOG_DBG("chan %p conn %p data %p len %d", chan, chan->conn, data, len);
|
||||
|
||||
struct net_buf *buf = net_buf_alloc(&sdu_tx_pool, K_NO_WAIT);
|
||||
|
||||
if (buf == NULL) {
|
||||
FAIL("No more memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
net_buf_reserve(buf, BT_L2CAP_SDU_CHAN_SEND_RESERVE);
|
||||
net_buf_add_mem(buf, data, len);
|
||||
|
||||
int ret = bt_l2cap_chan_send(chan, buf);
|
||||
|
||||
if (ret < 0) {
|
||||
FAIL("L2CAP error %d\n", ret);
|
||||
net_buf_unref(buf);
|
||||
}
|
||||
|
||||
LOG_DBG("sent %d len %d", ret, len);
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct net_buf *alloc_seg_cb(struct bt_l2cap_chan *chan)
|
||||
{
|
||||
struct net_buf *buf = net_buf_alloc(&segment_pool, K_NO_WAIT);
|
||||
|
||||
ASSERT(buf, "Ran out of segment buffers");
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
struct net_buf *alloc_buf_cb(struct bt_l2cap_chan *chan)
|
||||
{
|
||||
return net_buf_alloc(&sdu_rx_pool, K_NO_WAIT);
|
||||
}
|
||||
|
||||
static int get_l2cap_chan(struct bt_l2cap_chan *chan)
|
||||
{
|
||||
for (int i = 0; i < L2CAP_CHANS; i++) {
|
||||
if (l2cap_chans[i] == chan) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
FAIL("Channel %p not found\n", chan);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void register_channel(struct bt_l2cap_chan *chan)
|
||||
{
|
||||
int i = get_l2cap_chan(NULL);
|
||||
|
||||
l2cap_chans[i] = chan;
|
||||
}
|
||||
|
||||
void sent_cb(struct bt_l2cap_chan *chan)
|
||||
{
|
||||
LOG_DBG("%p", chan);
|
||||
int idx = get_l2cap_chan(chan);
|
||||
|
||||
if (tx_left[idx]) {
|
||||
tx_left[idx]--;
|
||||
l2cap_chan_send(chan, tx_data, sizeof(tx_data));
|
||||
} else {
|
||||
LOG_DBG("Done sending %p", chan->conn);
|
||||
}
|
||||
}
|
||||
|
||||
int recv_cb(struct bt_l2cap_chan *chan, struct net_buf *buf)
|
||||
{
|
||||
LOG_DBG("len %d", buf->len);
|
||||
rx_cnt++;
|
||||
|
||||
/* Verify SDU data matches TX'd data. */
|
||||
ASSERT(memcmp(buf->data, tx_data, buf->len) == 0, "RX data doesn't match TX");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void l2cap_chan_connected_cb(struct bt_l2cap_chan *l2cap_chan)
|
||||
{
|
||||
struct bt_l2cap_le_chan *chan =
|
||||
CONTAINER_OF(l2cap_chan, struct bt_l2cap_le_chan, chan);
|
||||
|
||||
SET_FLAG(flag_l2cap_connected);
|
||||
LOG_DBG("%p (tx mtu %d mps %d) (tx mtu %d mps %d)",
|
||||
l2cap_chan,
|
||||
chan->tx.mtu,
|
||||
chan->tx.mps,
|
||||
chan->rx.mtu,
|
||||
chan->rx.mps);
|
||||
|
||||
register_channel(l2cap_chan);
|
||||
}
|
||||
|
||||
void l2cap_chan_disconnected_cb(struct bt_l2cap_chan *chan)
|
||||
{
|
||||
UNSET_FLAG(flag_l2cap_connected);
|
||||
LOG_DBG("%p", chan);
|
||||
}
|
||||
|
||||
static struct bt_l2cap_chan_ops ops = {
|
||||
.connected = l2cap_chan_connected_cb,
|
||||
.disconnected = l2cap_chan_disconnected_cb,
|
||||
.alloc_buf = alloc_buf_cb,
|
||||
.alloc_seg = alloc_seg_cb,
|
||||
.recv = recv_cb,
|
||||
.sent = sent_cb,
|
||||
};
|
||||
|
||||
struct bt_l2cap_le_chan *get_free_l2cap_le_chan(void)
|
||||
{
|
||||
for (int i = 0; i < L2CAP_CHANS; i++) {
|
||||
struct bt_l2cap_le_chan *le_chan = &l2cap_channels[i];
|
||||
|
||||
if (le_chan->chan.state != BT_L2CAP_DISCONNECTED) {
|
||||
continue;
|
||||
}
|
||||
|
||||
memset(le_chan, 0, sizeof(*le_chan));
|
||||
return le_chan;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int server_accept_cb(struct bt_conn *conn, struct bt_l2cap_chan **chan)
|
||||
{
|
||||
struct bt_l2cap_le_chan *le_chan = NULL;
|
||||
|
||||
le_chan = get_free_l2cap_le_chan();
|
||||
if (le_chan == NULL) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
memset(le_chan, 0, sizeof(*le_chan));
|
||||
le_chan->chan.ops = &ops;
|
||||
le_chan->rx.mtu = SDU_LEN;
|
||||
le_chan->rx.init_credits = INIT_CREDITS;
|
||||
le_chan->tx.init_credits = INIT_CREDITS;
|
||||
*chan = &le_chan->chan;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct bt_l2cap_server test_l2cap_server = {
|
||||
.accept = server_accept_cb
|
||||
};
|
||||
|
||||
static int l2cap_server_register(bt_security_t sec_level)
|
||||
{
|
||||
test_l2cap_server.psm = 0;
|
||||
test_l2cap_server.sec_level = sec_level;
|
||||
|
||||
int err = bt_l2cap_server_register(&test_l2cap_server);
|
||||
|
||||
ASSERT(err == 0, "Failed to register l2cap server.");
|
||||
|
||||
return test_l2cap_server.psm;
|
||||
}
|
||||
|
||||
static void connected(struct bt_conn *conn, uint8_t conn_err)
|
||||
{
|
||||
char addr[BT_ADDR_LE_STR_LEN];
|
||||
|
||||
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
|
||||
|
||||
if (conn_err) {
|
||||
FAIL("Failed to connect to %s (%u)", addr, conn_err);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_DBG("%s", addr);
|
||||
|
||||
SET_FLAG(is_connected);
|
||||
}
|
||||
|
||||
static void disconnected(struct bt_conn *conn, uint8_t reason)
|
||||
{
|
||||
char addr[BT_ADDR_LE_STR_LEN];
|
||||
|
||||
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
|
||||
|
||||
LOG_DBG("%p %s (reason 0x%02x)", conn, addr, reason);
|
||||
|
||||
UNSET_FLAG(is_connected);
|
||||
disconnect_counter++;
|
||||
}
|
||||
|
||||
BT_CONN_CB_DEFINE(conn_callbacks) = {
|
||||
.connected = connected,
|
||||
.disconnected = disconnected,
|
||||
};
|
||||
|
||||
static void disconnect_device(struct bt_conn *conn, void *data)
|
||||
{
|
||||
int err;
|
||||
|
||||
SET_FLAG(is_connected);
|
||||
|
||||
err = bt_conn_disconnect(conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
|
||||
ASSERT(!err, "Failed to initate disconnect (err %d)", err);
|
||||
|
||||
LOG_DBG("Waiting for disconnection...");
|
||||
WAIT_FOR_FLAG_UNSET(is_connected);
|
||||
}
|
||||
|
||||
#define BT_LE_ADV_CONN_NAME_OT BT_LE_ADV_PARAM(BT_LE_ADV_OPT_CONNECTABLE | \
|
||||
BT_LE_ADV_OPT_USE_NAME | \
|
||||
BT_LE_ADV_OPT_ONE_TIME, \
|
||||
BT_GAP_ADV_FAST_INT_MIN_2, \
|
||||
BT_GAP_ADV_FAST_INT_MAX_2, NULL)
|
||||
|
||||
static const struct bt_data ad[] = {
|
||||
BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
|
||||
};
|
||||
|
||||
static void test_peripheral_main(void)
|
||||
{
|
||||
LOG_DBG("*L2CAP STRESS Peripheral started*");
|
||||
int err;
|
||||
|
||||
/* Prepare tx_data */
|
||||
for (size_t i = 0; i < sizeof(tx_data); i++) {
|
||||
tx_data[i] = (uint8_t)i;
|
||||
}
|
||||
|
||||
err = bt_enable(NULL);
|
||||
if (err) {
|
||||
FAIL("Can't enable Bluetooth (err %d)", err);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_DBG("Peripheral Bluetooth initialized.");
|
||||
LOG_DBG("Connectable advertising...");
|
||||
err = bt_le_adv_start(BT_LE_ADV_CONN_NAME_OT, ad, ARRAY_SIZE(ad), NULL, 0);
|
||||
if (err) {
|
||||
FAIL("Advertising failed to start (err %d)", err);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_DBG("Advertising started.");
|
||||
LOG_DBG("Peripheral waiting for connection...");
|
||||
WAIT_FOR_FLAG_SET(is_connected);
|
||||
LOG_DBG("Peripheral Connected.");
|
||||
|
||||
int psm = l2cap_server_register(BT_SECURITY_L1);
|
||||
|
||||
LOG_DBG("Registered server PSM %x", psm);
|
||||
|
||||
LOG_DBG("Peripheral waiting for transfer completion");
|
||||
while (rx_cnt < SDU_NUM) {
|
||||
k_msleep(100);
|
||||
}
|
||||
|
||||
bt_conn_foreach(BT_CONN_TYPE_LE, disconnect_device, NULL);
|
||||
WAIT_FOR_FLAG_UNSET(is_connected);
|
||||
LOG_INF("Total received: %d", rx_cnt);
|
||||
PASS("L2CAP STRESS Peripheral passed\n");
|
||||
}
|
||||
|
||||
static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type,
|
||||
struct net_buf_simple *ad)
|
||||
{
|
||||
struct bt_le_conn_param *param;
|
||||
struct bt_conn *conn;
|
||||
int err;
|
||||
|
||||
err = bt_le_scan_stop();
|
||||
if (err) {
|
||||
FAIL("Stop LE scan failed (err %d)", err);
|
||||
return;
|
||||
}
|
||||
|
||||
char str[BT_ADDR_LE_STR_LEN];
|
||||
|
||||
bt_addr_le_to_str(addr, str, sizeof(str));
|
||||
|
||||
LOG_DBG("Connecting to %s", str);
|
||||
|
||||
param = BT_LE_CONN_PARAM_DEFAULT;
|
||||
err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN, param, &conn);
|
||||
if (err) {
|
||||
FAIL("Create conn failed (err %d)", err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void connect_peripheral(void)
|
||||
{
|
||||
struct bt_le_scan_param scan_param = {
|
||||
.type = BT_LE_SCAN_TYPE_ACTIVE,
|
||||
.options = BT_LE_SCAN_OPT_NONE,
|
||||
.interval = BT_GAP_SCAN_FAST_INTERVAL,
|
||||
.window = BT_GAP_SCAN_FAST_WINDOW,
|
||||
};
|
||||
|
||||
UNSET_FLAG(is_connected);
|
||||
|
||||
int err = bt_le_scan_start(&scan_param, device_found);
|
||||
|
||||
ASSERT(!err, "Scanning failed to start (err %d)\n", err);
|
||||
|
||||
LOG_DBG("Central initiating connection...");
|
||||
WAIT_FOR_FLAG_SET(is_connected);
|
||||
}
|
||||
|
||||
static void connect_l2cap_channel(struct bt_conn *conn, void *data)
|
||||
{
|
||||
int err;
|
||||
struct bt_l2cap_le_chan *le_chan = get_free_l2cap_le_chan();
|
||||
|
||||
ASSERT(le_chan, "No more available channels\n");
|
||||
|
||||
le_chan->chan.ops = &ops;
|
||||
le_chan->rx.mtu = SDU_LEN;
|
||||
le_chan->rx.init_credits = INIT_CREDITS;
|
||||
le_chan->tx.init_credits = INIT_CREDITS;
|
||||
|
||||
UNSET_FLAG(flag_l2cap_connected);
|
||||
|
||||
err = bt_l2cap_chan_connect(conn, &le_chan->chan, 0x0080);
|
||||
ASSERT(!err, "Error connecting l2cap channel (err %d)\n", err);
|
||||
|
||||
WAIT_FOR_FLAG_SET(flag_l2cap_connected);
|
||||
}
|
||||
|
||||
static void test_central_main(void)
|
||||
{
|
||||
LOG_DBG("*L2CAP STRESS Central started*");
|
||||
int err;
|
||||
|
||||
/* Prepare tx_data */
|
||||
for (size_t i = 0; i < sizeof(tx_data); i++) {
|
||||
tx_data[i] = (uint8_t)i;
|
||||
}
|
||||
|
||||
err = bt_enable(NULL);
|
||||
ASSERT(err == 0, "Can't enable Bluetooth (err %d)\n", err);
|
||||
LOG_DBG("Central Bluetooth initialized.");
|
||||
|
||||
/* Connect all peripherals */
|
||||
for (int i = 0; i < NUM_PERIPHERALS; i++) {
|
||||
connect_peripheral();
|
||||
}
|
||||
|
||||
/* Connect L2CAP channels */
|
||||
LOG_DBG("Connect L2CAP channels");
|
||||
bt_conn_foreach(BT_CONN_TYPE_LE, connect_l2cap_channel, NULL);
|
||||
|
||||
/* Send SDU_NUM SDUs to each peripheral */
|
||||
for (int i = 0; i < NUM_PERIPHERALS; i++) {
|
||||
tx_left[i] = SDU_NUM;
|
||||
l2cap_chan_send(l2cap_chans[i], tx_data, sizeof(tx_data));
|
||||
}
|
||||
|
||||
LOG_DBG("Wait until all transfers are completed.");
|
||||
int remaining_tx_total;
|
||||
|
||||
do {
|
||||
k_msleep(100);
|
||||
|
||||
remaining_tx_total = 0;
|
||||
for (int i = 0; i < L2CAP_CHANS; i++) {
|
||||
remaining_tx_total += tx_left[i];
|
||||
}
|
||||
} while (remaining_tx_total);
|
||||
|
||||
LOG_DBG("Waiting until all peripherals are disconnected..");
|
||||
while (disconnect_counter < NUM_PERIPHERALS) {
|
||||
k_msleep(100);
|
||||
}
|
||||
LOG_DBG("All peripherals disconnected.");
|
||||
|
||||
PASS("L2CAP STRESS Central passed\n");
|
||||
}
|
||||
|
||||
static const struct bst_test_instance test_def[] = {
|
||||
{
|
||||
.test_id = "peripheral",
|
||||
.test_descr = "Peripheral L2CAP STRESS",
|
||||
.test_post_init_f = test_init,
|
||||
.test_tick_f = test_tick,
|
||||
.test_main_f = test_peripheral_main
|
||||
},
|
||||
{
|
||||
.test_id = "central",
|
||||
.test_descr = "Central L2CAP STRESS",
|
||||
.test_post_init_f = test_init,
|
||||
.test_tick_f = test_tick,
|
||||
.test_main_f = test_central_main
|
||||
},
|
||||
BSTEST_END_MARKER
|
||||
};
|
||||
|
||||
struct bst_test_list *test_main_l2cap_stress_install(struct bst_test_list *tests)
|
||||
{
|
||||
return bst_add_tests(tests, test_def);
|
||||
}
|
||||
|
||||
extern struct bst_test_list *test_main_l2cap_stress_install(struct bst_test_list *tests);
|
||||
|
||||
bst_test_install_t test_installers[] = {
|
||||
test_main_l2cap_stress_install,
|
||||
NULL
|
||||
};
|
||||
|
||||
void main(void)
|
||||
{
|
||||
bst_main();
|
||||
}
|
||||
43
tests/bluetooth/bsim_bt/bsim_test_l2cap_stress/tests_scripts/l2cap.sh
Executable file
43
tests/bluetooth/bsim_bt/bsim_test_l2cap_stress/tests_scripts/l2cap.sh
Executable file
@@ -0,0 +1,43 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copyright (c) 2022 Nordic Semiconductor
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# EATT test
|
||||
simulation_id="l2cap_stress"
|
||||
verbosity_level=2
|
||||
process_ids=""; exit_code=0
|
||||
|
||||
function Execute(){
|
||||
if [ ! -f $1 ]; then
|
||||
echo -e " \e[91m`pwd`/`basename $1` cannot be found (did you forget to\
|
||||
compile it?)\e[39m"
|
||||
exit 1
|
||||
fi
|
||||
# timeout 30 $@ & process_ids="$process_ids $!"
|
||||
$@ & process_ids="$process_ids $!"
|
||||
}
|
||||
|
||||
: "${BSIM_OUT_PATH:?BSIM_OUT_PATH must be defined}"
|
||||
|
||||
#Give a default value to BOARD if it does not have one yet:
|
||||
BOARD="${BOARD:-nrf52_bsim}"
|
||||
|
||||
cd ${BSIM_OUT_PATH}/bin
|
||||
|
||||
bsim_exe=./bs_${BOARD}_tests_bluetooth_bsim_bt_bsim_test_l2cap_stress_prj_conf
|
||||
|
||||
Execute "${bsim_exe}" -v=${verbosity_level} -s=${simulation_id} -d=0 -testid=central -rs=43
|
||||
|
||||
Execute "${bsim_exe}" -v=${verbosity_level} -s=${simulation_id} -d=1 -testid=peripheral -rs=42
|
||||
Execute "${bsim_exe}" -v=${verbosity_level} -s=${simulation_id} -d=2 -testid=peripheral -rs=10
|
||||
Execute "${bsim_exe}" -v=${verbosity_level} -s=${simulation_id} -d=3 -testid=peripheral -rs=23
|
||||
Execute "${bsim_exe}" -v=${verbosity_level} -s=${simulation_id} -d=4 -testid=peripheral -rs=7884
|
||||
Execute "${bsim_exe}" -v=${verbosity_level} -s=${simulation_id} -d=5 -testid=peripheral -rs=230
|
||||
Execute "${bsim_exe}" -v=${verbosity_level} -s=${simulation_id} -d=6 -testid=peripheral -rs=9
|
||||
|
||||
Execute ./bs_2G4_phy_v1 -v=${verbosity_level} -s=${simulation_id} -D=7 -sim_length=270e6 $@
|
||||
|
||||
for process_id in $process_ids; do
|
||||
wait $process_id || let "exit_code=$?"
|
||||
done
|
||||
exit $exit_code #the last exit code != 0
|
||||
@@ -61,6 +61,7 @@ app=tests/bluetooth/bsim_bt/bsim_test_app conf_file=prj_split_low_lat.conf \
|
||||
compile
|
||||
app=tests/bluetooth/bsim_bt/bsim_test_multiple compile
|
||||
app=tests/bluetooth/bsim_bt/bsim_test_advx compile
|
||||
app=tests/bluetooth/bsim_bt/bsim_test_l2cap_stress compile
|
||||
app=tests/bluetooth/bsim_bt/bsim_test_iso compile
|
||||
app=tests/bluetooth/bsim_bt/bsim_test_audio compile
|
||||
app=tests/bluetooth/bsim_bt/edtt_ble_test_app/hci_test_app \
|
||||
|
||||
@@ -440,7 +440,7 @@ static void test_icmpv4_send_echo_req(void)
|
||||
zassert_true(false, "EchoRequest packet prep failed");
|
||||
}
|
||||
|
||||
if (net_ipv4_input(pkt)) {
|
||||
if (net_ipv4_input(pkt, false)) {
|
||||
net_pkt_unref(pkt);
|
||||
zassert_true(false, "Failed to send");
|
||||
}
|
||||
@@ -457,7 +457,7 @@ static void test_icmpv4_send_echo_rep(void)
|
||||
zassert_true(false, "EchoReply packet prep failed");
|
||||
}
|
||||
|
||||
if (net_ipv4_input(pkt)) {
|
||||
if (net_ipv4_input(pkt, false)) {
|
||||
net_pkt_unref(pkt);
|
||||
zassert_true(false, "Failed to send");
|
||||
}
|
||||
@@ -476,7 +476,7 @@ static void test_icmpv4_send_echo_req_opt(void)
|
||||
zassert_true(false, "EchoRequest with opts packet prep failed");
|
||||
}
|
||||
|
||||
if (net_ipv4_input(pkt)) {
|
||||
if (net_ipv4_input(pkt, false)) {
|
||||
net_pkt_unref(pkt);
|
||||
zassert_true(false, "Failed to send");
|
||||
}
|
||||
@@ -492,7 +492,7 @@ static void test_icmpv4_send_echo_req_bad_opt(void)
|
||||
"EchoRequest with bad opts packet prep failed");
|
||||
}
|
||||
|
||||
if (!net_ipv4_input(pkt)) {
|
||||
if (!net_ipv4_input(pkt, false)) {
|
||||
net_pkt_unref(pkt);
|
||||
zassert_true(false, "Failed to send");
|
||||
}
|
||||
|
||||
@@ -1171,7 +1171,7 @@ static void test_src_localaddr_recv(void)
|
||||
struct in6_addr localaddr = { { { 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0x1 } } };
|
||||
struct in6_addr addr = { { { 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0x1 } } };
|
||||
0, 0, 0, 0, 0, 0, 0, 0x10 } } };
|
||||
enum net_verdict verdict;
|
||||
|
||||
verdict = recv_msg(&localaddr, &addr);
|
||||
@@ -1184,7 +1184,7 @@ static void test_dst_localaddr_recv(void)
|
||||
struct in6_addr localaddr = { { { 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0x1 } } };
|
||||
struct in6_addr addr = { { { 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0x1 } } };
|
||||
0, 0, 0, 0, 0, 0, 0, 0x10 } } };
|
||||
enum net_verdict verdict;
|
||||
|
||||
verdict = recv_msg(&addr, &localaddr);
|
||||
@@ -1197,7 +1197,7 @@ static void test_dst_iface_scope_mcast_recv(void)
|
||||
struct in6_addr mcast_iface = { { { 0xff, 0x01, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0 } } };
|
||||
struct in6_addr addr = { { { 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0x1 } } };
|
||||
0, 0, 0, 0, 0, 0, 0, 0x10 } } };
|
||||
enum net_verdict verdict;
|
||||
|
||||
verdict = recv_msg(&addr, &mcast_iface);
|
||||
@@ -1210,7 +1210,7 @@ static void test_dst_zero_scope_mcast_recv(void)
|
||||
struct in6_addr mcast_zero = { { { 0xff, 0x00, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0 } } };
|
||||
struct in6_addr addr = { { { 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0x1 } } };
|
||||
0, 0, 0, 0, 0, 0, 0, 0x10 } } };
|
||||
enum net_verdict verdict;
|
||||
|
||||
verdict = recv_msg(&addr, &mcast_zero);
|
||||
@@ -1223,7 +1223,7 @@ static void test_dst_site_scope_mcast_recv_drop(void)
|
||||
struct in6_addr mcast_site = { { { 0xff, 0x05, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0 } } };
|
||||
struct in6_addr addr = { { { 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0x1 } } };
|
||||
0, 0, 0, 0, 0, 0, 0, 0x10 } } };
|
||||
enum net_verdict verdict;
|
||||
|
||||
verdict = recv_msg(&addr, &mcast_site);
|
||||
@@ -1302,7 +1302,7 @@ static void test_dst_site_scope_mcast_recv_ok(void)
|
||||
struct in6_addr mcast_all_dhcp = { { { 0xff, 0x05, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0x01, 0, 0, 0, 0x03 } } };
|
||||
struct in6_addr addr = { { { 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0x1 } } };
|
||||
0, 0, 0, 0, 0, 0, 0, 0x10 } } };
|
||||
enum net_verdict verdict;
|
||||
struct net_context *ctx;
|
||||
|
||||
@@ -1329,7 +1329,7 @@ static void test_dst_org_scope_mcast_recv(void)
|
||||
struct in6_addr mcast_org = { { { 0xff, 0x08, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0 } } };
|
||||
struct in6_addr addr = { { { 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0x1 } } };
|
||||
0, 0, 0, 0, 0, 0, 0, 0x10 } } };
|
||||
enum net_verdict verdict;
|
||||
|
||||
verdict = recv_msg(&addr, &mcast_org);
|
||||
@@ -1342,7 +1342,7 @@ static void test_dst_iface_scope_mcast_send(void)
|
||||
struct in6_addr mcast_iface = { { { 0xff, 0x01, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0 } } };
|
||||
struct in6_addr addr = { { { 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0x1 } } };
|
||||
0, 0, 0, 0, 0, 0, 0, 0x10 } } };
|
||||
struct net_if_mcast_addr *maddr;
|
||||
struct net_context *ctx;
|
||||
int ret;
|
||||
@@ -1382,7 +1382,7 @@ static void test_dst_unknown_group_mcast_recv(void)
|
||||
};
|
||||
struct in6_addr in6_addr_any = IN6ADDR_ANY_INIT;
|
||||
struct in6_addr addr = { { { 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0x1 } } };
|
||||
0, 0, 0, 0, 0, 0x10 } } };
|
||||
struct net_context *ctx;
|
||||
enum net_verdict verdict;
|
||||
|
||||
@@ -1412,7 +1412,7 @@ static void test_dst_unjoined_group_mcast_recv(void)
|
||||
};
|
||||
struct in6_addr in6_addr_any = IN6ADDR_ANY_INIT;
|
||||
struct in6_addr addr = { { { 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0x1 } } };
|
||||
0, 0, 0, 0, 0, 0x10 } } };
|
||||
struct net_if_mcast_addr *maddr;
|
||||
struct net_context *ctx;
|
||||
enum net_verdict verdict;
|
||||
@@ -1454,7 +1454,7 @@ static void test_dst_is_other_iface_mcast_recv(void)
|
||||
0x08 } } };
|
||||
struct in6_addr in6_addr_any = IN6ADDR_ANY_INIT;
|
||||
struct in6_addr addr = { { { 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0x1 } } };
|
||||
0, 0, 0, 0, 0, 0x10 } } };
|
||||
struct net_if_mcast_addr *maddr;
|
||||
struct net_context *ctx;
|
||||
enum net_verdict verdict;
|
||||
|
||||
@@ -976,7 +976,7 @@ static void test_virtual_recv_data_from_tunnel(int remote_ip,
|
||||
net_pkt_cursor_init(outer);
|
||||
|
||||
if (peer_addr.sa_family == AF_INET) {
|
||||
verdict = net_ipv4_input(outer);
|
||||
verdict = net_ipv4_input(outer, false);
|
||||
} else {
|
||||
verdict = net_ipv6_input(outer, false);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user