ble_dtm.c has some bugs related to signed integer representation which prevent negative power (less than 0 dBm) from being requested in the vendor specific message.
-
First, this bug is not yet patched: devzone.nordicsemi.com/.../44704
-
In dtm_vendor_specific_pkt() the freq value (now called vendor_option) is passed to dtm_set_txpower() without proper sign extension.
-
In dtm_set_txpower() the value is converted to int8_t without proper sign extension, taking into account the value is only six bits so the int8_t sign bit will always be 0.
-
Any attempt to set negative power will therefore fail the "if (new_power8 > 4)" test in dtm_set_txpower().
-
If these checks are bypassed, m_tx_power is set to an invalid value because of the lack of proper sign extension.
EDIT: Here's an example.
Suppose we are commanding a power of -4 dBm.
In examples/dtm/direct_test_mode/main.c:87,
typedef uint32_t dtm_freq_t;
dtm_freq_t freq = (command >> 8) & 0x3F;
Now freq is -4 & 0x3F, or 0x0000003c
in dtm_cmd(), line 537, freq is supplied as the second arg to dtm_vendor_specific_pkt(), which is defined on ble_dtm.c:327:
static uint32_t dtm_vendor_specific_pkt(uint32_t vendor_cmd,
dtm_freq_t vendor_option)
so now vendor_option is 0x0000003c
This is passed directly to dtm_set_txpower(), which is defined in ble_dtm.c:577:
bool dtm_set_txpower(uint32_t new_tx_power)
so new_tx_power is 0x0000003c
on ble_dtm.c:580, new_power8 is defined:
int8_t new_power8 = (int8_t)(new_tx_power & 0xFF);
so new_power8 is now 0x3c. This is a positive value. The sign bit is clear.
0x3c is 60 decimal.
on ble_dtm.c:588, new_power8 is compared to 4. 60 is greater than 4, so false is returned. This false value causes dtm_vendor_specific_pkt() to return DTM_ERROR_ILLEGAL_CONFIGURATION, but this return value is ignored at main.c:169 and vendor specific commands do not set m_event on error, so it doesn't appear on the UART. But the tx_power being set will be the old one.