tizen 2.3.1 release
[framework/connectivity/bluez.git] / profiles / thermometer / thermometer.c
1 /*
2  *
3  *  BlueZ - Bluetooth protocol stack for Linux
4  *
5  *  Copyright (C) 2011 GSyC/LibreSoft, Universidad Rey Juan Carlos.
6  *
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.
11  *
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.
16  *
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
20  *
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <stdbool.h>
28 #include <errno.h>
29
30 #include "lib/bluetooth.h"
31 #include "lib/sdp.h"
32 #include "lib/uuid.h"
33
34 #include "gdbus/gdbus.h"
35
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"
44 #include "src/log.h"
45 #include "attrib/gattrib.h"
46 #include "src/attio.h"
47 #include "attrib/att.h"
48 #include "attrib/gatt.h"
49
50 #define THERMOMETER_INTERFACE           "org.bluez.Thermometer1"
51 #define THERMOMETER_MANAGER_INTERFACE   "org.bluez.ThermometerManager1"
52 #define THERMOMETER_WATCHER_INTERFACE   "org.bluez.ThermometerWatcher1"
53
54 /* Temperature measurement flag fields */
55 #define TEMP_UNITS              0x01
56 #define TEMP_TIME_STAMP         0x02
57 #define TEMP_TYPE               0x04
58
59 #define FLOAT_MAX_MANTISSA      16777216 /* 2^24 */
60
61 #define VALID_RANGE_DESC_SIZE   4
62 #define TEMPERATURE_TYPE_SIZE   1
63 #define MEASUREMENT_INTERVAL_SIZE       2
64
65 struct thermometer_adapter {
66         struct btd_adapter      *adapter;
67         GSList                  *devices;
68         GSList                  *fwatchers;     /* Final measurements */
69         GSList                  *iwatchers;     /* Intermediate measurements */
70 };
71
72 struct thermometer {
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;
85         uint8_t                         type;
86         uint16_t                        interval;
87         uint16_t                        max;
88         uint16_t                        min;
89         gboolean                        has_type;
90         gboolean                        has_interval;
91
92         uint16_t                        measurement_ccc_handle;
93         uint16_t                        intermediate_ccc_handle;
94         uint16_t                        interval_val_handle;
95 };
96
97 struct characteristic {
98         struct thermometer      *t;     /* Thermometer where the char belongs */
99         char                    uuid[MAX_LEN_UUID_STR + 1];
100 };
101
102 struct watcher {
103         struct thermometer_adapter      *tadapter;
104         guint                           id;
105         char                            *srv;
106         char                            *path;
107 };
108
109 struct measurement {
110         struct thermometer      *t;
111         int16_t                 exp;
112         int32_t                 mant;
113         uint64_t                time;
114         gboolean                suptime;
115         char                    *unit;
116         char                    *type;
117         char                    *value;
118 };
119
120 struct tmp_interval_data {
121         struct thermometer      *thermometer;
122         uint16_t                interval;
123 };
124
125 static GSList *thermometer_adapters = NULL;
126
127 static const char * const temp_type[] = {
128         "<reserved>",
129         "armpit",
130         "body",
131         "ear",
132         "finger",
133         "intestines",
134         "mouth",
135         "rectum",
136         "toe",
137         "tympanum"
138 };
139
140 static const char *temptype2str(uint8_t value)
141 {
142          if (value > 0 && value < G_N_ELEMENTS(temp_type))
143                 return temp_type[value];
144
145         error("Temperature type %d reserved for future use", value);
146         return NULL;
147 }
148
149 static void destroy_watcher(gpointer user_data)
150 {
151         struct watcher *watcher = user_data;
152
153         g_free(watcher->path);
154         g_free(watcher->srv);
155         g_free(watcher);
156 }
157
158 static void remove_watcher(gpointer user_data)
159 {
160         struct watcher *watcher = user_data;
161
162         g_dbus_remove_watch(btd_get_dbus_connection(), watcher->id);
163 }
164
165 static void destroy_thermometer(gpointer user_data)
166 {
167         struct thermometer *t = user_data;
168
169         if (t->attioid > 0)
170                 btd_device_remove_attio_callback(t->dev, t->attioid);
171
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);
177         }
178
179         btd_device_unref(t->dev);
180         g_free(t->svc_range);
181         g_free(t);
182 }
183
184 static void destroy_thermometer_adapter(gpointer user_data)
185 {
186         struct thermometer_adapter *tadapter = user_data;
187
188         if (tadapter->devices != NULL)
189                 g_slist_free_full(tadapter->devices, destroy_thermometer);
190
191         if (tadapter->fwatchers != NULL)
192                 g_slist_free_full(tadapter->fwatchers, remove_watcher);
193
194         g_free(tadapter);
195 }
196
197 static int cmp_adapter(gconstpointer a, gconstpointer b)
198 {
199         const struct thermometer_adapter *tadapter = a;
200         const struct btd_adapter *adapter = b;
201
202         if (adapter == tadapter->adapter)
203                 return 0;
204
205         return -1;
206 }
207
208 static int cmp_device(gconstpointer a, gconstpointer b)
209 {
210         const struct thermometer *t = a;
211         const struct btd_device *dev = b;
212
213         if (dev == t->dev)
214                 return 0;
215
216         return -1;
217 }
218
219 static int cmp_watcher(gconstpointer a, gconstpointer b)
220 {
221         const struct watcher *watcher = a;
222         const struct watcher *match = b;
223         int ret;
224
225         ret = g_strcmp0(watcher->srv, match->srv);
226         if (ret != 0)
227                 return ret;
228
229         return g_strcmp0(watcher->path, match->path);
230 }
231
232 static struct thermometer_adapter *
233 find_thermometer_adapter(struct btd_adapter *adapter)
234 {
235         GSList *l = g_slist_find_custom(thermometer_adapters, adapter,
236                                                                 cmp_adapter);
237         if (!l)
238                 return NULL;
239
240         return l->data;
241 }
242
243 static void change_property(struct thermometer *t, const char *name,
244                                                         gpointer value) {
245         if (g_strcmp0(name, "Intermediate") == 0) {
246                 gboolean *intermediate = value;
247                 if (t->intermediate == *intermediate)
248                         return;
249
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)
254                         return;
255
256                 t->has_interval = TRUE;
257                 t->interval = *interval;
258         } else if (g_strcmp0(name, "Maximum") == 0) {
259                 uint16_t *max = value;
260                 if (t->max == *max)
261                         return;
262
263                 t->max = *max;
264         } else if (g_strcmp0(name, "Minimum") == 0) {
265                 uint16_t *min = value;
266                 if (t->min == *min)
267                         return;
268
269                 t->min = *min;
270         } else {
271                 DBG("%s is not a thermometer property", name);
272                 return;
273         }
274
275         g_dbus_emit_property_changed(btd_get_dbus_connection(),
276                                                 device_get_path(t->dev),
277                                                 THERMOMETER_INTERFACE, name);
278 }
279
280 static void update_watcher(gpointer data, gpointer user_data)
281 {
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;
287         DBusMessage *msg;
288
289         msg = dbus_message_new_method_call(w->srv, w->path,
290                                 THERMOMETER_WATCHER_INTERFACE,
291                                 "MeasurementReceived");
292         if (msg == NULL)
293                 return;
294
295         dbus_message_iter_init_append(msg, &iter);
296
297         dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH , &path);
298
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);
303
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);
307
308         if (m->suptime)
309                 dict_append_entry(&dict, "Time", DBUS_TYPE_UINT64, &m->time);
310
311         dict_append_entry(&dict, "Type", DBUS_TYPE_STRING, &m->type);
312         dict_append_entry(&dict, "Measurement", DBUS_TYPE_STRING, &m->value);
313
314         dbus_message_iter_close_container(&iter, &dict);
315
316         dbus_message_set_no_reply(msg, TRUE);
317         g_dbus_send_message(btd_get_dbus_connection(), msg);
318 }
319
320 static void recv_measurement(struct thermometer *t, struct measurement *m)
321 {
322         GSList *wlist;
323
324         m->t = t;
325
326         if (g_strcmp0(m->value, "intermediate") == 0)
327                 wlist = t->tadapter->iwatchers;
328         else
329                 wlist = t->tadapter->fwatchers;
330
331         g_slist_foreach(wlist, update_watcher, m);
332 }
333
334 static void proc_measurement(struct thermometer *t, const uint8_t *pdu,
335                                                 uint16_t len, gboolean final)
336 {
337         struct measurement m;
338         const char *type = NULL;
339         uint8_t flags;
340         uint32_t raw;
341
342         /* skip opcode and handle */
343         pdu += 3;
344         len -= 3;
345
346         if (len < 1) {
347                 DBG("Mandatory flags are not provided");
348                 return;
349         }
350
351         memset(&m, 0, sizeof(m));
352
353         flags = *pdu;
354
355         if (flags & TEMP_UNITS)
356                 m.unit = "fahrenheit";
357         else
358                 m.unit = "celsius";
359
360         pdu++;
361         len--;
362
363         if (len < 4) {
364                 DBG("Mandatory temperature measurement value is not provided");
365                 return;
366         }
367
368         raw = get_le32(pdu);
369         m.mant = raw & 0x00FFFFFF;
370         m.exp = ((int32_t) raw) >> 24;
371
372         if (m.mant & 0x00800000) {
373                 /* convert to C2 negative value */
374                 m.mant = m.mant - FLOAT_MAX_MANTISSA;
375         }
376
377         pdu += 4;
378         len -= 4;
379
380         if (flags & TEMP_TIME_STAMP) {
381                 struct tm ts;
382                 time_t time;
383
384                 if (len < 7) {
385                         DBG("Time stamp is not provided");
386                         return;
387                 }
388
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);
395                 ts.tm_isdst = -1;
396
397                 time = mktime(&ts);
398                 m.time = (uint64_t) time;
399                 m.suptime = TRUE;
400
401                 pdu += 7;
402                 len -= 7;
403         }
404
405         if (flags & TEMP_TYPE) {
406                 if (len < 1) {
407                         DBG("Temperature type is not provided");
408                         return;
409                 }
410
411                 type = temptype2str(*pdu);
412         } else if (t->has_type) {
413                 type = temptype2str(t->type);
414         }
415
416         m.type = g_strdup(type);
417         m.value = final ? "final" : "intermediate";
418
419         recv_measurement(t, &m);
420         g_free(m.type);
421 }
422
423
424 static void measurement_ind_handler(const uint8_t *pdu, uint16_t len,
425                                                         gpointer user_data)
426 {
427         struct thermometer *t = user_data;
428         uint8_t *opdu;
429         uint16_t olen;
430         size_t plen;
431
432         if (len < 3) {
433                 DBG("Bad pdu received");
434                 return;
435         }
436
437         proc_measurement(t, pdu, len, TRUE);
438
439         opdu = g_attrib_get_buffer(t->attrib, &plen);
440         olen = enc_confirmation(opdu, plen);
441
442         if (olen > 0)
443                 g_attrib_send(t->attrib, 0, opdu, olen, NULL, NULL, NULL);
444 }
445
446 static void intermediate_notify_handler(const uint8_t *pdu, uint16_t len,
447                                                         gpointer user_data)
448 {
449         struct thermometer *t = user_data;
450
451         if (len < 3) {
452                 DBG("Bad pdu received");
453                 return;
454         }
455
456         proc_measurement(t, pdu, len, FALSE);
457 }
458
459 static void interval_ind_handler(const uint8_t *pdu, uint16_t len,
460                                                         gpointer user_data)
461 {
462         struct thermometer *t = user_data;
463         uint16_t interval;
464         uint8_t *opdu;
465         uint16_t olen;
466         size_t plen;
467
468         if (len < 5) {
469                 DBG("Bad pdu received");
470                 return;
471         }
472
473         interval = get_le16(pdu + 3);
474         change_property(t, "Interval", &interval);
475
476         opdu = g_attrib_get_buffer(t->attrib, &plen);
477         olen = enc_confirmation(opdu, plen);
478
479         if (olen > 0)
480                 g_attrib_send(t->attrib, 0, opdu, olen, NULL, NULL, NULL);
481 }
482
483 static void valid_range_desc_cb(guint8 status, const guint8 *pdu, guint16 len,
484                                                         gpointer user_data)
485 {
486         struct thermometer *t = user_data;
487         uint8_t value[VALID_RANGE_DESC_SIZE];
488         uint16_t max, min;
489         ssize_t vlen;
490
491         if (status != 0) {
492                 DBG("Valid Range descriptor read failed: %s",
493                                                         att_ecode2str(status));
494                 return;
495         }
496
497         vlen = dec_read_resp(pdu, len, value, sizeof(value));
498         if (vlen < 0) {
499                 DBG("Protocol error\n");
500                 return;
501         }
502
503         if (vlen < 4) {
504                 DBG("Invalid range received");
505                 return;
506         }
507
508         min = get_le16(&value[0]);
509         max = get_le16(&value[2]);
510
511         if (min == 0 || min > max) {
512                 DBG("Invalid range");
513                 return;
514         }
515
516         change_property(t, "Maximum", &max);
517         change_property(t, "Minimum", &min);
518 }
519
520 static void write_ccc_cb(guint8 status, const guint8 *pdu,
521                                                 guint16 len, gpointer user_data)
522 {
523         char *msg = user_data;
524
525         if (status != 0)
526                 error("%s failed", msg);
527
528         g_free(msg);
529 }
530
531 static void process_thermometer_desc(struct characteristic *ch, uint16_t uuid,
532                                                                 uint16_t handle)
533 {
534         uint8_t atval[2];
535         uint16_t val;
536         char *msg;
537
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);
542                 return;
543         }
544
545         if (uuid != GATT_CLIENT_CHARAC_CFG_UUID)
546                 return;
547
548         if (g_strcmp0(ch->uuid, TEMPERATURE_MEASUREMENT_UUID) == 0) {
549                 ch->t->measurement_ccc_handle = handle;
550
551                 if (g_slist_length(ch->t->tadapter->fwatchers) == 0) {
552                         val = 0x0000;
553                         msg = g_strdup("Disable Temperature Measurement ind");
554                 } else {
555                         val = GATT_CLIENT_CHARAC_CFG_IND_BIT;
556                         msg = g_strdup("Enable Temperature Measurement ind");
557                 }
558         } else if (g_strcmp0(ch->uuid, INTERMEDIATE_TEMPERATURE_UUID) == 0) {
559                 ch->t->intermediate_ccc_handle = handle;
560
561                 if (g_slist_length(ch->t->tadapter->iwatchers) == 0) {
562                         val = 0x0000;
563                         msg = g_strdup("Disable Intermediate Temperature noti");
564                 } else {
565                         val = GATT_CLIENT_CHARAC_CFG_NOTIF_BIT;
566                         msg = g_strdup("Enable Intermediate Temperature noti");
567                 }
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");
571         } else {
572                 return;
573         }
574
575         put_le16(val, atval);
576         gatt_write_char(ch->t->attrib, handle, atval, sizeof(atval),
577                                                         write_ccc_cb, msg);
578 }
579
580 static void discover_desc_cb(guint8 status, GSList *descs, gpointer user_data)
581 {
582         struct characteristic *ch = user_data;
583
584         if (status != 0) {
585                 error("Discover all characteristic descriptors failed [%s]: %s",
586                                         ch->uuid, att_ecode2str(status));
587                 goto done;
588         }
589
590         for ( ; descs; descs = descs->next) {
591                 struct gatt_desc *desc = descs->data;
592
593                 process_thermometer_desc(ch, desc->uuid16, desc->handle);
594         }
595
596 done:
597         g_free(ch);
598 }
599
600 static void discover_desc(struct thermometer *t, struct gatt_char *c,
601                                                 struct gatt_char *c_next)
602 {
603         struct characteristic *ch;
604         uint16_t start, end;
605
606         start = c->value_handle + 1;
607
608         if (c_next != NULL) {
609                 if (start == c_next->handle)
610                         return;
611                 end = c_next->handle - 1;
612         } else if (c->value_handle != t->svc_range->end) {
613                 end = t->svc_range->end;
614         } else {
615                 return;
616         }
617
618         ch = g_new0(struct characteristic, 1);
619         ch->t = t;
620         memcpy(ch->uuid, c->uuid, sizeof(c->uuid));
621
622         gatt_discover_desc(t->attrib, start, end, NULL, discover_desc_cb, ch);
623 }
624
625 static void read_temp_type_cb(guint8 status, const guint8 *pdu, guint16 len,
626                                                         gpointer user_data)
627 {
628         struct thermometer *t = user_data;
629         uint8_t value[TEMPERATURE_TYPE_SIZE];
630         ssize_t vlen;
631
632         if (status != 0) {
633                 DBG("Temperature Type value read failed: %s",
634                                                         att_ecode2str(status));
635                 return;
636         }
637
638         vlen = dec_read_resp(pdu, len, value, sizeof(value));
639         if (vlen < 0) {
640                 DBG("Protocol error.");
641                 return;
642         }
643
644         if (vlen != 1) {
645                 DBG("Invalid length for Temperature type");
646                 return;
647         }
648
649         t->has_type = TRUE;
650         t->type = value[0];
651 }
652
653 static void read_interval_cb(guint8 status, const guint8 *pdu, guint16 len,
654                                                         gpointer user_data)
655 {
656         struct thermometer *t = user_data;
657         uint8_t value[MEASUREMENT_INTERVAL_SIZE];
658         uint16_t interval;
659         ssize_t vlen;
660
661         if (status != 0) {
662                 DBG("Measurement Interval value read failed: %s",
663                                                         att_ecode2str(status));
664                 return;
665         }
666
667         vlen = dec_read_resp(pdu, len, value, sizeof(value));
668         if (vlen < 0) {
669                 DBG("Protocol error\n");
670                 return;
671         }
672
673         if (vlen < 2) {
674                 DBG("Invalid Interval received");
675                 return;
676         }
677
678         interval = get_le16(&value[0]);
679         change_property(t, "Interval", &interval);
680 }
681
682 static void process_thermometer_char(struct thermometer *t,
683                                 struct gatt_char *c, struct gatt_char *c_next)
684 {
685         if (g_strcmp0(c->uuid, INTERMEDIATE_TEMPERATURE_UUID) == 0) {
686                 gboolean intermediate = TRUE;
687                 change_property(t, "Intermediate", &intermediate);
688
689                 t->attio_intermediate_id = g_attrib_register(t->attrib,
690                                         ATT_OP_HANDLE_NOTIFY, c->value_handle,
691                                         intermediate_notify_handler, t, NULL);
692
693                 discover_desc(t, c, c_next);
694         } else if (g_strcmp0(c->uuid, TEMPERATURE_MEASUREMENT_UUID) == 0) {
695
696                 t->attio_measurement_id = g_attrib_register(t->attrib,
697                                         ATT_OP_HANDLE_IND, c->value_handle,
698                                         measurement_ind_handler, t, NULL);
699
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;
706
707                 gatt_read_char(t->attrib, c->value_handle, read_interval_cb, t);
708
709                 if (c->properties & GATT_CHR_PROP_WRITE) {
710                         t->interval_val_handle = c->value_handle;
711                         need_desc = true;
712                 }
713
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);
718                         need_desc = true;
719                 }
720
721                 if (need_desc)
722                         discover_desc(t, c, c_next);
723         }
724 }
725
726 static void configure_thermometer_cb(uint8_t status, GSList *characteristics,
727                                                                 void *user_data)
728 {
729         struct thermometer *t = user_data;
730         GSList *l;
731
732         if (status != 0) {
733                 error("Discover thermometer characteristics: %s",
734                                                         att_ecode2str(status));
735                 return;
736         }
737
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);
741
742                 process_thermometer_char(t, c, c_next);
743         }
744 }
745
746 static void write_interval_cb(guint8 status, const guint8 *pdu, guint16 len,
747                                                         gpointer user_data)
748 {
749         struct tmp_interval_data *data = user_data;
750
751         if (status != 0) {
752                 error("Interval Write Request failed %s",
753                                                         att_ecode2str(status));
754                 goto done;
755         }
756
757         if (!dec_write_resp(pdu, len)) {
758                 error("Interval Write Request: protocol error");
759                 goto done;
760         }
761
762         change_property(data->thermometer, "Interval", &data->interval);
763
764 done:
765         g_free(user_data);
766 }
767
768 static void enable_final_measurement(gpointer data, gpointer user_data)
769 {
770         struct thermometer *t = data;
771         uint16_t handle = t->measurement_ccc_handle;
772         uint8_t value[2];
773         char *msg;
774
775         if (t->attrib == NULL || !handle)
776                 return;
777
778         put_le16(GATT_CLIENT_CHARAC_CFG_IND_BIT, value);
779         msg = g_strdup("Enable Temperature Measurement indications");
780
781         gatt_write_char(t->attrib, handle, value, sizeof(value),
782                                                         write_ccc_cb, msg);
783 }
784
785 static void enable_intermediate_measurement(gpointer data, gpointer user_data)
786 {
787         struct thermometer *t = data;
788         uint16_t handle = t->intermediate_ccc_handle;
789         uint8_t value[2];
790         char *msg;
791
792         if (t->attrib == NULL || !handle)
793                 return;
794
795         put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value);
796         msg = g_strdup("Enable Intermediate Temperature notifications");
797
798         gatt_write_char(t->attrib, handle, value, sizeof(value),
799                                                         write_ccc_cb, msg);
800 }
801
802 static void disable_final_measurement(gpointer data, gpointer user_data)
803 {
804         struct thermometer *t = data;
805         uint16_t handle = t->measurement_ccc_handle;
806         uint8_t value[2];
807         char *msg;
808
809         if (t->attrib == NULL || !handle)
810                 return;
811
812         put_le16(0x0000, value);
813         msg = g_strdup("Disable Temperature Measurement indications");
814
815         gatt_write_char(t->attrib, handle, value, sizeof(value),
816                                                         write_ccc_cb, msg);
817 }
818
819 static void disable_intermediate_measurement(gpointer data, gpointer user_data)
820 {
821         struct thermometer *t = data;
822         uint16_t handle = t->intermediate_ccc_handle;
823         uint8_t value[2];
824         char *msg;
825
826         if (t->attrib == NULL || !handle)
827                 return;
828
829         put_le16(0x0000, value);
830         msg = g_strdup("Disable Intermediate Temperature notifications");
831
832         gatt_write_char(t->attrib, handle, value, sizeof(value),
833                                                         write_ccc_cb, msg);
834 }
835
836 static void remove_int_watcher(struct thermometer_adapter *tadapter,
837                                                         struct watcher *w)
838 {
839         if (!g_slist_find(tadapter->iwatchers, w))
840                 return;
841
842         tadapter->iwatchers = g_slist_remove(tadapter->iwatchers, w);
843
844         if (g_slist_length(tadapter->iwatchers) == 0)
845                 g_slist_foreach(tadapter->devices,
846                                         disable_intermediate_measurement, 0);
847 }
848
849 static void watcher_exit(DBusConnection *conn, void *user_data)
850 {
851         struct watcher *watcher = user_data;
852         struct thermometer_adapter *tadapter = watcher->tadapter;
853
854         DBG("Thermometer watcher %s disconnected", watcher->path);
855
856         remove_int_watcher(tadapter, watcher);
857
858         tadapter->fwatchers = g_slist_remove(tadapter->fwatchers, watcher);
859         g_dbus_remove_watch(btd_get_dbus_connection(), watcher->id);
860
861         if (g_slist_length(tadapter->fwatchers) == 0)
862                 g_slist_foreach(tadapter->devices,
863                                         disable_final_measurement, 0);
864 }
865
866 static struct watcher *find_watcher(GSList *list, const char *sender,
867                                                         const char *path)
868 {
869         struct watcher *match;
870         GSList *l;
871
872         match = g_new0(struct watcher, 1);
873         match->srv = g_strdup(sender);
874         match->path = g_strdup(path);
875
876         l = g_slist_find_custom(list, match, cmp_watcher);
877         destroy_watcher(match);
878
879         if (l != NULL)
880                 return l->data;
881
882         return NULL;
883 }
884
885 static DBusMessage *register_watcher(DBusConnection *conn, DBusMessage *msg,
886                                                                 void *data)
887 {
888         const char *sender = dbus_message_get_sender(msg);
889         struct thermometer_adapter *tadapter = data;
890         struct watcher *watcher;
891         char *path;
892
893         if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
894                                                         DBUS_TYPE_INVALID))
895                 return btd_error_invalid_args(msg);
896
897         watcher = find_watcher(tadapter->fwatchers, sender, path);
898         if (watcher != NULL)
899                 return btd_error_already_exists(msg);
900
901         DBG("Thermometer watcher %s registered", path);
902
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);
909
910         if (g_slist_length(tadapter->fwatchers) == 0)
911                 g_slist_foreach(tadapter->devices, enable_final_measurement, 0);
912
913         tadapter->fwatchers = g_slist_prepend(tadapter->fwatchers, watcher);
914
915         return dbus_message_new_method_return(msg);
916 }
917
918 static DBusMessage *unregister_watcher(DBusConnection *conn, DBusMessage *msg,
919                                                                 void *data)
920 {
921         const char *sender = dbus_message_get_sender(msg);
922         struct thermometer_adapter *tadapter = data;
923         struct watcher *watcher;
924         char *path;
925
926         if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
927                                                         DBUS_TYPE_INVALID))
928                 return btd_error_invalid_args(msg);
929
930         watcher = find_watcher(tadapter->fwatchers, sender, path);
931         if (watcher == NULL)
932                 return btd_error_does_not_exist(msg);
933
934         DBG("Thermometer watcher %s unregistered", path);
935
936         remove_int_watcher(tadapter, watcher);
937
938         tadapter->fwatchers = g_slist_remove(tadapter->fwatchers, watcher);
939         g_dbus_remove_watch(btd_get_dbus_connection(), watcher->id);
940
941         if (g_slist_length(tadapter->fwatchers) == 0)
942                 g_slist_foreach(tadapter->devices,
943                                         disable_final_measurement, 0);
944
945         return dbus_message_new_method_return(msg);
946 }
947
948 static DBusMessage *enable_intermediate(DBusConnection *conn, DBusMessage *msg,
949                                                                 void *data)
950 {
951         const char *sender = dbus_message_get_sender(msg);
952         struct thermometer_adapter *ta = data;
953         struct watcher *watcher;
954         char *path;
955
956         if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
957                                                         DBUS_TYPE_INVALID))
958                 return btd_error_invalid_args(msg);
959
960         watcher = find_watcher(ta->fwatchers, sender, path);
961         if (watcher == NULL)
962                 return btd_error_does_not_exist(msg);
963
964         if (find_watcher(ta->iwatchers, sender, path))
965                 return btd_error_already_exists(msg);
966
967         DBG("Intermediate measurement watcher %s registered", path);
968
969         if (g_slist_length(ta->iwatchers) == 0)
970                 g_slist_foreach(ta->devices,
971                                         enable_intermediate_measurement, 0);
972
973         ta->iwatchers = g_slist_prepend(ta->iwatchers, watcher);
974
975         return dbus_message_new_method_return(msg);
976 }
977
978 static DBusMessage *disable_intermediate(DBusConnection *conn, DBusMessage *msg,
979                                                                 void *data)
980 {
981         const char *sender = dbus_message_get_sender(msg);
982         struct thermometer_adapter *ta = data;
983         struct watcher *watcher;
984         char *path;
985
986         if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
987                                                         DBUS_TYPE_INVALID))
988                 return btd_error_invalid_args(msg);
989
990         watcher = find_watcher(ta->iwatchers, sender, path);
991         if (watcher == NULL)
992                 return btd_error_does_not_exist(msg);
993
994         DBG("Intermediate measurement %s unregistered", path);
995
996         remove_int_watcher(ta, watcher);
997
998         return dbus_message_new_method_return(msg);
999 }
1000
1001 static gboolean property_get_intermediate(const GDBusPropertyTable *property,
1002                                         DBusMessageIter *iter, void *data)
1003 {
1004         struct thermometer *t = data;
1005         dbus_bool_t val;
1006
1007         val = !!t->intermediate;
1008
1009         dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &val);
1010
1011         return TRUE;
1012 }
1013
1014 static gboolean property_get_interval(const GDBusPropertyTable *property,
1015                                         DBusMessageIter *iter, void *data)
1016 {
1017         struct thermometer *t = data;
1018
1019         if (!t->has_interval)
1020                 return FALSE;
1021
1022         dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &t->interval);
1023
1024         return TRUE;
1025 }
1026
1027 static void property_set_interval(const GDBusPropertyTable *property,
1028                                         DBusMessageIter *iter,
1029                                         GDBusPendingPropertySet id, void *data)
1030 {
1031         struct thermometer *t = data;
1032         struct tmp_interval_data *interval_data;
1033         uint16_t val;
1034         uint8_t atval[2];
1035
1036         if (t->interval_val_handle == 0) {
1037                 g_dbus_pending_property_error(id,
1038                                         ERROR_INTERFACE ".NotSupported",
1039                                         "Operation is not supported");
1040                 return;
1041         }
1042
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");
1047                 return;
1048         }
1049
1050         dbus_message_iter_get_basic(iter, &val);
1051
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");
1056                 return;
1057         }
1058
1059         put_le16(val, &atval[0]);
1060
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);
1066
1067         g_dbus_pending_property_success(id);
1068 }
1069
1070 static gboolean property_exists_interval(const GDBusPropertyTable *property,
1071                                                                 void *data)
1072 {
1073         struct thermometer *t = data;
1074
1075         return t->has_interval;
1076 }
1077
1078 static gboolean property_get_maximum(const GDBusPropertyTable *property,
1079                                         DBusMessageIter *iter, void *data)
1080 {
1081         struct thermometer *t = data;
1082
1083         if (!t->has_interval)
1084                 return FALSE;
1085
1086         dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &t->max);
1087
1088         return TRUE;
1089 }
1090
1091 static gboolean property_get_minimum(const GDBusPropertyTable *property,
1092                                         DBusMessageIter *iter, void *data)
1093 {
1094         struct thermometer *t = data;
1095
1096         if (!t->has_interval)
1097                 return FALSE;
1098
1099         dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &t->min);
1100
1101         return TRUE;
1102 }
1103
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 },
1112         { }
1113 };
1114
1115 static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
1116 {
1117         struct thermometer *t = user_data;
1118
1119         t->attrib = g_attrib_ref(attrib);
1120
1121         gatt_discover_char(t->attrib, t->svc_range->start, t->svc_range->end,
1122                                         NULL, configure_thermometer_cb, t);
1123 }
1124
1125 static void attio_disconnected_cb(gpointer user_data)
1126 {
1127         struct thermometer *t = user_data;
1128
1129         DBG("GATT Disconnected");
1130
1131         if (t->attio_measurement_id > 0) {
1132                 g_attrib_unregister(t->attrib, t->attio_measurement_id);
1133                 t->attio_measurement_id = 0;
1134         }
1135
1136         if (t->attio_intermediate_id > 0) {
1137                 g_attrib_unregister(t->attrib, t->attio_intermediate_id);
1138                 t->attio_intermediate_id = 0;
1139         }
1140
1141         if (t->attio_interval_id > 0) {
1142                 g_attrib_unregister(t->attrib, t->attio_interval_id);
1143                 t->attio_interval_id = 0;
1144         }
1145
1146         g_attrib_unref(t->attrib);
1147         t->attrib = NULL;
1148 }
1149
1150 static int thermometer_register(struct btd_device *device,
1151                                                 struct gatt_primary *tattr)
1152 {
1153         const char *path = device_get_path(device);
1154         struct thermometer *t;
1155         struct btd_adapter *adapter;
1156         struct thermometer_adapter *tadapter;
1157
1158         adapter = device_get_adapter(device);
1159
1160         tadapter = find_thermometer_adapter(adapter);
1161
1162         if (tadapter == NULL)
1163                 return -1;
1164
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;
1171
1172         tadapter->devices = g_slist_prepend(tadapter->devices, t);
1173
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);
1181                 return -EIO;
1182         }
1183
1184         t->attioid = btd_device_add_attio_callback(device, attio_connected_cb,
1185                                                 attio_disconnected_cb, t);
1186         return 0;
1187 }
1188
1189 static void thermometer_unregister(struct btd_device *device)
1190 {
1191         struct thermometer *t;
1192         struct btd_adapter *adapter;
1193         struct thermometer_adapter *tadapter;
1194         GSList *l;
1195
1196         adapter = device_get_adapter(device);
1197
1198         tadapter = find_thermometer_adapter(adapter);
1199
1200         if (tadapter == NULL)
1201                 return;
1202
1203         l = g_slist_find_custom(tadapter->devices, device, cmp_device);
1204         if (l == NULL)
1205                 return;
1206
1207         t = l->data;
1208
1209         tadapter->devices = g_slist_remove(tadapter->devices, t);
1210
1211         g_dbus_unregister_interface(btd_get_dbus_connection(),
1212                                 device_get_path(t->dev), THERMOMETER_INTERFACE);
1213 }
1214
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) },
1228         { }
1229 };
1230
1231 static int thermometer_adapter_register(struct btd_adapter *adapter)
1232 {
1233         struct thermometer_adapter *tadapter;
1234
1235         tadapter = g_new0(struct thermometer_adapter, 1);
1236         tadapter->adapter = adapter;
1237
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);
1247                 return -EIO;
1248         }
1249
1250         thermometer_adapters = g_slist_prepend(thermometer_adapters, tadapter);
1251
1252         return 0;
1253 }
1254
1255 static void thermometer_adapter_unregister(struct btd_adapter *adapter)
1256 {
1257         struct thermometer_adapter *tadapter;
1258
1259         tadapter = find_thermometer_adapter(adapter);
1260         if (tadapter == NULL)
1261                 return;
1262
1263         thermometer_adapters = g_slist_remove(thermometer_adapters, tadapter);
1264
1265         g_dbus_unregister_interface(btd_get_dbus_connection(),
1266                                         adapter_get_path(tadapter->adapter),
1267                                         THERMOMETER_MANAGER_INTERFACE);
1268 }
1269
1270 static int thermometer_device_probe(struct btd_service *service)
1271 {
1272         struct btd_device *device = btd_service_get_device(service);
1273         struct gatt_primary *tattr;
1274
1275         tattr = btd_device_get_primary(device, HEALTH_THERMOMETER_UUID);
1276         if (tattr == NULL)
1277                 return -EINVAL;
1278
1279         return thermometer_register(device, tattr);
1280 }
1281
1282 static void thermometer_device_remove(struct btd_service *service)
1283 {
1284         struct btd_device *device = btd_service_get_device(service);
1285
1286         thermometer_unregister(device);
1287 }
1288
1289 static int thermometer_adapter_probe(struct btd_profile *p,
1290                                                 struct btd_adapter *adapter)
1291 {
1292         return thermometer_adapter_register(adapter);
1293 }
1294
1295 static void thermometer_adapter_remove(struct btd_profile *p,
1296                                                 struct btd_adapter *adapter)
1297 {
1298         thermometer_adapter_unregister(adapter);
1299 }
1300
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
1308 };
1309
1310 static int thermometer_init(void)
1311 {
1312         return btd_profile_register(&thermometer_profile);
1313 }
1314
1315 static void thermometer_exit(void)
1316 {
1317         btd_profile_unregister(&thermometer_profile);
1318 }
1319
1320 BLUETOOTH_PLUGIN_DEFINE(thermometer, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
1321                                         thermometer_init, thermometer_exit)