uint16_t enc_prep_write_req(uint16_t handle, uint16_t offset, const uint8_t *value, size_t vlen, uint8_t *pdu, size_t len) { const uint16_t min_len = sizeof(pdu[0]) + sizeof(handle) + sizeof(offset); if (pdu == NULL) return 0; if (len < min_len) return 0; if (vlen > len - min_len) vlen = len - min_len; pdu[0] = ATT_OP_PREP_WRITE_REQ; att_put_u16(handle, &pdu[1]); att_put_u16(offset, &pdu[3]); if (vlen > 0) { memcpy(&pdu[5], value, vlen); return min_len + vlen; } return min_len; }
uint16_t enc_read_by_type_req(uint16_t start, uint16_t end, bt_uuid_t *uuid, uint8_t *pdu, size_t len) { uint16_t min_len = sizeof(pdu[0]) + sizeof(start) + sizeof(end); uint16_t length; if (!uuid) return 0; if (uuid->type == BT_UUID16) length = 2; else if (uuid->type == BT_UUID128) length = 16; else return 0; if (len < min_len + length) return 0; pdu[0] = ATT_OP_READ_BY_TYPE_REQ; att_put_u16(start, &pdu[1]); att_put_u16(end, &pdu[3]); att_put_uuid(*uuid, &pdu[5]); return min_len + length; }
uint16_t enc_read_by_type_req(uint16_t start, uint16_t end, bt_uuid_t *uuid, uint8_t *pdu, size_t len) { uint16_t uuid_len; if (!uuid) return 0; if (uuid->type == BT_UUID16) uuid_len = 2; else if (uuid->type == BT_UUID128) uuid_len = 16; else return 0; /* Attribute Opcode (1 octet) */ pdu[0] = ATT_OP_READ_BY_TYPE_REQ; /* Starting Handle (2 octets) */ att_put_u16(start, &pdu[1]); /* Ending Handle (2 octets) */ att_put_u16(end, &pdu[3]); /* Attribute Type (2 or 16 octet UUID) */ att_put_uuid(*uuid, &pdu[5]); return 5 + uuid_len; }
uint16_t enc_read_by_grp_req(uint16_t start, uint16_t end, uuid_t *uuid, uint8_t *pdu, int len) { const uint16_t min_len = sizeof(pdu[0]) + sizeof(start) + sizeof(end); uint16_t length; if (!uuid) return 0; if (uuid->type == SDP_UUID16) length = 2; else if (uuid->type == SDP_UUID128) length = 16; else return 0; if (len < min_len + length) return 0; pdu[0] = ATT_OP_READ_BY_GROUP_REQ; att_put_u16(start, &pdu[1]); att_put_u16(end, &pdu[3]); if (uuid->type == SDP_UUID16) att_put_u16(uuid->value.uuid16, &pdu[5]); else memcpy(&pdu[5], &uuid->value.uuid128, length); return min_len + length; }
uint16_t enc_find_by_type_req(uint16_t start, uint16_t end, bt_uuid_t *uuid, const uint8_t *value, size_t vlen, uint8_t *pdu, size_t len) { uint16_t min_len = sizeof(pdu[0]) + sizeof(start) + sizeof(end) + sizeof(uint16_t); if (pdu == NULL) return 0; if (!uuid) return 0; if (uuid->type != BT_UUID16) return 0; if (len < min_len) return 0; if (vlen > len - min_len) vlen = len - min_len; pdu[0] = ATT_OP_FIND_BY_TYPE_REQ; att_put_u16(start, &pdu[1]); att_put_u16(end, &pdu[3]); att_put_uuid16(*uuid, &pdu[5]); if (vlen > 0) { memcpy(&pdu[7], value, vlen); return min_len + vlen; } return min_len; }
static void discover_ccc_cb(guint8 status, const guint8 *pdu, guint16 len, gpointer user_data) { struct heartrate *hr = user_data; struct att_data_list *list; uint8_t format; int i; if (status != 0) { error("Discover Heart Rate Measurement descriptors failed: %s", att_ecode2str(status)); return; } list = dec_find_info_resp(pdu, len, &format); if (list == NULL) return; if (format != ATT_FIND_INFO_RESP_FMT_16BIT) goto done; for (i = 0; i < list->num; i++) { uint8_t *value; uint16_t handle, uuid; char *msg; uint8_t attr_val[2]; value = list->data[i]; handle = att_get_u16(value); uuid = att_get_u16(value + 2); if (uuid != GATT_CLIENT_CHARAC_CFG_UUID) continue; hr->measurement_ccc_handle = handle; if (g_slist_length(hr->hradapter->watchers) == 0) { att_put_u16(0x0000, attr_val); msg = g_strdup("Disable measurement"); } else { att_put_u16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, attr_val); msg = g_strdup("Enable measurement"); } gatt_write_char(hr->attrib, handle, attr_val, sizeof(attr_val), char_write_cb, msg); break; } done: att_data_list_free(list); }
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; }
uint16_t enc_find_info_req(uint16_t start, uint16_t end, uint8_t *pdu, size_t len) { if (pdu == NULL) return 0; /* Attribute Opcode (1 octet) */ pdu[0] = ATT_OP_FIND_INFO_REQ; /* Starting Handle (2 octets) */ att_put_u16(start, &pdu[1]); /* Ending Handle (2 octets) */ att_put_u16(end, &pdu[3]); return 5; }
uint16_t enc_read_blob_req(uint16_t handle, uint16_t offset, uint8_t *pdu, size_t len) { if (pdu == NULL) return 0; /* Attribute Opcode (1 octet) */ pdu[0] = ATT_OP_READ_BLOB_REQ; /* Attribute Handle (2 octets) */ att_put_u16(handle, &pdu[1]); /* Value Offset (2 octets) */ att_put_u16(offset, &pdu[3]); return 5; }
uint16_t enc_find_info_req(uint16_t start, uint16_t end, uint8_t *pdu, size_t len) { const uint16_t min_len = sizeof(pdu[0]) + sizeof(start) + sizeof(end); if (pdu == NULL) return 0; if (len < min_len) return 0; pdu[0] = ATT_OP_FIND_INFO_REQ; att_put_u16(start, &pdu[1]); att_put_u16(end, &pdu[3]); return min_len; }
static int encode_current_time(uint8_t value[10]) { struct timespec tp; struct tm tm; if (clock_gettime(CLOCK_REALTIME, &tp) == -1) { int err = -errno; error("clock_gettime: %s", strerror(-err)); return err; } if (localtime_r(&tp.tv_sec, &tm) == NULL) { error("localtime_r() failed"); /* localtime_r() does not set errno */ return -EINVAL; } att_put_u16(1900 + tm.tm_year, &value[0]); /* Year */ value[2] = tm.tm_mon + 1; /* Month */ value[3] = tm.tm_mday; /* Day */ value[4] = tm.tm_hour; /* Hours */ value[5] = tm.tm_min; /* Minutes */ value[6] = tm.tm_sec; /* Seconds */ value[7] = tm.tm_wday == 0 ? 7 : tm.tm_wday; /* Day of Week */ /* From Time Profile spec: "The number of 1/256 fractions of a second." * In 1s there are 256 fractions, in 1ns there are 256/10^9 fractions. * To avoid integer overflow, we use the equivalent 1/3906250 ratio. */ value[8] = tp.tv_nsec / 3906250; /* Fractions256 */ value[9] = 0x00; /* Adjust Reason */ return 0; }
uint16_t enc_read_blob_req(uint16_t handle, uint16_t offset, uint8_t *pdu, size_t len) { const uint16_t min_len = sizeof(pdu[0]) + sizeof(handle) + sizeof(offset); if (pdu == NULL) return 0; if (len < min_len) return 0; pdu[0] = ATT_OP_READ_BLOB_REQ; att_put_u16(handle, &pdu[1]); att_put_u16(offset, &pdu[3]); return min_len; }
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); }
uint16_t enc_find_by_type_resp(GSList *matches, uint8_t *pdu, int len) { GSList *l; uint16_t offset; if (pdu == NULL || len < 5) return 0; pdu[0] = ATT_OP_FIND_BY_TYPE_RESP; for (l = matches, offset = 1; l && len >= (offset + 4); l = l->next, offset += 4) { struct att_range *range = l->data; att_put_u16(range->start, &pdu[offset]); att_put_u16(range->end, &pdu[offset + 2]); } return offset; }
uint16_t enc_read_req(uint16_t handle, uint8_t *pdu, size_t len) { if (pdu == NULL) return 0; /* Attribute Opcode (1 octet) */ pdu[0] = ATT_OP_READ_REQ; /* Attribute Handle (2 octets) */ att_put_u16(handle, &pdu[1]); return 3; }
uint16_t enc_mtu_resp(uint16_t mtu, uint8_t *pdu, size_t len) { if (pdu == NULL) return 0; /* Attribute Opcode (1 octet) */ pdu[0] = ATT_OP_MTU_RESP; /* Server Rx MTU (2 octets) */ att_put_u16(mtu, &pdu[1]); return 3; }
uint16_t enc_error_resp(uint8_t opcode, uint16_t handle, uint8_t status, uint8_t *pdu, size_t len) { /* Attribute Opcode (1 octet) */ pdu[0] = ATT_OP_ERROR; /* Request Opcode In Error (1 octet) */ pdu[1] = opcode; /* Attribute Handle In Error (2 octets) */ att_put_u16(handle, &pdu[2]); /* Error Code (1 octet) */ pdu[4] = status; return 5; }
static void register_link_loss(struct btd_adapter *adapter) { uint16_t start_handle, h; const int svc_size = 3; uint8_t atval[256]; bt_uuid_t uuid; bt_uuid16_create(&uuid, LINK_LOSS_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(LINK_LOSS_SVC_UUID, &atval[0]); attrib_db_add(adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 2); /* Alert level characteristic */ bt_uuid16_create(&uuid, GATT_CHARAC_UUID); atval[0] = ATT_CHAR_PROPER_READ | ATT_CHAR_PROPER_WRITE; att_put_u16(h + 1, &atval[1]); att_put_u16(ALERT_LEVEL_CHR_UUID, &atval[3]); attrib_db_add(adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 5); /* Alert level value */ bt_uuid16_create(&uuid, ALERT_LEVEL_CHR_UUID); att_put_u8(NO_ALERT, &atval[0]); attrib_db_add(adapter, h++, &uuid, ATT_NONE, ATT_NONE, atval, 1); g_assert(h - start_handle == svc_size); }
uint16_t enc_mtu_resp(uint16_t mtu, uint8_t *pdu, size_t len) { const uint16_t min_len = sizeof(pdu[0]) + sizeof(mtu); if (pdu == NULL) return 0; if (len < min_len) return 0; pdu[0] = ATT_OP_MTU_RESP; att_put_u16(mtu, &pdu[1]); return min_len; }
uint16_t enc_read_req(uint16_t handle, uint8_t *pdu, int len) { const uint16_t min_len = sizeof(pdu[0]) + sizeof(handle); if (pdu == NULL) return 0; if (len < min_len) return 0; pdu[0] = ATT_OP_READ_REQ; att_put_u16(handle, &pdu[1]); return min_len; }
uint16_t enc_indication(struct attribute *a, uint8_t *pdu, int len) { const uint16_t min_len = sizeof(pdu[0]) + sizeof(uint16_t); if (pdu == NULL) return 0; if (len < (a->len + min_len)) return 0; pdu[0] = ATT_OP_HANDLE_IND; att_put_u16(a->handle, &pdu[1]); memcpy(&pdu[3], a->data, a->len); return a->len + min_len; }
static void disable_measurement(gpointer data, gpointer user_data) { struct heartrate *hr = data; uint16_t handle = hr->measurement_ccc_handle; uint8_t value[2]; char *msg; if (hr->attrib == NULL || !handle) return; att_put_u16(0x0000, value); msg = g_strdup("Disable measurement"); gatt_write_char(hr->attrib, handle, value, sizeof(value), char_write_cb, msg); }
static void enable_measurement(gpointer data, gpointer user_data) { struct heartrate *hr = data; uint16_t handle = hr->measurement_ccc_handle; uint8_t value[2]; char *msg; if (hr->attrib == NULL || !handle) return; att_put_u16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value); msg = g_strdup("Enable measurement"); gatt_write_char(hr->attrib, handle, value, sizeof(value), char_write_cb, msg); }
uint16_t enc_indicate(uint16_t handle, const uint8_t *value, int vlen, uint8_t *pdu, int len) { const uint16_t min_len = sizeof(pdu[0]) + sizeof(uint16_t); if (pdu == NULL || len < min_len) return 0; if (vlen + min_len > len) vlen = len - min_len; pdu[0] = ATT_OP_HANDLE_IND; att_put_u16(handle, &pdu[1]); memcpy(&pdu[3], value, vlen); return vlen + min_len; }
uint16_t enc_indication(uint16_t handle, uint8_t *value, size_t vlen, uint8_t *pdu, size_t len) { const uint16_t min_len = sizeof(pdu[0]) + sizeof(uint16_t); if (pdu == NULL) return 0; if (len < (vlen + min_len)) return 0; pdu[0] = ATT_OP_HANDLE_IND; att_put_u16(handle, &pdu[1]); memcpy(&pdu[3], value, vlen); return vlen + min_len; }
uint16_t enc_write_cmd(uint16_t handle, const uint8_t *value, size_t vlen, uint8_t *pdu, size_t len) { const uint16_t min_len = sizeof(pdu[0]) + sizeof(handle); if (pdu == NULL) return 0; if (vlen > len - min_len) vlen = len - min_len; pdu[0] = ATT_OP_WRITE_CMD; att_put_u16(handle, &pdu[1]); if (vlen > 0) { memcpy(&pdu[3], value, vlen); return min_len + vlen; } return min_len; }
static int encode_date_time(time_t time, struct date_time *date_time, struct day_of_week *day_of_week) { struct tm tm; if (localtime_r(&time, &tm) == NULL) { error("localtime_r() failed"); /* localtime_r() does not set errno */ return -EINVAL; } att_put_u16(1900 + tm.tm_year, &date_time->year); date_time->month = tm.tm_mon + 1; date_time->day = tm.tm_mday; date_time->hours = tm.tm_hour; date_time->minutes = tm.tm_min; date_time->seconds = tm.tm_sec; if (day_of_week) day_of_week->day = tm.tm_wday == 0 ? 7 : tm.tm_wday; return 0; }
static void register_manuf2_service(struct gatt_example_adapter *adapter, uint16_t range[2]) { const char *manufacturer_name2 = "ACME Weighing Scales"; const char *serial2 = "11267-2327A00239"; uint16_t start_handle, h; const int svc_size = 5; uint8_t atval[256]; bt_uuid_t uuid; int len; bt_uuid16_create(&uuid, MANUFACTURER_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: Manufacturer Service */ bt_uuid16_create(&uuid, GATT_SND_SVC_UUID); att_put_u16(MANUFACTURER_SVC_UUID, &atval[0]); attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 2); /* Manufacturer name 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(MANUFACTURER_NAME_UUID, &atval[3]); attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 5); /* Manufacturer name attribute */ bt_uuid16_create(&uuid, MANUFACTURER_NAME_UUID); len = strlen(manufacturer_name2); strncpy((char *) atval, manufacturer_name2, len); attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, len); /* Characteristic: serial number */ bt_uuid16_create(&uuid, GATT_CHARAC_UUID); atval[0] = ATT_CHAR_PROPER_READ; att_put_u16(h + 1, &atval[1]); att_put_u16(MANUFACTURER_SERIAL_UUID, &atval[3]); attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 5); /* Serial number characteristic value */ bt_uuid16_create(&uuid, MANUFACTURER_SERIAL_UUID); len = strlen(serial2); strncpy((char *) atval, serial2, len); attrib_db_add(adapter->adapter, h, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, len); g_assert(h - start_handle + 1 == svc_size); range[0] = start_handle; range[1] = start_handle + svc_size - 1; }
static void register_weight_service(struct gatt_example_adapter *adapter, const uint16_t vendor[2]) { const char *desc_weight = "Rucksack Weight"; const uint128_t char_weight_uuid_btorder = { .data = { 0x80, 0x88, 0xF2, 0x18, 0x90, 0x2C, 0x45, 0x0B, 0xB6, 0xC4, 0x62, 0x89, 0x1E, 0x8C, 0x25, 0xE9 } }; const uint128_t prim_weight_uuid_btorder = { .data = { 0x4F, 0x0A, 0xC0, 0x96, 0x35, 0xD4, 0x49, 0x11, 0x96, 0x31, 0xDE, 0xA8, 0xDC, 0x74, 0xEE, 0xFE } }; uint128_t prim_weight_uuid, char_weight_uuid; uint16_t start_handle, h; const int svc_size = 6; uint32_t sdp_handle; uint8_t atval[256]; bt_uuid_t uuid; int len; btoh128(&char_weight_uuid_btorder, &char_weight_uuid); btoh128(&prim_weight_uuid_btorder, &prim_weight_uuid); bt_uuid128_create(&uuid, prim_weight_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, vendor=0x%04x-0x%04x", start_handle, vendor[0], vendor[1]); h = start_handle; /* Weight service: primary service definition */ bt_uuid16_create(&uuid, GATT_PRIM_SVC_UUID); memcpy(atval, &prim_weight_uuid_btorder, 16); attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 16); if (vendor[0] && vendor[1]) { /* Weight: include */ bt_uuid16_create(&uuid, GATT_INCLUDE_UUID); att_put_u16(vendor[0], &atval[0]); att_put_u16(vendor[1], &atval[2]); att_put_u16(MANUFACTURER_SVC_UUID, &atval[4]); attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 6); } /* Weight: characteristic */ bt_uuid16_create(&uuid, GATT_CHARAC_UUID); atval[0] = ATT_CHAR_PROPER_READ; att_put_u16(h + 1, &atval[1]); memcpy(&atval[3], &char_weight_uuid_btorder, 16); attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 19); /* Weight: characteristic value */ bt_uuid128_create(&uuid, char_weight_uuid); atval[0] = 0x82; atval[1] = 0x55; atval[2] = 0x00; atval[3] = 0x00; attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 4); /* Weight: characteristic format */ bt_uuid16_create(&uuid, GATT_CHARAC_FMT_UUID); atval[0] = 0x08; atval[1] = 0xFD; att_put_u16(FMT_KILOGRAM_UUID, &atval[2]); att_put_u16(BLUETOOTH_SIG_UUID, &atval[4]); att_put_u16(FMT_HANGING_UUID, &atval[6]); attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 8); /* Weight: characteristic user description */ bt_uuid16_create(&uuid, GATT_CHARAC_USER_DESC_UUID); len = strlen(desc_weight); strncpy((char *) atval, desc_weight, len); attrib_db_add(adapter->adapter, h, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, len); g_assert(h - start_handle + 1 == svc_size); /* Add an SDP record for the above service */ sdp_handle = attrib_create_sdp(adapter->adapter, start_handle, "Weight Service"); if (sdp_handle) adapter->sdp_handles = g_slist_prepend(adapter->sdp_handles, GUINT_TO_POINTER(sdp_handle)); }
static int register_attributes(void) { const char *desc_out_temp = "Outside Temperature"; const char *desc_out_hum = "Outside Relative Humidity"; const char *desc_weight = "Rucksack Weight"; const char *manufacturer_name1 = "ACME Temperature Sensor"; const char *manufacturer_name2 = "ACME Weighing Scales"; const char *serial1 = "237495-3282-A"; const char *serial2 = "11267-2327A00239"; const unsigned char char_weight_uuid[] = { 0x80, 0x88, 0xF2, 0x18, 0x90, 0x2C, 0x45, 0x0B, 0xB6, 0xC4, 0x62, 0x89, 0x1E, 0x8C, 0x25, 0xE9 }; const unsigned char prim_weight_uuid[] = { 0x4F, 0x0A, 0xC0, 0x96, 0x35, 0xD4, 0x49, 0x11, 0x96, 0x31, 0xDE, 0xA8, 0xDC, 0x74, 0xEE, 0xFE }; uint8_t atval[256]; uuid_t uuid; int len; /* Battery state service: primary service definition */ sdp_uuid16_create(&uuid, GATT_PRIM_SVC_UUID); att_put_u16(BATTERY_STATE_SVC_UUID, &atval[0]); attrib_db_add(0x0100, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 2); /* Battery: battery state characteristic */ sdp_uuid16_create(&uuid, GATT_CHARAC_UUID); atval[0] = ATT_CHAR_PROPER_READ; att_put_u16(0x0110, &atval[1]); att_put_u16(BATTERY_STATE_UUID, &atval[3]); attrib_db_add(0x0106, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 5); /* Battery: battery state attribute */ sdp_uuid16_create(&uuid, BATTERY_STATE_UUID); atval[0] = 0x04; attrib_db_add(0x0110, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 1); /* Battery: Client Characteristic Configuration */ sdp_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID); atval[0] = 0x00; atval[1] = 0x00; attrib_db_add(0x0111, &uuid, ATT_NONE, ATT_AUTHENTICATION, atval, 2); /* Thermometer: primary service definition */ sdp_uuid16_create(&uuid, GATT_PRIM_SVC_UUID); att_put_u16(THERM_HUMIDITY_SVC_UUID, &atval[0]); attrib_db_add(0x0200, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 2); /* Thermometer: Include */ sdp_uuid16_create(&uuid, GATT_INCLUDE_UUID); att_put_u16(0x0500, &atval[0]); att_put_u16(0x0504, &atval[2]); att_put_u16(MANUFACTURER_SVC_UUID, &atval[4]); attrib_db_add(0x0201, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 6); /* Thermometer: Include */ att_put_u16(0x0550, &atval[0]); att_put_u16(0x0568, &atval[2]); attrib_db_add(0x0202, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 4); /* Thermometer: temperature characteristic */ sdp_uuid16_create(&uuid, GATT_CHARAC_UUID); atval[0] = ATT_CHAR_PROPER_READ; att_put_u16(0x0204, &atval[1]); att_put_u16(TEMPERATURE_UUID, &atval[3]); attrib_db_add(0x0203, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 5); /* Thermometer: temperature characteristic value */ sdp_uuid16_create(&uuid, TEMPERATURE_UUID); atval[0] = 0x8A; atval[1] = 0x02; attrib_db_add(0x0204, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 2); /* Thermometer: temperature characteristic format */ sdp_uuid16_create(&uuid, GATT_CHARAC_FMT_UUID); atval[0] = 0x0E; atval[1] = 0xFE; att_put_u16(FMT_CELSIUS_UUID, &atval[2]); atval[4] = 0x01; att_put_u16(FMT_OUTSIDE_UUID, &atval[5]); attrib_db_add(0x0205, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 7); /* Thermometer: characteristic user description */ sdp_uuid16_create(&uuid, GATT_CHARAC_USER_DESC_UUID); len = strlen(desc_out_temp); strncpy((char *) atval, desc_out_temp, len); attrib_db_add(0x0206, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, len); /* Thermometer: relative humidity characteristic */ sdp_uuid16_create(&uuid, GATT_CHARAC_UUID); atval[0] = ATT_CHAR_PROPER_READ; att_put_u16(0x0212, &atval[1]); att_put_u16(RELATIVE_HUMIDITY_UUID, &atval[3]); attrib_db_add(0x0210, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 5); /* Thermometer: relative humidity value */ sdp_uuid16_create(&uuid, RELATIVE_HUMIDITY_UUID); atval[0] = 0x27; attrib_db_add(0x0212, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 1); /* Thermometer: relative humidity characteristic format */ sdp_uuid16_create(&uuid, GATT_CHARAC_FMT_UUID); atval[0] = 0x04; atval[1] = 0x00; att_put_u16(FMT_PERCENT_UUID, &atval[2]); att_put_u16(BLUETOOTH_SIG_UUID, &atval[4]); att_put_u16(FMT_OUTSIDE_UUID, &atval[6]); attrib_db_add(0x0213, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 8); /* Thermometer: characteristic user description */ sdp_uuid16_create(&uuid, GATT_CHARAC_USER_DESC_UUID); len = strlen(desc_out_hum); strncpy((char *) atval, desc_out_hum, len); attrib_db_add(0x0214, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, len); /* Secondary Service: Manufacturer Service */ sdp_uuid16_create(&uuid, GATT_SND_SVC_UUID); att_put_u16(MANUFACTURER_SVC_UUID, &atval[0]); attrib_db_add(0x0500, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 2); /* Manufacturer name characteristic definition */ sdp_uuid16_create(&uuid, GATT_CHARAC_UUID); atval[0] = ATT_CHAR_PROPER_READ; att_put_u16(0x0502, &atval[1]); att_put_u16(MANUFACTURER_NAME_UUID, &atval[3]); attrib_db_add(0x0501, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 5); /* Manufacturer name characteristic value */ sdp_uuid16_create(&uuid, MANUFACTURER_NAME_UUID); len = strlen(manufacturer_name1); strncpy((char *) atval, manufacturer_name1, len); attrib_db_add(0x0502, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, len); /* Manufacturer serial number characteristic */ sdp_uuid16_create(&uuid, GATT_CHARAC_UUID); atval[0] = ATT_CHAR_PROPER_READ; att_put_u16(0x0504, &atval[1]); att_put_u16(MANUFACTURER_SERIAL_UUID, &atval[3]); attrib_db_add(0x0503, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 5); /* Manufacturer serial number characteristic value */ sdp_uuid16_create(&uuid, MANUFACTURER_SERIAL_UUID); len = strlen(serial1); strncpy((char *) atval, serial1, len); attrib_db_add(0x0504, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, len); /* Secondary Service: Manufacturer Service */ sdp_uuid16_create(&uuid, GATT_SND_SVC_UUID); att_put_u16(MANUFACTURER_SVC_UUID, &atval[0]); attrib_db_add(0x0505, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 2); /* Manufacturer name characteristic definition */ sdp_uuid16_create(&uuid, GATT_CHARAC_UUID); atval[0] = ATT_CHAR_PROPER_READ; att_put_u16(0x0507, &atval[1]); att_put_u16(MANUFACTURER_NAME_UUID, &atval[3]); attrib_db_add(0x0506, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 5); /* Secondary Service: Vendor Specific Service */ sdp_uuid16_create(&uuid, GATT_SND_SVC_UUID); att_put_u16(VENDOR_SPECIFIC_SVC_UUID, &atval[0]); attrib_db_add(0x0550, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 2); /* Vendor Specific Type characteristic definition */ sdp_uuid16_create(&uuid, GATT_CHARAC_UUID); atval[0] = ATT_CHAR_PROPER_READ; att_put_u16(0x0568, &atval[1]); att_put_u16(VENDOR_SPECIFIC_TYPE_UUID, &atval[3]); attrib_db_add(0x0560, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 5); /* Vendor Specific Type characteristic value */ sdp_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(0x0568, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 6); /* Manufacturer name attribute */ sdp_uuid16_create(&uuid, MANUFACTURER_NAME_UUID); len = strlen(manufacturer_name2); strncpy((char *) atval, manufacturer_name2, len); attrib_db_add(0x0507, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, len); /* Characteristic: serial number */ sdp_uuid16_create(&uuid, GATT_CHARAC_UUID); atval[0] = ATT_CHAR_PROPER_READ; att_put_u16(0x0509, &atval[1]); att_put_u16(MANUFACTURER_SERIAL_UUID, &atval[3]); attrib_db_add(0x0508, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 5); /* Serial number characteristic value */ sdp_uuid16_create(&uuid, MANUFACTURER_SERIAL_UUID); len = strlen(serial2); strncpy((char *) atval, serial2, len); attrib_db_add(0x0509, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, len); /* Weight service: primary service definition */ sdp_uuid16_create(&uuid, GATT_PRIM_SVC_UUID); memcpy(atval, prim_weight_uuid, 16); attrib_db_add(0x0680, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 16); /* Weight: include */ sdp_uuid16_create(&uuid, GATT_INCLUDE_UUID); att_put_u16(0x0505, &atval[0]); att_put_u16(0x0509, &atval[2]); att_put_u16(MANUFACTURER_SVC_UUID, &atval[4]); attrib_db_add(0x0681, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 6); /* Weight: characteristic */ sdp_uuid16_create(&uuid, GATT_CHARAC_UUID); atval[0] = ATT_CHAR_PROPER_READ; att_put_u16(0x0683, &atval[1]); memcpy(&atval[3], char_weight_uuid, 16); attrib_db_add(0x0682, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 19); /* Weight: characteristic value */ sdp_uuid128_create(&uuid, char_weight_uuid); atval[0] = 0x82; atval[1] = 0x55; atval[2] = 0x00; atval[3] = 0x00; attrib_db_add(0x0683, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 4); /* Weight: characteristic format */ sdp_uuid16_create(&uuid, GATT_CHARAC_FMT_UUID); atval[0] = 0x08; atval[1] = 0xFD; att_put_u16(FMT_KILOGRAM_UUID, &atval[2]); att_put_u16(BLUETOOTH_SIG_UUID, &atval[4]); att_put_u16(FMT_HANGING_UUID, &atval[6]); attrib_db_add(0x0684, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 8); /* Weight: characteristic user description */ sdp_uuid16_create(&uuid, GATT_CHARAC_USER_DESC_UUID); len = strlen(desc_weight); strncpy((char *) atval, desc_weight, len); attrib_db_add(0x0685, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, len); return 0; }