static bool disconnect_cb(struct io *io, void *user_data) { struct bt_att *att = user_data; int err; socklen_t len; len = sizeof(err); if (getsockopt(att->fd, SOL_SOCKET, SO_ERROR, &err, &len) < 0) { util_debug(att->debug_callback, att->debug_data, "Failed to obtain disconnect error: %s", strerror(errno)); err = 0; } util_debug(att->debug_callback, att->debug_data, "Physical link disconnected: %s", strerror(err)); io_destroy(att->io); att->io = NULL; bt_att_cancel_all(att); bt_att_ref(att); queue_foreach(att->disconn_list, disconn_handler, INT_TO_PTR(err)); bt_att_unregister_all(att); bt_att_unref(att); return false; }
struct bt_gatt_server *bt_gatt_server_new(struct gatt_db *db, struct bt_att *att, uint16_t mtu) { struct bt_gatt_server *server; if (!att || !db) return NULL; server = new0(struct bt_gatt_server, 1); if (!server) return NULL; server->db = gatt_db_ref(db); server->att = bt_att_ref(att); server->mtu = MAX(mtu, BT_ATT_DEFAULT_LE_MTU); server->max_prep_queue_len = DEFAULT_MAX_PREP_QUEUE_LEN; server->prep_queue = queue_new(); if (!server->prep_queue) { bt_gatt_server_free(server); return NULL; } if (!gatt_server_register_att_handlers(server)) { bt_gatt_server_free(server); return NULL; } return bt_gatt_server_ref(server); }
static void handle_notify(struct bt_att *att, uint8_t opcode, uint8_t *pdu, ssize_t pdu_len) { struct notify_data data; bt_att_ref(att); att->in_notify = true; memset(&data, 0, sizeof(data)); data.opcode = opcode; if (pdu_len > 0) { data.pdu = pdu; data.pdu_len = pdu_len; } queue_foreach(att->notify_list, notify_handler, &data); att->in_notify = false; if (att->need_notify_cleanup) { queue_remove_all(att->notify_list, match_notify_removed, NULL, destroy_att_notify); att->need_notify_cleanup = false; } bt_att_unref(att); }
struct bt_att *bt_att_new(int fd) { struct bt_att *att; if (fd < 0) return NULL; att = new0(struct bt_att, 1); if (!att) return NULL; att->fd = fd; att->mtu = ATT_DEFAULT_LE_MTU; att->buf = malloc(att->mtu); if (!att->buf) goto fail; att->io = io_new(fd); if (!att->io) goto fail; att->req_queue = queue_new(); if (!att->req_queue) goto fail; att->ind_queue = queue_new(); if (!att->ind_queue) goto fail; att->write_queue = queue_new(); if (!att->write_queue) goto fail; att->notify_list = queue_new(); if (!att->notify_list) goto fail; if (!io_set_read_handler(att->io, can_read_data, att, NULL)) goto fail; return bt_att_ref(att); fail: queue_destroy(att->req_queue, NULL); queue_destroy(att->ind_queue, NULL); queue_destroy(att->write_queue, NULL); io_destroy(att->io); free(att->buf); free(att); return NULL; }
static void handle_notify(struct bt_att *att, uint8_t opcode, uint8_t *pdu, ssize_t pdu_len) { const struct queue_entry *entry; bool found; if ((opcode & ATT_OP_SIGNED_MASK) && !att->crypto) { if (!handle_signed(att, opcode, pdu, pdu_len)) return; pdu_len -= BT_ATT_SIGNATURE_LEN; } bt_att_ref(att); found = false; entry = queue_get_entries(att->notify_list); while (entry) { struct att_notify *notify = entry->data; entry = entry->next; if (!opcode_match(notify->opcode, opcode)) continue; found = true; if (notify->callback) notify->callback(opcode, pdu, pdu_len, notify->user_data); /* callback could remove all entries from notify list */ if (queue_isempty(att->notify_list)) break; } /* * If this was a request and no handler was registered for it, respond * with "Not Supported" */ if (!found && get_op_type(opcode) == ATT_OP_TYPE_REQ) respond_not_supported(att, opcode); bt_att_unref(att); }
struct bt_att *bt_att_new(int fd, bool ext_signed) { struct bt_att *att; if (fd < 0) return NULL; att = new0(struct bt_att, 1); if (!att) return NULL; att->fd = fd; att->mtu = BT_ATT_DEFAULT_LE_MTU; att->buf = malloc(att->mtu); if (!att->buf) goto fail; att->io = io_new(fd); if (!att->io) goto fail; /* crypto is optional, if not available leave it NULL */ if (!ext_signed) att->crypto = bt_crypto_new(); att->req_queue = queue_new(); if (!att->req_queue) goto fail; att->ind_queue = queue_new(); if (!att->ind_queue) goto fail; att->write_queue = queue_new(); if (!att->write_queue) goto fail; att->notify_list = queue_new(); if (!att->notify_list) goto fail; att->disconn_list = queue_new(); if (!att->disconn_list) goto fail; if (!io_set_read_handler(att->io, can_read_data, att, NULL)) goto fail; if (!io_set_disconnect_handler(att->io, disconnect_cb, att, NULL)) goto fail; att->io_on_l2cap = is_io_l2cap_based(att->fd); if (!att->io_on_l2cap) att->io_sec_level = BT_SECURITY_LOW; return bt_att_ref(att); fail: bt_att_free(att); return NULL; }
static bool can_read_data(struct io *io, void *user_data) { struct bt_att *att = user_data; uint8_t opcode; uint8_t *pdu; ssize_t bytes_read; bytes_read = read(att->fd, att->buf, att->mtu); if (bytes_read < 0) return false; util_hexdump('>', att->buf, bytes_read, att->debug_callback, att->debug_data); if (bytes_read < ATT_MIN_PDU_LEN) return true; pdu = att->buf; opcode = pdu[0]; bt_att_ref(att); /* Act on the received PDU based on the opcode type */ switch (get_op_type(opcode)) { case ATT_OP_TYPE_RSP: util_debug(att->debug_callback, att->debug_data, "ATT response received: 0x%02x", opcode); handle_rsp(att, 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); handle_conf(att, pdu + 1, bytes_read - 1); break; case ATT_OP_TYPE_REQ: /* * If a request is currently pending, then the sequential * protocol was violated. Disconnect the bearer, which will * promptly notify the upper layer via disconnect handlers. */ if (att->in_req) { util_debug(att->debug_callback, att->debug_data, "Received request while another is " "pending: 0x%02x", opcode); io_shutdown(att->io); bt_att_unref(att); return false; } att->in_req = true; /* Fall through to the next case */ case ATT_OP_TYPE_CMD: case ATT_OP_TYPE_NOT: case ATT_OP_TYPE_UNKNOWN: case ATT_OP_TYPE_IND: default: /* For all other opcodes notify the upper layer of the PDU and * let them act on it. */ util_debug(att->debug_callback, att->debug_data, "ATT PDU received: 0x%02x", opcode); handle_notify(att, opcode, pdu + 1, bytes_read - 1); break; } bt_att_unref(att); return true; }