Fix build break for rpm
[framework/connectivity/bluez.git] / proximity / immalert.c
1 /*
2  *
3  *  BlueZ - Bluetooth protocol stack for Linux
4  *
5  *  Copyright (C) 2012 Texas Instruments Corporation
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 <glib.h>
28 #include <bluetooth/uuid.h>
29 #include <adapter.h>
30
31 #include <dbus/dbus.h>
32 #include <gdbus.h>
33
34 #include "log.h"
35 #include "gattrib.h"
36 #include "att.h"
37 #include "gatt.h"
38 #include "att-database.h"
39 #include "gatt-service.h"
40 #include "attrib-server.h"
41 #include "device.h"
42 #include "attio.h"
43 #include "dbus-common.h"
44 #include "reporter.h"
45 #include "immalert.h"
46
47 struct imm_alert_adapter {
48         struct btd_adapter *adapter;
49         DBusConnection *conn;
50         GSList *connected_devices;
51 };
52
53 struct connected_device {
54         struct btd_device *device;
55         struct imm_alert_adapter *adapter;
56         uint8_t alert_level;
57         guint callback_id;
58 };
59
60 static GSList *imm_alert_adapters;
61
62 static int imdevice_cmp(gconstpointer a, gconstpointer b)
63 {
64         const struct connected_device *condev = a;
65         const struct btd_device *device = b;
66
67         if (condev->device == device)
68                 return 0;
69
70         return -1;
71 }
72
73 static struct connected_device *
74 find_connected_device(struct imm_alert_adapter *ia, struct btd_device *device)
75 {
76         GSList *l = g_slist_find_custom(ia->connected_devices, device,
77                                                                 imdevice_cmp);
78         if (!l)
79                 return NULL;
80
81         return l->data;
82 }
83
84 static int imadapter_cmp(gconstpointer a, gconstpointer b)
85 {
86         const struct imm_alert_adapter *imadapter = a;
87         const struct btd_adapter *adapter = b;
88
89         if (imadapter->adapter == adapter)
90                 return 0;
91
92         return -1;
93 }
94
95 static struct imm_alert_adapter *
96 find_imm_alert_adapter(struct btd_adapter *adapter)
97 {
98         GSList *l = g_slist_find_custom(imm_alert_adapters, adapter,
99                                                                 imadapter_cmp);
100         if (!l)
101                 return NULL;
102
103         return l->data;
104 }
105
106 const char *imm_alert_get_level(struct btd_device *device)
107 {
108         struct imm_alert_adapter *imadapter;
109         struct connected_device *condev;
110
111         if (!device)
112                 return get_alert_level_string(NO_ALERT);
113
114         imadapter = find_imm_alert_adapter(device_get_adapter(device));
115         if (!imadapter)
116                 return get_alert_level_string(NO_ALERT);
117
118         condev = find_connected_device(imadapter, device);
119         if (!condev)
120                 return get_alert_level_string(NO_ALERT);
121
122         return get_alert_level_string(condev->alert_level);
123 }
124
125 static void imm_alert_emit_alert_signal(struct connected_device *condev,
126                                                         uint8_t alert_level)
127 {
128         struct imm_alert_adapter *adapter;
129         const char *path, *alert_level_str;
130
131         if (!condev)
132                 return;
133
134         adapter = condev->adapter;
135         path = device_get_path(condev->device);
136         alert_level_str = get_alert_level_string(alert_level);
137
138         DBG("alert %s remote %s", alert_level_str, path);
139
140         emit_property_changed(adapter->conn, path,
141                         PROXIMITY_REPORTER_INTERFACE, "ImmediateAlertLevel",
142                         DBUS_TYPE_STRING, &alert_level_str);
143 }
144
145 static void imm_alert_remove_condev(struct connected_device *condev)
146 {
147         struct imm_alert_adapter *ia;
148
149         if (!condev)
150                 return;
151
152         ia = condev->adapter;
153
154         if (condev->callback_id && condev->device)
155                 btd_device_remove_attio_callback(condev->device,
156                                                         condev->callback_id);
157
158         if (condev->device)
159                 btd_device_unref(condev->device);
160
161         ia->connected_devices = g_slist_remove(ia->connected_devices, condev);
162         g_free(condev);
163 }
164
165 /* condev can be NULL */
166 static void imm_alert_disc_cb(gpointer user_data)
167 {
168         struct connected_device *condev = user_data;
169
170         if (!condev)
171                 return;
172
173         DBG("immediate alert remove device %p", condev->device);
174
175         imm_alert_emit_alert_signal(condev, NO_ALERT);
176         imm_alert_remove_condev(condev);
177 }
178
179 static uint8_t imm_alert_alert_lvl_write(struct attribute *a,
180                                 struct btd_device *device, gpointer user_data)
181 {
182         uint8_t value;
183         struct imm_alert_adapter *ia = user_data;
184         struct connected_device *condev = NULL;
185
186         if (!device)
187                 goto set_error;
188
189         condev = find_connected_device(ia, device);
190
191         if (a->len == 0) {
192                 DBG("Illegal alert level length");
193                 goto set_error;
194         }
195
196         value = a->data[0];
197         if (value != NO_ALERT && value != MILD_ALERT && value != HIGH_ALERT) {
198                 DBG("Illegal alert value");
199                 goto set_error;
200         }
201
202         /* Register a disconnect cb if the alert level is non-zero */
203         if (value != NO_ALERT && !condev) {
204                 condev = g_new0(struct connected_device, 1);
205                 condev->device = btd_device_ref(device);
206                 condev->adapter = ia;
207                 condev->callback_id = btd_device_add_attio_callback(device,
208                                         NULL, imm_alert_disc_cb, condev);
209                 ia->connected_devices = g_slist_append(ia->connected_devices,
210                                                                 condev);
211                 DBG("added connected dev %p", device);
212         }
213
214         if (value != NO_ALERT) {
215                 condev->alert_level = value;
216                 imm_alert_emit_alert_signal(condev, value);
217         }
218
219         /*
220          * Emit NO_ALERT if the alert level was non-zero before. This is
221          * guaranteed when there's a condev.
222          */
223         if (value == NO_ALERT && condev)
224                 imm_alert_disc_cb(condev);
225
226         DBG("alert level set to %d by device %p", value, device);
227         return 0;
228
229 set_error:
230         error("Set immediate alert level for dev %p", device);
231         /* remove alerts by erroneous devices */
232         imm_alert_disc_cb(condev);
233         return ATT_ECODE_IO;
234 }
235
236 void imm_alert_register(struct btd_adapter *adapter, DBusConnection *conn)
237 {
238         gboolean svc_added;
239         bt_uuid_t uuid;
240         struct imm_alert_adapter *imadapter;
241
242         bt_uuid16_create(&uuid, IMMEDIATE_ALERT_SVC_UUID);
243
244         imadapter = g_new0(struct imm_alert_adapter, 1);
245         imadapter->adapter = adapter;
246         imadapter->conn = dbus_connection_ref(conn);
247
248         imm_alert_adapters = g_slist_append(imm_alert_adapters, imadapter);
249
250         /* Immediate Alert Service */
251         svc_added = gatt_service_add(adapter,
252                                 GATT_PRIM_SVC_UUID, &uuid,
253                                 /* Alert level characteristic */
254                                 GATT_OPT_CHR_UUID, ALERT_LEVEL_CHR_UUID,
255                                 GATT_OPT_CHR_PROPS,
256                                         ATT_CHAR_PROPER_WRITE_WITHOUT_RESP,
257                                 GATT_OPT_CHR_VALUE_CB, ATTRIB_WRITE,
258                                         imm_alert_alert_lvl_write, imadapter,
259                                 GATT_OPT_INVALID);
260
261         if (!svc_added) {
262                 imm_alert_unregister(adapter);
263                 return;
264         }
265
266         DBG("Immediate Alert service added");
267 }
268
269 static void remove_condev_list_item(gpointer data, gpointer user_data)
270 {
271         struct connected_device *condev = data;
272
273         imm_alert_remove_condev(condev);
274 }
275
276 void imm_alert_unregister(struct btd_adapter *adapter)
277 {
278         struct imm_alert_adapter *imadapter;
279
280         imadapter = find_imm_alert_adapter(adapter);
281         if (!imadapter)
282                 return;
283
284         g_slist_foreach(imadapter->connected_devices, remove_condev_list_item,
285                                                                         NULL);
286         dbus_connection_unref(imadapter->conn);
287
288         imm_alert_adapters = g_slist_remove(imm_alert_adapters, imadapter);
289         g_free(imadapter);
290 }