Skip to content
Merged
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
49 changes: 49 additions & 0 deletions include/zephyr/bluetooth/services/ias.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,23 @@
* as a part of ongoing development.
*/

#include <zephyr/bluetooth/conn.h>

#ifdef __cplusplus
extern "C" {
#endif

enum bt_ias_alert_lvl {
/** No alerting should be done on device */
BT_IAS_ALERT_LVL_NO_ALERT,

/** Device shall alert */
BT_IAS_ALERT_LVL_MILD_ALERT,

/** Device should alert in strongest possible way */
BT_IAS_ALERT_LVL_HIGH_ALERT,
};

/** @brief Immediate Alert Service callback structure. */
struct bt_ias_cb {
/**
Expand Down Expand Up @@ -59,6 +72,42 @@ int bt_ias_local_alert_stop(void);
#define BT_IAS_CB_DEFINE(_name) \
static const STRUCT_SECTION_ITERABLE(bt_ias_cb, _CONCAT(bt_ias_cb_, _name))

struct bt_ias_client_cb {
/** @brief Callback function for bt_ias_discover.
*
* This callback is called when discovery procedure is complete.
*
* @param conn Bluetooth connection object.
* @param err 0 on success, ATT error or negative errno otherwise
*/
void (*discover)(struct bt_conn *conn, int err);
};

/** @brief Set alert level
*
* @param conn Bluetooth connection object
* @param bt_ias_alert_lvl Level of alert to write
*
* @return Zero in case of success and error code in case of error.
*/
int bt_ias_client_alert_write(struct bt_conn *conn, enum bt_ias_alert_lvl);

/** @brief Discover Immediate Alert Service
*
* @param conn Bluetooth connection object
*
* @return Zero in case of success and error code in case of error.
*/
int bt_ias_discover(struct bt_conn *conn);

/** @brief Register Immediate Alert Client callbacks
*
* @param cb The callback structure
*
* @return Zero in case of success and error code in case of error.
*/
int bt_ias_client_cb_register(const struct bt_ias_client_cb *cb);
Comment on lines +103 to +109
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not that there's anything wrong with this, but how come the server callbacks are statically registered with BT_IAS_CB_DEFINE, and the client callbacks are dynamically registered with bt_ias_client_cb_register?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the main reason for doing this way on the client-side was actually looking at other clients' code and seeing that it was generally using dynamically registered callbacks. Do you think this will be more proper to do this in convention between IAS client and server? If so, would it be better to do generally dynamic or static callbacks?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a good question. We generally haven't done much of statically defined callbacks in the host (that was only quite recently introduced at all), so all existing profiles simply use(d) the dynamical register.

Going forward i am not sure what approach we'll take: The dynamic approach has the advantage of being able (at least if implemented) to de-register callbacks when it is no longer interested in it, which can be a small run-time performance optimization (as fewer callbacks are called).

I am not sure what general approach we'll use going forward:

  1. Statically registered callbacks
  2. Dynamically registered callbacks
  3. Both (i.e. like the struct bt_conn_cb)

This service is marked as experimental, so we can just go ahead with the dynamic approach now, but perhaps we'll need to agree on a general approach at some point (soon).

@jhedberg @alwa-nordic @asbjornsabo Any input?


#ifdef __cplusplus
}
#endif
Expand Down
6 changes: 4 additions & 2 deletions subsys/bluetooth/services/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ zephyr_sources_ifdef(CONFIG_BT_HRS hrs.c)

zephyr_sources_ifdef(CONFIG_BT_TPS tps.c)

zephyr_sources_ifdef(CONFIG_BT_IAS ias.c)

if(CONFIG_BT_OTS OR CONFIG_BT_OTS_CLIENT)
add_subdirectory(ots)
endif()

if(CONFIG_BT_IAS OR CONFIG_BT_IAS_CLIENT)
add_subdirectory(ias)
endif()
2 changes: 1 addition & 1 deletion subsys/bluetooth/services/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ rsource "Kconfig.hrs"

rsource "Kconfig.tps"

rsource "Kconfig.ias"
rsource "ias/Kconfig.ias"

rsource "ots/Kconfig"

Expand Down
9 changes: 9 additions & 0 deletions subsys/bluetooth/services/ias/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# SPDX-License-Identifier: Apache-2.0

zephyr_sources_ifdef(
CONFIG_BT_IAS
ias.c)

zephyr_library_sources_ifdef(
CONFIG_BT_IAS_CLIENT
ias_client.c)
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,23 @@ config BT_IAS_SEC_AUTH

endchoice #BT_IAS_SEC_CHOICE

endif # BT_IAS

#### Immediate Alert Service Client ################################

config BT_IAS_CLIENT
bool "Immediate Alert Service Client [Experimental]"
select BT_GATT_CLIENT
select EXPERIMENTAL
help
This option enables support for Immediate Alert Service Client

config BT_DEBUG_IAS_CLIENT
bool "Immediate Alert Service Client debug"
depends on BT_IAS_CLIENT
help
This option enables enables Immediate Alert Service Client debug logs.

module = BT_IAS
module-str = IAS
source "${ZEPHYR_BASE}/subsys/logging/Kconfig.template.log_config"

endif # BT_IAS
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,6 @@ LOG_MODULE_REGISTER(ias);
#define IAS_ALERT_LEVEL_PERM BT_GATT_PERM_WRITE
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see that this files was moved to an ias directory.

Are we sure we want that?
@jhedberg @alwa-nordic Should we keep the services directory flat, or have these sub-directories?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've taken the pattern set up by OTS here in that matter.

#endif

enum bt_ias_alert_lvl {
BT_IAS_ALERT_LVL_NO_ALERT,
BT_IAS_ALERT_LVL_MILD_ALERT,
BT_IAS_ALERT_LVL_HIGH_ALERT,
};

struct alerting_device {
enum bt_ias_alert_lvl alert_level;
};
Expand Down
193 changes: 193 additions & 0 deletions subsys/bluetooth/services/ias/ias_client.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
/*
* Copyright (c) 2022 Codecoup
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <errno.h>
#include <stdint.h>
#include <zephyr/sys/check.h>
#include <zephyr/logging/log.h>
#include <zephyr/bluetooth/gatt.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/services/ias.h>

#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_IAS_CLIENT)
#define LOG_MODULE_NAME bt_ias_client
#include "../../common/log.h"

enum {
IAS_DISCOVER_IN_PROGRESS,

IAS_NUM_FLAGS, /* keep as last */
};

struct bt_ias_client {
/* Handle for alert writes */
uint16_t alert_level_handle;

/** Internal flags **/
ATOMIC_DEFINE(flags, IAS_NUM_FLAGS);

/* Gatt discover procedure parameters */
struct bt_gatt_discover_params discover;
};

static const struct bt_uuid *alert_lvl_uuid = BT_UUID_ALERT_LEVEL;
static const struct bt_uuid *ias_uuid = BT_UUID_IAS;

static const struct bt_ias_client_cb *ias_client_cb;
static struct bt_ias_client client_list[CONFIG_BT_MAX_CONN];

static struct bt_ias_client *client_by_conn(struct bt_conn *conn)
{
return &client_list[bt_conn_index(conn)];
}

static void client_cleanup(struct bt_ias_client *ias_client)
{
(void)memset(ias_client, 0, sizeof(*ias_client));
}

static void discover_complete(struct bt_conn *conn, int err)
{
BT_DBG("conn %p", (void *)conn);

if (err) {
client_cleanup(client_by_conn(conn));
BT_DBG("Discover failed (err %d\n)", err);
}

if (ias_client_cb != NULL && ias_client_cb->discover != NULL) {
ias_client_cb->discover(conn, err);
}
}

int bt_ias_client_alert_write(struct bt_conn *conn, enum bt_ias_alert_lvl lvl)
{
int err;
uint8_t lvl_u8;

CHECKIF(conn == NULL) {
return -ENOTCONN;
}

if (client_by_conn(conn)->alert_level_handle == 0) {
return -EINVAL;
}

lvl_u8 = (uint8_t)lvl;

if (lvl_u8 < BT_IAS_ALERT_LVL_NO_ALERT || lvl_u8 > BT_IAS_ALERT_LVL_HIGH_ALERT) {
BT_ERR("Invalid alert value: %u", lvl_u8);
return -EINVAL;
}

err = bt_gatt_write_without_response(conn,
client_by_conn(conn)->alert_level_handle,
&lvl_u8, sizeof(lvl_u8), false);
if (err < 0) {
BT_ERR("IAS client level %d write failed: %d", lvl, err);
}

return err;
}

static uint8_t bt_ias_alert_lvl_disc_cb(struct bt_conn *conn,
const struct bt_gatt_attr *attr,
struct bt_gatt_discover_params *discover)
{
const struct bt_gatt_chrc *chrc = (struct bt_gatt_chrc *)attr->user_data;

atomic_clear_bit(client_by_conn(conn)->flags, IAS_DISCOVER_IN_PROGRESS);

if (attr == NULL) {
discover_complete(conn, -ENOENT);

return BT_GATT_ITER_STOP;
}

client_by_conn(conn)->alert_level_handle = chrc->value_handle;
discover_complete(conn, 0);

return BT_GATT_ITER_STOP;
}

static uint8_t bt_ias_prim_disc_cb(struct bt_conn *conn,
const struct bt_gatt_attr *attr,
struct bt_gatt_discover_params *discover)
{
int err;
const struct bt_gatt_service_val *data;
struct bt_ias_client *client = client_by_conn(conn);

if (!attr) {
discover_complete(conn, -ENOENT);

return BT_GATT_ITER_STOP;
}

data = attr->user_data;

client->discover.uuid = alert_lvl_uuid;
client->discover.start_handle = attr->handle + 1;
client->discover.end_handle = data->end_handle;
client->discover.type = BT_GATT_DISCOVER_CHARACTERISTIC;
client->discover.func = bt_ias_alert_lvl_disc_cb;

err = bt_gatt_discover(conn, &client->discover);
if (err) {
discover_complete(conn, err);
}

return BT_GATT_ITER_STOP;
}

int bt_ias_discover(struct bt_conn *conn)
{
int err;
struct bt_ias_client *client = client_by_conn(conn);

CHECKIF(!conn || !ias_client_cb || !ias_client_cb->discover) {
return -EINVAL;
}

if (atomic_test_bit(client->flags, IAS_DISCOVER_IN_PROGRESS)) {
return -EBUSY;
}

client_cleanup(client);
atomic_set_bit(client->flags, IAS_DISCOVER_IN_PROGRESS);

client->discover.uuid = ias_uuid;
client->discover.func = bt_ias_prim_disc_cb;
client->discover.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE;
client->discover.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE;
client->discover.type = BT_GATT_DISCOVER_PRIMARY;

err = bt_gatt_discover(conn, &client->discover);
if (err < 0) {
discover_complete(conn, err);
}

return err;
}

int bt_ias_client_cb_register(const struct bt_ias_client_cb *cb)
{
CHECKIF(!cb) {
return -EINVAL;
}

CHECKIF(cb->discover == NULL) {
return -EINVAL;
}

CHECKIF(ias_client_cb) {
return -EALREADY;
}

ias_client_cb = cb;

return 0;
}
9 changes: 9 additions & 0 deletions subsys/bluetooth/shell/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,15 @@ zephyr_library_sources_ifdef(
bass_client.c
)

zephyr_library_sources_ifdef(
CONFIG_BT_IAS
ias.c
)
zephyr_library_sources_ifdef(
CONFIG_BT_IAS_CLIENT
ias_client.c
)

if(CONFIG_BT_CTLR AND CONFIG_BT_LL_SW_SPLIT)
zephyr_library_sources(
ll.c
Expand Down
Loading