3 * BlueZ - Bluetooth protocol stack for Linux
5 * Copyright (C) 2011 GSyC/LibreSoft, Universidad Rey Juan Carlos.
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
29 #include <bluetooth/uuid.h>
31 #include "dbus-common.h"
40 #include "thermometer.h"
42 #define THERMOMETER_INTERFACE "org.bluez.Thermometer"
44 /* Temperature measurement flag fields */
45 #define TEMP_UNITS 0x01
46 #define TEMP_TIME_STAMP 0x02
47 #define TEMP_TYPE 0x04
49 #define FLOAT_MAX_MANTISSA 16777216 /* 2^24 */
52 DBusConnection *conn; /* The connection to the bus */
53 struct btd_device *dev; /* Device reference */
54 GAttrib *attrib; /* GATT connection */
55 struct att_range *svc_range; /* Thermometer range */
56 guint attioid; /* Att watcher id */
57 guint attindid; /* Att incications id */
58 guint attnotid; /* Att notifications id */
59 GSList *chars; /* Characteristics */
60 GSList *fwatchers; /* Final measurements */
61 GSList *iwatchers; /* Intermediate measurements */
62 gboolean intermediate;
68 gboolean has_interval;
71 struct characteristic {
72 struct gatt_char attr; /* Characteristic */
73 GSList *desc; /* Descriptors */
74 struct thermometer *t; /* Thermometer where the char belongs */
78 struct characteristic *ch;
84 struct thermometer *t;
100 struct tmp_interval_data {
101 struct thermometer *thermometer;
105 static GSList *thermometers = NULL;
107 const char *temp_type[] = {
120 static const gchar *temptype2str(uint8_t value)
122 if (value > 0 && value < G_N_ELEMENTS(temp_type))
123 return temp_type[value];
125 error("Temperature type %d reserved for future use", value);
129 static void destroy_watcher(gpointer user_data)
131 struct watcher *watcher = user_data;
133 g_free(watcher->path);
134 g_free(watcher->srv);
138 static void remove_watcher(gpointer user_data)
140 struct watcher *watcher = user_data;
142 g_dbus_remove_watch(watcher->t->conn, watcher->id);
145 static void destroy_char(gpointer user_data)
147 struct characteristic *c = user_data;
149 g_slist_free_full(c->desc, g_free);
153 static void destroy_thermometer(gpointer user_data)
155 struct thermometer *t = user_data;
158 btd_device_remove_attio_callback(t->dev, t->attioid);
161 g_attrib_unregister(t->attrib, t->attindid);
164 g_attrib_unregister(t->attrib, t->attnotid);
166 if (t->attrib != NULL)
167 g_attrib_unref(t->attrib);
169 if (t->chars != NULL)
170 g_slist_free_full(t->chars, destroy_char);
172 if (t->fwatchers != NULL)
173 g_slist_free_full(t->fwatchers, remove_watcher);
175 dbus_connection_unref(t->conn);
176 btd_device_unref(t->dev);
177 g_free(t->svc_range);
181 static gint cmp_device(gconstpointer a, gconstpointer b)
183 const struct thermometer *t = a;
184 const struct btd_device *dev = b;
192 static gint cmp_watcher(gconstpointer a, gconstpointer b)
194 const struct watcher *watcher = a;
195 const struct watcher *match = b;
198 ret = g_strcmp0(watcher->srv, match->srv);
202 return g_strcmp0(watcher->path, match->path);
205 static gint cmp_char_uuid(gconstpointer a, gconstpointer b)
207 const struct characteristic *ch = a;
208 const char *uuid = b;
210 return g_strcmp0(ch->attr.uuid, uuid);
213 static gint cmp_char_val_handle(gconstpointer a, gconstpointer b)
215 const struct characteristic *ch = a;
216 const uint16_t *handle = b;
218 return ch->attr.value_handle - *handle;
221 static gint cmp_descriptor(gconstpointer a, gconstpointer b)
223 const struct descriptor *desc = a;
224 const bt_uuid_t *uuid = b;
226 return bt_uuid_cmp(&desc->uuid, uuid);
229 static struct characteristic *get_characteristic(struct thermometer *t,
234 l = g_slist_find_custom(t->chars, uuid, cmp_char_uuid);
241 static struct descriptor *get_descriptor(struct characteristic *ch,
242 const bt_uuid_t *uuid)
246 l = g_slist_find_custom(ch->desc, uuid, cmp_descriptor);
253 static void change_property(struct thermometer *t, const char *name,
255 if (g_strcmp0(name, "Intermediate") == 0) {
256 gboolean *intermediate = value;
257 if (t->intermediate == *intermediate)
260 t->intermediate = *intermediate;
261 emit_property_changed(t->conn, device_get_path(t->dev),
262 THERMOMETER_INTERFACE, name,
263 DBUS_TYPE_BOOLEAN, &t->intermediate);
264 } else if (g_strcmp0(name, "Interval") == 0) {
265 uint16_t *interval = value;
266 if (t->has_interval && t->interval == *interval)
269 t->has_interval = TRUE;
270 t->interval = *interval;
271 emit_property_changed(t->conn, device_get_path(t->dev),
272 THERMOMETER_INTERFACE, name,
273 DBUS_TYPE_UINT16, &t->interval);
274 } else if (g_strcmp0(name, "Maximum") == 0) {
275 uint16_t *max = value;
280 emit_property_changed(t->conn, device_get_path(t->dev),
281 THERMOMETER_INTERFACE, name,
282 DBUS_TYPE_UINT16, &t->max);
283 } else if (g_strcmp0(name, "Minimum") == 0) {
284 uint16_t *min = value;
289 emit_property_changed(t->conn, device_get_path(t->dev),
290 THERMOMETER_INTERFACE, name,
291 DBUS_TYPE_UINT16, &t->min);
293 DBG("%s is not a thermometer property", name);
296 static void valid_range_desc_cb(guint8 status, const guint8 *pdu, guint16 len,
299 struct descriptor *desc = user_data;
300 uint8_t value[ATT_MAX_MTU];
305 DBG("Valid Range descriptor read failed: %s",
306 att_ecode2str(status));
310 if (!dec_read_resp(pdu, len, value, &vlen)) {
311 DBG("Protocol error\n");
316 DBG("Invalid range received");
320 min = att_get_u16(&value[0]);
321 max = att_get_u16(&value[2]);
323 if (min == 0 || min > max) {
324 DBG("Invalid range");
328 change_property(desc->ch->t, "Maximum", &max);
329 change_property(desc->ch->t, "Minimum", &min);
332 static void measurement_cb(guint8 status, const guint8 *pdu,
333 guint16 len, gpointer user_data)
335 char *msg = user_data;
338 error("%s failed", msg);
343 static void process_thermometer_desc(struct descriptor *desc)
345 struct characteristic *ch = desc->ch;
346 char uuidstr[MAX_LEN_UUID_STR];
349 bt_uuid16_create(&btuuid, GATT_CLIENT_CHARAC_CFG_UUID);
351 if (bt_uuid_cmp(&desc->uuid, &btuuid) == 0) {
356 if (g_strcmp0(ch->attr.uuid,
357 TEMPERATURE_MEASUREMENT_UUID) == 0) {
358 if (g_slist_length(ch->t->fwatchers) == 0)
361 val = GATT_CLIENT_CHARAC_CFG_IND_BIT;
362 msg = g_strdup("Enable Temperature Measurement "
364 } else if (g_strcmp0(ch->attr.uuid,
365 INTERMEDIATE_TEMPERATURE_UUID) == 0) {
366 if (g_slist_length(ch->t->iwatchers) == 0)
369 val = GATT_CLIENT_CHARAC_CFG_NOTIF_BIT;
370 msg = g_strdup("Enable Intermediate Temperature "
372 } else if (g_strcmp0(ch->attr.uuid,
373 MEASUREMENT_INTERVAL_UUID) == 0) {
374 val = GATT_CLIENT_CHARAC_CFG_IND_BIT;
375 msg = g_strdup("Enable Measurement Interval "
380 att_put_u16(val, atval);
381 gatt_write_char(ch->t->attrib, desc->handle, atval, 2,
382 measurement_cb, msg);
386 bt_uuid16_create(&btuuid, GATT_CHARAC_VALID_RANGE_UUID);
388 if (bt_uuid_cmp(&desc->uuid, &btuuid) == 0 && g_strcmp0(ch->attr.uuid,
389 MEASUREMENT_INTERVAL_UUID) == 0) {
390 gatt_read_char(ch->t->attrib, desc->handle, 0,
391 valid_range_desc_cb, desc);
396 bt_uuid_to_string(&desc->uuid, uuidstr, MAX_LEN_UUID_STR);
397 DBG("Ignored descriptor %s in characteristic %s", uuidstr,
401 static void discover_desc_cb(guint8 status, const guint8 *pdu, guint16 len,
404 struct characteristic *ch = user_data;
405 struct att_data_list *list;
410 error("Discover all characteristic descriptors failed [%s]: %s",
411 ch->attr.uuid, att_ecode2str(status));
415 list = dec_find_info_resp(pdu, len, &format);
419 for (i = 0; i < list->num; i++) {
420 struct descriptor *desc;
423 value = list->data[i];
424 desc = g_new0(struct descriptor, 1);
425 desc->handle = att_get_u16(value);
429 desc->uuid = att_get_uuid16(&value[2]);
431 desc->uuid = att_get_uuid128(&value[2]);
433 ch->desc = g_slist_append(ch->desc, desc);
434 process_thermometer_desc(desc);
437 att_data_list_free(list);
440 static void read_temp_type_cb(guint8 status, const guint8 *pdu, guint16 len,
443 struct characteristic *ch = user_data;
444 struct thermometer *t = ch->t;
445 uint8_t value[ATT_MAX_MTU];
449 DBG("Temperature Type value read failed: %s",
450 att_ecode2str(status));
454 if (!dec_read_resp(pdu, len, value, &vlen)) {
455 DBG("Protocol error.");
460 DBG("Invalid length for Temperature type");
468 static void read_interval_cb(guint8 status, const guint8 *pdu, guint16 len,
471 struct characteristic *ch = user_data;
472 uint8_t value[ATT_MAX_MTU];
477 DBG("Measurement Interval value read failed: %s",
478 att_ecode2str(status));
482 if (!dec_read_resp(pdu, len, value, &vlen)) {
483 DBG("Protocol error\n");
488 DBG("Invalid Interval received");
492 interval = att_get_u16(&value[0]);
493 change_property(ch->t, "Interval", &interval);
496 static void process_thermometer_char(struct characteristic *ch)
498 if (g_strcmp0(ch->attr.uuid, INTERMEDIATE_TEMPERATURE_UUID) == 0) {
499 gboolean intermediate = TRUE;
500 change_property(ch->t, "Intermediate", &intermediate);
502 } else if (g_strcmp0(ch->attr.uuid, TEMPERATURE_TYPE_UUID) == 0)
503 gatt_read_char(ch->t->attrib, ch->attr.value_handle, 0,
504 read_temp_type_cb, ch);
505 else if (g_strcmp0(ch->attr.uuid, MEASUREMENT_INTERVAL_UUID) == 0)
506 gatt_read_char(ch->t->attrib, ch->attr.value_handle, 0,
507 read_interval_cb, ch);
510 static void configure_thermometer_cb(GSList *characteristics, guint8 status,
513 struct thermometer *t = user_data;
517 error("Discover thermometer characteristics: %s",
518 att_ecode2str(status));
522 for (l = characteristics; l; l = l->next) {
523 struct gatt_char *c = l->data;
524 struct characteristic *ch;
527 ch = g_new0(struct characteristic, 1);
528 ch->attr.handle = c->handle;
529 ch->attr.properties = c->properties;
530 ch->attr.value_handle = c->value_handle;
531 memcpy(ch->attr.uuid, c->uuid, MAX_LEN_UUID_STR + 1);
534 t->chars = g_slist_append(t->chars, ch);
536 process_thermometer_char(ch);
538 start = c->value_handle + 1;
540 if (l->next != NULL) {
541 struct gatt_char *c = l->next->data;
542 if (start == c->handle)
545 } else if (c->value_handle != t->svc_range->end)
546 end = t->svc_range->end;
550 gatt_find_info(t->attrib, start, end, discover_desc_cb, ch);
554 static DBusMessage *get_properties(DBusConnection *conn, DBusMessage *msg,
557 struct thermometer *t = data;
558 DBusMessageIter iter;
559 DBusMessageIter dict;
562 reply = dbus_message_new_method_return(msg);
566 dbus_message_iter_init_append(reply, &iter);
568 dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
569 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
570 DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
571 DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
573 dict_append_entry(&dict, "Intermediate", DBUS_TYPE_BOOLEAN,
576 if (t->has_interval) {
577 dict_append_entry(&dict, "Interval", DBUS_TYPE_UINT16,
579 dict_append_entry(&dict, "Maximum", DBUS_TYPE_UINT16, &t->max);
580 dict_append_entry(&dict, "Minimum", DBUS_TYPE_UINT16, &t->min);
583 dbus_message_iter_close_container(&iter, &dict);
588 static void write_interval_cb (guint8 status, const guint8 *pdu, guint16 len,
591 struct tmp_interval_data *data = user_data;
594 error("Interval Write Request failed %s",
595 att_ecode2str(status));
599 if (!dec_write_resp(pdu, len)) {
600 error("Interval Write Request: protocol error");
604 change_property(data->thermometer, "Interval", &data->interval);
610 static DBusMessage *write_attr_interval(struct thermometer *t, DBusMessage *msg,
613 struct tmp_interval_data *data;
614 struct characteristic *ch;
617 if (t->attrib == NULL)
618 return btd_error_not_connected(msg);
620 ch = get_characteristic(t, MEASUREMENT_INTERVAL_UUID);
622 return btd_error_not_available(msg);
624 if (value < t->min || value > t->max)
625 return btd_error_invalid_args(msg);
627 att_put_u16(value, &atval[0]);
629 data = g_new0(struct tmp_interval_data, 1);
630 data->thermometer = t;
631 data->interval = value;
632 gatt_write_char(t->attrib, ch->attr.value_handle, atval, 2,
633 write_interval_cb, data);
635 return dbus_message_new_method_return(msg);
638 static DBusMessage *set_property(DBusConnection *conn, DBusMessage *msg,
641 struct thermometer *t = data;
642 const char *property;
643 DBusMessageIter iter;
647 if (!dbus_message_iter_init(msg, &iter))
648 return btd_error_invalid_args(msg);
650 if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
651 return btd_error_invalid_args(msg);
653 dbus_message_iter_get_basic(&iter, &property);
654 if (g_strcmp0("Interval", property) != 0)
655 return btd_error_invalid_args(msg);
657 if (!t->has_interval)
658 return btd_error_not_available(msg);
660 dbus_message_iter_next(&iter);
661 if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
662 return btd_error_invalid_args(msg);
664 dbus_message_iter_recurse(&iter, &sub);
666 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_UINT16)
667 return btd_error_invalid_args(msg);
669 dbus_message_iter_get_basic(&sub, &value);
671 return write_attr_interval(t, msg, value);
674 static void enable_final_measurement(struct thermometer *t)
676 struct characteristic *ch;
677 struct descriptor *desc;
682 if (t->attrib == NULL)
685 ch = get_characteristic(t, TEMPERATURE_MEASUREMENT_UUID);
687 DBG("Temperature measurement characteristic not found");
691 bt_uuid16_create(&btuuid, GATT_CLIENT_CHARAC_CFG_UUID);
692 desc = get_descriptor(ch, &btuuid);
694 DBG("Client characteristic configuration descriptor not found");
700 msg = g_strdup("Enable final measurement");
701 gatt_write_char(t->attrib, desc->handle, atval, 2, measurement_cb, msg);
704 static void enable_intermediate_measurement(struct thermometer *t)
706 struct characteristic *ch;
707 struct descriptor *desc;
712 if (t->attrib == NULL)
715 ch = get_characteristic(t, INTERMEDIATE_TEMPERATURE_UUID);
717 DBG("Intermediate measurement characteristic not found");
721 bt_uuid16_create(&btuuid, GATT_CLIENT_CHARAC_CFG_UUID);
722 desc = get_descriptor(ch, &btuuid);
724 DBG("Client characteristic configuration descriptor not found");
730 msg = g_strdup("Enable intermediate measurement");
731 gatt_write_char(t->attrib, desc->handle, atval, 2, measurement_cb, msg);
734 static void disable_final_measurement(struct thermometer *t)
736 struct characteristic *ch;
737 struct descriptor *desc;
742 if (t->attrib == NULL)
745 ch = get_characteristic(t, TEMPERATURE_MEASUREMENT_UUID);
747 DBG("Temperature measurement characteristic not found");
751 bt_uuid16_create(&btuuid, GATT_CLIENT_CHARAC_CFG_UUID);
752 desc = get_descriptor(ch, &btuuid);
754 DBG("Client characteristic configuration descriptor not found");
760 msg = g_strdup("Disable final measurement");
761 gatt_write_char(t->attrib, desc->handle, atval, 2, measurement_cb, msg);
764 static void disable_intermediate_measurement(struct thermometer *t)
766 struct characteristic *ch;
767 struct descriptor *desc;
772 if (t->attrib == NULL)
775 ch = get_characteristic(t, INTERMEDIATE_TEMPERATURE_UUID);
777 DBG("Intermediate measurement characteristic not found");
781 bt_uuid16_create(&btuuid, GATT_CLIENT_CHARAC_CFG_UUID);
782 desc = get_descriptor(ch, &btuuid);
784 DBG("Client characteristic configuration descriptor not found");
790 msg = g_strdup("Disable intermediate measurement");
791 gatt_write_char(t->attrib, desc->handle, atval, 2, measurement_cb, msg);
794 static void remove_int_watcher(struct thermometer *t, struct watcher *w)
796 if (!g_slist_find(t->iwatchers, w))
799 t->iwatchers = g_slist_remove(t->iwatchers, w);
801 if (g_slist_length(t->iwatchers) == 0)
802 disable_intermediate_measurement(t);
805 static void watcher_exit(DBusConnection *conn, void *user_data)
807 struct watcher *watcher = user_data;
808 struct thermometer *t = watcher->t;
810 DBG("Thermometer watcher %s disconnected", watcher->path);
812 remove_int_watcher(t, watcher);
814 t->fwatchers = g_slist_remove(t->fwatchers, watcher);
815 g_dbus_remove_watch(watcher->t->conn, watcher->id);
817 if (g_slist_length(t->fwatchers) == 0)
818 disable_final_measurement(t);
821 static struct watcher *find_watcher(GSList *list, const char *sender,
824 struct watcher *match;
827 match = g_new0(struct watcher, 1);
828 match->srv = g_strdup(sender);
829 match->path = g_strdup(path);
831 l = g_slist_find_custom(list, match, cmp_watcher);
832 destroy_watcher(match);
840 static DBusMessage *register_watcher(DBusConnection *conn, DBusMessage *msg,
843 const char *sender = dbus_message_get_sender(msg);
844 struct thermometer *t = data;
845 struct watcher *watcher;
848 if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
850 return btd_error_invalid_args(msg);
852 watcher = find_watcher(t->fwatchers, sender, path);
854 return btd_error_already_exists(msg);
856 DBG("Thermometer watcher %s registered", path);
858 watcher = g_new0(struct watcher, 1);
859 watcher->srv = g_strdup(sender);
860 watcher->path = g_strdup(path);
862 watcher->id = g_dbus_add_disconnect_watch(conn, sender, watcher_exit,
863 watcher, destroy_watcher);
865 if (g_slist_length(t->fwatchers) == 0)
866 enable_final_measurement(t);
868 t->fwatchers = g_slist_prepend(t->fwatchers, watcher);
870 return dbus_message_new_method_return(msg);
873 static DBusMessage *unregister_watcher(DBusConnection *conn, DBusMessage *msg,
876 const char *sender = dbus_message_get_sender(msg);
877 struct thermometer *t = data;
878 struct watcher *watcher;
881 if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
883 return btd_error_invalid_args(msg);
885 watcher = find_watcher(t->fwatchers, sender, path);
887 return btd_error_does_not_exist(msg);
889 DBG("Thermometer watcher %s unregistered", path);
891 remove_int_watcher(t, watcher);
893 t->fwatchers = g_slist_remove(t->fwatchers, watcher);
894 g_dbus_remove_watch(watcher->t->conn, watcher->id);
896 if (g_slist_length(t->fwatchers) == 0)
897 disable_final_measurement(t);
899 return dbus_message_new_method_return(msg);
902 static DBusMessage *enable_intermediate(DBusConnection *conn, DBusMessage *msg,
905 const char *sender = dbus_message_get_sender(msg);
906 struct thermometer *t = data;
907 struct watcher *watcher;
910 if (!t->intermediate)
911 return btd_error_not_supported(msg);
913 if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
915 return btd_error_invalid_args(msg);
917 watcher = find_watcher(t->fwatchers, sender, path);
919 return btd_error_does_not_exist(msg);
921 if (find_watcher(t->iwatchers, sender, path))
922 return btd_error_already_exists(msg);
924 DBG("Intermediate measurement watcher %s registered", path);
926 if (g_slist_length(t->iwatchers) == 0)
927 enable_intermediate_measurement(t);
929 t->iwatchers = g_slist_prepend(t->iwatchers, watcher);
931 return dbus_message_new_method_return(msg);
934 static DBusMessage *disable_intermediate(DBusConnection *conn, DBusMessage *msg,
937 const char *sender = dbus_message_get_sender(msg);
938 struct thermometer *t = data;
939 struct watcher *watcher;
942 if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
944 return btd_error_invalid_args(msg);
946 watcher = find_watcher(t->iwatchers, sender, path);
948 return btd_error_does_not_exist(msg);
950 DBG("Intermediate measurement %s unregistered", path);
952 remove_int_watcher(t, watcher);
954 return dbus_message_new_method_return(msg);
957 static const GDBusMethodTable thermometer_methods[] = {
958 { GDBUS_METHOD("GetProperties",
959 NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
961 { GDBUS_ASYNC_METHOD("SetProperty",
962 GDBUS_ARGS({ "name", "s" }, { "value", "v" }), NULL,
964 { GDBUS_METHOD("RegisterWatcher",
965 GDBUS_ARGS({ "agent", "o" }), NULL,
967 { GDBUS_METHOD("UnregisterWatcher",
968 GDBUS_ARGS({ "agent", "o" }), NULL,
969 unregister_watcher) },
970 { GDBUS_METHOD("EnableIntermediateMeasurement",
971 GDBUS_ARGS({ "agent", "o" }), NULL,
972 enable_intermediate) },
973 { GDBUS_METHOD("DisableIntermediateMeasurement",
974 GDBUS_ARGS({ "agent", "o" }), NULL,
975 disable_intermediate) },
979 static const GDBusSignalTable thermometer_signals[] = {
980 { GDBUS_SIGNAL("PropertyChanged",
981 GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
985 static void update_watcher(gpointer data, gpointer user_data)
987 struct watcher *w = data;
988 struct measurement *m = user_data;
989 DBusConnection *conn = w->t->conn;
990 DBusMessageIter iter;
991 DBusMessageIter dict;
994 msg = dbus_message_new_method_call(w->srv, w->path,
995 "org.bluez.ThermometerWatcher",
996 "MeasurementReceived");
1000 dbus_message_iter_init_append(msg, &iter);
1002 dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
1003 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
1004 DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
1005 DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
1007 dict_append_entry(&dict, "Exponent", DBUS_TYPE_INT16, &m->exp);
1008 dict_append_entry(&dict, "Mantissa", DBUS_TYPE_INT32, &m->mant);
1009 dict_append_entry(&dict, "Unit", DBUS_TYPE_STRING, &m->unit);
1012 dict_append_entry(&dict, "Time", DBUS_TYPE_UINT64, &m->time);
1014 dict_append_entry(&dict, "Type", DBUS_TYPE_STRING, &m->type);
1015 dict_append_entry(&dict, "Measurement", DBUS_TYPE_STRING, &m->value);
1017 dbus_message_iter_close_container(&iter, &dict);
1019 dbus_message_set_no_reply(msg, TRUE);
1020 g_dbus_send_message(conn, msg);
1023 static void recv_measurement(struct thermometer *t, struct measurement *m)
1027 if (g_strcmp0(m->value, "Intermediate") == 0)
1028 wlist = t->iwatchers;
1030 wlist = t->fwatchers;
1032 g_slist_foreach(wlist, update_watcher, m);
1035 static void proc_measurement(struct thermometer *t, const uint8_t *pdu,
1036 uint16_t len, gboolean final)
1038 struct measurement m;
1044 DBG("Mandatory flags are not provided");
1049 if (flags & TEMP_UNITS)
1050 m.unit = "Fahrenheit";
1055 DBG("Temperature measurement value is not provided");
1059 raw = att_get_u32(&pdu[4]);
1060 m.mant = raw & 0x00FFFFFF;
1061 m.exp = ((int32_t) raw) >> 24;
1063 if (m.mant & 0x00800000) {
1064 /* convert to C2 negative value */
1065 m.mant = m.mant - FLOAT_MAX_MANTISSA;
1068 if (flags & TEMP_TIME_STAMP) {
1073 DBG("Can't get time stamp value");
1077 ts.tm_year = att_get_u16(&pdu[8]) - 1900;
1078 ts.tm_mon = pdu[10] - 1;
1079 ts.tm_mday = pdu[11];
1080 ts.tm_hour = pdu[12];
1081 ts.tm_min = pdu[13];
1082 ts.tm_sec = pdu[14];
1086 m.time = (uint64_t) time;
1091 if (flags & TEMP_TYPE) {
1094 if (m.suptime && len >= 16)
1096 else if (!m.suptime && len >= 9)
1099 DBG("Can't get temperature type");
1103 type = temptype2str(pdu[index]);
1104 } else if (t->has_type)
1105 type = temptype2str(t->type);
1109 m.type = type ? g_strdup(type) : NULL;
1110 m.value = final ? "Final" : "Intermediate";
1112 recv_measurement(t, &m);
1116 static void proc_measurement_interval(struct thermometer *t, const uint8_t *pdu,
1122 DBG("Measurement interval value is not provided");
1126 interval = att_get_u16(&pdu[3]);
1128 change_property(t, "Interval", &interval);
1131 static void ind_handler(const uint8_t *pdu, uint16_t len, gpointer user_data)
1133 struct thermometer *t = user_data;
1134 const struct characteristic *ch;
1135 uint8_t opdu[ATT_MAX_MTU];
1136 uint16_t handle, olen;
1140 DBG("Bad pdu received");
1144 handle = att_get_u16(&pdu[1]);
1145 l = g_slist_find_custom(t->chars, &handle, cmp_char_val_handle);
1147 DBG("Unexpected handle: 0x%04x", handle);
1153 if (g_strcmp0(ch->attr.uuid, TEMPERATURE_MEASUREMENT_UUID) == 0)
1154 proc_measurement(t, pdu, len, TRUE);
1155 else if (g_strcmp0(ch->attr.uuid, MEASUREMENT_INTERVAL_UUID) == 0)
1156 proc_measurement_interval(t, pdu, len);
1158 olen = enc_confirmation(opdu, sizeof(opdu));
1161 g_attrib_send(t->attrib, 0, opdu[0], opdu, olen, NULL, NULL,
1165 static void notif_handler(const uint8_t *pdu, uint16_t len, gpointer user_data)
1167 struct thermometer *t = user_data;
1168 const struct characteristic *ch;
1173 DBG("Bad pdu received");
1177 handle = att_get_u16(&pdu[1]);
1178 l = g_slist_find_custom(t->chars, &handle, cmp_char_val_handle);
1180 DBG("Unexpected handle: 0x%04x", handle);
1185 if (g_strcmp0(ch->attr.uuid, INTERMEDIATE_TEMPERATURE_UUID) == 0)
1186 proc_measurement(t, pdu, len, FALSE);
1189 static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
1191 struct thermometer *t = user_data;
1193 t->attrib = g_attrib_ref(attrib);
1195 t->attindid = g_attrib_register(t->attrib, ATT_OP_HANDLE_IND,
1196 ind_handler, t, NULL);
1197 t->attnotid = g_attrib_register(t->attrib, ATT_OP_HANDLE_NOTIFY,
1198 notif_handler, t, NULL);
1199 gatt_discover_char(t->attrib, t->svc_range->start, t->svc_range->end,
1200 NULL, configure_thermometer_cb, t);
1203 static void attio_disconnected_cb(gpointer user_data)
1205 struct thermometer *t = user_data;
1207 DBG("GATT Disconnected");
1209 if (t->attindid > 0) {
1210 g_attrib_unregister(t->attrib, t->attindid);
1214 if (t->attnotid > 0) {
1215 g_attrib_unregister(t->attrib, t->attnotid);
1219 g_attrib_unref(t->attrib);
1223 int thermometer_register(DBusConnection *connection, struct btd_device *device,
1224 struct gatt_primary *tattr)
1226 const gchar *path = device_get_path(device);
1227 struct thermometer *t;
1229 t = g_new0(struct thermometer, 1);
1230 t->conn = dbus_connection_ref(connection);
1231 t->dev = btd_device_ref(device);
1232 t->svc_range = g_new0(struct att_range, 1);
1233 t->svc_range->start = tattr->range.start;
1234 t->svc_range->end = tattr->range.end;
1236 if (!g_dbus_register_interface(t->conn, path, THERMOMETER_INTERFACE,
1237 thermometer_methods, thermometer_signals,
1238 NULL, t, destroy_thermometer)) {
1239 error("D-Bus failed to register %s interface",
1240 THERMOMETER_INTERFACE);
1241 destroy_thermometer(t);
1245 thermometers = g_slist_prepend(thermometers, t);
1247 t->attioid = btd_device_add_attio_callback(device, attio_connected_cb,
1248 attio_disconnected_cb, t);
1252 void thermometer_unregister(struct btd_device *device)
1254 struct thermometer *t;
1257 l = g_slist_find_custom(thermometers, device, cmp_device);
1262 thermometers = g_slist_remove(thermometers, t);
1263 g_dbus_unregister_interface(t->conn, device_get_path(t->dev),
1264 THERMOMETER_INTERFACE);