Fix build break for rpm
[framework/connectivity/bluez.git] / proximity / reporter.c
1 /*
2  *
3  *  BlueZ - Bluetooth protocol stack for Linux
4  *
5  *  Copyright (C) 2011  Nokia Corporation
6  *  Copyright (C) 2011  Marcel Holtmann <marcel@holtmann.org>
7  *
8  *
9  *  This program is free software; you can redistribute it and/or modify
10  *  it under the terms of the GNU General Public License as published by
11  *  the Free Software Foundation; either version 2 of the License, or
12  *  (at your option) any later version.
13  *
14  *  This program is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *  GNU General Public License for more details.
18  *
19  *  You should have received a copy of the GNU General Public License
20  *  along with this program; if not, write to the Free Software
21  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
22  *
23  */
24
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28
29 #include <glib.h>
30 #include <bluetooth/uuid.h>
31 #include <adapter.h>
32 #include <errno.h>
33
34 #include <dbus/dbus.h>
35 #include <gdbus.h>
36
37 #include "log.h"
38
39 #include "dbus-common.h"
40 #include "error.h"
41 #include "device.h"
42 #include "hcid.h"
43 #include "gattrib.h"
44 #include "att.h"
45 #include "gatt.h"
46 #include "att-database.h"
47 #include "attrib-server.h"
48 #include "reporter.h"
49 #include "linkloss.h"
50 #include "immalert.h"
51
52 #define BLUEZ_SERVICE "org.bluez"
53
54 struct reporter_adapter {
55         DBusConnection *conn;
56         struct btd_adapter *adapter;
57         GSList *devices;
58 };
59
60 static GSList *reporter_adapters;
61
62 static int radapter_cmp(gconstpointer a, gconstpointer b)
63 {
64         const struct reporter_adapter *radapter = a;
65         const struct btd_adapter *adapter = b;
66
67         if (radapter->adapter == adapter)
68                 return 0;
69
70         return -1;
71 }
72
73 static struct reporter_adapter *
74 find_reporter_adapter(struct btd_adapter *adapter)
75 {
76         GSList *l = g_slist_find_custom(reporter_adapters, adapter,
77                                                                 radapter_cmp);
78         if (!l)
79                 return NULL;
80
81         return l->data;
82 }
83
84 const char *get_alert_level_string(uint8_t level)
85 {
86         switch (level) {
87         case NO_ALERT:
88                 return "none";
89         case MILD_ALERT:
90                 return "mild";
91         case HIGH_ALERT:
92                 return "high";
93         }
94
95         return "unknown";
96 }
97
98 static void register_tx_power(struct btd_adapter *adapter)
99 {
100         uint16_t start_handle, h;
101         const int svc_size = 4;
102         uint8_t atval[256];
103         bt_uuid_t uuid;
104
105         bt_uuid16_create(&uuid, TX_POWER_SVC_UUID);
106         start_handle = attrib_db_find_avail(adapter, &uuid, svc_size);
107         if (start_handle == 0) {
108                 error("Not enough free handles to register service");
109                 return;
110         }
111
112         DBG("start_handle=0x%04x", start_handle);
113
114         h = start_handle;
115
116         /* Primary service definition */
117         bt_uuid16_create(&uuid, GATT_PRIM_SVC_UUID);
118         att_put_u16(TX_POWER_SVC_UUID, &atval[0]);
119         attrib_db_add(adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 2);
120
121         /* Power level characteristic */
122         bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
123         atval[0] = ATT_CHAR_PROPER_READ | ATT_CHAR_PROPER_NOTIFY;
124         att_put_u16(h + 1, &atval[1]);
125         att_put_u16(POWER_LEVEL_CHR_UUID, &atval[3]);
126         attrib_db_add(adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 5);
127
128         /* Power level value */
129         bt_uuid16_create(&uuid, POWER_LEVEL_CHR_UUID);
130         att_put_u8(0x00, &atval[0]);
131         attrib_db_add(adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 1);
132
133         /* Client characteristic configuration */
134         bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
135         atval[0] = 0x00;
136         atval[1] = 0x00;
137         attrib_db_add(adapter, h++, &uuid, ATT_NONE, ATT_NONE, atval, 2);
138
139         g_assert(h - start_handle == svc_size);
140 }
141
142 static DBusMessage *get_properties(DBusConnection *conn,
143                                                 DBusMessage *msg, void *data)
144 {
145         DBusMessageIter iter;
146         DBusMessageIter dict;
147         DBusMessage *reply = NULL;
148         const char *linkloss_level, *immalert_level;
149         struct btd_device *device = data;
150
151         reply = dbus_message_new_method_return(msg);
152         if (!reply)
153                 return NULL;
154
155         linkloss_level = link_loss_get_alert_level(device);
156         immalert_level = imm_alert_get_level(device);
157
158         dbus_message_iter_init_append(reply, &iter);
159
160         if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
161                         DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
162                         DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
163                         DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict))
164                 goto err;
165
166         dict_append_entry(&dict, "LinkLossAlertLevel", DBUS_TYPE_STRING,
167                                                         &linkloss_level);
168         dict_append_entry(&dict, "ImmediateAlertLevel", DBUS_TYPE_STRING,
169                                                         &immalert_level);
170
171         if (!dbus_message_iter_close_container(&iter, &dict))
172                 goto err;
173
174         return reply;
175
176 err:
177         if (reply)
178                 dbus_message_unref(reply);
179         return btd_error_failed(msg, "not enough memory");
180 }
181
182 static const GDBusMethodTable reporter_methods[] = {
183         { GDBUS_METHOD("GetProperties",
184                         NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
185                         get_properties) },
186         { }
187 };
188
189 static const GDBusSignalTable reporter_signals[] = {
190         { GDBUS_SIGNAL("PropertyChanged",
191                         GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
192         { }
193 };
194
195 static void unregister_reporter_device(gpointer data, gpointer user_data)
196 {
197         struct btd_device *device = data;
198         struct reporter_adapter *radapter = user_data;
199         const char *path = device_get_path(device);
200
201         DBG("unregister on device %s", path);
202
203         g_dbus_unregister_interface(radapter->conn, path,
204                                         PROXIMITY_REPORTER_INTERFACE);
205
206         radapter->devices = g_slist_remove(radapter->devices, device);
207         btd_device_unref(device);
208 }
209
210 static void register_reporter_device(struct btd_device *device,
211                                         struct reporter_adapter *radapter)
212 {
213         const char *path = device_get_path(device);
214
215         DBG("register on device %s", path);
216
217         g_dbus_register_interface(radapter->conn, path,
218                                         PROXIMITY_REPORTER_INTERFACE,
219                                         reporter_methods, reporter_signals,
220                                         NULL, device, NULL);
221
222         btd_device_ref(device);
223         radapter->devices = g_slist_prepend(radapter->devices, device);
224 }
225
226 static int reporter_device_probe(struct btd_device *device, GSList *uuids)
227 {
228         struct reporter_adapter *radapter;
229         struct btd_adapter *adapter = device_get_adapter(device);
230
231         radapter = find_reporter_adapter(adapter);
232         if (!radapter)
233                 return -1;
234
235         register_reporter_device(device, radapter);
236         return 0;
237 }
238
239 static void reporter_device_remove(struct btd_device *device)
240 {
241         struct reporter_adapter *radapter;
242         struct btd_adapter *adapter = device_get_adapter(device);
243
244         radapter = find_reporter_adapter(adapter);
245         if (!radapter)
246                 return;
247
248         unregister_reporter_device(device, radapter);
249 }
250
251 /* device driver for tracking remote GATT client devices */
252 static struct btd_device_driver reporter_device_driver = {
253         .name = "Proximity GATT Reporter Device Tracker Driver",
254         .uuids = BTD_UUIDS(GATT_UUID),
255         .probe = reporter_device_probe,
256         .remove = reporter_device_remove,
257 };
258
259 int reporter_init(struct btd_adapter *adapter)
260 {
261         struct reporter_adapter *radapter;
262         DBusConnection *conn;
263
264         if (!main_opts.gatt_enabled) {
265                 DBG("GATT is disabled");
266                 return -ENOTSUP;
267         }
268
269         conn = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
270         if (!conn)
271                 return -1;
272
273         radapter = g_new0(struct reporter_adapter, 1);
274         radapter->adapter = adapter;
275         radapter->conn = conn;
276
277         link_loss_register(adapter, radapter->conn);
278         register_tx_power(adapter);
279         imm_alert_register(adapter, radapter->conn);
280
281         btd_register_device_driver(&reporter_device_driver);
282
283         reporter_adapters = g_slist_prepend(reporter_adapters, radapter);
284         DBG("Proximity Reporter for adapter %p", adapter);
285
286         return 0;
287 }
288
289 void reporter_exit(struct btd_adapter *adapter)
290 {
291         struct reporter_adapter *radapter = find_reporter_adapter(adapter);
292         if (!radapter)
293                 return;
294
295         btd_unregister_device_driver(&reporter_device_driver);
296
297         g_slist_foreach(radapter->devices, unregister_reporter_device,
298                                                                 radapter);
299
300         link_loss_unregister(adapter);
301         imm_alert_unregister(adapter);
302         dbus_connection_unref(radapter->conn);
303
304         reporter_adapters = g_slist_remove(reporter_adapters, radapter);
305         g_free(radapter);
306 }