switch-pico/bluepad32_config/parser/uni_switch2_pairing.c

110 lines
4.1 KiB
C

// SPDX-License-Identifier: Apache-2.0
#include "parser/uni_switch2_pairing.h"
#include <stddef.h>
#include <string.h>
#include <btstack_tlv.h>
#define PAIRING_TAG 0x53325052u // S2PR, separate from BTstack BTD/BTL records.
#define HEADER_SIZE 2u
#define RECORD_SIZE 7u
#define STORE_SIZE (HEADER_SIZE + UNI_SWITCH2_PAIRING_CAPACITY * RECORD_SIZE)
static bool stable_address(uint8_t type, const uint8_t address[6]) {
if (address == NULL || type > 1) return false;
unsigned any = 0;
unsigned not_ff = 0;
for (unsigned i = 0; i < 6; ++i) {
any |= address[i];
not_ff |= address[i] ^ 0xffu;
}
if (any == 0 || not_ff == 0) return false;
if (type == 0) return true;
if ((address[0] & 0xc0u) != 0xc0u) return false;
// The random portion of a static random address cannot be all zero/one.
any = address[0] & 0x3fu;
not_ff = (address[0] & 0x3fu) ^ 0x3fu;
for (unsigned i = 1; i < 6; ++i) {
any |= address[i];
not_ff |= address[i] ^ 0xffu;
}
return any != 0 && not_ff != 0;
}
static bool load(const btstack_tlv_t** tlv, void** context, uint8_t data[STORE_SIZE]) {
btstack_tlv_get_instance(tlv, context);
if (*tlv == NULL || (*tlv)->get_tag == NULL || (*tlv)->store_tag == NULL) return false;
int size = (*tlv)->get_tag(*context, PAIRING_TAG, data, STORE_SIZE);
if (size == 0) {
data[0] = 1;
data[1] = 0;
return true;
}
if (size < (int)HEADER_SIZE || data[0] != 1 || data[1] > UNI_SWITCH2_PAIRING_CAPACITY ||
size != (int)(HEADER_SIZE + data[1] * RECORD_SIZE)) return false;
for (uint8_t i = 0; i < data[1]; ++i) {
const uint8_t* row = data + HEADER_SIZE + i * RECORD_SIZE;
if (!stable_address(row[0], row + 1)) return false;
if (i != 0 && memcmp(row - RECORD_SIZE, row, RECORD_SIZE) >= 0) return false;
}
return true;
}
bool uni_switch2_pairing_known(uint8_t type, const uint8_t address[6]) {
if (!stable_address(type, address)) return false;
const btstack_tlv_t* tlv;
void* context;
uint8_t data[STORE_SIZE];
if (!load(&tlv, &context, data)) return false;
for (uint8_t i = 0; i < data[1]; ++i) {
const uint8_t* row = data + HEADER_SIZE + i * RECORD_SIZE;
if (row[0] == type && memcmp(row + 1, address, 6) == 0) return true;
}
return false;
}
bool uni_switch2_pairing_remember(uint8_t type, const uint8_t address[6]) {
if (!stable_address(type, address)) return false;
const btstack_tlv_t* tlv;
void* context;
uint8_t data[STORE_SIZE];
if (!load(&tlv, &context, data)) return false;
uint8_t record[RECORD_SIZE];
record[0] = type;
memcpy(record + 1, address, 6);
uint8_t index = 0;
for (; index < data[1]; ++index) {
int order = memcmp(record, data + HEADER_SIZE + index * RECORD_SIZE, RECORD_SIZE);
if (order == 0) return true;
if (order < 0) break;
}
if (data[1] == UNI_SWITCH2_PAIRING_CAPACITY) return false;
uint8_t* insertion = data + HEADER_SIZE + index * RECORD_SIZE;
memmove(insertion + RECORD_SIZE, insertion, (data[1] - index) * RECORD_SIZE);
memcpy(insertion, record, RECORD_SIZE);
++data[1];
return tlv->store_tag(context, PAIRING_TAG, data, HEADER_SIZE + data[1] * RECORD_SIZE) == 0;
}
bool uni_switch2_pairing_clear(void) {
const btstack_tlv_t* tlv;
void* context;
btstack_tlv_get_instance(&tlv, &context);
if (tlv == NULL || tlv->store_tag == NULL) return false;
// Store an empty record rather than the void delete API: callers must know
// whether forgetting was persisted before acknowledging clear-pairings.
const uint8_t empty[HEADER_SIZE] = {1, 0};
return tlv->store_tag(context, PAIRING_TAG, empty, sizeof(empty)) == 0;
}
bool uni_switch2_pairing_get(uint8_t index, uint8_t* type, uint8_t address[6]) {
if (type == NULL || address == NULL) return false;
const btstack_tlv_t* tlv;
void* context;
uint8_t data[STORE_SIZE];
if (!load(&tlv, &context, data) || index >= data[1]) return false;
const uint8_t* row = data + HEADER_SIZE + index * RECORD_SIZE;
*type = row[0];
memcpy(address, row + 1, 6);
return true;
}