3 * BlueZ - Bluetooth protocol stack for Linux
5 * Copyright (C) 2014 Instituto Nokia de Tecnologia - INdT
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
32 #include <sys/signalfd.h>
35 #include <dbus/dbus.h>
37 #include "gdbus/gdbus.h"
39 #include "src/error.h"
41 #define GATT_MGR_IFACE "org.bluez.GattManager1"
42 #define GATT_SERVICE_IFACE "org.bluez.GattService1"
43 #define GATT_CHR_IFACE "org.bluez.GattCharacteristic1"
44 #define GATT_DESCRIPTOR_IFACE "org.bluez.GattDescriptor1"
46 /* Immediate Alert Service UUID */
47 #define IAS_UUID "00001802-0000-1000-8000-00805f9b34fb"
48 #define ALERT_LEVEL_CHR_UUID "00002a06-0000-1000-8000-00805f9b34fb"
50 /* Random UUID for testing purpose */
51 #define READ_WRITE_DESCRIPTOR_UUID "8260c653-1a54-426b-9e36-e84c238bc669"
53 static GMainLoop *main_loop;
54 static GSList *services;
55 static DBusConnection *connection;
57 struct characteristic {
73 * Alert Level support Write Without Response only. Supported
74 * properties are defined at doc/gatt-api.txt. See "Flags"
75 * property of the GattCharacteristic1.
77 static const char *ias_alert_level_props[] = { "write-without-response", NULL };
79 static gboolean desc_get_uuid(const GDBusPropertyTable *property,
80 DBusMessageIter *iter, void *user_data)
82 struct descriptor *desc = user_data;
84 dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &desc->uuid);
89 static gboolean desc_get_value(const GDBusPropertyTable *property,
90 DBusMessageIter *iter, void *user_data)
92 struct descriptor *desc = user_data;
93 DBusMessageIter array;
95 printf("Descriptor(%s): Get(\"Value\")\n", desc->uuid);
97 dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
98 DBUS_TYPE_BYTE_AS_STRING, &array);
100 if (desc->vlen && desc->value)
101 dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
102 &desc->value, desc->vlen);
104 dbus_message_iter_close_container(iter, &array);
109 static void desc_set_value(const GDBusPropertyTable *property,
110 DBusMessageIter *iter,
111 GDBusPendingPropertySet id, void *user_data)
113 struct descriptor *desc = user_data;
114 DBusMessageIter array;
115 const uint8_t *value;
118 printf("Descriptor(%s): Set(\"Value\", ...)\n", desc->uuid);
120 dbus_message_iter_recurse(iter, &array);
121 dbus_message_iter_get_fixed_array(&array, &value, &vlen);
124 desc->value = g_memdup(value, vlen);
127 g_dbus_pending_property_success(id);
129 g_dbus_emit_property_changed(connection, desc->path,
130 GATT_DESCRIPTOR_IFACE, "Value");
134 static const GDBusPropertyTable desc_properties[] = {
135 { "UUID", "s", desc_get_uuid },
136 { "Value", "ay", desc_get_value, desc_set_value, NULL },
140 static gboolean chr_get_uuid(const GDBusPropertyTable *property,
141 DBusMessageIter *iter, void *user_data)
143 struct characteristic *chr = user_data;
145 dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &chr->uuid);
150 static gboolean chr_get_value(const GDBusPropertyTable *property,
151 DBusMessageIter *iter, void *user_data)
153 struct characteristic *chr = user_data;
154 DBusMessageIter array;
156 printf("Characteristic(%s): Get(\"Value\")\n", chr->uuid);
158 dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
159 DBUS_TYPE_BYTE_AS_STRING, &array);
161 dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
162 &chr->value, chr->vlen);
164 dbus_message_iter_close_container(iter, &array);
169 static gboolean chr_get_props(const GDBusPropertyTable *property,
170 DBusMessageIter *iter, void *data)
172 struct characteristic *chr = data;
173 DBusMessageIter array;
176 dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
177 DBUS_TYPE_STRING_AS_STRING, &array);
179 for (i = 0; chr->props[i]; i++)
180 dbus_message_iter_append_basic(&array,
181 DBUS_TYPE_STRING, &chr->props[i]);
183 dbus_message_iter_close_container(iter, &array);
188 static void chr_set_value(const GDBusPropertyTable *property,
189 DBusMessageIter *iter,
190 GDBusPendingPropertySet id, void *user_data)
192 struct characteristic *chr = user_data;
193 DBusMessageIter array;
197 printf("Characteristic(%s): Set('Value', ...)\n", chr->uuid);
199 if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) {
200 printf("Invalid value for Set('Value'...)\n");
201 g_dbus_pending_property_error(id,
202 ERROR_INTERFACE ".InvalidArguments",
203 "Invalid arguments in method call");
207 dbus_message_iter_recurse(iter, &array);
208 dbus_message_iter_get_fixed_array(&array, &value, &len);
211 chr->value = g_memdup(value, len);
214 g_dbus_pending_property_success(id);
215 g_dbus_emit_property_changed(connection, chr->path,
216 GATT_CHR_IFACE, "Value");
219 static const GDBusPropertyTable chr_properties[] = {
220 { "UUID", "s", chr_get_uuid },
221 { "Value", "ay", chr_get_value, chr_set_value, NULL },
222 { "Flags", "as", chr_get_props, NULL, NULL },
226 static gboolean service_get_uuid(const GDBusPropertyTable *property,
227 DBusMessageIter *iter, void *user_data)
229 const char *uuid = user_data;
231 printf("Get UUID: %s\n", uuid);
233 dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &uuid);
238 static gboolean service_get_includes(const GDBusPropertyTable *property,
239 DBusMessageIter *iter, void *user_data)
241 const char *uuid = user_data;
243 printf("Get Includes: %s\n", uuid);
248 static gboolean service_exist_includes(const GDBusPropertyTable *property,
251 const char *uuid = user_data;
253 printf("Exist Includes: %s\n", uuid);
258 static const GDBusPropertyTable service_properties[] = {
259 { "UUID", "s", service_get_uuid },
260 { "Includes", "ao", service_get_includes, NULL,
261 service_exist_includes },
265 static void chr_iface_destroy(gpointer user_data)
267 struct characteristic *chr = user_data;
275 static void desc_iface_destroy(gpointer user_data)
277 struct descriptor *desc = user_data;
285 static gboolean register_characteristic(const char *chr_uuid,
286 const uint8_t *value, int vlen,
288 const char *desc_uuid,
289 const char *service_path)
291 struct characteristic *chr;
292 struct descriptor *desc;
295 chr = g_new0(struct characteristic, 1);
296 chr->uuid = g_strdup(chr_uuid);
297 chr->value = g_memdup(value, vlen);
300 chr->path = g_strdup_printf("%s/characteristic%d", service_path, id++);
302 if (!g_dbus_register_interface(connection, chr->path, GATT_CHR_IFACE,
303 NULL, NULL, chr_properties,
304 chr, chr_iface_destroy)) {
305 printf("Couldn't register characteristic interface\n");
306 chr_iface_destroy(chr);
313 desc = g_new0(struct descriptor, 1);
314 desc->uuid = g_strdup(desc_uuid);
315 desc->path = g_strdup_printf("%s/descriptor%d", chr->path, id++);
317 if (!g_dbus_register_interface(connection, desc->path,
318 GATT_DESCRIPTOR_IFACE,
319 NULL, NULL, desc_properties,
320 desc, desc_iface_destroy)) {
321 printf("Couldn't register descriptor interface\n");
322 g_dbus_unregister_interface(connection, chr->path,
325 desc_iface_destroy(desc);
332 static char *register_service(const char *uuid)
337 path = g_strdup_printf("/service%d", id++);
338 if (!g_dbus_register_interface(connection, path, GATT_SERVICE_IFACE,
339 NULL, NULL, service_properties,
340 g_strdup(uuid), g_free)) {
341 printf("Couldn't register service interface\n");
349 static void create_services()
354 service_path = register_service(IAS_UUID);
358 /* Add Alert Level Characteristic to Immediate Alert Service */
359 if (!register_characteristic(ALERT_LEVEL_CHR_UUID,
360 &level, sizeof(level),
361 ias_alert_level_props,
362 READ_WRITE_DESCRIPTOR_UUID,
364 printf("Couldn't register Alert Level characteristic (IAS)\n");
365 g_dbus_unregister_interface(connection, service_path,
367 g_free(service_path);
371 services = g_slist_prepend(services, service_path);
372 printf("Registered service: %s\n", service_path);
375 static void register_external_service_reply(DBusPendingCall *call,
378 DBusMessage *reply = dbus_pending_call_steal_reply(call);
381 dbus_error_init(&derr);
382 dbus_set_error_from_message(&derr, reply);
384 if (dbus_error_is_set(&derr))
385 printf("RegisterService: %s\n", derr.message);
387 printf("RegisterService: OK\n");
389 dbus_message_unref(reply);
390 dbus_error_free(&derr);
393 static void register_external_service(gpointer a, gpointer b)
395 DBusConnection *conn = b;
396 const char *path = a;
398 DBusPendingCall *call;
399 DBusMessageIter iter, dict;
401 msg = dbus_message_new_method_call("org.bluez", "/org/bluez",
402 GATT_MGR_IFACE, "RegisterService");
404 printf("Couldn't allocate D-Bus message\n");
408 dbus_message_iter_init_append(msg, &iter);
410 dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &path);
412 dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &dict);
414 /* TODO: Add options dictionary */
416 dbus_message_iter_close_container(&iter, &dict);
418 if (!g_dbus_send_message_with_reply(conn, msg, &call, -1)) {
419 dbus_message_unref(msg);
423 dbus_pending_call_set_notify(call, register_external_service_reply,
426 dbus_pending_call_unref(call);
429 static void connect_handler(DBusConnection *conn, void *user_data)
431 g_slist_foreach(services, register_external_service, conn);
434 static gboolean signal_handler(GIOChannel *channel, GIOCondition cond,
437 static bool __terminated = false;
438 struct signalfd_siginfo si;
442 if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP))
445 fd = g_io_channel_unix_get_fd(channel);
447 result = read(fd, &si, sizeof(si));
448 if (result != sizeof(si))
451 switch (si.ssi_signo) {
455 printf("Terminating\n");
456 g_main_loop_quit(main_loop);
466 static guint setup_signalfd(void)
474 sigaddset(&mask, SIGINT);
475 sigaddset(&mask, SIGTERM);
477 if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) {
478 perror("Failed to set signal mask");
482 fd = signalfd(-1, &mask, 0);
484 perror("Failed to create signal descriptor");
488 channel = g_io_channel_unix_new(fd);
490 g_io_channel_set_close_on_unref(channel, TRUE);
491 g_io_channel_set_encoding(channel, NULL, NULL);
492 g_io_channel_set_buffered(channel, FALSE);
494 source = g_io_add_watch(channel,
495 G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
496 signal_handler, NULL);
498 g_io_channel_unref(channel);
503 int main(int argc, char *argv[])
508 signal = setup_signalfd();
512 connection = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL);
514 main_loop = g_main_loop_new(NULL, FALSE);
516 g_dbus_attach_object_manager(connection);
518 printf("gatt-service unique name: %s\n",
519 dbus_bus_get_unique_name(connection));
523 client = g_dbus_client_new(connection, "org.bluez", "/org/bluez");
525 g_dbus_client_set_connect_watch(client, connect_handler, NULL);
527 g_main_loop_run(main_loop);
529 g_dbus_client_unref(client);
531 g_source_remove(signal);
533 g_slist_free_full(services, g_free);
534 dbus_connection_unref(connection);