/* * Copyright (c) 2022 Codecoup * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include LOG_MODULE_REGISTER(bt_ias_client, CONFIG_BT_IAS_CLIENT_LOG_LEVEL); #include #include #include 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) { LOG_DBG("conn %p", (void *)conn); if (err) { client_cleanup(client_by_conn(conn)); LOG_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_MILD_ALERT && lvl_u8 != BT_IAS_ALERT_LVL_HIGH_ALERT) { LOG_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) { LOG_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; atomic_clear_bit(client_by_conn(conn)->flags, IAS_DISCOVER_IN_PROGRESS); if (attr == NULL) { discover_complete(conn, -ENOENT); return BT_GATT_ITER_STOP; } chrc = (struct bt_gatt_chrc *)attr->user_data; 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; }