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
30 #include "lib/bluetooth.h"
34 #include "gdbus/gdbus.h"
36 #include "src/plugin.h"
37 #include "src/dbus-common.h"
38 #include "src/adapter.h"
39 #include "src/device.h"
40 #include "src/profile.h"
41 #include "src/service.h"
42 #include "src/shared/util.h"
43 #include "src/error.h"
45 #include "attrib/gattrib.h"
46 #include "src/attio.h"
47 #include "attrib/att.h"
48 #include "attrib/gatt.h"
50 #define THERMOMETER_INTERFACE "org.bluez.Thermometer1"
51 #define THERMOMETER_MANAGER_INTERFACE "org.bluez.ThermometerManager1"
52 #define THERMOMETER_WATCHER_INTERFACE "org.bluez.ThermometerWatcher1"
54 /* Temperature measurement flag fields */
55 #define TEMP_UNITS 0x01
56 #define TEMP_TIME_STAMP 0x02
57 #define TEMP_TYPE 0x04
59 #define FLOAT_MAX_MANTISSA 16777216 /* 2^24 */
61 #define VALID_RANGE_DESC_SIZE 4
62 #define TEMPERATURE_TYPE_SIZE 1
63 #define MEASUREMENT_INTERVAL_SIZE 2
65 struct thermometer_adapter {
66 struct btd_adapter *adapter;
68 GSList *fwatchers; /* Final measurements */
69 GSList *iwatchers; /* Intermediate measurements */
73 struct btd_device *dev; /* Device reference */
74 struct thermometer_adapter *tadapter;
75 GAttrib *attrib; /* GATT connection */
76 struct att_range *svc_range; /* Thermometer range */
77 guint attioid; /* Att watcher id */
78 /* attio id for Temperature Measurement value indications */
79 guint attio_measurement_id;
80 /* attio id for Intermediate Temperature value notifications */
81 guint attio_intermediate_id;
82 /* attio id for Measurement Interval value indications */
83 guint attio_interval_id;
84 gboolean intermediate;
90 gboolean has_interval;
92 uint16_t measurement_ccc_handle;
93 uint16_t intermediate_ccc_handle;
94 uint16_t interval_val_handle;
97 struct characteristic {
98 struct thermometer *t; /* Thermometer where the char belongs */
99 char uuid[MAX_LEN_UUID_STR + 1];
103 struct thermometer_adapter *tadapter;
110 struct thermometer *t;
120 struct tmp_interval_data {
121 struct thermometer *thermometer;
125 static GSList *thermometer_adapters = NULL;
127 static const char * const temp_type[] = {
140 static const char *temptype2str(uint8_t value)
142 if (value > 0 && value < G_N_ELEMENTS(temp_type))
143 return temp_type[value];
145 error("Temperature type %d reserved for future use", value);
149 static void destroy_watcher(gpointer user_data)
151 struct watcher *watcher = user_data;
153 g_free(watcher->path);
154 g_free(watcher->srv);
158 static void remove_watcher(gpointer user_data)
160 struct watcher *watcher = user_data;
162 g_dbus_remove_watch(btd_get_dbus_connection(), watcher->id);
165 static void destroy_thermometer(gpointer user_data)
167 struct thermometer *t = user_data;
170 btd_device_remove_attio_callback(t->dev, t->attioid);
172 if (t->attrib != NULL) {
173 g_attrib_unregister(t->attrib, t->attio_measurement_id);
174 g_attrib_unregister(t->attrib, t->attio_intermediate_id);
175 g_attrib_unregister(t->attrib, t->attio_interval_id);
176 g_attrib_unref(t->attrib);
179 btd_device_unref(t->dev);
180 g_free(t->svc_range);
184 static void destroy_thermometer_adapter(gpointer user_data)
186 struct thermometer_adapter *tadapter = user_data;
188 if (tadapter->devices != NULL)
189 g_slist_free_full(tadapter->devices, destroy_thermometer);
191 if (tadapter->fwatchers != NULL)
192 g_slist_free_full(tadapter->fwatchers, remove_watcher);
197 static int cmp_adapter(gconstpointer a, gconstpointer b)
199 const struct thermometer_adapter *tadapter = a;
200 const struct btd_adapter *adapter = b;
202 if (adapter == tadapter->adapter)
208 static int cmp_device(gconstpointer a, gconstpointer b)
210 const struct thermometer *t = a;
211 const struct btd_device *dev = b;
219 static int cmp_watcher(gconstpointer a, gconstpointer b)
221 const struct watcher *watcher = a;
222 const struct watcher *match = b;
225 ret = g_strcmp0(watcher->srv, match->srv);
229 return g_strcmp0(watcher->path, match->path);
232 static struct thermometer_adapter *
233 find_thermometer_adapter(struct btd_adapter *adapter)
235 GSList *l = g_slist_find_custom(thermometer_adapters, adapter,
243 static void change_property(struct thermometer *t, const char *name,
245 if (g_strcmp0(name, "Intermediate") == 0) {
246 gboolean *intermediate = value;
247 if (t->intermediate == *intermediate)
250 t->intermediate = *intermediate;
251 } else if (g_strcmp0(name, "Interval") == 0) {
252 uint16_t *interval = value;
253 if (t->has_interval && t->interval == *interval)
256 t->has_interval = TRUE;
257 t->interval = *interval;
258 } else if (g_strcmp0(name, "Maximum") == 0) {
259 uint16_t *max = value;
264 } else if (g_strcmp0(name, "Minimum") == 0) {
265 uint16_t *min = value;
271 DBG("%s is not a thermometer property", name);
275 g_dbus_emit_property_changed(btd_get_dbus_connection(),
276 device_get_path(t->dev),
277 THERMOMETER_INTERFACE, name);
280 static void update_watcher(gpointer data, gpointer user_data)
282 struct watcher *w = data;
283 struct measurement *m = user_data;
284 const char *path = device_get_path(m->t->dev);
285 DBusMessageIter iter;
286 DBusMessageIter dict;
289 msg = dbus_message_new_method_call(w->srv, w->path,
290 THERMOMETER_WATCHER_INTERFACE,
291 "MeasurementReceived");
295 dbus_message_iter_init_append(msg, &iter);
297 dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH , &path);
299 dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
300 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
301 DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
302 DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
304 dict_append_entry(&dict, "Exponent", DBUS_TYPE_INT16, &m->exp);
305 dict_append_entry(&dict, "Mantissa", DBUS_TYPE_INT32, &m->mant);
306 dict_append_entry(&dict, "Unit", DBUS_TYPE_STRING, &m->unit);
309 dict_append_entry(&dict, "Time", DBUS_TYPE_UINT64, &m->time);
311 dict_append_entry(&dict, "Type", DBUS_TYPE_STRING, &m->type);
312 dict_append_entry(&dict, "Measurement", DBUS_TYPE_STRING, &m->value);
314 dbus_message_iter_close_container(&iter, &dict);
316 dbus_message_set_no_reply(msg, TRUE);
317 g_dbus_send_message(btd_get_dbus_connection(), msg);
320 static void recv_measurement(struct thermometer *t, struct measurement *m)
326 if (g_strcmp0(m->value, "intermediate") == 0)
327 wlist = t->tadapter->iwatchers;
329 wlist = t->tadapter->fwatchers;
331 g_slist_foreach(wlist, update_watcher, m);
334 static void proc_measurement(struct thermometer *t, const uint8_t *pdu,
335 uint16_t len, gboolean final)
337 struct measurement m;
338 const char *type = NULL;
342 /* skip opcode and handle */
347 DBG("Mandatory flags are not provided");
351 memset(&m, 0, sizeof(m));
355 if (flags & TEMP_UNITS)
356 m.unit = "fahrenheit";
364 DBG("Mandatory temperature measurement value is not provided");
369 m.mant = raw & 0x00FFFFFF;
370 m.exp = ((int32_t) raw) >> 24;
372 if (m.mant & 0x00800000) {
373 /* convert to C2 negative value */
374 m.mant = m.mant - FLOAT_MAX_MANTISSA;
380 if (flags & TEMP_TIME_STAMP) {
385 DBG("Time stamp is not provided");
389 ts.tm_year = get_le16(pdu) - 1900;
390 ts.tm_mon = *(pdu + 2) - 1;
391 ts.tm_mday = *(pdu + 3);
392 ts.tm_hour = *(pdu + 4);
393 ts.tm_min = *(pdu + 5);
394 ts.tm_sec = *(pdu + 6);
398 m.time = (uint64_t) time;
405 if (flags & TEMP_TYPE) {
407 DBG("Temperature type is not provided");
411 type = temptype2str(*pdu);
412 } else if (t->has_type) {
413 type = temptype2str(t->type);
416 m.type = g_strdup(type);
417 m.value = final ? "final" : "intermediate";
419 recv_measurement(t, &m);
424 static void measurement_ind_handler(const uint8_t *pdu, uint16_t len,
427 struct thermometer *t = user_data;
433 DBG("Bad pdu received");
437 proc_measurement(t, pdu, len, TRUE);
439 opdu = g_attrib_get_buffer(t->attrib, &plen);
440 olen = enc_confirmation(opdu, plen);
443 g_attrib_send(t->attrib, 0, opdu, olen, NULL, NULL, NULL);
446 static void intermediate_notify_handler(const uint8_t *pdu, uint16_t len,
449 struct thermometer *t = user_data;
452 DBG("Bad pdu received");
456 proc_measurement(t, pdu, len, FALSE);
459 static void interval_ind_handler(const uint8_t *pdu, uint16_t len,
462 struct thermometer *t = user_data;
469 DBG("Bad pdu received");
473 interval = get_le16(pdu + 3);
474 change_property(t, "Interval", &interval);
476 opdu = g_attrib_get_buffer(t->attrib, &plen);
477 olen = enc_confirmation(opdu, plen);
480 g_attrib_send(t->attrib, 0, opdu, olen, NULL, NULL, NULL);
483 static void valid_range_desc_cb(guint8 status, const guint8 *pdu, guint16 len,
486 struct thermometer *t = user_data;
487 uint8_t value[VALID_RANGE_DESC_SIZE];
492 DBG("Valid Range descriptor read failed: %s",
493 att_ecode2str(status));
497 vlen = dec_read_resp(pdu, len, value, sizeof(value));
499 DBG("Protocol error\n");
504 DBG("Invalid range received");
508 min = get_le16(&value[0]);
509 max = get_le16(&value[2]);
511 if (min == 0 || min > max) {
512 DBG("Invalid range");
516 change_property(t, "Maximum", &max);
517 change_property(t, "Minimum", &min);
520 static void write_ccc_cb(guint8 status, const guint8 *pdu,
521 guint16 len, gpointer user_data)
523 char *msg = user_data;
526 error("%s failed", msg);
531 static void process_thermometer_desc(struct characteristic *ch, uint16_t uuid,
538 if (uuid == GATT_CHARAC_VALID_RANGE_UUID) {
539 if (g_strcmp0(ch->uuid, MEASUREMENT_INTERVAL_UUID) == 0)
540 gatt_read_char(ch->t->attrib, handle,
541 valid_range_desc_cb, ch->t);
545 if (uuid != GATT_CLIENT_CHARAC_CFG_UUID)
548 if (g_strcmp0(ch->uuid, TEMPERATURE_MEASUREMENT_UUID) == 0) {
549 ch->t->measurement_ccc_handle = handle;
551 if (g_slist_length(ch->t->tadapter->fwatchers) == 0) {
553 msg = g_strdup("Disable Temperature Measurement ind");
555 val = GATT_CLIENT_CHARAC_CFG_IND_BIT;
556 msg = g_strdup("Enable Temperature Measurement ind");
558 } else if (g_strcmp0(ch->uuid, INTERMEDIATE_TEMPERATURE_UUID) == 0) {
559 ch->t->intermediate_ccc_handle = handle;
561 if (g_slist_length(ch->t->tadapter->iwatchers) == 0) {
563 msg = g_strdup("Disable Intermediate Temperature noti");
565 val = GATT_CLIENT_CHARAC_CFG_NOTIF_BIT;
566 msg = g_strdup("Enable Intermediate Temperature noti");
568 } else if (g_strcmp0(ch->uuid, MEASUREMENT_INTERVAL_UUID) == 0) {
569 val = GATT_CLIENT_CHARAC_CFG_IND_BIT;
570 msg = g_strdup("Enable Measurement Interval indication");
575 put_le16(val, atval);
576 gatt_write_char(ch->t->attrib, handle, atval, sizeof(atval),
580 static void discover_desc_cb(guint8 status, GSList *descs, gpointer user_data)
582 struct characteristic *ch = user_data;
585 error("Discover all characteristic descriptors failed [%s]: %s",
586 ch->uuid, att_ecode2str(status));
590 for ( ; descs; descs = descs->next) {
591 struct gatt_desc *desc = descs->data;
593 process_thermometer_desc(ch, desc->uuid16, desc->handle);
600 static void discover_desc(struct thermometer *t, struct gatt_char *c,
601 struct gatt_char *c_next)
603 struct characteristic *ch;
606 start = c->value_handle + 1;
608 if (c_next != NULL) {
609 if (start == c_next->handle)
611 end = c_next->handle - 1;
612 } else if (c->value_handle != t->svc_range->end) {
613 end = t->svc_range->end;
618 ch = g_new0(struct characteristic, 1);
620 memcpy(ch->uuid, c->uuid, sizeof(c->uuid));
622 gatt_discover_desc(t->attrib, start, end, NULL, discover_desc_cb, ch);
625 static void read_temp_type_cb(guint8 status, const guint8 *pdu, guint16 len,
628 struct thermometer *t = user_data;
629 uint8_t value[TEMPERATURE_TYPE_SIZE];
633 DBG("Temperature Type value read failed: %s",
634 att_ecode2str(status));
638 vlen = dec_read_resp(pdu, len, value, sizeof(value));
640 DBG("Protocol error.");
645 DBG("Invalid length for Temperature type");
653 static void read_interval_cb(guint8 status, const guint8 *pdu, guint16 len,
656 struct thermometer *t = user_data;
657 uint8_t value[MEASUREMENT_INTERVAL_SIZE];
662 DBG("Measurement Interval value read failed: %s",
663 att_ecode2str(status));
667 vlen = dec_read_resp(pdu, len, value, sizeof(value));
669 DBG("Protocol error\n");
674 DBG("Invalid Interval received");
678 interval = get_le16(&value[0]);
679 change_property(t, "Interval", &interval);
682 static void process_thermometer_char(struct thermometer *t,
683 struct gatt_char *c, struct gatt_char *c_next)
685 if (g_strcmp0(c->uuid, INTERMEDIATE_TEMPERATURE_UUID) == 0) {
686 gboolean intermediate = TRUE;
687 change_property(t, "Intermediate", &intermediate);
689 t->attio_intermediate_id = g_attrib_register(t->attrib,
690 ATT_OP_HANDLE_NOTIFY, c->value_handle,
691 intermediate_notify_handler, t, NULL);
693 discover_desc(t, c, c_next);
694 } else if (g_strcmp0(c->uuid, TEMPERATURE_MEASUREMENT_UUID) == 0) {
696 t->attio_measurement_id = g_attrib_register(t->attrib,
697 ATT_OP_HANDLE_IND, c->value_handle,
698 measurement_ind_handler, t, NULL);
700 discover_desc(t, c, c_next);
701 } else if (g_strcmp0(c->uuid, TEMPERATURE_TYPE_UUID) == 0) {
702 gatt_read_char(t->attrib, c->value_handle,
703 read_temp_type_cb, t);
704 } else if (g_strcmp0(c->uuid, MEASUREMENT_INTERVAL_UUID) == 0) {
705 bool need_desc = false;
707 gatt_read_char(t->attrib, c->value_handle, read_interval_cb, t);
709 if (c->properties & GATT_CHR_PROP_WRITE) {
710 t->interval_val_handle = c->value_handle;
714 if (c->properties & GATT_CHR_PROP_INDICATE) {
715 t->attio_interval_id = g_attrib_register(t->attrib,
716 ATT_OP_HANDLE_IND, c->value_handle,
717 interval_ind_handler, t, NULL);
722 discover_desc(t, c, c_next);
726 static void configure_thermometer_cb(uint8_t status, GSList *characteristics,
729 struct thermometer *t = user_data;
733 error("Discover thermometer characteristics: %s",
734 att_ecode2str(status));
738 for (l = characteristics; l; l = l->next) {
739 struct gatt_char *c = l->data;
740 struct gatt_char *c_next = (l->next ? l->next->data : NULL);
742 process_thermometer_char(t, c, c_next);
746 static void write_interval_cb(guint8 status, const guint8 *pdu, guint16 len,
749 struct tmp_interval_data *data = user_data;
752 error("Interval Write Request failed %s",
753 att_ecode2str(status));
757 if (!dec_write_resp(pdu, len)) {
758 error("Interval Write Request: protocol error");
762 change_property(data->thermometer, "Interval", &data->interval);
768 static void enable_final_measurement(gpointer data, gpointer user_data)
770 struct thermometer *t = data;
771 uint16_t handle = t->measurement_ccc_handle;
775 if (t->attrib == NULL || !handle)
778 put_le16(GATT_CLIENT_CHARAC_CFG_IND_BIT, value);
779 msg = g_strdup("Enable Temperature Measurement indications");
781 gatt_write_char(t->attrib, handle, value, sizeof(value),
785 static void enable_intermediate_measurement(gpointer data, gpointer user_data)
787 struct thermometer *t = data;
788 uint16_t handle = t->intermediate_ccc_handle;
792 if (t->attrib == NULL || !handle)
795 put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value);
796 msg = g_strdup("Enable Intermediate Temperature notifications");
798 gatt_write_char(t->attrib, handle, value, sizeof(value),
802 static void disable_final_measurement(gpointer data, gpointer user_data)
804 struct thermometer *t = data;
805 uint16_t handle = t->measurement_ccc_handle;
809 if (t->attrib == NULL || !handle)
812 put_le16(0x0000, value);
813 msg = g_strdup("Disable Temperature Measurement indications");
815 gatt_write_char(t->attrib, handle, value, sizeof(value),
819 static void disable_intermediate_measurement(gpointer data, gpointer user_data)
821 struct thermometer *t = data;
822 uint16_t handle = t->intermediate_ccc_handle;
826 if (t->attrib == NULL || !handle)
829 put_le16(0x0000, value);
830 msg = g_strdup("Disable Intermediate Temperature notifications");
832 gatt_write_char(t->attrib, handle, value, sizeof(value),
836 static void remove_int_watcher(struct thermometer_adapter *tadapter,
839 if (!g_slist_find(tadapter->iwatchers, w))
842 tadapter->iwatchers = g_slist_remove(tadapter->iwatchers, w);
844 if (g_slist_length(tadapter->iwatchers) == 0)
845 g_slist_foreach(tadapter->devices,
846 disable_intermediate_measurement, 0);
849 static void watcher_exit(DBusConnection *conn, void *user_data)
851 struct watcher *watcher = user_data;
852 struct thermometer_adapter *tadapter = watcher->tadapter;
854 DBG("Thermometer watcher %s disconnected", watcher->path);
856 remove_int_watcher(tadapter, watcher);
858 tadapter->fwatchers = g_slist_remove(tadapter->fwatchers, watcher);
859 g_dbus_remove_watch(btd_get_dbus_connection(), watcher->id);
861 if (g_slist_length(tadapter->fwatchers) == 0)
862 g_slist_foreach(tadapter->devices,
863 disable_final_measurement, 0);
866 static struct watcher *find_watcher(GSList *list, const char *sender,
869 struct watcher *match;
872 match = g_new0(struct watcher, 1);
873 match->srv = g_strdup(sender);
874 match->path = g_strdup(path);
876 l = g_slist_find_custom(list, match, cmp_watcher);
877 destroy_watcher(match);
885 static DBusMessage *register_watcher(DBusConnection *conn, DBusMessage *msg,
888 const char *sender = dbus_message_get_sender(msg);
889 struct thermometer_adapter *tadapter = data;
890 struct watcher *watcher;
893 if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
895 return btd_error_invalid_args(msg);
897 watcher = find_watcher(tadapter->fwatchers, sender, path);
899 return btd_error_already_exists(msg);
901 DBG("Thermometer watcher %s registered", path);
903 watcher = g_new0(struct watcher, 1);
904 watcher->srv = g_strdup(sender);
905 watcher->path = g_strdup(path);
906 watcher->tadapter = tadapter;
907 watcher->id = g_dbus_add_disconnect_watch(conn, sender, watcher_exit,
908 watcher, destroy_watcher);
910 if (g_slist_length(tadapter->fwatchers) == 0)
911 g_slist_foreach(tadapter->devices, enable_final_measurement, 0);
913 tadapter->fwatchers = g_slist_prepend(tadapter->fwatchers, watcher);
915 return dbus_message_new_method_return(msg);
918 static DBusMessage *unregister_watcher(DBusConnection *conn, DBusMessage *msg,
921 const char *sender = dbus_message_get_sender(msg);
922 struct thermometer_adapter *tadapter = data;
923 struct watcher *watcher;
926 if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
928 return btd_error_invalid_args(msg);
930 watcher = find_watcher(tadapter->fwatchers, sender, path);
932 return btd_error_does_not_exist(msg);
934 DBG("Thermometer watcher %s unregistered", path);
936 remove_int_watcher(tadapter, watcher);
938 tadapter->fwatchers = g_slist_remove(tadapter->fwatchers, watcher);
939 g_dbus_remove_watch(btd_get_dbus_connection(), watcher->id);
941 if (g_slist_length(tadapter->fwatchers) == 0)
942 g_slist_foreach(tadapter->devices,
943 disable_final_measurement, 0);
945 return dbus_message_new_method_return(msg);
948 static DBusMessage *enable_intermediate(DBusConnection *conn, DBusMessage *msg,
951 const char *sender = dbus_message_get_sender(msg);
952 struct thermometer_adapter *ta = data;
953 struct watcher *watcher;
956 if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
958 return btd_error_invalid_args(msg);
960 watcher = find_watcher(ta->fwatchers, sender, path);
962 return btd_error_does_not_exist(msg);
964 if (find_watcher(ta->iwatchers, sender, path))
965 return btd_error_already_exists(msg);
967 DBG("Intermediate measurement watcher %s registered", path);
969 if (g_slist_length(ta->iwatchers) == 0)
970 g_slist_foreach(ta->devices,
971 enable_intermediate_measurement, 0);
973 ta->iwatchers = g_slist_prepend(ta->iwatchers, watcher);
975 return dbus_message_new_method_return(msg);
978 static DBusMessage *disable_intermediate(DBusConnection *conn, DBusMessage *msg,
981 const char *sender = dbus_message_get_sender(msg);
982 struct thermometer_adapter *ta = data;
983 struct watcher *watcher;
986 if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
988 return btd_error_invalid_args(msg);
990 watcher = find_watcher(ta->iwatchers, sender, path);
992 return btd_error_does_not_exist(msg);
994 DBG("Intermediate measurement %s unregistered", path);
996 remove_int_watcher(ta, watcher);
998 return dbus_message_new_method_return(msg);
1001 static gboolean property_get_intermediate(const GDBusPropertyTable *property,
1002 DBusMessageIter *iter, void *data)
1004 struct thermometer *t = data;
1007 val = !!t->intermediate;
1009 dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &val);
1014 static gboolean property_get_interval(const GDBusPropertyTable *property,
1015 DBusMessageIter *iter, void *data)
1017 struct thermometer *t = data;
1019 if (!t->has_interval)
1022 dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &t->interval);
1027 static void property_set_interval(const GDBusPropertyTable *property,
1028 DBusMessageIter *iter,
1029 GDBusPendingPropertySet id, void *data)
1031 struct thermometer *t = data;
1032 struct tmp_interval_data *interval_data;
1036 if (t->interval_val_handle == 0) {
1037 g_dbus_pending_property_error(id,
1038 ERROR_INTERFACE ".NotSupported",
1039 "Operation is not supported");
1043 if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT16) {
1044 g_dbus_pending_property_error(id,
1045 ERROR_INTERFACE ".InvalidArguments",
1046 "Invalid arguments in method call");
1050 dbus_message_iter_get_basic(iter, &val);
1052 if (val < t->min || val > t->max) {
1053 g_dbus_pending_property_error(id,
1054 ERROR_INTERFACE ".InvalidArguments",
1055 "Invalid arguments in method call");
1059 put_le16(val, &atval[0]);
1061 interval_data = g_new0(struct tmp_interval_data, 1);
1062 interval_data->thermometer = t;
1063 interval_data->interval = val;
1064 gatt_write_char(t->attrib, t->interval_val_handle, atval, sizeof(atval),
1065 write_interval_cb, interval_data);
1067 g_dbus_pending_property_success(id);
1070 static gboolean property_exists_interval(const GDBusPropertyTable *property,
1073 struct thermometer *t = data;
1075 return t->has_interval;
1078 static gboolean property_get_maximum(const GDBusPropertyTable *property,
1079 DBusMessageIter *iter, void *data)
1081 struct thermometer *t = data;
1083 if (!t->has_interval)
1086 dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &t->max);
1091 static gboolean property_get_minimum(const GDBusPropertyTable *property,
1092 DBusMessageIter *iter, void *data)
1094 struct thermometer *t = data;
1096 if (!t->has_interval)
1099 dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &t->min);
1104 static const GDBusPropertyTable thermometer_properties[] = {
1105 { "Intermediate", "b", property_get_intermediate },
1106 { "Interval", "q", property_get_interval, property_set_interval,
1107 property_exists_interval },
1108 { "Maximum", "q", property_get_maximum, NULL,
1109 property_exists_interval },
1110 { "Minimum", "q", property_get_minimum, NULL,
1111 property_exists_interval },
1115 static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
1117 struct thermometer *t = user_data;
1119 t->attrib = g_attrib_ref(attrib);
1121 gatt_discover_char(t->attrib, t->svc_range->start, t->svc_range->end,
1122 NULL, configure_thermometer_cb, t);
1125 static void attio_disconnected_cb(gpointer user_data)
1127 struct thermometer *t = user_data;
1129 DBG("GATT Disconnected");
1131 if (t->attio_measurement_id > 0) {
1132 g_attrib_unregister(t->attrib, t->attio_measurement_id);
1133 t->attio_measurement_id = 0;
1136 if (t->attio_intermediate_id > 0) {
1137 g_attrib_unregister(t->attrib, t->attio_intermediate_id);
1138 t->attio_intermediate_id = 0;
1141 if (t->attio_interval_id > 0) {
1142 g_attrib_unregister(t->attrib, t->attio_interval_id);
1143 t->attio_interval_id = 0;
1146 g_attrib_unref(t->attrib);
1150 static int thermometer_register(struct btd_device *device,
1151 struct gatt_primary *tattr)
1153 const char *path = device_get_path(device);
1154 struct thermometer *t;
1155 struct btd_adapter *adapter;
1156 struct thermometer_adapter *tadapter;
1158 adapter = device_get_adapter(device);
1160 tadapter = find_thermometer_adapter(adapter);
1162 if (tadapter == NULL)
1165 t = g_new0(struct thermometer, 1);
1166 t->dev = btd_device_ref(device);
1167 t->tadapter = tadapter;
1168 t->svc_range = g_new0(struct att_range, 1);
1169 t->svc_range->start = tattr->range.start;
1170 t->svc_range->end = tattr->range.end;
1172 tadapter->devices = g_slist_prepend(tadapter->devices, t);
1174 if (!g_dbus_register_interface(btd_get_dbus_connection(),
1175 path, THERMOMETER_INTERFACE,
1176 NULL, NULL, thermometer_properties,
1177 t, destroy_thermometer)) {
1178 error("D-Bus failed to register %s interface",
1179 THERMOMETER_INTERFACE);
1180 destroy_thermometer(t);
1184 t->attioid = btd_device_add_attio_callback(device, attio_connected_cb,
1185 attio_disconnected_cb, t);
1189 static void thermometer_unregister(struct btd_device *device)
1191 struct thermometer *t;
1192 struct btd_adapter *adapter;
1193 struct thermometer_adapter *tadapter;
1196 adapter = device_get_adapter(device);
1198 tadapter = find_thermometer_adapter(adapter);
1200 if (tadapter == NULL)
1203 l = g_slist_find_custom(tadapter->devices, device, cmp_device);
1209 tadapter->devices = g_slist_remove(tadapter->devices, t);
1211 g_dbus_unregister_interface(btd_get_dbus_connection(),
1212 device_get_path(t->dev), THERMOMETER_INTERFACE);
1215 static const GDBusMethodTable thermometer_manager_methods[] = {
1216 { GDBUS_METHOD("RegisterWatcher",
1217 GDBUS_ARGS({ "agent", "o" }), NULL,
1218 register_watcher) },
1219 { GDBUS_METHOD("UnregisterWatcher",
1220 GDBUS_ARGS({ "agent", "o" }), NULL,
1221 unregister_watcher) },
1222 { GDBUS_METHOD("EnableIntermediateMeasurement",
1223 GDBUS_ARGS({ "agent", "o" }), NULL,
1224 enable_intermediate) },
1225 { GDBUS_METHOD("DisableIntermediateMeasurement",
1226 GDBUS_ARGS({ "agent", "o" }), NULL,
1227 disable_intermediate) },
1231 static int thermometer_adapter_register(struct btd_adapter *adapter)
1233 struct thermometer_adapter *tadapter;
1235 tadapter = g_new0(struct thermometer_adapter, 1);
1236 tadapter->adapter = adapter;
1238 if (!g_dbus_register_interface(btd_get_dbus_connection(),
1239 adapter_get_path(adapter),
1240 THERMOMETER_MANAGER_INTERFACE,
1241 thermometer_manager_methods,
1242 NULL, NULL, tadapter,
1243 destroy_thermometer_adapter)) {
1244 error("D-Bus failed to register %s interface",
1245 THERMOMETER_MANAGER_INTERFACE);
1246 destroy_thermometer_adapter(tadapter);
1250 thermometer_adapters = g_slist_prepend(thermometer_adapters, tadapter);
1255 static void thermometer_adapter_unregister(struct btd_adapter *adapter)
1257 struct thermometer_adapter *tadapter;
1259 tadapter = find_thermometer_adapter(adapter);
1260 if (tadapter == NULL)
1263 thermometer_adapters = g_slist_remove(thermometer_adapters, tadapter);
1265 g_dbus_unregister_interface(btd_get_dbus_connection(),
1266 adapter_get_path(tadapter->adapter),
1267 THERMOMETER_MANAGER_INTERFACE);
1270 static int thermometer_device_probe(struct btd_service *service)
1272 struct btd_device *device = btd_service_get_device(service);
1273 struct gatt_primary *tattr;
1275 tattr = btd_device_get_primary(device, HEALTH_THERMOMETER_UUID);
1279 return thermometer_register(device, tattr);
1282 static void thermometer_device_remove(struct btd_service *service)
1284 struct btd_device *device = btd_service_get_device(service);
1286 thermometer_unregister(device);
1289 static int thermometer_adapter_probe(struct btd_profile *p,
1290 struct btd_adapter *adapter)
1292 return thermometer_adapter_register(adapter);
1295 static void thermometer_adapter_remove(struct btd_profile *p,
1296 struct btd_adapter *adapter)
1298 thermometer_adapter_unregister(adapter);
1301 static struct btd_profile thermometer_profile = {
1302 .name = "Health Thermometer GATT driver",
1303 .remote_uuid = HEALTH_THERMOMETER_UUID,
1304 .device_probe = thermometer_device_probe,
1305 .device_remove = thermometer_device_remove,
1306 .adapter_probe = thermometer_adapter_probe,
1307 .adapter_remove = thermometer_adapter_remove
1310 static int thermometer_init(void)
1312 return btd_profile_register(&thermometer_profile);
1315 static void thermometer_exit(void)
1317 btd_profile_unregister(&thermometer_profile);
1320 BLUETOOTH_PLUGIN_DEFINE(thermometer, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
1321 thermometer_init, thermometer_exit)