* [PATCH 1/9] battery: Add generic device battery documentation
2013-01-07 19:40 [PATCH 0/9] Battery Profile implementation Paulo Borges
@ 2013-01-07 19:40 ` Paulo Borges
2013-01-07 19:40 ` [PATCH 2/9] battery: Implement Generic device battery Paulo Borges
` (7 subsequent siblings)
8 siblings, 0 replies; 11+ messages in thread
From: Paulo Borges @ 2013-01-07 19:40 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Paulo Borges
Add documentation for the generic battery D-Bus interface.
---
doc/battery-api.txt | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)
create mode 100644 doc/battery-api.txt
diff --git a/doc/battery-api.txt b/doc/battery-api.txt
new file mode 100644
index 0000000..9267bbd
--- /dev/null
+++ b/doc/battery-api.txt
@@ -0,0 +1,24 @@
+BlueZ D-Bus Battery API description
+***********************************
+
+ Texas Instruments, Inc. <chen.ganir@ti.com>
+
+Device Battery hierarchy
+========================
+
+Service org.bluez
+Interface org.bluez.Battery1
+Object path [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/BATTYYYY
+
+Methods Refresh()
+
+ Refresh the battery level. If the battery level changed,
+ the Level signal will be sent with the new value.
+
+Properties uint16 Level [readonly]
+
+ Battery level (0-100).
+
+ object Device [readonly]
+
+ Device object that owns the battery.
--
1.7.9.5
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH 2/9] battery: Implement Generic device battery
2013-01-07 19:40 [PATCH 0/9] Battery Profile implementation Paulo Borges
2013-01-07 19:40 ` [PATCH 1/9] battery: Add generic device battery documentation Paulo Borges
@ 2013-01-07 19:40 ` Paulo Borges
2013-01-07 19:40 ` [PATCH 3/9] battery: Add GATT Battery Client Service skeleton Paulo Borges
` (6 subsequent siblings)
8 siblings, 0 replies; 11+ messages in thread
From: Paulo Borges @ 2013-01-07 19:40 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Paulo Borges
Add implementation for the generic battery in bluetooth device.
This patch adds new D-Bus interface for adding/removing/changing
peer device battery status, allowing management of remote device
batteries.
---
src/device.c | 140 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/device.h | 15 ++++++
test/test-device | 15 ++++++
3 files changed, 170 insertions(+)
diff --git a/src/device.c b/src/device.c
index 20099e6..bbd0bbf 100644
--- a/src/device.c
+++ b/src/device.c
@@ -187,6 +187,7 @@ struct btd_device {
int8_t rssi;
gint ref;
+ GSList *batteries;
GIOChannel *att_io;
guint cleanup_id;
@@ -331,6 +332,24 @@ static void store_device_info(struct btd_device *device)
device->store_id = g_idle_add(store_device_info_cb, device);
}
+struct btd_battery {
+ uint16_t level;
+ char *path;
+ struct btd_device *device;
+ refresh_battery_cb refresh;
+};
+
+static void battery_free(gpointer user_data)
+{
+ struct btd_battery *b = user_data;
+
+ g_dbus_unregister_interface(btd_get_dbus_connection(), b->path,
+ BATTERY_INTERFACE);
+ btd_device_unref(b->device);
+ g_free(b->path);
+ g_free(b);
+}
+
static void browse_request_free(struct browse_req *req)
{
if (req->listener_id)
@@ -393,6 +412,7 @@ static void device_free(gpointer user_data)
g_slist_free_full(device->primaries, g_free);
g_slist_free_full(device->attios, g_free);
g_slist_free_full(device->attios_offline, g_free);
+ g_slist_free_full(device->batteries, battery_free);
attio_cleanup(device);
@@ -4111,3 +4131,123 @@ void btd_device_set_pnpid(struct btd_device *device, uint16_t source,
store_device_info(device);
}
+
+static DBusMessage *refresh_batt_level(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct btd_battery *b = data;
+
+ if (!b->refresh)
+ return btd_error_not_supported(msg);
+
+ b->refresh(b);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static gboolean battery_property_get_level(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct btd_battery *batt = data;
+ uint16_t level = batt->level;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &level);
+
+ return TRUE;
+}
+
+static gboolean battery_property_get_device(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct btd_battery *batt = data;
+ const char *str = device_get_path(batt->device);
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &str);
+
+ return TRUE;
+}
+
+static GDBusMethodTable battery_methods[] = {
+ { GDBUS_METHOD("Refresh",
+ NULL, NULL,
+ refresh_batt_level) },
+ { }
+};
+
+static const GDBusPropertyTable battery_properties[] = {
+ { "Level", "q", battery_property_get_level },
+ { "Device", "o", battery_property_get_device },
+ { }
+};
+
+struct btd_battery *btd_device_add_battery(struct btd_device *device)
+{
+ struct btd_battery *batt;
+
+ batt = g_new0(struct btd_battery, 1);
+ batt->path = g_strdup_printf("%s/BATT%04X", device->path,
+ g_slist_length(device->batteries));
+
+ if (!g_dbus_register_interface(btd_get_dbus_connection(), batt->path,
+ BATTERY_INTERFACE, battery_methods, NULL,
+ battery_properties, batt, NULL)) {
+ error("D-Bus register interface %s failed", BATTERY_INTERFACE);
+ g_free(batt->path);
+ g_free(batt);
+ return NULL;
+ }
+
+ batt->device = btd_device_ref(device);
+ device->batteries = g_slist_append(device->batteries, batt);
+
+ return batt;
+}
+
+void btd_device_remove_battery(struct btd_battery *batt)
+{
+ struct btd_device *dev = batt->device;
+
+ dev->batteries = g_slist_remove(dev->batteries, batt);
+
+ battery_free(batt);
+}
+
+gboolean btd_device_set_battery_opt(struct btd_battery *batt,
+ battery_option_t opt1, ...)
+{
+ va_list args;
+ battery_option_t opt = opt1;
+ int level;
+
+ if (!batt)
+ return FALSE;
+
+ va_start(args, opt1);
+
+ while (opt != BATTERY_OPT_INVALID) {
+ switch (opt) {
+ case BATTERY_OPT_LEVEL:
+ level = va_arg(args, int);
+ if (level != batt->level) {
+ batt->level = level;
+ g_dbus_emit_property_changed(
+ btd_get_dbus_connection(),
+ batt->path, BATTERY_INTERFACE,
+ "Level");
+ }
+ break;
+ case BATTERY_OPT_REFRESH_FUNC:
+ batt->refresh = va_arg(args, refresh_battery_cb);
+ break;
+ default:
+ error("Unknown option %d", opt);
+ return FALSE;
+ }
+
+ opt = va_arg(args, int);
+ }
+
+ va_end(args);
+
+ return TRUE;
+}
diff --git a/src/device.h b/src/device.h
index ae70690..0f7babd 100644
--- a/src/device.h
+++ b/src/device.h
@@ -23,8 +23,18 @@
*/
#define DEVICE_INTERFACE "org.bluez.Device1"
+#define BATTERY_INTERFACE "org.bluez.Battery1"
struct btd_device;
+struct btd_battery;
+
+typedef void (*refresh_battery_cb) (struct btd_battery *batt);
+
+typedef enum {
+ BATTERY_OPT_INVALID = 0,
+ BATTERY_OPT_LEVEL,
+ BATTERY_OPT_REFRESH_FUNC,
+} battery_option_t;
struct btd_device *device_create(struct btd_adapter *adapter,
const char *address, uint8_t bdaddr_type);
@@ -116,3 +126,8 @@ void device_profile_connected(struct btd_device *dev,
struct btd_profile *profile, int err);
void device_profile_disconnected(struct btd_device *dev,
struct btd_profile *profile, int err);
+
+struct btd_battery *btd_device_add_battery(struct btd_device *device);
+void btd_device_remove_battery(struct btd_battery *batt);
+gboolean btd_device_set_battery_opt(struct btd_battery *batt,
+ battery_option_t opt1, ...);
diff --git a/test/test-device b/test/test-device
index 3d7b852..ee144f1 100755
--- a/test/test-device
+++ b/test/test-device
@@ -27,6 +27,7 @@ if (len(args) < 1):
print("Usage: %s <command>" % (sys.argv[0]))
print("")
print(" list")
+ print(" batteries <address>")
print(" create <address>")
print(" remove <address|path>")
print(" connect <address> [profile]")
@@ -196,5 +197,19 @@ if (args[0] == "blocked"):
props.Set("org.bluez.Device1", "Blocked", value)
sys.exit(0)
+if (args[0] == "batteries"):
+ if (len(args) < 2):
+ print("Need address parameter")
+ else:
+ BATT_INTERFACE = "org.bluez.Battery1"
+ objs = bluezutils.get_managed_objects()
+ device = bluezutils.find_device(args[1], options.dev_id)
+ batteries = [path for path, ifaces in objs.iteritems()
+ if (lambda obj: obj and obj.get("Device") ==
+ device.object_path)(ifaces.get(BATT_INTERFACE))]
+ for batt in batteries:
+ print(batt)
+ sys.exit(0)
+
print("Unknown command")
sys.exit(1)
--
1.7.9.5
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH 3/9] battery: Add GATT Battery Client Service skeleton
2013-01-07 19:40 [PATCH 0/9] Battery Profile implementation Paulo Borges
2013-01-07 19:40 ` [PATCH 1/9] battery: Add generic device battery documentation Paulo Borges
2013-01-07 19:40 ` [PATCH 2/9] battery: Implement Generic device battery Paulo Borges
@ 2013-01-07 19:40 ` Paulo Borges
2013-01-07 19:40 ` [PATCH 4/9] battery: Discover Characteristic Descriptors Paulo Borges
` (5 subsequent siblings)
8 siblings, 0 replies; 11+ messages in thread
From: Paulo Borges @ 2013-01-07 19:40 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Chen Ganir
From: Chen Ganir <chen.ganir@ti.com>
Add support for the Battery Service Gatt Client side. Implement
the basic skeleton.
---
Makefile.plugins | 3 +
lib/uuid.h | 2 +
profiles/battery/battery.c | 214 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 219 insertions(+)
create mode 100644 profiles/battery/battery.c
diff --git a/Makefile.plugins b/Makefile.plugins
index faab011..33694b1 100644
--- a/Makefile.plugins
+++ b/Makefile.plugins
@@ -79,6 +79,9 @@ builtin_sources += profiles/scanparam/scan.c
builtin_modules += deviceinfo
builtin_sources += profiles/deviceinfo/deviceinfo.c
+builtin_modules += battery
+builtin_sources += profiles/battery/battery.c
+
if EXPERIMENTAL
builtin_modules += alert
builtin_sources += profiles/alert/server.c
diff --git a/lib/uuid.h b/lib/uuid.h
index 1e8188a..8736749 100644
--- a/lib/uuid.h
+++ b/lib/uuid.h
@@ -56,6 +56,8 @@ extern "C" {
#define PNPID_UUID "00002a50-0000-1000-8000-00805f9b34fb"
#define DEVICE_INFORMATION_UUID "0000180a-0000-1000-8000-00805f9b34fb"
+#define BATTERY_SERVICE_UUID "0000180f-0000-1000-8000-00805f9b34fb"
+
#define GATT_UUID "00001801-0000-1000-8000-00805f9b34fb"
#define IMMEDIATE_ALERT_UUID "00001802-0000-1000-8000-00805f9b34fb"
#define LINK_LOSS_UUID "00001803-0000-1000-8000-00805f9b34fb"
diff --git a/profiles/battery/battery.c b/profiles/battery/battery.c
new file mode 100644
index 0000000..73c06ff
--- /dev/null
+++ b/profiles/battery/battery.c
@@ -0,0 +1,214 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2012 Texas Instruments, 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <bluetooth/uuid.h>
+#include <stdbool.h>
+
+#include "adapter.h"
+#include "device.h"
+#include "profile.h"
+#include "plugin.h"
+#include "attrib/att.h"
+#include "attrib/gattrib.h"
+#include "attrib/gatt.h"
+#include "attio.h"
+#include "log.h"
+
+struct battery {
+ struct btd_device *dev; /* Device reference */
+ GAttrib *attrib; /* GATT connection */
+ guint attioid; /* Att watcher id */
+ struct att_range *svc_range; /* Battery range */
+ GSList *chars; /* Characteristics */
+};
+
+struct characteristic {
+ struct gatt_char attr; /* Characteristic */
+ struct battery *batt; /* Parent Battery Service */
+};
+
+static GSList *servers;
+
+static gint cmp_device(gconstpointer a, gconstpointer b)
+{
+ const struct battery *batt = a;
+ const struct btd_device *dev = b;
+
+ if (dev == batt->dev)
+ return 0;
+
+ return -1;
+}
+
+static void battery_free(gpointer user_data)
+{
+ struct battery *batt = user_data;
+
+ if (batt->chars != NULL)
+ g_slist_free_full(batt->chars, g_free);
+
+ if (batt->attioid > 0)
+ btd_device_remove_attio_callback(batt->dev, batt->attioid);
+
+ if (batt->attrib != NULL)
+ g_attrib_unref(batt->attrib);
+
+ btd_device_unref(batt->dev);
+ g_free(batt);
+}
+
+static void configure_battery_cb(GSList *characteristics, guint8 status,
+ gpointer user_data)
+{
+ struct battery *batt = user_data;
+ GSList *l;
+
+ if (status != 0) {
+ error("Discover Battery characteristics: %s",
+ att_ecode2str(status));
+ return;
+ }
+
+ for (l = characteristics; l; l = l->next) {
+ struct gatt_char *c = l->data;
+ struct characteristic *ch;
+
+ ch = g_new0(struct characteristic, 1);
+ ch->attr.handle = c->handle;
+ ch->attr.properties = c->properties;
+ ch->attr.value_handle = c->value_handle;
+ memcpy(ch->attr.uuid, c->uuid, MAX_LEN_UUID_STR + 1);
+ ch->batt = batt;
+
+ batt->chars = g_slist_append(batt->chars, ch);
+ }
+}
+
+static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
+{
+ struct battery *batt = user_data;
+
+ batt->attrib = g_attrib_ref(attrib);
+
+ if (batt->chars == NULL) {
+ gatt_discover_char(batt->attrib, batt->svc_range->start,
+ batt->svc_range->end, NULL,
+ configure_battery_cb, batt);
+ }
+}
+
+static void attio_disconnected_cb(gpointer user_data)
+{
+ struct battery *batt = user_data;
+
+ g_attrib_unref(batt->attrib);
+ batt->attrib = NULL;
+}
+
+static gint primary_uuid_cmp(gconstpointer a, gconstpointer b)
+{
+ const struct gatt_primary *prim = a;
+ const char *uuid = b;
+
+ return g_strcmp0(prim->uuid, uuid);
+}
+
+static int battery_register(struct btd_device *device)
+{
+ struct battery *batt;
+ struct gatt_primary *prim;
+ GSList *primaries, *l;
+
+ primaries = btd_device_get_primaries(device);
+
+ while ((l = g_slist_find_custom(primaries, BATTERY_SERVICE_UUID,
+ primary_uuid_cmp))) {
+ prim = l->data;
+
+ batt = g_new0(struct battery, 1);
+ batt->dev = btd_device_ref(device);
+
+ batt->svc_range = g_new0(struct att_range, 1);
+ batt->svc_range->start = prim->range.start;
+ batt->svc_range->end = prim->range.end;
+
+ servers = g_slist_prepend(servers, batt);
+
+ batt->attioid = btd_device_add_attio_callback(device,
+ attio_connected_cb, attio_disconnected_cb, batt);
+
+ primaries = g_slist_remove(primaries, prim);
+ }
+
+ return 0;
+}
+
+static void battery_unregister(struct btd_device *device)
+{
+ struct battery *batt;
+ GSList *l;
+
+ while ((l = g_slist_find_custom(servers, device, cmp_device))) {
+ batt = l->data;
+ servers = g_slist_remove(servers, batt);
+
+ battery_free(batt);
+ }
+}
+
+static int battery_driver_probe(struct btd_profile *p,
+ struct btd_device *device,
+ GSList *uuids)
+{
+ return battery_register(device);
+}
+
+static void battery_driver_remove(struct btd_profile *p,
+ struct btd_device *device)
+{
+ battery_unregister(device);
+}
+
+static struct btd_profile battery_profile = {
+ .name = "battery",
+ .remote_uuids = BTD_UUIDS(BATTERY_SERVICE_UUID),
+ .device_probe = battery_driver_probe,
+ .device_remove = battery_driver_remove
+};
+
+static int battery_manager_init(void)
+{
+ return btd_profile_register(&battery_profile);
+}
+
+static void battery_manager_exit(void)
+{
+ btd_profile_unregister(&battery_profile);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(battery, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+ battery_manager_init, battery_manager_exit)
--
1.7.9.5
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH 4/9] battery: Discover Characteristic Descriptors
2013-01-07 19:40 [PATCH 0/9] Battery Profile implementation Paulo Borges
` (2 preceding siblings ...)
2013-01-07 19:40 ` [PATCH 3/9] battery: Add GATT Battery Client Service skeleton Paulo Borges
@ 2013-01-07 19:40 ` Paulo Borges
2013-01-07 19:41 ` [PATCH 5/9] battery: Get Battery ID Paulo Borges
` (4 subsequent siblings)
8 siblings, 0 replies; 11+ messages in thread
From: Paulo Borges @ 2013-01-07 19:40 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Chen Ganir
From: Chen Ganir <chen.ganir@ti.com>
Discover all characteristic descriptors, and build a descriptor
list. Presentation Format Descriptor and Client Characteristic
Configuration descriptors are searched.
---
profiles/battery/battery.c | 72 +++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 71 insertions(+), 1 deletion(-)
diff --git a/profiles/battery/battery.c b/profiles/battery/battery.c
index 73c06ff..8e7e4c5 100644
--- a/profiles/battery/battery.c
+++ b/profiles/battery/battery.c
@@ -49,6 +49,13 @@ struct battery {
struct characteristic {
struct gatt_char attr; /* Characteristic */
struct battery *batt; /* Parent Battery Service */
+ GSList *desc; /* Descriptors */
+};
+
+struct descriptor {
+ struct characteristic *ch; /* Parent Characteristic */
+ uint16_t handle; /* Descriptor Handle */
+ bt_uuid_t uuid; /* UUID */
};
static GSList *servers;
@@ -64,12 +71,21 @@ static gint cmp_device(gconstpointer a, gconstpointer b)
return -1;
}
+static void char_free(gpointer user_data)
+{
+ struct characteristic *c = user_data;
+
+ g_slist_free_full(c->desc, g_free);
+
+ g_free(c);
+}
+
static void battery_free(gpointer user_data)
{
struct battery *batt = user_data;
if (batt->chars != NULL)
- g_slist_free_full(batt->chars, g_free);
+ g_slist_free_full(batt->chars, char_free);
if (batt->attioid > 0)
btd_device_remove_attio_callback(batt->dev, batt->attioid);
@@ -78,9 +94,48 @@ static void battery_free(gpointer user_data)
g_attrib_unref(batt->attrib);
btd_device_unref(batt->dev);
+ g_free(batt->svc_range);
g_free(batt);
}
+static void discover_desc_cb(guint8 status, const guint8 *pdu, guint16 len,
+ gpointer user_data)
+{
+ struct characteristic *ch = user_data;
+ struct att_data_list *list;
+ uint8_t format;
+ int i;
+
+ if (status != 0) {
+ error("Discover all characteristic descriptors failed [%s]: %s",
+ ch->attr.uuid, att_ecode2str(status));
+ return;
+ }
+
+ list = dec_find_info_resp(pdu, len, &format);
+ if (list == NULL)
+ return;
+
+ for (i = 0; i < list->num; i++) {
+ struct descriptor *desc;
+ uint8_t *value;
+
+ value = list->data[i];
+ desc = g_new0(struct descriptor, 1);
+ desc->handle = att_get_u16(value);
+ desc->ch = ch;
+
+ if (format == 0x01)
+ desc->uuid = att_get_uuid16(&value[2]);
+ else
+ desc->uuid = att_get_uuid128(&value[2]);
+
+ ch->desc = g_slist_append(ch->desc, desc);
+ }
+
+ att_data_list_free(list);
+}
+
static void configure_battery_cb(GSList *characteristics, guint8 status,
gpointer user_data)
{
@@ -96,6 +151,7 @@ static void configure_battery_cb(GSList *characteristics, guint8 status,
for (l = characteristics; l; l = l->next) {
struct gatt_char *c = l->data;
struct characteristic *ch;
+ uint16_t start, end;
ch = g_new0(struct characteristic, 1);
ch->attr.handle = c->handle;
@@ -105,6 +161,20 @@ static void configure_battery_cb(GSList *characteristics, guint8 status,
ch->batt = batt;
batt->chars = g_slist_append(batt->chars, ch);
+
+ start = c->value_handle + 1;
+
+ if (l->next != NULL) {
+ struct gatt_char *c = l->next->data;
+ if (start == c->handle)
+ continue;
+ end = c->handle - 1;
+ } else if (c->value_handle != batt->svc_range->end)
+ end = batt->svc_range->end;
+ else
+ continue;
+
+ gatt_find_info(batt->attrib, start, end, discover_desc_cb, ch);
}
}
--
1.7.9.5
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH 5/9] battery: Get Battery ID
2013-01-07 19:40 [PATCH 0/9] Battery Profile implementation Paulo Borges
` (3 preceding siblings ...)
2013-01-07 19:40 ` [PATCH 4/9] battery: Discover Characteristic Descriptors Paulo Borges
@ 2013-01-07 19:41 ` Paulo Borges
2013-01-07 19:41 ` [PATCH 6/9] battery: Add Battery to device Paulo Borges
` (3 subsequent siblings)
8 siblings, 0 replies; 11+ messages in thread
From: Paulo Borges @ 2013-01-07 19:41 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Chen Ganir
From: Chen Ganir <chen.ganir@ti.com>
Read the battery level format characteristic descriptor to get the
unique namespace and description values.
---
lib/uuid.h | 1 +
profiles/battery/battery.c | 53 ++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 54 insertions(+)
diff --git a/lib/uuid.h b/lib/uuid.h
index 8736749..3abdd44 100644
--- a/lib/uuid.h
+++ b/lib/uuid.h
@@ -57,6 +57,7 @@ extern "C" {
#define DEVICE_INFORMATION_UUID "0000180a-0000-1000-8000-00805f9b34fb"
#define BATTERY_SERVICE_UUID "0000180f-0000-1000-8000-00805f9b34fb"
+#define BATTERY_LEVEL_UUID "00002a19-0000-1000-8000-00805f9b34fb"
#define GATT_UUID "00001801-0000-1000-8000-00805f9b34fb"
#define IMMEDIATE_ALERT_UUID "00001802-0000-1000-8000-00805f9b34fb"
diff --git a/profiles/battery/battery.c b/profiles/battery/battery.c
index 8e7e4c5..8bbfaf7 100644
--- a/profiles/battery/battery.c
+++ b/profiles/battery/battery.c
@@ -50,6 +50,8 @@ struct characteristic {
struct gatt_char attr; /* Characteristic */
struct battery *batt; /* Parent Battery Service */
GSList *desc; /* Descriptors */
+ uint8_t ns; /* Battery Namespace */
+ uint16_t description; /* Battery description */
};
struct descriptor {
@@ -98,6 +100,53 @@ static void battery_free(gpointer user_data)
g_free(batt);
}
+static void batterylevel_presentation_format_desc_cb(guint8 status,
+ const guint8 *pdu, guint16 len,
+ gpointer user_data)
+{
+ struct descriptor *desc = user_data;
+ uint8_t value[ATT_MAX_VALUE_LEN];
+ int vlen;
+
+ if (status != 0) {
+ error("Presentation Format desc read failed: %s",
+ att_ecode2str(status));
+ return;
+ }
+
+ vlen = dec_read_resp(pdu, len, value, sizeof(value));
+ if (vlen < 0) {
+ error("Presentation Format desc read failed: Protocol error");
+ return;
+ }
+
+ if (vlen < 7) {
+ error("Presentation Format desc read failed: Invalid range");
+ return;
+ }
+
+ desc->ch->ns = value[4];
+ desc->ch->description = att_get_u16(&value[5]);
+}
+
+static void process_batterylevel_desc(struct descriptor *desc)
+{
+ struct characteristic *ch = desc->ch;
+ char uuidstr[MAX_LEN_UUID_STR];
+ bt_uuid_t btuuid;
+
+ bt_uuid16_create(&btuuid, GATT_CHARAC_FMT_UUID);
+
+ if (bt_uuid_cmp(&desc->uuid, &btuuid) == 0) {
+ gatt_read_char(ch->batt->attrib, desc->handle,
+ batterylevel_presentation_format_desc_cb, desc);
+ return;
+ }
+
+ bt_uuid_to_string(&desc->uuid, uuidstr, MAX_LEN_UUID_STR);
+ DBG("Ignored descriptor %s characteristic %s", uuidstr, ch->attr.uuid);
+}
+
static void discover_desc_cb(guint8 status, const guint8 *pdu, guint16 len,
gpointer user_data)
{
@@ -131,6 +180,7 @@ static void discover_desc_cb(guint8 status, const guint8 *pdu, guint16 len,
desc->uuid = att_get_uuid128(&value[2]);
ch->desc = g_slist_append(ch->desc, desc);
+ process_batterylevel_desc(desc);
}
att_data_list_free(list);
@@ -153,6 +203,9 @@ static void configure_battery_cb(GSList *characteristics, guint8 status,
struct characteristic *ch;
uint16_t start, end;
+ if (g_strcmp0(c->uuid, BATTERY_LEVEL_UUID) != 0)
+ continue;
+
ch = g_new0(struct characteristic, 1);
ch->attr.handle = c->handle;
ch->attr.properties = c->properties;
--
1.7.9.5
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH 6/9] battery: Add Battery to device
2013-01-07 19:40 [PATCH 0/9] Battery Profile implementation Paulo Borges
` (4 preceding siblings ...)
2013-01-07 19:41 ` [PATCH 5/9] battery: Get Battery ID Paulo Borges
@ 2013-01-07 19:41 ` Paulo Borges
2013-01-07 19:41 ` [PATCH 7/9] battery: Read Battery level characteristic Paulo Borges
` (2 subsequent siblings)
8 siblings, 0 replies; 11+ messages in thread
From: Paulo Borges @ 2013-01-07 19:41 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Chen Ganir
From: Chen Ganir <chen.ganir@ti.com>
Add/Remove battery from device
---
profiles/battery/battery.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/profiles/battery/battery.c b/profiles/battery/battery.c
index 8bbfaf7..fef83d9 100644
--- a/profiles/battery/battery.c
+++ b/profiles/battery/battery.c
@@ -47,6 +47,7 @@ struct battery {
};
struct characteristic {
+ struct btd_battery *devbatt; /* device_battery pointer */
struct gatt_char attr; /* Characteristic */
struct battery *batt; /* Parent Battery Service */
GSList *desc; /* Descriptors */
@@ -79,6 +80,8 @@ static void char_free(gpointer user_data)
g_slist_free_full(c->desc, g_free);
+ btd_device_remove_battery(c->devbatt);
+
g_free(c);
}
@@ -217,6 +220,8 @@ static void configure_battery_cb(GSList *characteristics, guint8 status,
start = c->value_handle + 1;
+ ch->devbatt = btd_device_add_battery(ch->batt->dev);
+
if (l->next != NULL) {
struct gatt_char *c = l->next->data;
if (start == c->handle)
--
1.7.9.5
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH 7/9] battery: Read Battery level characteristic
2013-01-07 19:40 [PATCH 0/9] Battery Profile implementation Paulo Borges
` (5 preceding siblings ...)
2013-01-07 19:41 ` [PATCH 6/9] battery: Add Battery to device Paulo Borges
@ 2013-01-07 19:41 ` Paulo Borges
2013-01-07 19:41 ` [PATCH 8/9] battery: Add support for notifications Paulo Borges
2013-01-07 19:41 ` [PATCH 9/9] battery: Support persistent battery level Paulo Borges
8 siblings, 0 replies; 11+ messages in thread
From: Paulo Borges @ 2013-01-07 19:41 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Chen Ganir
From: Chen Ganir <chen.ganir@ti.com>
Implement support for reading the battery level characteristic on
connection establishment.
---
profiles/battery/battery.c | 86 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 86 insertions(+)
diff --git a/profiles/battery/battery.c b/profiles/battery/battery.c
index fef83d9..186bb1c 100644
--- a/profiles/battery/battery.c
+++ b/profiles/battery/battery.c
@@ -53,6 +53,7 @@ struct characteristic {
GSList *desc; /* Descriptors */
uint8_t ns; /* Battery Namespace */
uint16_t description; /* Battery description */
+ uint8_t level; /* Battery level */
};
struct descriptor {
@@ -103,6 +104,78 @@ static void battery_free(gpointer user_data)
g_free(batt);
}
+static void read_batterylevel_cb(guint8 status, const guint8 *pdu, guint16 len,
+ gpointer user_data)
+{
+ struct characteristic *ch = user_data;
+ uint8_t value[ATT_MAX_VALUE_LEN];
+ int vlen;
+
+ if (status != 0) {
+ error("Failed to read Battery Level:%s", att_ecode2str(status));
+ return;
+ }
+
+ vlen = dec_read_resp(pdu, len, value, sizeof(value));
+ if (vlen < 0) {
+ error("Failed to read Battery Level: Protocol error");
+ return;
+ }
+
+ if (vlen != 1) {
+ error("Failed to read Battery Level: Wrong pdu len");
+ return;
+ }
+
+ ch->level = value[0];
+ btd_device_set_battery_opt(ch->devbatt, BATTERY_OPT_LEVEL, ch->level,
+ BATTERY_OPT_INVALID);
+}
+
+static void process_batteryservice_char(struct characteristic *ch)
+{
+ if (g_strcmp0(ch->attr.uuid, BATTERY_LEVEL_UUID) == 0) {
+ gatt_read_char(ch->batt->attrib, ch->attr.value_handle,
+ read_batterylevel_cb, ch);
+ }
+}
+
+static gint device_battery_cmp(gconstpointer a, gconstpointer b)
+{
+ const struct characteristic *ch = a;
+ const struct btd_battery *batt = b;
+
+ if (batt == ch->devbatt)
+ return 0;
+
+ return -1;
+}
+
+static struct characteristic *find_battery_char(struct btd_battery *db)
+{
+ GSList *l, *b;
+
+ for (l = servers; l != NULL; l = g_slist_next(l)) {
+ struct battery *batt = l->data;
+
+ b = g_slist_find_custom(batt->chars, db, device_battery_cmp);
+ if (b && batt->attrib)
+ return b->data;
+ }
+
+ return NULL;
+}
+
+static void batterylevel_refresh_cb(struct btd_battery *batt)
+{
+ struct characteristic *ch;
+
+ ch = find_battery_char(batt);
+
+ if (ch)
+ process_batteryservice_char(ch);
+}
+
static void batterylevel_presentation_format_desc_cb(guint8 status,
const guint8 *pdu, guint16 len,
gpointer user_data)
@@ -220,8 +293,15 @@ static void configure_battery_cb(GSList *characteristics, guint8 status,
start = c->value_handle + 1;
+ process_batteryservice_char(ch);
+
ch->devbatt = btd_device_add_battery(ch->batt->dev);
+ btd_device_set_battery_opt(ch->devbatt,
+ BATTERY_OPT_REFRESH_FUNC,
+ batterylevel_refresh_cb,
+ BATTERY_OPT_INVALID);
+
if (l->next != NULL) {
struct gatt_char *c = l->next->data;
if (start == c->handle)
@@ -246,6 +326,12 @@ static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
gatt_discover_char(batt->attrib, batt->svc_range->start,
batt->svc_range->end, NULL,
configure_battery_cb, batt);
+ } else {
+ GSList *l;
+ for (l = batt->chars; l; l = l->next) {
+ struct characteristic *c = l->data;
+ process_batteryservice_char(c);
+ }
}
}
--
1.7.9.5
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH 8/9] battery: Add support for notifications
2013-01-07 19:40 [PATCH 0/9] Battery Profile implementation Paulo Borges
` (6 preceding siblings ...)
2013-01-07 19:41 ` [PATCH 7/9] battery: Read Battery level characteristic Paulo Borges
@ 2013-01-07 19:41 ` Paulo Borges
2013-01-07 19:41 ` [PATCH 9/9] battery: Support persistent battery level Paulo Borges
8 siblings, 0 replies; 11+ messages in thread
From: Paulo Borges @ 2013-01-07 19:41 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Chen Ganir
From: Chen Ganir <chen.ganir@ti.com>
Add support for emitting PropertyChanged when a battery level
characteristic notification is sent from the peer device.
---
profiles/battery/battery.c | 79 +++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 78 insertions(+), 1 deletion(-)
diff --git a/profiles/battery/battery.c b/profiles/battery/battery.c
index 186bb1c..2ef4f6b 100644
--- a/profiles/battery/battery.c
+++ b/profiles/battery/battery.c
@@ -54,6 +54,8 @@ struct characteristic {
uint8_t ns; /* Battery Namespace */
uint16_t description; /* Battery description */
uint8_t level; /* Battery level */
+ gboolean can_notify; /* Char can notify flag */
+ guint notifyid;
};
struct descriptor {
@@ -79,6 +81,9 @@ static void char_free(gpointer user_data)
{
struct characteristic *c = user_data;
+ if (c->notifyid && c->batt->attrib != NULL)
+ g_attrib_unregister(c->batt->attrib, c->notifyid);
+
g_slist_free_full(c->desc, g_free);
btd_device_remove_battery(c->devbatt);
@@ -140,6 +145,44 @@ static void process_batteryservice_char(struct characteristic *ch)
}
}
+static void proc_batterylevel(struct characteristic *c, const uint8_t *pdu,
+ uint16_t len, gboolean final)
+{
+ if (!pdu) {
+ error("Battery level notification: Invalid pdu length");
+ return;
+ }
+
+ c->level = pdu[1];
+
+ btd_device_set_battery_opt(c->devbatt, BATTERY_OPT_LEVEL, c->level,
+ BATTERY_OPT_INVALID);
+}
+
+static void notif_handler(const uint8_t *pdu, uint16_t len, gpointer user_data)
+{
+ struct characteristic *ch = user_data;
+
+ proc_batterylevel(ch, pdu, len, FALSE);
+}
+
+static void batterylevel_enable_notify_cb(guint8 status, const guint8 *pdu,
+ guint16 len, gpointer user_data)
+{
+ struct characteristic *ch = user_data;
+ struct battery *batt = ch->batt;
+
+ if (status != 0) {
+ error("Could not enable batt level notification.");
+ ch->can_notify = FALSE;
+ process_batteryservice_char(ch);
+ }
+
+ ch->notifyid = g_attrib_register(batt->attrib,
+ ATT_OP_HANDLE_NOTIFY, ch->attr.value_handle,
+ notif_handler, ch, NULL);
+}
+
static gint device_battery_cmp(gconstpointer a, gconstpointer b)
{
const struct characteristic *ch = a;
@@ -176,6 +219,21 @@ static void batterylevel_refresh_cb(struct btd_battery *batt)
process_batteryservice_char(ch);
}
+static void enable_battery_notification(struct characteristic *ch,
+ uint16_t handle)
+{
+ uint8_t atval[2];
+ uint16_t val;
+
+ val = GATT_CLIENT_CHARAC_CFG_NOTIF_BIT;
+
+ ch->can_notify = TRUE;
+
+ att_put_u16(val, atval);
+ gatt_write_char(ch->batt->attrib, handle, atval, 2,
+ batterylevel_enable_notify_cb, ch);
+}
+
static void batterylevel_presentation_format_desc_cb(guint8 status,
const guint8 *pdu, guint16 len,
gpointer user_data)
@@ -211,6 +269,14 @@ static void process_batterylevel_desc(struct descriptor *desc)
char uuidstr[MAX_LEN_UUID_STR];
bt_uuid_t btuuid;
+ bt_uuid16_create(&btuuid, GATT_CLIENT_CHARAC_CFG_UUID);
+
+ if (bt_uuid_cmp(&desc->uuid, &btuuid) == 0 && g_strcmp0(ch->attr.uuid,
+ BATTERY_LEVEL_UUID) == 0) {
+ enable_battery_notification(ch, desc->handle);
+ return;
+ }
+
bt_uuid16_create(&btuuid, GATT_CHARAC_FMT_UUID);
if (bt_uuid_cmp(&desc->uuid, &btuuid) == 0) {
@@ -330,7 +396,8 @@ static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
GSList *l;
for (l = batt->chars; l; l = l->next) {
struct characteristic *c = l->data;
- process_batteryservice_char(c);
+ if (!c->can_notify)
+ process_batteryservice_char(c);
}
}
}
@@ -338,6 +405,16 @@ static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
static void attio_disconnected_cb(gpointer user_data)
{
struct battery *batt = user_data;
+ GSList *l;
+
+ for (l = batt->chars; l; l = l->next) {
+ struct characteristic *c = l->data;
+
+ if (c->notifyid) {
+ g_attrib_unregister(batt->attrib, c->notifyid);
+ c->notifyid = 0;
+ }
+ }
g_attrib_unref(batt->attrib);
batt->attrib = NULL;
--
1.7.9.5
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH 9/9] battery: Support persistent battery level
2013-01-07 19:40 [PATCH 0/9] Battery Profile implementation Paulo Borges
` (7 preceding siblings ...)
2013-01-07 19:41 ` [PATCH 8/9] battery: Add support for notifications Paulo Borges
@ 2013-01-07 19:41 ` Paulo Borges
8 siblings, 0 replies; 11+ messages in thread
From: Paulo Borges @ 2013-01-07 19:41 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Paulo Borges
Store battery level when read, and use the level from storage
when connecting, to reduce GATT traffic.
---
profiles/battery/battery.c | 137 +++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 135 insertions(+), 2 deletions(-)
diff --git a/profiles/battery/battery.c b/profiles/battery/battery.c
index 2ef4f6b..e4a6e53 100644
--- a/profiles/battery/battery.c
+++ b/profiles/battery/battery.c
@@ -27,6 +27,8 @@
#include <glib.h>
#include <bluetooth/uuid.h>
#include <stdbool.h>
+#include <sys/file.h>
+#include <stdlib.h>
#include "adapter.h"
#include "device.h"
@@ -37,6 +39,11 @@
#include "attrib/gatt.h"
#include "attio.h"
#include "log.h"
+#include "storage.h"
+
+#define BATTERY_FILE "batteries"
+#define BATTERY_GROUP_FORMAT "%04X"
+#define BATTERY_KEY_LEVEL "Level"
struct battery {
struct btd_device *dev; /* Device reference */
@@ -77,10 +84,133 @@ static gint cmp_device(gconstpointer a, gconstpointer b)
return -1;
}
+static gboolean store_battery_char(struct characteristic *chr)
+{
+ GKeyFile *key_file;
+ char filename[PATH_MAX + 1];
+ char adapter_addr[18];
+ char device_addr[18];
+ char group[5];
+ char *str;
+ gsize length = 0;
+
+ ba2str(adapter_get_address(device_get_adapter(chr->batt->dev)),
+ adapter_addr);
+ ba2str(device_get_address(chr->batt->dev), device_addr);
+
+ snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/" BATTERY_FILE,
+ adapter_addr, device_addr);
+
+ snprintf(group, sizeof(group), BATTERY_GROUP_FORMAT, chr->attr.handle);
+
+ key_file = g_key_file_new();
+ g_key_file_load_from_file(key_file, filename, 0, NULL);
+
+ g_key_file_set_integer(key_file, group, BATTERY_KEY_LEVEL, chr->level);
+
+ create_file(filename, S_IRUSR | S_IWUSR);
+
+ str = g_key_file_to_data(key_file, &length, NULL);
+ g_file_set_contents(filename, str, length, NULL);
+
+ g_free(str);
+ g_key_file_free(key_file);
+
+ return TRUE;
+}
+
+static int read_battery_char(struct characteristic *chr)
+{
+ GKeyFile *key_file;
+ char filename[PATH_MAX + 1];
+ char adapter_addr[18];
+ char device_addr[18];
+ char group[5];
+ int chr_value;
+
+ ba2str(adapter_get_address(device_get_adapter(chr->batt->dev)),
+ adapter_addr);
+ ba2str(device_get_address(chr->batt->dev), device_addr);
+
+ snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/" BATTERY_FILE,
+ adapter_addr, device_addr);
+
+ snprintf(group, sizeof(group), BATTERY_GROUP_FORMAT, chr->attr.handle);
+
+ key_file = g_key_file_new();
+ if (!g_key_file_load_from_file(key_file, filename, 0, NULL)) {
+ g_key_file_free(key_file);
+ return 0;
+ }
+
+ chr_value = g_key_file_get_integer(key_file, group, BATTERY_KEY_LEVEL,
+ NULL);
+ g_key_file_free(key_file);
+
+ return chr_value;
+}
+
+static void del_battery_char(struct characteristic *chr)
+{
+ GKeyFile *key_file;
+ char filename[PATH_MAX + 1];
+ char adapter_addr[18];
+ char device_addr[18];
+ char group[5];
+ char *str;
+ gsize length = 0;
+
+ ba2str(adapter_get_address(device_get_adapter(chr->batt->dev)),
+ adapter_addr);
+ ba2str(device_get_address(chr->batt->dev), device_addr);
+
+ snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/" BATTERY_FILE,
+ adapter_addr, device_addr);
+
+ snprintf(group, sizeof(group), BATTERY_GROUP_FORMAT, chr->attr.handle);
+
+ key_file = g_key_file_new();
+ if (!g_key_file_load_from_file(key_file, filename, 0, NULL)) {
+ g_key_file_free(key_file);
+ return;
+ }
+
+ g_key_file_remove_key(key_file, group, BATTERY_KEY_LEVEL, NULL);
+
+ create_file(filename, S_IRUSR | S_IWUSR);
+
+ str = g_key_file_to_data(key_file, &length, NULL);
+ g_file_set_contents(filename, str, length, NULL);
+
+ g_free(str);
+ g_key_file_free(key_file);
+}
+
+static gboolean read_battery_level_value(struct characteristic *chr)
+{
+ int level;
+
+ if (!chr)
+ return FALSE;
+
+ level = read_battery_char(chr);
+ if (!level)
+ return FALSE;
+
+ chr->level = level;
+
+ btd_device_set_battery_opt(chr->devbatt, BATTERY_OPT_LEVEL, chr->level,
+ BATTERY_OPT_INVALID);
+
+ return TRUE;
+}
+
static void char_free(gpointer user_data)
{
struct characteristic *c = user_data;
+ del_battery_char(c);
+
if (c->notifyid && c->batt->attrib != NULL)
g_attrib_unregister(c->batt->attrib, c->notifyid);
@@ -135,6 +265,8 @@ static void read_batterylevel_cb(guint8 status, const guint8 *pdu, guint16 len,
ch->level = value[0];
btd_device_set_battery_opt(ch->devbatt, BATTERY_OPT_LEVEL, ch->level,
BATTERY_OPT_INVALID);
+
+ store_battery_char(ch);
}
static void process_batteryservice_char(struct characteristic *ch)
@@ -359,7 +491,8 @@ static void configure_battery_cb(GSList *characteristics, guint8 status,
start = c->value_handle + 1;
- process_batteryservice_char(ch);
+ if (!read_battery_level_value(ch))
+ process_batteryservice_char(ch);
ch->devbatt = btd_device_add_battery(ch->batt->dev);
@@ -396,7 +529,7 @@ static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
GSList *l;
for (l = batt->chars; l; l = l->next) {
struct characteristic *c = l->data;
- if (!c->can_notify)
+ if (!read_battery_level_value(c) && !c->can_notify)
process_batteryservice_char(c);
}
}
--
1.7.9.5
^ permalink raw reply related [flat|nested] 11+ messages in thread