/* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2012 Texas Instruments, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include "src/log.h" #include "lib/uuid.h" #include "src/shared/util.h" #include "attrib/gattrib.h" #include "attrib/att.h" #include "attrib/gatt.h" #include "android/dis.h" #define PNP_ID_SIZE 7 struct bt_dis { int ref_count; uint16_t handle; uint8_t source; uint16_t vendor; uint16_t product; uint16_t version; GAttrib *attrib; /* GATT connection */ struct gatt_primary *primary; /* Primary details */ bt_dis_notify notify; void *notify_data; }; struct characteristic { struct gatt_char attr; /* Characteristic */ struct bt_dis *d; /* deviceinfo where the char belongs */ }; static void dis_free(struct bt_dis *dis) { bt_dis_detach(dis); g_free(dis->primary); g_free(dis); } struct bt_dis *bt_dis_new(void *primary) { struct bt_dis *dis; dis = g_try_new0(struct bt_dis, 1); if (!dis) return NULL; if (primary) dis->primary = g_memdup(primary, sizeof(*dis->primary)); return bt_dis_ref(dis); } struct bt_dis *bt_dis_ref(struct bt_dis *dis) { if (!dis) return NULL; __sync_fetch_and_add(&dis->ref_count, 1); return dis; } void bt_dis_unref(struct bt_dis *dis) { if (!dis) return; if (__sync_sub_and_fetch(&dis->ref_count, 1)) return; dis_free(dis); } static void read_pnpid_cb(guint8 status, const guint8 *pdu, guint16 len, gpointer user_data) { struct bt_dis *dis = user_data; uint8_t value[PNP_ID_SIZE]; ssize_t vlen; if (status != 0) { error("Error reading PNP_ID value: %s", att_ecode2str(status)); return; } vlen = dec_read_resp(pdu, len, value, sizeof(value)); if (vlen < 0) { error("Error reading PNP_ID: Protocol error"); return; } if (vlen < 7) { error("Error reading PNP_ID: Invalid pdu length received"); return; } dis->source = value[0]; dis->vendor = get_le16(&value[1]); dis->product = get_le16(&value[3]); dis->version = get_le16(&value[5]); DBG("source: 0x%02X vendor: 0x%04X product: 0x%04X version: 0x%04X", dis->source, dis->vendor, dis->product, dis->version); if (dis->notify) dis->notify(dis->source, dis->vendor, dis->product, dis->version, dis->notify_data); } static void configure_deviceinfo_cb(uint8_t status, GSList *characteristics, void *user_data) { struct bt_dis *d = user_data; GSList *l; if (status != 0) { error("Discover deviceinfo characteristics: %s", att_ecode2str(status)); return; } for (l = characteristics; l; l = l->next) { struct gatt_char *c = l->data; if (strcmp(c->uuid, PNPID_UUID) == 0) { d->handle = c->value_handle; gatt_read_char(d->attrib, d->handle, read_pnpid_cb, d); break; } } } 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) gatt_discover_char(dis->attrib, primary->range.start, primary->range.end, NULL, configure_deviceinfo_cb, dis); return true; } void bt_dis_detach(struct bt_dis *dis) { if (!dis->attrib) return; g_attrib_unref(dis->attrib); dis->attrib = NULL; } bool bt_dis_set_notification(struct bt_dis *dis, bt_dis_notify func, void *user_data) { if (!dis) return false; dis->notify = func; dis->notify_data = user_data; return true; }