* [RFC v2 obexd 04/11] fuse: Add request helpers and setpath operation
From: Michał Poczwardowski @ 2012-10-28 0:29 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Michał Poczwardowski
In-Reply-To: <1351384166-15586-1-git-send-email-dmp0x7c5@gmail.com>
---
fuse/helpers.c | 108 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 108 insertions(+), 0 deletions(-)
diff --git a/fuse/helpers.c b/fuse/helpers.c
index 856e1d5..1b8082f 100644
--- a/fuse/helpers.c
+++ b/fuse/helpers.c
@@ -273,3 +273,111 @@ void gobexhlp_disconnect(struct gobexhlp_session* session)
g_free(session);
}
+
+void request_new(struct gobexhlp_session *session,
+ gchar *name)
+{
+ g_print("REQUEST %s\n", name);
+
+ if (session->request != NULL)
+ g_error("Another request (%s) active!\n",
+ session->request->name);
+
+ session->status = 0;
+ session->request = g_malloc0(sizeof(struct gobexhlp_request));
+ session->request->name = name;
+
+ /*
+ * suspend/resume operations recreates g_io_add_watch(),
+ * it fixes obex->io freeze during transfer
+ */
+ g_obex_suspend(session->obex);
+ g_obex_resume(session->obex);
+}
+
+void request_wait_free(struct gobexhlp_session *session)
+{
+ g_print("WAIT for %s\n", session->request->name);
+
+ g_obex_suspend(session->obex);
+ g_obex_resume(session->obex);
+
+ if (session->err != NULL) {
+ g_print("ERROR: %s (%d)\n", session->err->message,
+ session->err->code);
+ g_error_free(session->err);
+ raise(SIGTERM);
+ return;
+ }
+
+ g_mutex_lock(gobexhlp_mutex);
+
+ while (session->request->complete != TRUE)
+ g_cond_wait(gobexhlp_cond, gobexhlp_mutex);
+
+ g_mutex_unlock(gobexhlp_mutex);
+
+ g_free(session->request->name);
+ g_free(session->request);
+ session->request = NULL;
+}
+
+static void complete_func(GObex *obex, GError *err,
+ gpointer user_data)
+{
+ struct gobexhlp_session *session = user_data;
+
+ if (err != NULL) {
+ g_print("ERROR: %s\n", err->message);
+ session->status = -ECANCELED;
+ g_error_free(err);
+ } else {
+ g_print("COMPLETE %s\n", session->request->name);
+ }
+
+ g_mutex_lock(gobexhlp_mutex);
+ session->request->complete = TRUE;
+ g_cond_signal(gobexhlp_cond);
+ g_mutex_unlock(gobexhlp_mutex);
+}
+
+static void response_func(GObex *obex, GError *err, GObexPacket *rsp,
+ gpointer user_data)
+{
+ complete_func(obex, err, user_data);
+}
+
+void gobexhlp_setpath(struct gobexhlp_session *session, const char *path)
+{
+ guint i = 0, split = 0;
+ gchar **path_v;
+ gsize len;
+
+ g_print("gobexhlp_setpath(%s)\n", path);
+
+ if (g_str_has_prefix(path, session->setpath)) {
+ split = strlen(session->setpath);
+ } else {
+ request_new(session, g_strdup_printf("setpath root"));
+ g_obex_setpath(session->obex, "", response_func,
+ session, &session->err);
+ request_wait_free(session);
+ }
+
+ path_v = g_strsplit(path+split, "/", -1);
+ len = g_strv_length(path_v);
+
+ for (i = 0; i < len; i++)
+ if (path_v[i][0] != '\0') {
+ request_new(session,
+ g_strdup_printf("setpath %s", path_v[i]));
+ g_obex_setpath(session->obex, path_v[i],
+ response_func, session, &session->err);
+ request_wait_free(session);
+ }
+
+ g_free(session->setpath);
+ session->setpath = g_strdup(path);
+
+ g_strfreev(path_v);
+}
--
1.7.8.6
^ permalink raw reply related
* [RFC v2 obexd 05/11] fuse: Add readdir operation
From: Michał Poczwardowski @ 2012-10-28 0:29 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Michał Poczwardowski
In-Reply-To: <1351384166-15586-1-git-send-email-dmp0x7c5@gmail.com>
---
fuse/helpers.c | 128 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
fuse/helpers.h | 2 +
fuse/obexfuse.c | 23 ++++++++++
3 files changed, 153 insertions(+), 0 deletions(-)
diff --git a/fuse/helpers.c b/fuse/helpers.c
index 1b8082f..cb0b51d 100644
--- a/fuse/helpers.c
+++ b/fuse/helpers.c
@@ -381,3 +381,131 @@ void gobexhlp_setpath(struct gobexhlp_session *session, const char *path)
g_strfreev(path_v);
}
+
+static void listfolder_xml_element(GMarkupParseContext *ctxt,
+ const gchar *element, const gchar **names,
+ const gchar **values, gpointer user_data,
+ GError **gerr)
+{
+ gchar *key, *pathname, *name = NULL;
+ struct gobexhlp_session *session = user_data;
+ struct stat *stbuf;
+ gint i = 0;
+
+ stbuf = g_malloc0(sizeof(struct stat));
+
+ if ((strcasecmp("file", element) == 0)) {
+ stbuf->st_mode = S_IFREG;
+ } else if ((strcasecmp("folder", element)) == 0) {
+ stbuf->st_mode = S_IFDIR;
+ stbuf->st_mtime = time(NULL);
+ } else {
+ g_free(stbuf);
+ return;
+ }
+
+ for (key = (gchar *) names[i]; key; key = (gchar *) names[++i]) {
+ if (g_str_equal("name", key) == TRUE) {
+ session->lsfiles = g_list_append(session->lsfiles,
+ g_strdup(values[i]));
+ name = g_strdup(values[i]);
+
+ } else if (g_str_equal("size", key) == TRUE) {
+ guint64 size;
+ size = g_ascii_strtoll(values[i], NULL, 10);
+ stbuf->st_size = size;
+
+ } else if (g_str_equal("created", key) == TRUE) {
+ GTimeVal time;
+ GDateTime *datetime;
+ g_time_val_from_iso8601(values[i], &time);
+ datetime = g_date_time_new_from_timeval_utc(&time);
+ stbuf->st_mtime = g_date_time_to_unix(datetime);
+ }
+ }
+
+ if (g_str_equal("/", session->setpath) == TRUE)
+ pathname = g_strdup_printf("/%s", name);
+ else
+ pathname = g_strdup_printf("%s/%s", session->setpath, name);
+
+ g_hash_table_replace(session->file_stat, pathname, stbuf);
+ g_free(name);
+}
+
+static const GMarkupParser parser = {
+ listfolder_xml_element,
+ NULL, NULL, NULL, NULL
+};
+
+static void complete_listfolder_func(GObex *obex, GError *err,
+ gpointer user_data)
+{
+ GMarkupParseContext *ctxt;
+ struct gobexhlp_session *session = user_data;
+ struct gobexhlp_buffer *buffer = session->buffer;
+
+ if (err == NULL) {
+ ctxt = g_markup_parse_context_new(&parser, 0, session, NULL);
+ g_markup_parse_context_parse(ctxt, buffer->data, buffer->size,
+ NULL);
+ g_markup_parse_context_free(ctxt);
+ }
+
+ complete_func(obex, err, user_data);
+}
+
+static gboolean async_get_consumer(const void *buf, gsize len,
+ gpointer user_data)
+{
+ struct gobexhlp_session *session = user_data;
+ struct gobexhlp_buffer *buffer = session->buffer;
+
+ if (buffer->size == 0)
+ buffer->data = g_malloc0(sizeof(char) * len);
+ else
+ buffer->data = g_realloc(buffer->data, buffer->size + len);
+
+ memcpy(buffer->data + buffer->size, buf, len);
+ buffer->size += len;
+
+ g_obex_suspend(session->obex);
+ g_obex_resume(session->obex);
+
+ return TRUE;
+}
+
+GList *gobexhlp_listfolder(struct gobexhlp_session* session,
+ const char *path)
+{
+ struct gobexhlp_buffer *buffer;
+ GObexPacket *req;
+ guint reqpkt;
+
+ gobexhlp_setpath(session, path);
+
+ g_print("gobexhlp_listfolder(%s)\n", path);
+
+ if (session->lsfiles != NULL) {
+ g_list_free_full(session->lsfiles, g_free);
+ session->lsfiles = NULL;
+ }
+
+ session->lsfiles = g_list_alloc();
+ buffer = g_malloc0(sizeof(struct gobexhlp_buffer));
+ session->buffer = buffer;
+
+ request_new(session, g_strdup_printf("listfolder %s", path));
+ req = g_obex_packet_new(G_OBEX_OP_GET, TRUE, G_OBEX_HDR_INVALID);
+ g_obex_packet_add_bytes(req, G_OBEX_HDR_TYPE, OBEX_FTP_LS,
+ strlen(OBEX_FTP_LS) + 1);
+ reqpkt = g_obex_get_req_pkt(session->obex, req,
+ async_get_consumer,
+ complete_listfolder_func,
+ session, &session->err);
+ request_wait_free(session);
+ g_free(buffer->data);
+ g_free(buffer);
+
+ return session->lsfiles;
+}
diff --git a/fuse/helpers.h b/fuse/helpers.h
index 29dc6cf..546d79c 100644
--- a/fuse/helpers.h
+++ b/fuse/helpers.h
@@ -50,3 +50,5 @@ struct gobexhlp_session {
struct gobexhlp_session* gobexhlp_connect(const char *srcstr,
const char *dstsrc);
void gobexhlp_disconnect(struct gobexhlp_session* session);
+
+GList *gobexhlp_listfolder(struct gobexhlp_session* session, const char *path);
diff --git a/fuse/obexfuse.c b/fuse/obexfuse.c
index 79eb990..5de82ec 100644
--- a/fuse/obexfuse.c
+++ b/fuse/obexfuse.c
@@ -89,7 +89,30 @@ void obexfuse_destroy()
g_thread_join(main_gthread);
}
+static int obexfuse_readdir(const char *path, void *buf,
+ fuse_fill_dir_t filler, off_t offset,
+ struct fuse_file_info *fi)
+{
+ int len, i;
+ gchar *string;
+ GList *files;
+
+ filler(buf, ".", NULL, 0);
+ filler(buf, "..", NULL, 0);
+
+ files = gobexhlp_listfolder(session, path);
+ len = g_list_length(files);
+
+ for (i = 1; i < len; i++) { /* element for i==0 is NULL */
+ string = g_list_nth_data(files, i);
+ filler(buf, string, NULL, 0);
+ }
+
+ return session->status;
+}
+
static struct fuse_operations obexfuse_oper = {
+ .readdir = obexfuse_readdir,
.init = obexfuse_init,
.destroy = obexfuse_destroy,
};
--
1.7.8.6
^ permalink raw reply related
* [RFC v2 obexd 06/11] fuse: Add getattr operation
From: Michał Poczwardowski @ 2012-10-28 0:29 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Michał Poczwardowski
In-Reply-To: <1351384166-15586-1-git-send-email-dmp0x7c5@gmail.com>
---
fuse/helpers.c | 6 ++++++
fuse/helpers.h | 2 ++
fuse/obexfuse.c | 34 ++++++++++++++++++++++++++++++++++
3 files changed, 42 insertions(+), 0 deletions(-)
diff --git a/fuse/helpers.c b/fuse/helpers.c
index cb0b51d..a10b706 100644
--- a/fuse/helpers.c
+++ b/fuse/helpers.c
@@ -509,3 +509,9 @@ GList *gobexhlp_listfolder(struct gobexhlp_session* session,
return session->lsfiles;
}
+
+struct stat *gobexhlp_getattr(struct gobexhlp_session* session,
+ const char *path)
+{
+ return g_hash_table_lookup(session->file_stat, path);
+}
diff --git a/fuse/helpers.h b/fuse/helpers.h
index 546d79c..047abae 100644
--- a/fuse/helpers.h
+++ b/fuse/helpers.h
@@ -52,3 +52,5 @@ struct gobexhlp_session* gobexhlp_connect(const char *srcstr,
void gobexhlp_disconnect(struct gobexhlp_session* session);
GList *gobexhlp_listfolder(struct gobexhlp_session* session, const char *path);
+struct stat *gobexhlp_getattr(struct gobexhlp_session* session,
+ const char *path);
diff --git a/fuse/obexfuse.c b/fuse/obexfuse.c
index 5de82ec..f4e85fa 100644
--- a/fuse/obexfuse.c
+++ b/fuse/obexfuse.c
@@ -111,8 +111,42 @@ static int obexfuse_readdir(const char *path, void *buf,
return session->status;
}
+static int obexfuse_getattr(const char *path, struct stat *stbuf)
+{
+ int res = 0;
+ struct stat *stfile;
+
+ memset(stbuf, 0, sizeof(struct stat));
+
+ if (strcmp(path, "/") == 0) {
+ stbuf->st_mode = S_IFDIR | 0755;
+ stbuf->st_nlink = 2;
+ } else {
+ stfile = gobexhlp_getattr(session, path);
+
+ if (stfile == NULL)
+ return -ENOENT;
+
+ if (stfile->st_mode == S_IFREG)
+ stbuf->st_mode = stfile->st_mode | 0666;
+ else /* S_IFDIR */
+ stbuf->st_mode = stfile->st_mode | 0755;
+
+ stbuf->st_nlink = 1;
+ stbuf->st_size = stfile->st_size;
+ stbuf->st_mtime = stbuf->st_atime = stbuf->st_ctime =
+ stfile->st_mtime;
+ stbuf->st_blksize = 512;
+ stbuf->st_blocks = (stbuf->st_size + stbuf->st_blksize)
+ / stbuf->st_blksize;
+ }
+
+ return res;
+}
+
static struct fuse_operations obexfuse_oper = {
.readdir = obexfuse_readdir,
+ .getattr = obexfuse_getattr,
.init = obexfuse_init,
.destroy = obexfuse_destroy,
};
--
1.7.8.6
^ permalink raw reply related
* [RFC v2 obexd 07/11] fuse: Add open and read operations plus location helpers
From: Michał Poczwardowski @ 2012-10-28 0:29 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Michał Poczwardowski
In-Reply-To: <1351384166-15586-1-git-send-email-dmp0x7c5@gmail.com>
---
fuse/helpers.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
fuse/helpers.h | 2 +
fuse/obexfuse.c | 32 ++++++++++++++++++++++++++++
3 files changed, 96 insertions(+), 0 deletions(-)
diff --git a/fuse/helpers.c b/fuse/helpers.c
index a10b706..9b453e0 100644
--- a/fuse/helpers.c
+++ b/fuse/helpers.c
@@ -515,3 +515,65 @@ struct stat *gobexhlp_getattr(struct gobexhlp_session* session,
{
return g_hash_table_lookup(session->file_stat, path);
}
+
+static struct gobexhlp_location *get_location(const char *path)
+{
+ struct gobexhlp_location *location;
+ gchar **directories;
+ guint i, len, fid = 0;
+
+ location = g_malloc0(sizeof(*location));
+ directories = g_strsplit(path, "/", -1);
+ len = g_strv_length(directories);
+
+ for (i = 0; i < len; i++)
+ if (directories[i][0] != '\0') /* protect multi slashes */
+ fid = i; /* last nonempty is a file */
+
+ location->file = g_strdup(directories[fid]);
+ directories[fid][0] = '\0'; /* remove file */
+ location->dir = g_strjoinv("/", directories);
+
+ g_strfreev(directories);
+
+ return location;
+}
+
+void free_location(struct gobexhlp_location *location)
+{
+ g_free(location->file);
+ g_free(location->dir);
+ g_free(location);
+}
+
+struct gobexhlp_buffer *gobexhlp_get(struct gobexhlp_session* session,
+ const char *path)
+{
+ struct gobexhlp_location *l;
+ struct gobexhlp_buffer *buffer;
+ struct stat *stfile;
+ l = get_location(path);
+
+ g_print("gobexhlp_get(%s%s)\n", l->dir, l->file);
+
+ stfile = gobexhlp_getattr(session, path);
+ if (stfile == NULL)
+ return NULL;
+
+ buffer = g_malloc0(sizeof(*buffer));
+
+ if (stfile->st_size == 0)
+ return buffer;
+
+ gobexhlp_setpath(session, l->dir);
+ request_new(session, g_strdup_printf("get %s", path));
+ session->buffer = buffer;
+ g_obex_get_req(session->obex, async_get_consumer,
+ complete_func, session, &session->err,
+ G_OBEX_HDR_NAME, l->file,
+ G_OBEX_HDR_INVALID);
+ free_location(l);
+ request_wait_free(session);
+
+ return buffer;
+}
diff --git a/fuse/helpers.h b/fuse/helpers.h
index 047abae..aa2a194 100644
--- a/fuse/helpers.h
+++ b/fuse/helpers.h
@@ -54,3 +54,5 @@ void gobexhlp_disconnect(struct gobexhlp_session* session);
GList *gobexhlp_listfolder(struct gobexhlp_session* session, const char *path);
struct stat *gobexhlp_getattr(struct gobexhlp_session* session,
const char *path);
+struct gobexhlp_buffer *gobexhlp_get(struct gobexhlp_session* session,
+ const char *path);
diff --git a/fuse/obexfuse.c b/fuse/obexfuse.c
index f4e85fa..9fe2dea 100644
--- a/fuse/obexfuse.c
+++ b/fuse/obexfuse.c
@@ -144,9 +144,41 @@ static int obexfuse_getattr(const char *path, struct stat *stbuf)
return res;
}
+static int obexfuse_open(const char *path, struct fuse_file_info *fi)
+{
+ struct gobexhlp_buffer *file_buffer;
+
+ file_buffer = gobexhlp_get(session, path);
+
+ if (file_buffer == NULL)
+ return -ENOENT;
+
+ fi->fh = (uint64_t)file_buffer;
+
+ return session->status;
+}
+
+static int obexfuse_read(const char *path, char *buf, size_t size,
+ off_t offset, struct fuse_file_info *fi)
+{
+ gsize asize;
+ struct gobexhlp_buffer *file_buffer = (struct gobexhlp_buffer*)fi->fh;
+
+ asize = file_buffer->size - offset;
+
+ if (asize > size)
+ asize = size;
+
+ memcpy(buf, file_buffer->data + offset, asize);
+
+ return asize;
+}
+
static struct fuse_operations obexfuse_oper = {
.readdir = obexfuse_readdir,
.getattr = obexfuse_getattr,
+ .open = obexfuse_open,
+ .read = obexfuse_read,
.init = obexfuse_init,
.destroy = obexfuse_destroy,
};
--
1.7.8.6
^ permalink raw reply related
* [RFC v2 obexd 08/11] fuse: Add write operation
From: Michał Poczwardowski @ 2012-10-28 0:29 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Michał Poczwardowski
In-Reply-To: <1351384166-15586-1-git-send-email-dmp0x7c5@gmail.com>
---
fuse/helpers.c | 44 ++++++++++++++++++++++++++++++++++++++++++++
fuse/helpers.h | 3 +++
fuse/obexfuse.c | 44 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 91 insertions(+), 0 deletions(-)
diff --git a/fuse/helpers.c b/fuse/helpers.c
index 9b453e0..796c4a3 100644
--- a/fuse/helpers.c
+++ b/fuse/helpers.c
@@ -577,3 +577,47 @@ struct gobexhlp_buffer *gobexhlp_get(struct gobexhlp_session* session,
return buffer;
}
+
+static gssize async_put_producer(void *buf, gsize len, gpointer user_data)
+{
+ gssize size;
+ struct gobexhlp_session *session = user_data;
+ struct gobexhlp_buffer *buffer = session->buffer;
+
+ size = buffer->size - buffer->tmpsize;
+
+ if (size > len)
+ size = len;
+
+ g_obex_suspend(session->obex);
+ g_obex_resume(session->obex);
+
+ if (size == 0)
+ return 0;
+
+ memcpy(buf, buffer->data + buffer->tmpsize, size);
+ buffer->tmpsize += size;
+
+ return size;
+}
+
+void gobexhlp_put(struct gobexhlp_session* session,
+ struct gobexhlp_buffer *buffer,
+ const char *path)
+{
+ struct gobexhlp_location *l;
+ l = get_location(path);
+
+ g_print("gobexhlp_put(%s%s)\n", l->dir, l->file);
+
+ gobexhlp_setpath(session, l->dir);
+ buffer->tmpsize = 0;
+ session->buffer = buffer;
+ request_new(session, g_strdup_printf("put %s", path));
+ g_obex_put_req(session->obex, async_put_producer,
+ complete_func, session, &session->err,
+ G_OBEX_HDR_NAME, l->file,
+ G_OBEX_HDR_INVALID);
+ free_location(l);
+ request_wait_free(session);
+}
diff --git a/fuse/helpers.h b/fuse/helpers.h
index aa2a194..1eca29d 100644
--- a/fuse/helpers.h
+++ b/fuse/helpers.h
@@ -56,3 +56,6 @@ struct stat *gobexhlp_getattr(struct gobexhlp_session* session,
const char *path);
struct gobexhlp_buffer *gobexhlp_get(struct gobexhlp_session* session,
const char *path);
+void gobexhlp_put(struct gobexhlp_session* session,
+ struct gobexhlp_buffer *buffer,
+ const char *path);
diff --git a/fuse/obexfuse.c b/fuse/obexfuse.c
index 9fe2dea..900e6d7 100644
--- a/fuse/obexfuse.c
+++ b/fuse/obexfuse.c
@@ -174,11 +174,55 @@ static int obexfuse_read(const char *path, char *buf, size_t size,
return asize;
}
+static int obexfuse_write(const char *path, const char *buf, size_t size,
+ off_t offset, struct fuse_file_info *fi)
+{
+ gsize nsize;
+ struct gobexhlp_buffer *file_buffer = (struct gobexhlp_buffer*)fi->fh;
+
+ if (file_buffer->size < offset + size) {
+ nsize = offset + size;
+ file_buffer->data = g_realloc(file_buffer->data, nsize);
+ file_buffer->size = nsize;
+ } else {
+ nsize = file_buffer->size;
+ }
+
+ file_buffer->edited = TRUE;
+ memcpy(file_buffer->data + offset, buf, size);
+
+ return size;
+}
+
+static int obexfuse_truncate(const char *path, off_t offset)
+{
+ /*
+ * Allow to change the size of a file.
+ */
+ return 0;
+}
+
+static int obexfuse_release(const char *path, struct fuse_file_info *fi)
+{
+ struct gobexhlp_buffer *file_buffer = (struct gobexhlp_buffer*)fi->fh;
+
+ if (file_buffer->edited == TRUE)
+ gobexhlp_put(session, file_buffer, path); /* send to device */
+
+ g_free(file_buffer->data);
+ g_free(file_buffer);
+
+ return session->status;
+}
+
static struct fuse_operations obexfuse_oper = {
.readdir = obexfuse_readdir,
.getattr = obexfuse_getattr,
.open = obexfuse_open,
.read = obexfuse_read,
+ .write = obexfuse_write,
+ .truncate = obexfuse_truncate,
+ .release = obexfuse_release,
.init = obexfuse_init,
.destroy = obexfuse_destroy,
};
--
1.7.8.6
^ permalink raw reply related
* [RFC v2 obexd 09/11] fuse: Add touch operation
From: Michał Poczwardowski @ 2012-10-28 0:29 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Michał Poczwardowski
In-Reply-To: <1351384166-15586-1-git-send-email-dmp0x7c5@gmail.com>
---
fuse/helpers.c | 40 ++++++++++++++++++++++++++++++++++++++++
fuse/helpers.h | 1 +
fuse/obexfuse.c | 17 +++++++++++++++++
3 files changed, 58 insertions(+), 0 deletions(-)
diff --git a/fuse/helpers.c b/fuse/helpers.c
index 796c4a3..cabf8f7 100644
--- a/fuse/helpers.c
+++ b/fuse/helpers.c
@@ -56,6 +56,8 @@ struct gobexhlp_location {
gchar *file;
};
+void gobexhlp_touch_real(struct gobexhlp_session* session, gchar *path);
+
static uint16_t find_rfcomm_uuid(void *user_data)
{
sdp_list_t *pds = (sdp_list_t*) user_data;
@@ -279,6 +281,12 @@ void request_new(struct gobexhlp_session *session,
{
g_print("REQUEST %s\n", name);
+ if (session->vtouch == TRUE) {
+ session->vtouch = FALSE;
+ gobexhlp_touch_real(session, session->vtouch_path);
+ g_free(session->vtouch_path);
+ }
+
if (session->request != NULL)
g_error("Another request (%s) active!\n",
session->request->name);
@@ -621,3 +629,35 @@ void gobexhlp_put(struct gobexhlp_session* session,
free_location(l);
request_wait_free(session);
}
+
+/* virtual file creation */
+void gobexhlp_touch(struct gobexhlp_session* session, const char *path)
+{
+ struct stat *stbuf;
+
+ g_print("gobexhlp_touch(%s)\n", path);
+
+ stbuf = g_malloc0(sizeof(struct stat));
+ stbuf->st_mode = S_IFREG;
+ g_hash_table_replace(session->file_stat, g_strdup(path), stbuf);
+
+ session->vtouch = TRUE;
+ session->vtouch_path = g_strdup(path);
+}
+
+void gobexhlp_touch_real(struct gobexhlp_session* session, gchar *path)
+{
+ struct gobexhlp_buffer *buffer, *tmpbuf;
+
+ g_print("gobexhlp_touch_real(%s)\n", path);
+
+ tmpbuf = session->buffer; /* save buffer state */
+
+ buffer = g_malloc0(sizeof(struct gobexhlp_buffer));
+ session->rtouch = TRUE;
+ gobexhlp_put(session, buffer, path);
+ session->rtouch = FALSE;
+ g_free(buffer);
+
+ session->buffer = tmpbuf;
+}
diff --git a/fuse/helpers.h b/fuse/helpers.h
index 1eca29d..c2b28ca 100644
--- a/fuse/helpers.h
+++ b/fuse/helpers.h
@@ -59,3 +59,4 @@ struct gobexhlp_buffer *gobexhlp_get(struct gobexhlp_session* session,
void gobexhlp_put(struct gobexhlp_session* session,
struct gobexhlp_buffer *buffer,
const char *path);
+void gobexhlp_touch(struct gobexhlp_session* session, const char *path);
diff --git a/fuse/obexfuse.c b/fuse/obexfuse.c
index 900e6d7..b4599d8 100644
--- a/fuse/obexfuse.c
+++ b/fuse/obexfuse.c
@@ -215,6 +215,21 @@ static int obexfuse_release(const char *path, struct fuse_file_info *fi)
return session->status;
}
+static int obexfuse_utimens(const char *path, const struct timespec tv[2])
+{
+ /*
+ * Important for mknod (touch) operation
+ */
+ return 0;
+}
+
+static int obexfuse_mknod(const char *path, mode_t mode, dev_t dev)
+{
+ gobexhlp_touch(session, path);
+
+ return 0;
+}
+
static struct fuse_operations obexfuse_oper = {
.readdir = obexfuse_readdir,
.getattr = obexfuse_getattr,
@@ -223,6 +238,8 @@ static struct fuse_operations obexfuse_oper = {
.write = obexfuse_write,
.truncate = obexfuse_truncate,
.release = obexfuse_release,
+ .utimens = obexfuse_utimens,
+ .mknod = obexfuse_mknod,
.init = obexfuse_init,
.destroy = obexfuse_destroy,
};
--
1.7.8.6
^ permalink raw reply related
* [RFC v2 obexd 10/11] fuse: Add unlink/rmdir operation
From: Michał Poczwardowski @ 2012-10-28 0:29 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Michał Poczwardowski
In-Reply-To: <1351384166-15586-1-git-send-email-dmp0x7c5@gmail.com>
---
fuse/helpers.c | 28 ++++++++++++++++++++++++++++
fuse/helpers.h | 1 +
fuse/obexfuse.c | 9 +++++++++
3 files changed, 38 insertions(+), 0 deletions(-)
diff --git a/fuse/helpers.c b/fuse/helpers.c
index cabf8f7..d0580c2 100644
--- a/fuse/helpers.c
+++ b/fuse/helpers.c
@@ -618,6 +618,16 @@ void gobexhlp_put(struct gobexhlp_session* session,
g_print("gobexhlp_put(%s%s)\n", l->dir, l->file);
+ if (g_strcmp0(path, session->vtouch_path) == 0 &&
+ session->vtouch == TRUE) {
+ session->vtouch = FALSE;
+ g_free(session->vtouch_path);
+ } else {
+ /* delete existing file */
+ if (session->rtouch == FALSE)
+ gobexhlp_delete(session, path);
+ }
+
gobexhlp_setpath(session, l->dir);
buffer->tmpsize = 0;
session->buffer = buffer;
@@ -661,3 +671,21 @@ void gobexhlp_touch_real(struct gobexhlp_session* session, gchar *path)
session->buffer = tmpbuf;
}
+
+void gobexhlp_delete(struct gobexhlp_session* session, const char *path)
+{
+ struct gobexhlp_location *l;
+ l = get_location(path);
+
+ g_print("gobexhlp_delete(%s)\n", l->file);
+
+ gobexhlp_setpath(session, l->dir);
+ request_new(session, g_strdup_printf("delete %s", path));
+ g_obex_delete(session->obex, l->file, response_func, session,
+ &session->err);
+
+ g_hash_table_remove(session->file_stat, path);
+
+ free_location(l);
+ request_wait_free(session);
+}
diff --git a/fuse/helpers.h b/fuse/helpers.h
index c2b28ca..803cb59 100644
--- a/fuse/helpers.h
+++ b/fuse/helpers.h
@@ -60,3 +60,4 @@ void gobexhlp_put(struct gobexhlp_session* session,
struct gobexhlp_buffer *buffer,
const char *path);
void gobexhlp_touch(struct gobexhlp_session* session, const char *path);
+void gobexhlp_delete(struct gobexhlp_session* session, const char *path);
diff --git a/fuse/obexfuse.c b/fuse/obexfuse.c
index b4599d8..2a6ee11 100644
--- a/fuse/obexfuse.c
+++ b/fuse/obexfuse.c
@@ -230,6 +230,13 @@ static int obexfuse_mknod(const char *path, mode_t mode, dev_t dev)
return 0;
}
+static int obexfuse_unlink(const char *path)
+{
+ gobexhlp_delete(session, path);
+
+ return session->status;
+}
+
static struct fuse_operations obexfuse_oper = {
.readdir = obexfuse_readdir,
.getattr = obexfuse_getattr,
@@ -240,6 +247,8 @@ static struct fuse_operations obexfuse_oper = {
.release = obexfuse_release,
.utimens = obexfuse_utimens,
.mknod = obexfuse_mknod,
+ .unlink = obexfuse_unlink,
+ .rmdir = obexfuse_unlink,
.init = obexfuse_init,
.destroy = obexfuse_destroy,
};
--
1.7.8.6
^ permalink raw reply related
* [RFC v2 obexd 11/11] fuse: Add rename operation
From: Michał Poczwardowski @ 2012-10-28 0:29 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Michał Poczwardowski
In-Reply-To: <1351384166-15586-1-git-send-email-dmp0x7c5@gmail.com>
---
fuse/helpers.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++
fuse/helpers.h | 3 +++
fuse/obexfuse.c | 16 ++++++++++++++++
3 files changed, 65 insertions(+), 0 deletions(-)
diff --git a/fuse/helpers.c b/fuse/helpers.c
index d0580c2..2487d09 100644
--- a/fuse/helpers.c
+++ b/fuse/helpers.c
@@ -689,3 +689,49 @@ void gobexhlp_delete(struct gobexhlp_session* session, const char *path)
free_location(l);
request_wait_free(session);
}
+
+void gobexhlp_mkdir(struct gobexhlp_session* session, const char *path)
+{
+ struct gobexhlp_location *l;
+ struct stat *stbuf;
+
+ g_print("gobexhlp_mkdir(%s)\n", path);
+
+ l = get_location(path);
+ gobexhlp_setpath(session, l->dir);
+
+ request_new(session, g_strdup_printf("mkdir %s", path));
+ /* g_obex_mkdir also sets path, to new folder */
+ g_obex_mkdir(session->obex, l->file, response_func, session,
+ &session->err);
+ g_free(session->setpath);
+ session->setpath = g_strdup(path);
+
+ stbuf = g_malloc0(sizeof(struct stat));
+ stbuf->st_mode = S_IFDIR;
+ stbuf->st_mtime = time(NULL);
+ g_hash_table_replace(session->file_stat, g_strdup(path), stbuf);
+
+ free_location(l);
+ request_wait_free(session);
+}
+
+void gobexhlp_move(struct gobexhlp_session* session, const char *oldpath,
+ const char* newpath)
+{
+ struct gobexhlp_location *l_from, *l_to;
+
+ l_to = get_location(newpath);
+ l_from = get_location(oldpath);
+ gobexhlp_setpath(session, l_from->dir);
+
+ g_print("gobexhlp_move(%s to %s)\n", l_from->file, l_to->file);
+
+ request_new(session, g_strdup_printf("move %s:%s",
+ oldpath, newpath));
+ g_obex_move(session->obex, l_from->file, l_to->file, response_func,
+ session, &session->err);
+ free_location(l_to);
+ free_location(l_from);
+ request_wait_free(session);
+}
diff --git a/fuse/helpers.h b/fuse/helpers.h
index 803cb59..80e3aa8 100644
--- a/fuse/helpers.h
+++ b/fuse/helpers.h
@@ -61,3 +61,6 @@ void gobexhlp_put(struct gobexhlp_session* session,
const char *path);
void gobexhlp_touch(struct gobexhlp_session* session, const char *path);
void gobexhlp_delete(struct gobexhlp_session* session, const char *path);
+void gobexhlp_mkdir(struct gobexhlp_session* session, const char *path);
+void gobexhlp_move(struct gobexhlp_session* session, const char *oldpath,
+ const char* newpath);
diff --git a/fuse/obexfuse.c b/fuse/obexfuse.c
index 2a6ee11..f9f9d0e 100644
--- a/fuse/obexfuse.c
+++ b/fuse/obexfuse.c
@@ -237,6 +237,20 @@ static int obexfuse_unlink(const char *path)
return session->status;
}
+static int obexfuse_mkdir(const char *path, mode_t mode)
+{
+ gobexhlp_mkdir(session, path);
+
+ return session->status;
+}
+
+static int obexfuse_rename(const char *from, const char *to)
+{
+ gobexhlp_move(session, from, to);
+
+ return session->status;
+}
+
static struct fuse_operations obexfuse_oper = {
.readdir = obexfuse_readdir,
.getattr = obexfuse_getattr,
@@ -249,6 +263,8 @@ static struct fuse_operations obexfuse_oper = {
.mknod = obexfuse_mknod,
.unlink = obexfuse_unlink,
.rmdir = obexfuse_unlink,
+ .mkdir = obexfuse_mkdir,
+ .rename = obexfuse_rename,
.init = obexfuse_init,
.destroy = obexfuse_destroy,
};
--
1.7.8.6
^ permalink raw reply related
* BLE issue: Start_LE_Encryption fails
From: Ajay @ 2012-10-28 17:08 UTC (permalink / raw)
To: linux-bluetooth
Hi,
I am getting le_long_term_key_negative reply from the remote device ,
on sending le_start_encryption . I am testing this with 2 ubuntu
machines(3.2.5) with IOGEAR dual mode dongles .
I did pairing using blueman and got corresponding link key
from(/var/lib/bluetooth) . But i doubt, this key is specific to BR/EDR
not for LE controller , since hcidump is not showing any packets related
to smp .
so please suggest me a way to do pairing using SMP and LE specific
--
Thanks & regards
AJAY KV
GlobalEdge software Ltd
8892753703
^ permalink raw reply
* Crashing bluetoothd when keyboard enters sleep mode / stand-by
From: Patkos Csaba @ 2012-10-28 18:46 UTC (permalink / raw)
To: linux-bluetooth
Hi,
When my keyboard enters stand-by and disconnects from the computer I see
high cpu usage by bluetoothd. Sometimes, not always, if I don't wake up
the keyboard, bluetoothd will crash leaving this in /var/log/messages:
Oct 28 18:04:49 localhost kernel: [27658.431018] hid-generic
0005:045E:077C.0013: unknown main item tag 0x0
Oct 28 18:04:49 localhost kernel: [27658.431243] input: Microsoft Sculpt
Touch Mouse as
/devices/pci0000:00/0000:00:1d.1/usb3/3-1/3-1:1.0/bluetooth/hci0/hci0:45/input32
Oct 28 18:04:49 localhost kernel: [27658.431438] hid-generic
0005:045E:077C.0013: input,hidraw3: BLUETOOTH HID v1.1e Mouse [Microsoft
Sculpt Touch Mouse] on 00:15:83:3D:0A:57
There are no such problems with the mouse, which, as well as the keyboard,
enters stand-by if not used. I have bluez 4.101 provided by Sabayon Linux.
I don't know if it is related, but there is also a problem with the
keyboard state after starting bluetoothd. The initial state of the
keyboard is "disconnected" which is correct. After hitting a key the
keyboard tries to connect, state changes to "connected" but keyboard
doesn't work and nothing is printed in the logs. If I do a disconnect from
the KDE's bluedevil interface, or from the console by running "hcitool dc
7C:1E:52:A8:47:74" and than hit a key again, the keyboard is correctly
detected and working.
I have this bluetooth keyboard:
Oct 28 18:04:49 localhost kernel: [27658.427978] hid-generic
0005:045E:0762.0012: unknown main item tag 0x0
Oct 28 18:04:49 localhost kernel: [27658.428887] input: Microsoft
Bluetooth Mobile Keyboard 6000 as
/devices/pci0000:00/0000:00:1d.1/usb3/3-1/3-1:1.0/bluetooth/hci0/hci0:42/input31
Oct 28 18:04:49 localhost kernel: [27658.429030] hid-generic
0005:045E:0762.0012: input,hidraw2: BLUETOOTH HID v0.13 Keyboard
[Microsoft Bluetooth Mobile Keyboard 6000] on 00:15:83:3D:0A:57
With this bluetooth mouse:
Oct 28 18:04:49 localhost kernel: [27658.431018] hid-generic
0005:045E:077C.0013: unknown main item tag 0x0
Oct 28 18:04:49 localhost kernel: [27658.431243] input: Microsoft Sculpt
Touch Mouse as
/devices/pci0000:00/0000:00:1d.1/usb3/3-1/3-1:1.0/bluetooth/hci0/hci0:45/input32
Oct 28 18:04:49 localhost kernel: [27658.431438] hid-generic
0005:045E:077C.0013: input,hidraw3: BLUETOOTH HID v1.1e Mouse [Microsoft
Sculpt Touch Mouse] on 00:15:83:3D:0A:57
Running on this usb-bluetooth adapter:
localhost ~ # lsusb -v
Bus 003 Device 002: ID 0a12:0001 Cambridge Silicon Radio, Ltd Bluetooth
Dongle (HCI mode)
Device Descriptor:
bLength 18
bDescriptorType 1
bcdUSB 2.00
bDeviceClass 224 Wireless
bDeviceSubClass 1 Radio Frequency
bDeviceProtocol 1 Bluetooth
bMaxPacketSize0 64
idVendor 0x0a12 Cambridge Silicon Radio, Ltd
idProduct 0x0001 Bluetooth Dongle (HCI mode)
bcdDevice 31.64
iManufacturer 0
iProduct 0
iSerial 0
bNumConfigurations 1
Configuration Descriptor:
bLength 9
bDescriptorType 2
wTotalLength 177
bNumInterfaces 2
bConfigurationValue 1
iConfiguration 0
bmAttributes 0xc0
Self Powered
MaxPower 0mA
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 0
bAlternateSetting 0
bNumEndpoints 3
bInterfaceClass 224 Wireless
bInterfaceSubClass 1 Radio Frequency
bInterfaceProtocol 1 Bluetooth
iInterface 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x81 EP 1 IN
bmAttributes 3
Transfer Type Interrupt
Synch Type None
Usage Type Data
wMaxPacketSize 0x0010 1x 16 bytes
bInterval 1
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x02 EP 2 OUT
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0040 1x 64 bytes
bInterval 1
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x82 EP 2 IN
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0040 1x 64 bytes
bInterval 1
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 1
bAlternateSetting 0
bNumEndpoints 2
bInterfaceClass 224 Wireless
bInterfaceSubClass 1 Radio Frequency
bInterfaceProtocol 1 Bluetooth
iInterface 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x03 EP 3 OUT
bmAttributes 1
Transfer Type Isochronous
Synch Type None
Usage Type Data
wMaxPacketSize 0x0000 1x 0 bytes
bInterval 1
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x83 EP 3 IN
bmAttributes 1
Transfer Type Isochronous
Synch Type None
Usage Type Data
wMaxPacketSize 0x0000 1x 0 bytes
bInterval 1
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 1
bAlternateSetting 1
bNumEndpoints 2
bInterfaceClass 224 Wireless
bInterfaceSubClass 1 Radio Frequency
bInterfaceProtocol 1 Bluetooth
iInterface 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x03 EP 3 OUT
bmAttributes 1
Transfer Type Isochronous
Synch Type None
Usage Type Data
wMaxPacketSize 0x0009 1x 9 bytes
bInterval 1
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x83 EP 3 IN
bmAttributes 1
Transfer Type Isochronous
Synch Type None
Usage Type Data
wMaxPacketSize 0x0009 1x 9 bytes
bInterval 1
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 1
bAlternateSetting 2
bNumEndpoints 2
bInterfaceClass 224 Wireless
bInterfaceSubClass 1 Radio Frequency
bInterfaceProtocol 1 Bluetooth
iInterface 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x03 EP 3 OUT
bmAttributes 1
Transfer Type Isochronous
Synch Type None
Usage Type Data
wMaxPacketSize 0x0011 1x 17 bytes
bInterval 1
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x83 EP 3 IN
bmAttributes 1
Transfer Type Isochronous
Synch Type None
Usage Type Data
wMaxPacketSize 0x0011 1x 17 bytes
bInterval 1
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 1
bAlternateSetting 3
bNumEndpoints 2
bInterfaceClass 224 Wireless
bInterfaceSubClass 1 Radio Frequency
bInterfaceProtocol 1 Bluetooth
iInterface 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x03 EP 3 OUT
bmAttributes 1
Transfer Type Isochronous
Synch Type None
Usage Type Data
wMaxPacketSize 0x0019 1x 25 bytes
bInterval 1
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x83 EP 3 IN
bmAttributes 1
Transfer Type Isochronous
Synch Type None
Usage Type Data
wMaxPacketSize 0x0019 1x 25 bytes
bInterval 1
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 1
bAlternateSetting 4
bNumEndpoints 2
bInterfaceClass 224 Wireless
bInterfaceSubClass 1 Radio Frequency
bInterfaceProtocol 1 Bluetooth
iInterface 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x03 EP 3 OUT
bmAttributes 1
Transfer Type Isochronous
Synch Type None
Usage Type Data
wMaxPacketSize 0x0021 1x 33 bytes
bInterval 1
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x83 EP 3 IN
bmAttributes 1
Transfer Type Isochronous
Synch Type None
Usage Type Data
wMaxPacketSize 0x0021 1x 33 bytes
bInterval 1
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 1
bAlternateSetting 5
bNumEndpoints 2
bInterfaceClass 224 Wireless
bInterfaceSubClass 1 Radio Frequency
bInterfaceProtocol 1 Bluetooth
iInterface 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x03 EP 3 OUT
bmAttributes 1
Transfer Type Isochronous
Synch Type None
Usage Type Data
wMaxPacketSize 0x0031 1x 49 bytes
bInterval 1
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x83 EP 3 IN
bmAttributes 1
Transfer Type Isochronous
Synch Type None
Usage Type Data
wMaxPacketSize 0x0031 1x 49 bytes
bInterval 1
Device Status: 0x0001
Self Powered
--
ing. Patkós Csaba
Software Developer @ Syneto SRL (http://www.syneto.net)
Member of The Romanian Mandriva Users Group (http://www.mandrivausrs.ro)
----
I use Dropbox. Do you? http://db.tt/IqeKInu
^ permalink raw reply
* BLE GATT subcriber example?
From: Couch, Kelly J @ 2012-10-28 21:27 UTC (permalink / raw)
To: linux-bluetooth@vger.kernel.org
Hi,
I'm interested in building a C application that utilizes BLE GATT via DBUS. However, I have been unable to identify a good source of documentation or example that demonstrates how.
How to subscribe to attribute/characteristic change notifications?
So, far I am able to utilize Bluez dbus interface to discover, pair, observe connect/disconnect events. But unable to use DiscoverServices, etc..
Any suggestions are greatly appreciated.
-Kelly
^ permalink raw reply
* Re: BLE issue: Start_LE_Encryption fails
From: Ajay @ 2012-10-29 0:30 UTC (permalink / raw)
To: Anderson Lizardo; +Cc: linux-bluetooth
In-Reply-To: <CAJdJm_NHaBzobi=k3cMaM-J_3d6R7FCEvfrSJWbkiKxKpjKHWA@mail.gmail.com>
On Tuesday 30 October 2012 05:20 PM, Anderson Lizardo wrote:
> [Forgot to reply to the list]
>
> On Tue, Oct 30, 2012 at 7:49 AM, Anderson Lizardo
> <anderson.lizardo@openbossa.org> wrote:
>> Hi Ajay,
>>
>> On Sun, Oct 28, 2012 at 1:08 PM, Ajay <ajay.kv@globaledgesoft.com> wrote:
>>> Hi,
>>> I am getting le_long_term_key_negative reply from the remote device
>>> , on sending le_start_encryption . I am testing this with 2 ubuntu
>>> machines(3.2.5) with IOGEAR dual mode dongles .
>>
>> To connect to a dual mode dongle, you need to set LE Adv. flags to
>> 0x06 (which means general discoverable + BR/EDR not supported) on the
>> acceptor side. You can use this command (on the acceptor/slave side):
>>
>> sudo hcitool -i hci0 cmd 0x08 0x0008 03 02 01 06 \
>> $(perl -e 'print "00 " x 28')
>>
>> Next, enable LE advertising:
>>
>> sudo hciconfig hci0 leadv
>>
>> On the initiator/master side, run "hcitool lescan" and try pairing again.
>
> Regards,
>
Thanks, but "sudo hcitool -i hci0 cmd 0x08 0x0008 03 02 01 06 " , this
command only sets the advertising data to zero right . so how do i set
the adv flag as 0x06 . Which hci command is used for this purpose .
--
Thanks & regards
AJAY KV
GlobalEdge software Ltd
8892753703
^ permalink raw reply
* Re: BLE issue: Start_LE_Encryption fails
From: Ajay @ 2012-10-29 1:33 UTC (permalink / raw)
To: Anderson Lizardo; +Cc: linux-bluetooth
In-Reply-To: <CAJdJm_P=USyDO5op7_7ynOT8Qe7dOifyN=notezK1HGCxOe8bA@mail.gmail.com>
On Tuesday 30 October 2012 06:42 PM, Anderson Lizardo wrote:
> Hi Ajay,
>
> On Sun, Oct 28, 2012 at 8:30 PM, Ajay <ajay.kv@globaledgesoft.com> wrote:
>> Thanks, but "sudo hcitool -i hci0 cmd 0x08 0x0008 03 02 01 06 " , this
>> command only sets the advertising data to zero right . so how do i set the
>> adv flag as 0x06 . Which hci command is used for this purpose .
>
> No, this sets adv. data to have "Flags" AD set to 0x06. But you missed
> the second line of the command:
>
> sudo hcitool -i hci0 cmd 0x08 0x0008 03 02 01 06 \
> $(perl -e 'print "00 " x 28')
>
> The second line is important because it fills the other bytes with
> zero (which some controllers require).
>
> Regards,
>
ya , i got your point ,advertiser is informing the remote device ,
that it is not BR/EDR capable .That is done and device is connecting now.
But still i dont know, how to do le specific pairing and
start_encryption enable(part of le pairing) . Is there any tool in bluez
providing LE pairing before connecting the devices (want to try with 2
ubuntu pc's ).
--
Thanks & regards
AJAY KV
GlobalEdge software Ltd
8892753703
^ permalink raw reply
* Re: [PATCH v3 04/10] sbc: Add msbc flag and generic C primitive
From: Dalleau, Frederic @ 2012-10-29 6:47 UTC (permalink / raw)
To: Siarhei Siamashka; +Cc: Frédéric Dalleau, linux-bluetooth
In-Reply-To: <20121028011948.30f09efe@i7>
Hi Siarhei,
Thanks for review, I mostly agree but have a few remarks.
On Sun, Oct 28, 2012 at 12:19 AM, Siarhei Siamashka
<siarhei.siamashka@gmail.com> wrote:
> On Fri, 26 Oct 2012 19:20:30 +0200
> If "position" was not decremented by 16 for 8 samples here, then you
> would not need to do
> if (state->pending == state->position)
> x += 8;
> elsewhere.
Good idea, just note that in this case, one sample (x[1] = PCM(0 + 7
* nchannels))
will receive a negative index x[-7]. there is no technical problème
just a matter of style.
> One more nitpick about "sbc_encoder_process_input_s8". The old
> variant was taking "position" as a function argument and returning
> an updated position.
Addtionnally, sbc_encoder_process_input_s8 would return void, and
state->position
would be used directly from process_input function. However, it requires changes
in the neon code, which I'd rather avoid for now. I can send a patch
later for this one.
Regards,
Frédéric
^ permalink raw reply
* Re: [PATCH v3 06/10] sbc: Add mmx primitive for 1b 8s analyse
From: Dalleau, Frederic @ 2012-10-29 7:42 UTC (permalink / raw)
To: Siarhei Siamashka; +Cc: Frédéric Dalleau, linux-bluetooth
In-Reply-To: <20121028022942.3bcd1bd7@i7>
Hi Siarhei,
Since only neon is concerned by this, I'd rather add a one liner like this :
#ifdef SBC_BUILD_WITH_NEON_SUPPORT
sbc_init_primitives_neon(state);
+
+ if (state->increment == 1)
+ state->sbc_analyze_4b_8s = sbc_analyze_1b_8s_simd;
#endif
It is more explicit, doesn't change priority and doesn't add needless code
to other implementations.
And what about sbc_analyze_8s?
Regarding point 2, this is the reason why patch 4 was a bit bigger, the simd
implementation is complete.
Regards,
Frédéric
^ permalink raw reply
* Re: [RFCv1 03/11] Bluetooth: AMP: Process Physical Link Complete evt
From: Andrei Emeltchenko @ 2012-10-29 9:22 UTC (permalink / raw)
To: Mat Martineau; +Cc: linux-bluetooth
In-Reply-To: <alpine.DEB.2.02.1210261012440.2459@mathewm-linux>
Hi Mat,
On Fri, Oct 26, 2012 at 10:16:53AM -0700, Mat Martineau wrote:\
...
> >+static void hci_phy_link_complete_evt(struct hci_dev *hdev,
> >+ struct sk_buff *skb)
> >+{
> >+ struct hci_ev_phy_link_complete *ev = (void *) skb->data;
> >+ struct hci_conn *hcon, *bredr_hcon;
> >+
> >+ BT_DBG("%s handle 0x%2.2x status 0x%2.2x", hdev->name, ev->phy_handle,
> >+ ev->status);
> >+
> >+ hci_dev_lock(hdev);
> >+
> >+ hcon = hci_conn_hash_lookup_handle(hdev, ev->phy_handle);
> >+ if (!hcon) {
> >+ hci_dev_unlock(hdev);
> >+ return;
> >+ }
> >+
> >+ if (ev->status) {
> >+ hci_conn_del(hcon);
> >+ hci_dev_unlock(hdev);
> >+ return;
> >+ }
> >+
> >+ bredr_hcon = hcon->amp_mgr->l2cap_conn->hcon;
> >+
> >+ hcon->state = BT_CONNECTED;
> >+ bacpy(&hcon->dst, &bredr_hcon->dst);
> >+
> >+ hci_conn_hold(hcon);
> >+ hcon->disc_timeout = HCI_DISCONN_TIMEOUT;
> >+ hci_conn_put(hcon);
> >+
> >+ hci_conn_hold_device(hcon);
> >+ hci_conn_add_sysfs(hcon);
> >+
> >+ hci_dev_unlock(hdev);
> >+
> >+ if (hcon->out) {
> >+ struct hci_dev *bredr_hdev = hci_dev_hold(bredr_hcon->hdev);
> >+
> >+ if (!bredr_hdev)
> >+ return;
> >+
> >+ /* Placeholder - create chan req
> >+ l2cap_chan_create_cfm(bredr_hcon, hcon->remote_id);
> >+ */
>
> I think this is where you would call l2cap_physical_cfm(), but that
> function requires more information. Is there enough context in hcon
> to get the local amp ID
This one is easy to manage: hcon->dev->id
> and l2cap_chan, or does the AMP manager need
> to be notified of the physical link so it can match up the physical
> link with other information?
I get chan from amp_mgr through hcon->amp_mgr
I will send the patch later this week.
Best regards
Andrei Emeltchenko
^ permalink raw reply
* Re: [RFCv1 05/11] Bluetooth: AMP: Add Logical Link Create function
From: Andrei Emeltchenko @ 2012-10-29 9:24 UTC (permalink / raw)
To: Mat Martineau; +Cc: linux-bluetooth
In-Reply-To: <alpine.DEB.2.02.1210260958240.2459@mathewm-linux>
Hi Mat,
On Fri, Oct 26, 2012 at 10:01:01AM -0700, Mat Martineau wrote:
...
> >@@ -3948,13 +3955,15 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn,
> > goto done;
> > }
> >
> >- /* check compatibility */
> >-
> > if (!chan->ctrl_id)
> > l2cap_send_efs_conf_rsp(chan, buf, cmd->ident,
> > 0);
> >- else
> >- chan->ident = cmd->ident;
> >+ else {
> >+ if (l2cap_check_efs(chan)) {
> >+ amp_create_logical_link(chan);
> >+ chan->ident = cmd->ident;
> >+ }
> >+ }
>
> Minor style issue - if one block of an if/else needs braces, then
> they all get braces.
Sure. Will fix this.
Best regards
Andrei Emeltchenko
^ permalink raw reply
* Re: [PATCH] workqueue: cancel_delayed_work() should return %NULL if work item is idle
From: Andrei Emeltchenko @ 2012-10-29 9:47 UTC (permalink / raw)
To: Tejun Heo, Gustavo Padovan, linux-bluetooth
Cc: Dan Magenheimer, linux-kernel, Konrad Wilk
In-Reply-To: <20121018233928.GY13370@google.com>
Gustavo, could you apply the patch below to bluetooth-next tree, otherwise
it is deadly broken.
Best regards
Andrei Emeltchenko
On Thu, Oct 18, 2012 at 04:39:28PM -0700, Tejun Heo wrote:
> From e65120fcfc1cb9697655d29ecd7982451c05d3c2 Mon Sep 17 00:00:00 2001
> From: Dan Magenheimer <dan.magenheimer@oracle.com>
> Date: Thu, 18 Oct 2012 16:31:37 -0700
>
> 57b30ae77b ("workqueue: reimplement cancel_delayed_work() using
> try_to_grab_pending()") made cancel_delayed_work() always return %true
> unless someone else is also trying to cancel the work item, which is
> broken - if the target work item is idle, the return value should be
> %false.
>
> try_to_grab_pending() indicates that the target work item was idle by
> zero return value. Use it for return. Note that this brings
> cancel_delayed_work() in line with __cancel_work_timer() in return
> value handling.
>
> Signed-off-by: Dan Magenheimer <dan.magenheimer@oracle.com>
> Signed-off-by: Tejun Heo <tj@kernel.org>
> LKML-Reference: <444a6439-b1a4-4740-9e7e-bc37267cfe73@default>
> ---
> kernel/workqueue.c | 2 +-
> 1 files changed, 1 insertions(+), 1 deletions(-)
>
> diff --git a/kernel/workqueue.c b/kernel/workqueue.c
> index d951daa..042d221 100644
> --- a/kernel/workqueue.c
> +++ b/kernel/workqueue.c
> @@ -2982,7 +2982,7 @@ bool cancel_delayed_work(struct delayed_work *dwork)
>
> set_work_cpu_and_clear_pending(&dwork->work, work_cpu(&dwork->work));
> local_irq_restore(flags);
> - return true;
> + return ret;
> }
> EXPORT_SYMBOL(cancel_delayed_work);
>
> --
> 1.7.7.3
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at http://www.tux.org/lkml/
^ permalink raw reply
* Re: [PATCH BlueZ v4] AVDTP: Do not keep a internal reference
From: Johan Hedberg @ 2012-10-29 11:07 UTC (permalink / raw)
To: Luiz Augusto von Dentz; +Cc: linux-bluetooth
In-Reply-To: <1351259069-21516-1-git-send-email-luiz.dentz@gmail.com>
Hi Luiz,
On Fri, Oct 26, 2012, Luiz Augusto von Dentz wrote:
> Don't initialize reference with 1, instead always start disconnect timer
> when reference drops to 0, so in case nobody reclaims the session it
> automatically disconnect after 1 second and frees the memory.
> ---
> v2: Fix with stream_setup flag being ignored in disconnect_timeout
> v3: Do avrcp_free on connection_lost to avoid having dangling sessions in
> disconnected state waiting the timeout to be freed.
> v4: Remove unnecessary reference when authorizing, disconnect timer is only
> started when the session is really connected.
>
> audio/avdtp.c | 204 +++++++++++++++++++++++-----------------------------------
> 1 file changed, 82 insertions(+), 122 deletions(-)
Applied. Thanks.
Johan
^ permalink raw reply
* Re: Crashing bluetoothd when keyboard enters sleep mode / stand-by
From: Johan Hedberg @ 2012-10-29 11:21 UTC (permalink / raw)
To: Patkos Csaba; +Cc: linux-bluetooth
In-Reply-To: <op.wmwjsbelgqbfom@localhost>
Hi,
On Sun, Oct 28, 2012, Patkos Csaba wrote:
> When my keyboard enters stand-by and disconnects from the computer I
> see high cpu usage by bluetoothd. Sometimes, not always, if I don't
> wake up the keyboard, bluetoothd will crash
Could you please try to get a proper backtrace of what's happening.
I.e. run bluetoothd with valgrind or gdb, e.g.:
sudo killall bluetoothd
sudo valgrind /usr/sbin/bluetoothd -nd
If this doesn't give useful info because of missing debug symbols either
try to install them (no idea how to do that with your distro though) or
compile and run 4.101 from the source tree (no need to install the
custom compilation on your system).
The HCI-level log would also be helpful in addition to the bluetoothd
debug logs, so please run "hcidump -X" in a separate terminal while
reproducing the issue. Thanks.
Johan
^ permalink raw reply
* [PATCH] Add bluetooth support for VAIO VPCEH [0489:e027]
From: Marcos Chaparro @ 2012-10-29 13:05 UTC (permalink / raw)
To: Marcel Holtmann, Gustavo Padovan, Johan Hedberg, linux-bluetooth
Cc: linux-kernel
[-- Attachment #1.1: Type: text/plain, Size: 907 bytes --]
Hi,
this patch against 3.7-rc3 identifies my Atheros AR3011 internal bluetooth
device, I just transferred some files from my phone to this debian PC using
bluetooth and it works fine (btw, it needs atheros-firmware package).
Before changing this, lsusb was identifying it as a Foxconn / Hon Hai device,
which is wrong, and it probably loaded a different firmware to the bluetooth
module.
The patches solves the following discussions:
https://answers.launchpad.net/ubuntu/+source/bluez/+question/168992
http://ubuntuforums.org/showthread.php?t=1867214
http://linux-unix-open-source.1053819.n5.nabble.com/Atheros-AR9285-on-
Ubuntu-11-10-Sony-Vaio-VPCEH-wifi-does-t-works-td4931321.html
http://www.vivaolinux.com.br/topico/Rede-Wireless/Bluetooth-vaio-VPCEH-
desabilitado
Please tell me if you won't add it to mainstream for some reason.
Thanks in advance, and greetings from Argentina.
--
Marcos
[-- Attachment #1.2: Type: text/html, Size: 4344 bytes --]
[-- Attachment #2: patchfile --]
[-- Type: text/x-patch, Size: 1165 bytes --]
diff -uNr linux-3.7-rc3.orig/drivers/bluetooth/ath3k.c linux-3.7-rc3.new/drivers/bluetooth/ath3k.c
--- linux-3.7-rc3.orig/drivers/bluetooth/ath3k.c 2012-10-28 16:24:48.000000000 -0300
+++ linux-3.7-rc3.new/drivers/bluetooth/ath3k.c 2012-10-29 09:39:03.459764720 -0300
@@ -67,6 +67,7 @@
{ USB_DEVICE(0x13d3, 0x3304) },
{ USB_DEVICE(0x0930, 0x0215) },
{ USB_DEVICE(0x0489, 0xE03D) },
+ { USB_DEVICE(0x0489, 0xE027) },
/* Atheros AR9285 Malbec with sflash firmware */
{ USB_DEVICE(0x03F0, 0x311D) },
diff -uNr linux-3.7-rc3.orig/drivers/bluetooth/btusb.c linux-3.7-rc3.new/drivers/bluetooth/btusb.c
--- linux-3.7-rc3.orig/drivers/bluetooth/btusb.c 2012-10-28 16:24:48.000000000 -0300
+++ linux-3.7-rc3.new/drivers/bluetooth/btusb.c 2012-10-29 09:39:06.219765159 -0300
@@ -124,6 +124,7 @@
{ USB_DEVICE(0x13d3, 0x3304), .driver_info = BTUSB_IGNORE },
{ USB_DEVICE(0x0930, 0x0215), .driver_info = BTUSB_IGNORE },
{ USB_DEVICE(0x0489, 0xe03d), .driver_info = BTUSB_IGNORE },
+ { USB_DEVICE(0x0489, 0xe027), .driver_info = BTUSB_IGNORE },
/* Atheros AR9285 Malbec with sflash firmware */
{ USB_DEVICE(0x03f0, 0x311d), .driver_info = BTUSB_IGNORE },
^ permalink raw reply
* [PATCH] Add bluetooth support for VAIO VPCEH [0489:e027]
From: Marcos Chaparro @ 2012-10-29 13:09 UTC (permalink / raw)
To: Marcel Holtmann, Gustavo Padovan, Johan Hedberg, linux-bluetooth
Cc: linux-kernel
[-- Attachment #1: Type: text/plain, Size: 947 bytes --]
Hi,
this patch against 3.7-rc3 identifies my Atheros AR3011 internal bluetooth
device, I just transferred some files from my phone to this debian PC using
bluetooth and it works fine (btw, it needs atheros-firmware package).
Before changing this, lsusb was identifying it as a Foxconn / Hon Hai device,
which is wrong, and it probably loaded a different firmware to the bluetooth
module.
The patches solves the following discussions:
https://answers.launchpad.net/ubuntu/+source/bluez/+question/168992
http://ubuntuforums.org/showthread.php?t=1867214
http://linux-unix-open-source.1053819.n5.nabble.com/Atheros-AR9285-on-
Ubuntu-11-10-Sony-Vaio-VPCEH-wifi-does-t-works-td4931321.html
http://www.vivaolinux.com.br/topico/Rede-Wireless/Bluetooth-vaio-VPCEH-
desabilitado
Please tell me if you won't add it to mainstream for some reason.
Thanks in advance, and greetings from Argentina.
--
Marcos
PS: Sent again without HTML formatting
[-- Attachment #2: patchfile --]
[-- Type: text/x-patch, Size: 1165 bytes --]
diff -uNr linux-3.7-rc3.orig/drivers/bluetooth/ath3k.c linux-3.7-rc3.new/drivers/bluetooth/ath3k.c
--- linux-3.7-rc3.orig/drivers/bluetooth/ath3k.c 2012-10-28 16:24:48.000000000 -0300
+++ linux-3.7-rc3.new/drivers/bluetooth/ath3k.c 2012-10-29 09:39:03.459764720 -0300
@@ -67,6 +67,7 @@
{ USB_DEVICE(0x13d3, 0x3304) },
{ USB_DEVICE(0x0930, 0x0215) },
{ USB_DEVICE(0x0489, 0xE03D) },
+ { USB_DEVICE(0x0489, 0xE027) },
/* Atheros AR9285 Malbec with sflash firmware */
{ USB_DEVICE(0x03F0, 0x311D) },
diff -uNr linux-3.7-rc3.orig/drivers/bluetooth/btusb.c linux-3.7-rc3.new/drivers/bluetooth/btusb.c
--- linux-3.7-rc3.orig/drivers/bluetooth/btusb.c 2012-10-28 16:24:48.000000000 -0300
+++ linux-3.7-rc3.new/drivers/bluetooth/btusb.c 2012-10-29 09:39:06.219765159 -0300
@@ -124,6 +124,7 @@
{ USB_DEVICE(0x13d3, 0x3304), .driver_info = BTUSB_IGNORE },
{ USB_DEVICE(0x0930, 0x0215), .driver_info = BTUSB_IGNORE },
{ USB_DEVICE(0x0489, 0xe03d), .driver_info = BTUSB_IGNORE },
+ { USB_DEVICE(0x0489, 0xe027), .driver_info = BTUSB_IGNORE },
/* Atheros AR9285 Malbec with sflash firmware */
{ USB_DEVICE(0x03f0, 0x311d), .driver_info = BTUSB_IGNORE },
^ permalink raw reply
* [PATCH BlueZ 1/7] AVRCP: Add initial support for controller player
From: Luiz Augusto von Dentz @ 2012-10-29 14:41 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This also bump controller record to 1.3.
---
audio/avrcp.c | 531 ++++++++++++++++++++++++++++++++++++++++++++++++----------
1 file changed, 441 insertions(+), 90 deletions(-)
diff --git a/audio/avrcp.c b/audio/avrcp.c
index 57502ff..4f14513 100644
--- a/audio/avrcp.c
+++ b/audio/avrcp.c
@@ -190,6 +190,7 @@ struct avrcp {
int features;
void (*init) (struct avrcp *session);
+ void (*destroy) (struct avrcp *sesion);
const struct control_pdu_handler *control_handlers;
@@ -216,7 +217,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 +228,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 +432,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 +483,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;
@@ -643,14 +636,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)
@@ -1095,7 +1080,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;
@@ -1357,6 +1342,319 @@ 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_header *pdu = (void *) operands;
+ uint32_t duration;
+ uint32_t position;
+ uint8_t status;
+
+ if (code == AVC_CTYPE_REJECTED || ntohs(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);
+
+ memcpy(&status, pdu->params + 8, sizeof(uint8_t));
+ DBG("%s", 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_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;
+
+ DBG("%s: %s", 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_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);
+ char *value = g_strndup((char *) &pdu->params[i], len);
+
+ DBG("%s: %s", key , value);
+ g_free(value);
+ }
+
+ 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,
@@ -1366,7 +1664,9 @@ static gboolean avrcp_handle_event(struct avctp *conn,
struct avrcp_player *player = session->player;
struct avrcp_header *pdu = (void *) operands;
uint8_t event;
- uint8_t volume;
+ uint8_t value;
+ uint8_t count;
+ int i;
if (code != AVC_CTYPE_INTERIM && code != AVC_CTYPE_CHANGED)
return FALSE;
@@ -1375,24 +1675,58 @@ 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:
+ value = pdu->params[1];
+
+ avrcp_get_play_status(session);
+
+ break;
+ case AVRCP_EVENT_TRACK_CHANGED:
+ avrcp_get_element_attributes(session);
+
+ break;
+
+ case AVRCP_EVENT_SETTINGS_CHANGED:
+ 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;
+
+ DBG("%s: %s", 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;
@@ -1420,6 +1754,7 @@ static gboolean avrcp_get_capabilities_resp(struct avctp *conn,
{
struct avrcp *session = user_data;
struct avrcp_header *pdu = (void *) operands;
+ uint16_t events = 0;
uint8_t count;
if (pdu->params[0] != CAP_EVENTS_SUPPORTED)
@@ -1430,14 +1765,26 @@ 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;
}
}
+ if (!(events & (1 << AVRCP_EVENT_SETTINGS_CHANGED)))
+ avrcp_list_player_attributes(session);
+
+ if (!(events & (1 << AVRCP_EVENT_STATUS_CHANGED)))
+ avrcp_get_play_status(session);
+
+ if (!(events & (1 << AVRCP_EVENT_STATUS_CHANGED)))
+ avrcp_get_element_attributes(session);
+
return FALSE;
}
@@ -1463,31 +1810,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) {
@@ -1516,7 +1838,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);
}
@@ -1533,19 +1856,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,
@@ -1574,9 +1953,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);
}
@@ -1595,25 +1976,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)
{
@@ -1631,7 +1993,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:
@@ -1740,17 +2102,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;
--
1.7.11.7
^ permalink raw reply related
* [PATCH BlueZ 2/7] audio: Export remote player as children object path of device
From: Luiz Augusto von Dentz @ 2012-10-29 14:41 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1351521709-4063-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
The object implements org.bluez.MediaPlayer similar to the target role.
---
Makefile.am | 1 +
audio/avrcp.c | 260 ++++++++++++++++++++++++++---------------
audio/player.c | 343 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
audio/player.h | 37 ++++++
doc/media-api.txt | 20 +++-
5 files changed, 564 insertions(+), 97 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 4f14513..7c26491 100644
--- a/audio/avrcp.c
+++ b/audio/avrcp.c
@@ -61,6 +61,7 @@
#include "control.h"
#include "avdtp.h"
#include "sink.h"
+#include "player.h"
/* Company IDs for vendor dependent commands */
#define IEEEID_BTSIG 0x001958
@@ -432,6 +433,36 @@ 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 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)
{
if (player == NULL)
@@ -440,6 +471,24 @@ static int player_get_setting(struct avrcp_player *player, uint8_t id)
return player->cb->get_setting(id, player->user_data);
}
+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)
{
uint8_t buf[AVRCP_HEADER_LENGTH + 9];
@@ -463,7 +512,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:
@@ -480,9 +529,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;
@@ -519,13 +573,35 @@ void avrcp_player_event(struct avrcp_player *player, uint8_t id, void *data)
return;
}
+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 attr)
+ uint32_t id)
{
if (player != NULL)
- return player->cb->get_metadata(attr, player->user_data);
+ return player->cb->get_metadata(id, player->user_data);
- if (attr == AVRCP_MEDIA_ATTRIBUTE_TITLE)
+ if (id == AVRCP_MEDIA_ATTRIBUTE_TITLE)
return "";
return NULL;
@@ -627,6 +703,49 @@ 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)
{
@@ -1077,7 +1196,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);
@@ -1367,6 +1487,9 @@ static gboolean avrcp_get_play_status_rsp(struct avctp *conn,
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;
@@ -1380,9 +1503,10 @@ static gboolean avrcp_get_play_status_rsp(struct avctp *conn,
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));
- DBG("%s", status_to_string(status));
+ media_player_set_status(mp, status_to_string(status));
return FALSE;
}
@@ -1404,70 +1528,14 @@ 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,
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;
@@ -1492,7 +1560,7 @@ static gboolean avrcp_player_value_rsp(struct avctp *conn,
if (value == NULL)
continue;
- DBG("%s: %s", key, value);
+ media_player_set_setting(mp, key, value);
}
return FALSE;
@@ -1563,33 +1631,14 @@ 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,
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;
@@ -1622,10 +1671,11 @@ static gboolean avrcp_get_attributes_rsp(struct avctp *conn,
if (charset == 106) {
const char *key = metadata_to_str(id);
- char *value = g_strndup((char *) &pdu->params[i], len);
- DBG("%s: %s", key , value);
- g_free(value);
+ if (key != NULL)
+ media_player_set_metadata(mp,
+ metadata_to_str(id),
+ &pdu->params[i], len);
}
i += len;
@@ -1663,9 +1713,11 @@ 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 value;
uint8_t count;
+ const char *curval, *strval;
int i;
if (code != AVC_CTYPE_INTERIM && code != AVC_CTYPE_CHANGED)
@@ -1683,17 +1735,29 @@ static gboolean avrcp_handle_event(struct avctp *conn,
break;
case AVRCP_EVENT_STATUS_CHANGED:
+ mp = player->user_data;
value = pdu->params[1];
- avrcp_get_play_status(session);
+ 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) {
@@ -1709,7 +1773,7 @@ static gboolean avrcp_handle_event(struct avctp *conn,
if (value == NULL)
continue;
- DBG("%s: %s", key, value);
+ media_player_set_setting(mp, key, value);
}
break;
@@ -1753,9 +1817,12 @@ static gboolean avrcp_get_capabilities_resp(struct avctp *conn,
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;
@@ -1776,6 +1843,11 @@ static gboolean avrcp_get_capabilities_resp(struct avctp *conn,
}
}
+ path = device_get_path(session->dev->btd_dev);
+ mp = media_player_controller_create(path);
+ player->user_data = mp;
+ player->destroy = (GDestroyNotify) media_player_destroy;
+
if (!(events & (1 << AVRCP_EVENT_SETTINGS_CHANGED)))
avrcp_list_player_attributes(session);
diff --git a/audio/player.c b/audio/player.c
new file mode 100644
index 0000000..1957594
--- /dev/null
+++ b/audio/player.c
@@ -0,0 +1,343 @@
+/*
+ *
+ * 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;
+};
+
+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)
+{
+ 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);
+
+ 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);
+}
diff --git a/audio/player.h b/audio/player.h
new file mode 100644
index 0000000..f3a421a
--- /dev/null
+++ b/audio/player.h
@@ -0,0 +1,37 @@
+/*
+ *
+ * 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 *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);
diff --git a/doc/media-api.txt b/doc/media-api.txt
index d15d22a..b4f2fc6 100644
--- a/doc/media-api.txt
+++ b/doc/media-api.txt
@@ -126,11 +126,25 @@ Methods void RegisterEndpoint(object endpoint, dict properties)
MediaPlayer hierarchy
=====================
-Service unique name
+Service unique name (Target role)
+ org.bluez (Controller role)
Interface org.bluez.MediaPlayer
-Object path freely definable
+Object path freely definable (Target role)
+ [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/playerX
+ (Controller role)
+
+Methods dict GetProperties()
+
+ Returns all properties for the interface. See the
+ properties section for available properties.
+
+ dict GetTrack()
+
+ Returns known metadata of the current track.
+
+ See TrackChanged for possible values.
-Methods void SetProperty(string property, variant value)
+ void SetProperty(string property, variant value)
Changes the value of the specified property. Only
properties that are listed as read-write can be changed.
--
1.7.11.7
^ permalink raw reply related
* [PATCH BlueZ 3/7] player: Add support for SetProperty
From: Luiz Augusto von Dentz @ 2012-10-29 14:41 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1351521709-4063-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Properties Equalizer, Repeat, Shuffle and Scan can be set by user
application.
---
audio/avrcp.c | 129 ++++++++++++++++++++++++++++++++++++++++++++++-
audio/player.c | 154 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--
audio/player.h | 9 ++++
3 files changed, 288 insertions(+), 4 deletions(-)
diff --git a/audio/avrcp.c b/audio/avrcp.c
index 7c26491..724e139 100644
--- a/audio/avrcp.c
+++ b/audio/avrcp.c
@@ -449,6 +449,60 @@ static const char *attr_to_str(uint8_t attr)
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"))
@@ -1528,6 +1582,22 @@ static void avrcp_get_play_status(struct avrcp *session)
session);
}
+static const char *status_to_str(uint8_t status)
+{
+ switch (status) {
+ case AVRCP_STATUS_INVALID_COMMAND:
+ return "Invalid Command";
+ case AVRCP_STATUS_INVALID_PARAM:
+ return "Invalid Parameter";
+ case AVRCP_STATUS_INTERNAL_ERROR:
+ return "Internal Error";
+ case AVRCP_STATUS_SUCCESS:
+ return "Success";
+ default:
+ return "Unknown";
+ }
+}
+
static gboolean avrcp_player_value_rsp(struct avctp *conn,
uint8_t code, uint8_t subunit,
uint8_t *operands, size_t operand_count,
@@ -1540,8 +1610,11 @@ static gboolean avrcp_player_value_rsp(struct avctp *conn,
uint8_t count;
int i;
- if (code == AVC_CTYPE_REJECTED)
+ if (code == AVC_CTYPE_REJECTED) {
+ media_player_set_setting(mp, "Error",
+ status_to_str(pdu->params[0]));
return FALSE;
+ }
count = pdu->params[0];
@@ -1811,6 +1884,59 @@ static void avrcp_register_notification(struct avrcp *session, uint8_t event)
avrcp_handle_event, session);
}
+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,
@@ -1845,6 +1971,7 @@ static gboolean avrcp_get_capabilities_resp(struct avctp *conn,
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;
diff --git a/audio/player.c b/audio/player.c
index 1957594..e83b761 100644
--- a/audio/player.c
+++ b/audio/player.c
@@ -49,6 +49,12 @@ struct player_callback {
void *user_data;
};
+struct pending_req {
+ DBusMessage *msg;
+ const char *key;
+ const char *value;
+};
+
struct media_player {
char *path; /* Player object path */
GHashTable *settings; /* Player settings */
@@ -58,6 +64,7 @@ struct media_player {
GTimer *progress;
guint process_id;
struct player_callback *cb;
+ GSList *pending;
};
static void append_settings(void *key, void *value, void *user_data)
@@ -142,10 +149,96 @@ static DBusMessage *media_player_get_track(DBusConnection *conn,
return reply;
}
+static struct pending_req *find_pending(struct media_player *mp,
+ const char *key)
+{
+ GSList *l;
+
+ for (l = mp->pending; l; l = l->next) {
+ struct pending_req *p = l->data;
+
+ if (strcasecmp(key, p->key) == 0)
+ return p;
+ }
+
+ return NULL;
+}
+
+static struct pending_req *pending_new(DBusMessage *msg, const char *key,
+ const char *value)
+{
+ struct pending_req *p;
+
+ p = g_new0(struct pending_req, 1);
+ p->msg = dbus_message_ref(msg);
+ p->key = key;
+ p->value = value;
+
+ return p;
+}
+
+static DBusMessage *player_set_setting(struct media_player *mp,
+ DBusMessage *msg, const char *key,
+ const char *value)
+{
+ struct player_callback *cb = mp->cb;
+ struct pending_req *p;
+
+ if (cb == NULL || cb->cbs->set_setting == NULL)
+ return btd_error_not_supported(msg);
+
+ p = find_pending(mp, key);
+ if (p != NULL)
+ return btd_error_in_progress(msg);
+
+ if (!cb->cbs->set_setting(mp, key, value, cb->user_data))
+ return btd_error_invalid_args(msg);
+
+ p = pending_new(msg, key, value);
+
+ mp->pending = g_slist_append(mp->pending, p);
+
+ return NULL;
+}
+
static DBusMessage *media_player_set_property(DBusConnection *conn,
DBusMessage *msg, void *data)
{
- return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+ struct media_player *mp = data;
+ 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);
+
+ curval = g_hash_table_lookup(mp->settings, key);
+ if (g_strcmp0(curval, value) == 0)
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+
+ return player_set_setting(mp, msg, key, value);
}
static const GDBusMethodTable media_player_methods[] = {
@@ -155,7 +248,7 @@ static const GDBusMethodTable media_player_methods[] = {
{ GDBUS_METHOD("GetTrack",
NULL, GDBUS_ARGS({ "metadata", "a{sv}" }),
media_player_get_track) },
- { GDBUS_METHOD("SetProperty",
+ { GDBUS_ASYNC_METHOD("SetProperty",
GDBUS_ARGS({ "name", "s" }, { "value", "v" }),
NULL, media_player_set_property) },
{ }
@@ -169,6 +262,14 @@ static const GDBusSignalTable media_player_signals[] = {
{ }
};
+static void pending_free(void *data)
+{
+ struct pending_req *p = data;
+
+ dbus_message_unref(p->msg);
+ g_free(p);
+}
+
void media_player_destroy(struct media_player *mp)
{
DBG("%s", mp->path);
@@ -185,6 +286,8 @@ void media_player_destroy(struct media_player *mp)
if (mp->process_id > 0)
g_source_remove(mp->process_id);
+ g_slist_free_full(mp->pending, pending_free);
+
g_timer_destroy(mp->progress);
g_free(mp->cb);
g_free(mp->status);
@@ -250,17 +353,46 @@ void media_player_set_setting(struct media_player *mp, const char *key,
const char *value)
{
char *curval;
+ struct pending_req *p;
+ DBusMessage *reply;
DBG("%s: %s", key, value);
+ if (strcasecmp(key, "Error") == 0) {
+ p = g_slist_nth_data(mp->pending, 0);
+ if (p == NULL)
+ return;
+
+ reply = btd_error_failed(p->msg, value);
+ goto send;
+ }
+
curval = g_hash_table_lookup(mp->settings, key);
if (g_strcmp0(curval, value) == 0)
- return;
+ goto done;
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);
+
+done:
+ p = find_pending(mp, key);
+ if (p == NULL)
+ return;
+
+ if (strcasecmp(value, p->value) == 0)
+ reply = g_dbus_create_reply(p->msg, DBUS_TYPE_INVALID);
+ else
+ reply = btd_error_not_supported(p->msg);
+
+send:
+ g_dbus_send_message(btd_get_dbus_connection(), reply);
+
+ mp->pending = g_slist_remove(mp->pending, p);
+ pending_free(p);
+
+ return;
}
const char *media_player_get_status(struct media_player *mp)
@@ -341,3 +473,19 @@ void media_player_set_metadata(struct media_player *mp, const char *key,
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
index f3a421a..4a6a9cc 100644
--- a/audio/player.h
+++ b/audio/player.h
@@ -25,6 +25,11 @@
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);
@@ -35,3 +40,7 @@ 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
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