- Firmware: rewrite obu-firmware TX loop to be serial-driven (no on-chip timer), add promiscuous RX + GeoNetworking/BTP unwrap (gn_unwrap.c), add binary UART framing to the phone (serial_link.c/.h). Drop local cam_encode() - CAM is now built on the phone. - Kotlin: byte-exact UPER CAM encoder/decoder ported from cam.c (BitWriter/BitReader/CamUperCodec), matching SerialFrame codec, real UsbSerialTransport (usb-serial-for-android), CamTransmitLoop (1Hz base rate, event/geofence boost, ESP32-C5-only), wired into CamUseCaseRepository for RX and TripRecordingService for TX. - Add V2X message retention: persist all CAM (own+remote) to Room while recording, drop otherwise (DB v2 -> v3 migration). - Add jitpack repo + usb-serial-for-android dependency. Fixes: UsbSerialTransport now uses SerialInputOutputManager.start()/stop() (this lib version manages its own thread internally) instead of manual Runnable/Thread submission, which didn't compile.
115 lines
4.0 KiB
C
115 lines
4.0 KiB
C
#include "gn_unwrap.h"
|
|
#include <string.h>
|
|
|
|
// Mirrors dot11p.c / geonet.c's constants and layout, in reverse. Keep these two files in sync
|
|
// if either the TX-side frame shape or these constants change.
|
|
#define GN_ETHERTYPE (0x8947)
|
|
#define LLC_SNAP_HEADER_LEN (8)
|
|
#define IEEE80211_HEADER_LEN (24) // non-QoS Data
|
|
#define IEEE80211_QOS_CTRL_LEN (2) // extra field QoS Data frames add
|
|
#define IEEE80211_FC_TYPE_DATA (2)
|
|
#define IEEE80211_FC_QOS_SUBTYPE_BIT (0x08)
|
|
|
|
#define GN_BASIC_HEADER_LEN (4)
|
|
#define GN_COMMON_HEADER_LEN (8)
|
|
#define GN_SHB_EXT_HEADER_LEN (24) // Source Long Position Vector, geonet.c's SHB shape
|
|
#define BTP_B_HEADER_LEN (4)
|
|
|
|
#define GN_HEADER_TYPE_TSB (5) // Topologically-Scoped Broadcast
|
|
#define GN_HEADER_SUBTYPE_SINGLE_HOP (0)
|
|
|
|
#define BTP_DEST_PORT_CAM (2001) // ETSI TS 103 248
|
|
|
|
static const uint8_t s_llc_snap_prefix[6] = {0xAA, 0xAA, 0x03, 0x00, 0x00, 0x00};
|
|
|
|
bool gn_unwrap_cam(const uint8_t *frame, int frame_len,
|
|
const uint8_t **out_cam, int *out_cam_len)
|
|
{
|
|
if (!frame || frame_len < IEEE80211_HEADER_LEN) {
|
|
return false;
|
|
}
|
|
|
|
uint8_t fc0 = frame[0];
|
|
uint8_t fc1 = frame[1];
|
|
uint8_t type = (fc0 >> 2) & 0x03;
|
|
uint8_t subtype = (fc0 >> 4) & 0x0F;
|
|
bool to_ds = fc1 & 0x01;
|
|
bool from_ds = fc1 & 0x02;
|
|
|
|
// Only plain broadcast Data frames, no WDS - matches what dot11p_build_frame ever produces
|
|
// (and what real ITS-G5 hardware sends).
|
|
if (type != IEEE80211_FC_TYPE_DATA || (to_ds && from_ds)) {
|
|
return false;
|
|
}
|
|
|
|
int offset = IEEE80211_HEADER_LEN;
|
|
if (subtype & IEEE80211_FC_QOS_SUBTYPE_BIT) {
|
|
offset += IEEE80211_QOS_CTRL_LEN;
|
|
}
|
|
|
|
if (frame_len < offset + LLC_SNAP_HEADER_LEN) {
|
|
return false;
|
|
}
|
|
if (memcmp(frame + offset, s_llc_snap_prefix, sizeof(s_llc_snap_prefix)) != 0) {
|
|
return false;
|
|
}
|
|
uint16_t ethertype = ((uint16_t)frame[offset + 6] << 8) | frame[offset + 7];
|
|
if (ethertype != GN_ETHERTYPE) {
|
|
return false;
|
|
}
|
|
offset += LLC_SNAP_HEADER_LEN;
|
|
|
|
// ---- GN Basic Header (4 bytes) ---- nothing here we need to validate for our purposes;
|
|
// just skip it. (version/NextHeader in byte0, lifetime in byte2, RHL in byte3.)
|
|
if (frame_len < offset + GN_BASIC_HEADER_LEN) {
|
|
return false;
|
|
}
|
|
offset += GN_BASIC_HEADER_LEN;
|
|
|
|
// ---- GN Common Header (8 bytes) ----
|
|
if (frame_len < offset + GN_COMMON_HEADER_LEN) {
|
|
return false;
|
|
}
|
|
uint8_t next_header = (frame[offset + 0] >> 4) & 0x0F;
|
|
uint8_t header_type = (frame[offset + 1] >> 4) & 0x0F;
|
|
uint8_t header_subtype = frame[offset + 1] & 0x0F;
|
|
if (next_header != 2 /* BTP-B */) {
|
|
return false;
|
|
}
|
|
if (header_type != GN_HEADER_TYPE_TSB || header_subtype != GN_HEADER_SUBTYPE_SINGLE_HOP) {
|
|
// Not a single-hop-broadcast frame - e.g. GeoBroadcast (DENM-style dissemination) or
|
|
// something this project doesn't transmit/expect. Not an error, just not for us yet -
|
|
// see gn_unwrap.h's note on scope.
|
|
return false;
|
|
}
|
|
offset += GN_COMMON_HEADER_LEN;
|
|
|
|
// ---- SHB extended header (24 bytes) ---- skip straight past it, we don't need the
|
|
// sender's claimed position/speed/heading here (the CAM payload has its own, more precise
|
|
// versions of those same fields).
|
|
if (frame_len < offset + GN_SHB_EXT_HEADER_LEN) {
|
|
return false;
|
|
}
|
|
offset += GN_SHB_EXT_HEADER_LEN;
|
|
|
|
// ---- BTP-B header (4 bytes) ----
|
|
if (frame_len < offset + BTP_B_HEADER_LEN) {
|
|
return false;
|
|
}
|
|
uint16_t dest_port = ((uint16_t)frame[offset + 0] << 8) | frame[offset + 1];
|
|
if (dest_port != BTP_DEST_PORT_CAM) {
|
|
return false; // e.g. DENM (2002) - not decoded by this project yet
|
|
}
|
|
offset += BTP_B_HEADER_LEN;
|
|
|
|
// ---- Whatever's left is the CAM UPER payload ----
|
|
int cam_len = frame_len - offset;
|
|
if (cam_len <= 0) {
|
|
return false;
|
|
}
|
|
|
|
*out_cam = frame + offset;
|
|
*out_cam_len = cam_len;
|
|
return true;
|
|
}
|