* [RFC 06/18] cyclingspeed: Read Sensor Location characteristic value
From: Andrzej Kaczmarek @ 2012-10-25 14:43 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Andrzej Kaczmarek
In-Reply-To: <1351176228-5789-1-git-send-email-andrzej.kaczmarek@tieto.com>
---
profiles/cyclingspeed/cyclingspeed.c | 32 +++++++++++++++++++++++++++++++-
1 file changed, 31 insertions(+), 1 deletion(-)
diff --git a/profiles/cyclingspeed/cyclingspeed.c b/profiles/cyclingspeed/cyclingspeed.c
index fa97911..67963d1 100644
--- a/profiles/cyclingspeed/cyclingspeed.c
+++ b/profiles/cyclingspeed/cyclingspeed.c
@@ -56,6 +56,8 @@ struct csc {
uint16_t controlpoint_val_handle;
uint16_t feature;
+ gboolean has_location;
+ uint8_t location;
};
struct characteristic {
@@ -145,6 +147,33 @@ static void read_feature_cb(guint8 status, const guint8 *pdu,
csc->feature = att_get_u16(value);
}
+static void read_location_cb(guint8 status, const guint8 *pdu,
+ guint16 len, gpointer user_data)
+{
+ struct csc *csc = user_data;
+ uint8_t value;
+ ssize_t vlen;
+
+ if (status) {
+ error("Sensor Location read failed: %s", att_ecode2str(status));
+ return;
+ }
+
+ vlen = dec_read_resp(pdu, len, &value, sizeof(value));
+ if (vlen < 0) {
+ error("Protocol error");
+ return;
+ }
+
+ if (vlen != sizeof(value)) {
+ error("Invalid value length for Sensor Location");
+ return;
+ }
+
+ csc->has_location = TRUE;
+ csc->location = value;
+}
+
static void discover_desc_cb(guint8 status, const guint8 *pdu,
guint16 len, gpointer user_data)
{
@@ -237,7 +266,8 @@ static void discover_char_cb(GSList *chars, guint8 status, gpointer user_data)
feature_val_handle = c->value_handle;
} else if (g_strcmp0(c->uuid, SENSOR_LOCATION_UUID) == 0) {
DBG("Sensor Location supported");
- /* TODO: read characterictic value */
+ gatt_read_char(csc->attrib, c->value_handle,
+ read_location_cb, csc);
} else if (g_strcmp0(c->uuid, SC_CONTROL_POINT_UUID) == 0) {
DBG("SC Control Point supported");
csc->controlpoint_val_handle = c->value_handle;
--
1.8.0
^ permalink raw reply related
* [RFC 05/18] cyclingspeed: Read CSC Feature characteristic value
From: Andrzej Kaczmarek @ 2012-10-25 14:43 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Andrzej Kaczmarek
In-Reply-To: <1351176228-5789-1-git-send-email-andrzej.kaczmarek@tieto.com>
---
profiles/cyclingspeed/cyclingspeed.c | 35 ++++++++++++++++++++++++++++++++++-
1 file changed, 34 insertions(+), 1 deletion(-)
diff --git a/profiles/cyclingspeed/cyclingspeed.c b/profiles/cyclingspeed/cyclingspeed.c
index 0191f1b..fa97911 100644
--- a/profiles/cyclingspeed/cyclingspeed.c
+++ b/profiles/cyclingspeed/cyclingspeed.c
@@ -54,6 +54,8 @@ struct csc {
uint16_t measurement_ccc_handle;
uint16_t controlpoint_val_handle;
+
+ uint16_t feature;
};
struct characteristic {
@@ -117,6 +119,32 @@ static void destroy_csc(gpointer user_data)
g_free(csc);
}
+static void read_feature_cb(guint8 status, const guint8 *pdu,
+ guint16 len, gpointer user_data)
+{
+ struct csc *csc = user_data;
+ uint8_t value[2];
+ ssize_t vlen;
+
+ if (status) {
+ error("CSC Feature read failed: %s", att_ecode2str(status));
+ return;
+ }
+
+ vlen = dec_read_resp(pdu, len, value, sizeof(value));
+ if (vlen < 0) {
+ error("Protocol error");
+ return;
+ }
+
+ if (vlen != sizeof(value)) {
+ error("Invalid value length for CSC Feature");
+ return;
+ }
+
+ csc->feature = att_get_u16(value);
+}
+
static void discover_desc_cb(guint8 status, const guint8 *pdu,
guint16 len, gpointer user_data)
{
@@ -190,6 +218,7 @@ static void discover_desc(struct csc *csc, struct gatt_char *c,
static void discover_char_cb(GSList *chars, guint8 status, gpointer user_data)
{
struct csc *csc = user_data;
+ uint16_t feature_val_handle = 0;
if (status) {
error("Discover CSCS characteristics: %s",
@@ -205,7 +234,7 @@ static void discover_char_cb(GSList *chars, guint8 status, gpointer user_data)
if (g_strcmp0(c->uuid, CSC_MEASUREMENT_UUID) == 0) {
discover_desc(csc, c, c_next);
} else if (g_strcmp0(c->uuid, CSC_FEATURE_UUID) == 0) {
- /* TODO: read characterictic value */
+ feature_val_handle = c->value_handle;
} else if (g_strcmp0(c->uuid, SENSOR_LOCATION_UUID) == 0) {
DBG("Sensor Location supported");
/* TODO: read characterictic value */
@@ -215,6 +244,10 @@ static void discover_char_cb(GSList *chars, guint8 status, gpointer user_data)
discover_desc(csc, c, c_next);
}
}
+
+ if (feature_val_handle > 0)
+ gatt_read_char(csc->attrib, feature_val_handle,
+ read_feature_cb, csc);
}
static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
--
1.8.0
^ permalink raw reply related
* [RFC 04/18] cyclingspeed: Discover characteristics CCC
From: Andrzej Kaczmarek @ 2012-10-25 14:43 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Andrzej Kaczmarek
In-Reply-To: <1351176228-5789-1-git-send-email-andrzej.kaczmarek@tieto.com>
---
profiles/cyclingspeed/cyclingspeed.c | 82 +++++++++++++++++++++++++++++++++++-
1 file changed, 80 insertions(+), 2 deletions(-)
diff --git a/profiles/cyclingspeed/cyclingspeed.c b/profiles/cyclingspeed/cyclingspeed.c
index 5744891..0191f1b 100644
--- a/profiles/cyclingspeed/cyclingspeed.c
+++ b/profiles/cyclingspeed/cyclingspeed.c
@@ -52,9 +52,15 @@ struct csc {
struct att_range *svc_range;
+ uint16_t measurement_ccc_handle;
uint16_t controlpoint_val_handle;
};
+struct characteristic {
+ struct csc *csc;
+ char uuid[MAX_LEN_UUID_STR + 1];
+};
+
static GSList *csc_adapters = NULL;
static gint cmp_adapter(gconstpointer a, gconstpointer b)
@@ -111,6 +117,76 @@ static void destroy_csc(gpointer user_data)
g_free(csc);
}
+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 = NULL;
+ uint8_t format;
+ int i;
+
+ if (status != 0) {
+ error("Discover %s descriptors failed: %s", ch->uuid,
+ att_ecode2str(status));
+ goto done;
+ }
+
+ list = dec_find_info_resp(pdu, len, &format);
+ if (list == NULL)
+ goto done;
+
+ if (format != ATT_FIND_INFO_RESP_FMT_16BIT)
+ goto done;
+
+ for (i = 0; i < list->num; i++) {
+ uint8_t *value;
+ uint16_t handle, uuid;
+
+ value = list->data[i];
+ handle = att_get_u16(value);
+ uuid = att_get_u16(value + 2);
+
+ if (uuid != GATT_CLIENT_CHARAC_CFG_UUID)
+ continue;
+
+ if (g_strcmp0(ch->uuid, CSC_MEASUREMENT_UUID) == 0)
+ ch->csc->measurement_ccc_handle = handle;
+
+ /* We only want CCC, can break here */
+ break;
+ }
+
+done:
+ if (list)
+ att_data_list_free(list);
+ g_free(ch);
+}
+
+static void discover_desc(struct csc *csc, struct gatt_char *c,
+ struct gatt_char *c_next)
+{
+ struct characteristic *ch;
+ uint16_t start, end;
+
+ start = c->value_handle + 1;
+
+ if (c_next != NULL) {
+ if (start == c_next->handle)
+ return;
+ end = c_next->handle - 1;
+ } else if (c->value_handle != csc->svc_range->end) {
+ end = csc->svc_range->end;
+ } else {
+ return;
+ }
+
+ ch = g_new0(struct characteristic, 1);
+ ch->csc = csc;
+ memcpy(ch->uuid, c->uuid, sizeof(c->uuid));
+
+ gatt_find_info(csc->attrib, start, end, discover_desc_cb, ch);
+}
+
static void discover_char_cb(GSList *chars, guint8 status, gpointer user_data)
{
struct csc *csc = user_data;
@@ -123,9 +199,11 @@ static void discover_char_cb(GSList *chars, guint8 status, gpointer user_data)
for (; chars; chars = chars->next) {
struct gatt_char *c = chars->data;
+ struct gatt_char *c_next =
+ (chars->next ? chars->next->data : NULL);
if (g_strcmp0(c->uuid, CSC_MEASUREMENT_UUID) == 0) {
- /* TODO: discover CCC handle */
+ discover_desc(csc, c, c_next);
} else if (g_strcmp0(c->uuid, CSC_FEATURE_UUID) == 0) {
/* TODO: read characterictic value */
} else if (g_strcmp0(c->uuid, SENSOR_LOCATION_UUID) == 0) {
@@ -134,7 +212,7 @@ static void discover_char_cb(GSList *chars, guint8 status, gpointer user_data)
} else if (g_strcmp0(c->uuid, SC_CONTROL_POINT_UUID) == 0) {
DBG("SC Control Point supported");
csc->controlpoint_val_handle = c->value_handle;
- /* TODO: discover CCC handle */
+ discover_desc(csc, c, c_next);
}
}
}
--
1.8.0
^ permalink raw reply related
* [RFC 03/18] cyclingspeed: Discover CSCS characteristics
From: Andrzej Kaczmarek @ 2012-10-25 14:43 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Andrzej Kaczmarek
In-Reply-To: <1351176228-5789-1-git-send-email-andrzej.kaczmarek@tieto.com>
---
lib/uuid.h | 4 ++++
profiles/cyclingspeed/cyclingspeed.c | 43 +++++++++++++++++++++++++++++++++++-
profiles/cyclingspeed/cyclingspeed.h | 2 +-
profiles/cyclingspeed/manager.c | 19 +++++++++++++++-
4 files changed, 65 insertions(+), 3 deletions(-)
diff --git a/lib/uuid.h b/lib/uuid.h
index ebc6d27..def4fea 100644
--- a/lib/uuid.h
+++ b/lib/uuid.h
@@ -75,6 +75,10 @@ extern "C" {
#define MEASUREMENT_INTERVAL_UUID "00002a21-0000-1000-8000-00805f9b34fb"
#define CYCLING_SC_UUID "00001816-0000-1000-8000-00805f9b34fb"
+#define CSC_MEASUREMENT_UUID "00002a5b-0000-1000-8000-00805f9b34fb"
+#define CSC_FEATURE_UUID "00002a5c-0000-1000-8000-00805f9b34fb"
+#define SENSOR_LOCATION_UUID "00002a5d-0000-1000-8000-00805f9b34fb"
+#define SC_CONTROL_POINT_UUID "00002a55-0000-1000-8000-00805f9b34fb"
#define RFCOMM_UUID_STR "00000003-0000-1000-8000-00805f9b34fb"
diff --git a/profiles/cyclingspeed/cyclingspeed.c b/profiles/cyclingspeed/cyclingspeed.c
index 4d98810..5744891 100644
--- a/profiles/cyclingspeed/cyclingspeed.c
+++ b/profiles/cyclingspeed/cyclingspeed.c
@@ -49,6 +49,10 @@ struct csc {
GAttrib *attrib;
guint attioid;
+
+ struct att_range *svc_range;
+
+ uint16_t controlpoint_val_handle;
};
static GSList *csc_adapters = NULL;
@@ -103,9 +107,38 @@ static void destroy_csc(gpointer user_data)
g_attrib_unref(csc->attrib);
btd_device_unref(csc->dev);
+ g_free(csc->svc_range);
g_free(csc);
}
+static void discover_char_cb(GSList *chars, guint8 status, gpointer user_data)
+{
+ struct csc *csc = user_data;
+
+ if (status) {
+ error("Discover CSCS characteristics: %s",
+ att_ecode2str(status));
+ return;
+ }
+
+ for (; chars; chars = chars->next) {
+ struct gatt_char *c = chars->data;
+
+ if (g_strcmp0(c->uuid, CSC_MEASUREMENT_UUID) == 0) {
+ /* TODO: discover CCC handle */
+ } else if (g_strcmp0(c->uuid, CSC_FEATURE_UUID) == 0) {
+ /* TODO: read characterictic value */
+ } else if (g_strcmp0(c->uuid, SENSOR_LOCATION_UUID) == 0) {
+ DBG("Sensor Location supported");
+ /* TODO: read characterictic value */
+ } else if (g_strcmp0(c->uuid, SC_CONTROL_POINT_UUID) == 0) {
+ DBG("SC Control Point supported");
+ csc->controlpoint_val_handle = c->value_handle;
+ /* TODO: discover CCC handle */
+ }
+ }
+}
+
static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
{
struct csc *csc = user_data;
@@ -113,6 +146,10 @@ static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
DBG("");
csc->attrib = g_attrib_ref(attrib);
+
+ gatt_discover_char(csc->attrib, csc->svc_range->start,
+ csc->svc_range->end, NULL,
+ discover_char_cb, csc);
}
static void attio_disconnected_cb(gpointer user_data)
@@ -150,7 +187,7 @@ void csc_adapter_unregister(struct btd_adapter *adapter)
destroy_csc_adapter(cadapter);
}
-int csc_device_register(struct btd_device *device)
+int csc_device_register(struct btd_device *device, struct gatt_primary *prim)
{
struct btd_adapter *adapter;
struct csc_adapter *cadapter;
@@ -166,6 +203,10 @@ int csc_device_register(struct btd_device *device)
csc->dev = btd_device_ref(device);
csc->cadapter = cadapter;
+ csc->svc_range = g_new0(struct att_range, 1);
+ csc->svc_range->start = prim->range.start;
+ csc->svc_range->end = prim->range.end;
+
cadapter->devices = g_slist_prepend(cadapter->devices, csc);
csc->attioid = btd_device_add_attio_callback(device, attio_connected_cb,
diff --git a/profiles/cyclingspeed/cyclingspeed.h b/profiles/cyclingspeed/cyclingspeed.h
index b83fa9e..3400d5f 100644
--- a/profiles/cyclingspeed/cyclingspeed.h
+++ b/profiles/cyclingspeed/cyclingspeed.h
@@ -22,5 +22,5 @@
int csc_adapter_register(struct btd_adapter *adapter);
void csc_adapter_unregister(struct btd_adapter *adapter);
-int csc_device_register(struct btd_device *device);
+int csc_device_register(struct btd_device *device, struct gatt_primary *prim);
void csc_device_unregister(struct btd_device *device);
diff --git a/profiles/cyclingspeed/manager.c b/profiles/cyclingspeed/manager.c
index e5f7553..c147af2 100644
--- a/profiles/cyclingspeed/manager.c
+++ b/profiles/cyclingspeed/manager.c
@@ -34,6 +34,14 @@
#include "cyclingspeed.h"
#include "manager.h"
+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 csc_adapter_probe(struct btd_profile *p, struct btd_adapter *adapter)
{
return csc_adapter_register(adapter);
@@ -48,7 +56,16 @@ static void csc_adapter_remove(struct btd_profile *p,
static int csc_device_probe(struct btd_profile *p,
struct btd_device *device, GSList *uuids)
{
- return csc_device_register(device);
+ GSList *primaries;
+ GSList *l;
+
+ primaries = btd_device_get_primaries(device);
+
+ l = g_slist_find_custom(primaries, CYCLING_SC_UUID, primary_uuid_cmp);
+ if (l == NULL)
+ return -EINVAL;
+
+ return csc_device_register(device, l->data);
}
static void csc_device_remove(struct btd_profile *p,
--
1.8.0
^ permalink raw reply related
* [RFC 02/18] cyclingspeed: Add attio callbacks
From: Andrzej Kaczmarek @ 2012-10-25 14:43 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Andrzej Kaczmarek
In-Reply-To: <1351176228-5789-1-git-send-email-andrzej.kaczmarek@tieto.com>
---
profiles/cyclingspeed/cyclingspeed.c | 36 ++++++++++++++++++++++++++++++++++++
1 file changed, 36 insertions(+)
diff --git a/profiles/cyclingspeed/cyclingspeed.c b/profiles/cyclingspeed/cyclingspeed.c
index 9674a94..4d98810 100644
--- a/profiles/cyclingspeed/cyclingspeed.c
+++ b/profiles/cyclingspeed/cyclingspeed.c
@@ -31,6 +31,11 @@
#include "adapter.h"
#include "device.h"
+#include "gattrib.h"
+#include "att.h"
+#include "gatt.h"
+#include "attio.h"
+#include "log.h"
#include "cyclingspeed.h"
struct csc_adapter {
@@ -41,6 +46,9 @@ struct csc_adapter {
struct csc {
struct btd_device *dev;
struct csc_adapter *cadapter;
+
+ GAttrib *attrib;
+ guint attioid;
};
static GSList *csc_adapters = NULL;
@@ -88,10 +96,35 @@ static void destroy_csc(gpointer user_data)
{
struct csc *csc = user_data;
+ if (csc->attioid > 0)
+ btd_device_remove_attio_callback(csc->dev, csc->attioid);
+
+ if (csc->attrib != NULL)
+ g_attrib_unref(csc->attrib);
+
btd_device_unref(csc->dev);
g_free(csc);
}
+static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
+{
+ struct csc *csc = user_data;
+
+ DBG("");
+
+ csc->attrib = g_attrib_ref(attrib);
+}
+
+static void attio_disconnected_cb(gpointer user_data)
+{
+ struct csc *csc = user_data;
+
+ DBG("");
+
+ g_attrib_unref(csc->attrib);
+ csc->attrib = NULL;
+}
+
int csc_adapter_register(struct btd_adapter *adapter)
{
struct csc_adapter *cadapter;
@@ -135,6 +168,9 @@ int csc_device_register(struct btd_device *device)
cadapter->devices = g_slist_prepend(cadapter->devices, csc);
+ csc->attioid = btd_device_add_attio_callback(device, attio_connected_cb,
+ attio_disconnected_cb, csc);
+
return 0;
}
--
1.8.0
^ permalink raw reply related
* [RFC 01/18] cyclingspeed: Add CSC profile plugin skeleton
From: Andrzej Kaczmarek @ 2012-10-25 14:43 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Andrzej Kaczmarek
In-Reply-To: <1351176228-5789-1-git-send-email-andrzej.kaczmarek@tieto.com>
This patch adds stub profile driver plugin for CSC profile.
---
Makefile.am | 9 +-
lib/uuid.h | 2 +
profiles/cyclingspeed/cyclingspeed.c | 163 +++++++++++++++++++++++++++++++++++
profiles/cyclingspeed/cyclingspeed.h | 26 ++++++
profiles/cyclingspeed/main.c | 53 ++++++++++++
profiles/cyclingspeed/manager.c | 79 +++++++++++++++++
profiles/cyclingspeed/manager.h | 24 ++++++
7 files changed, 354 insertions(+), 2 deletions(-)
create mode 100644 profiles/cyclingspeed/cyclingspeed.c
create mode 100644 profiles/cyclingspeed/cyclingspeed.h
create mode 100644 profiles/cyclingspeed/main.c
create mode 100644 profiles/cyclingspeed/manager.c
create mode 100644 profiles/cyclingspeed/manager.h
diff --git a/Makefile.am b/Makefile.am
index 35b1520..9bdfd71 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -212,7 +212,7 @@ endif
if GATTMODULES
builtin_modules += thermometer alert time gatt_example proximity deviceinfo \
- gatt scanparam heartrate
+ gatt scanparam heartrate cyclingspeed
builtin_sources += profiles/thermometer/main.c \
profiles/thermometer/manager.h \
profiles/thermometer/manager.c \
@@ -251,7 +251,12 @@ builtin_sources += profiles/thermometer/main.c \
profiles/heartrate/manager.c \
profiles/heartrate/manager.h \
profiles/heartrate/heartrate.c \
- profiles/heartrate/heartrate.h
+ profiles/heartrate/heartrate.h \
+ profiles/cyclingspeed/main.c \
+ profiles/cyclingspeed/manager.c \
+ profiles/cyclingspeed/manager.h \
+ profiles/cyclingspeed/cyclingspeed.c \
+ profiles/cyclingspeed/cyclingspeed.h
endif
diff --git a/lib/uuid.h b/lib/uuid.h
index a812309..ebc6d27 100644
--- a/lib/uuid.h
+++ b/lib/uuid.h
@@ -74,6 +74,8 @@ extern "C" {
#define INTERMEDIATE_TEMPERATURE_UUID "00002a1e-0000-1000-8000-00805f9b34fb"
#define MEASUREMENT_INTERVAL_UUID "00002a21-0000-1000-8000-00805f9b34fb"
+#define CYCLING_SC_UUID "00001816-0000-1000-8000-00805f9b34fb"
+
#define RFCOMM_UUID_STR "00000003-0000-1000-8000-00805f9b34fb"
#define HDP_UUID "00001400-0000-1000-8000-00805f9b34fb"
diff --git a/profiles/cyclingspeed/cyclingspeed.c b/profiles/cyclingspeed/cyclingspeed.c
new file mode 100644
index 0000000..9674a94
--- /dev/null
+++ b/profiles/cyclingspeed/cyclingspeed.c
@@ -0,0 +1,163 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2012 Tieto Poland
+ *
+ * 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 <errno.h>
+#include <stdbool.h>
+#include <glib.h>
+#include <bluetooth/uuid.h>
+
+#include "adapter.h"
+#include "device.h"
+#include "cyclingspeed.h"
+
+struct csc_adapter {
+ struct btd_adapter *adapter;
+ GSList *devices; /* list of registered devices */
+};
+
+struct csc {
+ struct btd_device *dev;
+ struct csc_adapter *cadapter;
+};
+
+static GSList *csc_adapters = NULL;
+
+static gint cmp_adapter(gconstpointer a, gconstpointer b)
+{
+ const struct csc_adapter *cadapter = a;
+ const struct btd_adapter *adapter = b;
+
+ if (adapter == cadapter->adapter)
+ return 0;
+
+ return -1;
+}
+
+static gint cmp_device(gconstpointer a, gconstpointer b)
+{
+ const struct csc *csc = a;
+ const struct btd_device *dev = b;
+
+ if (dev == csc->dev)
+ return 0;
+
+ return -1;
+}
+
+static struct csc_adapter *find_csc_adapter(struct btd_adapter *adapter)
+{
+ GSList *l = g_slist_find_custom(csc_adapters, adapter, cmp_adapter);
+
+ if (!l)
+ return NULL;
+
+ return l->data;
+}
+
+static void destroy_csc_adapter(gpointer user_data)
+{
+ struct csc_adapter *cadapter = user_data;
+
+ g_free(cadapter);
+}
+
+static void destroy_csc(gpointer user_data)
+{
+ struct csc *csc = user_data;
+
+ btd_device_unref(csc->dev);
+ g_free(csc);
+}
+
+int csc_adapter_register(struct btd_adapter *adapter)
+{
+ struct csc_adapter *cadapter;
+
+ cadapter = g_new0(struct csc_adapter, 1);
+ cadapter->adapter = adapter;
+
+ csc_adapters = g_slist_prepend(csc_adapters, cadapter);
+
+ return 0;
+}
+
+void csc_adapter_unregister(struct btd_adapter *adapter)
+{
+ struct csc_adapter *cadapter;
+
+ cadapter = find_csc_adapter(adapter);
+ if (cadapter == NULL)
+ return;
+
+ csc_adapters = g_slist_remove(csc_adapters, cadapter);
+
+ destroy_csc_adapter(cadapter);
+}
+
+int csc_device_register(struct btd_device *device)
+{
+ struct btd_adapter *adapter;
+ struct csc_adapter *cadapter;
+ struct csc *csc;
+
+ adapter = device_get_adapter(device);
+
+ cadapter = find_csc_adapter(adapter);
+ if (cadapter == NULL)
+ return -1;
+
+ csc = g_new0(struct csc, 1);
+ csc->dev = btd_device_ref(device);
+ csc->cadapter = cadapter;
+
+ cadapter->devices = g_slist_prepend(cadapter->devices, csc);
+
+ return 0;
+}
+
+void csc_device_unregister(struct btd_device *device)
+{
+ struct btd_adapter *adapter;
+ struct csc_adapter *cadapter;
+ struct csc *csc;
+ GSList *l;
+
+ adapter = device_get_adapter(device);
+
+ cadapter = find_csc_adapter(adapter);
+ if (cadapter == NULL)
+ return;
+
+ l = g_slist_find_custom(cadapter->devices, device, cmp_device);
+ if (l == NULL)
+ return;
+
+ csc = l->data;
+
+ cadapter->devices = g_slist_remove(cadapter->devices, csc);
+
+ destroy_csc(csc);
+}
diff --git a/profiles/cyclingspeed/cyclingspeed.h b/profiles/cyclingspeed/cyclingspeed.h
new file mode 100644
index 0000000..b83fa9e
--- /dev/null
+++ b/profiles/cyclingspeed/cyclingspeed.h
@@ -0,0 +1,26 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2012 Tieto Poland
+ *
+ * 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
+ *
+ */
+
+int csc_adapter_register(struct btd_adapter *adapter);
+void csc_adapter_unregister(struct btd_adapter *adapter);
+int csc_device_register(struct btd_device *device);
+void csc_device_unregister(struct btd_device *device);
diff --git a/profiles/cyclingspeed/main.c b/profiles/cyclingspeed/main.c
new file mode 100644
index 0000000..c2ef765
--- /dev/null
+++ b/profiles/cyclingspeed/main.c
@@ -0,0 +1,53 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2012 Tieto Poland
+ *
+ * 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 <stdint.h>
+#include <glib.h>
+#include <errno.h>
+
+#include "plugin.h"
+#include "manager.h"
+#include "hcid.h"
+#include "log.h"
+
+static int cyclingspeed_init(void)
+{
+ if (!main_opts.gatt_enabled) {
+ DBG("GATT is disabled");
+ return -ENOTSUP;
+ }
+
+ return cyclingspeed_manager_init();
+}
+
+static void cyclingspeed_exit(void)
+{
+ cyclingspeed_manager_exit();
+}
+
+BLUETOOTH_PLUGIN_DEFINE(cyclingspeed, VERSION,
+ BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+ cyclingspeed_init, cyclingspeed_exit)
diff --git a/profiles/cyclingspeed/manager.c b/profiles/cyclingspeed/manager.c
new file mode 100644
index 0000000..e5f7553
--- /dev/null
+++ b/profiles/cyclingspeed/manager.c
@@ -0,0 +1,79 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2012 Tieto Poland
+ *
+ * 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
+ *
+ */
+
+#include <gdbus.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <bluetooth/uuid.h>
+
+#include "adapter.h"
+#include "device.h"
+#include "profile.h"
+#include "att.h"
+#include "gattrib.h"
+#include "gatt.h"
+#include "cyclingspeed.h"
+#include "manager.h"
+
+static int csc_adapter_probe(struct btd_profile *p, struct btd_adapter *adapter)
+{
+ return csc_adapter_register(adapter);
+}
+
+static void csc_adapter_remove(struct btd_profile *p,
+ struct btd_adapter *adapter)
+{
+ csc_adapter_unregister(adapter);
+}
+
+static int csc_device_probe(struct btd_profile *p,
+ struct btd_device *device, GSList *uuids)
+{
+ return csc_device_register(device);
+}
+
+static void csc_device_remove(struct btd_profile *p,
+ struct btd_device *device)
+{
+ csc_device_unregister(device);
+}
+
+static struct btd_profile cscp_profile = {
+ .name = "Cycling Speed and Cadence GATT Driver",
+ .remote_uuids = BTD_UUIDS(CYCLING_SC_UUID),
+
+ .adapter_probe = csc_adapter_probe,
+ .adapter_remove = csc_adapter_remove,
+
+ .device_probe = csc_device_probe,
+ .device_remove = csc_device_remove,
+};
+
+int cyclingspeed_manager_init(void)
+{
+ return btd_profile_register(&cscp_profile);
+}
+
+void cyclingspeed_manager_exit(void)
+{
+ btd_profile_unregister(&cscp_profile);
+}
diff --git a/profiles/cyclingspeed/manager.h b/profiles/cyclingspeed/manager.h
new file mode 100644
index 0000000..7d25ae4
--- /dev/null
+++ b/profiles/cyclingspeed/manager.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2012 Tieto Poland
+ *
+ * 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
+ *
+ */
+
+int cyclingspeed_manager_init(void);
+void cyclingspeed_manager_exit(void);
--
1.8.0
^ permalink raw reply related
* [RFC 00/18] CSCP plugin
From: Andrzej Kaczmarek @ 2012-10-25 14:43 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Andrzej Kaczmarek
Hi,
Here are patches to add cyclingspeed plugin which is CSC profile
implementation. All features for Collector Role are implemented.
It's not yet fully tested so sending this as RFC. Reason for this
is that PTS testcases for CSCP are buggy and I'm not able to verify
my implementation reliably. I already filled few issues and hopefully
they will be addressed soon.
Anyway, it works fine with Wahoo BlueSC (but it does not support all
features).
Comments are welcome.
Andrzej Kaczmarek (18):
cyclingspeed: Add CSC profile plugin skeleton
cyclingspeed: Add attio callbacks
cyclingspeed: Discover CSCS characteristics
cyclingspeed: Discover characteristics CCC
cyclingspeed: Read CSC Feature characteristic value
cyclingspeed: Read Sensor Location characteristic value
cyclingspeed: Add CyclingSpeedManager interface
cyclingspeed: Add support to enable measurement notifications
cyclingspeed: Process measurement notifications
cyclingspeed: Add DBus.Properties for org.bluez.CyclingSpeed
interface
doc: Remove Get-/SetProperties from CSC API document
cyclingspeed: Add stub to use SC Control Point
cyclingspeed: Add support for Request Supported Sensor Locations
cyclingspeed: Add support for Update Sensor Location
cyclingspeed: Add support for Set Cumulative Value
core: Add CyclingSpeedWatcher interface to default policy
test: Add cyclingspeed test script
doc: Rename cycling API to cyclingspeed
Makefile.am | 9 +-
Makefile.tools | 2 +-
doc/cycling-api.txt | 118 ----
doc/cyclingspeed-api.txt | 100 +++
lib/uuid.h | 6 +
profiles/cyclingspeed/cyclingspeed.c | 1225 ++++++++++++++++++++++++++++++++++
profiles/cyclingspeed/cyclingspeed.h | 26 +
profiles/cyclingspeed/main.c | 53 ++
profiles/cyclingspeed/manager.c | 96 +++
profiles/cyclingspeed/manager.h | 24 +
src/bluetooth.conf | 1 +
test/test-cyclingspeed | 115 ++++
12 files changed, 1654 insertions(+), 121 deletions(-)
delete mode 100644 doc/cycling-api.txt
create mode 100644 doc/cyclingspeed-api.txt
create mode 100644 profiles/cyclingspeed/cyclingspeed.c
create mode 100644 profiles/cyclingspeed/cyclingspeed.h
create mode 100644 profiles/cyclingspeed/main.c
create mode 100644 profiles/cyclingspeed/manager.c
create mode 100644 profiles/cyclingspeed/manager.h
create mode 100755 test/test-cyclingspeed
--
1.8.0
^ permalink raw reply
* Re: [PATCH 5/6] Bluetooth: mgmt: Add support for switching to LE peripheral mode
From: Vinicius Costa Gomes @ 2012-10-25 14:31 UTC (permalink / raw)
To: Anderson Lizardo, Marcel Holtmann, linux-bluetooth
In-Reply-To: <20121025120407.GA3274@x220>
Hi,
On 15:04 Thu 25 Oct, Johan Hedberg wrote:
> Hi,
>
> On Thu, Oct 25, 2012, Johan Hedberg wrote:
> > This will need some more thinking. We could add a new mgmt command for
> > that, or if you wanna go crazy why not map this to the L2CAP socket
> > interface's connect() system call. I.e. if LE GAP is in peripheral mode
> > instead of doing HCI_LE_Create_Connection or just failing with "not
> > allowed" a socket connect() would simply trigger directed advertising to
> > the device in question and deliver the successful connection in the same
> > way as a central role triggered connect would do (unless we time out
> > waiting for the remote device to connect). Now that I think of this
> > second option it actually sounds quite natural and not so crazy after
> > all :)
>
> There's on problem with this though: we'd still be undirected
> connectable between doing mgmt_set_le(peripheral) and issuing the socket
> connect(). So maybe we might need to introduce a mgmt_set_le_connectable
> command and a "le-connectable" setting after all (that could only be set
> in peripheral mode).
Why not sending undirected connectable events when there's an active
listen()?
>
> Johan
Cheers,
--
Vinicius
^ permalink raw reply
* [PATCH BlueZ 11/11] test: Add support for using external player
From: Luiz Augusto von Dentz @ 2012-10-25 13:59 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1351173554-28039-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This add support for passing a device player object which is then
registered as a local one in a loopback fashion.
---
test/simple-player | 80 +++++++++++++++++++++++++++++++++++++++++-------------
1 file changed, 61 insertions(+), 19 deletions(-)
diff --git a/test/simple-player b/test/simple-player
index f8751d6..9e72f3e 100755
--- a/test/simple-player
+++ b/test/simple-player
@@ -9,6 +9,50 @@ import dbus.mainloop.glib
import gobject
class Player(dbus.service.Object):
+ properties = None
+ metadata = None
+
+ def set_object(self, obj = None):
+ if obj != None:
+ bus = dbus.SystemBus()
+ mp = dbus.Interface(bus.get_object("org.bluez", obj),
+ "org.bluez.MediaPlayer")
+
+ self.properties = mp.GetProperties()
+ self.metadata = mp.GetTrack()
+
+ bus.add_signal_receiver(self.property_changed,
+ path = obj,
+ dbus_interface = "org.bluez.MediaPlayer",
+ signal_name = "PropertyChanged")
+
+ bus.add_signal_receiver(self.track_changed,
+ path = obj,
+ dbus_interface ="org.bluez.MediaPlayer",
+ signal_name = "TrackChanged")
+ else:
+ self.properties = dbus.Dictionary({
+ "Equalizer" : "off",
+ "Repeat" : "off",
+ "Shuffle" : "off",
+ "Scan" : "off",
+ "Status" : "playing",
+ "Position" : dbus.UInt32(0) },
+ signature="sv")
+
+ self.metadata = dbus.Dictionary({
+ "Title" : "Title",
+ "Artist" : "Artist",
+ "Album" : "Album",
+ "Genre" : "Genre",
+ "NumberOfTracks" : dbus.UInt32(10),
+ "Track" : dbus.UInt32(1),
+ "Duration" : dbus.UInt32(10000) },
+ signature="sv")
+ handler = InputHandler(self)
+ gobject.io_add_watch(sys.stdin, gobject.IO_IN,
+ handler.handle)
+
@dbus.service.method("org.bluez.MediaPlayer",
in_signature="", out_signature="")
def Release(self):
@@ -42,6 +86,16 @@ class Player(dbus.service.Object):
def help(self, func):
help(self.__class__.__dict__[func])
+ def property_changed(self, setting, value):
+ print("property_changed(%s, %s)" % (setting, value))
+
+ self.PropertyChanged(setting, value)
+
+ def track_changed(self, metadata):
+ print("track_changed(%s)" % (metadata))
+
+ self.TrackChanged(metadata)
+
class InputHandler:
commands = { 'TrackChanged': '(metadata)',
'PropertyChanged': '(key, value)',
@@ -79,7 +133,6 @@ class InputHandler:
return True
-
if __name__ == '__main__':
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
@@ -99,27 +152,16 @@ if __name__ == '__main__':
player = Player(bus, path)
mainloop = gobject.MainLoop()
- properties = dbus.Dictionary({ "Equalizer" : "off",
- "Repeat" : "off",
- "Shuffle" : "off",
- "Scan" : "off",
- "Status" : "playing",
- "Position" : dbus.UInt32(0) }, signature="sv")
-
- metadata = dbus.Dictionary({ "Title" : "Title",
- "Artist" : "Artist",
- "Album" : "Album",
- "Genre" : "Genre",
- "NumberOfTracks" : dbus.UInt32(10),
- "Track" : dbus.UInt32(1),
- "Duration" : dbus.UInt32(10000) }, signature="sv")
+ if len(sys.argv) > 2:
+ player.set_object(sys.argv[2])
+ else:
+ player.set_object()
print('Register media player with:\n\tProperties: %s\n\tMetadata: %s' \
- % (properties, metadata))
+ % (player.properties, player.metadata))
- handler = InputHandler(player)
- gobject.io_add_watch(sys.stdin, gobject.IO_IN, handler.handle)
- media.RegisterPlayer(dbus.ObjectPath(path), properties, metadata)
+ media.RegisterPlayer(dbus.ObjectPath(path), player.properties,
+ player.metadata)
mainloop.run()
--
1.7.11.7
^ permalink raw reply related
* [PATCH BlueZ 10/11] test: Fix using Number instead of Track in mpris-player
From: Luiz Augusto von Dentz @ 2012-10-25 13:59 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1351173554-28039-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
---
test/mpris-player.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/test/mpris-player.c b/test/mpris-player.c
index a2c4cc6..13f5c85 100644
--- a/test/mpris-player.c
+++ b/test/mpris-player.c
@@ -338,7 +338,7 @@ static int parse_metadata_entry(DBusMessageIter *entry, const char *key,
dbus_message_iter_get_basic(&var, &value);
- dict_append_entry(metadata, "Number", DBUS_TYPE_UINT32,
+ dict_append_entry(metadata, "Track", DBUS_TYPE_UINT32,
&value);
}
--
1.7.11.7
^ permalink raw reply related
* [PATCH BlueZ 09/11] test: Fix using Number instead of Track in simple-player
From: Luiz Augusto von Dentz @ 2012-10-25 13:59 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1351173554-28039-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
---
test/simple-player | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/test/simple-player b/test/simple-player
index 70701da..f8751d6 100755
--- a/test/simple-player
+++ b/test/simple-player
@@ -111,7 +111,7 @@ if __name__ == '__main__':
"Album" : "Album",
"Genre" : "Genre",
"NumberOfTracks" : dbus.UInt32(10),
- "Number" : dbus.UInt32(1),
+ "Track" : dbus.UInt32(1),
"Duration" : dbus.UInt32(10000) }, signature="sv")
print('Register media player with:\n\tProperties: %s\n\tMetadata: %s' \
--
1.7.11.7
^ permalink raw reply related
* [PATCH BlueZ 08/11] AVRCP: Remove conversions inside media.c
From: Luiz Augusto von Dentz @ 2012-10-25 13:59 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1351173554-28039-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
The conversion should be done in avrcp.c as it is already doing for
controller players.
---
audio/avrcp.c | 369 +++++++++++++++++++++++++++++++++-------------------------
audio/avrcp.h | 9 +-
audio/media.c | 315 ++++++++-----------------------------------------
3 files changed, 267 insertions(+), 426 deletions(-)
diff --git a/audio/avrcp.c b/audio/avrcp.c
index 35fc249..6ed085b 100644
--- a/audio/avrcp.c
+++ b/audio/avrcp.c
@@ -435,12 +435,123 @@ static void set_company_id(uint8_t cid[3], const uint32_t cid_in)
cid[2] = cid_in;
}
+static const char *attr_to_str(uint8_t attr)
+{
+ switch (attr) {
+ case AVRCP_ATTRIBUTE_EQUALIZER:
+ return "Equalizer";
+ case AVRCP_ATTRIBUTE_REPEAT_MODE:
+ return "Repeat";
+ case AVRCP_ATTRIBUTE_SHUFFLE:
+ return "Shuffle";
+ case AVRCP_ATTRIBUTE_SCAN:
+ return "Scan";
+ }
+
+ return NULL;
+}
+
+static int attrval_to_val(uint8_t attr, const char *value)
+{
+ int ret;
+
+ switch (attr) {
+ case AVRCP_ATTRIBUTE_EQUALIZER:
+ if (!strcmp(value, "off"))
+ ret = AVRCP_EQUALIZER_OFF;
+ else if (!strcmp(value, "on"))
+ ret = AVRCP_EQUALIZER_ON;
+ else
+ ret = -EINVAL;
+
+ return ret;
+ case AVRCP_ATTRIBUTE_REPEAT_MODE:
+ if (!strcmp(value, "off"))
+ ret = AVRCP_REPEAT_MODE_OFF;
+ else if (!strcmp(value, "singletrack"))
+ ret = AVRCP_REPEAT_MODE_SINGLE;
+ else if (!strcmp(value, "alltracks"))
+ ret = AVRCP_REPEAT_MODE_ALL;
+ else if (!strcmp(value, "group"))
+ ret = AVRCP_REPEAT_MODE_GROUP;
+ else
+ ret = -EINVAL;
+
+ return ret;
+ case AVRCP_ATTRIBUTE_SHUFFLE:
+ if (!strcmp(value, "off"))
+ ret = AVRCP_SHUFFLE_OFF;
+ else if (!strcmp(value, "alltracks"))
+ ret = AVRCP_SHUFFLE_ALL;
+ else if (!strcmp(value, "group"))
+ ret = AVRCP_SHUFFLE_GROUP;
+ else
+ ret = -EINVAL;
+
+ return ret;
+ case AVRCP_ATTRIBUTE_SCAN:
+ if (!strcmp(value, "off"))
+ ret = AVRCP_SCAN_OFF;
+ else if (!strcmp(value, "alltracks"))
+ ret = AVRCP_SCAN_ALL;
+ else if (!strcmp(value, "group"))
+ ret = AVRCP_SCAN_GROUP;
+ else
+ ret = -EINVAL;
+
+ return ret;
+ }
+
+ return -EINVAL;
+}
+
+static int attr_to_val(const char *str)
+{
+ if (!strcasecmp(str, "Equalizer"))
+ return AVRCP_ATTRIBUTE_EQUALIZER;
+ else if (!strcasecmp(str, "Repeat"))
+ return AVRCP_ATTRIBUTE_REPEAT_MODE;
+ else if (!strcasecmp(str, "Shuffle"))
+ return AVRCP_ATTRIBUTE_SHUFFLE;
+ else if (!strcasecmp(str, "Scan"))
+ return AVRCP_ATTRIBUTE_SCAN;
+
+ return -EINVAL;
+}
+
static int player_get_setting(struct avrcp_player *player, uint8_t id)
{
+ const char *key;
+ const char *value;
+
if (player == NULL)
return -ENOENT;
- return player->cb->get_setting(id, player->user_data);
+ key = attr_to_str(id);
+ if (key == NULL)
+ return -EINVAL;
+
+ value = player->cb->get_setting(key, player->user_data);
+
+ return attrval_to_val(id, value);
+}
+
+static int play_status_to_val(const char *status)
+{
+ if (!strcasecmp(status, "stopped"))
+ return AVRCP_PLAY_STATUS_STOPPED;
+ else if (!strcasecmp(status, "playing"))
+ return AVRCP_PLAY_STATUS_PLAYING;
+ else if (!strcasecmp(status, "paused"))
+ return AVRCP_PLAY_STATUS_PAUSED;
+ else if (!strcasecmp(status, "forward-seek"))
+ return AVRCP_PLAY_STATUS_FWD_SEEK;
+ else if (!strcasecmp(status, "reverse-seek"))
+ return AVRCP_PLAY_STATUS_REV_SEEK;
+ else if (!strcasecmp(status, "error"))
+ return AVRCP_PLAY_STATUS_ERROR;
+
+ return -EINVAL;
}
void avrcp_player_event(struct avrcp_player *player, uint8_t id, void *data)
@@ -466,7 +577,7 @@ void avrcp_player_event(struct avrcp_player *player, uint8_t id, void *data)
switch (id) {
case AVRCP_EVENT_STATUS_CHANGED:
size = 2;
- pdu->params[1] = *((uint8_t *)data);
+ pdu->params[1] = play_status_to_val(data);
break;
case AVRCP_EVENT_TRACK_CHANGED:
@@ -483,9 +594,14 @@ void avrcp_player_event(struct avrcp_player *player, uint8_t id, void *data)
settings = data;
pdu->params[1] = g_list_length(settings);
for (; settings; settings = settings->next) {
- uint8_t attr = GPOINTER_TO_UINT(settings->data);
+ const char *key = settings->data;
+ int attr;
int val;
+ attr = attr_to_val(key);
+ if (attr < 0)
+ continue;
+
val = player_get_setting(player, attr);
if (val < 0)
continue;
@@ -522,12 +638,40 @@ void avrcp_player_event(struct avrcp_player *player, uint8_t id, void *data)
return;
}
-static const char *player_get_metadata(struct avrcp_player *player, uint32_t attr)
+static const char *metadata_to_str(uint32_t id)
+{
+ switch (id) {
+ case AVRCP_MEDIA_ATTRIBUTE_TITLE:
+ return "Title";
+ case AVRCP_MEDIA_ATTRIBUTE_ARTIST:
+ return "Artist";
+ case AVRCP_MEDIA_ATTRIBUTE_ALBUM:
+ return "Album";
+ case AVRCP_MEDIA_ATTRIBUTE_GENRE:
+ return "Genre";
+ case AVRCP_MEDIA_ATTRIBUTE_TRACK:
+ return "Track";
+ case AVRCP_MEDIA_ATTRIBUTE_N_TRACKS:
+ return "NumberOfTracks";
+ case AVRCP_MEDIA_ATTRIBUTE_DURATION:
+ return "Duration";
+ }
+
+ return NULL;
+}
+
+static const char *player_get_metadata(struct avrcp_player *player, uint32_t id)
{
+ const char *key;
+
+ key = metadata_to_str(id);
+ if (key == NULL)
+ return NULL;
+
if (player != NULL)
- return player->cb->get_metadata(attr, player->user_data);
+ return player->cb->get_metadata(key, player->user_data);
- if (attr == AVRCP_MEDIA_ATTRIBUTE_TITLE)
+ if (id == AVRCP_MEDIA_ATTRIBUTE_TITLE)
return "";
return NULL;
@@ -629,13 +773,66 @@ static gboolean session_abort_pending_pdu(struct avrcp *session)
return TRUE;
}
+static const char *attrval_to_str(uint8_t attr, uint8_t value)
+{
+ switch (attr) {
+ case AVRCP_ATTRIBUTE_EQUALIZER:
+ switch (value) {
+ case AVRCP_EQUALIZER_ON:
+ return "on";
+ case AVRCP_EQUALIZER_OFF:
+ return "off";
+ }
+
+ break;
+ case AVRCP_ATTRIBUTE_REPEAT_MODE:
+ switch (value) {
+ case AVRCP_REPEAT_MODE_OFF:
+ return "off";
+ case AVRCP_REPEAT_MODE_SINGLE:
+ return "singletrack";
+ case AVRCP_REPEAT_MODE_ALL:
+ return "alltracks";
+ case AVRCP_REPEAT_MODE_GROUP:
+ return "group";
+ }
+
+ break;
+ /* Shuffle and scan have the same values */
+ case AVRCP_ATTRIBUTE_SHUFFLE:
+ case AVRCP_ATTRIBUTE_SCAN:
+ switch (value) {
+ case AVRCP_SCAN_OFF:
+ return "off";
+ case AVRCP_SCAN_ALL:
+ return "alltracks";
+ case AVRCP_SCAN_GROUP:
+ return "group";
+ }
+
+ break;
+ }
+
+ return NULL;
+}
+
static int player_set_setting(struct avrcp_player *player, uint8_t id,
uint8_t val)
{
+ const char *key, *value;
+
+ key = attr_to_str(id);
+ if (key == NULL)
+ return -EINVAL;
+
+ value = attrval_to_str(id, val);
+ if (value == NULL)
+ return -EINVAL;
+
if (player == NULL)
return -ENOENT;
- return player->cb->set_setting(id, val, player->user_data);
+ return player->cb->set_setting(key, value, player->user_data);
}
static uint8_t avrcp_handle_get_capabilities(struct avrcp *session,
@@ -989,10 +1186,14 @@ static uint32_t player_get_duration(struct avrcp_player *player)
static uint8_t player_get_status(struct avrcp_player *player)
{
+ const char *value;
+
if (player == NULL)
return AVRCP_PLAY_STATUS_STOPPED;
- return player->cb->get_status(player->user_data);
+ value = player->cb->get_status(player->user_data);
+
+ return play_status_to_val(value);
}
static uint8_t avrcp_handle_get_play_status(struct avrcp *session,
@@ -1079,7 +1280,8 @@ static uint8_t avrcp_handle_register_notification(struct avrcp *session,
pdu->params[++len] = g_list_length(settings);
for (; settings; settings = settings->next) {
- uint8_t attr = GPOINTER_TO_UINT(settings->data);
+ const char *key = settings->data;
+ uint8_t attr = attr_to_val(key);
int val;
val = player_get_setting(player, attr);
@@ -1410,65 +1612,6 @@ static void avrcp_get_play_status(struct avrcp *session)
session);
}
-static const char *attrval_to_str(uint8_t attr, uint8_t value)
-{
- switch (attr) {
- case AVRCP_ATTRIBUTE_EQUALIZER:
- switch (value) {
- case AVRCP_EQUALIZER_ON:
- return "on";
- case AVRCP_EQUALIZER_OFF:
- return "off";
- }
-
- break;
- case AVRCP_ATTRIBUTE_REPEAT_MODE:
- switch (value) {
- case AVRCP_REPEAT_MODE_OFF:
- return "off";
- case AVRCP_REPEAT_MODE_SINGLE:
- return "singletrack";
- case AVRCP_REPEAT_MODE_ALL:
- return "alltracks";
- case AVRCP_REPEAT_MODE_GROUP:
- return "group";
- }
-
- break;
- /* Shuffle and scan have the same values */
- case AVRCP_ATTRIBUTE_SHUFFLE:
- case AVRCP_ATTRIBUTE_SCAN:
- switch (value) {
- case AVRCP_SCAN_OFF:
- return "off";
- case AVRCP_SCAN_ALL:
- return "alltracks";
- case AVRCP_SCAN_GROUP:
- return "group";
- }
-
- break;
- }
-
- return NULL;
-}
-
-static const char *attr_to_str(uint8_t attr)
-{
- switch (attr) {
- case AVRCP_ATTRIBUTE_EQUALIZER:
- return "Equalizer";
- case AVRCP_ATTRIBUTE_REPEAT_MODE:
- return "Repeat";
- case AVRCP_ATTRIBUTE_SHUFFLE:
- return "Shuffle";
- case AVRCP_ATTRIBUTE_SCAN:
- return "Scan";
- }
-
- return NULL;
-}
-
static gboolean avrcp_player_value_rsp(struct avctp *conn,
uint8_t code, uint8_t subunit,
uint8_t *operands, size_t operand_count,
@@ -1572,28 +1715,6 @@ static void avrcp_list_player_attributes(struct avrcp *session)
session);
}
-static const char *metadata_to_str(uint32_t id)
-{
- switch (id) {
- case AVRCP_MEDIA_ATTRIBUTE_TITLE:
- return "Title";
- case AVRCP_MEDIA_ATTRIBUTE_ARTIST:
- return "Artist";
- case AVRCP_MEDIA_ATTRIBUTE_ALBUM:
- return "Album";
- case AVRCP_MEDIA_ATTRIBUTE_GENRE:
- return "Genre";
- case AVRCP_MEDIA_ATTRIBUTE_TRACK:
- return "Track";
- case AVRCP_MEDIA_ATTRIBUTE_N_TRACKS:
- return "NumberOfTracks";
- case AVRCP_MEDIA_ATTRIBUTE_DURATION:
- return "Duration";
- }
-
- return NULL;
-}
-
static gboolean avrcp_get_attributes_rsp(struct avctp *conn,
uint8_t code, uint8_t subunit,
uint8_t *operands, size_t operand_count,
@@ -1774,74 +1895,6 @@ static void avrcp_register_notification(struct avrcp *session, uint8_t event)
avrcp_handle_event, session);
}
-static int attr_to_val(const char *str)
-{
- if (!strcasecmp(str, "Equalizer"))
- return AVRCP_ATTRIBUTE_EQUALIZER;
- else if (!strcasecmp(str, "Repeat"))
- return AVRCP_ATTRIBUTE_REPEAT_MODE;
- else if (!strcasecmp(str, "Shuffle"))
- return AVRCP_ATTRIBUTE_SHUFFLE;
- else if (!strcasecmp(str, "Scan"))
- return AVRCP_ATTRIBUTE_SCAN;
-
- return -EINVAL;
-}
-
-static int attrval_to_val(uint8_t attr, const char *value)
-{
- int ret;
-
- switch (attr) {
- case AVRCP_ATTRIBUTE_EQUALIZER:
- if (!strcmp(value, "off"))
- ret = AVRCP_EQUALIZER_OFF;
- else if (!strcmp(value, "on"))
- ret = AVRCP_EQUALIZER_ON;
- else
- ret = -EINVAL;
-
- return ret;
- case AVRCP_ATTRIBUTE_REPEAT_MODE:
- if (!strcmp(value, "off"))
- ret = AVRCP_REPEAT_MODE_OFF;
- else if (!strcmp(value, "singletrack"))
- ret = AVRCP_REPEAT_MODE_SINGLE;
- else if (!strcmp(value, "alltracks"))
- ret = AVRCP_REPEAT_MODE_ALL;
- else if (!strcmp(value, "group"))
- ret = AVRCP_REPEAT_MODE_GROUP;
- else
- ret = -EINVAL;
-
- return ret;
- case AVRCP_ATTRIBUTE_SHUFFLE:
- if (!strcmp(value, "off"))
- ret = AVRCP_SHUFFLE_OFF;
- else if (!strcmp(value, "alltracks"))
- ret = AVRCP_SHUFFLE_ALL;
- else if (!strcmp(value, "group"))
- ret = AVRCP_SHUFFLE_GROUP;
- else
- ret = -EINVAL;
-
- return ret;
- case AVRCP_ATTRIBUTE_SCAN:
- if (!strcmp(value, "off"))
- ret = AVRCP_SCAN_OFF;
- else if (!strcmp(value, "alltracks"))
- ret = AVRCP_SCAN_ALL;
- else if (!strcmp(value, "group"))
- ret = AVRCP_SCAN_GROUP;
- else
- ret = -EINVAL;
-
- return ret;
- }
-
- return -EINVAL;
-}
-
static void avrcp_set_player_value(struct avrcp *session, uint8_t attr,
uint8_t val)
{
diff --git a/audio/avrcp.h b/audio/avrcp.h
index 7f54adb..e607fb1 100644
--- a/audio/avrcp.h
+++ b/audio/avrcp.h
@@ -79,12 +79,13 @@
struct avrcp_player_cb {
GList *(*list_settings) (void *user_data);
- int (*get_setting) (uint8_t attr, void *user_data);
- int (*set_setting) (uint8_t attr, uint8_t value, void *user_data);
+ const char *(*get_setting) (const char *key, void *user_data);
+ int (*set_setting) (const char *key, const char *value,
+ void *user_data);
uint64_t (*get_uid) (void *user_data);
- const char *(*get_metadata) (uint32_t id, void *user_data);
+ const char *(*get_metadata) (const char *key, void *user_data);
GList *(*list_metadata) (void *user_data);
- uint8_t (*get_status) (void *user_data);
+ const char *(*get_status) (void *user_data);
uint32_t (*get_position) (void *user_data);
uint32_t (*get_duration) (void *user_data);
void (*set_volume) (uint8_t volume, struct audio_device *dev,
diff --git a/audio/media.c b/audio/media.c
index 7abf077..cec938a 100644
--- a/audio/media.c
+++ b/audio/media.c
@@ -98,7 +98,7 @@ struct media_player {
guint watch;
guint property_watch;
guint track_watch;
- uint8_t status;
+ char *status;
uint32_t position;
uint32_t duration;
uint8_t volume;
@@ -980,6 +980,7 @@ static void media_player_free(gpointer data)
g_timer_destroy(mp->timer);
g_free(mp->sender);
g_free(mp->path);
+ g_free(mp->status);
g_free(mp);
}
@@ -1007,193 +1008,6 @@ static void media_player_remove(struct media_player *mp)
media_player_destroy(mp);
}
-static const char *attrval_to_str(uint8_t attr, uint8_t value)
-{
- switch (attr) {
- case AVRCP_ATTRIBUTE_EQUALIZER:
- switch (value) {
- case AVRCP_EQUALIZER_ON:
- return "on";
- case AVRCP_EQUALIZER_OFF:
- return "off";
- }
-
- break;
- case AVRCP_ATTRIBUTE_REPEAT_MODE:
- switch (value) {
- case AVRCP_REPEAT_MODE_OFF:
- return "off";
- case AVRCP_REPEAT_MODE_SINGLE:
- return "singletrack";
- case AVRCP_REPEAT_MODE_ALL:
- return "alltracks";
- case AVRCP_REPEAT_MODE_GROUP:
- return "group";
- }
-
- break;
- /* Shuffle and scan have the same values */
- case AVRCP_ATTRIBUTE_SHUFFLE:
- case AVRCP_ATTRIBUTE_SCAN:
- switch (value) {
- case AVRCP_SCAN_OFF:
- return "off";
- case AVRCP_SCAN_ALL:
- return "alltracks";
- case AVRCP_SCAN_GROUP:
- return "group";
- }
-
- break;
- }
-
- return NULL;
-}
-
-static int attrval_to_val(uint8_t attr, const char *value)
-{
- int ret;
-
- switch (attr) {
- case AVRCP_ATTRIBUTE_EQUALIZER:
- if (!strcmp(value, "off"))
- ret = AVRCP_EQUALIZER_OFF;
- else if (!strcmp(value, "on"))
- ret = AVRCP_EQUALIZER_ON;
- else
- ret = -EINVAL;
-
- return ret;
- case AVRCP_ATTRIBUTE_REPEAT_MODE:
- if (!strcmp(value, "off"))
- ret = AVRCP_REPEAT_MODE_OFF;
- else if (!strcmp(value, "singletrack"))
- ret = AVRCP_REPEAT_MODE_SINGLE;
- else if (!strcmp(value, "alltracks"))
- ret = AVRCP_REPEAT_MODE_ALL;
- else if (!strcmp(value, "group"))
- ret = AVRCP_REPEAT_MODE_GROUP;
- else
- ret = -EINVAL;
-
- return ret;
- case AVRCP_ATTRIBUTE_SHUFFLE:
- if (!strcmp(value, "off"))
- ret = AVRCP_SHUFFLE_OFF;
- else if (!strcmp(value, "alltracks"))
- ret = AVRCP_SHUFFLE_ALL;
- else if (!strcmp(value, "group"))
- ret = AVRCP_SHUFFLE_GROUP;
- else
- ret = -EINVAL;
-
- return ret;
- case AVRCP_ATTRIBUTE_SCAN:
- if (!strcmp(value, "off"))
- ret = AVRCP_SCAN_OFF;
- else if (!strcmp(value, "alltracks"))
- ret = AVRCP_SCAN_ALL;
- else if (!strcmp(value, "group"))
- ret = AVRCP_SCAN_GROUP;
- else
- ret = -EINVAL;
-
- return ret;
- }
-
- return -EINVAL;
-}
-
-static const char *attr_to_str(uint8_t attr)
-{
- switch (attr) {
- case AVRCP_ATTRIBUTE_EQUALIZER:
- return "Equalizer";
- case AVRCP_ATTRIBUTE_REPEAT_MODE:
- return "Repeat";
- case AVRCP_ATTRIBUTE_SHUFFLE:
- return "Shuffle";
- case AVRCP_ATTRIBUTE_SCAN:
- return "Scan";
- }
-
- return NULL;
-}
-
-static int attr_to_val(const char *str)
-{
- if (!strcasecmp(str, "Equalizer"))
- return AVRCP_ATTRIBUTE_EQUALIZER;
- else if (!strcasecmp(str, "Repeat"))
- return AVRCP_ATTRIBUTE_REPEAT_MODE;
- else if (!strcasecmp(str, "Shuffle"))
- return AVRCP_ATTRIBUTE_SHUFFLE;
- else if (!strcasecmp(str, "Scan"))
- return AVRCP_ATTRIBUTE_SCAN;
-
- return -EINVAL;
-}
-
-static int play_status_to_val(const char *status)
-{
- if (!strcasecmp(status, "stopped"))
- return AVRCP_PLAY_STATUS_STOPPED;
- else if (!strcasecmp(status, "playing"))
- return AVRCP_PLAY_STATUS_PLAYING;
- else if (!strcasecmp(status, "paused"))
- return AVRCP_PLAY_STATUS_PAUSED;
- else if (!strcasecmp(status, "forward-seek"))
- return AVRCP_PLAY_STATUS_FWD_SEEK;
- else if (!strcasecmp(status, "reverse-seek"))
- return AVRCP_PLAY_STATUS_REV_SEEK;
- else if (!strcasecmp(status, "error"))
- return AVRCP_PLAY_STATUS_ERROR;
-
- return -EINVAL;
-}
-
-static int metadata_to_val(const char *str)
-{
- if (!strcasecmp(str, "Title"))
- return AVRCP_MEDIA_ATTRIBUTE_TITLE;
- else if (!strcasecmp(str, "Artist"))
- return AVRCP_MEDIA_ATTRIBUTE_ARTIST;
- else if (!strcasecmp(str, "Album"))
- return AVRCP_MEDIA_ATTRIBUTE_ALBUM;
- else if (!strcasecmp(str, "Genre"))
- return AVRCP_MEDIA_ATTRIBUTE_GENRE;
- else if (!strcasecmp(str, "NumberOfTracks"))
- return AVRCP_MEDIA_ATTRIBUTE_N_TRACKS;
- else if (!strcasecmp(str, "Number"))
- return AVRCP_MEDIA_ATTRIBUTE_TRACK;
- else if (!strcasecmp(str, "Duration"))
- return AVRCP_MEDIA_ATTRIBUTE_DURATION;
-
- return -EINVAL;
-}
-
-static const char *metadata_to_str(uint32_t id)
-{
- switch (id) {
- case AVRCP_MEDIA_ATTRIBUTE_TITLE:
- return "Title";
- case AVRCP_MEDIA_ATTRIBUTE_ARTIST:
- return "Artist";
- case AVRCP_MEDIA_ATTRIBUTE_ALBUM:
- return "Album";
- case AVRCP_MEDIA_ATTRIBUTE_GENRE:
- return "Genre";
- case AVRCP_MEDIA_ATTRIBUTE_TRACK:
- return "Track";
- case AVRCP_MEDIA_ATTRIBUTE_N_TRACKS:
- return "NumberOfTracks";
- case AVRCP_MEDIA_ATTRIBUTE_DURATION:
- return "Duration";
- }
-
- return NULL;
-}
-
static GList *list_settings(void *user_data)
{
struct media_player *mp = user_data;
@@ -1206,38 +1020,24 @@ static GList *list_settings(void *user_data)
return g_hash_table_get_keys(mp->settings);
}
-static int get_setting(uint8_t attr, void *user_data)
+static const char *get_setting(const char *key, void *user_data)
{
struct media_player *mp = user_data;
- guint attr_uint = attr;
- void *value;
-
- DBG("%s", attr_to_str(attr));
- value = g_hash_table_lookup(mp->settings, GUINT_TO_POINTER(attr_uint));
- if (!value)
- return -EINVAL;
+ DBG("%s", key);
- return GPOINTER_TO_UINT(value);
+ return g_hash_table_lookup(mp->settings, key);
}
-static int set_setting(uint8_t attr, uint8_t val, void *user_data)
+static int set_setting(const char *key, const char *value, void *user_data)
{
struct media_player *mp = user_data;
- const char *property, *value;
- guint attr_uint = attr;
DBusMessage *msg;
DBusMessageIter iter, var;
- property = attr_to_str(attr);
- value = attrval_to_str(attr, val);
-
- DBG("%s = %s", property, value);
+ DBG("%s = %s", key, value);
- if (property == NULL || value == NULL)
- return -EINVAL;
-
- if (!g_hash_table_lookup(mp->settings, GUINT_TO_POINTER(attr_uint)))
+ if (!g_hash_table_lookup(mp->settings, key))
return -EINVAL;
msg = dbus_message_new_method_call(mp->sender, mp->path,
@@ -1249,7 +1049,7 @@ static int set_setting(uint8_t attr, uint8_t val, void *user_data)
}
dbus_message_iter_init_append(msg, &iter);
- dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &property);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &key);
dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
DBUS_TYPE_STRING_AS_STRING,
@@ -1286,19 +1086,19 @@ static uint64_t get_uid(void *user_data)
return 0;
}
-static const char *get_metadata(uint32_t id, void *user_data)
+static const char *get_metadata(const char *key, void *user_data)
{
struct media_player *mp = user_data;
- DBG("%s", metadata_to_str(id));
+ DBG("%s", key);
if (mp->track == NULL)
return NULL;
- return g_hash_table_lookup(mp->track, GUINT_TO_POINTER(id));
+ return g_hash_table_lookup(mp->track, key);
}
-static uint8_t get_status(void *user_data)
+static const char *get_status(void *user_data)
{
struct media_player *mp = user_data;
@@ -1311,7 +1111,7 @@ static uint32_t get_position(void *user_data)
double timedelta;
uint32_t sec, msec;
- if (mp->status != AVRCP_PLAY_STATUS_PLAYING)
+ if (g_strcmp0(mp->status, "playing") != 0)
return mp->position;
timedelta = g_timer_elapsed(mp->timer, NULL);
@@ -1379,7 +1179,6 @@ static void media_player_exit(DBusConnection *connection, void *user_data)
static gboolean set_status(struct media_player *mp, DBusMessageIter *iter)
{
const char *value;
- int val;
if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING)
return FALSE;
@@ -1387,21 +1186,16 @@ static gboolean set_status(struct media_player *mp, DBusMessageIter *iter)
dbus_message_iter_get_basic(iter, &value);
DBG("Status=%s", value);
- val = play_status_to_val(value);
- if (val < 0) {
- error("Invalid status");
- return FALSE;
- }
-
- if (mp->status == val)
+ if (g_strcmp0(mp->status, value) == 0)
return TRUE;
mp->position = get_position(mp);
g_timer_start(mp->timer);
- mp->status = val;
+ g_free(mp->status);
+ mp->status = g_strdup(value);
- avrcp_player_event(mp->player, AVRCP_EVENT_STATUS_CHANGED, &val);
+ avrcp_player_event(mp->player, AVRCP_EVENT_STATUS_CHANGED, mp->status);
return TRUE;
}
@@ -1441,7 +1235,6 @@ static gboolean set_player_property(struct media_player *mp, const char *key,
{
DBusMessageIter var;
const char *value, *curval;
- int attr, val;
GList *settings;
if (dbus_message_iter_get_arg_type(entry) != DBUS_TYPE_VARIANT)
@@ -1455,27 +1248,18 @@ static gboolean set_player_property(struct media_player *mp, const char *key,
if (strcasecmp(key, "Position") == 0)
return set_position(mp, &var);
- attr = attr_to_val(key);
- if (attr < 0)
- return FALSE;
-
if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING)
return FALSE;
dbus_message_iter_get_basic(&var, &value);
- val = attrval_to_val(attr, value);
- if (val < 0)
- return FALSE;
-
- curval = g_hash_table_lookup(mp->settings, GUINT_TO_POINTER(attr));
+ curval = g_hash_table_lookup(mp->settings, key);
if (g_strcmp0(curval, value) == 0)
return TRUE;
DBG("%s=%s", key, value);
- g_hash_table_replace(mp->settings, GUINT_TO_POINTER(attr),
- GUINT_TO_POINTER(val));
+ g_hash_table_replace(mp->settings, g_strdup(key), g_strdup(value));
settings = list_settings(mp);
@@ -1529,7 +1313,7 @@ static gboolean parse_player_metadata(struct media_player *mp,
dbus_message_iter_recurse(iter, &dict);
- track = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL,
+ track = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
g_free);
while ((ctype = dbus_message_iter_get_arg_type(&dict)) !=
@@ -1540,7 +1324,6 @@ static gboolean parse_player_metadata(struct media_player *mp,
char valstr[20];
char *value;
uint32_t num;
- int id;
int type;
if (ctype != DBUS_TYPE_DICT_ENTRY)
@@ -1553,10 +1336,6 @@ static gboolean parse_player_metadata(struct media_player *mp,
dbus_message_iter_get_basic(&entry, &key);
dbus_message_iter_next(&entry);
- id = metadata_to_val(key);
- if (id < 0)
- goto parse_error;
-
if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT)
goto parse_error;
@@ -1564,36 +1343,46 @@ static gboolean parse_player_metadata(struct media_player *mp,
type = dbus_message_iter_get_arg_type(&var);
- switch (id) {
- case AVRCP_MEDIA_ATTRIBUTE_TITLE:
+ if (strcasecmp(key, "Title") == 0) {
+ if (type != DBUS_TYPE_STRING)
+ goto parse_error;
title = TRUE;
- case AVRCP_MEDIA_ATTRIBUTE_ARTIST:
- case AVRCP_MEDIA_ATTRIBUTE_ALBUM:
- case AVRCP_MEDIA_ATTRIBUTE_GENRE:
+ dbus_message_iter_get_basic(&var, &string);
+ } else if (strcasecmp(key, "Artist") == 0) {
if (type != DBUS_TYPE_STRING)
goto parse_error;
dbus_message_iter_get_basic(&var, &string);
- break;
- case AVRCP_MEDIA_ATTRIBUTE_TRACK:
- case AVRCP_MEDIA_ATTRIBUTE_N_TRACKS:
+ } else if (strcasecmp(key, "Album") == 0) {
+ if (type != DBUS_TYPE_STRING)
+ goto parse_error;
+
+ dbus_message_iter_get_basic(&var, &string);
+ } else if (strcasecmp(key, "Genre") == 0) {
+ if (type != DBUS_TYPE_STRING)
+ goto parse_error;
+
+ dbus_message_iter_get_basic(&var, &string);
+ } else if (strcasecmp(key, "Duration") == 0) {
if (type != DBUS_TYPE_UINT32)
goto parse_error;
dbus_message_iter_get_basic(&var, &num);
- break;
- case AVRCP_MEDIA_ATTRIBUTE_DURATION:
+ mp->duration = num;
+ } else if (strcasecmp(key, "Track") == 0) {
if (type != DBUS_TYPE_UINT32)
goto parse_error;
dbus_message_iter_get_basic(&var, &num);
- mp->duration = num;
- break;
- default:
+ } else if (strcasecmp(key, "NumberOfTracks") == 0) {
+ if (type != DBUS_TYPE_UINT32)
+ goto parse_error;
+
+ dbus_message_iter_get_basic(&var, &num);
+ } else
goto parse_error;
- }
- switch (dbus_message_iter_get_arg_type(&var)) {
+ switch (type) {
case DBUS_TYPE_STRING:
value = g_strdup(string);
break;
@@ -1603,18 +1392,15 @@ static gboolean parse_player_metadata(struct media_player *mp,
}
DBG("%s=%s", key, value);
- g_hash_table_replace(track, GUINT_TO_POINTER(id), value);
+ g_hash_table_replace(track, g_strdup(key), value);
dbus_message_iter_next(&dict);
}
if (g_hash_table_size(track) == 0) {
g_hash_table_unref(track);
track = NULL;
- } else if (title == FALSE) {
- uint32_t id = AVRCP_MEDIA_ATTRIBUTE_TITLE;
-
- g_hash_table_insert(track, GUINT_TO_POINTER(id), g_strdup(""));
- }
+ } else if (title == FALSE)
+ g_hash_table_insert(track, g_strdup("Title"), g_strdup(""));
if (mp->track != NULL)
g_hash_table_unref(mp->track);
@@ -1692,7 +1478,8 @@ static struct media_player *media_player_create(struct media_adapter *adapter,
return NULL;
}
- mp->settings = g_hash_table_new(g_direct_hash, g_direct_equal);
+ mp->settings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
+ g_free);
adapter->players = g_slist_append(adapter->players, mp);
--
1.7.11.7
^ permalink raw reply related
* [PATCH BlueZ 07/11] AVRCP: Add initial support for controller player
From: Luiz Augusto von Dentz @ 2012-10-25 13:59 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1351173554-28039-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This also bump controller record to 1.3.
---
Makefile.am | 1 +
audio/avrcp.c | 686 +++++++++++++++++++++++++++++++++++++++++++++++++--------
audio/player.c | 404 +++++++++++++++++++++++++++++++++
audio/player.h | 46 ++++
4 files changed, 1047 insertions(+), 90 deletions(-)
create mode 100644 audio/player.c
create mode 100644 audio/player.h
diff --git a/Makefile.am b/Makefile.am
index 35b1520..6ac6a73 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -148,6 +148,7 @@ builtin_sources += audio/main.c \
audio/avdtp.h audio/avdtp.c \
audio/media.h audio/media.c \
audio/transport.h audio/transport.c \
+ audio/player.h audio/player.c \
audio/telephony.h audio/a2dp-codecs.h
builtin_nodist += audio/telephony.c
diff --git a/audio/avrcp.c b/audio/avrcp.c
index 02e4581..35fc249 100644
--- a/audio/avrcp.c
+++ b/audio/avrcp.c
@@ -61,6 +61,9 @@
#include "control.h"
#include "avdtp.h"
#include "sink.h"
+#include "player.h"
+
+#define MEDIA_PLAYER_INTERFACE "org.bluez.MediaPlayer"
/* Company IDs for vendor dependent commands */
#define IEEEID_BTSIG 0x001958
@@ -190,6 +193,7 @@ struct avrcp {
int features;
void (*init) (struct avrcp *session);
+ void (*destroy) (struct avrcp *sesion);
const struct control_pdu_handler *control_handlers;
@@ -216,7 +220,7 @@ static uint32_t company_ids[] = {
IEEEID_BTSIG,
};
-static void register_notification(struct avrcp *session, uint8_t event);
+static void avrcp_register_notification(struct avrcp *session, uint8_t event);
static sdp_record_t *avrcp_ct_record(void)
{
@@ -227,7 +231,7 @@ static sdp_record_t *avrcp_ct_record(void)
sdp_record_t *record;
sdp_data_t *psm, *version, *features;
uint16_t lp = AVCTP_CONTROL_PSM;
- uint16_t avrcp_ver = 0x0100, avctp_ver = 0x0103;
+ uint16_t avrcp_ver = 0x0103, avctp_ver = 0x0103;
uint16_t feat = ( AVRCP_FEATURE_CATEGORY_1 |
AVRCP_FEATURE_CATEGORY_2 |
AVRCP_FEATURE_CATEGORY_3 |
@@ -431,20 +435,12 @@ static void set_company_id(uint8_t cid[3], const uint32_t cid_in)
cid[2] = cid_in;
}
-static int player_get_attribute(struct avrcp_player *player, uint8_t attr)
+static int player_get_setting(struct avrcp_player *player, uint8_t id)
{
- int value;
-
- DBG("attr %u", attr);
-
if (player == NULL)
return -ENOENT;
- value = player->cb->get_setting(attr, player->user_data);
- if (value < 0)
- DBG("attr %u not supported by player", attr);
-
- return value;
+ return player->cb->get_setting(id, player->user_data);
}
void avrcp_player_event(struct avrcp_player *player, uint8_t id, void *data)
@@ -490,7 +486,7 @@ void avrcp_player_event(struct avrcp_player *player, uint8_t id, void *data)
uint8_t attr = GPOINTER_TO_UINT(settings->data);
int val;
- val = player_get_attribute(player, attr);
+ val = player_get_setting(player, attr);
if (val < 0)
continue;
@@ -642,14 +638,6 @@ static int player_set_setting(struct avrcp_player *player, uint8_t id,
return player->cb->set_setting(id, val, player->user_data);
}
-static int player_get_setting(struct avrcp_player *player, uint8_t id)
-{
- if (player == NULL)
- return -ENOENT;
-
- return player->cb->get_setting(id, player->user_data);
-}
-
static uint8_t avrcp_handle_get_capabilities(struct avrcp *session,
struct avrcp_header *pdu,
uint8_t transaction)
@@ -1094,7 +1082,7 @@ static uint8_t avrcp_handle_register_notification(struct avrcp *session,
uint8_t attr = GPOINTER_TO_UINT(settings->data);
int val;
- val = player_get_attribute(player, attr);
+ val = player_get_setting(player, attr);
if (val < 0)
continue;
@@ -1356,6 +1344,330 @@ static struct avrcp_server *find_server(GSList *list, const bdaddr_t *src)
return NULL;
}
+static const char *status_to_string(uint8_t status)
+{
+ switch (status) {
+ case AVRCP_PLAY_STATUS_STOPPED:
+ return "stopped";
+ case AVRCP_PLAY_STATUS_PLAYING:
+ return "playing";
+ case AVRCP_PLAY_STATUS_PAUSED:
+ return "paused";
+ case AVRCP_PLAY_STATUS_FWD_SEEK:
+ return "forward-seek";
+ case AVRCP_PLAY_STATUS_REV_SEEK:
+ return "reverse-seek";
+ case AVRCP_PLAY_STATUS_ERROR:
+ return "error";
+ default:
+ return NULL;
+ }
+}
+
+static gboolean avrcp_get_play_status_rsp(struct avctp *conn,
+ uint8_t code, uint8_t subunit,
+ uint8_t *operands, size_t operand_count,
+ void *user_data)
+{
+ struct avrcp *session = user_data;
+ struct avrcp_player *player = session->player;
+ struct media_player *mp = player->user_data;
+ struct avrcp_header *pdu = (void *) operands;
+ uint32_t duration;
+ uint32_t position;
+ uint8_t status;
+
+ if (code == AVC_CTYPE_REJECTED || pdu->params_len != 9)
+ return FALSE;
+
+ memcpy(&duration, pdu->params, sizeof(uint32_t));
+ duration = ntohl(duration);
+
+ memcpy(&position, pdu->params + 4, sizeof(uint32_t));
+ position = ntohl(position);
+ media_player_set_position(mp, position);
+
+ memcpy(&status, pdu->params + 8, sizeof(uint8_t));
+ media_player_set_status(mp, status_to_string(status));
+
+ return FALSE;
+}
+
+static void avrcp_get_play_status(struct avrcp *session)
+{
+ uint8_t buf[AVRCP_HEADER_LENGTH];
+ struct avrcp_header *pdu = (void *) buf;
+
+ memset(buf, 0, sizeof(buf));
+
+ set_company_id(pdu->company_id, IEEEID_BTSIG);
+ pdu->pdu_id = AVRCP_GET_PLAY_STATUS;
+ pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
+
+ avctp_send_vendordep_req(session->conn, AVC_CTYPE_STATUS,
+ AVC_SUBUNIT_PANEL, buf, sizeof(buf),
+ avrcp_get_play_status_rsp,
+ session);
+}
+
+static const char *attrval_to_str(uint8_t attr, uint8_t value)
+{
+ switch (attr) {
+ case AVRCP_ATTRIBUTE_EQUALIZER:
+ switch (value) {
+ case AVRCP_EQUALIZER_ON:
+ return "on";
+ case AVRCP_EQUALIZER_OFF:
+ return "off";
+ }
+
+ break;
+ case AVRCP_ATTRIBUTE_REPEAT_MODE:
+ switch (value) {
+ case AVRCP_REPEAT_MODE_OFF:
+ return "off";
+ case AVRCP_REPEAT_MODE_SINGLE:
+ return "singletrack";
+ case AVRCP_REPEAT_MODE_ALL:
+ return "alltracks";
+ case AVRCP_REPEAT_MODE_GROUP:
+ return "group";
+ }
+
+ break;
+ /* Shuffle and scan have the same values */
+ case AVRCP_ATTRIBUTE_SHUFFLE:
+ case AVRCP_ATTRIBUTE_SCAN:
+ switch (value) {
+ case AVRCP_SCAN_OFF:
+ return "off";
+ case AVRCP_SCAN_ALL:
+ return "alltracks";
+ case AVRCP_SCAN_GROUP:
+ return "group";
+ }
+
+ break;
+ }
+
+ return NULL;
+}
+
+static const char *attr_to_str(uint8_t attr)
+{
+ switch (attr) {
+ case AVRCP_ATTRIBUTE_EQUALIZER:
+ return "Equalizer";
+ case AVRCP_ATTRIBUTE_REPEAT_MODE:
+ return "Repeat";
+ case AVRCP_ATTRIBUTE_SHUFFLE:
+ return "Shuffle";
+ case AVRCP_ATTRIBUTE_SCAN:
+ return "Scan";
+ }
+
+ return NULL;
+}
+
+static gboolean avrcp_player_value_rsp(struct avctp *conn,
+ uint8_t code, uint8_t subunit,
+ uint8_t *operands, size_t operand_count,
+ void *user_data)
+{
+ struct avrcp *session = user_data;
+ struct avrcp_player *player = session->player;
+ struct media_player *mp = player->user_data;
+ struct avrcp_header *pdu = (void *) operands;
+ uint8_t count;
+ int i;
+
+ if (code == AVC_CTYPE_REJECTED)
+ return FALSE;
+
+ count = pdu->params[0];
+
+ if (pdu->params_len < count * 2)
+ return FALSE;
+
+ for (i = 1; count > 0; count--, i += 2) {
+ const char *key;
+ const char *value;
+
+ key = attr_to_str(pdu->params[i]);
+ if (key == NULL)
+ continue;
+
+ value = attrval_to_str(pdu->params[i], pdu->params[i + 1]);
+ if (value == NULL)
+ continue;
+
+ media_player_set_setting(mp, key, value);
+ }
+
+ return FALSE;
+}
+
+static void avrcp_get_current_player_value(struct avrcp *session,
+ uint8_t *attrs, uint8_t count)
+{
+ uint8_t buf[AVRCP_HEADER_LENGTH + 5];
+ struct avrcp_header *pdu = (void *) buf;
+ int i;
+
+ memset(buf, 0, sizeof(buf));
+
+ set_company_id(pdu->company_id, IEEEID_BTSIG);
+ pdu->pdu_id = AVRCP_GET_CURRENT_PLAYER_VALUE;
+ pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
+ pdu->params_len = htons(count + 1);
+ pdu->params[0] = count;
+
+ for (i = 0; count > 0; count--, i++)
+ pdu->params[i + 1] = attrs[i];
+
+ avctp_send_vendordep_req(session->conn, AVC_CTYPE_STATUS,
+ AVC_SUBUNIT_PANEL, buf, sizeof(buf),
+ avrcp_player_value_rsp, session);
+}
+
+static gboolean avrcp_list_player_attributes_rsp(struct avctp *conn,
+ uint8_t code, uint8_t subunit,
+ uint8_t *operands, size_t operand_count,
+ void *user_data)
+{
+ struct avrcp *session = user_data;
+ struct avrcp_header *pdu = (void *) operands;
+ uint8_t count;
+
+ if (code == AVC_CTYPE_REJECTED)
+ return FALSE;
+
+ count = pdu->params[0];
+
+ if (ntohs(pdu->params_len) < count) {
+ error("Invalid parameters");
+ return FALSE;
+ }
+
+ avrcp_get_current_player_value(session, &pdu->params[1],
+ pdu->params[0]);
+
+ return FALSE;
+}
+
+static void avrcp_list_player_attributes(struct avrcp *session)
+{
+ uint8_t buf[AVRCP_HEADER_LENGTH];
+ struct avrcp_header *pdu = (void *) buf;
+
+ memset(buf, 0, sizeof(buf));
+
+ set_company_id(pdu->company_id, IEEEID_BTSIG);
+ pdu->pdu_id = AVRCP_LIST_PLAYER_ATTRIBUTES;
+ pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
+
+ avctp_send_vendordep_req(session->conn, AVC_CTYPE_STATUS,
+ AVC_SUBUNIT_PANEL, buf, sizeof(buf),
+ avrcp_list_player_attributes_rsp,
+ session);
+}
+
+static const char *metadata_to_str(uint32_t id)
+{
+ switch (id) {
+ case AVRCP_MEDIA_ATTRIBUTE_TITLE:
+ return "Title";
+ case AVRCP_MEDIA_ATTRIBUTE_ARTIST:
+ return "Artist";
+ case AVRCP_MEDIA_ATTRIBUTE_ALBUM:
+ return "Album";
+ case AVRCP_MEDIA_ATTRIBUTE_GENRE:
+ return "Genre";
+ case AVRCP_MEDIA_ATTRIBUTE_TRACK:
+ return "Track";
+ case AVRCP_MEDIA_ATTRIBUTE_N_TRACKS:
+ return "NumberOfTracks";
+ case AVRCP_MEDIA_ATTRIBUTE_DURATION:
+ return "Duration";
+ }
+
+ return NULL;
+}
+
+static gboolean avrcp_get_attributes_rsp(struct avctp *conn,
+ uint8_t code, uint8_t subunit,
+ uint8_t *operands, size_t operand_count,
+ void *user_data)
+{
+ struct avrcp *session = user_data;
+ struct avrcp_player *player = session->player;
+ struct media_player *mp = player->user_data;
+ struct avrcp_header *pdu = (void *) operands;
+ uint8_t count;
+ int i;
+
+ if (code == AVC_CTYPE_REJECTED)
+ return FALSE;
+
+ count = pdu->params[0];
+
+ if (ntohs(pdu->params_len) - 1 < count * 8) {
+ error("Invalid parameters");
+ return FALSE;
+ }
+
+ for (i = 1; count > 0; count--) {
+ uint32_t id;
+ uint16_t charset, len;
+
+ memcpy(&id, &pdu->params[i], sizeof(uint32_t));
+ id = ntohl(id);
+ i += sizeof(uint32_t);
+
+ memcpy(&charset, &pdu->params[i], sizeof(uint16_t));
+ charset = ntohs(charset);
+ i += sizeof(uint16_t);
+
+ memcpy(&len, &pdu->params[i], sizeof(uint16_t));
+ len = ntohs(len);
+ i += sizeof(uint16_t);
+
+ if (charset == 106) {
+ const char *key = metadata_to_str(id);
+
+ if (key != NULL)
+ media_player_set_metadata(mp,
+ metadata_to_str(id),
+ &pdu->params[i], len);
+ }
+
+ i += len;
+ }
+
+ return FALSE;
+}
+
+static void avrcp_get_element_attributes(struct avrcp *session)
+{
+ uint8_t buf[AVRCP_HEADER_LENGTH + 9];
+ struct avrcp_header *pdu = (void *) buf;
+ uint16_t length;
+
+ memset(buf, 0, sizeof(buf));
+
+ set_company_id(pdu->company_id, IEEEID_BTSIG);
+ pdu->pdu_id = AVRCP_GET_ELEMENT_ATTRIBUTES;
+ pdu->params_len = htons(9);
+ pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
+
+ length = AVRCP_HEADER_LENGTH + ntohs(pdu->params_len);
+
+ avctp_send_vendordep_req(session->conn, AVC_CTYPE_STATUS,
+ AVC_SUBUNIT_PANEL, buf, length,
+ avrcp_get_attributes_rsp,
+ session);
+}
+
static gboolean avrcp_handle_event(struct avctp *conn,
uint8_t code, uint8_t subunit,
uint8_t *operands, size_t operand_count,
@@ -1364,8 +1676,12 @@ static gboolean avrcp_handle_event(struct avctp *conn,
struct avrcp *session = user_data;
struct avrcp_player *player = session->player;
struct avrcp_header *pdu = (void *) operands;
+ struct media_player *mp;
uint8_t event;
- uint8_t volume;
+ uint8_t value;
+ uint8_t count;
+ const char *curval, *strval;
+ int i;
if (code != AVC_CTYPE_INTERIM && code != AVC_CTYPE_CHANGED)
return FALSE;
@@ -1374,24 +1690,70 @@ static gboolean avrcp_handle_event(struct avctp *conn,
switch (event) {
case AVRCP_EVENT_VOLUME_CHANGED:
- volume = pdu->params[1] & 0x7F;
+ value = pdu->params[1] & 0x7F;
if (player)
- player->cb->set_volume(volume, session->dev,
+ player->cb->set_volume(value, session->dev,
player->user_data);
break;
+ case AVRCP_EVENT_STATUS_CHANGED:
+ mp = player->user_data;
+ value = pdu->params[1];
+
+ curval = media_player_get_status(mp);
+ strval = status_to_string(value);
+
+ if (g_strcmp0(curval, strval) != 0) {
+ media_player_set_status(mp, strval);
+ avrcp_get_play_status(session);
+ }
+
+ break;
+ case AVRCP_EVENT_TRACK_CHANGED:
+ mp = player->user_data;
+ if (code == AVC_CTYPE_CHANGED)
+ media_player_set_position(mp, 0);
+
+ avrcp_get_element_attributes(session);
+
+ break;
+
+ case AVRCP_EVENT_SETTINGS_CHANGED:
+ mp = player->user_data;
+ count = pdu->params[1];
+
+ for (i = 2; count > 0; count--, i += 2) {
+ const char *key;
+ const char *value;
+
+ key = attr_to_str(pdu->params[i]);
+ if (key == NULL)
+ continue;
+
+ value = attrval_to_str(pdu->params[i],
+ pdu->params[i + 1]);
+ if (value == NULL)
+ continue;
+
+ media_player_set_setting(mp, key, value);
+ }
+
+ break;
}
if (code == AVC_CTYPE_CHANGED) {
- register_notification(session, event);
+ session->registered_events ^= (1 << event);
+ avrcp_register_notification(session, event);
return FALSE;
}
+ session->registered_events |= (1 << event);
+
return TRUE;
}
-static void register_notification(struct avrcp *session, uint8_t event)
+static void avrcp_register_notification(struct avrcp *session, uint8_t event)
{
uint8_t buf[AVRCP_HEADER_LENGTH + AVRCP_REGISTER_NOTIFICATION_PARAM_LENGTH];
struct avrcp_header *pdu = (void *) buf;
@@ -1412,14 +1774,139 @@ static void register_notification(struct avrcp *session, uint8_t event)
avrcp_handle_event, session);
}
+static int attr_to_val(const char *str)
+{
+ if (!strcasecmp(str, "Equalizer"))
+ return AVRCP_ATTRIBUTE_EQUALIZER;
+ else if (!strcasecmp(str, "Repeat"))
+ return AVRCP_ATTRIBUTE_REPEAT_MODE;
+ else if (!strcasecmp(str, "Shuffle"))
+ return AVRCP_ATTRIBUTE_SHUFFLE;
+ else if (!strcasecmp(str, "Scan"))
+ return AVRCP_ATTRIBUTE_SCAN;
+
+ return -EINVAL;
+}
+
+static int attrval_to_val(uint8_t attr, const char *value)
+{
+ int ret;
+
+ switch (attr) {
+ case AVRCP_ATTRIBUTE_EQUALIZER:
+ if (!strcmp(value, "off"))
+ ret = AVRCP_EQUALIZER_OFF;
+ else if (!strcmp(value, "on"))
+ ret = AVRCP_EQUALIZER_ON;
+ else
+ ret = -EINVAL;
+
+ return ret;
+ case AVRCP_ATTRIBUTE_REPEAT_MODE:
+ if (!strcmp(value, "off"))
+ ret = AVRCP_REPEAT_MODE_OFF;
+ else if (!strcmp(value, "singletrack"))
+ ret = AVRCP_REPEAT_MODE_SINGLE;
+ else if (!strcmp(value, "alltracks"))
+ ret = AVRCP_REPEAT_MODE_ALL;
+ else if (!strcmp(value, "group"))
+ ret = AVRCP_REPEAT_MODE_GROUP;
+ else
+ ret = -EINVAL;
+
+ return ret;
+ case AVRCP_ATTRIBUTE_SHUFFLE:
+ if (!strcmp(value, "off"))
+ ret = AVRCP_SHUFFLE_OFF;
+ else if (!strcmp(value, "alltracks"))
+ ret = AVRCP_SHUFFLE_ALL;
+ else if (!strcmp(value, "group"))
+ ret = AVRCP_SHUFFLE_GROUP;
+ else
+ ret = -EINVAL;
+
+ return ret;
+ case AVRCP_ATTRIBUTE_SCAN:
+ if (!strcmp(value, "off"))
+ ret = AVRCP_SCAN_OFF;
+ else if (!strcmp(value, "alltracks"))
+ ret = AVRCP_SCAN_ALL;
+ else if (!strcmp(value, "group"))
+ ret = AVRCP_SCAN_GROUP;
+ else
+ ret = -EINVAL;
+
+ return ret;
+ }
+
+ return -EINVAL;
+}
+
+static void avrcp_set_player_value(struct avrcp *session, uint8_t attr,
+ uint8_t val)
+{
+ uint8_t buf[AVRCP_HEADER_LENGTH + 3];
+ struct avrcp_header *pdu = (void *) buf;
+ uint8_t length;
+
+ memset(buf, 0, sizeof(buf));
+
+ set_company_id(pdu->company_id, IEEEID_BTSIG);
+ pdu->pdu_id = AVRCP_SET_PLAYER_VALUE;
+ pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
+ pdu->params[0] = 1;
+ pdu->params[1] = attr;
+ pdu->params[2] = val;
+ pdu->params_len = htons(3);
+
+ length = AVRCP_HEADER_LENGTH + ntohs(pdu->params_len);
+
+ avctp_send_vendordep_req(session->conn, AVC_CTYPE_NOTIFY,
+ AVC_SUBUNIT_PANEL, buf, length,
+ avrcp_player_value_rsp, session);
+}
+
+static bool ct_set_setting(struct media_player *mp, const char *key,
+ const char *value, void *user_data)
+{
+ struct avrcp_player *player = user_data;
+ int attr = attr_to_val(key);
+ int val = attrval_to_val(attr, value);
+ struct avrcp *session;
+
+ session = player->sessions->data;
+ if (session == NULL)
+ return false;
+
+ attr = attr_to_val(key);
+ if (attr < 0)
+ return false;
+
+ val = attrval_to_val(attr, value);
+ if (val < 0)
+ return false;
+
+ avrcp_set_player_value(session, attr, val);
+
+ return true;
+}
+
+static const struct media_player_callback ct_cbs = {
+ .set_setting = ct_set_setting,
+};
+
static gboolean avrcp_get_capabilities_resp(struct avctp *conn,
uint8_t code, uint8_t subunit,
uint8_t *operands, size_t operand_count,
void *user_data)
{
struct avrcp *session = user_data;
+ struct avrcp_player *player = session->player;
+ struct media_player *mp;
struct avrcp_header *pdu = (void *) operands;
+ uint16_t events = 0;
uint8_t count;
+ const char *path;
if (pdu->params[0] != CAP_EVENTS_SUPPORTED)
return FALSE;
@@ -1429,14 +1916,29 @@ static gboolean avrcp_get_capabilities_resp(struct avctp *conn,
for (; count > 0; count--) {
uint8_t event = pdu->params[1 + count];
+ events |= (1 << event);
+
switch (event) {
case AVRCP_EVENT_STATUS_CHANGED:
case AVRCP_EVENT_TRACK_CHANGED:
- register_notification(session, event);
+ case AVRCP_EVENT_SETTINGS_CHANGED:
+ avrcp_register_notification(session, event);
break;
}
}
+ path = device_get_path(session->dev->btd_dev);
+ mp = media_player_controller_create(path);
+ media_player_set_callbacks(mp, &ct_cbs, player);
+ player->user_data = mp;
+ player->destroy = (GDestroyNotify) media_player_destroy;
+
+ if (!(events & (1 << AVRCP_EVENT_SETTINGS_CHANGED)))
+ avrcp_list_player_attributes(session);
+
+ if (!(events & (1 << AVRCP_EVENT_STATUS_CHANGED)))
+ avrcp_get_play_status(session);
+
return FALSE;
}
@@ -1462,31 +1964,6 @@ static void avrcp_get_capabilities(struct avrcp *session)
session);
}
-static gboolean avrcp_get_play_status_rsp(struct avctp *conn,
- uint8_t code, uint8_t subunit,
- uint8_t *operands, size_t operand_count,
- void *user_data)
-{
- return FALSE;
-}
-
-static void avrcp_get_play_status(struct avrcp *session)
-{
- uint8_t buf[AVRCP_HEADER_LENGTH];
- struct avrcp_header *pdu = (void *) buf;
-
- memset(buf, 0, sizeof(buf));
-
- set_company_id(pdu->company_id, IEEEID_BTSIG);
- pdu->pdu_id = AVRCP_GET_PLAY_STATUS;
- pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
-
- avctp_send_vendordep_req(session->conn, AVC_CTYPE_STATUS,
- AVC_SUBUNIT_PANEL, buf, sizeof(buf),
- avrcp_get_play_status_rsp,
- session);
-}
-
static struct avrcp *find_session(GSList *list, struct audio_device *dev)
{
for (; list; list = list->next) {
@@ -1515,7 +1992,8 @@ static void session_tg_init(struct avrcp *session)
session->control_handlers = tg_control_handlers;
if (session->version >= 0x0104) {
- register_notification(session, AVRCP_EVENT_VOLUME_CHANGED);
+ avrcp_register_notification(session,
+ AVRCP_EVENT_VOLUME_CHANGED);
if (session->features & AVRCP_FEATURE_BROWSING)
avctp_connect_browsing(session->conn);
}
@@ -1532,19 +2010,75 @@ static void session_tg_init(struct avrcp *session)
static void session_ct_init(struct avrcp *session)
{
+ struct avrcp_player *player;
+
session->control_handlers = ct_control_handlers;
DBG("%p version 0x%04x", session, session->version);
- if (session->version >= 0x0103) {
- avrcp_get_capabilities(session);
- avrcp_get_play_status(session);
- }
-
session->control_id = avctp_register_pdu_handler(session->conn,
AVC_OP_VENDORDEP,
handle_vendordep_pdu,
session);
+
+ if (session->version < 0x0103)
+ return;
+
+ player = g_new0(struct avrcp_player, 1);
+ player->sessions = g_slist_prepend(player->sessions, session);
+ session->player = player;
+
+ avrcp_get_capabilities(session);
+}
+
+static void session_destroy(struct avrcp *session)
+{
+ struct avrcp_server *server = session->server;
+
+ server->sessions = g_slist_remove(server->sessions, session);
+
+ if (session->control_id > 0)
+ avctp_unregister_pdu_handler(session->control_id);
+
+ if (session->browsing_id > 0)
+ avctp_unregister_browsing_pdu_handler(session->browsing_id);
+
+ g_free(session);
+}
+
+static void session_tg_destroy(struct avrcp *session)
+{
+ struct avrcp_player *player = session->player;
+
+ DBG("%p", session);
+
+ if (player != NULL)
+ player->sessions = g_slist_remove(player->sessions, session);
+
+ session_destroy(session);
+}
+
+static void player_destroy(gpointer data)
+{
+ struct avrcp_player *player = data;
+
+ if (player->destroy)
+ player->destroy(player->user_data);
+
+ g_slist_free(player->sessions);
+ g_free(player);
+}
+
+static void session_ct_destroy(struct avrcp *session)
+{
+ struct avrcp_player *player = session->player;
+
+ DBG("%p", session);
+
+ if (player != NULL)
+ player_destroy(player);
+
+ session_destroy(session);
}
static struct avrcp *session_create(struct avrcp_server *server,
@@ -1573,9 +2107,11 @@ static struct avrcp *session_create(struct avrcp_server *server,
if (session->target) {
session->init = session_tg_init;
+ session->destroy = session_tg_destroy;
rec = btd_device_get_record(dev->btd_dev, AVRCP_REMOTE_UUID);
} else {
session->init = session_ct_init;
+ session->destroy = session_ct_destroy;
rec = btd_device_get_record(dev->btd_dev, AVRCP_TARGET_UUID);
}
@@ -1594,25 +2130,6 @@ static struct avrcp *session_create(struct avrcp_server *server,
return session;
}
-static void session_destroy(struct avrcp *session)
-{
- struct avrcp_server *server = session->server;
- struct avrcp_player *player = session->player;
-
- server->sessions = g_slist_remove(server->sessions, session);
-
- if (session->control_id > 0)
- avctp_unregister_pdu_handler(session->control_id);
-
- if (session->browsing_id > 0)
- avctp_unregister_browsing_pdu_handler(session->browsing_id);
-
- if (player != NULL)
- player->sessions = g_slist_remove(player->sessions, session);
-
- g_free(session);
-}
-
static void state_changed(struct audio_device *dev, avctp_state_t old_state,
avctp_state_t new_state, void *user_data)
{
@@ -1630,7 +2147,7 @@ static void state_changed(struct audio_device *dev, avctp_state_t old_state,
if (session == NULL)
break;
- session_destroy(session);
+ session->destroy(session);
break;
case AVCTP_STATE_CONNECTING:
@@ -1739,17 +2256,6 @@ int avrcp_register(const bdaddr_t *src, GKeyFile *config)
return 0;
}
-static void player_destroy(gpointer data)
-{
- struct avrcp_player *player = data;
-
- if (player->destroy)
- player->destroy(player->user_data);
-
- g_slist_free(player->sessions);
- g_free(player);
-}
-
void avrcp_unregister(const bdaddr_t *src)
{
struct avrcp_server *server;
diff --git a/audio/player.c b/audio/player.c
new file mode 100644
index 0000000..d6e499c
--- /dev/null
+++ b/audio/player.c
@@ -0,0 +1,404 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2007 Nokia Corporation
+ * Copyright (C) 2004-2009 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2012-2012 Intel Corporation
+ *
+ * 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 <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <gdbus.h>
+
+#include "log.h"
+#include "player.h"
+#include "dbus-common.h"
+#include "error.h"
+
+#define MEDIA_PLAYER_INTERFACE "org.bluez.MediaPlayer"
+
+struct player_callback {
+ const struct media_player_callback *cbs;
+ void *user_data;
+};
+
+struct media_player {
+ char *path; /* Player object path */
+ GHashTable *settings; /* Player settings */
+ GHashTable *track; /* Player current track */
+ char *status;
+ uint32_t position;
+ GTimer *progress;
+ guint process_id;
+ struct player_callback *cb;
+ GSList *pending;
+};
+
+static void append_settings(void *key, void *value, void *user_data)
+{
+ DBusMessageIter *dict = user_data;
+
+ dict_append_entry(dict, key, DBUS_TYPE_STRING, &value);
+}
+
+static void append_metadata(void *key, void *value, void *user_data)
+{
+ DBusMessageIter *dict = user_data;
+
+ if (strcasecmp((char *) key, "Duration") == 0 ||
+ strcasecmp((char *) key, "Track") == 0 ||
+ strcasecmp((char *) key, "NumberOfTracks") == 0) {
+ uint32_t num = atoi(value);
+ dict_append_entry(dict, key, DBUS_TYPE_UINT32, &num);
+ return;
+ }
+
+ dict_append_entry(dict, key, DBUS_TYPE_STRING, &value);
+}
+
+static DBusMessage *media_player_get_properties(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct media_player *mp = data;
+ DBusMessage *reply;
+ DBusMessageIter iter, dict;
+ uint32_t position;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING
+ DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+ &dict);
+
+ position = media_player_get_position(mp);
+ dict_append_entry(&dict, "Position", DBUS_TYPE_UINT32, &position);
+
+ dict_append_entry(&dict, "Status", DBUS_TYPE_STRING, &mp->status);
+
+ g_hash_table_foreach(mp->settings, append_settings, &dict);
+
+ dbus_message_iter_close_container(&iter, &dict);
+
+ return reply;
+}
+
+static DBusMessage *media_player_get_track(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct media_player *mp = data;
+ DBusMessage *reply;
+ DBusMessageIter iter, dict;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING
+ DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+ &dict);
+
+ g_hash_table_foreach(mp->track, append_metadata, &dict);
+
+ dbus_message_iter_close_container(&iter, &dict);
+
+ return reply;
+}
+
+static DBusMessage *media_player_set_property(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct media_player *mp = data;
+ struct player_callback *cb = mp->cb;
+ DBusMessageIter iter;
+ DBusMessageIter var;
+ const char *key, *value, *curval;
+
+ if (!dbus_message_iter_init(msg, &iter))
+ return btd_error_invalid_args(msg);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+ return btd_error_invalid_args(msg);
+
+ dbus_message_iter_get_basic(&iter, &key);
+ dbus_message_iter_next(&iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+ return btd_error_invalid_args(msg);
+
+ dbus_message_iter_recurse(&iter, &var);
+
+ if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING)
+ return btd_error_invalid_args(msg);
+
+ dbus_message_iter_get_basic(&var, &value);
+
+ if (g_strcmp0(key, "Equalizer") != 0 &&
+ g_strcmp0(key, "Repeat") != 0 &&
+ g_strcmp0(key, "Shuffle") != 0 &&
+ g_strcmp0(key, "Scan") != 0)
+ return btd_error_invalid_args(msg);
+
+ if (cb == NULL || cb->cbs->set_setting == NULL)
+ return btd_error_not_supported(msg);
+
+ curval = g_hash_table_lookup(mp->settings, key);
+ if (g_strcmp0(curval, value) == 0)
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+
+ if (!cb->cbs->set_setting(mp, key, value, cb->user_data))
+ return btd_error_invalid_args(msg);
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static const GDBusMethodTable media_player_methods[] = {
+ { GDBUS_METHOD("GetProperties",
+ NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+ media_player_get_properties) },
+ { GDBUS_METHOD("GetTrack",
+ NULL, GDBUS_ARGS({ "metadata", "a{sv}" }),
+ media_player_get_track) },
+ { GDBUS_METHOD("SetProperty",
+ GDBUS_ARGS({ "name", "s" }, { "value", "v" }),
+ NULL, media_player_set_property) },
+ { }
+};
+
+static const GDBusSignalTable media_player_signals[] = {
+ { GDBUS_SIGNAL("PropertyChanged",
+ GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
+ { GDBUS_SIGNAL("TrackChanged",
+ GDBUS_ARGS({ "metadata", "a{sv}" })) },
+ { }
+};
+
+void media_player_destroy(struct media_player *mp)
+{
+ DBG("%s", mp->path);
+
+ g_dbus_unregister_interface(btd_get_dbus_connection(), mp->path,
+ MEDIA_PLAYER_INTERFACE);
+
+ if (mp->track)
+ g_hash_table_unref(mp->track);
+
+ if (mp->settings)
+ g_hash_table_unref(mp->settings);
+
+ if (mp->process_id > 0)
+ g_source_remove(mp->process_id);
+
+ g_timer_destroy(mp->progress);
+ g_free(mp->cb);
+ g_free(mp->status);
+ g_free(mp->path);
+ g_free(mp);
+}
+
+struct media_player *media_player_controller_create(const char *path)
+{
+ struct media_player *mp;
+
+ mp = g_new0(struct media_player, 1);
+ mp->path = g_strdup_printf("%s/player1", path);
+ mp->settings = g_hash_table_new_full(g_str_hash, g_str_equal,
+ g_free, g_free);
+ mp->track = g_hash_table_new_full(g_str_hash, g_str_equal,
+ g_free, g_free);
+ mp->progress = g_timer_new();
+
+ if (!g_dbus_register_interface(btd_get_dbus_connection(),
+ mp->path, MEDIA_PLAYER_INTERFACE,
+ media_player_methods,
+ media_player_signals,
+ NULL, mp, NULL)) {
+ error("D-Bus failed to register %s path", mp->path);
+ media_player_destroy(mp);
+ return NULL;
+ }
+
+ DBG("%s", mp->path);
+
+ return mp;
+}
+
+uint32_t media_player_get_position(struct media_player *mp)
+{
+ double timedelta;
+ uint32_t sec, msec;
+
+ if (g_strcmp0(mp->status, "playing") != 0)
+ return mp->position;
+
+ timedelta = g_timer_elapsed(mp->progress, NULL);
+
+ sec = (uint32_t) timedelta;
+ msec = (uint32_t) ((timedelta - sec) * 1000);
+
+ return mp->position + sec * 1000 + msec;
+}
+
+void media_player_set_position(struct media_player *mp, uint32_t position)
+{
+ DBG("%u", position);
+
+ if (mp->position == position)
+ return;
+
+ mp->position = position;
+ g_timer_start(mp->progress);
+
+ emit_property_changed(mp->path, MEDIA_PLAYER_INTERFACE, "Position",
+ DBUS_TYPE_UINT32, &mp->position);
+}
+
+void media_player_set_setting(struct media_player *mp, const char *key,
+ const char *value)
+{
+ char *curval;
+
+ DBG("%s: %s", key, value);
+
+ curval = g_hash_table_lookup(mp->settings, key);
+ if (g_strcmp0(curval, value) == 0)
+ return;
+
+ g_hash_table_replace(mp->settings, g_strdup(key), g_strdup(value));
+
+ emit_property_changed(mp->path, MEDIA_PLAYER_INTERFACE, key,
+ DBUS_TYPE_STRING, &value);
+}
+
+const char *media_player_get_status(struct media_player *mp)
+{
+ return mp->status;
+}
+
+void media_player_set_status(struct media_player *mp, const char *status)
+{
+ DBG("%s", status);
+
+ if (g_strcmp0(mp->status, status) == 0)
+ return;
+
+ g_free(mp->status);
+ mp->status = g_strdup(status);
+
+ emit_property_changed(mp->path, MEDIA_PLAYER_INTERFACE, "Status",
+ DBUS_TYPE_STRING, &status);
+
+ mp->position = media_player_get_position(mp);
+ g_timer_start(mp->progress);
+}
+
+static gboolean process_metadata_changed(void *user_data)
+{
+ struct media_player *mp = user_data;
+ DBusMessage *signal;
+ DBusMessageIter iter, dict;
+
+ mp->process_id = 0;
+
+ signal = dbus_message_new_signal(mp->path, MEDIA_PLAYER_INTERFACE,
+ "TrackChanged");
+ if (signal == NULL) {
+ error("Unable to allocate TrackChanged signal");
+ return FALSE;
+ }
+
+ dbus_message_iter_init_append(signal, &iter);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING
+ DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+ &dict);
+
+
+ g_hash_table_foreach(mp->track, append_metadata, &dict);
+
+ dbus_message_iter_close_container(&iter, &dict);
+
+ g_dbus_send_message(btd_get_dbus_connection(), signal);
+
+ return FALSE;
+}
+
+void media_player_set_metadata(struct media_player *mp, const char *key,
+ void *data, size_t len)
+{
+ char *value, *curval;
+
+ value = g_strndup(data, len);
+
+ DBG("%s: %s", key, value);
+
+ curval = g_hash_table_lookup(mp->track, key);
+ if (g_strcmp0(curval, value) == 0) {
+ g_free(value);
+ return;
+ }
+
+ if (mp->process_id == 0) {
+ g_hash_table_remove_all(mp->track);
+ mp->process_id = g_idle_add(process_metadata_changed, mp);
+ }
+
+ g_hash_table_replace(mp->track, g_strdup(key), value);
+}
+
+void media_player_set_callbacks(struct media_player *mp,
+ const struct media_player_callback *cbs,
+ void *user_data)
+{
+ struct player_callback *cb;
+
+ if (mp->cb)
+ g_free(mp->cb);
+
+ cb = g_new0(struct player_callback, 1);
+ cb->cbs = cbs;
+ cb->user_data = user_data;
+
+ mp->cb = cb;
+}
diff --git a/audio/player.h b/audio/player.h
new file mode 100644
index 0000000..4a6a9cc
--- /dev/null
+++ b/audio/player.h
@@ -0,0 +1,46 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2007 Nokia Corporation
+ * Copyright (C) 2004-2009 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2012-2012 Intel Corporation
+ *
+ *
+ * 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
+ *
+ */
+
+struct media_player;
+
+struct media_player_callback {
+ bool (*set_setting) (struct media_player *mp, const char *key,
+ const char *value, void *user_data);
+};
+
+struct media_player *media_player_controller_create(const char *path);
+void media_player_destroy(struct media_player *mp);
+uint32_t media_player_get_position(struct media_player *mp);
+void media_player_set_position(struct media_player *mp, uint32_t position);
+void media_player_set_setting(struct media_player *mp, const char *key,
+ const char *value);
+const char *media_player_get_status(struct media_player *mp);
+void media_player_set_status(struct media_player *mp, const char *status);
+void media_player_set_metadata(struct media_player *mp, const char *key,
+ void *data, size_t len);
+
+void media_player_set_callbacks(struct media_player *mp,
+ const struct media_player_callback *cbs,
+ void *user_data);
--
1.7.11.7
^ permalink raw reply related
* [PATCH BlueZ 06/11] AVRCP: Add support for settings changed event
From: Luiz Augusto von Dentz @ 2012-10-25 13:59 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1351173554-28039-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This event can be registered by the CT to get notified when any setting
is changed.
Per spec every single setting must be sent so the CT can track settings
being added or removed.
---
audio/avrcp.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
audio/avrcp.h | 2 ++
audio/media.c | 26 ++++++++++++++++++++++++-
3 files changed, 88 insertions(+), 2 deletions(-)
diff --git a/audio/avrcp.c b/audio/avrcp.c
index 88ba3aa..02e4581 100644
--- a/audio/avrcp.c
+++ b/audio/avrcp.c
@@ -431,12 +431,29 @@ static void set_company_id(uint8_t cid[3], const uint32_t cid_in)
cid[2] = cid_in;
}
+static int player_get_attribute(struct avrcp_player *player, uint8_t attr)
+{
+ int value;
+
+ DBG("attr %u", attr);
+
+ if (player == NULL)
+ return -ENOENT;
+
+ value = player->cb->get_setting(attr, player->user_data);
+ if (value < 0)
+ DBG("attr %u not supported by player", attr);
+
+ return value;
+}
+
void avrcp_player_event(struct avrcp_player *player, uint8_t id, void *data)
{
uint8_t buf[AVRCP_HEADER_LENGTH + 9];
struct avrcp_header *pdu = (void *) buf;
uint16_t size;
GSList *l;
+ GList *settings;
if (player->sessions == NULL)
return;
@@ -465,6 +482,22 @@ void avrcp_player_event(struct avrcp_player *player, uint8_t id, void *data)
case AVRCP_EVENT_TRACK_REACHED_START:
size = 1;
break;
+ case AVRCP_EVENT_SETTINGS_CHANGED:
+ size = 2;
+ settings = data;
+ pdu->params[1] = g_list_length(settings);
+ for (; settings; settings = settings->next) {
+ uint8_t attr = GPOINTER_TO_UINT(settings->data);
+ int val;
+
+ val = player_get_attribute(player, attr);
+ if (val < 0)
+ continue;
+
+ pdu->params[++size] = attr;
+ pdu->params[++size] = val;
+ }
+ break;
default:
error("Unknown event %u", id);
return;
@@ -641,11 +674,12 @@ static uint8_t avrcp_handle_get_capabilities(struct avrcp *session,
return AVC_CTYPE_STABLE;
case CAP_EVENTS_SUPPORTED:
- pdu->params[1] = 4;
+ pdu->params[1] = 5;
pdu->params[2] = AVRCP_EVENT_STATUS_CHANGED;
pdu->params[3] = AVRCP_EVENT_TRACK_CHANGED;
pdu->params[4] = AVRCP_EVENT_TRACK_REACHED_START;
pdu->params[5] = AVRCP_EVENT_TRACK_REACHED_END;
+ pdu->params[6] = AVRCP_EVENT_SETTINGS_CHANGED;
pdu->params_len = htons(2 + pdu->params[1]);
return AVC_CTYPE_STABLE;
@@ -1011,6 +1045,14 @@ static uint64_t player_get_uid(struct avrcp_player *player)
return player->cb->get_uid(player->user_data);
}
+static GList *player_list_settings(struct avrcp_player *player)
+{
+ if (player == NULL)
+ return NULL;
+
+ return player->cb->list_settings(player->user_data);
+}
+
static uint8_t avrcp_handle_register_notification(struct avrcp *session,
struct avrcp_header *pdu,
uint8_t transaction)
@@ -1018,6 +1060,7 @@ static uint8_t avrcp_handle_register_notification(struct avrcp *session,
struct avrcp_player *player = session->player;
uint16_t len = ntohs(pdu->params_len);
uint64_t uid;
+ GList *settings;
/*
* 1 byte for EventID, 4 bytes for Playback interval but the latest
@@ -1043,6 +1086,23 @@ static uint8_t avrcp_handle_register_notification(struct avrcp *session,
case AVRCP_EVENT_TRACK_REACHED_START:
len = 1;
break;
+ case AVRCP_EVENT_SETTINGS_CHANGED:
+ settings = player_list_settings(player);
+
+ pdu->params[++len] = g_list_length(settings);
+ for (; settings; settings = settings->next) {
+ uint8_t attr = GPOINTER_TO_UINT(settings->data);
+ int val;
+
+ val = player_get_attribute(player, attr);
+ if (val < 0)
+ continue;
+
+ pdu->params[++len] = attr;
+ pdu->params[++len] = val;
+ }
+
+ break;
default:
/* All other events are not supported yet */
goto err;
diff --git a/audio/avrcp.h b/audio/avrcp.h
index 31fdf8d..7f54adb 100644
--- a/audio/avrcp.h
+++ b/audio/avrcp.h
@@ -73,10 +73,12 @@
#define AVRCP_EVENT_TRACK_CHANGED 0x02
#define AVRCP_EVENT_TRACK_REACHED_END 0x03
#define AVRCP_EVENT_TRACK_REACHED_START 0x04
+#define AVRCP_EVENT_SETTINGS_CHANGED 0x08
#define AVRCP_EVENT_VOLUME_CHANGED 0x0d
#define AVRCP_EVENT_LAST AVRCP_EVENT_VOLUME_CHANGED
struct avrcp_player_cb {
+ GList *(*list_settings) (void *user_data);
int (*get_setting) (uint8_t attr, void *user_data);
int (*set_setting) (uint8_t attr, uint8_t value, void *user_data);
uint64_t (*get_uid) (void *user_data);
diff --git a/audio/media.c b/audio/media.c
index 28ed942..7abf077 100644
--- a/audio/media.c
+++ b/audio/media.c
@@ -1194,6 +1194,18 @@ static const char *metadata_to_str(uint32_t id)
return NULL;
}
+static GList *list_settings(void *user_data)
+{
+ struct media_player *mp = user_data;
+
+ DBG("");
+
+ if (mp->settings == NULL)
+ return NULL;
+
+ return g_hash_table_get_keys(mp->settings);
+}
+
static int get_setting(uint8_t attr, void *user_data)
{
struct media_player *mp = user_data;
@@ -1344,6 +1356,7 @@ static void set_volume(uint8_t volume, struct audio_device *dev, void *user_data
}
static struct avrcp_player_cb player_cb = {
+ .list_settings = list_settings,
.get_setting = get_setting,
.set_setting = set_setting,
.list_metadata = list_metadata,
@@ -1427,8 +1440,9 @@ static gboolean set_player_property(struct media_player *mp, const char *key,
DBusMessageIter *entry)
{
DBusMessageIter var;
- const char *value;
+ const char *value, *curval;
int attr, val;
+ GList *settings;
if (dbus_message_iter_get_arg_type(entry) != DBUS_TYPE_VARIANT)
return FALSE;
@@ -1454,11 +1468,21 @@ static gboolean set_player_property(struct media_player *mp, const char *key,
if (val < 0)
return FALSE;
+ curval = g_hash_table_lookup(mp->settings, GUINT_TO_POINTER(attr));
+ if (g_strcmp0(curval, value) == 0)
+ return TRUE;
+
DBG("%s=%s", key, value);
g_hash_table_replace(mp->settings, GUINT_TO_POINTER(attr),
GUINT_TO_POINTER(val));
+ settings = list_settings(mp);
+
+ avrcp_player_event(mp->player, AVRCP_EVENT_SETTINGS_CHANGED, settings);
+
+ g_list_free(settings);
+
return TRUE;
}
--
1.7.11.7
^ permalink raw reply related
* [PATCH BlueZ 05/11] AVCTP: Reduce verbosity of PDU parsing
From: Luiz Augusto von Dentz @ 2012-10-25 13:59 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1351173554-28039-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This information is not really useful as debug and there are better
tools for sniffing traffic such as hcidump and wireshark.
---
audio/avctp.c | 23 -----------------------
1 file changed, 23 deletions(-)
diff --git a/audio/avctp.c b/audio/avctp.c
index 0db6031..5f6a677 100644
--- a/audio/avctp.c
+++ b/audio/avctp.c
@@ -471,8 +471,6 @@ static int avctp_send(struct avctp_channel *control, uint8_t transaction,
struct iovec iov[2];
int sk, err = 0;
- DBG("transaction %u", transaction);
-
iov[0].iov_base = control->buffer;
iov[0].iov_len = sizeof(*avctp) + sizeof(*avc);
iov[1].iov_base = operands;
@@ -637,10 +635,6 @@ static gboolean session_browsing_cb(GIOChannel *chan, GIOCondition cond,
goto failed;
avctp = (struct avctp_header *) buf;
- DBG("AVCTP transaction %u, packet type %u, C/R %u, IPID %u, "
- "PID 0x%04X",
- avctp->transaction, avctp->packet_type,
- avctp->cr, avctp->ipid, ntohs(avctp->pid));
if (avctp->packet_type != AVCTP_PACKET_SINGLE)
goto failed;
@@ -698,8 +692,6 @@ static gboolean session_cb(GIOChannel *chan, GIOCondition cond,
if (ret <= 0)
goto failed;
- DBG("Got %d bytes of data for AVCTP session %p", ret, session);
-
if ((unsigned int) ret < sizeof(struct avctp_header)) {
error("Too small AVCTP packet");
goto failed;
@@ -707,11 +699,6 @@ static gboolean session_cb(GIOChannel *chan, GIOCondition cond,
avctp = (struct avctp_header *) buf;
- DBG("AVCTP transaction %u, packet type %u, C/R %u, IPID %u, "
- "PID 0x%04X",
- avctp->transaction, avctp->packet_type,
- avctp->cr, avctp->ipid, ntohs(avctp->pid));
-
ret -= sizeof(struct avctp_header);
if ((unsigned int) ret < sizeof(struct avc_header)) {
error("Too small AVCTP packet");
@@ -725,12 +712,6 @@ static gboolean session_cb(GIOChannel *chan, GIOCondition cond,
operands = buf + sizeof(struct avctp_header) + sizeof(struct avc_header);
operand_count = ret;
- DBG("AV/C %s 0x%01X, subunit_type 0x%02X, subunit_id 0x%01X, "
- "opcode 0x%02X, %d operands",
- avctp->cr ? "response" : "command",
- avc->code, avc->subunit_type, avc->subunit_id,
- avc->opcode, operand_count);
-
if (avctp->cr == AVCTP_RESPONSE) {
control_response(control, avctp, avc, operands, operand_count);
return TRUE;
@@ -1295,8 +1276,6 @@ static gboolean avctp_passthrough_rsp(struct avctp *session, uint8_t code,
uint8_t subunit, uint8_t *operands,
size_t operand_count, void *user_data)
{
- DBG("code %u", code);
-
if (code != AVC_CTYPE_ACCEPTED)
return FALSE;
@@ -1315,8 +1294,6 @@ int avctp_send_passthrough(struct avctp *session, uint8_t op)
{
uint8_t operands[2];
- DBG("");
-
/* Button pressed */
operands[0] = op & 0x7f;
operands[1] = 0;
--
1.7.11.7
^ permalink raw reply related
* [PATCH BlueZ 04/11] AVRCP: Fix not adding session to player's list of sessions
From: Luiz Augusto von Dentz @ 2012-10-25 13:59 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1351173554-28039-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
If the session is not added in the list if the player gets
unregistered it will not be cleared from the session which might cause a
crash if the tries to access it.
---
audio/avrcp.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/audio/avrcp.c b/audio/avrcp.c
index a14833a..88ba3aa 100644
--- a/audio/avrcp.c
+++ b/audio/avrcp.c
@@ -1442,10 +1442,16 @@ static struct avrcp *find_session(GSList *list, struct audio_device *dev)
static void session_tg_init(struct avrcp *session)
{
struct avrcp_server *server = session->server;
+ struct avrcp_player *player;
DBG("%p version 0x%04x", session, session->version);
- session->player = g_slist_nth_data(server->players, 0);
+ player = g_slist_nth_data(server->players, 0);
+ if (player != NULL) {
+ session->player = player;
+ player->sessions = g_slist_prepend(player->sessions, session);
+ }
+
session->control_handlers = tg_control_handlers;
if (session->version >= 0x0104) {
--
1.7.11.7
^ permalink raw reply related
* [PATCH BlueZ 03/11] AVRCP: Don't respond with errors when no player is registered
From: Luiz Augusto von Dentz @ 2012-10-25 13:59 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1351173554-28039-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Some devices w.g. Sony MW600 will stop using certain commands if an
error happen, so the code now just fake a player and once a real
player is registered it takes place of the fake one.
---
audio/avrcp.c | 117 +++++++++++++++++++++++++++++++++++++++++-----------------
1 file changed, 84 insertions(+), 33 deletions(-)
diff --git a/audio/avrcp.c b/audio/avrcp.c
index fe304d1..a14833a 100644
--- a/audio/avrcp.c
+++ b/audio/avrcp.c
@@ -493,6 +493,17 @@ void avrcp_player_event(struct avrcp_player *player, uint8_t id, void *data)
return;
}
+static const char *player_get_metadata(struct avrcp_player *player, uint32_t attr)
+{
+ if (player != NULL)
+ return player->cb->get_metadata(attr, player->user_data);
+
+ if (attr == AVRCP_MEDIA_ATTRIBUTE_TITLE)
+ return "";
+
+ return NULL;
+}
+
static uint16_t player_write_media_attribute(struct avrcp_player *player,
uint32_t id, uint8_t *buf,
uint16_t *pos,
@@ -504,7 +515,7 @@ static uint16_t player_write_media_attribute(struct avrcp_player *player,
DBG("%u", id);
- value = player->cb->get_metadata(id, player->user_data);
+ value = player_get_metadata(player, id);
if (value == NULL) {
*offset = 0;
return 0;
@@ -589,25 +600,21 @@ static gboolean session_abort_pending_pdu(struct avrcp *session)
return TRUE;
}
-static int player_set_attribute(struct avrcp_player *player,
- uint8_t attr, uint8_t val)
+static int player_set_setting(struct avrcp_player *player, uint8_t id,
+ uint8_t val)
{
- DBG("Change attribute: %u %u", attr, val);
+ if (player == NULL)
+ return -ENOENT;
- return player->cb->set_setting(attr, val, player->user_data);
+ return player->cb->set_setting(id, val, player->user_data);
}
-static int player_get_attribute(struct avrcp_player *player, uint8_t attr)
+static int player_get_setting(struct avrcp_player *player, uint8_t id)
{
- int value;
-
- DBG("attr %u", attr);
-
- value = player->cb->get_setting(attr, player->user_data);
- if (value < 0)
- DBG("attr %u not supported by player", attr);
+ if (player == NULL)
+ return -ENOENT;
- return value;
+ return player->cb->get_setting(id, player->user_data);
}
static uint8_t avrcp_handle_get_capabilities(struct avrcp *session,
@@ -659,7 +666,7 @@ static uint8_t avrcp_handle_list_player_attributes(struct avrcp *session,
uint16_t len = ntohs(pdu->params_len);
unsigned int i;
- if (len != 0 || player == NULL) {
+ if (len != 0) {
pdu->params_len = htons(1);
pdu->params[0] = AVRCP_STATUS_INVALID_PARAM;
return AVC_CTYPE_REJECTED;
@@ -669,7 +676,7 @@ static uint8_t avrcp_handle_list_player_attributes(struct avrcp *session,
goto done;
for (i = 1; i <= AVRCP_ATTRIBUTE_SCAN; i++) {
- if (player_get_attribute(player, i) < 0)
+ if (player_get_setting(player, i) < 0)
continue;
len++;
@@ -691,10 +698,10 @@ static uint8_t avrcp_handle_list_player_values(struct avrcp *session,
uint16_t len = ntohs(pdu->params_len);
unsigned int i;
- if (len != 1 || player == NULL)
+ if (len != 1)
goto err;
- if (player_get_attribute(player, pdu->params[0]) < 0)
+ if (player_get_setting(player, pdu->params[0]) < 0)
goto err;
len = attr_get_max_val(pdu->params[0]);
@@ -713,6 +720,15 @@ err:
return AVC_CTYPE_REJECTED;
}
+static GList *player_list_metadata(struct avrcp_player *player)
+{
+ if (player != NULL)
+ return player->cb->list_metadata(player->user_data);
+
+ return g_list_prepend(NULL,
+ GUINT_TO_POINTER(AVRCP_MEDIA_ATTRIBUTE_TITLE));
+}
+
static uint8_t avrcp_handle_get_element_attributes(struct avrcp *session,
struct avrcp_header *pdu,
uint8_t transaction)
@@ -725,7 +741,7 @@ static uint8_t avrcp_handle_get_element_attributes(struct avrcp *session,
GList *attr_ids;
uint16_t offset;
- if (len < 9 || identifier != 0 || player == NULL)
+ if (len < 9 || identifier != 0)
goto err;
nattr = pdu->params[8];
@@ -738,7 +754,7 @@ static uint8_t avrcp_handle_get_element_attributes(struct avrcp *session,
* Return all available information, at least
* title must be returned if there's a track selected.
*/
- attr_ids = player->cb->list_metadata(player->user_data);
+ attr_ids = player_list_metadata(player);
len = g_list_length(attr_ids);
} else {
unsigned int i;
@@ -794,8 +810,7 @@ static uint8_t avrcp_handle_get_current_player_value(struct avrcp *session,
uint8_t *settings;
unsigned int i;
- if (player == NULL || len <= 1 || pdu->params[0] != len - 1 ||
- player == NULL)
+ if (len <= 1 || pdu->params[0] != len - 1)
goto err;
/*
@@ -819,7 +834,7 @@ static uint8_t avrcp_handle_get_current_player_value(struct avrcp *session,
continue;
}
- val = player_get_attribute(player, settings[i]);
+ val = player_get_setting(player, settings[i]);
if (val < 0)
continue;
@@ -866,7 +881,7 @@ static uint8_t avrcp_handle_set_player_value(struct avrcp *session,
*/
for (len = 0, i = 0, param = &pdu->params[1]; i < pdu->params[0];
i++, param += 2) {
- if (player_set_attribute(player, param[0], param[1]) < 0)
+ if (player_set_setting(player, param[0], param[1]) < 0)
continue;
len++;
@@ -928,6 +943,36 @@ err:
return AVC_CTYPE_REJECTED;
}
+static uint32_t player_get_position(struct avrcp_player *player)
+{
+ if (player == NULL)
+ return 0;
+
+ return player->cb->get_position(player->user_data);
+}
+
+static uint32_t player_get_duration(struct avrcp_player *player)
+{
+ uint32_t num;
+
+ if (player == NULL)
+ return UINT32_MAX;
+
+ num = player->cb->get_duration(player->user_data);
+ if (num == 0)
+ return UINT32_MAX;
+
+ return num;
+}
+
+static uint8_t player_get_status(struct avrcp_player *player)
+{
+ if (player == NULL)
+ return AVRCP_PLAY_STATUS_STOPPED;
+
+ return player->cb->get_status(player->user_data);
+}
+
static uint8_t avrcp_handle_get_play_status(struct avrcp *session,
struct avrcp_header *pdu,
uint8_t transaction)
@@ -937,29 +982,35 @@ static uint8_t avrcp_handle_get_play_status(struct avrcp *session,
uint32_t position;
uint32_t duration;
- if (len != 0 || player == NULL) {
+ if (len != 0) {
pdu->params_len = htons(1);
pdu->params[0] = AVRCP_STATUS_INVALID_PARAM;
return AVC_CTYPE_REJECTED;
}
- position = player->cb->get_position(player->user_data);
- duration = player->cb->get_duration(player->user_data);
- if (duration == 0)
- duration = UINT32_MAX;
+ position = player_get_position(player);
+ duration = player_get_duration(player);
position = htonl(position);
duration = htonl(duration);
memcpy(&pdu->params[0], &duration, 4);
memcpy(&pdu->params[4], &position, 4);
- pdu->params[8] = player->cb->get_status(player->user_data);;
+ pdu->params[8] = player_get_status(player);;
pdu->params_len = htons(9);
return AVC_CTYPE_STABLE;
}
+static uint64_t player_get_uid(struct avrcp_player *player)
+{
+ if (player == NULL)
+ return UINT64_MAX;
+
+ return player->cb->get_uid(player->user_data);
+}
+
static uint8_t avrcp_handle_register_notification(struct avrcp *session,
struct avrcp_header *pdu,
uint8_t transaction)
@@ -973,18 +1024,18 @@ static uint8_t avrcp_handle_register_notification(struct avrcp *session,
* one is applicable only for EVENT_PLAYBACK_POS_CHANGED. See AVRCP
* 1.3 spec, section 5.4.2.
*/
- if (len != 5 || player == NULL)
+ if (len != 5)
goto err;
switch (pdu->params[0]) {
case AVRCP_EVENT_STATUS_CHANGED:
len = 2;
- pdu->params[1] = player->cb->get_status(player->user_data);
+ pdu->params[1] = player_get_status(player);
break;
case AVRCP_EVENT_TRACK_CHANGED:
len = 9;
- uid = player->cb->get_uid(player->user_data);
+ uid = player_get_uid(player);
memcpy(&pdu->params[1], &uid, sizeof(uint64_t));
break;
--
1.7.11.7
^ permalink raw reply related
* [PATCH BlueZ 02/11] AVRCP: Fix using void * for metadata values
From: Luiz Augusto von Dentz @ 2012-10-25 13:59 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1351173554-28039-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This replaces get_metadata callback with get_string and get_uint32
which uses proper types as return.
---
audio/avrcp.c | 23 ++++----------
audio/avrcp.h | 3 +-
audio/media.c | 96 ++++++++++++++++++++++-------------------------------------
3 files changed, 42 insertions(+), 80 deletions(-)
diff --git a/audio/avrcp.c b/audio/avrcp.c
index 2f5df21..fe304d1 100644
--- a/audio/avrcp.c
+++ b/audio/avrcp.c
@@ -500,8 +500,7 @@ static uint16_t player_write_media_attribute(struct avrcp_player *player,
{
uint16_t len;
uint16_t attr_len;
- char valstr[20];
- void *value;
+ const char *value = NULL;
DBG("%u", id);
@@ -511,15 +510,6 @@ static uint16_t player_write_media_attribute(struct avrcp_player *player,
return 0;
}
- switch (id) {
- case AVRCP_MEDIA_ATTRIBUTE_TRACK:
- case AVRCP_MEDIA_ATTRIBUTE_N_TRACKS:
- case AVRCP_MEDIA_ATTRIBUTE_DURATION:
- snprintf(valstr, 20, "%u", GPOINTER_TO_UINT(value));
- value = valstr;
- break;
- }
-
attr_len = strlen(value);
value = ((char *) value) + *offset;
len = attr_len - *offset;
@@ -946,7 +936,6 @@ static uint8_t avrcp_handle_get_play_status(struct avrcp *session,
uint16_t len = ntohs(pdu->params_len);
uint32_t position;
uint32_t duration;
- void *pduration;
if (len != 0 || player == NULL) {
pdu->params_len = htons(1);
@@ -955,14 +944,12 @@ static uint8_t avrcp_handle_get_play_status(struct avrcp *session,
}
position = player->cb->get_position(player->user_data);
- pduration = player->cb->get_metadata(AVRCP_MEDIA_ATTRIBUTE_DURATION,
- player->user_data);
- if (pduration != NULL)
- duration = htonl(GPOINTER_TO_UINT(pduration));
- else
- duration = htonl(UINT32_MAX);
+ duration = player->cb->get_duration(player->user_data);
+ if (duration == 0)
+ duration = UINT32_MAX;
position = htonl(position);
+ duration = htonl(duration);
memcpy(&pdu->params[0], &duration, 4);
memcpy(&pdu->params[4], &position, 4);
diff --git a/audio/avrcp.h b/audio/avrcp.h
index 6c651dd..31fdf8d 100644
--- a/audio/avrcp.h
+++ b/audio/avrcp.h
@@ -80,10 +80,11 @@ struct avrcp_player_cb {
int (*get_setting) (uint8_t attr, void *user_data);
int (*set_setting) (uint8_t attr, uint8_t value, void *user_data);
uint64_t (*get_uid) (void *user_data);
- void *(*get_metadata) (uint32_t id, void *user_data);
+ const char *(*get_metadata) (uint32_t id, void *user_data);
GList *(*list_metadata) (void *user_data);
uint8_t (*get_status) (void *user_data);
uint32_t (*get_position) (void *user_data);
+ uint32_t (*get_duration) (void *user_data);
void (*set_volume) (uint8_t volume, struct audio_device *dev,
void *user_data);
};
diff --git a/audio/media.c b/audio/media.c
index f2b5b2f..28ed942 100644
--- a/audio/media.c
+++ b/audio/media.c
@@ -100,18 +100,11 @@ struct media_player {
guint track_watch;
uint8_t status;
uint32_t position;
+ uint32_t duration;
uint8_t volume;
GTimer *timer;
};
-struct metadata_value {
- int type;
- union {
- char *str;
- uint32_t num;
- } value;
-};
-
static GSList *adapters = NULL;
static void endpoint_request_free(struct endpoint_request *request)
@@ -1281,28 +1274,16 @@ static uint64_t get_uid(void *user_data)
return 0;
}
-static void *get_metadata(uint32_t id, void *user_data)
+static const char *get_metadata(uint32_t id, void *user_data)
{
struct media_player *mp = user_data;
- struct metadata_value *value;
DBG("%s", metadata_to_str(id));
if (mp->track == NULL)
return NULL;
- value = g_hash_table_lookup(mp->track, GUINT_TO_POINTER(id));
- if (!value)
- return NULL;
-
- switch (value->type) {
- case DBUS_TYPE_STRING:
- return value->value.str;
- case DBUS_TYPE_UINT32:
- return GUINT_TO_POINTER(value->value.num);
- }
-
- return NULL;
+ return g_hash_table_lookup(mp->track, GUINT_TO_POINTER(id));
}
static uint8_t get_status(void *user_data)
@@ -1329,6 +1310,13 @@ static uint32_t get_position(void *user_data)
return mp->position + sec * 1000 + msec;
}
+static uint32_t get_duration(void *user_data)
+{
+ struct media_player *mp = user_data;
+
+ return mp->duration;
+}
+
static void set_volume(uint8_t volume, struct audio_device *dev, void *user_data)
{
struct media_player *mp = user_data;
@@ -1362,6 +1350,7 @@ static struct avrcp_player_cb player_cb = {
.get_uid = get_uid,
.get_metadata = get_metadata,
.get_position = get_position,
+ .get_duration = get_duration,
.get_status = get_status,
.set_volume = set_volume
};
@@ -1407,7 +1396,6 @@ static gboolean set_status(struct media_player *mp, DBusMessageIter *iter)
static gboolean set_position(struct media_player *mp, DBusMessageIter *iter)
{
uint32_t value;
- struct metadata_value *duration;
if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT32)
return FALSE;
@@ -1424,15 +1412,11 @@ static gboolean set_position(struct media_player *mp, DBusMessageIter *iter)
return TRUE;
}
- duration = g_hash_table_lookup(mp->track, GUINT_TO_POINTER(
- AVRCP_MEDIA_ATTRIBUTE_DURATION));
-
/*
* If position is the maximum value allowed or greater than track's
* duration, we send a track-reached-end event.
*/
- if (mp->position == UINT32_MAX ||
- (duration && mp->position >= duration->value.num))
+ if (mp->position == UINT32_MAX || mp->position >= mp->duration)
avrcp_player_event(mp->player, AVRCP_EVENT_TRACK_REACHED_END,
NULL);
@@ -1505,19 +1489,6 @@ static gboolean property_changed(DBusConnection *connection, DBusMessage *msg,
return TRUE;
}
-static void metadata_value_free(gpointer data)
-{
- struct metadata_value *value = data;
-
- switch (value->type) {
- case DBUS_TYPE_STRING:
- g_free(value->value.str);
- break;
- }
-
- g_free(value);
-}
-
static gboolean parse_player_metadata(struct media_player *mp,
DBusMessageIter *iter)
{
@@ -1535,14 +1506,18 @@ static gboolean parse_player_metadata(struct media_player *mp,
dbus_message_iter_recurse(iter, &dict);
track = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL,
- metadata_value_free);
+ g_free);
while ((ctype = dbus_message_iter_get_arg_type(&dict)) !=
DBUS_TYPE_INVALID) {
DBusMessageIter entry;
const char *key;
- struct metadata_value *value;
+ const char *string;
+ char valstr[20];
+ char *value;
+ uint32_t num;
int id;
+ int type;
if (ctype != DBUS_TYPE_DICT_ENTRY)
goto parse_error;
@@ -1563,8 +1538,7 @@ static gboolean parse_player_metadata(struct media_player *mp,
dbus_message_iter_recurse(&entry, &var);
- value = g_new0(struct metadata_value, 1);
- value->type = dbus_message_iter_get_arg_type(&var);
+ type = dbus_message_iter_get_arg_type(&var);
switch (id) {
case AVRCP_MEDIA_ATTRIBUTE_TITLE:
@@ -1572,36 +1546,39 @@ static gboolean parse_player_metadata(struct media_player *mp,
case AVRCP_MEDIA_ATTRIBUTE_ARTIST:
case AVRCP_MEDIA_ATTRIBUTE_ALBUM:
case AVRCP_MEDIA_ATTRIBUTE_GENRE:
- if (value->type != DBUS_TYPE_STRING) {
- g_free(value);
+ if (type != DBUS_TYPE_STRING)
goto parse_error;
- }
- dbus_message_iter_get_basic(&var, &value->value.str);
+ dbus_message_iter_get_basic(&var, &string);
break;
case AVRCP_MEDIA_ATTRIBUTE_TRACK:
case AVRCP_MEDIA_ATTRIBUTE_N_TRACKS:
+ if (type != DBUS_TYPE_UINT32)
+ goto parse_error;
+
+ dbus_message_iter_get_basic(&var, &num);
+ break;
case AVRCP_MEDIA_ATTRIBUTE_DURATION:
- if (value->type != DBUS_TYPE_UINT32) {
- g_free(value);
+ if (type != DBUS_TYPE_UINT32)
goto parse_error;
- }
- dbus_message_iter_get_basic(&var, &value->value.num);
+ dbus_message_iter_get_basic(&var, &num);
+ mp->duration = num;
break;
default:
goto parse_error;
}
- switch (value->type) {
+ switch (dbus_message_iter_get_arg_type(&var)) {
case DBUS_TYPE_STRING:
- value->value.str = g_strdup(value->value.str);
- DBG("%s=%s", key, value->value.str);
+ value = g_strdup(string);
break;
default:
- DBG("%s=%u", key, value->value.num);
+ snprintf(valstr, 20, "%u", num);
+ value = g_strdup(valstr);
}
+ DBG("%s=%s", key, value);
g_hash_table_replace(track, GUINT_TO_POINTER(id), value);
dbus_message_iter_next(&dict);
}
@@ -1610,12 +1587,9 @@ static gboolean parse_player_metadata(struct media_player *mp,
g_hash_table_unref(track);
track = NULL;
} else if (title == FALSE) {
- struct metadata_value *value = g_new(struct metadata_value, 1);
uint32_t id = AVRCP_MEDIA_ATTRIBUTE_TITLE;
- value->type = DBUS_TYPE_STRING;
- value->value.str = g_strdup("");
- g_hash_table_insert(track, GUINT_TO_POINTER(id), value);
+ g_hash_table_insert(track, GUINT_TO_POINTER(id), g_strdup(""));
}
if (mp->track != NULL)
--
1.7.11.7
^ permalink raw reply related
* [PATCH BlueZ 01/11] control: Fix Control.Disconnect not generating any reply
From: Luiz Augusto von Dentz @ 2012-10-25 13:59 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
avctp_disconnect does disconnect immediatelly so there is no time to
set the pending message.
---
audio/control.c | 20 ++------------------
1 file changed, 2 insertions(+), 18 deletions(-)
diff --git a/audio/control.c b/audio/control.c
index 37b027b..1620128 100644
--- a/audio/control.c
+++ b/audio/control.c
@@ -65,7 +65,6 @@ struct control {
struct avctp *session;
gboolean target;
DBusMessage *connect;
- DBusMessage *disconnect;
};
static void state_changed(struct audio_device *dev, avctp_state_t old_state,
@@ -88,13 +87,6 @@ static void state_changed(struct audio_device *dev, avctp_state_t old_state,
control->connect = NULL;
}
- if (control->disconnect) {
- g_dbus_send_reply(conn, control->disconnect,
- DBUS_TYPE_INVALID);
- dbus_message_unref(control->disconnect);
- control->disconnect = NULL;
- }
-
if (old_state != AVCTP_STATE_CONNECTED)
break;
@@ -184,14 +176,9 @@ static DBusMessage *control_disconnect(DBusConnection *conn, DBusMessage *msg,
if (!control->session)
return btd_error_not_connected(msg);
- if (control->disconnect)
- return btd_error_in_progress(msg);
-
avctp_disconnect(control->session);
- control->disconnect = dbus_message_ref(msg);
-
- return NULL;
+ return dbus_message_new_method_return(msg);
}
static DBusMessage *key_pressed(DBusConnection *conn, DBusMessage *msg,
@@ -273,7 +260,7 @@ static const GDBusMethodTable control_methods[] = {
NULL, GDBUS_ARGS({ "connected", "b" }),
control_is_connected) },
{ GDBUS_ASYNC_METHOD("Connect", NULL, NULL, control_connect) },
- { GDBUS_ASYNC_METHOD("Disconnect", NULL, NULL, control_disconnect) },
+ { GDBUS_METHOD("Disconnect", NULL, NULL, control_disconnect) },
{ GDBUS_METHOD("Play", NULL, NULL, control_play) },
{ GDBUS_METHOD("Pause", NULL, NULL, control_pause) },
{ GDBUS_METHOD("Stop", NULL, NULL, control_stop) },
@@ -309,9 +296,6 @@ static void path_unregister(void *data)
if (control->connect)
dbus_message_unref(control->connect);
- if (control->disconnect)
- dbus_message_unref(control->disconnect);
-
g_free(control);
dev->control = NULL;
}
--
1.7.11.7
^ permalink raw reply related
* Re: [RFCv1 00/11] Handling physical and logical link
From: Andrei Emeltchenko @ 2012-10-25 13:35 UTC (permalink / raw)
To: Marcel Holtmann; +Cc: linux-bluetooth, Mat Martineau
In-Reply-To: <1351170668.1785.93.camel@aeonflux>
Hi Marcel,
On Thu, Oct 25, 2012 at 06:11:08AM -0700, Marcel Holtmann wrote:
> Hi Andrei,
>
> > Set of patches adding handling for physical and logical link
> > complete events.
> >
> > Changes:
> > * rfcv1: Rebased on top of Mat's patches, preserve Marcel's ack for
> > not-changed-much patches only.
> > * rfcv0: original
>
> all in all, this looks pretty good to me.
>
> Mat, can you take an extra look at it as well.
>
> What is actually missing after this set of patches?
Still a lot. First it is finishing physical link establishment and then
channel move.
Best regards
Andrei Emeltchenko
^ permalink raw reply
* Re: [RFCv1 00/11] Handling physical and logical link
From: Marcel Holtmann @ 2012-10-25 13:11 UTC (permalink / raw)
To: Andrei Emeltchenko; +Cc: linux-bluetooth, Mat Martineau
In-Reply-To: <1351167652-12346-1-git-send-email-Andrei.Emeltchenko.news@gmail.com>
Hi Andrei,
> Set of patches adding handling for physical and logical link
> complete events.
>
> Changes:
> * rfcv1: Rebased on top of Mat's patches, preserve Marcel's ack for
> not-changed-much patches only.
> * rfcv0: original
all in all, this looks pretty good to me.
Mat, can you take an extra look at it as well.
What is actually missing after this set of patches?
Regards
Marcel
^ permalink raw reply
* Re: [RFCv1 11/11] Bluetooth: AMP: Check for hs_hcon instead of ctrl_id
From: Marcel Holtmann @ 2012-10-25 13:09 UTC (permalink / raw)
To: Andrei Emeltchenko; +Cc: linux-bluetooth
In-Reply-To: <1351167652-12346-12-git-send-email-Andrei.Emeltchenko.news@gmail.com>
Hi Andrei,
> When deciding whether to send EFS configuration response with success,
> check rather for existence of High Speed physical link hs_hcon then
> ctrl_id. There might be cases when there is ctrl_id but high speed link
> is not established yet.
>
> Signed-off-by: Andrei Emeltchenko <andrei.emeltchenko@intel.com>
> ---
> net/bluetooth/l2cap_core.c | 4 ++--
> 1 file changed, 2 insertions(+), 2 deletions(-)
Acked-by: Marcel Holtmann <marcel@holtmann.org>
Regards
Marcel
^ permalink raw reply
* Re: [RFCv1 10/11] Bluetooth: Add put(hcon) when deleting hchan
From: Marcel Holtmann @ 2012-10-25 13:09 UTC (permalink / raw)
To: Andrei Emeltchenko; +Cc: linux-bluetooth
In-Reply-To: <1351167652-12346-11-git-send-email-Andrei.Emeltchenko.news@gmail.com>
Hi Andrei,
> When refcnt reaches zero disconnect timeout will run and hci_conn
> will be disconnected.
>
> Signed-off-by: Andrei Emeltchenko <andrei.emeltchenko@intel.com>
> ---
> net/bluetooth/hci_conn.c | 2 ++
> 1 file changed, 2 insertions(+)
Acked-by: Marcel Holtmann <marcel@holtmann.org>
Regards
Marcel
^ permalink raw reply
* Re: [RFCv1 04/11] Bluetooth: AMP: Process Logical Link complete evt
From: Marcel Holtmann @ 2012-10-25 13:08 UTC (permalink / raw)
To: Andrei Emeltchenko; +Cc: linux-bluetooth
In-Reply-To: <1351167652-12346-5-git-send-email-Andrei.Emeltchenko.news@gmail.com>
Hi Andrei,
> After receiving HCI Logical Link Complete event finish EFS
> configuration by sending L2CAP Conf Response with success code.
>
> Signed-off-by: Andrei Emeltchenko <andrei.emeltchenko@intel.com>
> ---
> include/net/bluetooth/l2cap.h | 2 ++
> net/bluetooth/hci_event.c | 42 +++++++++++++++++++++++++++++++++++++++++
> net/bluetooth/l2cap_core.c | 4 ++--
> 3 files changed, 46 insertions(+), 2 deletions(-)
Acked-by: Marcel Holtmann <marcel@holtmann.org>
Regards
Marcel
^ permalink raw reply
* Re: [RFCv1 02/11] Bluetooth: Use helper function sending EFS conf rsp
From: Marcel Holtmann @ 2012-10-25 13:07 UTC (permalink / raw)
To: Andrei Emeltchenko; +Cc: linux-bluetooth
In-Reply-To: <1351167652-12346-3-git-send-email-Andrei.Emeltchenko.news@gmail.com>
Hi Andrei,
> There is helper function used to send EFS Configuration Response.
>
> Signed-off-by: Andrei Emeltchenko <andrei.emeltchenko@intel.com>
> ---
> net/bluetooth/l2cap_core.c | 7 +------
> 1 file changed, 1 insertion(+), 6 deletions(-)
Acked-by: Marcel Holtmann <marcel@holtmann.org>
Regards
Marcel
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox