* Health Thermometer Profile (HTP) @ 2011-11-10 11:36 Santiago Carot-Nemesio 2011-11-10 11:36 ` [PATCH 1/6] Manage GATT attribute indications in handle callback Santiago Carot-Nemesio 0 siblings, 1 reply; 11+ messages in thread From: Santiago Carot-Nemesio @ 2011-11-10 11:36 UTC (permalink / raw) To: linux-bluetooth This is a new set of patches to enhance thermometer plugin fuctionality. These patches enable watchers to receiave final/intermediate measurements from thermometers devices. They also includes latest changes suggested in the list. [PATCH 1/6] Manage GATT attribute indications in handle callback. [PATCH 2/6] Parse final measurement indication [PATCH 3/6] Add org.bluez.ThermometerWatcher interface to default [PATCH 4/6] Implement EnableIntermediateMeasurement D-Bus method [PATCH 5/6] Implement DisableIntermediateMeasurement D-Bus method [PATCH 6/6] Notify intermediate measurements ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 1/6] Manage GATT attribute indications in handle callback. 2011-11-10 11:36 Health Thermometer Profile (HTP) Santiago Carot-Nemesio @ 2011-11-10 11:36 ` Santiago Carot-Nemesio 2011-11-10 11:36 ` [PATCH 2/6] Parse final measurement indication Santiago Carot-Nemesio 0 siblings, 1 reply; 11+ messages in thread From: Santiago Carot-Nemesio @ 2011-11-10 11:36 UTC (permalink / raw) To: linux-bluetooth; +Cc: Santiago Carot-Nemesio --- thermometer/thermometer.c | 51 ++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 50 insertions(+), 1 deletions(-) diff --git a/thermometer/thermometer.c b/thermometer/thermometer.c index 5b0e30a..69ae708 100644 --- a/thermometer/thermometer.c +++ b/thermometer/thermometer.c @@ -163,6 +163,14 @@ static gint cmp_char_uuid(gconstpointer a, gconstpointer b) return g_strcmp0(ch->attr.uuid, uuid); } +static gint cmp_char_val_handle(gconstpointer a, gconstpointer b) +{ + const struct characteristic *ch = a; + const uint16_t *handle = b; + + return ch->attr.value_handle - *handle; +} + static gint cmp_descriptor(gconstpointer a, gconstpointer b) { const struct descriptor *desc = a; @@ -703,9 +711,50 @@ static GDBusSignalTable thermometer_signals[] = { { } }; +static void proc_measurement(struct thermometer *t, const uint8_t *pdu, + uint16_t len, gboolean final) +{ + DBG("TODO: Process measurement indication"); +} + +static void proc_measurement_interval(struct thermometer *t, const uint8_t *pdu, + uint16_t len) +{ + DBG("TODO: Process measurements interval indication"); +} + static void ind_handler(const uint8_t *pdu, uint16_t len, gpointer user_data) { - /* TODO: Process indication */ + struct thermometer *t = user_data; + const struct characteristic *ch; + uint8_t opdu[ATT_MAX_MTU]; + uint16_t handle, olen; + GSList *l; + + if (len < 3) { + DBG("Bad pdu received"); + return; + } + + handle = att_get_u16(&pdu[1]); + l = g_slist_find_custom(t->chars, &handle, cmp_char_val_handle); + if (l == NULL) { + DBG("Unexpected handle: 0x%04x", handle); + return; + } + + ch = l->data; + + if (g_strcmp0(ch->attr.uuid, TEMPERATURE_MEASUREMENT_UUID) == 0) + proc_measurement(t, pdu, len, TRUE); + else if (g_strcmp0(ch->attr.uuid, MEASUREMENT_INTERVAL_UUID) == 0) + proc_measurement_interval(t, pdu, len); + + olen = enc_confirmation(opdu, sizeof(opdu)); + + if (olen > 0) + g_attrib_send(t->attrib, 0, opdu[0], opdu, olen, NULL, NULL, + NULL); } static void attio_connected_cb(GAttrib *attrib, gpointer user_data) -- 1.7.7.3 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 2/6] Parse final measurement indication 2011-11-10 11:36 ` [PATCH 1/6] Manage GATT attribute indications in handle callback Santiago Carot-Nemesio @ 2011-11-10 11:36 ` Santiago Carot-Nemesio 2011-11-10 11:36 ` [PATCH 3/6] Add org.bluez.ThermometerWatcher interface to default policy Santiago Carot-Nemesio 2011-11-12 18:37 ` [PATCH 2/6] Parse final measurement indication Johan Hedberg 0 siblings, 2 replies; 11+ messages in thread From: Santiago Carot-Nemesio @ 2011-11-10 11:36 UTC (permalink / raw) To: linux-bluetooth; +Cc: Santiago Carot-Nemesio --- thermometer/thermometer.c | 153 ++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 152 insertions(+), 1 deletions(-) diff --git a/thermometer/thermometer.c b/thermometer/thermometer.c index 69ae708..616c21d 100644 --- a/thermometer/thermometer.c +++ b/thermometer/thermometer.c @@ -47,6 +47,13 @@ #define INTERMEDIATE_TEMPERATURE_UUID "00002a1e-0000-1000-8000-00805f9b34fb" #define MEASUREMENT_INTERVAL_UUID "00002a21-0000-1000-8000-00805f9b34fb" +/* Temperature measurement flag fields */ +#define TEMP_UNITS 0x01 +#define TEMP_TIME_STAMP 0x02 +#define TEMP_TYPE 0x04 + +#define FLOAT_MAX_MANTISSA 16777216 /* 2^24 */ + struct thermometer { DBusConnection *conn; /* The connection to the bus */ struct btd_device *dev; /* Device reference */ @@ -84,8 +91,36 @@ struct watcher { gchar *path; }; +struct measurement { + gint16 exp; + gint32 mant; + guint64 time; + gboolean suptime; + gchar *unit; + gchar *type; + gchar *msmnt; +}; + static GSList *thermometers = NULL; +static gchar *temptype2str(uint8_t value) +{ + switch (value) { + case 1: return "Armpit"; + case 2: return "Body"; + case 3: return "Ear"; + case 4: return "Finger"; + case 5: return "Intestines"; + case 6: return "Mouth"; + case 7: return "Rectum"; + case 8: return "Toe"; + case 9: return "Tympanum"; + default: + error("Temperature type %d reserved for future use", value); + return NULL; + }; +} + static void destroy_watcher(gpointer user_data) { struct watcher *watcher = user_data; @@ -711,10 +746,126 @@ static GDBusSignalTable thermometer_signals[] = { { } }; +static void update_watcher(gpointer data, gpointer user_data) +{ + struct watcher *w = data; + struct measurement *m = user_data; + DBusConnection *conn = w->t->conn; + DBusMessageIter iter; + DBusMessageIter dict; + DBusMessage *msg; + + msg = dbus_message_new_method_call(w->srv, w->path, + "org.bluez.ThermometerWatcher", + "MeasurementReceived"); + if (msg == NULL) + return; + + dbus_message_iter_init_append(msg, &iter); + + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING + DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); + + dict_append_entry(&dict, "Exponent", DBUS_TYPE_INT16, &m->exp); + dict_append_entry(&dict, "Mantissa", DBUS_TYPE_INT32, &m->mant); + dict_append_entry(&dict, "Unit", DBUS_TYPE_STRING, &m->unit); + + if (m->suptime) + dict_append_entry(&dict, "Time", DBUS_TYPE_UINT64, &m->time); + + dict_append_entry(&dict, "Type", DBUS_TYPE_STRING, &m->type); + dict_append_entry(&dict, "Measurement", DBUS_TYPE_STRING, &m->msmnt); + + dbus_message_iter_close_container(&iter, &dict); + + dbus_message_set_no_reply(msg, TRUE); + g_dbus_send_message(conn, msg); +} + +static void recv_measurement(struct thermometer *t, struct measurement *msmt) +{ + if (g_strcmp0(msmt->msmnt, "Intermediate") == 0) { + DBG("Notification of intermediate measurement not implemented"); + return; + } + + g_slist_foreach(t->fwatchers, update_watcher, msmt); +} + static void proc_measurement(struct thermometer *t, const uint8_t *pdu, uint16_t len, gboolean final) { - DBG("TODO: Process measurement indication"); + struct measurement msmt; + uint8_t flags; + uint32_t raw; + + if (len < 4) { + DBG("Mandatory flags are not provided"); + return; + } + + flags = pdu[3]; + if (flags & TEMP_UNITS) + msmt.unit = "Fahrenheit"; + else + msmt.unit = "Celsius"; + + if (len < 8) { + DBG("Temperature measurement value is not provided"); + return; + } + + raw = att_get_u32(&pdu[4]); + msmt.mant = raw & 0x00FFFFFF; + msmt.exp = ((gint32) raw) >> 24; + + if (msmt.mant & 0x00800000) { + /* convert to C2 negative value */ + msmt.mant = msmt.mant - FLOAT_MAX_MANTISSA; + } + + if (flags & TEMP_TIME_STAMP) { + struct tm ts; + time_t time; + + if (len < 15) { + DBG("Can't get time stamp value"); + return; + } + + ts.tm_year = att_get_u16(&pdu[8]) - 1900; + ts.tm_mon = pdu[10]; + ts.tm_mday = pdu[11]; + ts.tm_hour = pdu[12]; + ts.tm_min = pdu[13]; + ts.tm_sec = pdu[14]; + ts.tm_isdst = -1; + + time = mktime(&ts); + msmt.time = (guint64) time; + msmt.suptime = TRUE; + } else + msmt.suptime = FALSE; + + if (flags & TEMP_TYPE) { + if (len < 16) { + DBG("Can't get temperature type"); + return; + } + + msmt.type = temptype2str(pdu[15]); + } else if (t->has_type) + msmt.type = temptype2str(t->type); + else { + DBG("Can't get temperature type"); + return; + } + + msmt.msmnt = final ? "Final" : "Intermediate"; + + recv_measurement(t, &msmt); } static void proc_measurement_interval(struct thermometer *t, const uint8_t *pdu, -- 1.7.7.3 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 3/6] Add org.bluez.ThermometerWatcher interface to default policy 2011-11-10 11:36 ` [PATCH 2/6] Parse final measurement indication Santiago Carot-Nemesio @ 2011-11-10 11:36 ` Santiago Carot-Nemesio 2011-11-10 11:37 ` [PATCH 4/6] Implement EnableIntermediateMeasurement D-Bus method Santiago Carot-Nemesio 2011-11-12 18:37 ` [PATCH 2/6] Parse final measurement indication Johan Hedberg 1 sibling, 1 reply; 11+ messages in thread From: Santiago Carot-Nemesio @ 2011-11-10 11:36 UTC (permalink / raw) To: linux-bluetooth; +Cc: Santiago Carot-Nemesio --- src/bluetooth.conf | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diff --git a/src/bluetooth.conf b/src/bluetooth.conf index b5a6af3..664dbd9 100644 --- a/src/bluetooth.conf +++ b/src/bluetooth.conf @@ -15,6 +15,7 @@ <allow send_interface="org.bluez.MediaEndpoint"/> <allow send_interface="org.bluez.MediaPlayer"/> <allow send_interface="org.bluez.Watcher"/> + <allow send_interface="org.bluez.ThermometerWatcher"/> </policy> <policy at_console="true"> -- 1.7.7.3 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 4/6] Implement EnableIntermediateMeasurement D-Bus method 2011-11-10 11:36 ` [PATCH 3/6] Add org.bluez.ThermometerWatcher interface to default policy Santiago Carot-Nemesio @ 2011-11-10 11:37 ` Santiago Carot-Nemesio 2011-11-10 11:37 ` [PATCH 5/6] Implement DisableIntermediateMeasurement " Santiago Carot-Nemesio 0 siblings, 1 reply; 11+ messages in thread From: Santiago Carot-Nemesio @ 2011-11-10 11:37 UTC (permalink / raw) To: linux-bluetooth; +Cc: Santiago Carot-Nemesio --- thermometer/thermometer.c | 74 +++++++++++++++++++++++++++++++++++++------- 1 files changed, 62 insertions(+), 12 deletions(-) diff --git a/thermometer/thermometer.c b/thermometer/thermometer.c index 616c21d..49e6372 100644 --- a/thermometer/thermometer.c +++ b/thermometer/thermometer.c @@ -63,6 +63,7 @@ struct thermometer { guint attindid; /* Att incications id */ GSList *chars; /* Characteristics */ GSList *fwatchers; /* Final measurements */ + GSList *iwatchers; /* Intermediate measurements */ gboolean intermediate; guint8 type; guint16 interval; @@ -554,7 +555,7 @@ static struct descriptor *get_descriptor(struct characteristic *ch, return l->data; } -static void final_measurement_cb(guint8 status, const guint8 *pdu, +static void measurement_cb(guint8 status, const guint8 *pdu, guint16 len, gpointer user_data) { gchar *msg = user_data; @@ -589,8 +590,34 @@ static void enable_final_measurement(struct thermometer *t) atval[0] = 0x02; atval[1] = 0x00; msg = g_strdup("Enable final measurement"); - gatt_write_char(t->attrib, desc->handle, atval, 2, - final_measurement_cb, msg); + gatt_write_char(t->attrib, desc->handle, atval, 2, measurement_cb, msg); +} + +static void enable_intermediate_measurement(struct thermometer *t) +{ + struct characteristic *ch; + struct descriptor *desc; + bt_uuid_t btuuid; + uint8_t atval[2]; + gchar *msg; + + ch = get_characteristic(t, INTERMEDIATE_TEMPERATURE_UUID); + if (ch == NULL) { + DBG("Intermediate measurement characteristic not found"); + return; + } + + bt_uuid16_create(&btuuid, GATT_CLIENT_CHARAC_CFG_UUID); + desc = get_descriptor(ch, &btuuid); + if (desc == NULL) { + DBG("Client characteristic configuration descriptor not found"); + return; + } + + atval[0] = 0x01; + atval[1] = 0x00; + msg = g_strdup("Enable intermediate measurement"); + gatt_write_char(t->attrib, desc->handle, atval, 2, measurement_cb, msg); } static void disable_final_measurement(struct thermometer *t) @@ -617,8 +644,7 @@ static void disable_final_measurement(struct thermometer *t) atval[0] = 0x00; atval[1] = 0x00; msg = g_strdup("Disable final measurement"); - gatt_write_char(t->attrib, desc->handle, atval, 2, - final_measurement_cb, msg); + gatt_write_char(t->attrib, desc->handle, atval, 2, measurement_cb, msg); } static void watcher_exit(DBusConnection *conn, void *user_data) @@ -635,7 +661,7 @@ static void watcher_exit(DBusConnection *conn, void *user_data) disable_final_measurement(t); } -static struct watcher *find_watcher(struct thermometer *t, const gchar *sender, +static struct watcher *find_watcher(GSList *list, const gchar *sender, const gchar *path) { struct watcher *match; @@ -645,7 +671,7 @@ static struct watcher *find_watcher(struct thermometer *t, const gchar *sender, match->srv = g_strdup(sender); match->path = g_strdup(path); - l = g_slist_find_custom(t->fwatchers, match, cmp_watcher); + l = g_slist_find_custom(list, match, cmp_watcher); destroy_watcher(match); if (l != NULL) @@ -666,7 +692,7 @@ static DBusMessage *register_watcher(DBusConnection *conn, DBusMessage *msg, DBUS_TYPE_INVALID)) return btd_error_invalid_args(msg); - watcher = find_watcher(t, sender, path); + watcher = find_watcher(t->fwatchers, sender, path); if (watcher != NULL) return btd_error_already_exists(msg); @@ -699,7 +725,7 @@ static DBusMessage *unregister_watcher(DBusConnection *conn, DBusMessage *msg, DBUS_TYPE_INVALID)) return btd_error_invalid_args(msg); - watcher = find_watcher(t, sender, path); + watcher = find_watcher(t->fwatchers, sender, path); if (watcher == NULL) return btd_error_does_not_exist(msg); @@ -717,9 +743,33 @@ static DBusMessage *unregister_watcher(DBusConnection *conn, DBusMessage *msg, static DBusMessage *enable_intermediate(DBusConnection *conn, DBusMessage *msg, void *data) { - /* TODO: */ - return g_dbus_create_error(msg, ERROR_INTERFACE ".ThermometerError", - "Function not implemented."); + const gchar *sender = dbus_message_get_sender(msg); + struct thermometer *t = data; + struct watcher *watcher; + gchar *path; + + if (!t->intermediate) + return btd_error_not_supported(msg); + + if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path, + DBUS_TYPE_INVALID)) + return btd_error_invalid_args(msg); + + watcher = find_watcher(t->fwatchers, sender, path); + if (watcher == NULL) + return btd_error_does_not_exist(msg); + + if (find_watcher(t->iwatchers, sender, path)) + return btd_error_already_exists(msg); + + DBG("Intermediate measurement watcher %s registered", path); + + if (g_slist_length(t->iwatchers) == 0) + enable_intermediate_measurement(t); + + t->iwatchers = g_slist_prepend(t->iwatchers, watcher); + + return dbus_message_new_method_return(msg); } static DBusMessage *disable_intermediate(DBusConnection *conn, DBusMessage *msg, -- 1.7.7.3 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 5/6] Implement DisableIntermediateMeasurement D-Bus method 2011-11-10 11:37 ` [PATCH 4/6] Implement EnableIntermediateMeasurement D-Bus method Santiago Carot-Nemesio @ 2011-11-10 11:37 ` Santiago Carot-Nemesio 2011-11-10 11:37 ` [PATCH 6/6] Notify intermediate measurements Santiago Carot-Nemesio 0 siblings, 1 reply; 11+ messages in thread From: Santiago Carot-Nemesio @ 2011-11-10 11:37 UTC (permalink / raw) To: linux-bluetooth; +Cc: Santiago Carot-Nemesio --- thermometer/thermometer.c | 63 ++++++++++++++++++++++++++++++++++++++++++-- 1 files changed, 60 insertions(+), 3 deletions(-) diff --git a/thermometer/thermometer.c b/thermometer/thermometer.c index 49e6372..0663b3c 100644 --- a/thermometer/thermometer.c +++ b/thermometer/thermometer.c @@ -647,6 +647,44 @@ static void disable_final_measurement(struct thermometer *t) gatt_write_char(t->attrib, desc->handle, atval, 2, measurement_cb, msg); } +static void disable_intermediate_measurement(struct thermometer *t) +{ + struct characteristic *ch; + struct descriptor *desc; + bt_uuid_t btuuid; + uint8_t atval[2]; + gchar *msg; + + ch = get_characteristic(t, INTERMEDIATE_TEMPERATURE_UUID); + if (ch == NULL) { + DBG("Intermediate measurement characteristic not found"); + return; + } + + bt_uuid16_create(&btuuid, GATT_CLIENT_CHARAC_CFG_UUID); + desc = get_descriptor(ch, &btuuid); + if (desc == NULL) { + DBG("Client characteristic configuration descriptor not found"); + return; + } + + atval[0] = 0x00; + atval[1] = 0x00; + msg = g_strdup("Disable intermediate measurement"); + gatt_write_char(t->attrib, desc->handle, atval, 2, measurement_cb, msg); +} + +static void remove_int_watcher(struct thermometer *t, struct watcher *w) +{ + if (!g_slist_find(t->iwatchers, w)) + return; + + t->iwatchers = g_slist_remove(t->iwatchers, w); + + if (g_slist_length(t->iwatchers) == 0) + disable_intermediate_measurement(t); +} + static void watcher_exit(DBusConnection *conn, void *user_data) { struct watcher *watcher = user_data; @@ -654,6 +692,8 @@ static void watcher_exit(DBusConnection *conn, void *user_data) DBG("Thermometer watcher %s disconnected", watcher->path); + remove_int_watcher(t, watcher); + t->fwatchers = g_slist_remove(t->fwatchers, watcher); watcher->id = 0; @@ -731,6 +771,8 @@ static DBusMessage *unregister_watcher(DBusConnection *conn, DBusMessage *msg, DBG("Thermometer watcher %s unregistered", path); + remove_int_watcher(t, watcher); + t->fwatchers = g_slist_remove(t->fwatchers, watcher); destroy_watcher(watcher); @@ -775,9 +817,24 @@ static DBusMessage *enable_intermediate(DBusConnection *conn, DBusMessage *msg, static DBusMessage *disable_intermediate(DBusConnection *conn, DBusMessage *msg, void *data) { - /* TODO: */ - return g_dbus_create_error(msg, ERROR_INTERFACE ".ThermometerError", - "Function not implemented."); + const gchar *sender = dbus_message_get_sender(msg); + struct thermometer *t = data; + struct watcher *watcher; + gchar *path; + + if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path, + DBUS_TYPE_INVALID)) + return btd_error_invalid_args(msg); + + watcher = find_watcher(t->iwatchers, sender, path); + if (watcher == NULL) + return btd_error_does_not_exist(msg); + + DBG("Intermediate measurement %s unregistered", path); + + remove_int_watcher(t, watcher); + + return dbus_message_new_method_return(msg); } static GDBusMethodTable thermometer_methods[] = { -- 1.7.7.3 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 6/6] Notify intermediate measurements 2011-11-10 11:37 ` [PATCH 5/6] Implement DisableIntermediateMeasurement " Santiago Carot-Nemesio @ 2011-11-10 11:37 ` Santiago Carot-Nemesio 0 siblings, 0 replies; 11+ messages in thread From: Santiago Carot-Nemesio @ 2011-11-10 11:37 UTC (permalink / raw) To: linux-bluetooth; +Cc: Santiago Carot-Nemesio --- thermometer/thermometer.c | 47 ++++++++++++++++++++++++++++++++++++++++---- 1 files changed, 42 insertions(+), 5 deletions(-) diff --git a/thermometer/thermometer.c b/thermometer/thermometer.c index 0663b3c..fb03b8d 100644 --- a/thermometer/thermometer.c +++ b/thermometer/thermometer.c @@ -61,6 +61,7 @@ struct thermometer { struct att_range *svc_range; /* Thermometer range */ guint attioid; /* Att watcher id */ guint attindid; /* Att incications id */ + guint attnotid; /* Att notifications id */ GSList *chars; /* Characteristics */ GSList *fwatchers; /* Final measurements */ GSList *iwatchers; /* Intermediate measurements */ @@ -152,6 +153,9 @@ static void destroy_thermometer(gpointer user_data) if (t->attindid > 0) g_attrib_unregister(t->attrib, t->attindid); + if (t->attnotid > 0) + g_attrib_unregister(t->attrib, t->attnotid); + if (t->attrib != NULL) g_attrib_unref(t->attrib); @@ -893,12 +897,14 @@ static void update_watcher(gpointer data, gpointer user_data) static void recv_measurement(struct thermometer *t, struct measurement *msmt) { - if (g_strcmp0(msmt->msmnt, "Intermediate") == 0) { - DBG("Notification of intermediate measurement not implemented"); - return; - } + GSList *wlist; - g_slist_foreach(t->fwatchers, update_watcher, msmt); + if (g_strcmp0(msmt->msmnt, "Intermediate") == 0) + wlist = t->iwatchers; + else + wlist = t->fwatchers; + + g_slist_foreach(wlist, update_watcher, msmt); } static void proc_measurement(struct thermometer *t, const uint8_t *pdu, @@ -1015,6 +1021,30 @@ static void ind_handler(const uint8_t *pdu, uint16_t len, gpointer user_data) NULL); } +static void notif_handler(const uint8_t *pdu, uint16_t len, gpointer user_data) +{ + struct thermometer *t = user_data; + const struct characteristic *ch; + uint16_t handle; + GSList *l; + + if (len < 3) { + DBG("Bad pdu received"); + return; + } + + handle = att_get_u16(&pdu[1]); + l = g_slist_find_custom(t->chars, &handle, cmp_char_val_handle); + if (l == NULL) { + DBG("Unexpected handle: 0x%04x", handle); + return; + } + + ch = l->data; + if (g_strcmp0(ch->attr.uuid, INTERMEDIATE_TEMPERATURE_UUID) == 0) + proc_measurement(t, pdu, len, FALSE); +} + static void attio_connected_cb(GAttrib *attrib, gpointer user_data) { struct thermometer *t = user_data; @@ -1023,6 +1053,8 @@ static void attio_connected_cb(GAttrib *attrib, gpointer user_data) t->attindid = g_attrib_register(t->attrib, ATT_OP_HANDLE_IND, ind_handler, t, NULL); + t->attnotid = g_attrib_register(t->attrib, ATT_OP_HANDLE_NOTIFY, + notif_handler, t, NULL); gatt_discover_char(t->attrib, t->svc_range->start, t->svc_range->end, NULL, configure_thermometer_cb, t); } @@ -1038,6 +1070,11 @@ static void attio_disconnected_cb(gpointer user_data) t->attindid = 0; } + if (t->attnotid > 0) { + g_attrib_unregister(t->attrib, t->attnotid); + t->attnotid = 0; + } + g_attrib_unref(t->attrib); t->attrib = NULL; } -- 1.7.7.3 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH 2/6] Parse final measurement indication 2011-11-10 11:36 ` [PATCH 2/6] Parse final measurement indication Santiago Carot-Nemesio 2011-11-10 11:36 ` [PATCH 3/6] Add org.bluez.ThermometerWatcher interface to default policy Santiago Carot-Nemesio @ 2011-11-12 18:37 ` Johan Hedberg 2011-11-13 9:18 ` Santiago Carot 1 sibling, 1 reply; 11+ messages in thread From: Johan Hedberg @ 2011-11-12 18:37 UTC (permalink / raw) To: Santiago Carot-Nemesio; +Cc: linux-bluetooth Hi Santiago, On Thu, Nov 10, 2011, Santiago Carot-Nemesio wrote: > +struct measurement { > + gint16 exp; > + gint32 mant; > + guint64 time; > + gboolean suptime; > + gchar *unit; > + gchar *type; > + gchar *msmnt; Is msmnt supposed to be short for measurement? Could you just spell it out please since to me that short version is far from obvious. Or would "value" be a more suitable name? Also, please avoid usage of g* types where you're not directly accessing some GLib API that expects them (even then it's mostly fine to go ahead with uint16_t, char, etc). I can see that there are other places in thermometer.c using the g types too which should be fixed in a separate patch. > +static gchar *temptype2str(uint8_t value) > +{ > + switch (value) { > + case 1: return "Armpit"; > + case 2: return "Body"; > + case 3: return "Ear"; > + case 4: return "Finger"; > + case 5: return "Intestines"; > + case 6: return "Mouth"; > + case 7: return "Rectum"; > + case 8: return "Toe"; > + case 9: return "Tympanum"; > + default: > + error("Temperature type %d reserved for future use", value); > + return NULL; > + }; Please follow the coding style with switch statements. All those return statements should be on their own line. However, in this case I think it'd be just easier to make a lookup table for those values, i.e. const char *temp_type[] { "<unknown>", "Armpit", "Body", ... And then in the temptype2str function: if (type > 0 && val < G_N_ELEMENTS(temp_type)) return temp_type[type]; Johan ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 2/6] Parse final measurement indication 2011-11-12 18:37 ` [PATCH 2/6] Parse final measurement indication Johan Hedberg @ 2011-11-13 9:18 ` Santiago Carot 0 siblings, 0 replies; 11+ messages in thread From: Santiago Carot @ 2011-11-13 9:18 UTC (permalink / raw) To: johan.hedberg, linux-bluetooth Hi Johan, 2011/11/12 Johan Hedberg <johan.hedberg@gmail.com>: > Hi Santiago, > > On Thu, Nov 10, 2011, Santiago Carot-Nemesio wrote: >> +struct measurement { >> + gint16 exp; >> + gint32 mant; >> + guint64 time; >> + gboolean suptime; >> + gchar *unit; >> + gchar *type; >> + gchar *msmnt; > > Is msmnt supposed to be short for measurement? Could you just spell it > out please since to me that short version is far from obvious. Or would > "value" be a more suitable name? > Yes, that's supposed to be a kind of short for measurement, There's not problem to me in changing the name. The reason why I set that name was because I wanted to keep consistancy with the thermomether documentation in which we called it measurement. > Also, please avoid usage of g* types where you're not directly accessing > some GLib API that expects them (even then it's mostly fine to go ahead > with uint16_t, char, etc). I can see that there are other places in > thermometer.c using the g types too which should be fixed in a separate > patch. > Ok, I used only glib types because I was confused about when I should use these or the other types, Having a look at some parts in BlueZ it seemed a little mess, but now It's clear to me. Thank you for your explanation, If you dont mind I would preffer to send a separate patch fixing that issue at the end of the new set in order to avoid too many conflicts rebasing in my local tree. >> +static gchar *temptype2str(uint8_t value) >> +{ >> + switch (value) { >> + case 1: return "Armpit"; >> + case 2: return "Body"; >> + case 3: return "Ear"; >> + case 4: return "Finger"; >> + case 5: return "Intestines"; >> + case 6: return "Mouth"; >> + case 7: return "Rectum"; >> + case 8: return "Toe"; >> + case 9: return "Tympanum"; >> + default: >> + error("Temperature type %d reserved for future use", value); >> + return NULL; >> + }; > > Please follow the coding style with switch statements. All those return > statements should be on their own line. However, in this case I think > it'd be just easier to make a lookup table for those values, i.e. > > const char *temp_type[] { > "<unknown>", > "Armpit", > "Body", > ... > > And then in the temptype2str function: > > if (type > 0 && val < G_N_ELEMENTS(temp_type)) > return temp_type[type]; > > > Johan > Ok, I'll redo these patches. Thanks a lot for your comments. Santiago ^ permalink raw reply [flat|nested] 11+ messages in thread
* Health Thermometer Profile (HTP) @ 2011-11-14 12:11 Santiago Carot-Nemesio 2011-11-14 12:11 ` [PATCH 1/6] Manage GATT attribute indications in handle callback Santiago Carot-Nemesio 0 siblings, 1 reply; 11+ messages in thread From: Santiago Carot-Nemesio @ 2011-11-14 12:11 UTC (permalink / raw) To: linux-bluetooth This is a new set of patches to enhance thermometer plugin fuctionality. These patches enable watchers to receiave final/intermediate measurements from thermometers devices. They also includes latest changes suggested in the list. [PATCH 1/6] Manage GATT attribute indications in handle callback. [PATCH 2/6] Parse final measurement indication [PATCH 3/6] Add org.bluez.ThermometerWatcher interface to default [PATCH 4/6] Implement EnableIntermediateMeasurement D-Bus method [PATCH 5/6] Implement DisableIntermediateMeasurement D-Bus method [PATCH 6/6] Notify intermediate measurements ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 1/6] Manage GATT attribute indications in handle callback. 2011-11-14 12:11 Health Thermometer Profile (HTP) Santiago Carot-Nemesio @ 2011-11-14 12:11 ` Santiago Carot-Nemesio 2011-11-14 12:11 ` [PATCH 2/6] Parse final measurement indication Santiago Carot-Nemesio 0 siblings, 1 reply; 11+ messages in thread From: Santiago Carot-Nemesio @ 2011-11-14 12:11 UTC (permalink / raw) To: linux-bluetooth; +Cc: Santiago Carot-Nemesio --- thermometer/thermometer.c | 51 ++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 50 insertions(+), 1 deletions(-) diff --git a/thermometer/thermometer.c b/thermometer/thermometer.c index 5b0e30a..69ae708 100644 --- a/thermometer/thermometer.c +++ b/thermometer/thermometer.c @@ -163,6 +163,14 @@ static gint cmp_char_uuid(gconstpointer a, gconstpointer b) return g_strcmp0(ch->attr.uuid, uuid); } +static gint cmp_char_val_handle(gconstpointer a, gconstpointer b) +{ + const struct characteristic *ch = a; + const uint16_t *handle = b; + + return ch->attr.value_handle - *handle; +} + static gint cmp_descriptor(gconstpointer a, gconstpointer b) { const struct descriptor *desc = a; @@ -703,9 +711,50 @@ static GDBusSignalTable thermometer_signals[] = { { } }; +static void proc_measurement(struct thermometer *t, const uint8_t *pdu, + uint16_t len, gboolean final) +{ + DBG("TODO: Process measurement indication"); +} + +static void proc_measurement_interval(struct thermometer *t, const uint8_t *pdu, + uint16_t len) +{ + DBG("TODO: Process measurements interval indication"); +} + static void ind_handler(const uint8_t *pdu, uint16_t len, gpointer user_data) { - /* TODO: Process indication */ + struct thermometer *t = user_data; + const struct characteristic *ch; + uint8_t opdu[ATT_MAX_MTU]; + uint16_t handle, olen; + GSList *l; + + if (len < 3) { + DBG("Bad pdu received"); + return; + } + + handle = att_get_u16(&pdu[1]); + l = g_slist_find_custom(t->chars, &handle, cmp_char_val_handle); + if (l == NULL) { + DBG("Unexpected handle: 0x%04x", handle); + return; + } + + ch = l->data; + + if (g_strcmp0(ch->attr.uuid, TEMPERATURE_MEASUREMENT_UUID) == 0) + proc_measurement(t, pdu, len, TRUE); + else if (g_strcmp0(ch->attr.uuid, MEASUREMENT_INTERVAL_UUID) == 0) + proc_measurement_interval(t, pdu, len); + + olen = enc_confirmation(opdu, sizeof(opdu)); + + if (olen > 0) + g_attrib_send(t->attrib, 0, opdu[0], opdu, olen, NULL, NULL, + NULL); } static void attio_connected_cb(GAttrib *attrib, gpointer user_data) -- 1.7.7.3 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 2/6] Parse final measurement indication 2011-11-14 12:11 ` [PATCH 1/6] Manage GATT attribute indications in handle callback Santiago Carot-Nemesio @ 2011-11-14 12:11 ` Santiago Carot-Nemesio 2011-11-14 12:11 ` [PATCH 3/6] Add org.bluez.ThermometerWatcher interface to default policy Santiago Carot-Nemesio 0 siblings, 1 reply; 11+ messages in thread From: Santiago Carot-Nemesio @ 2011-11-14 12:11 UTC (permalink / raw) To: linux-bluetooth; +Cc: Santiago Carot-Nemesio --- thermometer/thermometer.c | 163 ++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 162 insertions(+), 1 deletions(-) diff --git a/thermometer/thermometer.c b/thermometer/thermometer.c index 69ae708..b35c3aa 100644 --- a/thermometer/thermometer.c +++ b/thermometer/thermometer.c @@ -47,6 +47,13 @@ #define INTERMEDIATE_TEMPERATURE_UUID "00002a1e-0000-1000-8000-00805f9b34fb" #define MEASUREMENT_INTERVAL_UUID "00002a21-0000-1000-8000-00805f9b34fb" +/* Temperature measurement flag fields */ +#define TEMP_UNITS 0x01 +#define TEMP_TIME_STAMP 0x02 +#define TEMP_TYPE 0x04 + +#define FLOAT_MAX_MANTISSA 16777216 /* 2^24 */ + struct thermometer { DBusConnection *conn; /* The connection to the bus */ struct btd_device *dev; /* Device reference */ @@ -84,8 +91,40 @@ struct watcher { gchar *path; }; +struct measurement { + gint16 exp; + gint32 mant; + guint64 time; + gboolean suptime; + gchar *unit; + gchar *type; + gchar *value; +}; + static GSList *thermometers = NULL; +const char *temp_type[] = { + "<reserved>", + "Armpit", + "Body", + "Ear", + "Finger", + "Intestines", + "Mouth", + "Rectum", + "Toe", + "Tympanum" +}; + +static const gchar *temptype2str(uint8_t value) +{ + if (value > 0 && value < G_N_ELEMENTS(temp_type)) + return temp_type[value]; + + error("Temperature type %d reserved for future use", value); + return NULL; +} + static void destroy_watcher(gpointer user_data) { struct watcher *watcher = user_data; @@ -711,10 +750,132 @@ static GDBusSignalTable thermometer_signals[] = { { } }; +static void update_watcher(gpointer data, gpointer user_data) +{ + struct watcher *w = data; + struct measurement *m = user_data; + DBusConnection *conn = w->t->conn; + DBusMessageIter iter; + DBusMessageIter dict; + DBusMessage *msg; + + msg = dbus_message_new_method_call(w->srv, w->path, + "org.bluez.ThermometerWatcher", + "MeasurementReceived"); + if (msg == NULL) + return; + + dbus_message_iter_init_append(msg, &iter); + + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING + DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); + + dict_append_entry(&dict, "Exponent", DBUS_TYPE_INT16, &m->exp); + dict_append_entry(&dict, "Mantissa", DBUS_TYPE_INT32, &m->mant); + dict_append_entry(&dict, "Unit", DBUS_TYPE_STRING, &m->unit); + + if (m->suptime) + dict_append_entry(&dict, "Time", DBUS_TYPE_UINT64, &m->time); + + dict_append_entry(&dict, "Type", DBUS_TYPE_STRING, &m->type); + dict_append_entry(&dict, "Measurement", DBUS_TYPE_STRING, &m->value); + + dbus_message_iter_close_container(&iter, &dict); + + dbus_message_set_no_reply(msg, TRUE); + g_dbus_send_message(conn, msg); +} + +static void recv_measurement(struct thermometer *t, struct measurement *m) +{ + if (g_strcmp0(m->value, "Intermediate") == 0) { + DBG("Notification of intermediate measurement not implemented"); + return; + } + + g_slist_foreach(t->fwatchers, update_watcher, m); +} + static void proc_measurement(struct thermometer *t, const uint8_t *pdu, uint16_t len, gboolean final) { - DBG("TODO: Process measurement indication"); + struct measurement m; + const gchar *type; + uint8_t flags; + uint32_t raw; + + if (len < 4) { + DBG("Mandatory flags are not provided"); + return; + } + + flags = pdu[3]; + if (flags & TEMP_UNITS) + m.unit = "Fahrenheit"; + else + m.unit = "Celsius"; + + if (len < 8) { + DBG("Temperature measurement value is not provided"); + return; + } + + raw = att_get_u32(&pdu[4]); + m.mant = raw & 0x00FFFFFF; + m.exp = ((gint32) raw) >> 24; + + if (m.mant & 0x00800000) { + /* convert to C2 negative value */ + m.mant = m.mant - FLOAT_MAX_MANTISSA; + } + + if (flags & TEMP_TIME_STAMP) { + struct tm ts; + time_t time; + + if (len < 15) { + DBG("Can't get time stamp value"); + return; + } + + ts.tm_year = att_get_u16(&pdu[8]) - 1900; + ts.tm_mon = pdu[10]; + ts.tm_mday = pdu[11]; + ts.tm_hour = pdu[12]; + ts.tm_min = pdu[13]; + ts.tm_sec = pdu[14]; + ts.tm_isdst = -1; + + time = mktime(&ts); + m.time = (guint64) time; + m.suptime = TRUE; + } else + m.suptime = FALSE; + + if (flags & TEMP_TYPE) { + if (len < 16) { + DBG("Can't get temperature type"); + return; + } + + type = temptype2str(pdu[15]); + } else if (t->has_type) + type = temptype2str(t->type); + else { + DBG("Can't get temperature type"); + return; + } + + if (type == NULL) + return; + + m.type = g_strdup(type); + m.value = final ? "Final" : "Intermediate"; + + recv_measurement(t, &m); + g_free(m.type); } static void proc_measurement_interval(struct thermometer *t, const uint8_t *pdu, -- 1.7.7.3 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 3/6] Add org.bluez.ThermometerWatcher interface to default policy 2011-11-14 12:11 ` [PATCH 2/6] Parse final measurement indication Santiago Carot-Nemesio @ 2011-11-14 12:11 ` Santiago Carot-Nemesio 2011-11-14 12:11 ` [PATCH 4/6] Implement EnableIntermediateMeasurement D-Bus method Santiago Carot-Nemesio 0 siblings, 1 reply; 11+ messages in thread From: Santiago Carot-Nemesio @ 2011-11-14 12:11 UTC (permalink / raw) To: linux-bluetooth; +Cc: Santiago Carot-Nemesio --- src/bluetooth.conf | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diff --git a/src/bluetooth.conf b/src/bluetooth.conf index b5a6af3..664dbd9 100644 --- a/src/bluetooth.conf +++ b/src/bluetooth.conf @@ -15,6 +15,7 @@ <allow send_interface="org.bluez.MediaEndpoint"/> <allow send_interface="org.bluez.MediaPlayer"/> <allow send_interface="org.bluez.Watcher"/> + <allow send_interface="org.bluez.ThermometerWatcher"/> </policy> <policy at_console="true"> -- 1.7.7.3 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 4/6] Implement EnableIntermediateMeasurement D-Bus method 2011-11-14 12:11 ` [PATCH 3/6] Add org.bluez.ThermometerWatcher interface to default policy Santiago Carot-Nemesio @ 2011-11-14 12:11 ` Santiago Carot-Nemesio 0 siblings, 0 replies; 11+ messages in thread From: Santiago Carot-Nemesio @ 2011-11-14 12:11 UTC (permalink / raw) To: linux-bluetooth; +Cc: Santiago Carot-Nemesio --- thermometer/thermometer.c | 74 +++++++++++++++++++++++++++++++++++++------- 1 files changed, 62 insertions(+), 12 deletions(-) diff --git a/thermometer/thermometer.c b/thermometer/thermometer.c index b35c3aa..64b6aa6 100644 --- a/thermometer/thermometer.c +++ b/thermometer/thermometer.c @@ -63,6 +63,7 @@ struct thermometer { guint attindid; /* Att incications id */ GSList *chars; /* Characteristics */ GSList *fwatchers; /* Final measurements */ + GSList *iwatchers; /* Intermediate measurements */ gboolean intermediate; guint8 type; guint16 interval; @@ -558,7 +559,7 @@ static struct descriptor *get_descriptor(struct characteristic *ch, return l->data; } -static void final_measurement_cb(guint8 status, const guint8 *pdu, +static void measurement_cb(guint8 status, const guint8 *pdu, guint16 len, gpointer user_data) { gchar *msg = user_data; @@ -593,8 +594,34 @@ static void enable_final_measurement(struct thermometer *t) atval[0] = 0x02; atval[1] = 0x00; msg = g_strdup("Enable final measurement"); - gatt_write_char(t->attrib, desc->handle, atval, 2, - final_measurement_cb, msg); + gatt_write_char(t->attrib, desc->handle, atval, 2, measurement_cb, msg); +} + +static void enable_intermediate_measurement(struct thermometer *t) +{ + struct characteristic *ch; + struct descriptor *desc; + bt_uuid_t btuuid; + uint8_t atval[2]; + gchar *msg; + + ch = get_characteristic(t, INTERMEDIATE_TEMPERATURE_UUID); + if (ch == NULL) { + DBG("Intermediate measurement characteristic not found"); + return; + } + + bt_uuid16_create(&btuuid, GATT_CLIENT_CHARAC_CFG_UUID); + desc = get_descriptor(ch, &btuuid); + if (desc == NULL) { + DBG("Client characteristic configuration descriptor not found"); + return; + } + + atval[0] = 0x01; + atval[1] = 0x00; + msg = g_strdup("Enable intermediate measurement"); + gatt_write_char(t->attrib, desc->handle, atval, 2, measurement_cb, msg); } static void disable_final_measurement(struct thermometer *t) @@ -621,8 +648,7 @@ static void disable_final_measurement(struct thermometer *t) atval[0] = 0x00; atval[1] = 0x00; msg = g_strdup("Disable final measurement"); - gatt_write_char(t->attrib, desc->handle, atval, 2, - final_measurement_cb, msg); + gatt_write_char(t->attrib, desc->handle, atval, 2, measurement_cb, msg); } static void watcher_exit(DBusConnection *conn, void *user_data) @@ -639,7 +665,7 @@ static void watcher_exit(DBusConnection *conn, void *user_data) disable_final_measurement(t); } -static struct watcher *find_watcher(struct thermometer *t, const gchar *sender, +static struct watcher *find_watcher(GSList *list, const gchar *sender, const gchar *path) { struct watcher *match; @@ -649,7 +675,7 @@ static struct watcher *find_watcher(struct thermometer *t, const gchar *sender, match->srv = g_strdup(sender); match->path = g_strdup(path); - l = g_slist_find_custom(t->fwatchers, match, cmp_watcher); + l = g_slist_find_custom(list, match, cmp_watcher); destroy_watcher(match); if (l != NULL) @@ -670,7 +696,7 @@ static DBusMessage *register_watcher(DBusConnection *conn, DBusMessage *msg, DBUS_TYPE_INVALID)) return btd_error_invalid_args(msg); - watcher = find_watcher(t, sender, path); + watcher = find_watcher(t->fwatchers, sender, path); if (watcher != NULL) return btd_error_already_exists(msg); @@ -703,7 +729,7 @@ static DBusMessage *unregister_watcher(DBusConnection *conn, DBusMessage *msg, DBUS_TYPE_INVALID)) return btd_error_invalid_args(msg); - watcher = find_watcher(t, sender, path); + watcher = find_watcher(t->fwatchers, sender, path); if (watcher == NULL) return btd_error_does_not_exist(msg); @@ -721,9 +747,33 @@ static DBusMessage *unregister_watcher(DBusConnection *conn, DBusMessage *msg, static DBusMessage *enable_intermediate(DBusConnection *conn, DBusMessage *msg, void *data) { - /* TODO: */ - return g_dbus_create_error(msg, ERROR_INTERFACE ".ThermometerError", - "Function not implemented."); + const gchar *sender = dbus_message_get_sender(msg); + struct thermometer *t = data; + struct watcher *watcher; + gchar *path; + + if (!t->intermediate) + return btd_error_not_supported(msg); + + if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path, + DBUS_TYPE_INVALID)) + return btd_error_invalid_args(msg); + + watcher = find_watcher(t->fwatchers, sender, path); + if (watcher == NULL) + return btd_error_does_not_exist(msg); + + if (find_watcher(t->iwatchers, sender, path)) + return btd_error_already_exists(msg); + + DBG("Intermediate measurement watcher %s registered", path); + + if (g_slist_length(t->iwatchers) == 0) + enable_intermediate_measurement(t); + + t->iwatchers = g_slist_prepend(t->iwatchers, watcher); + + return dbus_message_new_method_return(msg); } static DBusMessage *disable_intermediate(DBusConnection *conn, DBusMessage *msg, -- 1.7.7.3 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* Health Thermometer Profile (HTP) @ 2011-11-09 10:51 Santiago Carot-Nemesio 2011-11-09 10:51 ` [PATCH 1/6] Manage GATT attribute indications in handle callback Santiago Carot-Nemesio 0 siblings, 1 reply; 11+ messages in thread From: Santiago Carot-Nemesio @ 2011-11-09 10:51 UTC (permalink / raw) To: linux-bluetooth This is a new set of patches to enhance thermometer plugin fuctionality. These patches enable watchers to receiave final/intermediate measurements from thermometers devices. [PATCH 1/6] Manage GATT attribute indications in handle callback. [PATCH 2/6] Parse final measurement indication [PATCH 3/6] Add org.bluez.ThermometerWatcher interface to default [PATCH 4/6] Implement EnableIntermediateMeasurement D-Bus method [PATCH 5/6] Implement DisableIntermediateMeasurement D-Bus method [PATCH 6/6] Notify intermediate measurements ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 1/6] Manage GATT attribute indications in handle callback. 2011-11-09 10:51 Health Thermometer Profile (HTP) Santiago Carot-Nemesio @ 2011-11-09 10:51 ` Santiago Carot-Nemesio 2011-11-09 10:51 ` [PATCH 2/6] Parse final measurement indication Santiago Carot-Nemesio 0 siblings, 1 reply; 11+ messages in thread From: Santiago Carot-Nemesio @ 2011-11-09 10:51 UTC (permalink / raw) To: linux-bluetooth; +Cc: Santiago Carot-Nemesio --- thermometer/thermometer.c | 57 ++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 56 insertions(+), 1 deletions(-) diff --git a/thermometer/thermometer.c b/thermometer/thermometer.c index 5b0e30a..0928f15 100644 --- a/thermometer/thermometer.c +++ b/thermometer/thermometer.c @@ -163,6 +163,17 @@ static gint cmp_char_uuid(gconstpointer a, gconstpointer b) return g_strcmp0(ch->attr.uuid, uuid); } +static gint cmp_char_val_handle(gconstpointer a, gconstpointer b) +{ + const struct characteristic *ch = a; + const uint16_t *handle = b; + + if (ch->attr.value_handle == *handle) + return 0; + + return -1; +} + static gint cmp_descriptor(gconstpointer a, gconstpointer b) { const struct descriptor *desc = a; @@ -703,9 +714,53 @@ static GDBusSignalTable thermometer_signals[] = { { } }; +static void proc_measurement(struct thermometer *t, const uint8_t *pdu, + uint16_t len, gboolean final) +{ + DBG("TODO: Process measurement indication"); +} + +static void proc_measurement_interval(struct thermometer *t, const uint8_t *pdu, + uint16_t len) +{ + DBG("TODO: Process measurements interval indication"); +} + static void ind_handler(const uint8_t *pdu, uint16_t len, gpointer user_data) { - /* TODO: Process indication */ + struct thermometer *t = user_data; + const struct characteristic *ch; + uint8_t opdu[ATT_MAX_MTU]; + uint16_t handle, olen; + GSList *l; + + if (len < 3) { + DBG("Bad pdu received"); + goto done; + } + + if (pdu[0] != ATT_OP_HANDLE_IND) { + DBG("Not indication received"); + return; + } + + handle = att_get_u16(&pdu[1]); + l = g_slist_find_custom(t->chars, &handle, cmp_char_val_handle); + if (l == NULL) + goto done; + + ch = l->data; + if (g_strcmp0(ch->attr.uuid, TEMPERATURE_MEASUREMENT_UUID) == 0) + proc_measurement(t, pdu, len, TRUE); + else if (g_strcmp0(ch->attr.uuid, MEASUREMENT_INTERVAL_UUID) == 0) + proc_measurement_interval(t, pdu, len); + +done: + olen = enc_confirmation(opdu, sizeof(opdu)); + + if (olen > 0) + g_attrib_send(t->attrib, 0, opdu[0], opdu, olen, NULL, NULL, + NULL); } static void attio_connected_cb(GAttrib *attrib, gpointer user_data) -- 1.7.7.2 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 2/6] Parse final measurement indication 2011-11-09 10:51 ` [PATCH 1/6] Manage GATT attribute indications in handle callback Santiago Carot-Nemesio @ 2011-11-09 10:51 ` Santiago Carot-Nemesio 2011-11-09 10:52 ` [PATCH 3/6] Add org.bluez.ThermometerWatcher interface to default policy Santiago Carot-Nemesio 0 siblings, 1 reply; 11+ messages in thread From: Santiago Carot-Nemesio @ 2011-11-09 10:51 UTC (permalink / raw) To: linux-bluetooth; +Cc: Santiago Carot-Nemesio --- thermometer/thermometer.c | 154 ++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 153 insertions(+), 1 deletions(-) diff --git a/thermometer/thermometer.c b/thermometer/thermometer.c index 0928f15..3a82e20 100644 --- a/thermometer/thermometer.c +++ b/thermometer/thermometer.c @@ -47,6 +47,13 @@ #define INTERMEDIATE_TEMPERATURE_UUID "00002a1e-0000-1000-8000-00805f9b34fb" #define MEASUREMENT_INTERVAL_UUID "00002a21-0000-1000-8000-00805f9b34fb" +/* Temperature measurement flag fields */ +#define TEMP_UNITS 0x01 +#define TEMP_TIME_STAMP 0x02 +#define TEMP_TYPE 0x04 + +#define FLOAT_MAX_MANTISSA 16777216 /* 2^24 */ + struct thermometer { DBusConnection *conn; /* The connection to the bus */ struct btd_device *dev; /* Device reference */ @@ -84,8 +91,36 @@ struct watcher { gchar *path; }; +struct measurement { + gint16 exp; + gint32 mant; + guint64 time; + gboolean suptime; + gchar *unit; + gchar *type; + gchar *msmnt; +}; + static GSList *thermometers = NULL; +static gchar *temptype2str(uint8_t value) +{ + switch (value) { + case 1: return "Armpit"; + case 2: return "Body"; + case 3: return "Ear"; + case 4: return "Finger"; + case 5: return "Intestines"; + case 6: return "Mouth"; + case 7: return "Rectum"; + case 8: return "Toe"; + case 9: return "Tympanum"; + default: + error("Temperature type %d reserved for future use", value); + return NULL; + }; +} + static void destroy_watcher(gpointer user_data) { struct watcher *watcher = user_data; @@ -714,10 +749,127 @@ static GDBusSignalTable thermometer_signals[] = { { } }; +static void update_watcher(struct watcher *w, struct measurement *m) +{ + DBusConnection *conn = w->t->conn; + DBusMessageIter iter; + DBusMessageIter dict; + DBusMessage *msg; + + msg = dbus_message_new_method_call(w->srv, w->path, + "org.bluez.ThermometerWatcher", + "MeasurementReceived"); + if (msg == NULL) + return; + + dbus_message_iter_init_append(msg, &iter); + + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING + DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); + + dict_append_entry(&dict, "Exponent", DBUS_TYPE_INT16, &m->exp); + dict_append_entry(&dict, "Mantissa", DBUS_TYPE_INT32, &m->mant); + dict_append_entry(&dict, "Unit", DBUS_TYPE_STRING, &m->unit); + + if (m->suptime) + dict_append_entry(&dict, "Time", DBUS_TYPE_UINT64, &m->time); + + dict_append_entry(&dict, "Type", DBUS_TYPE_STRING, &m->type); + dict_append_entry(&dict, "Measurement", DBUS_TYPE_STRING, &m->msmnt); + + dbus_message_iter_close_container(&iter, &dict); + + dbus_message_set_no_reply(msg, TRUE); + g_dbus_send_message(conn, msg); +} + +static void recv_measurement(struct thermometer *t, struct measurement *msmt) +{ + GSList *l; + + if (g_strcmp0(msmt->msmnt, "Intermediate") == 0) { + DBG("Notification of intermediate measurement not implemented"); + return; + } + + for (l = t->fwatchers; l; l = l->next) + update_watcher(l->data, msmt); +} + static void proc_measurement(struct thermometer *t, const uint8_t *pdu, uint16_t len, gboolean final) { - DBG("TODO: Process measurement indication"); + struct measurement msmt; + uint8_t flags; + uint32_t raw; + + if (len < 4) { + DBG("Mandatory flags are not provided"); + return; + } + + flags = pdu[3]; + if (flags & TEMP_UNITS) + msmt.unit = "Fahrenheit"; + else + msmt.unit = "Celsius"; + + if (len < 8) { + DBG("Temperature measurement value is not provided"); + return; + } + + raw = att_get_u32(&pdu[4]); + msmt.mant = raw & 0x00FFFFFF; + msmt.exp = ((gint32) raw) >> 24; + + if (msmt.mant & 0x00800000) { + /* convert to C2 negative value */ + msmt.mant = msmt.mant - FLOAT_MAX_MANTISSA; + } + + if (flags & TEMP_TIME_STAMP) { + struct tm ts; + time_t time; + + if (len < 15) { + DBG("Can't get time stamp value"); + return; + } + + ts.tm_year = att_get_u16(&pdu[8]) - 1900; + ts.tm_mon = pdu[10]; + ts.tm_mday = pdu[11]; + ts.tm_hour = pdu[12]; + ts.tm_min = pdu[13]; + ts.tm_sec = pdu[14]; + ts.tm_isdst = -1; + + time = mktime(&ts); + msmt.time = (guint64) time; + msmt.suptime = TRUE; + } else + msmt.suptime = FALSE; + + if (flags & TEMP_TYPE) { + if (len < 16) { + DBG("Can't get temperature type"); + return; + } + + msmt.type = temptype2str(pdu[15]); + } else if (t->has_type) + msmt.type = temptype2str(t->type); + else { + DBG("Can't get temperature type"); + return; + } + + msmt.msmnt = final ? "Final" : "Intermediate"; + + recv_measurement(t, &msmt); } static void proc_measurement_interval(struct thermometer *t, const uint8_t *pdu, -- 1.7.7.2 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 3/6] Add org.bluez.ThermometerWatcher interface to default policy 2011-11-09 10:51 ` [PATCH 2/6] Parse final measurement indication Santiago Carot-Nemesio @ 2011-11-09 10:52 ` Santiago Carot-Nemesio 2011-11-09 10:52 ` [PATCH 4/6] Implement EnableIntermediateMeasurement D-Bus method Santiago Carot-Nemesio 0 siblings, 1 reply; 11+ messages in thread From: Santiago Carot-Nemesio @ 2011-11-09 10:52 UTC (permalink / raw) To: linux-bluetooth; +Cc: Santiago Carot-Nemesio --- src/bluetooth.conf | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diff --git a/src/bluetooth.conf b/src/bluetooth.conf index b5a6af3..664dbd9 100644 --- a/src/bluetooth.conf +++ b/src/bluetooth.conf @@ -15,6 +15,7 @@ <allow send_interface="org.bluez.MediaEndpoint"/> <allow send_interface="org.bluez.MediaPlayer"/> <allow send_interface="org.bluez.Watcher"/> + <allow send_interface="org.bluez.ThermometerWatcher"/> </policy> <policy at_console="true"> -- 1.7.7.2 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 4/6] Implement EnableIntermediateMeasurement D-Bus method 2011-11-09 10:52 ` [PATCH 3/6] Add org.bluez.ThermometerWatcher interface to default policy Santiago Carot-Nemesio @ 2011-11-09 10:52 ` Santiago Carot-Nemesio 0 siblings, 0 replies; 11+ messages in thread From: Santiago Carot-Nemesio @ 2011-11-09 10:52 UTC (permalink / raw) To: linux-bluetooth; +Cc: Santiago Carot-Nemesio --- thermometer/thermometer.c | 74 +++++++++++++++++++++++++++++++++++++------- 1 files changed, 62 insertions(+), 12 deletions(-) diff --git a/thermometer/thermometer.c b/thermometer/thermometer.c index 3a82e20..2472f1e 100644 --- a/thermometer/thermometer.c +++ b/thermometer/thermometer.c @@ -63,6 +63,7 @@ struct thermometer { guint attindid; /* Att incications id */ GSList *chars; /* Characteristics */ GSList *fwatchers; /* Final measurements */ + GSList *iwatchers; /* Intermediate measurements */ gboolean intermediate; guint8 type; guint16 interval; @@ -557,7 +558,7 @@ static struct descriptor *get_descriptor(struct characteristic *ch, return l->data; } -static void final_measurement_cb(guint8 status, const guint8 *pdu, +static void measurement_cb(guint8 status, const guint8 *pdu, guint16 len, gpointer user_data) { gchar *msg = user_data; @@ -592,8 +593,34 @@ static void enable_final_measurement(struct thermometer *t) atval[0] = 0x02; atval[1] = 0x00; msg = g_strdup("Enable final measurement"); - gatt_write_char(t->attrib, desc->handle, atval, 2, - final_measurement_cb, msg); + gatt_write_char(t->attrib, desc->handle, atval, 2, measurement_cb, msg); +} + +static void enable_intermediate_measurement(struct thermometer *t) +{ + struct characteristic *ch; + struct descriptor *desc; + bt_uuid_t btuuid; + uint8_t atval[2]; + gchar *msg; + + ch = get_characteristic(t, INTERMEDIATE_TEMPERATURE_UUID); + if (ch == NULL) { + DBG("Intermediate measurement characteristic not found"); + return; + } + + bt_uuid16_create(&btuuid, GATT_CLIENT_CHARAC_CFG_UUID); + desc = get_descriptor(ch, &btuuid); + if (desc == NULL) { + DBG("Client characteristic configuration descriptor not found"); + return; + } + + atval[0] = 0x01; + atval[1] = 0x00; + msg = g_strdup("Enable intermediate measurement"); + gatt_write_char(t->attrib, desc->handle, atval, 2, measurement_cb, msg); } static void disable_final_measurement(struct thermometer *t) @@ -620,8 +647,7 @@ static void disable_final_measurement(struct thermometer *t) atval[0] = 0x00; atval[1] = 0x00; msg = g_strdup("Disable final measurement"); - gatt_write_char(t->attrib, desc->handle, atval, 2, - final_measurement_cb, msg); + gatt_write_char(t->attrib, desc->handle, atval, 2, measurement_cb, msg); } static void watcher_exit(DBusConnection *conn, void *user_data) @@ -638,7 +664,7 @@ static void watcher_exit(DBusConnection *conn, void *user_data) disable_final_measurement(t); } -static struct watcher *find_watcher(struct thermometer *t, const gchar *sender, +static struct watcher *find_watcher(GSList *list, const gchar *sender, const gchar *path) { struct watcher *match; @@ -648,7 +674,7 @@ static struct watcher *find_watcher(struct thermometer *t, const gchar *sender, match->srv = g_strdup(sender); match->path = g_strdup(path); - l = g_slist_find_custom(t->fwatchers, match, cmp_watcher); + l = g_slist_find_custom(list, match, cmp_watcher); destroy_watcher(match); if (l != NULL) @@ -669,7 +695,7 @@ static DBusMessage *register_watcher(DBusConnection *conn, DBusMessage *msg, DBUS_TYPE_INVALID)) return btd_error_invalid_args(msg); - watcher = find_watcher(t, sender, path); + watcher = find_watcher(t->fwatchers, sender, path); if (watcher != NULL) return btd_error_already_exists(msg); @@ -702,7 +728,7 @@ static DBusMessage *unregister_watcher(DBusConnection *conn, DBusMessage *msg, DBUS_TYPE_INVALID)) return btd_error_invalid_args(msg); - watcher = find_watcher(t, sender, path); + watcher = find_watcher(t->fwatchers, sender, path); if (watcher == NULL) return btd_error_does_not_exist(msg); @@ -720,9 +746,33 @@ static DBusMessage *unregister_watcher(DBusConnection *conn, DBusMessage *msg, static DBusMessage *enable_intermediate(DBusConnection *conn, DBusMessage *msg, void *data) { - /* TODO: */ - return g_dbus_create_error(msg, ERROR_INTERFACE ".ThermometerError", - "Function not implemented."); + const gchar *sender = dbus_message_get_sender(msg); + struct thermometer *t = data; + struct watcher *watcher; + gchar *path; + + if (!t->intermediate) + return btd_error_not_supported(msg); + + if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path, + DBUS_TYPE_INVALID)) + return btd_error_invalid_args(msg); + + watcher = find_watcher(t->fwatchers, sender, path); + if (watcher == NULL) + return btd_error_does_not_exist(msg); + + if (find_watcher(t->iwatchers, sender, path)) + return btd_error_already_exists(msg); + + DBG("Intermediate measurement watcher %s registered", path); + + if (g_slist_length(t->iwatchers) == 0) + enable_intermediate_measurement(t); + + t->iwatchers = g_slist_prepend(t->iwatchers, watcher); + + return dbus_message_new_method_return(msg); } static DBusMessage *disable_intermediate(DBusConnection *conn, DBusMessage *msg, -- 1.7.7.2 ^ permalink raw reply related [flat|nested] 11+ messages in thread
end of thread, other threads:[~2011-11-14 12:11 UTC | newest] Thread overview: 11+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2011-11-10 11:36 Health Thermometer Profile (HTP) Santiago Carot-Nemesio 2011-11-10 11:36 ` [PATCH 1/6] Manage GATT attribute indications in handle callback Santiago Carot-Nemesio 2011-11-10 11:36 ` [PATCH 2/6] Parse final measurement indication Santiago Carot-Nemesio 2011-11-10 11:36 ` [PATCH 3/6] Add org.bluez.ThermometerWatcher interface to default policy Santiago Carot-Nemesio 2011-11-10 11:37 ` [PATCH 4/6] Implement EnableIntermediateMeasurement D-Bus method Santiago Carot-Nemesio 2011-11-10 11:37 ` [PATCH 5/6] Implement DisableIntermediateMeasurement " Santiago Carot-Nemesio 2011-11-10 11:37 ` [PATCH 6/6] Notify intermediate measurements Santiago Carot-Nemesio 2011-11-12 18:37 ` [PATCH 2/6] Parse final measurement indication Johan Hedberg 2011-11-13 9:18 ` Santiago Carot -- strict thread matches above, loose matches on Subject: below -- 2011-11-14 12:11 Health Thermometer Profile (HTP) Santiago Carot-Nemesio 2011-11-14 12:11 ` [PATCH 1/6] Manage GATT attribute indications in handle callback Santiago Carot-Nemesio 2011-11-14 12:11 ` [PATCH 2/6] Parse final measurement indication Santiago Carot-Nemesio 2011-11-14 12:11 ` [PATCH 3/6] Add org.bluez.ThermometerWatcher interface to default policy Santiago Carot-Nemesio 2011-11-14 12:11 ` [PATCH 4/6] Implement EnableIntermediateMeasurement D-Bus method Santiago Carot-Nemesio 2011-11-09 10:51 Health Thermometer Profile (HTP) Santiago Carot-Nemesio 2011-11-09 10:51 ` [PATCH 1/6] Manage GATT attribute indications in handle callback Santiago Carot-Nemesio 2011-11-09 10:51 ` [PATCH 2/6] Parse final measurement indication Santiago Carot-Nemesio 2011-11-09 10:52 ` [PATCH 3/6] Add org.bluez.ThermometerWatcher interface to default policy Santiago Carot-Nemesio 2011-11-09 10:52 ` [PATCH 4/6] Implement EnableIntermediateMeasurement D-Bus method Santiago Carot-Nemesio
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).