frameworks_bluetooth/tools/gatt_server.c

793 lines
25 KiB
C

/****************************************************************************
* Copyright (C) 2022 Xiaomi Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
***************************************************************************/
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "bluetooth.h"
#include "bt_gatts.h"
#include "bt_tools.h"
#define THROUGHTPUT_HORIZON 5
typedef struct {
struct list_node node;
bt_address_t remote_address;
uint16_t gatt_mtu;
} gatts_device_t;
static int register_cmd(void* handle, int argc, char* argv[]);
static int unregister_cmd(void* handle, int argc, char* argv[]);
static int start_cmd(void* handle, int argc, char* argv[]);
static int stop_cmd(void* handle, int argc, char* argv[]);
static int connect_cmd(void* handle, int argc, char* argv[]);
static int connect_bear_cmd(void* handle, int argc, char* argv[]);
static int disconnect_cmd(void* handle, int argc, char* argv[]);
static int notify_bas_cmd(void* handle, int argc, char* argv[]);
static int notify_cus_cmd(void* handle, int argc, char* argv[]);
static int indicate_cus_cmd(void* handle, int argc, char* argv[]);
static int read_phy_cmd(void* handle, int argc, char* argv[]);
static int update_phy_cmd(void* handle, int argc, char* argv[]);
static int throughput_cmd(void* handle, int argc, char* argv[]);
static gatts_handle_t g_dis_handle = NULL;
static gatts_handle_t g_bas_handle = NULL;
static gatts_handle_t g_custom_handle = NULL;
static volatile uint32_t throughtput_cursor = 0;
static uint16_t cccd_enable = 0;
static struct list_node gatts_device_list = LIST_INITIAL_VALUE(gatts_device_list);
enum {
GATT_SERVICE_DIS = 1,
GATT_SERVICE_BAS = 2,
GATT_SERVICE_CUSTOM = 3
};
#define GET_SERVICE_HANDLE(id, handle) \
{ \
switch (id) { \
case GATT_SERVICE_DIS: \
handle = g_dis_handle; \
break; \
case GATT_SERVICE_BAS: \
handle = g_bas_handle; \
break; \
case GATT_SERVICE_CUSTOM: \
handle = g_custom_handle; \
break; \
default: \
PRINT("invalid service id: %d", id); \
return CMD_INVALID_OPT; \
} \
if (!handle) { \
PRINT("service[%d] is not registered!", id); \
return CMD_ERROR; \
} \
}
enum {
/* IDs of Device Information service */
DIS_SERVICE_ID = 1,
DIS_MODEL_NUMBER_CHR_ID,
DIS_MANUFACTURER_NAME_CHR_ID,
DIS_PNP_CHR_ID,
};
enum {
/* IDs of Battery service */
BAS_SERVICE_ID = 1,
BAS_BATTERY_LEVEL_CHR_ID,
BAS_BATTERY_LEVEL_CHR_CCC_ID,
};
enum {
/* IDs of Private IOT service */
IOT_SERVICE_ID = 1,
IOT_SERVICE_TX_CHR_ID,
IOT_SERVICE_TX_CHR_CCC_ID,
IOT_SERVICE_RX_CHR_ID,
IOT_SERVICE_READ_CHR_ID,
};
uint8_t read_char_value[] = { 'H', 'e', 'l', 'l', 'o', ' ', 'V', 'E', 'L', 'A', '!' };
uint16_t tx_char_ccc_changed(void* srv_handle, bt_address_t* addr, uint16_t attr_handle, const uint8_t* value, uint16_t length, uint16_t offset)
{
PRINT_ADDR("gatts service TX char ccc changed, addr:%s", addr);
lib_dumpbuffer("new value:", value, length);
if (attr_handle == IOT_SERVICE_TX_CHR_CCC_ID)
cccd_enable = value[0];
return length;
}
uint16_t rx_char_on_read(void* srv_handle, bt_address_t* addr, uint16_t attr_handle, uint32_t req_handle)
{
PRINT_ADDR("gatts service RX char received read request, addr:%s", addr);
bt_status_t ret = bt_gatts_response(srv_handle, addr, req_handle, read_char_value, sizeof(read_char_value));
PRINT("gatts service RX char response. status: %d", ret);
return 0;
}
uint16_t rx_char_on_write(void* srv_handle, bt_address_t* addr, uint16_t attr_handle, const uint8_t* value, uint16_t length, uint16_t offset)
{
PRINT_ADDR("gatts service RX char received write request, addr:%s", addr);
lib_dumpbuffer("write value:", value, length);
return length;
}
const char model_number_str[] = "Vela_bt";
const char manufacturer_name_str[] = "Xiaomi";
const uint8_t pnp_id[7] = {
0x02, // pnp_vid_src
0x8F, 0x03, // pnp_vid
0x34, 0x12, // pnp_pid
0x00, 0x01 // pnp_ver
};
static gatt_attr_db_t s_dis_attr_db[] = {
GATT_H_PRIMARY_SERVICE(BT_UUID_DECLARE_16(0x180A), DIS_SERVICE_ID),
GATT_H_CHARACTERISTIC_AUTO_RSP(BT_UUID_DECLARE_16(0x2A24), GATT_PROP_READ, GATT_PERM_READ, (uint8_t*)model_number_str, sizeof(model_number_str), DIS_MODEL_NUMBER_CHR_ID),
GATT_H_CHARACTERISTIC_AUTO_RSP(BT_UUID_DECLARE_16(0x2A29), GATT_PROP_READ, GATT_PERM_READ, (uint8_t*)manufacturer_name_str, sizeof(manufacturer_name_str), DIS_MANUFACTURER_NAME_CHR_ID),
GATT_H_CHARACTERISTIC_AUTO_RSP(BT_UUID_DECLARE_16(0x2A50), GATT_PROP_READ, GATT_PERM_READ, (uint8_t*)pnp_id, sizeof(pnp_id), DIS_PNP_CHR_ID),
};
static gatt_srv_db_t s_dis_service_db = {
.attr_db = s_dis_attr_db,
.attr_num = sizeof(s_dis_attr_db) / sizeof(gatt_attr_db_t),
};
static uint8_t battery_level = 100U;
static gatt_attr_db_t s_bas_attr_db[] = {
GATT_H_PRIMARY_SERVICE(BT_UUID_DECLARE_16(0x180F), BAS_SERVICE_ID),
GATT_H_CHARACTERISTIC_AUTO_RSP(BT_UUID_DECLARE_16(0x2A19), GATT_PROP_READ | GATT_PROP_NOTIFY, GATT_PERM_READ, &battery_level, sizeof(battery_level), BAS_BATTERY_LEVEL_CHR_ID),
GATT_H_CCCD(GATT_PERM_READ | GATT_PERM_WRITE, tx_char_ccc_changed, BAS_BATTERY_LEVEL_CHR_CCC_ID),
};
static gatt_srv_db_t s_bas_service_db = {
.attr_db = s_bas_attr_db,
.attr_num = sizeof(s_bas_attr_db) / sizeof(gatt_attr_db_t),
};
static gatt_attr_db_t s_iot_attr_db[] = {
/* Private IOT Service - 0xFF00 */
GATT_H_PRIMARY_SERVICE(BT_UUID_DECLARE_16(0xFF00), IOT_SERVICE_ID),
/* Private Characteristic for TX - 0xFF01 */
GATT_H_CHARACTERISTIC_AUTO_RSP(BT_UUID_DECLARE_16(0xFF01), GATT_PROP_NOTIFY | GATT_PROP_INDICATE, 0, NULL, 0, IOT_SERVICE_TX_CHR_ID),
/* Client Characteristic Configuration Descriptor - 0x2902 */
GATT_H_CCCD(GATT_PERM_READ | GATT_PERM_WRITE | GATT_PERM_AUTHEN_REQUIRED, tx_char_ccc_changed, IOT_SERVICE_TX_CHR_CCC_ID),
/* Private Characteristic for RX - 0xFF02 */
GATT_H_CHARACTERISTIC_USER_RSP(BT_UUID_DECLARE_16(0xFF02), GATT_PROP_READ | GATT_PROP_WRITE_NR, GATT_PERM_READ | GATT_PERM_WRITE, rx_char_on_read, rx_char_on_write, IOT_SERVICE_RX_CHR_ID),
/* Private Characteristic for read operation demo - 0xFF05 */
GATT_H_CHARACTERISTIC_AUTO_RSP(BT_UUID_DECLARE_16(0xFF05), GATT_PROP_READ, GATT_PERM_READ, read_char_value, sizeof(read_char_value), IOT_SERVICE_READ_CHR_ID),
};
static gatt_srv_db_t s_iot_service_db = {
.attr_db = s_iot_attr_db,
.attr_num = sizeof(s_iot_attr_db) / sizeof(gatt_attr_db_t),
};
static bt_command_t g_gatts_tables[] = {
{ "register", register_cmd, 0, "\"register gatt service(DIS = 1, BAS = 2, CUSTOM = 3) :<id>\"" },
{ "unregister", unregister_cmd, 0, "\"unregister gatt service :<id>\"" },
{ "start", start_cmd, 0, "\"start gatt service :<id>\"" },
{ "stop", stop_cmd, 0, "\"stop gatt service :<id>\"" },
{ "connect", connect_cmd, 0, "\"connect remote device :<id><address>[addr type(0:public,1:random,2:public_id,3:random_id)]\"" },
{ "connect_bear", connect_bear_cmd, 0, "\"connect remote device :<conn id><address><bear type(1:le att, 2:le eatt, 3:bredr att, 4:bredr eatt)>\"" },
{ "disconnect", disconnect_cmd, 0, "\"disconnect remote device :<id><address>\"" },
{ "notify_battery", notify_bas_cmd, 0, "\"send battery notification :<address><level>(0-100)\"" },
{ "notify_custom", notify_cus_cmd, 0, "\"send custom notification :<address><playload>\"" },
{ "indicate_custom", indicate_cus_cmd, 0, "\"send custom indication :<address><playload>\"" },
{ "read_phy", read_phy_cmd, 0, "\"read phy :<id><address>\"" },
{ "update_phy", update_phy_cmd, 0, "\"update phy(0: 1M, 1: 2M, 3: LE_Coded) :<id><address><tx><rx>\"" },
{ "throughput", throughput_cmd, 0, "\"throughput test :<address><seconds>\"" },
};
static void usage(void)
{
printf("Usage:\n");
printf("\taddress: peer device address like 00:01:02:03:04:05\n");
printf("Commands:\n");
for (int i = 0; i < ARRAY_SIZE(g_gatts_tables); i++) {
printf("\t%-8s\t%s\n", g_gatts_tables[i].cmd, g_gatts_tables[i].help);
}
}
static gatts_device_t* find_gatts_device(bt_address_t* addr)
{
struct list_node* node;
list_for_every(&gatts_device_list, node)
{
gatts_device_t* device = (gatts_device_t*)node;
if (!bt_addr_compare(&device->remote_address, addr)) {
return device;
}
}
return NULL;
}
static gatts_device_t* add_gatts_device(bt_address_t* addr)
{
gatts_device_t* device = (gatts_device_t*)malloc(sizeof(gatts_device_t));
if (!device) {
PRINT("malloc device failed!");
return NULL;
}
memcpy(&device->remote_address, addr, sizeof(bt_address_t));
device->gatt_mtu = 23;
list_add_tail(&gatts_device_list, &device->node);
return device;
}
static void remove_gatts_device(gatts_device_t* device)
{
if (device) {
list_delete(&device->node);
free(device);
}
}
static int connect_cmd(void* handle, int argc, char* argv[])
{
ble_addr_type_t addr_type = BT_LE_ADDR_TYPE_RANDOM;
if (argc < 2)
return CMD_PARAM_NOT_ENOUGH;
bt_address_t addr;
if (bt_addr_str2ba(argv[1], &addr) < 0)
return CMD_INVALID_ADDR;
gatts_handle_t service_handle;
int service_id = atoi(argv[0]);
GET_SERVICE_HANDLE(service_id, service_handle)
if (argc >= 3) {
addr_type = atoi(argv[2]);
if (addr_type > BT_LE_ADDR_TYPE_ANONYMOUS || addr_type < BT_LE_ADDR_TYPE_PUBLIC) {
return CMD_INVALID_OPT;
}
}
if (bt_gatts_connect(service_handle, &addr, addr_type) != BT_STATUS_SUCCESS)
return CMD_ERROR;
return CMD_OK;
}
static int connect_bear_cmd(void* handle, int argc, char* argv[])
{
if (argc < 3)
return CMD_PARAM_NOT_ENOUGH;
bt_address_t addr;
if (bt_addr_str2ba(argv[1], &addr) < 0)
return CMD_INVALID_ADDR;
gatts_handle_t service_handle;
int service_id = atoi(argv[0]);
GET_SERVICE_HANDLE(service_id, service_handle)
uint8_t bear_type = atoi(argv[2]);
if (bear_type <= ATT_BEAR_TYPE_NONE || bear_type >= ATT_BEAR_TYPE_ANY)
return CMD_INVALID_ADDR;
if (bt_gatts_connect_bear(service_handle, &addr, BT_LE_ADDR_TYPE_UNKNOWN, bear_type) != BT_STATUS_SUCCESS)
return CMD_ERROR;
return CMD_OK;
}
static int disconnect_cmd(void* handle, int argc, char* argv[])
{
if (argc < 2)
return CMD_PARAM_NOT_ENOUGH;
bt_address_t addr;
if (bt_addr_str2ba(argv[1], &addr) < 0)
return CMD_INVALID_ADDR;
gatts_handle_t service_handle;
int service_id = atoi(argv[0]);
GET_SERVICE_HANDLE(service_id, service_handle)
gatts_device_t* device = find_gatts_device(&addr);
if (!device) {
PRINT_ADDR("device:%s is not connected", &addr);
return CMD_INVALID_ADDR;
}
if (bt_gatts_disconnect(service_handle, &addr) != BT_STATUS_SUCCESS)
return CMD_ERROR;
return CMD_OK;
}
static int start_cmd(void* handle, int argc, char* argv[])
{
if (argc < 1)
return CMD_PARAM_NOT_ENOUGH;
gatts_handle_t service_handle;
gatt_srv_db_t* service_db;
int service_id = atoi(argv[0]);
switch (service_id) {
case GATT_SERVICE_DIS:
service_handle = g_dis_handle;
service_db = &s_dis_service_db;
break;
case GATT_SERVICE_BAS:
service_handle = g_bas_handle;
service_db = &s_bas_service_db;
break;
case GATT_SERVICE_CUSTOM:
service_handle = g_custom_handle;
service_db = &s_iot_service_db;
break;
default:
PRINT("invalid service id: %d", service_id);
return CMD_INVALID_OPT;
}
if (!service_handle) {
PRINT("service[%d] is not registered!", service_id);
return CMD_ERROR;
}
if (bt_gatts_add_attr_table(service_handle, service_db) != BT_STATUS_SUCCESS)
return CMD_ERROR;
return CMD_OK;
}
static int stop_cmd(void* handle, int argc, char* argv[])
{
if (argc < 1)
return CMD_PARAM_NOT_ENOUGH;
gatts_handle_t service_handle;
uint16_t attr_handle;
int service_id = atoi(argv[0]);
switch (service_id) {
case GATT_SERVICE_DIS:
service_handle = g_dis_handle;
attr_handle = DIS_SERVICE_ID;
break;
case GATT_SERVICE_BAS:
service_handle = g_bas_handle;
attr_handle = BAS_SERVICE_ID;
break;
case GATT_SERVICE_CUSTOM:
service_handle = g_custom_handle;
attr_handle = IOT_SERVICE_ID;
break;
default:
PRINT("invalid service id: %d", service_id);
return CMD_INVALID_OPT;
}
if (!service_handle) {
PRINT("service[%d] is not registered!", service_id);
return CMD_ERROR;
}
if (bt_gatts_remove_attr_table(service_handle, attr_handle) != BT_STATUS_SUCCESS)
return CMD_ERROR;
return CMD_OK;
}
static int notify_bas_cmd(void* handle, int argc, char* argv[])
{
if (argc < 2)
return CMD_PARAM_NOT_ENOUGH;
bt_address_t addr;
if (bt_addr_str2ba(argv[0], &addr) < 0)
return CMD_INVALID_ADDR;
gatts_device_t* device = find_gatts_device(&addr);
if (!device) {
PRINT_ADDR("device:%s is not connected", &addr);
return CMD_INVALID_ADDR;
}
int new_level = atoi(argv[1]);
if (new_level < 0 || new_level > 100) {
PRINT("invalid battery level: %d", new_level);
return CMD_INVALID_OPT;
}
if (!g_bas_handle) {
PRINT("battery service is not registered!");
return CMD_ERROR;
}
battery_level = new_level;
if (bt_gatts_set_attr_value(g_bas_handle, BAS_BATTERY_LEVEL_CHR_ID, (uint8_t*)&battery_level, sizeof(battery_level)) != BT_STATUS_SUCCESS)
return CMD_ERROR;
if (bt_gatts_notify(g_bas_handle, &addr, BAS_BATTERY_LEVEL_CHR_ID, (uint8_t*)&battery_level, sizeof(battery_level)) != BT_STATUS_SUCCESS)
return CMD_ERROR;
return CMD_OK;
}
static int notify_cus_cmd(void* handle, int argc, char* argv[])
{
if (argc < 2)
return CMD_PARAM_NOT_ENOUGH;
bt_address_t addr;
if (bt_addr_str2ba(argv[0], &addr) < 0)
return CMD_INVALID_ADDR;
gatts_device_t* device = find_gatts_device(&addr);
if (!device) {
PRINT_ADDR("device:%s is not connected", &addr);
return CMD_INVALID_ADDR;
}
if (!g_custom_handle) {
PRINT("custom service is not registered!");
return CMD_ERROR;
}
if (bt_gatts_notify(g_custom_handle, &addr, IOT_SERVICE_TX_CHR_ID, (uint8_t*)argv[1], strlen(argv[1])) != BT_STATUS_SUCCESS)
return CMD_ERROR;
return CMD_OK;
}
static int indicate_cus_cmd(void* handle, int argc, char* argv[])
{
if (argc < 2)
return CMD_PARAM_NOT_ENOUGH;
bt_address_t addr;
if (bt_addr_str2ba(argv[0], &addr) < 0)
return CMD_INVALID_ADDR;
gatts_device_t* device = find_gatts_device(&addr);
if (!device) {
PRINT_ADDR("device:%s is not connected", &addr);
return CMD_INVALID_ADDR;
}
if (!g_custom_handle) {
PRINT("custom service is not registered!");
return CMD_ERROR;
}
if (bt_gatts_indicate(g_custom_handle, &addr, IOT_SERVICE_TX_CHR_ID, (uint8_t*)argv[1], strlen(argv[1])) != BT_STATUS_SUCCESS)
return CMD_ERROR;
return CMD_OK;
}
static int read_phy_cmd(void* handle, int argc, char* argv[])
{
if (argc < 2)
return CMD_PARAM_NOT_ENOUGH;
bt_address_t addr;
if (bt_addr_str2ba(argv[1], &addr) < 0)
return CMD_INVALID_ADDR;
gatts_handle_t service_handle;
int service_id = atoi(argv[0]);
GET_SERVICE_HANDLE(service_id, service_handle)
gatts_device_t* device = find_gatts_device(&addr);
if (!device) {
PRINT_ADDR("device:%s is not connected", &addr);
return CMD_INVALID_ADDR;
}
if (bt_gatts_read_phy(service_handle, &addr) != BT_STATUS_SUCCESS)
return CMD_ERROR;
return CMD_OK;
}
static int update_phy_cmd(void* handle, int argc, char* argv[])
{
if (argc < 4)
return CMD_PARAM_NOT_ENOUGH;
int tx = atoi(argv[2]);
int rx = atoi(argv[3]);
bt_address_t addr;
if (bt_addr_str2ba(argv[1], &addr) < 0)
return CMD_INVALID_ADDR;
gatts_handle_t service_handle;
int service_id = atoi(argv[0]);
GET_SERVICE_HANDLE(service_id, service_handle)
gatts_device_t* device = find_gatts_device(&addr);
if (!device) {
PRINT_ADDR("device:%s is not connected", &addr);
return CMD_INVALID_ADDR;
}
if (bt_gatts_update_phy(service_handle, &addr, tx, rx) != BT_STATUS_SUCCESS)
return CMD_ERROR;
return CMD_OK;
}
static int throughput_cmd(void* handle, int argc, char* argv[])
{
if (argc < 2)
return CMD_PARAM_NOT_ENOUGH;
bt_address_t addr;
if (bt_addr_str2ba(argv[0], &addr) < 0)
return CMD_INVALID_ADDR;
int32_t test_time = atoi(argv[1]);
if (test_time <= 0)
return CMD_INVALID_OPT;
if (!g_custom_handle) {
PRINT("please register and start custom service at first !");
return CMD_ERROR;
}
gatts_device_t* device = find_gatts_device(&addr);
if (!device) {
PRINT_ADDR("device:%s is not connected", &addr);
return CMD_INVALID_ADDR;
}
if (!cccd_enable) {
PRINT("please enable cccd of custom tx char at first !");
return CMD_ERROR;
}
uint32_t notify_length = device->gatt_mtu;
uint8_t* payload = (uint8_t*)malloc(sizeof(uint8_t) * notify_length);
if (!payload) {
PRINT("notify payload malloc failed");
return CMD_ERROR;
}
int32_t run_time = 0;
uint32_t notify_count = 0;
uint32_t bit_rate = 0;
struct timespec start_ts;
clock_gettime(CLOCK_BOOTTIME, &start_ts);
throughtput_cursor = 0;
PRINT("gatts notify throughput test start, mtu = %" PRIu32 ", time = %" PRId32 "s.", notify_length, test_time);
while (1) {
struct timespec current_ts;
clock_gettime(CLOCK_BOOTTIME, &current_ts);
if (run_time < (current_ts.tv_sec - start_ts.tv_sec)) {
run_time = (current_ts.tv_sec - start_ts.tv_sec);
bit_rate = notify_length * notify_count / run_time;
PRINT("gatts notify Bit rate = %" PRIu32 " Byte/s, = %" PRIu32 " bit/s, time = %" PRId32 "s.", bit_rate, bit_rate << 3, run_time);
}
device = find_gatts_device(&addr);
if (!device || run_time >= test_time) {
break;
}
if (throughtput_cursor >= THROUGHTPUT_HORIZON) {
usleep(500);
continue;
}
memset(payload, notify_count & 0xFF, notify_length);
int ret = bt_gatts_notify(g_custom_handle, &addr, IOT_SERVICE_TX_CHR_ID, payload, notify_length);
if (ret != BT_STATUS_SUCCESS) {
PRINT("notify failed, ret: %d.", ret);
break;
}
throughtput_cursor++;
notify_count++;
}
free(payload);
if (run_time <= 0) {
PRINT("gatts notify throughput test failed due to an unexpected interruption!");
return CMD_ERROR;
}
bit_rate = notify_length * notify_count / run_time;
PRINT("gatts notify throughput test finish, Bit rate = %" PRIu32 " Byte/s, = %" PRIu32 " bit/s, time = %" PRId32 "s.",
bit_rate, bit_rate << 3, run_time);
return CMD_OK;
}
static void connect_callback(void* srv_handle, bt_address_t* addr)
{
PRINT_ADDR("gatts_connect_callback, addr:%s", addr);
add_gatts_device(addr);
}
static void disconnect_callback(void* srv_handle, bt_address_t* addr)
{
gatts_device_t* device = find_gatts_device(addr);
remove_gatts_device(device);
PRINT_ADDR("gatts_disconnect_callback, addr:%s", addr);
}
static void attr_table_added_callback(void* srv_handle, gatt_status_t status, uint16_t attr_handle)
{
PRINT("gatts add attribute table complete, handle 0x%" PRIx16 ", status:%d", attr_handle, status);
}
static void attr_table_removed_callback(void* srv_handle, gatt_status_t status, uint16_t attr_handle)
{
PRINT("gatts remove attribute table complete, handle 0x%" PRIx16 ", status:%d", attr_handle, status);
}
static void notify_complete_callback(void* srv_handle, bt_address_t* addr, gatt_status_t status, uint16_t attr_handle)
{
if (status != GATT_STATUS_SUCCESS) {
PRINT_ADDR("gatts service notify failed, addr:%s, handle 0x%" PRIx16 ", status:%d", addr, attr_handle, status);
return;
}
if (throughtput_cursor) {
throughtput_cursor--;
} else {
PRINT_ADDR("gatts service notify complete, addr:%s, handle 0x%" PRIx16 ", status:%d", addr, attr_handle, status);
}
}
static void mtu_changed_callback(void* srv_handle, bt_address_t* addr, uint32_t mtu)
{
gatts_device_t* device = find_gatts_device(addr);
if (device) {
device->gatt_mtu = mtu;
}
PRINT_ADDR("gatts_mtu_changed_callback, addr:%s, mtu:%" PRIu32, addr, mtu);
}
static void phy_read_callback(void* srv_handle, bt_address_t* addr, ble_phy_type_t tx_phy, ble_phy_type_t rx_phy)
{
PRINT_ADDR("gatts read phy complete, addr:%s, tx:%d, rx:%d", addr, tx_phy, rx_phy);
}
static void phy_updated_callback(void* srv_handle, bt_address_t* addr, gatt_status_t status, ble_phy_type_t tx_phy, ble_phy_type_t rx_phy)
{
PRINT_ADDR("gatts phy updated, addr:%s, status:%d, tx:%d, rx:%d", addr, status, tx_phy, rx_phy);
}
static void conn_param_changed_callback(void* srv_handle, bt_address_t* addr, uint16_t connection_interval,
uint16_t peripheral_latency, uint16_t supervision_timeout)
{
PRINT_ADDR("gatts_conn_param_changed_callback, addr:%s, interval:%" PRIu16 ", latency:%" PRIu16 ", timeout:%" PRIu16,
addr, connection_interval, peripheral_latency, supervision_timeout);
}
static gatts_callbacks_t gatts_cbs = {
sizeof(gatts_cbs),
connect_callback,
disconnect_callback,
attr_table_added_callback,
attr_table_removed_callback,
notify_complete_callback,
mtu_changed_callback,
phy_read_callback,
phy_updated_callback,
conn_param_changed_callback,
};
static int register_cmd(void* handle, int argc, char* argv[])
{
int ret = 0;
if (argc < 1)
return CMD_PARAM_NOT_ENOUGH;
int service_id = atoi(argv[0]);
switch (service_id) {
case GATT_SERVICE_DIS:
if (g_dis_handle) {
PRINT("dis has registed, please unregister then try again");
return CMD_OK;
}
ret = bt_gatts_register_service(handle, &g_dis_handle, &gatts_cbs);
break;
case GATT_SERVICE_BAS:
if (g_bas_handle) {
PRINT("bas has registed, please unregister then try again");
return CMD_OK;
}
ret = bt_gatts_register_service(handle, &g_bas_handle, &gatts_cbs);
break;
case GATT_SERVICE_CUSTOM:
if (g_custom_handle) {
PRINT("custom service has registed, please unregister then try again");
return CMD_OK;
}
ret = bt_gatts_register_service(handle, &g_custom_handle, &gatts_cbs);
break;
default:
PRINT("invalid service id: %d", service_id);
return CMD_INVALID_OPT;
}
if (ret != BT_STATUS_SUCCESS)
return CMD_ERROR;
PRINT("register service successful, service_id: %d", service_id);
return CMD_OK;
}
static int unregister_cmd(void* handle, int argc, char* argv[])
{
if (argc < 1)
return CMD_PARAM_NOT_ENOUGH;
gatts_handle_t service_handle;
int service_id = atoi(argv[0]);
GET_SERVICE_HANDLE(service_id, service_handle)
if (bt_gatts_unregister_service(service_handle) != BT_STATUS_SUCCESS)
return CMD_ERROR;
PRINT("unregister service successful, service_id: %d", service_id);
return CMD_OK;
}
int gatts_command_init(void* handle)
{
return 0;
}
int gatts_command_uninit(void* handle)
{
if (g_dis_handle) {
bt_gatts_unregister_service(g_dis_handle);
}
if (g_bas_handle) {
bt_gatts_unregister_service(g_bas_handle);
}
if (g_custom_handle) {
bt_gatts_unregister_service(g_custom_handle);
}
return 0;
}
int gatts_command_exec(void* handle, int argc, char* argv[])
{
int ret = CMD_USAGE_FAULT;
if (argc > 0)
ret = execute_command_in_table(handle, g_gatts_tables, ARRAY_SIZE(g_gatts_tables), argc, argv);
if (ret < 0)
usage();
return ret;
}