bool bt_gatt_server_send_notification(struct bt_gatt_server *server, uint16_t handle, const uint8_t *value, uint16_t length) { uint16_t pdu_len; uint8_t *pdu; bool result; if (!server || (length && !value)) return false; pdu_len = MIN(bt_att_get_mtu(server->att) - 1, length + 2); pdu = malloc(pdu_len); if (!pdu) return false; put_le16(handle, pdu); memcpy(pdu + 2, value, pdu_len - 2); result = !!bt_att_send(server->att, BT_ATT_OP_HANDLE_VAL_NOT, pdu, pdu_len, NULL, NULL, NULL); free(pdu); return result; }
static void read_complete_cb(struct gatt_db_attribute *attr, int err, const uint8_t *value, size_t len, void *user_data) { struct async_read_op *op = user_data; struct bt_gatt_server *server = op->server; uint8_t rsp_opcode; uint16_t mtu; uint16_t handle; if (!server) { async_read_op_destroy(op); return; } mtu = bt_att_get_mtu(server->att); handle = gatt_db_attribute_get_handle(attr); if (err) { bt_att_send_error_rsp(server->att, op->opcode, handle, err); async_read_op_destroy(op); return; } rsp_opcode = get_read_rsp_opcode(op->opcode); bt_att_send(server->att, rsp_opcode, len ? value : NULL, MIN((unsigned) mtu - 1, len), NULL, NULL, NULL); async_read_op_destroy(op); }
int bt_gatt_write(struct bt_conn *conn, uint16_t handle, uint16_t offset, const void *data, uint16_t length, bt_gatt_rsp_func_t func) { struct net_buf *buf; struct bt_att_write_req *req; if (!conn || !handle || !func) { return -EINVAL; } /* Use Prepare Write if offset is set or Long Write is required */ if (offset || length > (bt_att_get_mtu(conn) - sizeof(*req) - 1)) { return gatt_prepare_write(conn, handle, offset, data, length, func); } buf = bt_att_create_pdu(conn, BT_ATT_OP_WRITE_REQ, sizeof(*req) + length); if (!buf) { return -ENOMEM; } req = net_buf_add(buf, sizeof(*req)); req->handle = sys_cpu_to_le16(handle); memcpy(req->value, data, length); net_buf_add(buf, length); BT_DBG("handle 0x%04x length %u", handle, length); return gatt_send(conn, buf, att_write_rsp, func, NULL); }
uint16_t bt_gatt_server_get_mtu(struct bt_gatt_server *server) { if (!server || !server->att) return 0; return bt_att_get_mtu(server->att); }
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 read_multiple_cb(uint8_t opcode, const void *pdu, uint16_t length, void *user_data) { struct bt_gatt_server *server = user_data; struct gatt_db_attribute *attr; struct read_multiple_resp_data data; uint8_t ecode = BT_ATT_ERROR_UNLIKELY; size_t i = 0; data.handles = NULL; data.rsp_data = NULL; if (length < 4) { ecode = BT_ATT_ERROR_INVALID_PDU; goto error; } data.server = server; data.num_handles = length / 2; data.cur_handle = 0; data.mtu = bt_att_get_mtu(server->att); data.length = 0; data.rsp_data = malloc(data.mtu - 1); if (!data.rsp_data) goto error; data.handles = new0(uint16_t, data.num_handles); if (!data.handles) goto error; for (i = 0; i < data.num_handles; i++) data.handles[i] = get_le16(pdu + i * 2); util_debug(server->debug_callback, server->debug_data, "Read Multiple Req - %zu handles, 1st: 0x%04x", data.num_handles, data.handles[0]); attr = gatt_db_get_attribute(server->db, data.handles[0]); if (!attr) { ecode = BT_ATT_ERROR_INVALID_HANDLE; goto error; } if (gatt_db_attribute_read(attr, 0, opcode, server->att, read_multiple_complete_cb, &data)) return; error: read_multiple_resp_data_free(&data); bt_att_send_error_rsp(server->att, opcode, 0, ecode); }
static void read_by_type_read_complete_cb(struct gatt_db_attribute *attr, int err, const uint8_t *value, size_t len, void *user_data) { struct async_read_op *op = user_data; struct bt_gatt_server *server = op->server; uint16_t mtu; uint16_t handle; if (!server) { async_read_op_destroy(op); return; } mtu = bt_att_get_mtu(server->att); handle = gatt_db_attribute_get_handle(attr); /* Terminate the operation if there was an error */ if (err) { bt_att_send_error_rsp(server->att, BT_ATT_OP_READ_BY_TYPE_REQ, handle, err); async_read_op_destroy(op); return; } if (op->pdu_len == 0) { op->value_len = MIN(MIN((unsigned) mtu - 4, 253), len); op->pdu[0] = op->value_len + 2; op->pdu_len++; } else if (len != op->value_len) { op->done = true; goto done; } /* Stop if this would surpass the MTU */ if (op->pdu_len + op->value_len + 2 > (unsigned) mtu - 1) { op->done = true; goto done; } /* Encode the current value */ put_le16(handle, op->pdu + op->pdu_len); memcpy(op->pdu + op->pdu_len + 2, value, op->value_len); op->pdu_len += op->value_len + 2; if (op->pdu_len == (unsigned) mtu - 1) op->done = true; done: process_read_by_type(op); }
bool bt_gatt_server_send_indication(struct bt_gatt_server *server, uint16_t handle, const uint8_t *value, uint16_t length, bt_gatt_server_conf_func_t callback, void *user_data, bt_gatt_server_destroy_func_t destroy) { uint16_t pdu_len; uint8_t *pdu; struct ind_data *data; bool result; if (!server || (length && !value)) return false; pdu_len = MIN(bt_att_get_mtu(server->att) - 1, length + 2); pdu = malloc(pdu_len); if (!pdu) return false; data = new0(struct ind_data, 1); if (!data) { free(pdu); return false; } data->callback = callback; data->destroy = destroy; data->user_data = user_data; put_le16(handle, pdu); memcpy(pdu + 2, value, pdu_len - 2); result = !!bt_att_send(server->att, BT_ATT_OP_HANDLE_VAL_IND, pdu, pdu_len, conf_cb, data, destroy_ind_data); if (!result) destroy_ind_data(data); free(pdu); return result; }
static void att_read_rsp(struct bt_conn *conn, uint8_t err, const void *pdu, uint16_t length, void *user_data) { struct bt_gatt_read_params *params = user_data; BT_DBG("err 0x%02x", err); if (err) { params->func(conn, err, NULL, 0); goto done; } if (params->func(conn, 0, pdu, length) == BT_GATT_ITER_STOP) { goto done; } /* * Core Spec 4.2, Vol. 3, Part G, 4.8.1 * If the Characteristic Value is greater than (ATT_MTU – 1) octets * in length, the Read Long Characteristic Value procedure may be used * if the rest of the Characteristic Value is required. */ if (length < (bt_att_get_mtu(conn) - 1)) { goto done; } params->offset += length; /* Continue reading the attribute */ if (bt_gatt_read(conn, params) < 0) { params->func(conn, BT_ATT_ERR_UNLIKELY, NULL, 0); goto done; } return; done: if (params->destroy) { params->destroy(params); } }
static int gatt_prepare_write(struct bt_conn *conn, uint16_t handle, uint16_t offset, const void *data, uint16_t length, bt_gatt_rsp_func_t func) { struct net_buf *buf; struct bt_att_prepare_write_req *req; static struct prepare_write_data prep_data; uint16_t len; if (prep_data.func) { return -EBUSY; } len = min(length, bt_att_get_mtu(conn) - sizeof(*req) - 1); prep_data.func = func; prep_data.handle = handle; prep_data.offset = offset + len; prep_data.data = data + len; prep_data.length = length - len; buf = bt_att_create_pdu(conn, BT_ATT_OP_PREPARE_WRITE_REQ, sizeof(*req) + len); if (!buf) { return -ENOMEM; } req = net_buf_add(buf, sizeof(*req)); req->handle = sys_cpu_to_le16(handle); req->offset = sys_cpu_to_le16(offset); memcpy(req->value, data, len); net_buf_add(buf, len); BT_DBG("handle 0x%04x offset %u len %u", handle, offset, len); return gatt_send(conn, buf, att_prepare_write_rsp, &prep_data, NULL); }
static void find_info_cb(uint8_t opcode, const void *pdu, uint16_t length, void *user_data) { struct bt_gatt_server *server = user_data; uint16_t start, end; uint16_t mtu = bt_att_get_mtu(server->att); uint8_t rsp_pdu[mtu]; uint16_t rsp_len; uint8_t ecode = 0; uint16_t ehandle = 0; struct queue *q = NULL; if (length != 4) { ecode = BT_ATT_ERROR_INVALID_PDU; goto error; } q = queue_new(); if (!q) { ecode = BT_ATT_ERROR_INSUFFICIENT_RESOURCES; goto error; } start = get_le16(pdu); end = get_le16(pdu + 2); util_debug(server->debug_callback, server->debug_data, "Find Info - start: 0x%04x end: 0x%04x", start, end); if (!start || !end) { ecode = BT_ATT_ERROR_INVALID_HANDLE; goto error; } ehandle = start; if (start > end) { ecode = BT_ATT_ERROR_INVALID_HANDLE; goto error; } gatt_db_find_information(server->db, start, end, q); if (queue_isempty(q)) { ecode = BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND; goto error; } if (!encode_find_info_rsp(server->db, q, mtu, rsp_pdu, &rsp_len)) { ecode = BT_ATT_ERROR_UNLIKELY; goto error; } bt_att_send(server->att, BT_ATT_OP_FIND_INFO_RSP, rsp_pdu, rsp_len, NULL, NULL, NULL); queue_destroy(q, NULL); return; error: bt_att_send_error_rsp(server->att, opcode, ehandle, ecode); queue_destroy(q, NULL); }
static void read_by_type_cb(uint8_t opcode, const void *pdu, uint16_t length, void *user_data) { struct bt_gatt_server *server = user_data; uint16_t start, end; bt_uuid_t type; uint16_t ehandle = 0; uint8_t ecode; struct queue *q = NULL; struct async_read_op *op; if (length != 6 && length != 20) { ecode = BT_ATT_ERROR_INVALID_PDU; goto error; } q = queue_new(); if (!q) { ecode = BT_ATT_ERROR_INSUFFICIENT_RESOURCES; goto error; } start = get_le16(pdu); end = get_le16(pdu + 2); get_uuid_le(pdu + 4, length - 4, &type); util_debug(server->debug_callback, server->debug_data, "Read By Type - start: 0x%04x end: 0x%04x", start, end); if (!start || !end) { ecode = BT_ATT_ERROR_INVALID_HANDLE; goto error; } ehandle = start; if (start > end) { ecode = BT_ATT_ERROR_INVALID_HANDLE; goto error; } gatt_db_read_by_type(server->db, start, end, type, q); if (queue_isempty(q)) { ecode = BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND; goto error; } if (server->pending_read_op) { ecode = BT_ATT_ERROR_UNLIKELY; goto error; } op = new0(struct async_read_op, 1); if (!op) { ecode = BT_ATT_ERROR_INSUFFICIENT_RESOURCES; goto error; } op->pdu = malloc(bt_att_get_mtu(server->att)); if (!op->pdu) { free(op); ecode = BT_ATT_ERROR_INSUFFICIENT_RESOURCES; goto error; } op->opcode = opcode; op->server = server; op->db_data = q; server->pending_read_op = op; process_read_by_type(op); return; error: bt_att_send_error_rsp(server->att, opcode, ehandle, ecode); queue_destroy(q, NULL); }
static void read_by_grp_type_cb(uint8_t opcode, const void *pdu, uint16_t length, void *user_data) { struct bt_gatt_server *server = user_data; uint16_t start, end; bt_uuid_t type; bt_uuid_t prim, snd; uint16_t mtu = bt_att_get_mtu(server->att); uint8_t rsp_pdu[mtu]; uint16_t rsp_len; uint8_t ecode = 0; uint16_t ehandle = 0; struct queue *q = NULL; if (length != 6 && length != 20) { ecode = BT_ATT_ERROR_INVALID_PDU; goto error; } q = queue_new(); if (!q) { ecode = BT_ATT_ERROR_INSUFFICIENT_RESOURCES; goto error; } start = get_le16(pdu); end = get_le16(pdu + 2); get_uuid_le(pdu + 4, length - 4, &type); util_debug(server->debug_callback, server->debug_data, "Read By Grp Type - start: 0x%04x end: 0x%04x", start, end); if (!start || !end) { ecode = BT_ATT_ERROR_INVALID_HANDLE; goto error; } ehandle = start; if (start > end) { ecode = BT_ATT_ERROR_INVALID_HANDLE; goto error; } /* * GATT defines that only the <<Primary Service>> and * <<Secondary Service>> group types can be used for the * "Read By Group Type" request (Core v4.1, Vol 3, sec 2.5.3). Return an * error if any other group type is given. */ bt_uuid16_create(&prim, GATT_PRIM_SVC_UUID); bt_uuid16_create(&snd, GATT_SND_SVC_UUID); if (bt_uuid_cmp(&type, &prim) && bt_uuid_cmp(&type, &snd)) { ecode = BT_ATT_ERROR_UNSUPPORTED_GROUP_TYPE; goto error; } gatt_db_read_by_group_type(server->db, start, end, type, q); if (queue_isempty(q)) { ecode = BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND; goto error; } if (!encode_read_by_grp_type_rsp(server->db, q, server->att, mtu, rsp_pdu, &rsp_len)) { ecode = BT_ATT_ERROR_UNLIKELY; goto error; } queue_destroy(q, NULL); bt_att_send(server->att, BT_ATT_OP_READ_BY_GRP_TYPE_RSP, rsp_pdu, rsp_len, NULL, NULL, NULL); return; error: queue_destroy(q, NULL); bt_att_send_error_rsp(server->att, opcode, ehandle, ecode); }