All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v4 1/2] battery: Add BT SIG reserved number used by Battery Service
@ 2017-10-30 16:55 Bastien Nocera
  2017-10-30 16:55 ` [PATCH v4 2/2] profiles/battery: Add Bluetooth LE Battery service Bastien Nocera
  0 siblings, 1 reply; 3+ messages in thread
From: Bastien Nocera @ 2017-10-30 16:55 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Bastien Nocera

Add the Battery Level UUID as per:
https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.battery_level.xml
---
 lib/uuid.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/lib/uuid.h b/lib/uuid.h
index 2c57a3378..7e13e0fda 100644
--- a/lib/uuid.h
+++ b/lib/uuid.h
@@ -119,6 +119,7 @@ extern "C" {
 #define GATT_CHARAC_RECONNECTION_ADDRESS		0x2A03
 #define GATT_CHARAC_PERIPHERAL_PREF_CONN		0x2A04
 #define GATT_CHARAC_SERVICE_CHANGED			0x2A05
+#define GATT_CHARAC_BATTERY_LEVEL			0x2A19
 #define GATT_CHARAC_SYSTEM_ID				0x2A23
 #define GATT_CHARAC_MODEL_NUMBER_STRING			0x2A24
 #define GATT_CHARAC_SERIAL_NUMBER_STRING		0x2A25
-- 
2.14.2


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* [PATCH v4 2/2] profiles/battery: Add Bluetooth LE Battery service
  2017-10-30 16:55 [PATCH v4 1/2] battery: Add BT SIG reserved number used by Battery Service Bastien Nocera
@ 2017-10-30 16:55 ` Bastien Nocera
  2017-11-02 11:17   ` Luiz Augusto von Dentz
  0 siblings, 1 reply; 3+ messages in thread
From: Bastien Nocera @ 2017-10-30 16:55 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Bastien Nocera

The Battery Level characteristic was tested with a
Microsoft Arc Touch Mouse SE.

The Battery1 interface is now exported for Bluetooth LE devices which
support the Battery Level characteristic, providing a single
"Percentage" value to other integration points in the OS, such as UPower
for consumption on most free desktops.

See https://bugs.freedesktop.org/show_bug.cgi?id=92370
---
 Makefile.plugins           |   3 +
 doc/battery-api.txt        |  14 ++
 profiles/battery/battery.c | 373 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 390 insertions(+)
 create mode 100644 doc/battery-api.txt
 create mode 100644 profiles/battery/battery.c

diff --git a/Makefile.plugins b/Makefile.plugins
index 73377e532..1f3b5b552 100644
--- a/Makefile.plugins
+++ b/Makefile.plugins
@@ -100,6 +100,9 @@ builtin_sources += profiles/midi/midi.c \
 builtin_ldadd += @ALSA_LIBS@
 endif
 
+builtin_modules += battery
+builtin_sources += profiles/battery/battery.c
+
 if SIXAXIS
 plugin_LTLIBRARIES += plugins/sixaxis.la
 plugins_sixaxis_la_SOURCES = plugins/sixaxis.c
diff --git a/doc/battery-api.txt b/doc/battery-api.txt
new file mode 100644
index 000000000..623929851
--- /dev/null
+++ b/doc/battery-api.txt
@@ -0,0 +1,14 @@
+BlueZ D-Bus Battery API description
+***********************************
+
+
+Battery hierarchy
+=================
+
+Service		org.bluez
+Interface	org.bluez.Battery1
+Object path	[variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
+
+Properties	uint16 Percentage [readonly]
+
+			The percentage of battery left.
diff --git a/profiles/battery/battery.c b/profiles/battery/battery.c
new file mode 100644
index 000000000..8dd0d0fa3
--- /dev/null
+++ b/profiles/battery/battery.c
@@ -0,0 +1,373 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012  Instituto Nokia de Tecnologia - INdT
+ *  Copyright (C) 2014  Google Inc.
+ *  Copyright (C) 2017  Red Hat Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <ctype.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include "gdbus/gdbus.h"
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+
+#include "src/dbus-common.h"
+#include "src/shared/util.h"
+#include "src/shared/att.h"
+#include "src/shared/queue.h"
+#include "src/shared/gatt-db.h"
+#include "src/shared/gatt-client.h"
+#include "src/plugin.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/profile.h"
+#include "src/service.h"
+#include "src/log.h"
+#include "attrib/att.h"
+
+#define BATTERY_INTERFACE "org.bluez.Battery1"
+
+#define BATT_UUID16 0x180f
+
+/* Generic Attribute/Access Service */
+struct batt {
+	char *path; /* D-Bus path of device */
+	struct btd_device *device;
+	struct gatt_db *db;
+	struct bt_gatt_client *client;
+	struct gatt_db_attribute *attr;
+
+	unsigned int batt_level_cb_id;
+	uint16_t batt_level_io_handle;
+
+	uint8_t *initial_value;
+	guint16 percentage;
+};
+
+static void batt_free(struct batt *batt)
+{
+	gatt_db_unref(batt->db);
+	bt_gatt_client_unref(batt->client);
+	btd_device_unref(batt->device);
+	g_free (batt->initial_value);
+	g_free(batt);
+}
+
+static void batt_reset(struct batt *batt)
+{
+	batt->attr = NULL;
+	gatt_db_unref(batt->db);
+	batt->db = NULL;
+	bt_gatt_client_unregister_notify(batt->client, batt->batt_level_cb_id);
+	bt_gatt_client_cancel_all(batt->client);
+	bt_gatt_client_unref(batt->client);
+	batt->client = NULL;
+	g_free (batt->initial_value);
+	batt->initial_value = NULL;
+	if (batt->path) {
+		g_dbus_unregister_interface(btd_get_dbus_connection(),
+					    batt->path, BATTERY_INTERFACE);
+		g_free(batt->path);
+		batt->path = NULL;
+	}
+	btd_device_unref(batt->device);
+}
+
+static void parse_battery_level(struct batt *batt,
+				const uint8_t *value)
+{
+	uint8_t percentage;
+
+	percentage = value[0];
+	if (batt->percentage != percentage) {
+		batt->percentage = percentage;
+		DBG("Battery Level updated: %d%%", percentage);
+		g_dbus_emit_property_changed(btd_get_dbus_connection(), batt->path,
+						BATTERY_INTERFACE, "Percentage");
+	}
+}
+
+static void batt_io_value_cb(uint16_t value_handle, const uint8_t *value,
+                             uint16_t length, void *user_data)
+{
+	struct batt *batt = user_data;
+
+	if (value_handle == batt->batt_level_io_handle) {
+		parse_battery_level(batt, value);
+	} else {
+		g_assert_not_reached();
+	}
+}
+
+static gboolean property_get_percentage(
+					const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct batt *batt = data;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &batt->percentage);
+
+	return TRUE;
+}
+
+static const GDBusPropertyTable battery_properties[] = {
+	{ "Percentage", "q", property_get_percentage },
+	{ }
+};
+
+static void batt_io_ccc_written_cb(uint16_t att_ecode, void *user_data)
+{
+	struct batt *batt = user_data;
+
+	if (att_ecode != 0) {
+		error("Battery Level: notifications not enabled %s",
+		      att_ecode2str(att_ecode));
+		return;
+	}
+
+	if (g_dbus_register_interface(btd_get_dbus_connection(),
+					batt->path, BATTERY_INTERFACE,
+					NULL, NULL,
+					battery_properties, batt,
+					NULL) == FALSE) {
+		error("Unable to register %s interface for %s",
+			BATTERY_INTERFACE, batt->path);
+		batt_reset(batt);
+		return;
+	}
+
+	parse_battery_level(batt, batt->initial_value);
+	g_free (batt->initial_value);
+	batt->initial_value = NULL;
+
+	DBG("Battery Level: notification enabled");
+}
+
+static void read_initial_battery_level_cb(bool success,
+						uint8_t att_ecode,
+						const uint8_t *value,
+						uint16_t length,
+						void *user_data)
+{
+	struct batt *batt = user_data;
+
+	if (!success) {
+		DBG("Reading battery level failed with ATT errror: %u",
+								att_ecode);
+		return;
+	}
+
+	if (!length)
+		return;
+
+	batt->initial_value = g_memdup(value, length);
+
+	/* request notify */
+	batt->batt_level_cb_id =
+		bt_gatt_client_register_notify(batt->client,
+		                               batt->batt_level_io_handle,
+		                               batt_io_ccc_written_cb,
+		                               batt_io_value_cb,
+		                               batt,
+		                               NULL);
+}
+
+static void handle_battery_level(struct batt *batt, uint16_t value_handle)
+{
+	batt->batt_level_io_handle = value_handle;
+
+	if (!bt_gatt_client_read_value(batt->client, batt->batt_level_io_handle,
+						read_initial_battery_level_cb, batt, NULL))
+		DBG("Failed to send request to read battery level");
+}
+
+static bool uuid_cmp(uint16_t u16, const bt_uuid_t *uuid)
+{
+	bt_uuid_t lhs;
+
+	bt_uuid16_create(&lhs, u16);
+
+	return bt_uuid_cmp(&lhs, uuid) == 0;
+}
+
+static void handle_characteristic(struct gatt_db_attribute *attr,
+								void *user_data)
+{
+	struct batt *batt = user_data;
+	uint16_t value_handle;
+	bt_uuid_t uuid;
+
+	if (!gatt_db_attribute_get_char_data(attr, NULL, &value_handle, NULL,
+								NULL, &uuid)) {
+		error("Failed to obtain characteristic data");
+		return;
+	}
+
+	if (uuid_cmp(GATT_CHARAC_BATTERY_LEVEL, &uuid)) {
+		handle_battery_level(batt, value_handle);
+	} else {
+		char uuid_str[MAX_LEN_UUID_STR];
+
+		bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str));
+		DBG("Unsupported characteristic: %s", uuid_str);
+	}
+}
+
+static void handle_batt_service(struct batt *batt)
+{
+	gatt_db_service_foreach_char(batt->attr, handle_characteristic, batt);
+}
+
+static int batt_probe(struct btd_service *service)
+{
+	struct btd_device *device = btd_service_get_device(service);
+	struct batt *batt = btd_service_get_user_data(service);
+	char addr[18];
+
+	ba2str(device_get_address(device), addr);
+	DBG("BATT profile probe (%s)", addr);
+
+	/* Ignore, if we were probed for this device already */
+	if (batt) {
+		error("Profile probed twice for the same device!");
+		return -1;
+	}
+
+	batt = g_new0(struct batt, 1);
+	if (!batt)
+		return -1;
+
+	batt->percentage = -1;
+	batt->device = btd_device_ref(device);
+	btd_service_set_user_data(service, batt);
+
+	return 0;
+}
+
+static void batt_remove(struct btd_service *service)
+{
+	struct btd_device *device = btd_service_get_device(service);
+	struct batt *batt;
+	char addr[18];
+
+	ba2str(device_get_address(device), addr);
+	DBG("BATT profile remove (%s)", addr);
+
+	batt = btd_service_get_user_data(service);
+	if (!batt) {
+		error("BATT service not handled by profile");
+		return;
+	}
+
+	batt_free(batt);
+}
+
+static void foreach_batt_service(struct gatt_db_attribute *attr, void *user_data)
+{
+	struct batt *batt = user_data;
+
+	if (batt->attr) {
+		error("More than one BATT service exists for this device");
+		return;
+	}
+
+	batt->attr = attr;
+	handle_batt_service(batt);
+}
+
+static int batt_accept(struct btd_service *service)
+{
+	struct btd_device *device = btd_service_get_device(service);
+	struct gatt_db *db = btd_device_get_gatt_db(device);
+	struct bt_gatt_client *client = btd_device_get_gatt_client(device);
+	struct batt *batt = btd_service_get_user_data(service);
+	char addr[18];
+	bt_uuid_t batt_uuid;
+
+	ba2str(device_get_address(device), addr);
+	DBG("BATT profile accept (%s)", addr);
+
+	if (!batt) {
+		error("BATT service not handled by profile");
+		return -1;
+	}
+
+	batt->db = gatt_db_ref(db);
+	batt->client = bt_gatt_client_clone(client);
+
+	/* Handle the BATT services */
+	bt_uuid16_create(&batt_uuid, BATT_UUID16);
+	gatt_db_foreach_service(db, &batt_uuid, foreach_batt_service, batt);
+
+	if (!batt->attr) {
+		error("BATT attribute not found");
+		batt_reset(batt);
+		return -1;
+	}
+
+	batt->path = g_strdup (device_get_path(device));
+
+	btd_service_connecting_complete(service, 0);
+
+	return 0;
+}
+
+static int batt_disconnect(struct btd_service *service)
+{
+	struct batt *batt = btd_service_get_user_data(service);
+
+	batt_reset(batt);
+
+	btd_service_disconnecting_complete(service, 0);
+
+	return 0;
+}
+
+static struct btd_profile batt_profile = {
+	.name		= "batt-profile",
+	.remote_uuid	= BATTERY_UUID,
+	.device_probe	= batt_probe,
+	.device_remove	= batt_remove,
+	.accept		= batt_accept,
+	.disconnect	= batt_disconnect,
+};
+
+static int batt_init(void)
+{
+	return btd_profile_register(&batt_profile);
+}
+
+static void batt_exit(void)
+{
+	btd_profile_unregister(&batt_profile);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(battery, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+							batt_init, batt_exit)
-- 
2.14.2


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re: [PATCH v4 2/2] profiles/battery: Add Bluetooth LE Battery service
  2017-10-30 16:55 ` [PATCH v4 2/2] profiles/battery: Add Bluetooth LE Battery service Bastien Nocera
@ 2017-11-02 11:17   ` Luiz Augusto von Dentz
  0 siblings, 0 replies; 3+ messages in thread
From: Luiz Augusto von Dentz @ 2017-11-02 11:17 UTC (permalink / raw)
  To: Bastien Nocera; +Cc: linux-bluetooth

Hi Bastien,

On Mon, Oct 30, 2017 at 6:55 PM, Bastien Nocera <hadess@hadess.net> wrote:
> The Battery Level characteristic was tested with a
> Microsoft Arc Touch Mouse SE.
>
> The Battery1 interface is now exported for Bluetooth LE devices which
> support the Battery Level characteristic, providing a single
> "Percentage" value to other integration points in the OS, such as UPower
> for consumption on most free desktops.
>
> See https://bugs.freedesktop.org/show_bug.cgi?id=92370
> ---
>  Makefile.plugins           |   3 +
>  doc/battery-api.txt        |  14 ++
>  profiles/battery/battery.c | 373 +++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 390 insertions(+)
>  create mode 100644 doc/battery-api.txt
>  create mode 100644 profiles/battery/battery.c
>
> diff --git a/Makefile.plugins b/Makefile.plugins
> index 73377e532..1f3b5b552 100644
> --- a/Makefile.plugins
> +++ b/Makefile.plugins
> @@ -100,6 +100,9 @@ builtin_sources += profiles/midi/midi.c \
>  builtin_ldadd += @ALSA_LIBS@
>  endif
>
> +builtin_modules += battery
> +builtin_sources += profiles/battery/battery.c
> +
>  if SIXAXIS
>  plugin_LTLIBRARIES += plugins/sixaxis.la
>  plugins_sixaxis_la_SOURCES = plugins/sixaxis.c
> diff --git a/doc/battery-api.txt b/doc/battery-api.txt
> new file mode 100644
> index 000000000..623929851
> --- /dev/null
> +++ b/doc/battery-api.txt
> @@ -0,0 +1,14 @@
> +BlueZ D-Bus Battery API description
> +***********************************
> +
> +
> +Battery hierarchy
> +=================
> +
> +Service                org.bluez
> +Interface      org.bluez.Battery1
> +Object path    [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
> +
> +Properties     uint16 Percentage [readonly]

As discussed in the irc, this should be a byte.

> +                       The percentage of battery left.
> diff --git a/profiles/battery/battery.c b/profiles/battery/battery.c
> new file mode 100644
> index 000000000..8dd0d0fa3
> --- /dev/null
> +++ b/profiles/battery/battery.c
> @@ -0,0 +1,373 @@
> +/*
> + *
> + *  BlueZ - Bluetooth protocol stack for Linux
> + *
> + *  Copyright (C) 2012  Instituto Nokia de Tecnologia - INdT
> + *  Copyright (C) 2014  Google Inc.
> + *  Copyright (C) 2017  Red Hat Inc.
> + *
> + *  This program is free software; you can redistribute it and/or modify
> + *  it under the terms of the GNU General Public License as published by
> + *  the Free Software Foundation; either version 2 of the License, or
> + *  (at your option) any later version.
> + *
> + *  This program is distributed in the hope that it will be useful,
> + *  but WITHOUT ANY WARRANTY; without even the implied warranty of
> + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + *  GNU General Public License for more details.
> + */
> +
> +#ifdef HAVE_CONFIG_H
> +#include <config.h>
> +#endif
> +
> +#include <ctype.h>
> +#include <stdbool.h>
> +#include <stdlib.h>
> +#include <sys/types.h>
> +#include <sys/stat.h>
> +#include <fcntl.h>
> +#include <errno.h>
> +
> +#include <glib.h>
> +
> +#include "gdbus/gdbus.h"
> +
> +#include "lib/bluetooth.h"
> +#include "lib/hci.h"
> +#include "lib/sdp.h"
> +#include "lib/uuid.h"
> +
> +#include "src/dbus-common.h"
> +#include "src/shared/util.h"
> +#include "src/shared/att.h"
> +#include "src/shared/queue.h"
> +#include "src/shared/gatt-db.h"
> +#include "src/shared/gatt-client.h"
> +#include "src/plugin.h"
> +#include "src/adapter.h"
> +#include "src/device.h"
> +#include "src/profile.h"
> +#include "src/service.h"
> +#include "src/log.h"
> +#include "attrib/att.h"
> +
> +#define BATTERY_INTERFACE "org.bluez.Battery1"
> +
> +#define BATT_UUID16 0x180f
> +
> +/* Generic Attribute/Access Service */
> +struct batt {
> +       char *path; /* D-Bus path of device */
> +       struct btd_device *device;
> +       struct gatt_db *db;
> +       struct bt_gatt_client *client;
> +       struct gatt_db_attribute *attr;
> +
> +       unsigned int batt_level_cb_id;
> +       uint16_t batt_level_io_handle;
> +
> +       uint8_t *initial_value;
> +       guint16 percentage;
> +};
> +
> +static void batt_free(struct batt *batt)
> +{
> +       gatt_db_unref(batt->db);
> +       bt_gatt_client_unref(batt->client);
> +       btd_device_unref(batt->device);
> +       g_free (batt->initial_value);
> +       g_free(batt);
> +}
> +
> +static void batt_reset(struct batt *batt)
> +{
> +       batt->attr = NULL;
> +       gatt_db_unref(batt->db);
> +       batt->db = NULL;
> +       bt_gatt_client_unregister_notify(batt->client, batt->batt_level_cb_id);
> +       bt_gatt_client_cancel_all(batt->client);
> +       bt_gatt_client_unref(batt->client);
> +       batt->client = NULL;
> +       g_free (batt->initial_value);
> +       batt->initial_value = NULL;
> +       if (batt->path) {
> +               g_dbus_unregister_interface(btd_get_dbus_connection(),
> +                                           batt->path, BATTERY_INTERFACE);
> +               g_free(batt->path);
> +               batt->path = NULL;
> +       }
> +       btd_device_unref(batt->device);
> +}
> +
> +static void parse_battery_level(struct batt *batt,
> +                               const uint8_t *value)
> +{
> +       uint8_t percentage;
> +
> +       percentage = value[0];
> +       if (batt->percentage != percentage) {
> +               batt->percentage = percentage;
> +               DBG("Battery Level updated: %d%%", percentage);
> +               g_dbus_emit_property_changed(btd_get_dbus_connection(), batt->path,
> +                                               BATTERY_INTERFACE, "Percentage");
> +       }
> +}
> +
> +static void batt_io_value_cb(uint16_t value_handle, const uint8_t *value,
> +                             uint16_t length, void *user_data)
> +{
> +       struct batt *batt = user_data;
> +
> +       if (value_handle == batt->batt_level_io_handle) {
> +               parse_battery_level(batt, value);
> +       } else {
> +               g_assert_not_reached();
> +       }
> +}
> +
> +static gboolean property_get_percentage(
> +                                       const GDBusPropertyTable *property,
> +                                       DBusMessageIter *iter, void *data)
> +{
> +       struct batt *batt = data;
> +
> +       dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &batt->percentage);
> +
> +       return TRUE;
> +}
> +
> +static const GDBusPropertyTable battery_properties[] = {
> +       { "Percentage", "q", property_get_percentage },
> +       { }
> +};
> +
> +static void batt_io_ccc_written_cb(uint16_t att_ecode, void *user_data)
> +{
> +       struct batt *batt = user_data;
> +
> +       if (att_ecode != 0) {
> +               error("Battery Level: notifications not enabled %s",
> +                     att_ecode2str(att_ecode));
> +               return;
> +       }
> +
> +       if (g_dbus_register_interface(btd_get_dbus_connection(),
> +                                       batt->path, BATTERY_INTERFACE,
> +                                       NULL, NULL,
> +                                       battery_properties, batt,
> +                                       NULL) == FALSE) {
> +               error("Unable to register %s interface for %s",
> +                       BATTERY_INTERFACE, batt->path);
> +               batt_reset(batt);
> +               return;
> +       }
> +
> +       parse_battery_level(batt, batt->initial_value);
> +       g_free (batt->initial_value);
> +       batt->initial_value = NULL;
> +
> +       DBG("Battery Level: notification enabled");
> +}
> +
> +static void read_initial_battery_level_cb(bool success,
> +                                               uint8_t att_ecode,
> +                                               const uint8_t *value,
> +                                               uint16_t length,
> +                                               void *user_data)
> +{
> +       struct batt *batt = user_data;
> +
> +       if (!success) {
> +               DBG("Reading battery level failed with ATT errror: %u",
> +                                                               att_ecode);
> +               return;
> +       }
> +
> +       if (!length)
> +               return;
> +
> +       batt->initial_value = g_memdup(value, length);
> +
> +       /* request notify */
> +       batt->batt_level_cb_id =
> +               bt_gatt_client_register_notify(batt->client,
> +                                              batt->batt_level_io_handle,
> +                                              batt_io_ccc_written_cb,
> +                                              batt_io_value_cb,
> +                                              batt,
> +                                              NULL);
> +}
> +
> +static void handle_battery_level(struct batt *batt, uint16_t value_handle)
> +{
> +       batt->batt_level_io_handle = value_handle;
> +
> +       if (!bt_gatt_client_read_value(batt->client, batt->batt_level_io_handle,
> +                                               read_initial_battery_level_cb, batt, NULL))
> +               DBG("Failed to send request to read battery level");
> +}
> +
> +static bool uuid_cmp(uint16_t u16, const bt_uuid_t *uuid)
> +{
> +       bt_uuid_t lhs;
> +
> +       bt_uuid16_create(&lhs, u16);
> +
> +       return bt_uuid_cmp(&lhs, uuid) == 0;
> +}
> +
> +static void handle_characteristic(struct gatt_db_attribute *attr,
> +                                                               void *user_data)
> +{
> +       struct batt *batt = user_data;
> +       uint16_t value_handle;
> +       bt_uuid_t uuid;
> +
> +       if (!gatt_db_attribute_get_char_data(attr, NULL, &value_handle, NULL,
> +                                                               NULL, &uuid)) {
> +               error("Failed to obtain characteristic data");
> +               return;
> +       }
> +
> +       if (uuid_cmp(GATT_CHARAC_BATTERY_LEVEL, &uuid)) {
> +               handle_battery_level(batt, value_handle);
> +       } else {
> +               char uuid_str[MAX_LEN_UUID_STR];
> +
> +               bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str));
> +               DBG("Unsupported characteristic: %s", uuid_str);
> +       }
> +}
> +
> +static void handle_batt_service(struct batt *batt)
> +{
> +       gatt_db_service_foreach_char(batt->attr, handle_characteristic, batt);
> +}
> +
> +static int batt_probe(struct btd_service *service)
> +{
> +       struct btd_device *device = btd_service_get_device(service);
> +       struct batt *batt = btd_service_get_user_data(service);
> +       char addr[18];
> +
> +       ba2str(device_get_address(device), addr);
> +       DBG("BATT profile probe (%s)", addr);
> +
> +       /* Ignore, if we were probed for this device already */
> +       if (batt) {
> +               error("Profile probed twice for the same device!");
> +               return -1;
> +       }
> +
> +       batt = g_new0(struct batt, 1);
> +       if (!batt)
> +               return -1;
> +
> +       batt->percentage = -1;
> +       batt->device = btd_device_ref(device);
> +       btd_service_set_user_data(service, batt);
> +
> +       return 0;
> +}
> +
> +static void batt_remove(struct btd_service *service)
> +{
> +       struct btd_device *device = btd_service_get_device(service);
> +       struct batt *batt;
> +       char addr[18];
> +
> +       ba2str(device_get_address(device), addr);
> +       DBG("BATT profile remove (%s)", addr);
> +
> +       batt = btd_service_get_user_data(service);
> +       if (!batt) {
> +               error("BATT service not handled by profile");
> +               return;
> +       }
> +
> +       batt_free(batt);
> +}
> +
> +static void foreach_batt_service(struct gatt_db_attribute *attr, void *user_data)
> +{
> +       struct batt *batt = user_data;
> +
> +       if (batt->attr) {
> +               error("More than one BATT service exists for this device");
> +               return;
> +       }
> +
> +       batt->attr = attr;
> +       handle_batt_service(batt);
> +}
> +
> +static int batt_accept(struct btd_service *service)
> +{
> +       struct btd_device *device = btd_service_get_device(service);
> +       struct gatt_db *db = btd_device_get_gatt_db(device);
> +       struct bt_gatt_client *client = btd_device_get_gatt_client(device);
> +       struct batt *batt = btd_service_get_user_data(service);
> +       char addr[18];
> +       bt_uuid_t batt_uuid;
> +
> +       ba2str(device_get_address(device), addr);
> +       DBG("BATT profile accept (%s)", addr);
> +
> +       if (!batt) {
> +               error("BATT service not handled by profile");
> +               return -1;
> +       }
> +
> +       batt->db = gatt_db_ref(db);
> +       batt->client = bt_gatt_client_clone(client);
> +
> +       /* Handle the BATT services */
> +       bt_uuid16_create(&batt_uuid, BATT_UUID16);
> +       gatt_db_foreach_service(db, &batt_uuid, foreach_batt_service, batt);
> +
> +       if (!batt->attr) {
> +               error("BATT attribute not found");
> +               batt_reset(batt);
> +               return -1;
> +       }
> +
> +       batt->path = g_strdup (device_get_path(device));
> +
> +       btd_service_connecting_complete(service, 0);
> +
> +       return 0;
> +}
> +
> +static int batt_disconnect(struct btd_service *service)
> +{
> +       struct batt *batt = btd_service_get_user_data(service);
> +
> +       batt_reset(batt);
> +
> +       btd_service_disconnecting_complete(service, 0);
> +
> +       return 0;
> +}
> +
> +static struct btd_profile batt_profile = {
> +       .name           = "batt-profile",
> +       .remote_uuid    = BATTERY_UUID,
> +       .device_probe   = batt_probe,
> +       .device_remove  = batt_remove,
> +       .accept         = batt_accept,
> +       .disconnect     = batt_disconnect,
> +};
> +
> +static int batt_init(void)
> +{
> +       return btd_profile_register(&batt_profile);
> +}
> +
> +static void batt_exit(void)
> +{
> +       btd_profile_unregister(&batt_profile);
> +}
> +
> +BLUETOOTH_PLUGIN_DEFINE(battery, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
> +                                                       batt_init, batt_exit)
> --
> 2.14.2
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html



-- 
Luiz Augusto von Dentz

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2017-11-02 11:17 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-10-30 16:55 [PATCH v4 1/2] battery: Add BT SIG reserved number used by Battery Service Bastien Nocera
2017-10-30 16:55 ` [PATCH v4 2/2] profiles/battery: Add Bluetooth LE Battery service Bastien Nocera
2017-11-02 11:17   ` Luiz Augusto von Dentz

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.