static void populate_gatt_service(struct server *server) { bt_uuid_t uuid; struct gatt_db_attribute *service, *svc_chngd; /* Add the GATT service */ bt_uuid16_create(&uuid, UUID_GATT); service = gatt_db_add_service(server->db, &uuid, true, 4); bt_uuid16_create(&uuid, GATT_CHARAC_SERVICE_CHANGED); svc_chngd = gatt_db_service_add_characteristic(service, &uuid, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ | BT_GATT_CHRC_PROP_INDICATE, gatt_service_changed_cb, NULL, server); server->gatt_svc_chngd_handle = gatt_db_attribute_get_handle(svc_chngd); bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID); gatt_db_service_add_descriptor(service, &uuid, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE, gatt_svc_chngd_ccc_read_cb, gatt_svc_chngd_ccc_write_cb, server); gatt_db_service_set_active(service, true); }
bool bt_scpp_attach(struct bt_scpp *scan, void *attrib) { bt_uuid_t iwin_uuid, refresh_uuid; if (!scan || scan->attrib || !scan->primary) return false; scan->attrib = g_attrib_ref(attrib); if (scan->iwhandle) write_scan_params(scan->attrib, scan->iwhandle, scan->interval, scan->window); else { bt_uuid16_create(&iwin_uuid, SCAN_INTERVAL_WIN_UUID); discover_char(scan, scan->attrib, scan->primary->range.start, scan->primary->range.end, &iwin_uuid, iwin_discovered_cb, scan); } if (scan->refresh_handle) scan->refresh_cb_id = g_attrib_register(scan->attrib, ATT_OP_HANDLE_NOTIFY, scan->refresh_handle, refresh_value_cb, scan, NULL); else { bt_uuid16_create(&refresh_uuid, SCAN_REFRESH_UUID); discover_char(scan, scan->attrib, scan->primary->range.start, scan->primary->range.end, &refresh_uuid, refresh_discovered_cb, scan); } return true; }
static void populate_gap_service(struct server *server) { bt_uuid_t uuid; struct gatt_db_attribute *service, *tmp; uint16_t appearance; /* Add the GAP service */ bt_uuid16_create(&uuid, UUID_GAP); service = gatt_db_add_service(server->db, &uuid, true, 6); /* * Device Name characteristic. Make the value dynamically read and * written via callbacks. */ bt_uuid16_create(&uuid, GATT_CHARAC_DEVICE_NAME); gatt_db_service_add_characteristic(service, &uuid, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE, BT_GATT_CHRC_PROP_READ, gap_device_name_read_cb, gap_device_name_write_cb, server); bt_uuid16_create(&uuid, GATT_CHARAC_EXT_PROPER_UUID); gatt_db_service_add_descriptor(service, &uuid, BT_ATT_PERM_READ, gap_device_name_ext_prop_read_cb, NULL, server); /* * Appearance characteristic. Reads and writes should obtain the value * from the database. */ bt_uuid16_create(&uuid, GATT_CHARAC_APPEARANCE); tmp = gatt_db_service_add_characteristic(service, &uuid, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ, NULL, NULL, server); /* * Write the appearance value to the database, since we're not using a * callback. */ put_le16(128, &appearance); gatt_db_attribute_write(tmp, 0, (void *) &appearance, sizeof(appearance), BT_ATT_OP_WRITE_REQ, NULL, confirm_write, NULL); gatt_db_service_set_active(service, true); }
static void populate_hr_service(struct server *server) { bt_uuid_t uuid; struct gatt_db_attribute *service, *hr_msrmt, *body; uint8_t body_loc = 1; /* "Chest" */ /* Add Heart Rate Service */ bt_uuid16_create(&uuid, UUID_HEART_RATE); service = gatt_db_add_service(server->db, &uuid, true, 8); server->hr_handle = gatt_db_attribute_get_handle(service); /* HR Measurement Characteristic */ bt_uuid16_create(&uuid, UUID_HEART_RATE_MSRMT); hr_msrmt = gatt_db_service_add_characteristic(service, &uuid, BT_ATT_PERM_NONE, BT_GATT_CHRC_PROP_NOTIFY, NULL, NULL, NULL); server->hr_msrmt_handle = gatt_db_attribute_get_handle(hr_msrmt); bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID); gatt_db_service_add_descriptor(service, &uuid, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE, hr_msrmt_ccc_read_cb, hr_msrmt_ccc_write_cb, server); /* * Body Sensor Location Characteristic. Make reads obtain the value from * the database. */ bt_uuid16_create(&uuid, UUID_HEART_RATE_BODY); body = gatt_db_service_add_characteristic(service, &uuid, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ, NULL, NULL, server); gatt_db_attribute_write(body, 0, (void *) &body_loc, sizeof(body_loc), BT_ATT_OP_WRITE_REQ, NULL, confirm_write, NULL); /* HR Control Point Characteristic */ bt_uuid16_create(&uuid, UUID_HEART_RATE_CTRL); gatt_db_service_add_characteristic(service, &uuid, BT_ATT_PERM_WRITE, BT_GATT_CHRC_PROP_WRITE, NULL, hr_control_point_write_cb, server); if (server->hr_visible) gatt_db_service_set_active(service, true); }
static void register_vendor_service(struct gatt_example_adapter *adapter, uint16_t range[2]) { uint16_t start_handle, h; const int svc_size = 3; uint8_t atval[256]; bt_uuid_t uuid; bt_uuid16_create(&uuid, VENDOR_SPECIFIC_SVC_UUID); start_handle = attrib_db_find_avail(adapter->adapter, &uuid, svc_size); if (start_handle == 0) { error("Not enough free handles to register service"); return; } DBG("start_handle=0x%04x", start_handle); h = start_handle; /* Secondary Service: Vendor Specific Service */ bt_uuid16_create(&uuid, GATT_SND_SVC_UUID); att_put_u16(VENDOR_SPECIFIC_SVC_UUID, &atval[0]); attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 2); /* Vendor Specific Type characteristic definition */ bt_uuid16_create(&uuid, GATT_CHARAC_UUID); atval[0] = ATT_CHAR_PROPER_READ; att_put_u16(h + 1, &atval[1]); att_put_u16(VENDOR_SPECIFIC_TYPE_UUID, &atval[3]); attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 5); /* Vendor Specific Type characteristic value */ bt_uuid16_create(&uuid, VENDOR_SPECIFIC_TYPE_UUID); atval[0] = 0x56; atval[1] = 0x65; atval[2] = 0x6E; atval[3] = 0x64; atval[4] = 0x6F; atval[5] = 0x72; attrib_db_add(adapter->adapter, h, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 6); g_assert(h - start_handle + 1 == svc_size); range[0] = start_handle; range[1] = start_handle + svc_size - 1; }
static void refresh_discovered_cb(uint8_t status, GSList *chars, void *user_data) { struct scan *scan = user_data; struct gatt_char *chr; uint16_t start, end; bt_uuid_t uuid; if (status) { error("Scan Refresh %s", att_ecode2str(status)); return; } if (!chars) { DBG("Scan Refresh not supported"); return; } chr = chars->data; DBG("Scan Refresh handle: 0x%04x", chr->value_handle); start = chr->value_handle + 1; end = scan->range.end; if (start > end) return; scan->refresh_handle = chr->value_handle; bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID); gatt_discover_desc(scan->attrib, start, end, &uuid, discover_descriptor_cb, user_data); }
static int gap_accept(struct btd_service *service) { struct btd_device *device = btd_service_get_device(service); struct gatt_db *db = btd_device_get_gatt_db(device); struct bt_gatt_client *client = btd_device_get_gatt_client(device); struct gas *gas = btd_service_get_user_data(service); char addr[18]; bt_uuid_t gap_uuid; ba2str(device_get_address(device), addr); DBG("GAP profile accept (%s)", addr); if (!gas) { error("GAP service not handled by profile"); return -1; } gas->db = gatt_db_ref(db); gas->client = bt_gatt_client_ref(client); /* Handle the GAP services */ bt_uuid16_create(&gap_uuid, GAP_UUID16); gatt_db_foreach_service(db, &gap_uuid, foreach_gap_service, gas); if (!gas->attr) { error("GAP attribute not found"); gas_reset(gas); return -1; } btd_service_connecting_complete(service, 0); return 0; }
guint gatt_discover_char(GAttrib *attrib, uint16_t start, uint16_t end, bt_uuid_t *uuid, gatt_cb_t func, gpointer user_data) { size_t buflen; uint8_t *buf = g_attrib_get_buffer(attrib, &buflen); struct discover_char *dc; bt_uuid_t type_uuid; guint16 plen; bt_uuid16_create(&type_uuid, GATT_CHARAC_UUID); plen = enc_read_by_type_req(start, end, &type_uuid, buf, buflen); if (plen == 0) return 0; dc = g_try_new0(struct discover_char, 1); if (dc == NULL) return 0; dc->attrib = g_attrib_ref(attrib); dc->cb = func; dc->user_data = user_data; dc->end = end; dc->uuid = g_memdup(uuid, sizeof(bt_uuid_t)); return g_attrib_send(attrib, 0, buf, plen, char_discovered_cb, dc, NULL); }
static guint16 encode_discover_primary(uint16_t start, uint16_t end, bt_uuid_t *uuid, uint8_t *pdu, size_t len) { bt_uuid_t prim; guint16 plen; bt_uuid16_create(&prim, GATT_PRIM_SVC_UUID); if (uuid == NULL) { /* Discover all primary services */ plen = enc_read_by_grp_req(start, end, &prim, pdu, len); } else { uint8_t value[16]; size_t vlen; /* Discover primary service by service UUID */ put_uuid_le(uuid, value); vlen = bt_uuid_len(uuid); plen = enc_find_by_type_req(start, end, &prim, value, vlen, pdu, len); } return plen; }
bool gatt_db_attribute_get_service_uuid(const struct gatt_db_attribute *attrib, bt_uuid_t *uuid) { struct gatt_db_service *service; if (!attrib || !uuid) return false; service = attrib->service; if (service->attributes[0]->value_len == sizeof(uint16_t)) { uint16_t value; value = get_le16(service->attributes[0]->value); bt_uuid16_create(uuid, value); return true; } if (service->attributes[0]->value_len == sizeof(uint128_t)) { uint128_t value; bswap_128(service->attributes[0]->value, &value); bt_uuid128_create(uuid, value); return true; } return false; }
static void bas_discovered_cb(uint8_t status, GSList *chars, void *user_data) { struct gatt_request *req = user_data; struct bt_bas *bas = req->user_data; struct gatt_char *chr; uint16_t start, end; bt_uuid_t uuid; destroy_gatt_req(req); if (status) { error("Battery: %s", att_ecode2str(status)); return; } chr = chars->data; bas->handle = chr->value_handle; DBG("Battery handle: 0x%04x", bas->handle); read_char(bas, bas->attrib, bas->handle, read_value_cb, bas); start = chr->value_handle + 1; end = bas->primary->range.end; bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID); discover_desc(bas, bas->attrib, start, end, &uuid, discover_descriptor_cb, bas); }
static void external_report_reference_cb(guint8 status, const guint8 *pdu, guint16 plen, gpointer user_data) { struct hog_device *hogdev = user_data; uint16_t uuid16; bt_uuid_t uuid; if (status != 0) { error("Read External Report Reference descriptor failed: %s", att_ecode2str(status)); return; } if (plen != 3) { error("Malformed ATT read response"); return; } uuid16 = get_le16(&pdu[1]); DBG("External report reference read, external report characteristic " "UUID: 0x%04x", uuid16); bt_uuid16_create(&uuid, uuid16); gatt_discover_char(hogdev->attrib, 0x00, 0xff, &uuid, external_service_char_cb, hogdev); }
uint16_t dec_find_by_type_req(const uint8_t *pdu, size_t len, uint16_t *start, uint16_t *end, bt_uuid_t *uuid, uint8_t *value, size_t *vlen) { if (pdu == NULL) return 0; if (len < 7) return 0; /* Attribute Opcode (1 octet) */ if (pdu[0] != ATT_OP_FIND_BY_TYPE_REQ) return 0; /* First requested handle number (2 octets) */ *start = get_le16(&pdu[1]); /* Last requested handle number (2 octets) */ *end = get_le16(&pdu[3]); /* 16-bit UUID to find (2 octets) */ bt_uuid16_create(uuid, get_le16(&pdu[5])); /* Attribute value to find */ *vlen = len - 7; if (*vlen > 0) memcpy(value, pdu + 7, *vlen); return len; }
static void register_phone_alert_service(struct alert_adapter *al_adapter) { bt_uuid_t uuid; bt_uuid16_create(&uuid, PHONE_ALERT_STATUS_SVC_UUID); /* Phone Alert Status Service */ gatt_service_add(al_adapter->adapter, GATT_PRIM_SVC_UUID, &uuid, /* Alert Status characteristic */ GATT_OPT_CHR_UUID, ALERT_STATUS_CHR_UUID, GATT_OPT_CHR_PROPS, ATT_CHAR_PROPER_READ | ATT_CHAR_PROPER_NOTIFY, GATT_OPT_CHR_VALUE_CB, ATTRIB_READ, alert_status_read, al_adapter->adapter, GATT_OPT_CCC_GET_HANDLE, &al_adapter->hnd_ccc[NOTIFY_ALERT_STATUS], GATT_OPT_CHR_VALUE_GET_HANDLE, &al_adapter->hnd_value[NOTIFY_ALERT_STATUS], /* Ringer Control Point characteristic */ GATT_OPT_CHR_UUID, RINGER_CP_CHR_UUID, GATT_OPT_CHR_PROPS, ATT_CHAR_PROPER_WRITE_WITHOUT_RESP, GATT_OPT_CHR_VALUE_CB, ATTRIB_WRITE, ringer_cp_write, NULL, /* Ringer Setting characteristic */ GATT_OPT_CHR_UUID, RINGER_SETTING_CHR_UUID, GATT_OPT_CHR_PROPS, ATT_CHAR_PROPER_READ | ATT_CHAR_PROPER_NOTIFY, GATT_OPT_CHR_VALUE_CB, ATTRIB_READ, ringer_setting_read, al_adapter->adapter, GATT_OPT_CCC_GET_HANDLE, &al_adapter->hnd_ccc[NOTIFY_RINGER_SETTING], GATT_OPT_CHR_VALUE_GET_HANDLE, &al_adapter->hnd_value[NOTIFY_RINGER_SETTING], GATT_OPT_INVALID); }
static guint16 encode_discover_primary(uint16_t start, uint16_t end, bt_uuid_t *uuid, uint8_t *pdu, size_t len) { bt_uuid_t prim; guint16 plen; bt_uuid16_create(&prim, GATT_PRIM_SVC_UUID); if (uuid == NULL) { /* Discover all primary services */ plen = enc_read_by_grp_req(start, end, &prim, pdu, len); } else { uint16_t u16; uint128_t u128; const void *value; size_t vlen; /* Discover primary service by service UUID */ if (uuid->type == BT_UUID16) { u16 = htobs(uuid->value.u16); value = &u16; vlen = sizeof(u16); } else { htob128(&uuid->value.u128, &u128); value = &u128; vlen = sizeof(u128); } plen = enc_find_by_type_req(start, end, &prim, value, vlen, pdu, len); } return plen; }
static void populate_gap_service(struct gatt_db *db) { struct gatt_db_attribute *service; bt_uuid_t uuid; bt_uuid16_create(&uuid, UUID_GAP); service = gatt_db_add_service(db, &uuid, true, 6); bt_uuid16_create(&uuid, GATT_CHARAC_DEVICE_NAME); gatt_db_service_add_characteristic(service, &uuid, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ, gap_device_name_read, NULL, NULL); gatt_db_service_set_active(service, true); }
static void discover_desc(struct csc *csc, struct gatt_char *c, struct gatt_char *c_next) { struct characteristic *ch; uint16_t start, end; bt_uuid_t uuid; start = c->value_handle + 1; if (c_next != NULL) { if (start == c_next->handle) return; end = c_next->handle - 1; } else if (c->value_handle != csc->svc_range->end) { end = csc->svc_range->end; } else { return; } ch = g_new0(struct characteristic, 1); ch->csc = csc; memcpy(ch->uuid, c->uuid, sizeof(c->uuid)); bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID); gatt_discover_desc(csc->attrib, start, end, &uuid, discover_desc_cb, ch); }
static bool uuid_cmp(uint16_t u16, const bt_uuid_t *uuid) { bt_uuid_t lhs; bt_uuid16_create(&lhs, u16); return bt_uuid_cmp(&lhs, uuid) == 0; }
static void char_discovered_cb(uint8_t status, GSList *chars, void *user_data) { struct hog_device *hogdev = user_data; struct gatt_primary *prim = hogdev->hog_primary; bt_uuid_t report_uuid, report_map_uuid, info_uuid; bt_uuid_t proto_mode_uuid, ctrlpt_uuid; struct report *report; GSList *l; uint16_t info_handle = 0, proto_mode_handle = 0; if (status != 0) { const char *str = att_ecode2str(status); DBG("Discover all characteristics failed: %s", str); return; } bt_uuid16_create(&report_uuid, HOG_REPORT_UUID); bt_uuid16_create(&report_map_uuid, HOG_REPORT_MAP_UUID); bt_uuid16_create(&info_uuid, HOG_INFO_UUID); bt_uuid16_create(&proto_mode_uuid, HOG_PROTO_MODE_UUID); bt_uuid16_create(&ctrlpt_uuid, HOG_CONTROL_POINT_UUID); for (l = chars; l; l = g_slist_next(l)) { struct gatt_char *chr, *next; bt_uuid_t uuid; uint16_t start, end; chr = l->data; next = l->next ? l->next->data : NULL; DBG("0x%04x UUID: %s properties: %02x", chr->handle, chr->uuid, chr->properties); bt_string_to_uuid(&uuid, chr->uuid); start = chr->value_handle + 1; end = (next ? next->handle - 1 : prim->range.end); if (bt_uuid_cmp(&uuid, &report_uuid) == 0) { report = g_new0(struct report, 1); report->hogdev = hogdev; report->decl = g_memdup(chr, sizeof(*chr)); hogdev->reports = g_slist_append(hogdev->reports, report); discover_descriptor(hogdev->attrib, start, end, report); } else if (bt_uuid_cmp(&uuid, &report_map_uuid) == 0) {
static int uuid_desc16_cmp(bt_uuid_t *uuid, guint16 desc) { bt_uuid_t u16; bt_uuid16_create(&u16, desc); return bt_uuid_cmp(uuid, &u16); }
static bool uuid_cmp(const bt_uuid_t *uuid, uint16_t u16) { bt_uuid_t uuid16; bt_uuid16_create(&uuid16, u16); return bt_uuid_cmp(uuid, &uuid16) == 0; }
static void populate_devinfo_service(struct gatt_db *db) { struct gatt_db_attribute *service; bt_uuid_t uuid; bt_uuid16_create(&uuid, 0x180a); service = gatt_db_add_service(db, &uuid, true, 17); gatt_db_service_set_active(service, true); }
static void discover_immediate_handle(struct monitor *monitor) { struct att_range *immediate = monitor->immediate; bt_uuid_t uuid; bt_uuid16_create(&uuid, ALERT_LEVEL_CHR_UUID); gatt_discover_char(monitor->attrib, immediate->start, immediate->end, &uuid, immediate_handle_cb, monitor); }
static void register_tx_power(struct btd_adapter *adapter) { uint16_t start_handle, h; const int svc_size = 4; uint8_t atval[256]; bt_uuid_t uuid; bt_uuid16_create(&uuid, TX_POWER_SVC_UUID); start_handle = attrib_db_find_avail(adapter, &uuid, svc_size); if (start_handle == 0) { error("Not enough free handles to register service"); return; } DBG("start_handle=0x%04x", start_handle); h = start_handle; /* Primary service definition */ bt_uuid16_create(&uuid, GATT_PRIM_SVC_UUID); att_put_u16(TX_POWER_SVC_UUID, &atval[0]); attrib_db_add(adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 2); /* Power level characteristic */ bt_uuid16_create(&uuid, GATT_CHARAC_UUID); atval[0] = ATT_CHAR_PROPER_READ | ATT_CHAR_PROPER_NOTIFY; att_put_u16(h + 1, &atval[1]); att_put_u16(POWER_LEVEL_CHR_UUID, &atval[3]); attrib_db_add(adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 5); /* Power level value */ bt_uuid16_create(&uuid, POWER_LEVEL_CHR_UUID); att_put_u8(0x00, &atval[0]); attrib_db_add(adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 1); /* Client characteristic configuration */ bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID); atval[0] = 0x00; atval[1] = 0x00; attrib_db_add(adapter, h++, &uuid, ATT_NONE, ATT_NONE, atval, 2); g_assert(h - start_handle == svc_size); }
static void find_by_type_val_cb(uint8_t opcode, const void *pdu, uint16_t length, void *user_data) { struct bt_gatt_server *server = user_data; uint16_t start, end, uuid16; struct find_by_type_val_data data; uint16_t mtu = bt_att_get_mtu(server->att); uint8_t rsp_pdu[mtu]; uint16_t ehandle = 0; bt_uuid_t uuid; if (length < 6) { data.ecode = BT_ATT_ERROR_INVALID_PDU; goto error; } data.pdu = rsp_pdu; data.len = 0; data.mtu = mtu; data.ecode = 0; start = get_le16(pdu); end = get_le16(pdu + 2); uuid16 = get_le16(pdu + 4); util_debug(server->debug_callback, server->debug_data, "Find By Type Value - start: 0x%04x end: 0x%04x uuid: 0x%04x", start, end, uuid16); ehandle = start; if (start > end) { data.ecode = BT_ATT_ERROR_INVALID_HANDLE; goto error; } bt_uuid16_create(&uuid, uuid16); gatt_db_find_by_type_value(server->db, start, end, &uuid, pdu + 6, length - 6, find_by_type_val_att_cb, &data); if (!data.len) data.ecode = BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND; if (data.ecode) goto error; bt_att_send(server->att, BT_ATT_OP_FIND_BY_TYPE_VAL_RSP, data.pdu, data.len, NULL, NULL, NULL); return; error: bt_att_send_error_rsp(server->att, opcode, ehandle, data.ecode); }
static void attio_connected_cb(GAttrib *attrib, gpointer user_data) { struct scan *scan = user_data; bt_uuid_t iwin_uuid, refresh_uuid; scan->attrib = g_attrib_ref(attrib); if (scan->iwhandle) { write_scan_params(scan->attrib, scan->iwhandle); return; } bt_uuid16_create(&iwin_uuid, SCAN_INTERVAL_WIN_UUID); bt_uuid16_create(&refresh_uuid, SCAN_REFRESH_UUID); gatt_discover_char(scan->attrib, scan->range.start, scan->range.end, &iwin_uuid, iwin_discovered_cb, scan); gatt_discover_char(scan->attrib, scan->range.start, scan->range.end, &refresh_uuid, refresh_discovered_cb, scan); }
static void get_uuid(uint8_t type, const void *val, bt_uuid_t *uuid) { if (type == BT_UUID16) bt_uuid16_create(uuid, get_le16(val)); else { uint128_t u128; /* Convert from 128-bit LE to BE */ bswap_128(val, &u128); bt_uuid128_create(uuid, u128); } }
static int bt_string_to_uuid16(bt_uuid_t *uuid, const char *string) { uint16_t u16; char *endptr = NULL; u16 = strtol(string, &endptr, 16); if (endptr && (*endptr == '\0' || *endptr == '-')) { bt_uuid16_create(uuid, u16); return 0; } return -EINVAL; }
static void discover_external(GAttrib *attrib, uint16_t start, uint16_t end, gpointer user_data) { bt_uuid_t uuid; if (start > end) return; bt_uuid16_create(&uuid, GATT_EXTERNAL_REPORT_REFERENCE); gatt_discover_desc(attrib, start, end, NULL, discover_external_cb, user_data); }
static void register_services(struct time_adapter *ta) { bt_uuid_t curr_time_uuid; bt_uuid_t next_dst_uuid; bt_uuid16_create(&curr_time_uuid, CURRENT_TIME_SVC_UUID); /* Current Time Service */ gatt_service_add(ta->adapter, GATT_PRIM_SVC_UUID, &curr_time_uuid, /* CT Time characteristic */ GATT_OPT_CHR_UUID, CT_TIME_CHR_UUID, GATT_OPT_CHR_PROPS, ATT_CHAR_PROPER_READ | ATT_CHAR_PROPER_NOTIFY, GATT_OPT_CHR_VALUE_CB, ATTRIB_READ, current_time_read, ta, GATT_OPT_CCC_GET_HANDLE, &ta->h_curr_time_ccc, GATT_OPT_CHR_VALUE_GET_HANDLE, &ta->h_curr_time_value, /* Local Time Information characteristic */ GATT_OPT_CHR_UUID, LOCAL_TIME_INFO_CHR_UUID, GATT_OPT_CHR_PROPS, ATT_CHAR_PROPER_READ, GATT_OPT_CHR_VALUE_CB, ATTRIB_READ, local_time_info_read, ta, GATT_OPT_INVALID); bt_uuid16_create(&curr_time_uuid, NEXT_DST_CHANGE_SVC_UUID); /* Next DST Change Service */ gatt_service_add(ta->adapter, GATT_PRIM_SVC_UUID, &curr_time_uuid, /* Time with DST characteristic */ GATT_OPT_CHR_UUID, TIME_WITH_DATE_CHR_UUID, GATT_OPT_CHR_PROPS, ATT_CHAR_PROPER_READ, GATT_OPT_CHR_VALUE_CB, ATTRIB_READ, time_with_dst_read, ta, GATT_OPT_INVALID); }