mirror of
https://git.kernel.org/pub/scm/bluetooth/bluez.git
synced 2024-11-15 08:14:28 +08:00
shared/gatt-client: Add support for EATT features
This enables EATT in the Client Features if the EATT characteristic is present in the database.
This commit is contained in:
parent
0379b6c876
commit
76d63c91b2
@ -275,8 +275,9 @@ static void attrib_callback_result(uint8_t opcode, const void *pdu,
|
||||
free(buf);
|
||||
}
|
||||
|
||||
static void attrib_callback_notify(uint8_t opcode, const void *pdu,
|
||||
uint16_t length, void *user_data)
|
||||
static void attrib_callback_notify(struct bt_att_chan *chan, uint8_t opcode,
|
||||
const void *pdu, uint16_t length,
|
||||
void *user_data)
|
||||
{
|
||||
uint8_t *buf;
|
||||
struct attrib_callbacks *cb = user_data;
|
||||
|
@ -154,6 +154,9 @@ extern "C" {
|
||||
#define GATT_CHARAC_CLI_FEAT 0x2B29
|
||||
#define GATT_CHARAC_DB_HASH 0x2B2A
|
||||
|
||||
/* GATT Server Supported features */
|
||||
#define GATT_CHARAC_SERVER_FEAT 0x2B3A
|
||||
|
||||
typedef struct {
|
||||
enum {
|
||||
BT_UUID_UNSPEC = 0,
|
||||
|
@ -136,7 +136,7 @@ static struct gatt_conn *gatt_conn_new(int fd)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
conn->client = bt_gatt_client_new(gatt_cache, conn->att, mtu);
|
||||
conn->client = bt_gatt_client_new(gatt_cache, conn->att, mtu, 0);
|
||||
if (!conn->gatt) {
|
||||
fprintf(stderr, "Failed to create GATT client\n");
|
||||
bt_gatt_server_unref(conn->gatt);
|
||||
|
@ -4929,7 +4929,7 @@ static void gatt_client_init(struct btd_device *device)
|
||||
}
|
||||
|
||||
device->client = bt_gatt_client_new(device->db, device->att,
|
||||
device->att_mtu);
|
||||
device->att_mtu, 0);
|
||||
if (!device->client) {
|
||||
DBG("Failed to initialize");
|
||||
return;
|
||||
|
271
src/shared/att.c
271
src/shared/att.c
@ -56,6 +56,8 @@ struct bt_att_chan {
|
||||
uint8_t type;
|
||||
int sec_level; /* Only used for non-L2CAP */
|
||||
|
||||
struct queue *queue; /* Channel dedicated queue */
|
||||
|
||||
struct att_send_op *pending_req;
|
||||
struct att_send_op *pending_ind;
|
||||
bool writer_active;
|
||||
@ -375,32 +377,47 @@ static struct att_send_op *pick_next_send_op(struct bt_att_chan *chan)
|
||||
struct bt_att *att = chan->att;
|
||||
struct att_send_op *op;
|
||||
|
||||
/* See if any operations are already in the write queue */
|
||||
op = queue_pop_head(att->write_queue);
|
||||
/* Check if there is anything queued on the channel */
|
||||
op = queue_pop_head(chan->queue);
|
||||
if (op)
|
||||
return op;
|
||||
|
||||
/* See if any operations are already in the write queue */
|
||||
op = queue_peek_head(att->write_queue);
|
||||
if (op && op->len <= chan->mtu)
|
||||
return queue_pop_head(att->write_queue);
|
||||
|
||||
/* If there is no pending request, pick an operation from the
|
||||
* request queue.
|
||||
*/
|
||||
if (!chan->pending_req) {
|
||||
op = queue_pop_head(att->req_queue);
|
||||
if (op)
|
||||
return op;
|
||||
op = queue_peek_head(att->req_queue);
|
||||
if (op && op->len <= chan->mtu)
|
||||
return queue_pop_head(att->req_queue);
|
||||
}
|
||||
|
||||
/* There is either a request pending or no requests queued. If there is
|
||||
* no pending indication, pick an operation from the indication queue.
|
||||
*/
|
||||
if (!chan->pending_ind) {
|
||||
op = queue_pop_head(att->ind_queue);
|
||||
if (op)
|
||||
return op;
|
||||
op = queue_peek_head(att->ind_queue);
|
||||
if (op && op->len <= chan->mtu)
|
||||
return queue_pop_head(att->ind_queue);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void disc_att_send_op(void *data)
|
||||
{
|
||||
struct att_send_op *op = data;
|
||||
|
||||
if (op->callback)
|
||||
op->callback(BT_ATT_OP_ERROR_RSP, NULL, 0, op->user_data);
|
||||
|
||||
destroy_att_send_op(op);
|
||||
}
|
||||
|
||||
struct timeout_data {
|
||||
struct bt_att_chan *chan;
|
||||
unsigned int id;
|
||||
@ -425,13 +442,14 @@ static bool timeout_cb(void *user_data)
|
||||
return false;
|
||||
|
||||
util_debug(att->debug_callback, att->debug_data,
|
||||
"Operation timed out: 0x%02x", op->opcode);
|
||||
"(chan %p) Operation timed out: 0x%02x",
|
||||
chan, op->opcode);
|
||||
|
||||
if (att->timeout_callback)
|
||||
att->timeout_callback(op->id, op->opcode, att->timeout_data);
|
||||
|
||||
op->timeout_id = 0;
|
||||
destroy_att_send_op(op);
|
||||
disc_att_send_op(op);
|
||||
|
||||
/*
|
||||
* Directly terminate the connection as required by the ATT protocol.
|
||||
@ -450,39 +468,52 @@ static void write_watch_destroy(void *user_data)
|
||||
chan->writer_active = false;
|
||||
}
|
||||
|
||||
static ssize_t bt_att_chan_write(struct bt_att_chan *chan, uint8_t opcode,
|
||||
const void *pdu, uint16_t len)
|
||||
{
|
||||
struct bt_att *att = chan->att;
|
||||
ssize_t ret;
|
||||
struct iovec iov;
|
||||
|
||||
iov.iov_base = (void *) pdu;
|
||||
iov.iov_len = len;
|
||||
|
||||
util_debug(att->debug_callback, att->debug_data,
|
||||
"(chan %p) ATT op 0x%02x",
|
||||
chan, opcode);
|
||||
|
||||
ret = io_send(chan->io, &iov, 1);
|
||||
if (ret < 0) {
|
||||
util_debug(att->debug_callback, att->debug_data,
|
||||
"(chan %p) write failed: %s",
|
||||
chan, strerror(-ret));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
util_hexdump('<', pdu, ret, att->debug_callback, att->debug_data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool can_write_data(struct io *io, void *user_data)
|
||||
{
|
||||
struct bt_att_chan *chan = user_data;
|
||||
struct bt_att *att = chan->att;
|
||||
struct att_send_op *op;
|
||||
struct timeout_data *timeout;
|
||||
ssize_t ret;
|
||||
struct iovec iov;
|
||||
|
||||
op = pick_next_send_op(chan);
|
||||
if (!op)
|
||||
return false;
|
||||
|
||||
iov.iov_base = op->pdu;
|
||||
iov.iov_len = op->len;
|
||||
|
||||
ret = io_send(io, &iov, 1);
|
||||
if (ret < 0) {
|
||||
util_debug(att->debug_callback, att->debug_data,
|
||||
"write failed: %s", strerror(-ret));
|
||||
if (!bt_att_chan_write(chan, op->opcode, op->pdu, op->len)) {
|
||||
if (op->callback)
|
||||
op->callback(BT_ATT_OP_ERROR_RSP, NULL, 0,
|
||||
op->user_data);
|
||||
|
||||
destroy_att_send_op(op);
|
||||
return true;
|
||||
}
|
||||
|
||||
util_debug(att->debug_callback, att->debug_data,
|
||||
"ATT op 0x%02x", op->opcode);
|
||||
|
||||
util_hexdump('<', op->pdu, ret, att->debug_callback, att->debug_data);
|
||||
|
||||
/* Based on the operation type, set either the pending request or the
|
||||
* pending indication. If it came from the write queue, then there is
|
||||
* no need to keep it around.
|
||||
@ -528,7 +559,7 @@ static void wakeup_chan_writer(void *data, void *user_data)
|
||||
/* Set the write handler only if there is anything that can be sent
|
||||
* at all.
|
||||
*/
|
||||
if (queue_isempty(att->write_queue)) {
|
||||
if (queue_isempty(chan->queue) && queue_isempty(att->write_queue)) {
|
||||
if ((chan->pending_req || queue_isempty(att->req_queue)) &&
|
||||
(chan->pending_ind || queue_isempty(att->ind_queue)))
|
||||
return;
|
||||
@ -558,16 +589,6 @@ static void disconn_handler(void *data, void *user_data)
|
||||
disconn->callback(err, disconn->user_data);
|
||||
}
|
||||
|
||||
static void disc_att_send_op(void *data)
|
||||
{
|
||||
struct att_send_op *op = data;
|
||||
|
||||
if (op->callback)
|
||||
op->callback(BT_ATT_OP_ERROR_RSP, NULL, 0, op->user_data);
|
||||
|
||||
destroy_att_send_op(op);
|
||||
}
|
||||
|
||||
static void bt_att_chan_free(void *data)
|
||||
{
|
||||
struct bt_att_chan *chan = data;
|
||||
@ -578,6 +599,8 @@ static void bt_att_chan_free(void *data)
|
||||
if (chan->pending_ind)
|
||||
destroy_att_send_op(chan->pending_ind);
|
||||
|
||||
queue_destroy(chan->queue, destroy_att_send_op);
|
||||
|
||||
io_destroy(chan->io);
|
||||
|
||||
free(chan->buf);
|
||||
@ -595,8 +618,8 @@ static bool disconnect_cb(struct io *io, void *user_data)
|
||||
|
||||
if (getsockopt(chan->fd, SOL_SOCKET, SO_ERROR, &err, &len) < 0) {
|
||||
util_debug(chan->att->debug_callback, chan->att->debug_data,
|
||||
"Failed to obtain disconnect error: %s",
|
||||
strerror(errno));
|
||||
"(chan %p) Failed to obtain disconnect"
|
||||
" error: %s", chan, strerror(errno));
|
||||
err = 0;
|
||||
}
|
||||
|
||||
@ -728,7 +751,8 @@ static bool handle_error_rsp(struct bt_att_chan *chan, uint8_t *pdu,
|
||||
}
|
||||
|
||||
util_debug(att->debug_callback, att->debug_data,
|
||||
"Retrying operation %p", op);
|
||||
"(chan %p) Retrying operation "
|
||||
"%p", chan, op);
|
||||
|
||||
chan->pending_req = NULL;
|
||||
|
||||
@ -752,7 +776,8 @@ static void handle_rsp(struct bt_att_chan *chan, uint8_t opcode, uint8_t *pdu,
|
||||
*/
|
||||
if (!op) {
|
||||
util_debug(att->debug_callback, att->debug_data,
|
||||
"Received unexpected ATT response");
|
||||
"(chan %p) Received unexpected ATT "
|
||||
"response", chan);
|
||||
io_shutdown(chan->io);
|
||||
return;
|
||||
}
|
||||
@ -784,7 +809,8 @@ static void handle_rsp(struct bt_att_chan *chan, uint8_t opcode, uint8_t *pdu,
|
||||
|
||||
fail:
|
||||
util_debug(att->debug_callback, att->debug_data,
|
||||
"Failed to handle response PDU; opcode: 0x%02x", opcode);
|
||||
"(chan %p) Failed to handle response PDU; opcode: "
|
||||
"0x%02x", chan, opcode);
|
||||
|
||||
rsp_opcode = BT_ATT_OP_ERROR_RSP;
|
||||
|
||||
@ -809,7 +835,8 @@ static void handle_conf(struct bt_att_chan *chan, uint8_t *pdu, ssize_t pdu_len)
|
||||
*/
|
||||
if (!op || pdu_len) {
|
||||
util_debug(att->debug_callback, att->debug_data,
|
||||
"Received unexpected/invalid ATT confirmation");
|
||||
"(chan %p) Received unexpected/invalid ATT "
|
||||
"confirmation", chan);
|
||||
io_shutdown(chan->io);
|
||||
return;
|
||||
}
|
||||
@ -935,7 +962,7 @@ static void handle_notify(struct bt_att_chan *chan, uint8_t opcode,
|
||||
found = true;
|
||||
|
||||
if (notify->callback)
|
||||
notify->callback(opcode, pdu, pdu_len,
|
||||
notify->callback(chan, opcode, pdu, pdu_len,
|
||||
notify->user_data);
|
||||
|
||||
/* callback could remove all entries from notify list */
|
||||
@ -966,6 +993,10 @@ static bool can_read_data(struct io *io, void *user_data)
|
||||
if (bytes_read < 0)
|
||||
return false;
|
||||
|
||||
util_debug(att->debug_callback, att->debug_data,
|
||||
"(chan %p) ATT received: %zd",
|
||||
chan, bytes_read);
|
||||
|
||||
util_hexdump('>', chan->buf, bytes_read,
|
||||
att->debug_callback, att->debug_data);
|
||||
|
||||
@ -981,12 +1012,14 @@ static bool can_read_data(struct io *io, void *user_data)
|
||||
switch (get_op_type(opcode)) {
|
||||
case ATT_OP_TYPE_RSP:
|
||||
util_debug(att->debug_callback, att->debug_data,
|
||||
"ATT response received: 0x%02x", opcode);
|
||||
"(chan %p) ATT response received: 0x%02x",
|
||||
chan, opcode);
|
||||
handle_rsp(chan, opcode, pdu + 1, bytes_read - 1);
|
||||
break;
|
||||
case ATT_OP_TYPE_CONF:
|
||||
util_debug(att->debug_callback, att->debug_data,
|
||||
"ATT confirmation received: 0x%02x", opcode);
|
||||
"(chan %p) ATT confirmation received: 0x%02x",
|
||||
chan, opcode);
|
||||
handle_conf(chan, pdu + 1, bytes_read - 1);
|
||||
break;
|
||||
case ATT_OP_TYPE_REQ:
|
||||
@ -997,8 +1030,9 @@ static bool can_read_data(struct io *io, void *user_data)
|
||||
*/
|
||||
if (chan->in_req) {
|
||||
util_debug(att->debug_callback, att->debug_data,
|
||||
"Received request while another is "
|
||||
"pending: 0x%02x", opcode);
|
||||
"(chan %p) Received request while "
|
||||
"another is pending: 0x%02x",
|
||||
chan, opcode);
|
||||
io_shutdown(chan->io);
|
||||
bt_att_unref(chan->att);
|
||||
|
||||
@ -1017,7 +1051,8 @@ static bool can_read_data(struct io *io, void *user_data)
|
||||
* let them act on it.
|
||||
*/
|
||||
util_debug(att->debug_callback, att->debug_data,
|
||||
"ATT PDU received: 0x%02x", opcode);
|
||||
"(chan %p) ATT PDU received: 0x%02x",
|
||||
chan, opcode);
|
||||
handle_notify(chan, opcode, pdu + 1, bytes_read - 1);
|
||||
break;
|
||||
}
|
||||
@ -1075,16 +1110,19 @@ static void bt_att_free(struct bt_att *att)
|
||||
free(att);
|
||||
}
|
||||
|
||||
static uint16_t get_l2cap_mtu(int fd)
|
||||
static uint16_t io_get_mtu(int fd)
|
||||
{
|
||||
socklen_t len;
|
||||
struct l2cap_options l2o;
|
||||
|
||||
len = sizeof(l2o);
|
||||
if (getsockopt(fd, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &len) < 0)
|
||||
return 0;
|
||||
if (!getsockopt(fd, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &len))
|
||||
return l2o.omtu;
|
||||
|
||||
return l2o.omtu;
|
||||
if (!getsockopt(fd, SOL_BLUETOOTH, BT_SNDMTU, &l2o.omtu, &len))
|
||||
return l2o.omtu;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint8_t io_get_type(int fd)
|
||||
@ -1135,7 +1173,7 @@ static struct bt_att_chan *bt_att_chan_new(int fd, uint8_t type)
|
||||
chan->mtu = BT_ATT_DEFAULT_LE_MTU;
|
||||
break;
|
||||
default:
|
||||
chan->mtu = get_l2cap_mtu(chan->fd);
|
||||
chan->mtu = io_get_mtu(chan->fd);
|
||||
}
|
||||
|
||||
if (chan->mtu < BT_ATT_DEFAULT_LE_MTU)
|
||||
@ -1145,6 +1183,8 @@ static struct bt_att_chan *bt_att_chan_new(int fd, uint8_t type)
|
||||
if (!chan->buf)
|
||||
goto fail;
|
||||
|
||||
chan->queue = queue_new();
|
||||
|
||||
return chan;
|
||||
|
||||
fail:
|
||||
@ -1153,6 +1193,23 @@ fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void bt_att_attach_chan(struct bt_att *att, struct bt_att_chan *chan)
|
||||
{
|
||||
/* Push to head as EATT channels have higher priority */
|
||||
queue_push_head(att->chans, chan);
|
||||
chan->att = att;
|
||||
|
||||
if (chan->mtu > att->mtu)
|
||||
att->mtu = chan->mtu;
|
||||
|
||||
io_set_close_on_destroy(chan->io, att->close_on_unref);
|
||||
|
||||
util_debug(att->debug_callback, att->debug_data, "Channel %p attached",
|
||||
chan);
|
||||
|
||||
wakeup_chan_writer(chan, NULL);
|
||||
}
|
||||
|
||||
struct bt_att *bt_att_new(int fd, bool ext_signed)
|
||||
{
|
||||
struct bt_att *att;
|
||||
@ -1166,9 +1223,6 @@ struct bt_att *bt_att_new(int fd, bool ext_signed)
|
||||
att->chans = queue_new();
|
||||
att->mtu = chan->mtu;
|
||||
|
||||
queue_push_head(att->chans, chan);
|
||||
chan->att = att;
|
||||
|
||||
/* crypto is optional, if not available leave it NULL */
|
||||
if (!ext_signed)
|
||||
att->crypto = bt_crypto_new();
|
||||
@ -1179,6 +1233,8 @@ struct bt_att *bt_att_new(int fd, bool ext_signed)
|
||||
att->notify_list = queue_new();
|
||||
att->disconn_list = queue_new();
|
||||
|
||||
bt_att_attach_chan(att, chan);
|
||||
|
||||
return bt_att_ref(att);
|
||||
}
|
||||
|
||||
@ -1237,13 +1293,7 @@ int bt_att_attach_fd(struct bt_att *att, int fd)
|
||||
if (!chan)
|
||||
return -EINVAL;
|
||||
|
||||
queue_push_tail(att->chans, chan);
|
||||
chan->att = att;
|
||||
|
||||
if (chan->mtu > att->mtu)
|
||||
att->mtu = chan->mtu;
|
||||
|
||||
io_set_close_on_destroy(chan->io, att->close_on_unref);
|
||||
bt_att_attach_chan(att, chan);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1258,7 +1308,7 @@ int bt_att_get_fd(struct bt_att *att)
|
||||
if (queue_isempty(att->chans))
|
||||
return -ENOTCONN;
|
||||
|
||||
chan = queue_peek_head(att->chans);
|
||||
chan = queue_peek_tail(att->chans);
|
||||
|
||||
return chan->fd;
|
||||
}
|
||||
@ -1306,7 +1356,8 @@ bool bt_att_set_mtu(struct bt_att *att, uint16_t mtu)
|
||||
if (mtu < BT_ATT_DEFAULT_LE_MTU)
|
||||
return false;
|
||||
|
||||
chan = queue_peek_head(att->chans);
|
||||
/* Original channel is always the last */
|
||||
chan = queue_peek_tail(att->chans);
|
||||
if (!chan)
|
||||
return -ENOTCONN;
|
||||
|
||||
@ -1332,7 +1383,7 @@ uint8_t bt_att_get_link_type(struct bt_att *att)
|
||||
if (!att)
|
||||
return -EINVAL;
|
||||
|
||||
chan = queue_peek_head(att->chans);
|
||||
chan = queue_peek_tail(att->chans);
|
||||
if (!chan)
|
||||
return -ENOTCONN;
|
||||
|
||||
@ -1392,7 +1443,7 @@ bool bt_att_unregister_disconnect(struct bt_att *att, unsigned int id)
|
||||
return false;
|
||||
|
||||
/* Check if disconnect is running */
|
||||
if (!queue_isempty(att->chans)) {
|
||||
if (queue_isempty(att->chans)) {
|
||||
disconn = queue_find(att->disconn_list, match_disconn_id,
|
||||
UINT_TO_PTR(id));
|
||||
if (!disconn)
|
||||
@ -1461,6 +1512,33 @@ unsigned int bt_att_send(struct bt_att *att, uint8_t opcode,
|
||||
return op->id;
|
||||
}
|
||||
|
||||
unsigned int bt_att_chan_send(struct bt_att_chan *chan, uint8_t opcode,
|
||||
const void *pdu, uint16_t len,
|
||||
bt_att_response_func_t callback,
|
||||
void *user_data,
|
||||
bt_att_destroy_func_t destroy)
|
||||
{
|
||||
struct att_send_op *op;
|
||||
|
||||
if (!chan || !chan->att)
|
||||
return -EINVAL;
|
||||
|
||||
op = create_att_send_op(chan->att, opcode, pdu, len, callback,
|
||||
user_data, destroy);
|
||||
if (!op)
|
||||
return -EINVAL;
|
||||
|
||||
if (!queue_push_tail(chan->queue, op)) {
|
||||
free(op->pdu);
|
||||
free(op);
|
||||
return 0;
|
||||
}
|
||||
|
||||
wakeup_chan_writer(chan, NULL);
|
||||
|
||||
return op->id;
|
||||
}
|
||||
|
||||
static bool match_op_id(const void *a, const void *b)
|
||||
{
|
||||
const struct att_send_op *op = a;
|
||||
@ -1469,6 +1547,33 @@ static bool match_op_id(const void *a, const void *b)
|
||||
return op->id == id;
|
||||
}
|
||||
|
||||
bool bt_att_chan_cancel(struct bt_att_chan *chan, unsigned int id)
|
||||
{
|
||||
struct att_send_op *op;
|
||||
|
||||
if (chan->pending_req && chan->pending_req->id == id) {
|
||||
/* Don't cancel the pending request; remove it's handlers */
|
||||
cancel_att_send_op(chan->pending_req);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (chan->pending_ind && chan->pending_ind->id == id) {
|
||||
/* Don't cancel the pending indication; remove it's handlers. */
|
||||
cancel_att_send_op(chan->pending_ind);
|
||||
return true;
|
||||
}
|
||||
|
||||
op = queue_remove_if(chan->queue, match_op_id, UINT_TO_PTR(id));
|
||||
if (!op)
|
||||
return false;
|
||||
|
||||
destroy_att_send_op(op);
|
||||
|
||||
wakeup_chan_writer(chan, NULL);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bt_att_cancel(struct bt_att *att, unsigned int id)
|
||||
{
|
||||
const struct queue_entry *entry;
|
||||
@ -1477,25 +1582,13 @@ bool bt_att_cancel(struct bt_att *att, unsigned int id)
|
||||
if (!att || !id)
|
||||
return false;
|
||||
|
||||
/* Lookuo request on each channel first */
|
||||
for (entry = queue_get_entries(att->chans); entry;
|
||||
entry = entry->next) {
|
||||
struct bt_att_chan *chan = entry->data;
|
||||
|
||||
if (chan->pending_req && chan->pending_req->id == id) {
|
||||
/* Don't cancel the pending request; remove it's
|
||||
* handlers
|
||||
*/
|
||||
cancel_att_send_op(chan->pending_req);
|
||||
if (bt_att_chan_cancel(chan, id))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (chan->pending_ind && chan->pending_ind->id == id) {
|
||||
/* Don't cancel the pending indication; remove it's
|
||||
* handlers.
|
||||
*/
|
||||
cancel_att_send_op(chan->pending_ind);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
op = queue_remove_if(att->req_queue, match_op_id, UINT_TO_PTR(id));
|
||||
@ -1580,14 +1673,14 @@ static uint8_t att_ecode_from_error(int err)
|
||||
return BT_ATT_ERROR_UNLIKELY;
|
||||
}
|
||||
|
||||
unsigned int bt_att_send_error_rsp(struct bt_att *att, uint8_t opcode,
|
||||
int bt_att_chan_send_error_rsp(struct bt_att_chan *chan, uint8_t opcode,
|
||||
uint16_t handle, int error)
|
||||
{
|
||||
struct bt_att_pdu_error_rsp pdu;
|
||||
uint8_t ecode;
|
||||
|
||||
if (!att || !opcode)
|
||||
return 0;
|
||||
if (!chan || !chan->att || !opcode)
|
||||
return -EINVAL;
|
||||
|
||||
ecode = att_ecode_from_error(error);
|
||||
|
||||
@ -1597,8 +1690,8 @@ unsigned int bt_att_send_error_rsp(struct bt_att *att, uint8_t opcode,
|
||||
put_le16(handle, &pdu.handle);
|
||||
pdu.ecode = ecode;
|
||||
|
||||
return bt_att_send(att, BT_ATT_OP_ERROR_RSP, &pdu, sizeof(pdu),
|
||||
NULL, NULL, NULL);
|
||||
return bt_att_chan_send_rsp(chan, BT_ATT_OP_ERROR_RSP, &pdu,
|
||||
sizeof(pdu));
|
||||
}
|
||||
|
||||
unsigned int bt_att_register(struct bt_att *att, uint8_t opcode,
|
||||
@ -1665,7 +1758,7 @@ int bt_att_get_security(struct bt_att *att, uint8_t *enc_size)
|
||||
if (!att)
|
||||
return -EINVAL;
|
||||
|
||||
chan = queue_peek_head(att->chans);
|
||||
chan = queue_peek_tail(att->chans);
|
||||
if (!chan)
|
||||
return -ENOTCONN;
|
||||
|
||||
@ -1687,7 +1780,7 @@ bool bt_att_set_security(struct bt_att *att, int level)
|
||||
level > BT_ATT_SECURITY_HIGH)
|
||||
return false;
|
||||
|
||||
chan = queue_peek_head(att->chans);
|
||||
chan = queue_peek_tail(att->chans);
|
||||
if (!chan)
|
||||
return -ENOTCONN;
|
||||
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include "src/shared/att-types.h"
|
||||
|
||||
struct bt_att;
|
||||
struct bt_att_chan;
|
||||
|
||||
struct bt_att *bt_att_new(int fd, bool ext_signed);
|
||||
|
||||
@ -43,7 +44,8 @@ int bt_att_get_channels(struct bt_att *att);
|
||||
|
||||
typedef void (*bt_att_response_func_t)(uint8_t opcode, const void *pdu,
|
||||
uint16_t length, void *user_data);
|
||||
typedef void (*bt_att_notify_func_t)(uint8_t opcode, const void *pdu,
|
||||
typedef void (*bt_att_notify_func_t)(struct bt_att_chan *chan,
|
||||
uint8_t opcode, const void *pdu,
|
||||
uint16_t length, void *user_data);
|
||||
typedef void (*bt_att_destroy_func_t)(void *user_data);
|
||||
typedef void (*bt_att_debug_func_t)(const char *str, void *user_data);
|
||||
@ -68,10 +70,18 @@ unsigned int bt_att_send(struct bt_att *att, uint8_t opcode,
|
||||
bt_att_response_func_t callback,
|
||||
void *user_data,
|
||||
bt_att_destroy_func_t destroy);
|
||||
unsigned int bt_att_chan_send(struct bt_att_chan *chan, uint8_t opcode,
|
||||
const void *pdu, uint16_t len,
|
||||
bt_att_response_func_t callback,
|
||||
void *user_data,
|
||||
bt_att_destroy_func_t destroy);
|
||||
#define bt_att_chan_send_rsp(chan, opcode, pdu, len) \
|
||||
bt_att_chan_send(chan, opcode, pdu, len, NULL, NULL, NULL)
|
||||
bool bt_att_chan_cancel(struct bt_att_chan *chan, unsigned int id);
|
||||
bool bt_att_cancel(struct bt_att *att, unsigned int id);
|
||||
bool bt_att_cancel_all(struct bt_att *att);
|
||||
|
||||
unsigned int bt_att_send_error_rsp(struct bt_att *att, uint8_t opcode,
|
||||
int bt_att_chan_send_error_rsp(struct bt_att_chan *chan, uint8_t opcode,
|
||||
uint16_t handle, int error);
|
||||
|
||||
unsigned int bt_att_register(struct bt_att *att, uint8_t opcode,
|
||||
|
@ -60,6 +60,7 @@ struct ready_cb {
|
||||
struct bt_gatt_client {
|
||||
struct bt_att *att;
|
||||
int ref_count;
|
||||
uint8_t features;
|
||||
|
||||
struct bt_gatt_client *parent;
|
||||
struct queue *clones;
|
||||
@ -326,6 +327,7 @@ struct discovery_op {
|
||||
struct queue *ext_prop_desc;
|
||||
struct gatt_db_attribute *cur_svc;
|
||||
struct gatt_db_attribute *hash;
|
||||
uint8_t server_feat;
|
||||
bool success;
|
||||
uint16_t start;
|
||||
uint16_t end;
|
||||
@ -1278,6 +1280,9 @@ static void notify_client_ready(struct bt_gatt_client *client, bool success,
|
||||
bt_gatt_client_ref(client);
|
||||
client->ready = success;
|
||||
|
||||
if (client->parent)
|
||||
client->features = client->parent->features;
|
||||
|
||||
for (entry = queue_get_entries(client->ready_cbs); entry;
|
||||
entry = entry->next) {
|
||||
struct ready_cb *ready = entry->data;
|
||||
@ -1381,7 +1386,7 @@ static void db_hash_read_cb(bool success, uint8_t att_ecode,
|
||||
util_hexdump(' ', value, len, client->debug_callback,
|
||||
client->debug_data);
|
||||
|
||||
/* Store the new hash in the db */
|
||||
/* Store ithe new hash in the db */
|
||||
gatt_db_attribute_write(op->hash, 0, value, len, 0, NULL,
|
||||
db_hash_write_value_cb, client);
|
||||
|
||||
@ -1431,6 +1436,67 @@ static bool read_db_hash(struct discovery_op *op)
|
||||
return true;
|
||||
}
|
||||
|
||||
static void db_server_feat_read(bool success, uint8_t att_ecode,
|
||||
struct bt_gatt_result *result, void *user_data)
|
||||
{
|
||||
struct discovery_op *op = user_data;
|
||||
struct bt_gatt_client *client = op->client;
|
||||
const uint8_t *value;
|
||||
uint16_t len, handle;
|
||||
struct bt_gatt_iter iter;
|
||||
|
||||
if (!result)
|
||||
return;
|
||||
|
||||
bt_gatt_iter_init(&iter, result);
|
||||
bt_gatt_iter_next_read_by_type(&iter, &handle, &len, &value);
|
||||
|
||||
util_debug(client->debug_callback, client->debug_data,
|
||||
"Server Features found: handle 0x%04x "
|
||||
"length 0x%04x value 0x%02x", handle, len,
|
||||
value[0]);
|
||||
|
||||
op->server_feat = value[0];
|
||||
}
|
||||
|
||||
static void server_feat_read_value(struct gatt_db_attribute *attrib,
|
||||
int err, const uint8_t *value,
|
||||
size_t length, void *user_data)
|
||||
{
|
||||
const uint8_t **feat = user_data;
|
||||
|
||||
if (err)
|
||||
return;
|
||||
|
||||
*feat = value;
|
||||
}
|
||||
|
||||
static void read_server_feat(struct discovery_op *op)
|
||||
{
|
||||
struct bt_gatt_client *client = op->client;
|
||||
struct gatt_db_attribute *attr = NULL;
|
||||
const uint8_t *feat = NULL;
|
||||
bt_uuid_t uuid;
|
||||
|
||||
bt_uuid16_create(&uuid, GATT_CHARAC_SERVER_FEAT);
|
||||
|
||||
gatt_db_find_by_type(client->db, 0x0001, 0xffff, &uuid,
|
||||
get_first_attribute, &attr);
|
||||
if (attr) {
|
||||
/* Read stored value in the db */
|
||||
gatt_db_attribute_read(attr, 0, BT_ATT_OP_READ_REQ, NULL,
|
||||
server_feat_read_value, &feat);
|
||||
if (feat)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!bt_gatt_read_by_type(client->att, 0x0001, 0xffff, &uuid,
|
||||
db_server_feat_read,
|
||||
discovery_op_ref(op),
|
||||
discovery_op_unref))
|
||||
discovery_op_unref(op);
|
||||
}
|
||||
|
||||
static void exchange_mtu_cb(bool success, uint8_t att_ecode, void *user_data)
|
||||
{
|
||||
struct discovery_op *op = user_data;
|
||||
@ -1464,6 +1530,8 @@ static void exchange_mtu_cb(bool success, uint8_t att_ecode, void *user_data)
|
||||
bt_att_get_mtu(client->att));
|
||||
|
||||
discover:
|
||||
read_server_feat(op);
|
||||
|
||||
if (read_db_hash(op)) {
|
||||
op->success = false;
|
||||
return;
|
||||
@ -1839,12 +1907,41 @@ static void service_changed_cb(uint16_t value_handle, const uint8_t *value,
|
||||
queue_push_tail(client->svc_chngd_queue, op);
|
||||
}
|
||||
|
||||
static void server_feat_write_value(struct gatt_db_attribute *attrib,
|
||||
int err, void *user_data)
|
||||
{
|
||||
struct bt_gatt_client *client = user_data;
|
||||
|
||||
util_debug(client->debug_callback, client->debug_data,
|
||||
"Server Features Value set status: %d", err);
|
||||
}
|
||||
|
||||
static void write_server_features(struct bt_gatt_client *client, uint8_t feat)
|
||||
{
|
||||
bt_uuid_t uuid;
|
||||
struct gatt_db_attribute *attr = NULL;
|
||||
|
||||
bt_uuid16_create(&uuid, GATT_CHARAC_SERVER_FEAT);
|
||||
|
||||
gatt_db_find_by_type(client->db, 0x0001, 0xffff, &uuid,
|
||||
get_first_attribute, &attr);
|
||||
if (!attr)
|
||||
return;
|
||||
|
||||
/* Store value in the DB */
|
||||
if (!gatt_db_attribute_write(attr, 0, &feat, sizeof(feat),
|
||||
0, NULL, server_feat_write_value,
|
||||
client))
|
||||
util_debug(client->debug_callback, client->debug_data,
|
||||
"Unable to store Server Features");
|
||||
}
|
||||
|
||||
static void write_client_features(struct bt_gatt_client *client)
|
||||
{
|
||||
bt_uuid_t uuid;
|
||||
struct gatt_db_attribute *attr = NULL;
|
||||
uint16_t handle;
|
||||
uint8_t value;
|
||||
const uint8_t *feat = NULL;
|
||||
|
||||
bt_uuid16_create(&uuid, GATT_CHARAC_CLI_FEAT);
|
||||
|
||||
@ -1854,10 +1951,28 @@ static void write_client_features(struct bt_gatt_client *client)
|
||||
return;
|
||||
|
||||
handle = gatt_db_attribute_get_handle(attr);
|
||||
value = BT_GATT_CHRC_CLI_FEAT_ROBUST_CACHING;
|
||||
|
||||
bt_gatt_client_write_value(client, handle, &value, sizeof(value), NULL,
|
||||
NULL, NULL);
|
||||
client->features = BT_GATT_CHRC_CLI_FEAT_ROBUST_CACHING;
|
||||
|
||||
bt_uuid16_create(&uuid, GATT_CHARAC_SERVER_FEAT);
|
||||
|
||||
attr = NULL;
|
||||
gatt_db_find_by_type(client->db, 0x0001, 0xffff, &uuid,
|
||||
get_first_attribute, &attr);
|
||||
if (attr) {
|
||||
/* Read stored value in the db */
|
||||
gatt_db_attribute_read(attr, 0, BT_ATT_OP_READ_REQ,
|
||||
NULL, server_feat_read_value,
|
||||
&feat);
|
||||
if (feat && feat[0] & BT_GATT_CHRC_SERVER_FEAT_EATT)
|
||||
client->features |= BT_GATT_CHRC_CLI_FEAT_EATT;
|
||||
}
|
||||
|
||||
util_debug(client->debug_callback, client->debug_data,
|
||||
"Writing Client Features 0x%02x", client->features);
|
||||
|
||||
bt_gatt_client_write_value(client, handle, &client->features,
|
||||
sizeof(client->features), NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
static void init_complete(struct discovery_op *op, bool success,
|
||||
@ -1870,6 +1985,9 @@ static void init_complete(struct discovery_op *op, bool success,
|
||||
if (!success)
|
||||
goto fail;
|
||||
|
||||
if (op->server_feat)
|
||||
write_server_features(client, op->server_feat);
|
||||
|
||||
write_client_features(client);
|
||||
|
||||
if (register_service_changed(client))
|
||||
@ -1932,6 +2050,8 @@ static bool gatt_client_init(struct bt_gatt_client *client, uint16_t mtu)
|
||||
return true;
|
||||
|
||||
discover:
|
||||
read_server_feat(op);
|
||||
|
||||
if (read_db_hash(op)) {
|
||||
op->success = false;
|
||||
goto done;
|
||||
@ -2026,8 +2146,9 @@ static void notify_handler(void *data, void *user_data)
|
||||
notify_data->user_data);
|
||||
}
|
||||
|
||||
static void notify_cb(uint8_t opcode, const void *pdu, uint16_t length,
|
||||
void *user_data)
|
||||
static void notify_cb(struct bt_att_chan *chan, uint8_t opcode,
|
||||
const void *pdu, uint16_t length,
|
||||
void *user_data)
|
||||
{
|
||||
struct bt_gatt_client *client = user_data;
|
||||
struct pdu_data pdu_data;
|
||||
@ -2041,7 +2162,7 @@ static void notify_cb(uint8_t opcode, const void *pdu, uint16_t length,
|
||||
queue_foreach(client->notify_list, notify_handler, &pdu_data);
|
||||
|
||||
if (opcode == BT_ATT_OP_HANDLE_VAL_IND && !client->parent)
|
||||
bt_att_send(client->att, BT_ATT_OP_HANDLE_VAL_CONF, NULL, 0,
|
||||
bt_att_chan_send(chan, BT_ATT_OP_HANDLE_VAL_CONF, NULL, 0,
|
||||
NULL, NULL, NULL);
|
||||
|
||||
bt_gatt_client_unref(client);
|
||||
@ -2099,7 +2220,8 @@ static void att_disconnect_cb(int err, void *user_data)
|
||||
}
|
||||
|
||||
static struct bt_gatt_client *gatt_client_new(struct gatt_db *db,
|
||||
struct bt_att *att)
|
||||
struct bt_att *att,
|
||||
uint8_t features)
|
||||
{
|
||||
struct bt_gatt_client *client;
|
||||
|
||||
@ -2129,6 +2251,7 @@ static struct bt_gatt_client *gatt_client_new(struct gatt_db *db,
|
||||
|
||||
client->att = bt_att_ref(att);
|
||||
client->db = gatt_db_ref(db);
|
||||
client->features = features;
|
||||
|
||||
return client;
|
||||
|
||||
@ -2140,14 +2263,15 @@ fail:
|
||||
|
||||
struct bt_gatt_client *bt_gatt_client_new(struct gatt_db *db,
|
||||
struct bt_att *att,
|
||||
uint16_t mtu)
|
||||
uint16_t mtu,
|
||||
uint8_t features)
|
||||
{
|
||||
struct bt_gatt_client *client;
|
||||
|
||||
if (!att || !db)
|
||||
return NULL;
|
||||
|
||||
client = gatt_client_new(db, att);
|
||||
client = gatt_client_new(db, att, features);
|
||||
if (!client)
|
||||
return NULL;
|
||||
|
||||
@ -2166,7 +2290,7 @@ struct bt_gatt_client *bt_gatt_client_clone(struct bt_gatt_client *client)
|
||||
if (!client)
|
||||
return NULL;
|
||||
|
||||
clone = gatt_client_new(client->db, client->att);
|
||||
clone = gatt_client_new(client->db, client->att, client->features);
|
||||
if (!clone)
|
||||
return NULL;
|
||||
|
||||
@ -2284,6 +2408,14 @@ uint16_t bt_gatt_client_get_mtu(struct bt_gatt_client *client)
|
||||
return bt_att_get_mtu(client->att);
|
||||
}
|
||||
|
||||
struct bt_att *bt_gatt_client_get_att(struct bt_gatt_client *client)
|
||||
{
|
||||
if (!client)
|
||||
return NULL;
|
||||
|
||||
return client->att;
|
||||
}
|
||||
|
||||
struct gatt_db *bt_gatt_client_get_db(struct bt_gatt_client *client)
|
||||
{
|
||||
if (!client || !client->db)
|
||||
@ -2292,6 +2424,17 @@ struct gatt_db *bt_gatt_client_get_db(struct bt_gatt_client *client)
|
||||
return client->db;
|
||||
}
|
||||
|
||||
uint8_t bt_gatt_client_get_features(struct bt_gatt_client *client)
|
||||
{
|
||||
if (!client)
|
||||
return 0;
|
||||
|
||||
if (client->parent)
|
||||
return client->parent->features;
|
||||
|
||||
return client->features;
|
||||
}
|
||||
|
||||
static bool match_req_id(const void *a, const void *b)
|
||||
{
|
||||
const struct request *req = a;
|
||||
|
@ -31,7 +31,8 @@ struct bt_gatt_client;
|
||||
|
||||
struct bt_gatt_client *bt_gatt_client_new(struct gatt_db *db,
|
||||
struct bt_att *att,
|
||||
uint16_t mtu);
|
||||
uint16_t mtu,
|
||||
uint8_t features);
|
||||
struct bt_gatt_client *bt_gatt_client_clone(struct bt_gatt_client *client);
|
||||
|
||||
struct bt_gatt_client *bt_gatt_client_ref(struct bt_gatt_client *client);
|
||||
@ -73,7 +74,9 @@ bool bt_gatt_client_set_debug(struct bt_gatt_client *client,
|
||||
bt_gatt_client_destroy_func_t destroy);
|
||||
|
||||
uint16_t bt_gatt_client_get_mtu(struct bt_gatt_client *client);
|
||||
struct bt_att *bt_gatt_client_get_att(struct bt_gatt_client *client);
|
||||
struct gatt_db *bt_gatt_client_get_db(struct bt_gatt_client *client);
|
||||
uint8_t bt_gatt_client_get_features(struct bt_gatt_client *client);
|
||||
|
||||
bool bt_gatt_client_cancel(struct bt_gatt_client *client, unsigned int id);
|
||||
bool bt_gatt_client_cancel_all(struct bt_gatt_client *client);
|
||||
|
@ -52,6 +52,7 @@
|
||||
#define DEFAULT_MAX_PREP_QUEUE_LEN 30
|
||||
|
||||
struct async_read_op {
|
||||
struct bt_att_chan *chan;
|
||||
struct bt_gatt_server *server;
|
||||
uint8_t opcode;
|
||||
bool done;
|
||||
@ -62,6 +63,7 @@ struct async_read_op {
|
||||
};
|
||||
|
||||
struct async_write_op {
|
||||
struct bt_att_chan *chan;
|
||||
struct bt_gatt_server *server;
|
||||
uint8_t opcode;
|
||||
};
|
||||
@ -239,8 +241,9 @@ static bool encode_read_by_grp_type_rsp(struct gatt_db *db, struct queue *q,
|
||||
return true;
|
||||
}
|
||||
|
||||
static void read_by_grp_type_cb(uint8_t opcode, const void *pdu,
|
||||
uint16_t length, void *user_data)
|
||||
static void read_by_grp_type_cb(struct bt_att_chan *chan, uint8_t opcode,
|
||||
const void *pdu, uint16_t length,
|
||||
void *user_data)
|
||||
{
|
||||
struct bt_gatt_server *server = user_data;
|
||||
uint16_t start, end;
|
||||
@ -308,15 +311,14 @@ static void read_by_grp_type_cb(uint8_t opcode, const void *pdu,
|
||||
|
||||
queue_destroy(q, NULL);
|
||||
|
||||
bt_att_send(server->att, BT_ATT_OP_READ_BY_GRP_TYPE_RSP,
|
||||
rsp_pdu, rsp_len,
|
||||
NULL, NULL, NULL);
|
||||
bt_att_chan_send_rsp(chan, BT_ATT_OP_READ_BY_GRP_TYPE_RSP,
|
||||
rsp_pdu, rsp_len);
|
||||
|
||||
return;
|
||||
|
||||
error:
|
||||
queue_destroy(q, NULL);
|
||||
bt_att_send_error_rsp(server->att, opcode, ehandle, ecode);
|
||||
bt_att_chan_send_error_rsp(chan, opcode, ehandle, ecode);
|
||||
}
|
||||
|
||||
static void async_read_op_destroy(struct async_read_op *op)
|
||||
@ -350,7 +352,7 @@ static void read_by_type_read_complete_cb(struct gatt_db_attribute *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,
|
||||
bt_att_chan_send_error_rsp(op->chan, BT_ATT_OP_READ_BY_TYPE_REQ,
|
||||
handle, err);
|
||||
async_read_op_destroy(op);
|
||||
return;
|
||||
@ -451,10 +453,8 @@ static void process_read_by_type(struct async_read_op *op)
|
||||
attr = queue_pop_head(op->db_data);
|
||||
|
||||
if (op->done || !attr) {
|
||||
bt_att_send(server->att, BT_ATT_OP_READ_BY_TYPE_RSP, op->pdu,
|
||||
op->pdu_len,
|
||||
NULL, NULL,
|
||||
NULL);
|
||||
bt_att_chan_send_rsp(op->chan, BT_ATT_OP_READ_BY_TYPE_RSP,
|
||||
op->pdu, op->pdu_len);
|
||||
async_read_op_destroy(op);
|
||||
return;
|
||||
}
|
||||
@ -472,13 +472,14 @@ static void process_read_by_type(struct async_read_op *op)
|
||||
ecode = BT_ATT_ERROR_UNLIKELY;
|
||||
|
||||
error:
|
||||
bt_att_send_error_rsp(server->att, BT_ATT_OP_READ_BY_TYPE_REQ,
|
||||
bt_att_chan_send_error_rsp(op->chan, BT_ATT_OP_READ_BY_TYPE_REQ,
|
||||
gatt_db_attribute_get_handle(attr), ecode);
|
||||
async_read_op_destroy(op);
|
||||
}
|
||||
|
||||
static void read_by_type_cb(uint8_t opcode, const void *pdu,
|
||||
uint16_t length, void *user_data)
|
||||
static void read_by_type_cb(struct bt_att_chan *chan, uint8_t opcode,
|
||||
const void *pdu, uint16_t length,
|
||||
void *user_data)
|
||||
{
|
||||
struct bt_gatt_server *server = user_data;
|
||||
uint16_t start, end;
|
||||
@ -535,6 +536,7 @@ static void read_by_type_cb(uint8_t opcode, const void *pdu,
|
||||
goto error;
|
||||
}
|
||||
|
||||
op->chan = chan;
|
||||
op->opcode = opcode;
|
||||
op->server = server;
|
||||
op->db_data = q;
|
||||
@ -545,7 +547,7 @@ static void read_by_type_cb(uint8_t opcode, const void *pdu,
|
||||
return;
|
||||
|
||||
error:
|
||||
bt_att_send_error_rsp(server->att, opcode, ehandle, ecode);
|
||||
bt_att_chan_send_error_rsp(chan, opcode, ehandle, ecode);
|
||||
queue_destroy(q, NULL);
|
||||
}
|
||||
|
||||
@ -603,8 +605,9 @@ static bool encode_find_info_rsp(struct gatt_db *db, struct queue *q,
|
||||
return true;
|
||||
}
|
||||
|
||||
static void find_info_cb(uint8_t opcode, const void *pdu,
|
||||
uint16_t length, void *user_data)
|
||||
static void find_info_cb(struct bt_att_chan *chan, uint8_t opcode,
|
||||
const void *pdu, uint16_t length,
|
||||
void *user_data)
|
||||
{
|
||||
struct bt_gatt_server *server = user_data;
|
||||
uint16_t start, end;
|
||||
@ -653,14 +656,14 @@ static void find_info_cb(uint8_t opcode, const void *pdu,
|
||||
goto error;
|
||||
}
|
||||
|
||||
bt_att_send(server->att, BT_ATT_OP_FIND_INFO_RSP, rsp_pdu, rsp_len,
|
||||
NULL, NULL, NULL);
|
||||
bt_att_chan_send_rsp(chan, BT_ATT_OP_FIND_INFO_RSP, rsp_pdu, rsp_len);
|
||||
|
||||
queue_destroy(q, NULL);
|
||||
|
||||
return;
|
||||
|
||||
error:
|
||||
bt_att_send_error_rsp(server->att, opcode, ehandle, ecode);
|
||||
bt_att_chan_send_error_rsp(chan, opcode, ehandle, ecode);
|
||||
queue_destroy(q, NULL);
|
||||
|
||||
}
|
||||
@ -702,8 +705,9 @@ static void find_by_type_val_att_cb(struct gatt_db_attribute *attrib,
|
||||
data->len += 4;
|
||||
}
|
||||
|
||||
static void find_by_type_val_cb(uint8_t opcode, const void *pdu,
|
||||
uint16_t length, void *user_data)
|
||||
static void find_by_type_val_cb(struct bt_att_chan *chan, uint8_t opcode,
|
||||
const void *pdu, uint16_t length,
|
||||
void *user_data)
|
||||
{
|
||||
struct bt_gatt_server *server = user_data;
|
||||
uint16_t start, end, uuid16;
|
||||
@ -748,13 +752,13 @@ static void find_by_type_val_cb(uint8_t opcode, const void *pdu,
|
||||
if (data.ecode)
|
||||
goto error;
|
||||
|
||||
bt_att_send(server->att, BT_ATT_OP_FIND_BY_TYPE_RSP, data.pdu,
|
||||
data.len, NULL, NULL, NULL);
|
||||
bt_att_chan_send_rsp(chan, BT_ATT_OP_FIND_BY_TYPE_RSP,
|
||||
data.pdu, data.len);
|
||||
|
||||
return;
|
||||
|
||||
error:
|
||||
bt_att_send_error_rsp(server->att, opcode, ehandle, data.ecode);
|
||||
bt_att_chan_send_error_rsp(chan, opcode, ehandle, data.ecode);
|
||||
}
|
||||
|
||||
static void async_write_op_destroy(struct async_write_op *op)
|
||||
@ -772,6 +776,9 @@ static void write_complete_cb(struct gatt_db_attribute *attr, int err,
|
||||
struct bt_gatt_server *server = op->server;
|
||||
uint16_t handle;
|
||||
|
||||
util_debug(server->debug_callback, server->debug_data,
|
||||
"Write Complete: err %d", err);
|
||||
|
||||
if (!server || op->opcode == BT_ATT_OP_WRITE_CMD) {
|
||||
async_write_op_destroy(op);
|
||||
return;
|
||||
@ -780,10 +787,9 @@ static void write_complete_cb(struct gatt_db_attribute *attr, int err,
|
||||
handle = gatt_db_attribute_get_handle(attr);
|
||||
|
||||
if (err)
|
||||
bt_att_send_error_rsp(server->att, op->opcode, handle, err);
|
||||
bt_att_chan_send_error_rsp(op->chan, op->opcode, handle, err);
|
||||
else
|
||||
bt_att_send(server->att, BT_ATT_OP_WRITE_RSP, NULL, 0,
|
||||
NULL, NULL, NULL);
|
||||
bt_att_chan_send_rsp(op->chan, BT_ATT_OP_WRITE_RSP, NULL, 0);
|
||||
|
||||
async_write_op_destroy(op);
|
||||
}
|
||||
@ -798,7 +804,7 @@ static uint8_t authorize_req(struct bt_gatt_server *server,
|
||||
server->authorize_data);
|
||||
}
|
||||
|
||||
static void write_cb(uint8_t opcode, const void *pdu,
|
||||
static void write_cb(struct bt_att_chan *chan, uint8_t opcode, const void *pdu,
|
||||
uint16_t length, void *user_data)
|
||||
{
|
||||
struct bt_gatt_server *server = user_data;
|
||||
@ -840,6 +846,7 @@ static void write_cb(uint8_t opcode, const void *pdu,
|
||||
}
|
||||
|
||||
op = new0(struct async_write_op, 1);
|
||||
op->chan = chan;
|
||||
op->server = server;
|
||||
op->opcode = opcode;
|
||||
server->pending_write_op = op;
|
||||
@ -857,7 +864,7 @@ error:
|
||||
if (opcode == BT_ATT_OP_WRITE_CMD)
|
||||
return;
|
||||
|
||||
bt_att_send_error_rsp(server->att, opcode, handle, ecode);
|
||||
bt_att_chan_send_error_rsp(chan, opcode, handle, ecode);
|
||||
}
|
||||
|
||||
static uint8_t get_read_rsp_opcode(uint8_t opcode)
|
||||
@ -893,6 +900,9 @@ static void read_complete_cb(struct gatt_db_attribute *attr, int err,
|
||||
uint16_t mtu;
|
||||
uint16_t handle;
|
||||
|
||||
util_debug(server->debug_callback, server->debug_data,
|
||||
"Read Complete: err %d", err);
|
||||
|
||||
if (!server) {
|
||||
async_read_op_destroy(op);
|
||||
return;
|
||||
@ -902,22 +912,21 @@ static void read_complete_cb(struct gatt_db_attribute *attr, int err,
|
||||
handle = gatt_db_attribute_get_handle(attr);
|
||||
|
||||
if (err) {
|
||||
bt_att_send_error_rsp(server->att, op->opcode, handle, err);
|
||||
bt_att_chan_send_error_rsp(op->chan, 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);
|
||||
bt_att_chan_send_rsp(op->chan, rsp_opcode, len ? value : NULL,
|
||||
MIN((unsigned int) mtu - 1, len));
|
||||
async_read_op_destroy(op);
|
||||
}
|
||||
|
||||
static void handle_read_req(struct bt_gatt_server *server, uint8_t opcode,
|
||||
uint16_t handle,
|
||||
uint16_t offset)
|
||||
static void handle_read_req(struct bt_att_chan *chan,
|
||||
struct bt_gatt_server *server, uint8_t opcode,
|
||||
uint16_t handle, uint16_t offset)
|
||||
{
|
||||
struct gatt_db_attribute *attr;
|
||||
uint8_t ecode;
|
||||
@ -950,6 +959,7 @@ static void handle_read_req(struct bt_gatt_server *server, uint8_t opcode,
|
||||
}
|
||||
|
||||
op = new0(struct async_read_op, 1);
|
||||
op->chan = chan;
|
||||
op->opcode = opcode;
|
||||
op->server = server;
|
||||
server->pending_read_op = op;
|
||||
@ -964,34 +974,35 @@ error:
|
||||
if (op)
|
||||
async_read_op_destroy(op);
|
||||
|
||||
bt_att_send_error_rsp(server->att, opcode, handle, ecode);
|
||||
bt_att_chan_send_error_rsp(chan, opcode, handle, ecode);
|
||||
}
|
||||
|
||||
static void read_cb(uint8_t opcode, const void *pdu,
|
||||
static void read_cb(struct bt_att_chan *chan, uint8_t opcode, const void *pdu,
|
||||
uint16_t length, void *user_data)
|
||||
{
|
||||
struct bt_gatt_server *server = user_data;
|
||||
uint16_t handle;
|
||||
|
||||
if (length != 2) {
|
||||
bt_att_send_error_rsp(server->att, opcode, 0,
|
||||
bt_att_chan_send_error_rsp(chan, opcode, 0,
|
||||
BT_ATT_ERROR_INVALID_PDU);
|
||||
return;
|
||||
}
|
||||
|
||||
handle = get_le16(pdu);
|
||||
|
||||
handle_read_req(server, opcode, handle, 0);
|
||||
handle_read_req(chan, server, opcode, handle, 0);
|
||||
}
|
||||
|
||||
static void read_blob_cb(uint8_t opcode, const void *pdu,
|
||||
uint16_t length, void *user_data)
|
||||
static void read_blob_cb(struct bt_att_chan *chan, uint8_t opcode,
|
||||
const void *pdu, uint16_t length,
|
||||
void *user_data)
|
||||
{
|
||||
struct bt_gatt_server *server = user_data;
|
||||
uint16_t handle, offset;
|
||||
|
||||
if (length != 4) {
|
||||
bt_att_send_error_rsp(server->att, opcode, 0,
|
||||
bt_att_chan_send_error_rsp(chan, opcode, 0,
|
||||
BT_ATT_ERROR_INVALID_PDU);
|
||||
return;
|
||||
}
|
||||
@ -999,10 +1010,11 @@ static void read_blob_cb(uint8_t opcode, const void *pdu,
|
||||
handle = get_le16(pdu);
|
||||
offset = get_le16(pdu + 2);
|
||||
|
||||
handle_read_req(server, opcode, handle, offset);
|
||||
handle_read_req(chan, server, opcode, handle, offset);
|
||||
}
|
||||
|
||||
struct read_multiple_resp_data {
|
||||
struct bt_att_chan *chan;
|
||||
struct bt_gatt_server *server;
|
||||
uint16_t *handles;
|
||||
size_t cur_handle;
|
||||
@ -1029,7 +1041,7 @@ static void read_multiple_complete_cb(struct gatt_db_attribute *attr, int err,
|
||||
uint8_t ecode;
|
||||
|
||||
if (err != 0) {
|
||||
bt_att_send_error_rsp(data->server->att,
|
||||
bt_att_chan_send_error_rsp(data->chan,
|
||||
BT_ATT_OP_READ_MULT_REQ, handle, err);
|
||||
read_multiple_resp_data_free(data);
|
||||
return;
|
||||
@ -1039,7 +1051,7 @@ static void read_multiple_complete_cb(struct gatt_db_attribute *attr, int err,
|
||||
BT_ATT_PERM_READ_AUTHEN |
|
||||
BT_ATT_PERM_READ_ENCRYPT);
|
||||
if (ecode) {
|
||||
bt_att_send_error_rsp(data->server->att,
|
||||
bt_att_chan_send_error_rsp(data->chan,
|
||||
BT_ATT_OP_READ_MULT_REQ, handle, ecode);
|
||||
read_multiple_resp_data_free(data);
|
||||
return;
|
||||
@ -1054,8 +1066,8 @@ static void read_multiple_complete_cb(struct gatt_db_attribute *attr, int err,
|
||||
|
||||
if ((data->length >= data->mtu - 1) ||
|
||||
(data->cur_handle == data->num_handles)) {
|
||||
bt_att_send(data->server->att, BT_ATT_OP_READ_MULT_RSP,
|
||||
data->rsp_data, data->length, NULL, NULL, NULL);
|
||||
bt_att_chan_send_rsp(data->chan, BT_ATT_OP_READ_MULT_RSP,
|
||||
data->rsp_data, data->length);
|
||||
read_multiple_resp_data_free(data);
|
||||
return;
|
||||
}
|
||||
@ -1069,7 +1081,7 @@ static void read_multiple_complete_cb(struct gatt_db_attribute *attr, int err,
|
||||
data->handles[data->cur_handle]);
|
||||
|
||||
if (!next_attr) {
|
||||
bt_att_send_error_rsp(data->server->att,
|
||||
bt_att_chan_send_error_rsp(data->chan,
|
||||
BT_ATT_OP_READ_MULT_REQ,
|
||||
data->handles[data->cur_handle],
|
||||
BT_ATT_ERROR_INVALID_HANDLE);
|
||||
@ -1080,7 +1092,7 @@ static void read_multiple_complete_cb(struct gatt_db_attribute *attr, int err,
|
||||
if (!gatt_db_attribute_read(next_attr, 0, BT_ATT_OP_READ_MULT_REQ,
|
||||
data->server->att,
|
||||
read_multiple_complete_cb, data)) {
|
||||
bt_att_send_error_rsp(data->server->att,
|
||||
bt_att_chan_send_error_rsp(data->chan,
|
||||
BT_ATT_OP_READ_MULT_REQ,
|
||||
data->handles[data->cur_handle],
|
||||
BT_ATT_ERROR_UNLIKELY);
|
||||
@ -1088,8 +1100,9 @@ static void read_multiple_complete_cb(struct gatt_db_attribute *attr, int err,
|
||||
}
|
||||
}
|
||||
|
||||
static void read_multiple_cb(uint8_t opcode, const void *pdu,
|
||||
uint16_t length, void *user_data)
|
||||
static void read_multiple_cb(struct bt_att_chan *chan, uint8_t opcode,
|
||||
const void *pdu, uint16_t length,
|
||||
void *user_data)
|
||||
{
|
||||
struct bt_gatt_server *server = user_data;
|
||||
struct gatt_db_attribute *attr;
|
||||
@ -1103,6 +1116,7 @@ static void read_multiple_cb(uint8_t opcode, const void *pdu,
|
||||
}
|
||||
|
||||
data = new0(struct read_multiple_resp_data, 1);
|
||||
data->chan = chan;
|
||||
data->handles = NULL;
|
||||
data->rsp_data = NULL;
|
||||
data->server = server;
|
||||
@ -1139,7 +1153,7 @@ error:
|
||||
if (data)
|
||||
read_multiple_resp_data_free(data);
|
||||
|
||||
bt_att_send_error_rsp(server->att, opcode, 0, ecode);
|
||||
bt_att_chan_send_error_rsp(chan, opcode, 0, ecode);
|
||||
}
|
||||
|
||||
static bool append_prep_data(struct prep_write_data *prep_data, uint16_t handle,
|
||||
@ -1230,6 +1244,7 @@ static bool store_prep_data(struct bt_gatt_server *server,
|
||||
}
|
||||
|
||||
struct prep_write_complete_data {
|
||||
struct bt_att_chan *chan;
|
||||
void *pdu;
|
||||
uint16_t length;
|
||||
struct bt_gatt_server *server;
|
||||
@ -1245,8 +1260,8 @@ static void prep_write_complete_cb(struct gatt_db_attribute *attr, int err,
|
||||
handle = get_le16(pwcd->pdu);
|
||||
|
||||
if (err) {
|
||||
bt_att_send_error_rsp(pwcd->server->att,
|
||||
BT_ATT_OP_PREP_WRITE_REQ, handle, err);
|
||||
bt_att_chan_send_error_rsp(pwcd->chan, BT_ATT_OP_PREP_WRITE_REQ,
|
||||
handle, err);
|
||||
free(pwcd->pdu);
|
||||
free(pwcd);
|
||||
|
||||
@ -1257,19 +1272,20 @@ static void prep_write_complete_cb(struct gatt_db_attribute *attr, int err,
|
||||
|
||||
if (!store_prep_data(pwcd->server, handle, offset, pwcd->length - 4,
|
||||
&((uint8_t *) pwcd->pdu)[4]))
|
||||
bt_att_send_error_rsp(pwcd->server->att,
|
||||
BT_ATT_OP_PREP_WRITE_RSP, handle,
|
||||
bt_att_chan_send_error_rsp(pwcd->chan, BT_ATT_OP_PREP_WRITE_RSP,
|
||||
handle,
|
||||
BT_ATT_ERROR_INSUFFICIENT_RESOURCES);
|
||||
|
||||
bt_att_send(pwcd->server->att, BT_ATT_OP_PREP_WRITE_RSP, pwcd->pdu,
|
||||
pwcd->length, NULL, NULL, NULL);
|
||||
bt_att_chan_send_rsp(pwcd->chan, BT_ATT_OP_PREP_WRITE_RSP, pwcd->pdu,
|
||||
pwcd->length);
|
||||
|
||||
free(pwcd->pdu);
|
||||
free(pwcd);
|
||||
}
|
||||
|
||||
static void prep_write_cb(uint8_t opcode, const void *pdu,
|
||||
uint16_t length, void *user_data)
|
||||
static void prep_write_cb(struct bt_att_chan *chan, uint8_t opcode,
|
||||
const void *pdu, uint16_t length,
|
||||
void *user_data)
|
||||
{
|
||||
struct bt_gatt_server *server = user_data;
|
||||
uint16_t handle = 0;
|
||||
@ -1307,6 +1323,7 @@ static void prep_write_cb(uint8_t opcode, const void *pdu,
|
||||
goto error;
|
||||
|
||||
pwcd = new0(struct prep_write_complete_data, 1);
|
||||
pwcd->chan = chan;
|
||||
pwcd->pdu = malloc(length);
|
||||
memcpy(pwcd->pdu, pdu, length);
|
||||
pwcd->length = length;
|
||||
@ -1323,23 +1340,28 @@ static void prep_write_cb(uint8_t opcode, const void *pdu,
|
||||
ecode = BT_ATT_ERROR_UNLIKELY;
|
||||
|
||||
error:
|
||||
bt_att_send_error_rsp(server->att, opcode, handle, ecode);
|
||||
bt_att_chan_send_error_rsp(chan, opcode, handle, ecode);
|
||||
}
|
||||
|
||||
static void exec_next_prep_write(struct bt_gatt_server *server,
|
||||
uint16_t ehandle, int err);
|
||||
struct exec_data {
|
||||
struct bt_att_chan *chan;
|
||||
struct bt_gatt_server *server;
|
||||
};
|
||||
|
||||
static void exec_next_prep_write(struct exec_data *data, uint16_t ehandle,
|
||||
int err);
|
||||
|
||||
static void exec_write_complete_cb(struct gatt_db_attribute *attr, int err,
|
||||
void *user_data)
|
||||
{
|
||||
struct bt_gatt_server *server = user_data;
|
||||
struct exec_data *data = user_data;
|
||||
uint16_t handle = gatt_db_attribute_get_handle(attr);
|
||||
|
||||
exec_next_prep_write(server, handle, err);
|
||||
exec_next_prep_write(data, handle, err);
|
||||
}
|
||||
|
||||
static void exec_next_prep_write(struct bt_gatt_server *server,
|
||||
uint16_t ehandle, int err)
|
||||
static void exec_next_prep_write(struct exec_data *data, uint16_t ehandle,
|
||||
int err)
|
||||
{
|
||||
struct prep_write_data *next = NULL;
|
||||
struct gatt_db_attribute *attr;
|
||||
@ -1348,14 +1370,15 @@ static void exec_next_prep_write(struct bt_gatt_server *server,
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
next = queue_pop_head(server->prep_queue);
|
||||
next = queue_pop_head(data->server->prep_queue);
|
||||
if (!next) {
|
||||
bt_att_send(server->att, BT_ATT_OP_EXEC_WRITE_RSP, NULL, 0,
|
||||
NULL, NULL, NULL);
|
||||
bt_att_chan_send_rsp(data->chan, BT_ATT_OP_EXEC_WRITE_RSP,
|
||||
NULL, 0);
|
||||
free(data);
|
||||
return;
|
||||
}
|
||||
|
||||
attr = gatt_db_get_attribute(server->db, next->handle);
|
||||
attr = gatt_db_get_attribute(data->server->db, next->handle);
|
||||
if (!attr) {
|
||||
err = BT_ATT_ERROR_UNLIKELY;
|
||||
goto error;
|
||||
@ -1364,8 +1387,8 @@ static void exec_next_prep_write(struct bt_gatt_server *server,
|
||||
status = gatt_db_attribute_write(attr, next->offset,
|
||||
next->value, next->length,
|
||||
BT_ATT_OP_EXEC_WRITE_REQ,
|
||||
server->att,
|
||||
exec_write_complete_cb, server);
|
||||
data->server->att,
|
||||
exec_write_complete_cb, data);
|
||||
|
||||
prep_write_data_destroy(next);
|
||||
|
||||
@ -1375,11 +1398,12 @@ static void exec_next_prep_write(struct bt_gatt_server *server,
|
||||
err = BT_ATT_ERROR_UNLIKELY;
|
||||
|
||||
error:
|
||||
queue_remove_all(server->prep_queue, NULL, NULL,
|
||||
queue_remove_all(data->server->prep_queue, NULL, NULL,
|
||||
prep_write_data_destroy);
|
||||
|
||||
bt_att_send_error_rsp(server->att, BT_ATT_OP_EXEC_WRITE_REQ,
|
||||
bt_att_chan_send_error_rsp(data->chan, BT_ATT_OP_EXEC_WRITE_REQ,
|
||||
ehandle, err);
|
||||
free(data);
|
||||
}
|
||||
|
||||
static bool find_no_reliable_characteristic(const void *data,
|
||||
@ -1390,10 +1414,12 @@ static bool find_no_reliable_characteristic(const void *data,
|
||||
return !prep_data->reliable_supported;
|
||||
}
|
||||
|
||||
static void exec_write_cb(uint8_t opcode, const void *pdu,
|
||||
uint16_t length, void *user_data)
|
||||
static void exec_write_cb(struct bt_att_chan *chan, uint8_t opcode,
|
||||
const void *pdu, uint16_t length,
|
||||
void *user_data)
|
||||
{
|
||||
struct bt_gatt_server *server = user_data;
|
||||
struct exec_data *data;
|
||||
uint8_t flags;
|
||||
uint8_t ecode;
|
||||
bool write;
|
||||
@ -1421,8 +1447,7 @@ static void exec_write_cb(uint8_t opcode, const void *pdu,
|
||||
if (!write) {
|
||||
queue_remove_all(server->prep_queue, NULL, NULL,
|
||||
prep_write_data_destroy);
|
||||
bt_att_send(server->att, BT_ATT_OP_EXEC_WRITE_RSP, NULL, 0,
|
||||
NULL, NULL, NULL);
|
||||
bt_att_chan_send_rsp(chan, BT_ATT_OP_EXEC_WRITE_RSP, NULL, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1439,18 +1464,23 @@ static void exec_write_cb(uint8_t opcode, const void *pdu,
|
||||
}
|
||||
}
|
||||
|
||||
exec_next_prep_write(server, 0, 0);
|
||||
data = new0(struct exec_data, 1);
|
||||
data->chan = chan;
|
||||
data->server = server;
|
||||
|
||||
exec_next_prep_write(data, 0, 0);
|
||||
|
||||
return;
|
||||
|
||||
error:
|
||||
queue_remove_all(server->prep_queue, NULL, NULL,
|
||||
prep_write_data_destroy);
|
||||
bt_att_send_error_rsp(server->att, opcode, ehandle, ecode);
|
||||
bt_att_chan_send_error_rsp(chan, opcode, ehandle, ecode);
|
||||
}
|
||||
|
||||
static void exchange_mtu_cb(uint8_t opcode, const void *pdu,
|
||||
uint16_t length, void *user_data)
|
||||
static void exchange_mtu_cb(struct bt_att_chan *chan, uint8_t opcode,
|
||||
const void *pdu, uint16_t length,
|
||||
void *user_data)
|
||||
{
|
||||
struct bt_gatt_server *server = user_data;
|
||||
uint16_t client_rx_mtu;
|
||||
@ -1458,7 +1488,7 @@ static void exchange_mtu_cb(uint8_t opcode, const void *pdu,
|
||||
uint8_t rsp_pdu[2];
|
||||
|
||||
if (length != 2) {
|
||||
bt_att_send_error_rsp(server->att, opcode, 0,
|
||||
bt_att_chan_send_error_rsp(chan, opcode, 0,
|
||||
BT_ATT_ERROR_INVALID_PDU);
|
||||
return;
|
||||
}
|
||||
@ -1468,8 +1498,7 @@ static void exchange_mtu_cb(uint8_t opcode, const void *pdu,
|
||||
|
||||
/* Respond with the server MTU */
|
||||
put_le16(server->mtu, rsp_pdu);
|
||||
bt_att_send(server->att, BT_ATT_OP_MTU_RSP, rsp_pdu, 2, NULL, NULL,
|
||||
NULL);
|
||||
bt_att_chan_send_rsp(chan, BT_ATT_OP_MTU_RSP, rsp_pdu, 2);
|
||||
|
||||
/* Set MTU to be the minimum */
|
||||
server->mtu = final_mtu;
|
||||
|
@ -218,7 +218,7 @@ static struct client *client_create(int fd, uint16_t mtu)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cli->gatt = bt_gatt_client_new(cli->db, cli->att, mtu);
|
||||
cli->gatt = bt_gatt_client_new(cli->db, cli->att, mtu, 0);
|
||||
if (!cli->gatt) {
|
||||
fprintf(stderr, "Failed to create GATT client\n");
|
||||
gatt_db_unref(cli->db);
|
||||
|
@ -124,8 +124,16 @@ struct context {
|
||||
raw_pdu(0x02, 0x00, 0x02), \
|
||||
raw_pdu(0x03, 0x00, 0x02)
|
||||
|
||||
#define SERVICE_DATA_1_PDUS \
|
||||
#define READ_SERVER_FEAT_PDUS \
|
||||
raw_pdu(0x08, 0x01, 0x00, 0xff, 0xff, 0x3a, 0x2b), \
|
||||
raw_pdu(0x01, 0x08, 0x01, 0x00, 0x0a)
|
||||
|
||||
#define CLIENT_INIT_PDUS \
|
||||
MTU_EXCHANGE_CLIENT_PDUS, \
|
||||
READ_SERVER_FEAT_PDUS
|
||||
|
||||
#define SERVICE_DATA_1_PDUS \
|
||||
CLIENT_INIT_PDUS, \
|
||||
raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28), \
|
||||
raw_pdu(0x11, 0x06, 0x01, 0x00, 0x04, 0x00, 0x01, 0x18),\
|
||||
raw_pdu(0x10, 0x05, 0x00, 0xff, 0xff, 0x00, 0x28), \
|
||||
@ -150,7 +158,7 @@ struct context {
|
||||
raw_pdu(0x05, 0x01, 0x08, 0x00, 0x01, 0x29)
|
||||
|
||||
#define SERVICE_DATA_2_PDUS \
|
||||
MTU_EXCHANGE_CLIENT_PDUS, \
|
||||
CLIENT_INIT_PDUS, \
|
||||
raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28), \
|
||||
raw_pdu(0x11, 0x06, 0x01, 0x00, 0x04, 0x00, 0x01, 0x18),\
|
||||
raw_pdu(0x10, 0x05, 0x00, 0xff, 0xff, 0x00, 0x28), \
|
||||
@ -175,7 +183,7 @@ struct context {
|
||||
raw_pdu(0x05, 0x01, 0x0a, 0x00, 0x01, 0x29)
|
||||
|
||||
#define SERVICE_DATA_3_PDUS \
|
||||
MTU_EXCHANGE_CLIENT_PDUS, \
|
||||
CLIENT_INIT_PDUS, \
|
||||
raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28), \
|
||||
raw_pdu(0x11, 0x06, 0x00, 0x01, 0x21, 0x01, 0x00, 0x18, \
|
||||
0x00, 0x02, 0x00, 0x02, 0x01, 0x18), \
|
||||
@ -683,7 +691,7 @@ static struct context *create_context(uint16_t mtu, gconstpointer data)
|
||||
g_assert(context->client_db);
|
||||
|
||||
context->client = bt_gatt_client_new(context->client_db,
|
||||
context->att, mtu);
|
||||
context->att, mtu, 0);
|
||||
g_assert(context->client);
|
||||
|
||||
bt_gatt_client_set_debug(context->client, print_debug,
|
||||
@ -2371,8 +2379,7 @@ int main(int argc, char *argv[])
|
||||
* Discovery of Services and Service Characteristics.
|
||||
*/
|
||||
define_test_att("/TP/GAD/CL/BV-01-C", test_search_primary, NULL, NULL,
|
||||
raw_pdu(0x02, 0x00, 0x02),
|
||||
raw_pdu(0x03, 0x00, 0x02),
|
||||
MTU_EXCHANGE_CLIENT_PDUS,
|
||||
raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28),
|
||||
raw_pdu(0x11, 0x06, 0x10, 0x00, 0x13, 0x00, 0x00, 0x18,
|
||||
0x20, 0x00, 0x29, 0x00, 0xb0, 0x68,
|
||||
@ -3245,7 +3252,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
define_test_client("/TP/GAN/CL/BV-01-C", test_client, ts_small_db,
|
||||
&test_notification_1,
|
||||
MTU_EXCHANGE_CLIENT_PDUS,
|
||||
CLIENT_INIT_PDUS,
|
||||
SMALL_DB_DISCOVERY_PDUS,
|
||||
raw_pdu(0x12, 0x04, 0x00, 0x03, 0x00),
|
||||
raw_pdu(0x13),
|
||||
@ -3271,7 +3278,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
define_test_client("/TP/GAI/CL/BV-01-C", test_client, ts_small_db,
|
||||
&test_indication_1,
|
||||
MTU_EXCHANGE_CLIENT_PDUS,
|
||||
CLIENT_INIT_PDUS,
|
||||
SMALL_DB_DISCOVERY_PDUS,
|
||||
raw_pdu(0x12, 0x04, 0x00, 0x03, 0x00),
|
||||
raw_pdu(0x13),
|
||||
|
Loading…
Reference in New Issue
Block a user