GAttrib *g_attrib_new(GIOChannel *io) { struct _GAttrib *attrib; uint16_t omtu; g_io_channel_set_encoding(io, NULL, NULL); g_io_channel_set_buffered(io, FALSE); attrib = g_try_new0(struct _GAttrib, 1); if (attrib == NULL) return NULL; attrib->io = g_io_channel_ref(io); attrib->queue = g_queue_new(); attrib->read_watch = g_io_add_watch(attrib->io, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, received_data, attrib); if (bt_io_get(attrib->io, BT_IO_L2CAP, NULL, BT_IO_OPT_OMTU, &omtu, BT_IO_OPT_INVALID)) { if (omtu == 0 || omtu > ATT_MAX_MTU) omtu = ATT_MAX_MTU; } else omtu = ATT_DEFAULT_LE_MTU; attrib->buf = g_malloc0(omtu); attrib->buflen = omtu; return g_attrib_ref(attrib); }
guint gatt_discover_char(GAttrib *attrib, uint16_t start, uint16_t end, bt_uuid_t *uuid, gatt_cb_t func, gpointer user_data) { size_t buflen; uint8_t *buf = g_attrib_get_buffer(attrib, &buflen); struct discover_char *dc; bt_uuid_t type_uuid; guint16 plen; bt_uuid16_create(&type_uuid, GATT_CHARAC_UUID); plen = enc_read_by_type_req(start, end, &type_uuid, buf, buflen); if (plen == 0) return 0; dc = g_try_new0(struct discover_char, 1); if (dc == NULL) return 0; dc->attrib = g_attrib_ref(attrib); dc->cb = func; dc->user_data = user_data; dc->end = end; dc->uuid = g_memdup(uuid, sizeof(bt_uuid_t)); return g_attrib_send(attrib, 0, buf, plen, char_discovered_cb, dc, NULL); }
bool bt_scpp_attach(struct bt_scpp *scan, void *attrib) { bt_uuid_t iwin_uuid, refresh_uuid; if (!scan || scan->attrib || !scan->primary) return false; scan->attrib = g_attrib_ref(attrib); if (scan->iwhandle) write_scan_params(scan->attrib, scan->iwhandle, scan->interval, scan->window); else { bt_uuid16_create(&iwin_uuid, SCAN_INTERVAL_WIN_UUID); discover_char(scan, scan->attrib, scan->primary->range.start, scan->primary->range.end, &iwin_uuid, iwin_discovered_cb, scan); } if (scan->refresh_handle) scan->refresh_cb_id = g_attrib_register(scan->attrib, ATT_OP_HANDLE_NOTIFY, scan->refresh_handle, refresh_value_cb, scan, NULL); else { bt_uuid16_create(&refresh_uuid, SCAN_REFRESH_UUID); discover_char(scan, scan->attrib, scan->primary->range.start, scan->primary->range.end, &refresh_uuid, refresh_discovered_cb, scan); } return true; }
guint gatt_read_char(GAttrib *attrib, uint16_t handle, GAttribResultFunc func, gpointer user_data) { uint8_t *buf; size_t buflen; guint16 plen; guint id; struct read_long_data *long_read; long_read = g_try_new0(struct read_long_data, 1); if (long_read == NULL) return 0; long_read->attrib = g_attrib_ref(attrib); long_read->func = func; long_read->user_data = user_data; long_read->handle = handle; buf = g_attrib_get_buffer(attrib, &buflen); plen = enc_read_req(handle, buf, buflen); id = g_attrib_send(attrib, 0, buf, plen, read_char_helper, long_read, read_long_destroy); if (id == 0) { g_attrib_unref(long_read->attrib); g_free(long_read); } else { __sync_fetch_and_add(&long_read->ref, 1); long_read->id = id; } return id; }
static gboolean disconnect_timeout(gpointer data) { struct _GAttrib *attrib = data; struct command *c; g_attrib_ref(attrib); c = g_queue_pop_head(attrib->requests); if (c == NULL) goto done; if (c->func) c->func(ATT_ECODE_TIMEOUT, NULL, 0, c->user_data); command_destroy(c); while ((c = g_queue_pop_head(attrib->requests))) { if (c->func) c->func(ATT_ECODE_ABORTED, NULL, 0, c->user_data); command_destroy(c); } done: attrib->stale = true; g_attrib_unref(attrib); return FALSE; }
guint gatt_discover_primary(GAttrib *attrib, bt_uuid_t *uuid, gatt_cb_t func, gpointer user_data) { struct discover_primary *dp; size_t buflen; uint8_t *buf = g_attrib_get_buffer(attrib, &buflen); GAttribResultFunc cb; guint16 plen; plen = encode_discover_primary(0x0001, 0xffff, uuid, buf, buflen); if (plen == 0) return 0; dp = g_try_new0(struct discover_primary, 1); if (dp == NULL) return 0; dp->attrib = g_attrib_ref(attrib); dp->cb = func; dp->user_data = user_data; if (uuid) { dp->uuid = *uuid; cb = primary_by_uuid_cb; } else cb = primary_all_cb; return g_attrib_send(attrib, 0, buf, plen, cb, dp, NULL); }
static void test_refcount(struct context *cxt, gconstpointer unused) { GAttrib *extra_ref; int destroy_canary = 0; g_attrib_set_destroy_function(cxt->att, destroy_canary_increment, &destroy_canary); extra_ref = g_attrib_ref(cxt->att); g_assert(extra_ref == cxt->att); g_assert(destroy_canary == 0); g_attrib_unref(extra_ref); g_assert(destroy_canary == 0); g_attrib_unref(cxt->att); g_assert(destroy_canary == 1); /* Avoid a double-free from the teardown function */ cxt->att = NULL; }
guint gatt_discover_desc(GAttrib *attrib, uint16_t start, uint16_t end, bt_uuid_t *uuid, gatt_cb_t func, gpointer user_data) { size_t buflen; uint8_t *buf = g_attrib_get_buffer(attrib, &buflen); struct discover_desc *dd; guint16 plen; plen = enc_find_info_req(start, end, buf, buflen); if (plen == 0) return 0; dd = g_try_new0(struct discover_desc, 1); if (dd == NULL) return 0; dd->attrib = g_attrib_ref(attrib); dd->cb = func; dd->user_data = user_data; dd->start = start; dd->end = end; dd->uuid = g_memdup(uuid, sizeof(bt_uuid_t)); dd->id = g_attrib_send(attrib, 0, buf, plen, desc_discovered_cb, discover_desc_ref(dd), discover_desc_unref); return dd->id; }
static void cmd_char(int argcp, char **argvp) { int start = 0x0001; int end = 0xffff; if (conn_state != STATE_CONNECTED) { printf("Command failed: disconnected\n"); goto done; } if (argcp > 1) { start = strtohandle(argvp[1]); if (start < 0) { printf("Invalid start handle: %s\n", argvp[1]); goto done; } } if (argcp > 2) { end = strtohandle(argvp[2]); if (end < 0) { printf("Invalid end handle: %s\n", argvp[2]); goto done; } } if (argcp > 3) { bt_uuid_t uuid; if (bt_string_to_uuid(&uuid, argvp[3]) < 0) { printf("Invalid UUID\n"); goto done; } g_attrib_ref(attrib); gatt_discover_char(attrib, start, end, &uuid, char_cb, NULL); return; } g_attrib_ref(attrib); gatt_discover_char(attrib, start, end, NULL, char_cb, NULL); return; done: printf("\r%s", get_prompt()); fflush(stdout); }
static void attio_connected_cb(GAttrib *attrib, gpointer user_data) { struct deviceinfo *d = user_data; d->attrib = g_attrib_ref(attrib); gatt_discover_char(d->attrib, d->svc_range->start, d->svc_range->end, NULL, configure_deviceinfo_cb, d); }
static void wake_up_sender(struct _GAttrib *attrib) { if (attrib->write_watch > 0) return; attrib = g_attrib_ref(attrib); attrib->write_watch = g_io_add_watch_full(attrib->io, G_PRIORITY_DEFAULT, G_IO_OUT, can_write_data, attrib, destroy_sender); }
static void attio_connected_cb(GAttrib *attrib, gpointer user_data) { struct heartrate *hr = user_data; DBG(""); hr->attrib = g_attrib_ref(attrib); gatt_discover_char(hr->attrib, hr->svc_range->start, hr->svc_range->end, NULL, discover_char_cb, hr); }
static void send_discover(GAttrib *attrib, gpointer user_data) { struct query_data *qchr = user_data; struct gatt_service *gatt = qchr->gatt; struct gatt_primary *prim = gatt->prim; gatt->attrib = g_attrib_ref(attrib); gatt_discover_char(gatt->attrib, prim->range.start, prim->range.end, NULL, char_discovered_cb, qchr); }
static void attio_connected_cb(GAttrib *attrib, gpointer user_data) { struct csc *csc = user_data; DBG(""); csc->attrib = g_attrib_ref(attrib); gatt_discover_char(csc->attrib, csc->svc_range->start, csc->svc_range->end, NULL, discover_char_cb, csc); }
unsigned int gatt_find_included(GAttrib *attrib, uint16_t start, uint16_t end, gatt_cb_t func, gpointer user_data) { struct included_discovery *isd; isd = g_new0(struct included_discovery, 1); isd->attrib = g_attrib_ref(attrib); isd->end_handle = end; isd->cb = func; isd->user_data = user_data; return find_included(isd, start); }
static void attio_connected_cb(GAttrib *attrib, gpointer user_data) { struct heartrate *hr = user_data; DBG(""); hr->attrib = g_attrib_ref(attrib); hr->attionotid = g_attrib_register(hr->attrib, ATT_OP_HANDLE_NOTIFY, notify_handler, hr, NULL); gatt_discover_char(hr->attrib, hr->svc_range->start, hr->svc_range->end, NULL, discover_char_cb, hr); }
bool bt_hog_attach(struct bt_hog *hog, void *gatt) { struct gatt_primary *primary = hog->primary; GSList *l; if (hog->attrib) return false; hog->attrib = g_attrib_ref(gatt); if (!primary) { gatt_discover_primary(hog->attrib, NULL, primary_cb, hog); return true; } if (hog->scpp) bt_scpp_attach(hog->scpp, gatt); if (hog->dis) bt_dis_attach(hog->dis, gatt); if (hog->bas) bt_bas_attach(hog->bas, gatt); for (l = hog->instances; l; l = l->next) { struct bt_hog *instance = l->data; bt_hog_attach(instance, gatt); } if (hog->reports == NULL) { gatt_discover_char(hog->attrib, primary->range.start, primary->range.end, NULL, char_discovered_cb, hog); return true; } for (l = hog->reports; l; l = l->next) { struct report *r = l->data; r->notifyid = g_attrib_register(hog->attrib, ATT_OP_HANDLE_NOTIFY, r->decl->value_handle, report_value_cb, r, NULL); } return true; }
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; }
bool bt_dis_attach(struct bt_dis *dis, void *attrib) { struct gatt_primary *primary = dis->primary; if (dis->attrib || !primary) return false; dis->attrib = g_attrib_ref(attrib); if (!dis->handle) discover_char(dis, dis->attrib, primary->range.start, primary->range.end, NULL, configure_deviceinfo_cb, dis); return true; }
bool bt_bas_attach(struct bt_bas *bas, void *attrib) { if (!bas || bas->attrib || !bas->primary) return false; bas->attrib = g_attrib_ref(attrib); if (bas->handle > 0) return true; discover_char(bas, bas->attrib, bas->primary->range.start, bas->primary->range.end, NULL, bas_discovered_cb, bas); return true; }
static void attio_connected_cb(GAttrib *attrib, gpointer user_data) { struct monitor *monitor = user_data; monitor->attrib = g_attrib_ref(attrib); if (monitor->enabled.linkloss) write_alert_level(monitor); if (monitor->enabled.pathloss) read_tx_power(monitor); if (monitor->immediatehandle == 0) { if(monitor->enabled.pathloss || monitor->enabled.findme) discover_immediate_handle(monitor); } else if (monitor->fallbacklevel) write_immediate_alert(monitor); }
static void attio_connected(GAttrib *attrib, gpointer user_data) { struct gatt_service *gatt = user_data; gatt->attrib = g_attrib_ref(attrib); g_attrib_register(gatt->attrib, ATT_OP_HANDLE_NOTIFY, events_handler, gatt, NULL); g_attrib_register(gatt->attrib, ATT_OP_HANDLE_IND, events_handler, gatt, NULL); /* * TODO : we do not support offline writing */ /*g_slist_foreach(gatt->offline_chars, offline_char_write, attrib);*/ INFO("%s connected", gatt->path); }
GAttrib *g_attrib_new(GIOChannel *io) { struct _GAttrib *attrib; uint16_t imtu; uint16_t att_mtu; uint16_t cid; GError *gerr = NULL; g_io_channel_set_encoding(io, NULL, NULL); g_io_channel_set_buffered(io, FALSE); bt_io_get(io, BT_IO_L2CAP, &gerr, BT_IO_OPT_IMTU, &imtu, BT_IO_OPT_CID, &cid, BT_IO_OPT_INVALID); if (gerr) { error("%s", gerr->message); g_error_free(gerr); return NULL; } attrib = g_try_new0(struct _GAttrib, 1); if (attrib == NULL) return NULL; att_mtu = (cid == ATT_CID) ? ATT_DEFAULT_LE_MTU : imtu; attrib->buf = g_malloc0(att_mtu); attrib->buflen = att_mtu; attrib->io = g_io_channel_ref(io); attrib->requests = g_queue_new(); attrib->responses = g_queue_new(); attrib->read_watch = g_io_add_watch(attrib->io, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, received_data, attrib); return g_attrib_ref(attrib); }
static void attio_connected_cb(GAttrib *attrib, gpointer user_data) { struct scan *scan = user_data; bt_uuid_t iwin_uuid, refresh_uuid; scan->attrib = g_attrib_ref(attrib); if (scan->iwhandle) { write_scan_params(scan->attrib, scan->iwhandle); return; } bt_uuid16_create(&iwin_uuid, SCAN_INTERVAL_WIN_UUID); bt_uuid16_create(&refresh_uuid, SCAN_REFRESH_UUID); gatt_discover_char(scan->attrib, scan->range.start, scan->range.end, &iwin_uuid, iwin_discovered_cb, scan); gatt_discover_char(scan->attrib, scan->range.start, scan->range.end, &refresh_uuid, refresh_discovered_cb, scan); }
guint gatt_read_char_single(GAttrib *attrib, uint16_t handle, uint16_t offset, GAttribResultFunc func, gpointer user_data) { uint8_t *buf; size_t buflen; guint16 plen; guint id; struct read_data *read; read = g_try_new0(struct read_data, 1); if (read == NULL) return 0; read->attrib = g_attrib_ref(attrib); read->func = func; read->user_data = user_data; read->handle = handle; read->offset = offset; buf = g_attrib_get_buffer(attrib, &buflen); if (!offset) plen = enc_read_req(handle, buf, buflen); else plen = enc_read_blob_req(handle, offset, buf, buflen); id = g_attrib_send(attrib, 0, buf, plen, read_char_helper, read, read_destroy); if (id == 0) { g_attrib_unref(read->attrib); g_free(read); } else { __sync_fetch_and_add(&read->ref, 1); read->id = id; } return id; }