Skip to content

Commit cb8d0fd

Browse files
committed
info: initial implementation
Read INFO characteristic as the first action after discovering device. Save information about server and device certificates, i.e. whether they were already provisioned. Reading INFO characteristic replaces reading server cert serial number, since the same information is provided. When device certificate provisioned flag is set, this reduces overhead by about 350ms (with default configuration of pouch device). Signed-off-by: Marcin Niestroj <m.niestroj@emb.dev>
1 parent 2f92abd commit cb8d0fd

9 files changed

Lines changed: 292 additions & 85 deletions

File tree

.github/workflows/build.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ jobs:
8282
- name: Install pip packages
8383
run: |
8484
uv pip install \
85+
-r pouch/requirements.txt \
8586
-r zephyr/scripts/requirements-base.txt
8687
8788
uv pip install \

include/pouch_gateway/bt/info.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/*
2+
* Copyright (c) 2025 Golioth, Inc.
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#pragma once
8+
9+
struct bt_conn;
10+
11+
/**
12+
* Start reading info characteristic for the given Bluetooth connection.
13+
*
14+
* @param conn The Bluetooth connection.
15+
*/
16+
void pouch_gateway_info_read_start(struct bt_conn *conn);

include/pouch_gateway/info.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Copyright (c) 2025 Golioth, Inc.
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#pragma once
8+
9+
#include <stdbool.h>
10+
#include <stddef.h>
11+
#include <stdint.h>
12+
13+
struct pouch_gateway_info_context;
14+
15+
#define POUCH_GATEWAY_INFO_FLAG_DEVICE_PROVISIONED BIT(0)
16+
17+
struct pouch_gateway_info_context *pouch_gateway_info_start(void);
18+
int pouch_gateway_info_push(struct pouch_gateway_info_context *context,
19+
const void *data,
20+
size_t len);
21+
void pouch_gateway_info_abort(struct pouch_gateway_info_context *context);
22+
int pouch_gateway_info_finish(struct pouch_gateway_info_context *context,
23+
bool *server_cert_provisioned,
24+
bool *device_cert_provisioned);

include/pouch_gateway/types.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,11 @@ struct pouch_gateway_node_info
4444
void *server_cert_scratch;
4545
struct pouch_gatt_packetizer *packetizer;
4646
struct pouch_gateway_uplink *uplink;
47+
struct pouch_gateway_info_context *info_ctx;
4748
struct pouch_gateway_device_cert_context *device_cert_ctx;
4849
struct pouch_gateway_server_cert_context *server_cert_ctx;
50+
bool server_cert_provisioned;
51+
bool device_cert_provisioned;
4952
};
5053

5154
/**

lib/CMakeLists.txt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,14 @@ zephyr_library()
33
zephyr_library_sources(bt/cert.c)
44
zephyr_library_sources(bt/connect.c)
55
zephyr_library_sources(bt/downlink.c)
6+
zephyr_library_sources(bt/info.c)
67
zephyr_library_sources(bt/scan.c)
78
zephyr_library_sources(bt/uplink.c)
89
zephyr_library_sources(block.c)
910
zephyr_library_sources(cert.c)
1011
zephyr_library_sources(downlink.c)
12+
zephyr_library_sources(info.c)
13+
zephyr_library_sources(info_decode.c)
1114
zephyr_library_sources(uplink.c)
1215

1316
zephyr_library_link_libraries(mbedTLS)
@@ -23,3 +26,18 @@ endif()
2326
generate_inc_file_for_target(${lib_name}
2427
${cert_name}
2528
${ZEPHYR_BINARY_DIR}/include/generated/pouch_gateway_server.pem.inc)
29+
30+
message("${ZEPHYR_POUCH_MODULE_DIR}/src/transport/gatt/info.cddl")
31+
32+
add_custom_command(OUTPUT info_decode.c
33+
COMMAND zcbor code
34+
-c ${ZEPHYR_POUCH_MODULE_DIR}/src/transport/gatt/info.cddl
35+
-s
36+
-t pouch_gatt_info
37+
-d --include-prefix cddl/
38+
--output-c ${CMAKE_CURRENT_BINARY_DIR}/info_decode.c
39+
--output-h ${CMAKE_CURRENT_BINARY_DIR}/include/cddl/info_decode.h
40+
BYPRODUCTS include/cddl/info_decode.h include/cddl/info_decode_types.h
41+
DEPENDS ${ZEPHYR_POUCH_MODULE_DIR}/src/transport/gatt/info.cddl)
42+
43+
zephyr_library_include_directories(${CMAKE_CURRENT_BINARY_DIR}/include)

lib/bt/cert.c

Lines changed: 12 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -51,72 +51,6 @@ static void server_cert_cleanup(struct bt_conn *conn)
5151
}
5252
}
5353

54-
static uint8_t server_cert_read_cb(struct bt_conn *conn,
55-
uint8_t err,
56-
struct bt_gatt_read_params *params,
57-
const void *data,
58-
uint16_t length)
59-
{
60-
if (err)
61-
{
62-
LOG_ERR("Failed to read BLE GATT %s (err %d)", "server cert", err);
63-
return BT_GATT_ITER_STOP;
64-
}
65-
66-
if (length == 0)
67-
{
68-
gateway_server_cert_write_start(conn);
69-
return BT_GATT_ITER_STOP;
70-
}
71-
72-
bool is_first = false;
73-
bool is_last = false;
74-
const void *payload = NULL;
75-
ssize_t payload_len = pouch_gatt_packetizer_decode(data, length, &payload, &is_first, &is_last);
76-
if (payload_len < 0)
77-
{
78-
LOG_ERR("Failed to decode BLE GATT %s (err %d)", "server cert", (int) payload_len);
79-
server_cert_cleanup(conn);
80-
pouch_gateway_bt_finished(conn);
81-
return BT_GATT_ITER_STOP;
82-
}
83-
84-
if (data)
85-
{
86-
LOG_HEXDUMP_DBG(data, length, "[READ] BLE GATT server cert");
87-
}
88-
89-
if (is_last)
90-
{
91-
uint8_t serial[CERT_SERIAL_MAXLEN];
92-
size_t serial_len = sizeof(serial);
93-
94-
pouch_gateway_server_cert_get_serial(serial, &serial_len);
95-
96-
if (payload_len > 0 && payload_len == serial_len
97-
&& memcmp(serial, payload, payload_len) == 0)
98-
{
99-
LOG_DBG("server cert match");
100-
gateway_device_cert_read_start(conn);
101-
}
102-
else
103-
{
104-
LOG_DBG("server cert mismatch");
105-
gateway_server_cert_write_start(conn);
106-
}
107-
108-
return BT_GATT_ITER_STOP;
109-
}
110-
111-
err = bt_gatt_read(conn, params);
112-
if (err)
113-
{
114-
LOG_ERR("BT (re)read request failed: %d", err);
115-
return BT_GATT_ITER_STOP;
116-
}
117-
118-
return BT_GATT_ITER_STOP;
119-
}
12054

12155
static void device_cert_cleanup(struct bt_conn *conn)
12256
{
@@ -262,6 +196,7 @@ static void write_response_cb(struct bt_conn *conn,
262196
{
263197
// There was certificate update in the meantime, so send it once again.
264198
LOG_INF("Noticed certificate update, sending once again");
199+
node->server_cert_provisioned = false;
265200
gateway_server_cert_write_start(conn);
266201
}
267202
}
@@ -301,6 +236,12 @@ static void gateway_server_cert_write_start(struct bt_conn *conn)
301236
{
302237
struct pouch_gateway_node_info *node = pouch_gateway_get_node_info(conn);
303238

239+
if (node->server_cert_provisioned)
240+
{
241+
gateway_device_cert_read_start(conn);
242+
return;
243+
}
244+
304245
if (0 == node->attr_handles[POUCH_GATEWAY_GATT_ATTR_SERVER_CERT].value)
305246
{
306247
LOG_ERR("%s characteristic undiscovered", "server cert");
@@ -325,28 +266,15 @@ static void gateway_server_cert_write_start(struct bt_conn *conn)
325266
write_server_cert_characteristic(conn);
326267
}
327268

328-
static void gateway_server_cert_serial_read_start(struct bt_conn *conn)
269+
static void gateway_device_cert_read_start(struct bt_conn *conn)
329270
{
330271
struct pouch_gateway_node_info *node = pouch_gateway_get_node_info(conn);
331272

332-
struct bt_gatt_read_params *read_params = &node->read_params;
333-
memset(read_params, 0, sizeof(*read_params));
334-
335-
read_params->func = server_cert_read_cb;
336-
read_params->handle_count = 1;
337-
read_params->single.handle = node->attr_handles[POUCH_GATEWAY_GATT_ATTR_SERVER_CERT].value;
338-
int err = bt_gatt_read(conn, read_params);
339-
if (err)
273+
if (node->device_cert_provisioned)
340274
{
341-
LOG_ERR("BT read request failed: %d", err);
342-
server_cert_cleanup(conn);
343-
pouch_gateway_bt_finished(conn);
275+
pouch_gateway_uplink_start(conn);
276+
return;
344277
}
345-
}
346-
347-
static void gateway_device_cert_read_start(struct bt_conn *conn)
348-
{
349-
struct pouch_gateway_node_info *node = pouch_gateway_get_node_info(conn);
350278

351279
struct bt_gatt_read_params *read_params = &node->read_params;
352280
memset(read_params, 0, sizeof(*read_params));
@@ -366,5 +294,5 @@ static void gateway_device_cert_read_start(struct bt_conn *conn)
366294

367295
void pouch_gateway_cert_exchange_start(struct bt_conn *conn)
368296
{
369-
gateway_server_cert_serial_read_start(conn);
297+
gateway_server_cert_write_start(conn);
370298
}

lib/bt/connect.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include <pouch_gateway/bt/connect.h>
1818
#include <pouch_gateway/bt/cert.h>
1919
#include <pouch_gateway/bt/downlink.h>
20+
#include <pouch_gateway/bt/info.h>
2021
#include <pouch_gateway/bt/scan.h>
2122
#include <pouch_gateway/bt/uplink.h>
2223

@@ -79,7 +80,7 @@ static uint8_t discover_descriptors(struct bt_conn *conn,
7980
if (node->attr_handles[POUCH_GATEWAY_GATT_ATTR_SERVER_CERT].value
8081
&& node->attr_handles[POUCH_GATEWAY_GATT_ATTR_DEVICE_CERT].value)
8182
{
82-
pouch_gateway_cert_exchange_start(conn);
83+
pouch_gateway_info_read_start(conn);
8384
}
8485
else
8586
{

lib/bt/info.c

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
/*
2+
* Copyright (c) 2025 Golioth, Inc.
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <pouch/transport/gatt/common/packetizer.h>
8+
9+
#include <pouch_gateway/info.h>
10+
#include <pouch_gateway/bt/cert.h>
11+
#include <pouch_gateway/bt/connect.h>
12+
#include <pouch_gateway/bt/info.h>
13+
14+
#include <zephyr/logging/log.h>
15+
LOG_MODULE_REGISTER(info_gatt);
16+
17+
static void info_cleanup(struct bt_conn *conn)
18+
{
19+
struct pouch_gateway_node_info *node = pouch_gateway_get_node_info(conn);
20+
21+
if (node->info_ctx)
22+
{
23+
pouch_gateway_info_abort(node->info_ctx);
24+
node->info_ctx = NULL;
25+
}
26+
}
27+
28+
static uint8_t info_read_cb(struct bt_conn *conn,
29+
uint8_t err,
30+
struct bt_gatt_read_params *params,
31+
const void *data,
32+
uint16_t length)
33+
{
34+
struct pouch_gateway_node_info *node = pouch_gateway_get_node_info(conn);
35+
36+
if (err)
37+
{
38+
LOG_ERR("Failed to read BLE GATT %s (err %d)", "info", err);
39+
return BT_GATT_ITER_STOP;
40+
}
41+
42+
if (length == 0)
43+
{
44+
info_cleanup(conn);
45+
pouch_gateway_cert_exchange_start(conn);
46+
return BT_GATT_ITER_STOP;
47+
}
48+
49+
bool is_first = false;
50+
bool is_last = false;
51+
const void *payload = NULL;
52+
ssize_t payload_len = pouch_gatt_packetizer_decode(data, length, &payload, &is_first, &is_last);
53+
if (payload_len < 0)
54+
{
55+
LOG_ERR("Failed to decode BLE GATT %s (err %d)", "info", (int) payload_len);
56+
info_cleanup(conn);
57+
pouch_gateway_bt_finished(conn);
58+
return BT_GATT_ITER_STOP;
59+
}
60+
61+
if (data)
62+
{
63+
LOG_HEXDUMP_DBG(data, length, "[READ] BLE GATT info");
64+
}
65+
66+
pouch_gateway_info_push(node->info_ctx, payload, payload_len);
67+
68+
if (is_last)
69+
{
70+
int err = pouch_gateway_info_finish(node->info_ctx,
71+
&node->server_cert_provisioned,
72+
&node->device_cert_provisioned);
73+
node->info_ctx = NULL;
74+
if (err)
75+
{
76+
LOG_ERR("Failed to parse info: %d", err);
77+
/* Continue anyway, as nothing contained in info is critical */
78+
}
79+
80+
info_cleanup(conn);
81+
pouch_gateway_cert_exchange_start(conn);
82+
return BT_GATT_ITER_STOP;
83+
}
84+
85+
err = bt_gatt_read(conn, params);
86+
if (err)
87+
{
88+
LOG_ERR("BT (re)read request failed: %d", err);
89+
info_cleanup(conn);
90+
pouch_gateway_bt_finished(conn);
91+
return BT_GATT_ITER_STOP;
92+
}
93+
94+
return BT_GATT_ITER_STOP;
95+
}
96+
97+
static void gateway_info_read_start(struct bt_conn *conn)
98+
{
99+
struct pouch_gateway_node_info *node = pouch_gateway_get_node_info(conn);
100+
101+
struct bt_gatt_read_params *read_params = &node->read_params;
102+
memset(read_params, 0, sizeof(*read_params));
103+
104+
node->server_cert_provisioned = false;
105+
node->device_cert_provisioned = false;
106+
node->info_ctx = pouch_gateway_info_start();
107+
108+
read_params->func = info_read_cb;
109+
read_params->handle_count = 1;
110+
read_params->single.handle = node->attr_handles[POUCH_GATEWAY_GATT_ATTR_INFO].value;
111+
int err = bt_gatt_read(conn, read_params);
112+
if (err)
113+
{
114+
LOG_ERR("BT read request failed: %d", err);
115+
info_cleanup(conn);
116+
pouch_gateway_bt_finished(conn);
117+
}
118+
}
119+
120+
void pouch_gateway_info_read_start(struct bt_conn *conn)
121+
{
122+
gateway_info_read_start(conn);
123+
}

0 commit comments

Comments
 (0)