Sign ITS messages on the ESP32-C5 with vanetza-idf, over USB or BLE
obu-firmware is now a port of the colleague's standalone VRU station (microbu-esp32c5/firmware, kept beside this repository and gitignored): the vanetza-idf C-ITS stack with the TS 103 097 security entity, credentials in NVS, the station-link v1 protocol over the native USB port (frame type 0x10 in the existing 0xAA55 framing) and over a BLE GATT peripheral, and its ITS-G5 radio adapter. The phone still builds CAM and VAM; the board adds GeoNetworking/BTP and signs with the provisioned authorization ticket. The private key never leaves the board. Builds with ESP-IDF 6.0.2 only, which vanetza-idf pins for the radio's private driver ABI. The previous C firmware stays on disk unbuilt; a full-flash backup of the bench board is kept in firmware-backups/ (gitignored). Changed against the colleague's firmware, marked MicrOBU: in the sources: - Reception unchanged for the app. vanetza-idf drops what it cannot verify (unsigned traffic, every RSU), so each captured frame also goes through the previous gn_unwrap.c and reaches the phone as link opcode V2X_RX (0x85), whose body is the old SERIAL_MSG_V2X_RX payload. - Unsigned transmission still possible, with the previous geonet.c header; the phone chooses per message. - Console on UART0 (CH343 port); the native USB port carries only link frames. - BLE advertising pauses while the USB link is in use: BLE and ITS-G5 share one RF front end. - NVS 80 KB (app at 0x20000). At 24 KB, with Wi-Fi settings the previous firmware left behind, the BLE bond could not be stored and the phone had to pair on every connection. - Bench fixes: the radio queue is drained before the first PoTi (no RX and ~177 queue drops before); the station loop waited pdMS_TO_TICKS(5) = 0 ticks at 100 Hz and starved the idle task; the 2.4 KB RX capture buffer is off the Wi-Fi task stack; BLE notifications longer than the MTU are dropped instead of cut short, MTU 517; serial writes are skipped with no USB host. - Manual country policy and TX-power read-back from the previous radio setup; logs for BLE encryption changes and the number of stored bonds. Verified on the bench board (COM3) with the phone over USB and BLE: CAM and VAM, signed and unsigned, go out; reception of the sim car and the RSU's CAM/SPATEM/MAPEM continues; the board survives app restarts and reconnects. See docs/06-signed-its-vam-ble.md.
This commit is contained in:
@@ -0,0 +1,175 @@
|
||||
// Sourced from OpenTrafficMap's its-g5-receiver-firmware_txenabled, main/tx_custom.c
|
||||
// (https://codeberg.org/opentrafficmap/its-g5-receiver-firmware_txenabled, pinned commit
|
||||
// 674e34128279235ba34c9f8d778f43cf1d075397, no stated license). Reproduced here as generated by
|
||||
// implementation/external/vanetza-idf/ports/esp_idf/radio_c5.cmake (which hashes the upstream file
|
||||
// so this copy cannot silently drift) and checked in for readability. The struct layouts
|
||||
// (x_eb_txdesc_t, x_middle_data_t, x_ebuf_t) and the bit-level manipulation of esp_wifi's private
|
||||
// tx descriptor are OTM's own reverse engineering of the closed esp_wifi/libphy internals -- not
|
||||
// documented or supported by Espressif, and not verified independently by this project. The one
|
||||
// deliberate change from upstream is `result = ieee80211_post_hmac_tx(eb);` below: upstream
|
||||
// discards that return value, but ESP_OK from this function means the driver accepted the frame
|
||||
// for submission, not that it was independently observed on air (see
|
||||
// experiments/evidence/c5-radio-characterization/phy-internals-investigation.md).
|
||||
#include "esp_private/wifi_os_adapter.h"
|
||||
#include "esp_wifi.h"
|
||||
#include "otm_tx_custom.h"
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
|
||||
esp_err_t ieee80211_raw_frame_sanity_check(wifi_interface_t ifx, const void *buffer, int32_t len, bool en_sys_seq);
|
||||
esp_err_t ieee80211_post_hmac_tx(void *ebuf);
|
||||
void *ic_ebuf_alloc(const void *packet, uint32_t unknown, uint32_t len);
|
||||
void *ic_get_default_sched(void);
|
||||
|
||||
extern wifi_osi_funcs_t *g_osi_funcs_p;
|
||||
extern void *g_wifi_global_lock;
|
||||
|
||||
typedef struct x_eb_txdesc
|
||||
{
|
||||
uint32_t flags;
|
||||
uint32_t field_4;
|
||||
uint32_t field_8;
|
||||
uint8_t rate;
|
||||
uint8_t field_d;
|
||||
uint8_t field_e;
|
||||
uint8_t field_f;
|
||||
uint32_t field_10;
|
||||
uint32_t field_14;
|
||||
uint32_t timestamp;
|
||||
void* sched;
|
||||
uint32_t field_20;
|
||||
uint32_t field_24;
|
||||
uint32_t field_28;
|
||||
union {
|
||||
uint32_t field_2c_32;
|
||||
struct {
|
||||
uint8_t field_2c;
|
||||
uint8_t field_2d;
|
||||
uint8_t field_2e;
|
||||
uint8_t field_2f;
|
||||
};
|
||||
};
|
||||
union {
|
||||
uint32_t field_30_32;
|
||||
struct {
|
||||
uint8_t field_30;
|
||||
uint8_t field_31;
|
||||
uint8_t field_32;
|
||||
uint8_t field_33;
|
||||
};
|
||||
};
|
||||
uint32_t field_34;
|
||||
uint32_t field_38;
|
||||
uint32_t field_3c;
|
||||
uint32_t field_40;
|
||||
uint32_t field_44;
|
||||
} x_eb_txdesc_t;
|
||||
_Static_assert(sizeof(x_eb_txdesc_t) == 0x48, "eb_txdesc size");
|
||||
|
||||
typedef struct x_middle_data
|
||||
{
|
||||
uint32_t field_40;
|
||||
uint8_t* buf;
|
||||
uint32_t field_48;
|
||||
uint32_t field_4c;
|
||||
} x_middle_data_t;
|
||||
_Static_assert(sizeof(x_middle_data_t) == 0x10, "middle_data size");
|
||||
|
||||
typedef struct x_ebuf
|
||||
{
|
||||
uint32_t field_0;
|
||||
x_middle_data_t* ds_head;
|
||||
x_middle_data_t* ds_tail;
|
||||
uint16_t field_c;
|
||||
uint16_t field_e;
|
||||
uint32_t extra_data_start;
|
||||
uint16_t header_length;
|
||||
uint32_t data_length;
|
||||
uint16_t field_1c;
|
||||
uint8_t alloc_type;
|
||||
uint8_t field_1f;
|
||||
uint32_t field_20;
|
||||
uint8_t field_24;
|
||||
uint8_t field_25;
|
||||
uint8_t field_26;
|
||||
uint8_t field_27;
|
||||
uint32_t field_28;
|
||||
uint8_t field_2c;
|
||||
uint32_t field_30;
|
||||
uint32_t next_free;
|
||||
x_eb_txdesc_t* txdesc;
|
||||
uint16_t field_3c;
|
||||
uint8_t field_3e;
|
||||
uint8_t field_3f;
|
||||
} x_ebuf_t;
|
||||
_Static_assert(sizeof(x_ebuf_t) == 0x40, "ebuf size");
|
||||
_Static_assert(offsetof(x_ebuf_t, txdesc) == 0x38, "ebuf txdesc offset");
|
||||
_Static_assert(offsetof(x_eb_txdesc_t, rate) == 0x0c, "txdesc rate offset");
|
||||
|
||||
esp_err_t esp_wifi_80211_tx_custom(wifi_interface_t ifx, const void *buffer, int32_t len, bool en_sys_seq, wifi_tx_rate_config_t *tx_rate_config, wifi_band_t band, wifi_bandwidth_t bw)
|
||||
{
|
||||
esp_err_t result = 0;
|
||||
|
||||
if (!result)
|
||||
{
|
||||
g_osi_funcs_p->_mutex_lock(g_wifi_global_lock);
|
||||
x_ebuf_t* eb = ic_ebuf_alloc(buffer, 1, len);
|
||||
|
||||
if (eb)
|
||||
{
|
||||
eb->data_length = 0;
|
||||
x_eb_txdesc_t *txdesc_1 = eb->txdesc;
|
||||
eb->header_length = len;
|
||||
txdesc_1->flags |= 0x4000;
|
||||
txdesc_1->sched = ic_get_default_sched();
|
||||
wifi_phy_rate_t rate = tx_rate_config->rate;
|
||||
x_eb_txdesc_t *txdesc = eb->txdesc;
|
||||
|
||||
if (rate)
|
||||
txdesc->rate = (char)rate;
|
||||
else if (band != WIFI_BAND_5G)
|
||||
txdesc->rate = 0;
|
||||
else
|
||||
txdesc->rate = (char)WIFI_PHY_RATE_6M;
|
||||
|
||||
wifi_phy_mode_t phymode = tx_rate_config->phymode;
|
||||
|
||||
if (phymode == WIFI_PHY_MODE_HE20)
|
||||
{
|
||||
txdesc->flags |= 0x80000000;
|
||||
txdesc->field_2f =
|
||||
(char)((((uint32_t)tx_rate_config->ersu + 6) & 0xf) << 3)
|
||||
| (txdesc->field_2f & 0x87);
|
||||
|
||||
if ((uint32_t)tx_rate_config->dcm)
|
||||
txdesc->field_31 |= 0x80;
|
||||
}
|
||||
else if (phymode == WIFI_PHY_MODE_VHT20)
|
||||
txdesc->flags |= 0x1000000;
|
||||
|
||||
// OTM's own comment on this line upstream: "No idea if this is correct, but this is
|
||||
// what the original code does...". This project always passes WIFI_BW20 (see
|
||||
// c5_radio.cpp), so bw_is_bw40 is always 0 here; see the investigation doc above for
|
||||
// what is and isn't verified about ITS-G5's 10 MHz channel width on this path.
|
||||
uint32_t bw_is_bw40 = bw == WIFI_BW40;
|
||||
txdesc->field_8 = (bw_is_bw40 << 0xf) | (txdesc->field_8 & 0xffff7fff);
|
||||
|
||||
if (en_sys_seq)
|
||||
txdesc->flags |= 1;
|
||||
|
||||
txdesc->field_10 =
|
||||
(txdesc->field_10 & 0xfff3ffff) | ((ifx & WIFI_IF_MAX) << 0x12);
|
||||
txdesc->field_14 = 0x100;
|
||||
|
||||
result = ieee80211_post_hmac_tx(eb);
|
||||
g_osi_funcs_p->_mutex_unlock(g_wifi_global_lock);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = ESP_ERR_NO_MEM;
|
||||
g_osi_funcs_p->_mutex_unlock(g_wifi_global_lock);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
Reference in New Issue
Block a user