Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions include/zephyr/bluetooth/gap.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,15 @@ extern "C" {
* @{
*/

/**
* @brief Default GAP service name
*
* Use this name as a first argument for BT_GATT_SERVICE_DEFINE when creating
* GAP service implementation.
* This ensures that the GAP service would be placed just after BT_UUID_GATT.
*/
#define BT_GATT_GAP_SVC_DEFAULT_NAME _2_gap_svc

/**
* @name Company Identifiers (see Bluetooth Assigned Numbers)
* @{
Expand Down
7 changes: 7 additions & 0 deletions samples/bluetooth/peripheral_gap_svc/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(peripheral_gap_svc)

target_sources(app PRIVATE
src/main.c
)
29 changes: 29 additions & 0 deletions samples/bluetooth/peripheral_gap_svc/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
.. zephyr:code-sample:: ble_peripheral_gap_svc
:name: Peripheral GAP Server non default implementation
:relevant-api: bluetooth

Implement a dummy peripheral with GAP Server that limits the accepted names

Overview
********

This sample demonstrates the implementation of the GAP service




This sample demonstrates the usage of the NUS service (Nordic UART Service) as a serial
endpoint to exchange data. In this case, the sample assumes the data is UTF-8 encoded,
but it may be binary data. Once the user connects to the device and subscribes to the TX
characteristic, it will start receiving periodic notifications with "Hello World!\n".

Requirements
************

* BlueZ running on the host, or
* A board with Bluetooth LE support

Building and Running
********************

See :zephyr:code-sample-category:`bluetooth` samples for details.
14 changes: 14 additions & 0 deletions samples/bluetooth/peripheral_gap_svc/prj.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
CONFIG_LOG=y
CONFIG_BT=y
CONFIG_BT_SMP=y
CONFIG_BT_PERIPHERAL=y

# Testing locally redefined SVC implementation
CONFIG_BT_GAP_SVC_DEFAULT_IMPL=n

CONFIG_BT_DEVICE_NAME="Zephyr GAP service"
CONFIG_BT_DEVICE_NAME_DYNAMIC=y
CONFIG_BT_DEVICE_NAME_GATT_WRITABLE=y

CONFIG_BT_DEVICE_APPEARANCE_DYNAMIC=n
CONFIG_BT_DEVICE_APPEARANCE_GATT_WRITABLE=n
14 changes: 14 additions & 0 deletions samples/bluetooth/peripheral_gap_svc/sample.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
sample:
name: Bluetooth Peripheral GAP service non default implementation
description: Demonstrates the GAP service implementation on application side.
tests:
sample.bluetooth.peripheral_gap_svc:
harness: bluetooth
platform_allow:
- qemu_cortex_m3
- qemu_x86
- nrf52840dk/nrf52840
- ophelia4ev/nrf54l15/cpuapp
integration_platforms:
- qemu_cortex_m3
tags: bluetooth
157 changes: 157 additions & 0 deletions samples/bluetooth/peripheral_gap_svc/src/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
/*
* Copyright (c) 2025 Koppel Electronic
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <zephyr/kernel.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/bluetooth/gatt.h>


#define DEVICE_NAME CONFIG_BT_DEVICE_NAME
#define DEVICE_NAME_LEN (sizeof(DEVICE_NAME) - 1)

static const struct bt_data ad[] = {
BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
BT_DATA(BT_DATA_NAME_COMPLETE, DEVICE_NAME, DEVICE_NAME_LEN),
};

/* -----------------------------------------------------------------------------
* Local implementation of GAP service
*/

static ssize_t read_name(struct bt_conn *conn, const struct bt_gatt_attr *attr,
void *buf, uint16_t len, uint16_t offset)
{
const char *name = bt_get_name();

return bt_gatt_attr_read(conn, attr, buf, len, offset, name,
strlen(name));
}

static ssize_t write_name(struct bt_conn *conn, const struct bt_gatt_attr *attr, const void *buf,
uint16_t len, uint16_t offset, uint8_t flags)
{
/* adding one to fit the terminating null character */
char value[CONFIG_BT_DEVICE_NAME_MAX + 1] = {};

if (offset != 0) {
return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
}

if (offset + len > CONFIG_BT_DEVICE_NAME_MAX) {
return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
}

memcpy(value, buf, len);

value[len] = '\0';

/* Check if the name starts with capital letter */
if (value[0] < 'A' || value[0] > 'Z') {
printk("Rejected name change to \"%s\": must start with capital letter\n", value);
return BT_GATT_ERR(BT_ATT_ERR_VALUE_NOT_ALLOWED);
}

bt_set_name(value);

printk("Name changed to \"%s\"\n", value);

return len;
}

static ssize_t read_appearance(struct bt_conn *conn,
const struct bt_gatt_attr *attr, void *buf,
uint16_t len, uint16_t offset)
{
uint16_t appearance = sys_cpu_to_le16(bt_get_appearance());

return bt_gatt_attr_read(conn, attr, buf, len, offset, &appearance, sizeof(appearance));
}

BT_GATT_SERVICE_DEFINE(BT_GATT_GAP_SVC_DEFAULT_NAME,
BT_GATT_PRIMARY_SERVICE(BT_UUID_GAP),
/* Require pairing for writes to device name */
BT_GATT_CHARACTERISTIC(BT_UUID_GAP_DEVICE_NAME,
BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE,
BT_GATT_PERM_READ | BT_GATT_PERM_WRITE,
read_name, write_name, NULL),
BT_GATT_CHARACTERISTIC(BT_UUID_GAP_APPEARANCE,
BT_GATT_CHRC_READ,
BT_GATT_PERM_READ,
read_appearance, NULL, NULL),
);

/* End of local implementation of GAP service
* ---------------------------------------------------------------------------
*/

static void connected(struct bt_conn *conn, uint8_t conn_err)
{
char addr[BT_ADDR_LE_STR_LEN];

bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));

if (conn_err) {
printk("Failed to connect to %s (%u)\n", addr, conn_err);
return;
}

printk("Connected: %s\n", addr);
}

static void disconnected(struct bt_conn *conn, uint8_t reason)
{
char addr[BT_ADDR_LE_STR_LEN];

bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));

printk("Disconnected: %s (reason 0x%02x)\n", addr, reason);
}

static int start_advertising(void)
{
int err;

err = bt_le_adv_start(BT_LE_ADV_CONN_FAST_1, ad, ARRAY_SIZE(ad), NULL, 0);
if (err) {
printk("Advertising failed to start (err %d)\n", err);
}

return err;
}

static void recycled(void)
{
start_advertising();
}

BT_CONN_CB_DEFINE(conn_callbacks) = {
.connected = connected,
.disconnected = disconnected,
.recycled = recycled,
};

int main(void)
{
int err;

printk("Sample - Bluetooth Peripheral GAP service\n");

err = bt_enable(NULL);
if (err) {
printk("Failed to enable bluetooth: %d\n", err);
return err;
}

err = start_advertising();
if (err) {
return err;
}

printk("Initialization complete\n");

return 0;
}
5 changes: 5 additions & 0 deletions subsys/bluetooth/host/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ if(CONFIG_BT_HCI_HOST)
gatt.c
)

zephyr_library_sources_ifdef(
CONFIG_GATT_GAP_SVC_VALIDATE
gatt_gap_svc_validate.c
)

if(CONFIG_BT_SMP)
zephyr_library_sources(
smp.c
Expand Down
57 changes: 1 addition & 56 deletions subsys/bluetooth/host/Kconfig.gatt
Original file line number Diff line number Diff line change
Expand Up @@ -259,62 +259,6 @@ config BT_PERIPHERAL_PREF_TIMEOUT
Range 3200 to 65534 is invalid. 65535 represents no specific value.
endif # BT_GAP_PERIPHERAL_PREF_PARAMS

config BT_DEVICE_NAME_GATT_WRITABLE
bool "Allow to write device name by remote GATT clients"
depends on BT_DEVICE_NAME_DYNAMIC
default y
help
Enabling this option allows remote GATT clients to write to device
name GAP characteristic.

if BT_DEVICE_NAME_GATT_WRITABLE
choice BT_DEVICE_NAME_GATT_WRITABLE_SECURITY
prompt "Security requirements"
default DEVICE_NAME_GATT_WRITABLE_ENCRYPT
help
Select security requirements for writing device name by remote GATT
clients.

config DEVICE_NAME_GATT_WRITABLE_NONE
bool "No requirements"

config DEVICE_NAME_GATT_WRITABLE_ENCRYPT
bool "Encryption required"

config DEVICE_NAME_GATT_WRITABLE_AUTHEN
bool "Encryption and authentication required"

endchoice #BT_DEVICE_NAME_GATT_WRITABLE_SECURITY
endif #BT_DEVICE_NAME_GATT_WRITABLE

config BT_DEVICE_APPEARANCE_GATT_WRITABLE
bool "Allow to write GAP Appearance by remote GATT clients"
depends on BT_DEVICE_APPEARANCE_DYNAMIC
default y
help
Enabling this option allows remote GATT clients to write to device
appearance GAP characteristic.

if BT_DEVICE_APPEARANCE_GATT_WRITABLE
choice BT_DEVICE_APPEARANCE_GATT_WRITABLE
prompt "Security requirements"
default DEVICE_APPEARANCE_GATT_WRITABLE_AUTHEN
help
Select security requirements for writing device name by remote GATT
clients.

config BT_DEVICE_APPEARANCE_GATT_WRITABLE_NONE
bool "No requirements"

config BT_DEVICE_APPEARANCE_GATT_WRITABLE_ENCRYPT
bool "Encryption required"

config DEVICE_APPEARANCE_GATT_WRITABLE_AUTHEN
bool "Encryption and authentication required"

endchoice #BT_DEVICE_APPEARANCE_GATT_WRITABLE
endif #BT_DEVICE_APPEARANCE_GATT_WRITABLE

config BT_GATT_AUTHORIZATION_CUSTOM
bool "Custom authorization of GATT operations"
help
Expand All @@ -323,4 +267,5 @@ config BT_GATT_AUTHORIZATION_CUSTOM
with the bt_gatt_authorization_cb_register API. See the API
documentation for more details.

rsource 'Kconfig.gatt_gap_svc_validate'
endmenu
21 changes: 21 additions & 0 deletions subsys/bluetooth/host/Kconfig.gatt_gap_svc_validate
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Bluetooth GATT GAP service validation

# Copyright (c) 2025 Koppel Electronic
# SPDX-License-Identifier: Apache-2.0

menuconfig GATT_GAP_SVC_VALIDATE
bool "GATT GAP Service validation"
depends on BT_CONN
default y
help
Validate if in the GATT database exactly one GAP service is present.
This helps to debug any issues related to GAP service that may make
the Bluetooth implementation not compatible with the standard.

if GATT_GAP_SVC_VALIDATE

module = GATT_GAP_SVC_VALIDATE
module-str = gatt-gap-validate
source "subsys/logging/Kconfig.template.log_config"

endif # GATT_GAP_SVC_VALIDATE
9 changes: 9 additions & 0 deletions subsys/bluetooth/host/conn.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
#include "common/bt_str.h"
#include "conn_internal.h"
#include "direction_internal.h"
#include "gatt_gap_svc_validate.h"
#include "hci_core.h"
#include "id.h"
#include "iso_internal.h"
Expand Down Expand Up @@ -4428,6 +4429,14 @@ int bt_conn_init(void)

bt_att_init();

if (IS_ENABLED(CONFIG_GATT_GAP_SVC_VALIDATE)) {
err = gatt_gap_svc_validate();
if (err) {
LOG_ERR("GATT GAP service validation failed (err %d)", err);
return err;
}
}

err = bt_smp_init();
if (err) {
return err;
Expand Down
Loading