Files
utun2/lib/ble/ble_transport.c
T

421 lines
24 KiB
C

#include "ble_backend.h"
#include "../u_async.h"
#include "../mem.h"
#include "../debug_config.h"
#include <string.h>
#define CAT DEBUG_CATEGORY_DISCOVERY
#define HELLO 1
#define ACCEPT 2
#define FRAME 3
#define HEADER 7
#define DEADLINE_US 15000000ULL
#define STATS_US 5000000ULL
struct ble_stats {
uint64_t since, tx_bytes, rx_bytes, wait_us, max_wait_us;
unsigned tx_frames, rx_frames, fragments, dropped, missing;
};
struct ble_channel_state {
struct ll_queue *tx, *rx;
struct ll_entry* current;
uint64_t rx_deadline;
uint16_t sequence, offset, rx_sequence, rx_size, rx_offset, dropped_sequence, rx_next;
int blocked, dropping, rx_seen;
uint8_t assembly[BLE_FRAME_MAX];
};
struct ble_connection {
ble_link_id id;
uint64_t peer, deadline, submitted;
struct ble_stats stats;
struct ble_endpoint endpoint;
struct ble_channel_state channels[BLE_CHANNEL_COUNT];
unsigned int capacity;
uint16_t pending_bytes;
enum ble_channel pending_channel, next_channel;
int outgoing, opened, ready, pending, control_pending;
uint8_t control[17];
};
struct ble_transport {
struct UASYNC* ua;
struct ble_config config;
char adapter[64];
struct ble_backend* backend;
struct ble_connection links[BLE_LINKS_MAX];
ble_link_id sequence;
void* timer;
int started, ready, reject_incoming;
};
static uint16_t read16(const uint8_t* p) { return (uint16_t)((p[0] << 8) | p[1]); }
static void write16(uint8_t* p, uint16_t v) { p[0] = v >> 8; p[1] = v; }
static uint64_t read64(const uint8_t* p) {
uint64_t v = 0;
for (int i = 0; i < 8; i++) v = (v << 8) | p[i];
return v;
}
static void write64(uint8_t* p, uint64_t v) { for (int i = 7; i >= 0; i--) { p[i] = v; v >>= 8; } }
/* Диагностический отпечаток зашифрованного кадра для сопоставления журналов двух узлов. */
static uint32_t frame_tag(const uint8_t* data, size_t len) {
uint32_t tag = 2166136261u;
for (size_t i = 0; i < len; i++) tag = (tag ^ data[i]) * 16777619u;
return tag;
}
static void release_entry(struct ll_entry* e) { if (e) { queue_dgram_free(e); queue_entry_free(e); } }
static struct ble_connection* lookup(struct ble_transport* t, ble_link_id id) {
if (t && id) for (int i = 0; i < BLE_LINKS_MAX; i++) if (t->links[i].id == id) return &t->links[i];
return NULL;
}
/* Сводка раз в пять секунд и при закрытии: payload кадров, очереди и локальное ожидание GATT. */
static void log_stats(struct ble_connection* c, uint64_t now) {
struct ble_stats* s = &c->stats;
uint64_t elapsed = now > s->since ? now - s->since : 1;
if (s->tx_frames || s->rx_frames || s->fragments || s->dropped || c->pending) {
DEBUG_DEBUG(CAT, "BLE traffic link=%llu outgoing=%d elapsed_ms=%llu TX_Bps=%llu RX_Bps=%llu TX_frames=%u RX_frames=%u "
"fragments=%u drops=%u missing=%u wait_avg_us=%llu wait_max_us=%llu DATA_queue=%d INVITE_queue=%d pending_ms=%llu",
(unsigned long long)c->id, c->outgoing, (unsigned long long)(elapsed / 1000),
(unsigned long long)(s->tx_bytes * 1000000 / elapsed), (unsigned long long)(s->rx_bytes * 1000000 / elapsed),
s->tx_frames, s->rx_frames, s->fragments, s->dropped, s->missing,
(unsigned long long)(s->fragments ? s->wait_us / s->fragments : 0), (unsigned long long)s->max_wait_us,
queue_entry_count(c->channels[BLE_CHANNEL_DATA].tx) + !!c->channels[BLE_CHANNEL_DATA].current,
queue_entry_count(c->channels[BLE_CHANNEL_INVITE].tx) + !!c->channels[BLE_CHANNEL_INVITE].current,
(unsigned long long)(c->pending ? (now - c->submitted) / 1000 : 0));
}
memset(s, 0, sizeof(*s)); s->since = now;
}
int ble_get_outgoing(struct ble_transport* t, ble_link_id id, int* outgoing) {
struct ble_connection* c = lookup(t, id);
if (!c || !outgoing) { DEBUG_ERROR(CAT, "BLE direction: invalid link=%llu/output", (unsigned long long)id); return -1; }
*outgoing = c->outgoing;
return 0;
}
static void close_link(struct ble_transport* t, struct ble_connection* c, const char* reason) {
ble_link_id id = c->id;
uint64_t peer = c->peer;
log_stats(c, get_time_us());
DEBUG_INFO(CAT, "BLE closed link=%llu peer=%016llx reason=%s", (unsigned long long)id, (unsigned long long)peer, reason);
if (t->backend) ble_backend_close(t->backend, id);
for (int i = 0; i < BLE_CHANNEL_COUNT; i++) {
struct ble_channel_state* ch = &c->channels[i];
release_entry(ch->current);
queue_clear(ch->tx); queue_free(ch->tx);
queue_clear(ch->rx); queue_free(ch->rx);
}
memset(c, 0, sizeof(*c));
if (t->config.callbacks.connection) t->config.callbacks.connection(t->config.arg, id, peer, BLE_CLOSED, reason);
}
static struct ble_connection* new_link(struct ble_transport* t, const struct ble_endpoint* ep, int outgoing, uint64_t peer) {
if (!t || !t->started || !ep || !memchr(ep->id, 0, sizeof(ep->id)) || !ep->id[0]) {
DEBUG_ERROR(CAT, "BLE invalid connection request"); return NULL;
}
for (int i = 0; i < BLE_LINKS_MAX; i++) if (t->links[i].id && !strcmp(t->links[i].endpoint.id, ep->id)) {
DEBUG_WARN(CAT, "BLE duplicate connection endpoint=%s", ep->id); return NULL;
}
for (int i = 0; i < BLE_LINKS_MAX; i++) if (!t->links[i].id) {
struct ble_connection* c = &t->links[i];
int allocated = 1;
for (int ch = 0; ch < BLE_CHANNEL_COUNT; ch++) {
c->channels[ch].tx = queue_new(t->ua, 0, 0, 0, "ble_tx");
c->channels[ch].rx = queue_new(t->ua, 0, 0, 0, "ble_rx");
if (!c->channels[ch].tx || !c->channels[ch].rx) allocated = 0;
}
if (!allocated) {
DEBUG_ERROR(CAT, "BLE queue allocation failed");
for (int ch = 0; ch < BLE_CHANNEL_COUNT; ch++) { queue_free(c->channels[ch].tx); queue_free(c->channels[ch].rx); }
memset(c, 0, sizeof(*c)); return NULL;
}
c->id = ++t->sequence; c->peer = peer; c->endpoint = *ep; c->outgoing = outgoing;
c->capacity = 20; c->stats.since = get_time_us(); c->deadline = c->stats.since + DEADLINE_US;
DEBUG_INFO(CAT, "BLE connecting link=%llu endpoint=%s outgoing=%d", (unsigned long long)c->id, ep->id, outgoing);
if (t->config.callbacks.connection) t->config.callbacks.connection(t->config.arg, c->id, peer, BLE_STARTING, "connecting");
return c;
}
DEBUG_WARN(CAT, "BLE connection limit=%d", BLE_LINKS_MAX); return NULL;
}
static void pump(struct ble_transport* t, struct ble_connection* c) {
if (!c->opened || c->pending) return;
uint8_t value[BLE_VALUE_MAX];
size_t len;
c->pending_bytes = 0;
c->pending_channel = BLE_CHANNEL_DATA;
if (c->control_pending) { memcpy(value, c->control, 17); len = 17; }
else {
if (!c->ready) return;
struct ble_channel_state* ch = NULL;
for (int i = 0; i < BLE_CHANNEL_COUNT; i++) {
enum ble_channel channel = (c->next_channel + i) % BLE_CHANNEL_COUNT;
struct ble_channel_state* candidate = &c->channels[channel];
if (!candidate->current) {
candidate->current = queue_data_get(candidate->tx); candidate->offset = 0;
if (candidate->current) ++candidate->sequence;
}
if (candidate->current) { ch = candidate; c->pending_channel = channel; break; }
}
if (!ch) return;
c->next_channel = (c->pending_channel + 1) % BLE_CHANNEL_COUNT;
size_t n = ch->current->len - ch->offset;
if (n > c->capacity - HEADER) n = c->capacity - HEADER;
value[0] = FRAME; write16(value + 1, ch->sequence); write16(value + 3, ch->current->len); write16(value + 5, ch->offset);
memcpy(value + HEADER, ch->current->dgram + ch->offset, n); len = HEADER + n; c->pending_bytes = n;
}
int result = ble_backend_send(t->backend, c->id, c->pending_channel, value, len);
if (result == BLE_OK) { c->pending = 1; c->submitted = get_time_us(); c->deadline = c->submitted + DEADLINE_US; }
else { DEBUG_ERROR(CAT, "BLE backend send failed link=%llu result=%d", (unsigned long long)c->id, result);
close_link(t, c, "backend send failed"); }
}
static void tick(void* arg) {
struct ble_transport* t = arg; t->timer = NULL;
uint64_t now = get_time_us();
for (int i = 0; i < BLE_LINKS_MAX; i++) {
struct ble_connection* c = &t->links[i];
if (!c->id) continue;
if (now - c->stats.since >= STATS_US) log_stats(c, now);
if ((!c->ready || c->pending) && now >= c->deadline) {
DEBUG_WARN(CAT, "BLE handshake/send timeout link=%llu pending=%d", (unsigned long long)c->id, c->pending);
close_link(t, c, "operation timeout"); continue;
}
for (int channel = 0; channel < BLE_CHANNEL_COUNT; channel++) {
struct ble_channel_state* ch = &c->channels[channel];
if (ch->rx_size && now >= ch->rx_deadline) {
DEBUG_WARN(CAT, "BLE frame timeout link=%llu received=%u expected=%u",
(unsigned long long)c->id, ch->rx_offset, ch->rx_size);
ch->dropping = 1; ch->dropped_sequence = ch->rx_sequence;
c->stats.dropped++;
ch->rx_size = ch->rx_offset = 0;
}
}
}
t->timer = uasync_set_timeout(t->ua, 1000, t, tick, "ble_tick");
if (!t->timer) { DEBUG_ERROR(CAT, "BLE timer allocation failed"); ble_backend_status(t, 0, "timer allocation failed"); }
}
struct ble_transport* ble_create(struct UASYNC* ua, const struct ble_config* config) {
if (!ua || !config || !config->node_id || config->tx_limit > BLE_QUEUE_MAX ||
(config->adapter && strlen(config->adapter) >= 64)) {
DEBUG_ERROR(CAT, "BLE create invalid configuration"); return NULL;
}
struct ble_transport* t = u_calloc(1, sizeof(*t));
if (!t) { DEBUG_ERROR(CAT, "BLE create allocation failed"); return NULL; }
t->ua = ua; t->config = *config;
if (!t->config.tx_limit) t->config.tx_limit = BLE_QUEUE_MAX;
if (config->adapter) { strcpy(t->adapter, config->adapter); t->config.adapter = t->adapter; }
return t;
}
int ble_start(struct ble_transport* t) {
if (!t || t->started) { DEBUG_ERROR(CAT, "BLE invalid/repeated start"); return BLE_ERROR; }
t->started = 1;
t->backend = ble_backend_create(t, t->ua, &t->config);
if (!t->backend) { t->started = 0; DEBUG_ERROR(CAT, "BLE backend creation failed"); return BLE_ERROR; }
t->timer = uasync_set_timeout(t->ua, 1000, t, tick, "ble_tick");
if (!t->timer) { DEBUG_ERROR(CAT, "BLE timer allocation failed"); ble_stop(t); return BLE_ERROR; }
if (t->config.callbacks.status) t->config.callbacks.status(t->config.arg, BLE_STARTING, "registering BLE service");
return BLE_OK;
}
void ble_stop(struct ble_transport* t) {
if (!t || !t->started) return;
t->started = t->ready = 0;
if (t->timer) { uasync_cancel_timeout(t->ua, t->timer); t->timer = NULL; }
for (int i = 0; i < BLE_LINKS_MAX; i++) if (t->links[i].id) close_link(t, &t->links[i], "transport stopped");
ble_backend_destroy(t->backend); t->backend = NULL;
if (t->config.callbacks.status) t->config.callbacks.status(t->config.arg, BLE_CLOSED, "stopped");
}
void ble_set_incoming(struct ble_transport* t, int allowed) {
if (!t) { DEBUG_ERROR(CAT, "BLE incoming policy: null transport"); return; }
t->reject_incoming = !allowed;
DEBUG_INFO(CAT, "BLE incoming allowed=%d", !!allowed);
if (!allowed) for (int i = 0; i < BLE_LINKS_MAX; i++)
if (t->links[i].id && !t->links[i].outgoing) close_link(t, &t->links[i], "incoming disabled");
}
void ble_destroy(struct ble_transport* t) { if (t) { ble_stop(t); u_free(t); } }
int ble_advertise(struct ble_transport* t, const void* data, size_t len) {
if (!t || !t->ready) { DEBUG_WARN(CAT, "BLE advertise before ready"); return BLE_NOT_READY; }
if (len > BLE_BROADCAST_MAX || (len && !data)) { DEBUG_ERROR(CAT, "BLE invalid advertisement length=%zu", len); return BLE_ERROR; }
return ble_backend_advertise(t->backend, data, len);
}
ble_link_id ble_connect(struct ble_transport* t, const struct ble_endpoint* ep, uint64_t peer) {
if (!t || !t->ready) { DEBUG_WARN(CAT, "BLE connect before ready"); return 0; }
struct ble_connection* c = new_link(t, ep, 1, peer);
if (!c) return 0;
ble_link_id id = c->id;
if (ble_backend_connect(t->backend, id, ep) != BLE_OK) { close_link(t, c, "connect rejected"); return 0; }
return id;
}
void ble_disconnect(struct ble_transport* t, ble_link_id id) {
struct ble_connection* c = lookup(t, id);
if (c) close_link(t, c, "API disconnect");
else DEBUG_WARN(CAT, "BLE disconnect unknown link=%llu", (unsigned long long)id);
}
int ble_channel_tx_ready(struct ble_transport* t, ble_link_id id, enum ble_channel channel) {
if ((unsigned)channel >= BLE_CHANNEL_COUNT) { DEBUG_ERROR(CAT, "BLE invalid channel=%d", channel); return 0; }
struct ble_connection* c = lookup(t, id);
struct ble_channel_state* ch = c ? &c->channels[channel] : NULL;
return c && c->ready && (unsigned)queue_entry_count(ch->tx) + !!ch->current < (channel == BLE_CHANNEL_DATA ? t->config.tx_limit : 1);
}
int ble_channel_tx_put(struct ble_transport* t, ble_link_id id, enum ble_channel channel, struct ll_entry* e) {
if ((unsigned)channel >= BLE_CHANNEL_COUNT) { DEBUG_ERROR(CAT, "BLE invalid channel=%d", channel); return BLE_ERROR; }
struct ble_connection* c = lookup(t, id);
struct ble_channel_state* ch = c ? &c->channels[channel] : NULL;
if (!c || !c->ready) { DEBUG_WARN(CAT, "BLE TX link not ready=%llu", (unsigned long long)id); return BLE_NOT_READY; }
if (!e || e->owner || e->next || e->prev || !e->dgram || !e->len || e->len > BLE_FRAME_MAX || e->len > e->memlen) {
DEBUG_ERROR(CAT, "BLE invalid TX frame link=%llu", (unsigned long long)id); return BLE_ERROR;
}
if (!ble_channel_tx_ready(t, id, channel)) {
if (!ch->blocked) DEBUG_TRACE(CAT, "BLE TX paused link=%llu packets=%u", (unsigned long long)id,
(channel == BLE_CHANNEL_DATA ? t->config.tx_limit : 1));
ch->blocked = 1; return BLE_WOULD_BLOCK;
}
/* Без лимита/индекса/consumer put не имеет отказа после проверок и не вызывает чужой callback. */
if (queue_data_put(ch->tx, e) != 0) { DEBUG_ERROR(CAT, "BLE TX queue invariant failed"); return BLE_ERROR; }
if (!ble_channel_tx_ready(t, id, channel)) ch->blocked = 1;
pump(t, c); return BLE_OK;
}
struct ll_entry* ble_channel_rx_get(struct ble_transport* t, ble_link_id id, enum ble_channel channel) {
if ((unsigned)channel >= BLE_CHANNEL_COUNT) { DEBUG_ERROR(CAT, "BLE invalid channel=%d", channel); return 0; }
struct ble_connection* c = lookup(t, id);
struct ble_channel_state* ch = c ? &c->channels[channel] : NULL;
return c ? queue_data_get(ch->rx) : NULL;
}
int ble_tx_ready(struct ble_transport* t, ble_link_id id) { return ble_channel_tx_ready(t, id, BLE_CHANNEL_DATA); }
int ble_tx_put(struct ble_transport* t, ble_link_id id, struct ll_entry* e) { return ble_channel_tx_put(t, id, BLE_CHANNEL_DATA, e); }
struct ll_entry* ble_rx_get(struct ble_transport* t, ble_link_id id) { return ble_channel_rx_get(t, id, BLE_CHANNEL_DATA); }
ble_link_id ble_backend_incoming(struct ble_transport* t, const struct ble_endpoint* ep) {
if (t->reject_incoming) { DEBUG_INFO(CAT, "BLE incoming rejected by policy"); return 0; }
struct ble_connection* c = new_link(t, ep, 0, 0); return c ? c->id : 0;
}
void ble_backend_status(struct ble_transport* t, int ready, const char* reason) {
t->ready = ready;
if (!ready) {
DEBUG_ERROR(CAT, "BLE radio failed: %s", reason);
for (int i = 0; i < BLE_LINKS_MAX; i++) if (t->links[i].id) close_link(t, &t->links[i], reason);
} else DEBUG_INFO(CAT, "BLE radio ready node=%016llx", (unsigned long long)t->config.node_id);
if (t->config.callbacks.status) t->config.callbacks.status(t->config.arg, ready ? BLE_READY : BLE_CLOSED, reason);
}
void ble_backend_broadcast(struct ble_transport* t, const struct ble_endpoint* ep, const uint8_t* data, size_t len, int rssi) {
if (len > BLE_BROADCAST_MAX) { DEBUG_WARN(CAT, "BLE oversized broadcast bytes=%zu", len); return; }
if (t->config.callbacks.broadcast) t->config.callbacks.broadcast(t->config.arg, ep, data, len, rssi);
}
void ble_backend_advertising(struct ble_transport* t, enum ble_state state, const char* reason) {
DEBUG_INFO(CAT, "BLE advertising state=%d reason=%s", state, reason);
if (t->config.callbacks.advertising) t->config.callbacks.advertising(t->config.arg, state, reason);
}
void ble_backend_opened(struct ble_transport* t, ble_link_id id, unsigned int mtu) {
struct ble_connection* c = lookup(t, id);
if (!c) return;
if (mtu < 23) { DEBUG_ERROR(CAT, "BLE invalid MTU=%u", mtu); close_link(t, c, "invalid MTU"); return; }
c->capacity = mtu - 3; if (c->capacity > BLE_VALUE_MAX) c->capacity = BLE_VALUE_MAX;
c->opened = 1;
DEBUG_INFO(CAT, "BLE GATT open link=%llu mtu=%u fragment_payload=%u outgoing=%d DATA=without_response/notify INVITE=request/indicate tx_limit=%u",
(unsigned long long)id, mtu, c->capacity - HEADER, c->outgoing, t->config.tx_limit);
if (c->outgoing) {
c->control[0] = HELLO; write64(c->control + 1, t->config.node_id); write64(c->control + 9, c->peer);
c->control_pending = 1;
}
pump(t, c);
}
void ble_backend_received(struct ble_transport* t, ble_link_id id, enum ble_channel channel, const uint8_t* data, size_t len) {
struct ble_connection* c = lookup(t, id);
if (!c) return;
if ((unsigned)channel >= BLE_CHANNEL_COUNT) { DEBUG_ERROR(CAT, "BLE invalid RX channel=%d", channel); return; }
struct ble_channel_state* ch = &c->channels[channel];
if (channel == BLE_CHANNEL_DATA && len == 17 && !c->ready && ((data[0] == HELLO && !c->outgoing) || (data[0] == ACCEPT && c->outgoing))) {
uint64_t peer = read64(data + 1), target = read64(data + 9);
if (!peer || peer == t->config.node_id || (target && target != t->config.node_id) || (c->peer && peer != c->peer)) {
DEBUG_WARN(CAT, "BLE invalid HELLO identity link=%llu", (unsigned long long)id); close_link(t, c, "invalid identity"); return;
}
if (t->config.callbacks.validate && !t->config.callbacks.validate(t->config.arg, id, peer)) {
DEBUG_WARN(CAT, "BLE validation rejected link=%llu", (unsigned long long)id); close_link(t, c, "validation rejected"); return;
}
c->peer = peer; c->ready = 1;
if (!c->outgoing) {
c->control[0] = ACCEPT; write64(c->control + 1, t->config.node_id); write64(c->control + 9, peer); c->control_pending = 1;
}
DEBUG_INFO(CAT, "BLE session ready link=%llu peer=%016llx", (unsigned long long)id, (unsigned long long)peer);
if (t->config.callbacks.connection) t->config.callbacks.connection(t->config.arg, id, peer, BLE_READY, "HELLO accepted");
pump(t, c); return;
}
if (!c->ready || len <= HEADER || len > c->capacity || data[0] != FRAME) {
DEBUG_WARN(CAT, "BLE invalid value link=%llu channel=%d bytes=%zu capacity=%u ready=%d type=%u",
(unsigned long long)id, channel, len, c->capacity, c->ready, len ? data[0] : 0);
c->stats.dropped++;
ch->rx_size = ch->rx_offset = 0; return;
}
uint16_t seq = read16(data + 1), total = read16(data + 3), offset = read16(data + 5);
size_t n = len - HEADER;
if (offset && ch->dropping && ch->dropped_sequence == seq) {
DEBUG_TRACE(CAT, "BLE discarded frame tail link=%llu seq=%u offset=%u", (unsigned long long)id, seq, offset); return;
}
if (!offset) ch->dropping = 0;
if (!total || total > BLE_FRAME_MAX || offset > total || n > (size_t)(total - offset)) goto invalid;
if (!offset) {
uint16_t expected = ch->rx_seen ? ch->rx_next : 1;
if (seq != expected) {
uint16_t gap = (uint16_t)(seq - expected);
DEBUG_WARN(CAT, "BLE RX sequence gap link=%llu channel=%d expected=%u received=%u missing=%u",
(unsigned long long)id, channel, expected, seq, gap < 0x8000 ? gap : 0);
if (gap < 0x8000) c->stats.missing += gap;
}
ch->rx_seen = 1; ch->rx_next = (uint16_t)(seq + 1);
if (ch->rx_size) {
DEBUG_WARN(CAT, "BLE incomplete frame replaced link=%llu channel=%d seq=%u received=%u total=%u next=%u",
(unsigned long long)id, channel, ch->rx_sequence, ch->rx_offset, ch->rx_size, seq);
c->stats.dropped++;
}
ch->rx_sequence = seq; ch->rx_size = total; ch->rx_offset = 0; ch->rx_deadline = get_time_us() + DEADLINE_US;
}
if (ch->rx_size != total || ch->rx_sequence != seq || ch->rx_offset != offset) goto invalid;
memcpy(ch->assembly + offset, data + HEADER, n); ch->rx_offset += n;
if (ch->rx_offset != total) return;
ch->rx_size = ch->rx_offset = 0;
if (queue_entry_count(ch->rx) >= BLE_QUEUE_MAX) {
DEBUG_WARN(CAT, "BLE RX full: frame dropped link=%llu channel=%d", (unsigned long long)id, channel);
c->stats.dropped++; return;
}
struct ll_entry* entry = ll_alloc_lldgram(total);
if (!entry) { DEBUG_ERROR(CAT, "BLE RX allocation failed bytes=%u", total); c->stats.dropped++; return; }
memcpy(entry->dgram, ch->assembly, total); entry->len = total;
if (queue_data_put(ch->rx, entry) != 0) { DEBUG_ERROR(CAT, "BLE RX queue invariant failed"); return; }
c->stats.rx_bytes += total; c->stats.rx_frames++;
DEBUG_TRACE(DEBUG_CATEGORY_DEBUG, "BLE wire RX link=%llu channel=%d seq=%u bytes=%u tag=%08x",
(unsigned long long)id, channel, seq, total, frame_tag(entry->dgram, entry->len));
DEBUG_TRACE(CAT, "BLE RX frame link=%llu channel=%d seq=%u bytes=%u queued=%d", (unsigned long long)id, channel, seq, total, queue_entry_count(ch->rx));
void (*received)(void*, ble_link_id) = channel == BLE_CHANNEL_DATA ? t->config.callbacks.received : t->config.callbacks.invite_received;
if (received) received(t->config.arg, id);
return;
invalid:
c->stats.dropped++;
DEBUG_WARN(CAT, "BLE fragment dropped link=%llu channel=%d seq=%u offset=%u expected=%u total=%u bytes=%zu",
(unsigned long long)id, channel, seq, offset, ch->rx_offset, total, n);
ch->rx_size = ch->rx_offset = 0;
ch->dropping = 1; ch->dropped_sequence = seq;
}
void ble_backend_sent(struct ble_transport* t, ble_link_id id, int success) {
struct ble_connection* c = lookup(t, id);
if (!c) return;
if (!success || !c->pending) { DEBUG_WARN(CAT, "BLE send completion failed link=%llu success=%d pending=%d channel=%d bytes=%u",
(unsigned long long)id, success, c->pending, c->pending_channel, c->pending_bytes);
close_link(t, c, "send failed"); return; }
enum ble_channel channel = c->pending_channel;
uint64_t waited = get_time_us() - c->submitted;
c->stats.fragments++; c->stats.wait_us += waited;
if (waited > c->stats.max_wait_us) c->stats.max_wait_us = waited;
struct ble_channel_state* ch = &c->channels[channel];
c->pending = 0;
if (!c->pending_bytes) c->control_pending = 0;
else if (ch->current) {
ch->offset += c->pending_bytes;
if (ch->offset == ch->current->len) {
c->stats.tx_bytes += ch->current->len; c->stats.tx_frames++;
DEBUG_TRACE(DEBUG_CATEGORY_DEBUG, "BLE wire TX link=%llu channel=%d seq=%u bytes=%u tag=%08x",
(unsigned long long)id, channel, ch->sequence, ch->current->len,
frame_tag(ch->current->dgram, ch->current->len));
DEBUG_TRACE(CAT, "BLE TX frame link=%llu channel=%d seq=%u bytes=%u", (unsigned long long)id, channel, ch->sequence, ch->current->len);
release_entry(ch->current); ch->current = NULL;
}
}
if (ch->blocked && ble_channel_tx_ready(t, id, channel)) {
ch->blocked = 0; DEBUG_TRACE(CAT, "BLE TX resumed link=%llu", (unsigned long long)id);
void (*writable)(void*, ble_link_id) = channel == BLE_CHANNEL_DATA ? t->config.callbacks.writable : t->config.callbacks.invite_writable;
if (writable) writable(t->config.arg, id);
}
pump(t, c);
}
void ble_backend_closed(struct ble_transport* t, ble_link_id id, const char* reason) {
struct ble_connection* c = lookup(t, id); if (c) close_link(t, c, reason);
}