static struct client *client_create(int fd, uint16_t mtu) { struct client *cli; struct bt_att *att; cli = new0(struct client, 1); if (!cli) { fprintf(stderr, "Failed to allocate memory for client\n"); return NULL; } att = bt_att_new(fd); if (!att) { fprintf(stderr, "Failed to initialze ATT transport layer\n"); bt_att_unref(att); free(cli); return NULL; } if (!bt_att_set_close_on_unref(att, true)) { fprintf(stderr, "Failed to set up ATT transport layer\n"); bt_att_unref(att); free(cli); return NULL; } if (!bt_att_register_disconnect(att, att_disconnect_cb, NULL, NULL)) { fprintf(stderr, "Failed to set ATT disconnect handler\n"); bt_att_unref(att); free(cli); return NULL; } cli->fd = fd; cli->gatt = bt_gatt_client_new(att, mtu); if (!cli->gatt) { fprintf(stderr, "Failed to create GATT client\n"); bt_att_unref(att); free(cli); return NULL; } if (verbose) { bt_att_set_debug(att, att_debug_cb, "att: ", NULL); bt_gatt_client_set_debug(cli->gatt, gatt_debug_cb, "gatt: ", NULL); } bt_gatt_client_set_ready_handler(cli->gatt, ready_cb, cli, NULL); bt_gatt_client_set_service_changed(cli->gatt, service_changed_cb, cli, NULL); /* bt_gatt_client already holds a reference */ bt_att_unref(att); return cli; }
static void bt_gatt_server_free(struct bt_gatt_server *server) { if (server->debug_destroy) server->debug_destroy(server->debug_data); bt_att_unregister(server->att, server->mtu_id); bt_att_unregister(server->att, server->read_by_grp_type_id); bt_att_unregister(server->att, server->read_by_type_id); bt_att_unregister(server->att, server->find_info_id); bt_att_unregister(server->att, server->find_by_type_value_id); bt_att_unregister(server->att, server->write_id); bt_att_unregister(server->att, server->write_cmd_id); bt_att_unregister(server->att, server->read_id); bt_att_unregister(server->att, server->read_blob_id); bt_att_unregister(server->att, server->read_multiple_id); bt_att_unregister(server->att, server->prep_write_id); bt_att_unregister(server->att, server->exec_write_id); if (server->pending_read_op) server->pending_read_op->server = NULL; if (server->pending_write_op) server->pending_write_op->server = NULL; queue_destroy(server->prep_queue, prep_write_data_destroy); gatt_db_unref(server->db); bt_att_unref(server->att); free(server); }
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; }
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); }
static struct gatt_conn *gatt_conn_new(int fd) { struct gatt_conn *conn; uint16_t mtu = 0; conn = new0(struct gatt_conn, 1); if (!conn) return NULL; conn->att = bt_att_new(fd, false); if (!conn->att) { fprintf(stderr, "Failed to initialze ATT transport layer\n"); free(conn); return NULL; } bt_att_set_close_on_unref(conn->att, true); bt_att_register_disconnect(conn->att, gatt_conn_disconnect, conn, NULL); bt_att_set_security(conn->att, BT_SECURITY_MEDIUM); conn->gatt = bt_gatt_server_new(gatt_db, conn->att, mtu); if (!conn->gatt) { fprintf(stderr, "Failed to create GATT server\n"); bt_att_unref(conn->att); free(conn); return NULL; } conn->client = bt_gatt_client_new(gatt_cache, conn->att, mtu); if (!conn->gatt) { fprintf(stderr, "Failed to create GATT client\n"); bt_gatt_server_unref(conn->gatt); bt_att_unref(conn->att); free(conn); return NULL; } bt_gatt_client_set_ready_handler(conn->client, client_ready_callback, conn, NULL); bt_gatt_client_set_service_changed(conn->client, client_service_changed_callback, conn, NULL); return conn; }
static void gatt_conn_destroy(void *data) { struct gatt_conn *conn = data; bt_gatt_client_unref(conn->client); bt_gatt_server_unref(conn->gatt); bt_att_unref(conn->att); free(conn); }
GAttrib *g_attrib_new(GIOChannel *io, guint16 mtu, bool ext_signed) { gint fd; GAttrib *attr; if (!io) return NULL; fd = g_io_channel_unix_get_fd(io); attr = new0(GAttrib, 1); if (!attr) return NULL; g_io_channel_ref(io); attr->io = io; attr->att = bt_att_new(fd, ext_signed); if (!attr->att) goto fail; bt_att_set_close_on_unref(attr->att, true); g_io_channel_set_close_on_unref(io, FALSE); if (!bt_att_set_mtu(attr->att, mtu)) goto fail; attr->buf = malloc0(mtu); attr->buflen = mtu; if (!attr->buf) goto fail; attr->callbacks = queue_new(); if (!attr->callbacks) goto fail; attr->track_ids = queue_new(); if (!attr->track_ids) goto fail; return g_attrib_ref(attr); fail: free(attr->buf); bt_att_unref(attr->att); g_io_channel_unref(io); free(attr); 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); }
void g_attrib_unref(GAttrib *attrib) { if (!attrib) return; DBG("%p: g_attrib_unref=%d ", attrib, attrib->ref_count - 1); if (__sync_sub_and_fetch(&attrib->ref_count, 1)) return; if (attrib->destroy) attrib->destroy(attrib->destroy_user_data); bt_att_unref(attrib->att); queue_destroy(attrib->callbacks, attrib_callbacks_destroy); queue_destroy(attrib->track_ids, free); free(attrib->buf); g_io_channel_unref(attrib->io); free(attrib); }
static struct server *server_create(int fd, uint16_t mtu, bool hr_visible) { struct server *server; size_t name_len = strlen(test_device_name); server = new0(struct server, 1); if (!server) { fprintf(stderr, "Failed to allocate memory for server\n"); return NULL; } server->att = bt_att_new(fd, false); if (!server->att) { fprintf(stderr, "Failed to initialze ATT transport layer\n"); goto fail; } if (!bt_att_set_close_on_unref(server->att, true)) { fprintf(stderr, "Failed to set up ATT transport layer\n"); goto fail; } if (!bt_att_register_disconnect(server->att, att_disconnect_cb, NULL, NULL)) { fprintf(stderr, "Failed to set ATT disconnect handler\n"); goto fail; } server->name_len = name_len + 1; server->device_name = malloc(name_len + 1); if (!server->device_name) { fprintf(stderr, "Failed to allocate memory for device name\n"); goto fail; } memcpy(server->device_name, test_device_name, name_len); server->device_name[name_len] = '\0'; server->fd = fd; server->db = gatt_db_new(); if (!server->db) { fprintf(stderr, "Failed to create GATT database\n"); goto fail; } server->gatt = bt_gatt_server_new(server->db, server->att, mtu); if (!server->gatt) { fprintf(stderr, "Failed to create GATT server\n"); goto fail; } server->hr_visible = hr_visible; if (verbose) { bt_att_set_debug(server->att, att_debug_cb, "att: ", NULL); bt_gatt_server_set_debug(server->gatt, gatt_debug_cb, "server: ", NULL); } /* Random seed for generating fake Heart Rate measurements */ srand(time(NULL)); /* bt_gatt_server already holds a reference */ populate_db(server); return server; fail: gatt_db_unref(server->db); free(server->device_name); bt_att_unref(server->att); free(server); 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; }
static void client_destroy(struct client *cli) { bt_gatt_client_unref(cli->gatt); bt_att_unref(cli->att); free(cli); }