* [PATCH 2/4] sdp: Move spd_init_services_list to adapter
From: Grzegorz Kolodziejczyk @ 2013-08-26 13:07 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1377522441-28671-1-git-send-email-grzegorz.kolodziejczyk@tieto.com>
Make sdp_init_services_list to be runned in adapter instead of
sdp-database. sdp_init_service is invoked by adapter_register function.
Change-Id: Iddf5afbe410608ca96df727a7c9bb791e457ca77
---
src/adapter.c | 27 ++++++++++++++++++++++++++-
src/adapter.h | 2 ++
src/sdpd-database.c | 31 ++++++++-----------------------
src/sdpd.h | 3 +--
4 files changed, 37 insertions(+), 26 deletions(-)
diff --git a/src/adapter.c b/src/adapter.c
index 723e3a8..fd20d61 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -5603,7 +5603,7 @@ static int adapter_register(struct btd_adapter *adapter)
agent_unref(agent);
}
- sdp_init_services_list(&adapter->bdaddr);
+ adapter_sdp_init_services_list(&adapter->bdaddr);
btd_adapter_gatt_server_start(adapter);
@@ -6306,3 +6306,28 @@ void adapter_start_sdp_server(uint16_t sdp_mtu, uint32_t sdp_flags) {
for (; services; services = services->next)
adapter_foreach(adapter_service_insert, services->data);
}
+
+void adapter_sdp_init_services_list(bdaddr_t *device)
+{
+ sdp_list_t *p;
+
+ p = sdp_get_access_list();
+
+ DBG("");
+
+ for (; p != NULL; p = p->next) {
+ sdp_access_t *access = p->data;
+ sdp_record_t *rec;
+
+ if (bacmp(BDADDR_ANY, &access->device))
+ continue;
+
+ rec = sdp_record_find(access->handle);
+ if (rec == NULL)
+ continue;
+
+ SDPDBG("adding record with handle %x", access->handle);
+
+ adapter_foreach(adapter_service_insert, rec);
+ }
+}
diff --git a/src/adapter.h b/src/adapter.h
index a60a29f..ad01710 100644
--- a/src/adapter.h
+++ b/src/adapter.h
@@ -214,3 +214,5 @@ gboolean btd_adapter_check_oob_handler(struct btd_adapter *adapter);
void btd_adapter_for_each_device(struct btd_adapter *adapter,
void (*cb)(struct btd_device *device, void *data),
void *data);
+
+void adapter_sdp_init_services_list(bdaddr_t *device);
diff --git a/src/sdpd-database.c b/src/sdpd-database.c
index cf33f19..7228cd7 100644
--- a/src/sdpd-database.c
+++ b/src/sdpd-database.c
@@ -285,6 +285,14 @@ sdp_list_t *sdp_get_record_list(void)
return service_db;
}
+/*
+ * Return a pointer to the linked list containing the access db in sorted order
+ */
+sdp_list_t *sdp_get_access_list(void)
+{
+ return access_db;
+}
+
int sdp_check_access(uint32_t handle, bdaddr_t *device)
{
sdp_list_t *p = access_locate(handle);
@@ -314,26 +322,3 @@ uint32_t sdp_next_handle(void)
return handle;
}
-
-void sdp_init_services_list(bdaddr_t *device)
-{
- sdp_list_t *p;
-
- DBG("");
-
- for (p = access_db; p != NULL; p = p->next) {
- sdp_access_t *access = p->data;
- sdp_record_t *rec;
-
- if (bacmp(BDADDR_ANY, &access->device))
- continue;
-
- rec = sdp_record_find(access->handle);
- if (rec == NULL)
- continue;
-
- SDPDBG("adding record with handle %x", access->handle);
-
- adapter_foreach(adapter_service_insert, rec);
- }
-}
diff --git a/src/sdpd.h b/src/sdpd.h
index 9a0e1e9..ad384bd 100644
--- a/src/sdpd.h
+++ b/src/sdpd.h
@@ -68,6 +68,7 @@ sdp_record_t *sdp_record_find(uint32_t handle);
void sdp_record_add(const bdaddr_t *device, sdp_record_t *rec);
int sdp_record_remove(uint32_t handle);
sdp_list_t *sdp_get_record_list(void);
+sdp_list_t *sdp_get_access_list(void);
int sdp_check_access(uint32_t handle, bdaddr_t *device);
uint32_t sdp_next_handle(void);
@@ -81,5 +82,3 @@ void stop_sdp_server(void);
int add_record_to_server(const bdaddr_t *src, sdp_record_t *rec);
int remove_record_from_server(uint32_t handle);
-
-void sdp_init_services_list(bdaddr_t *device);
--
1.7.9.5
^ permalink raw reply related
* [PATCH 3/4] sdp: Make sdp_record_add to be handled by adapter
From: Grzegorz Kolodziejczyk @ 2013-08-26 13:07 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1377522441-28671-1-git-send-email-grzegorz.kolodziejczyk@tieto.com>
Make sdp_record_add to be called from adapter. First write services to
sdp db then to adapter db without revokes to adapter.
Change-Id: I65d638096c91aadacc3cb5ef7a8a9181fa4dae6e
---
profiles/audio/a2dp.c | 2 +-
profiles/audio/avrcp.c | 4 ++--
profiles/health/hdp_util.c | 2 +-
profiles/network/server.c | 2 +-
profiles/sap/server.c | 2 +-
src/adapter.c | 31 +++++++++++++++++++++++++++++++
src/adapter.h | 2 ++
src/attrib-server.c | 2 +-
src/profile.c | 2 +-
src/sdpd-database.c | 10 ----------
10 files changed, 41 insertions(+), 18 deletions(-)
diff --git a/profiles/audio/a2dp.c b/profiles/audio/a2dp.c
index 3f3cc1b..384b143 100644
--- a/profiles/audio/a2dp.c
+++ b/profiles/audio/a2dp.c
@@ -1298,7 +1298,7 @@ struct a2dp_sep *a2dp_add_sep(struct btd_adapter *adapter, uint8_t type,
return NULL;
}
- if (add_record_to_server(adapter_get_address(server->adapter),
+ if (adapter_add_record_to_server(adapter_get_address(server->adapter),
record) < 0) {
error("Unable to register A2DP service record");
sdp_record_free(record);
diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c
index 9f164e4..6734b0b 100644
--- a/profiles/audio/avrcp.c
+++ b/profiles/audio/avrcp.c
@@ -3829,7 +3829,7 @@ done:
return -1;
}
- if (add_record_to_server(adapter_get_address(adapter), record) < 0) {
+ if (adapter_add_record_to_server(adapter_get_address(adapter), record) < 0) {
error("Unable to register AVRCP target service record");
avrcp_target_server_remove(p, adapter);
sdp_record_free(record);
@@ -3912,7 +3912,7 @@ done:
return -1;
}
- if (add_record_to_server(adapter_get_address(adapter), record) < 0) {
+ if (adapter_add_record_to_server(adapter_get_address(adapter), record) < 0) {
error("Unable to register AVRCP service record");
avrcp_controller_server_remove(p, adapter);
sdp_record_free(record);
diff --git a/profiles/health/hdp_util.c b/profiles/health/hdp_util.c
index b53f1db..30e59bb 100644
--- a/profiles/health/hdp_util.c
+++ b/profiles/health/hdp_util.c
@@ -733,7 +733,7 @@ gboolean hdp_update_sdp_record(struct hdp_adapter *adapter, GSList *app_list)
if (sdp_set_record_state(sdp_record, adapter->record_state++) < 0)
goto fail;
- if (add_record_to_server(adapter_get_address(adapter->btd_adapter),
+ if (adapter_add_record_to_server(adapter_get_address(adapter->btd_adapter),
sdp_record) < 0)
goto fail;
adapter->sdp_handler = sdp_record->handle;
diff --git a/profiles/network/server.c b/profiles/network/server.c
index de48bec..980dc3a 100644
--- a/profiles/network/server.c
+++ b/profiles/network/server.c
@@ -587,7 +587,7 @@ static uint32_t register_server_record(struct network_server *ns)
return 0;
}
- if (add_record_to_server(&ns->src, record) < 0) {
+ if (adapter_add_record_to_server(&ns->src, record) < 0) {
error("Failed to register service record");
sdp_record_free(record);
return 0;
diff --git a/profiles/sap/server.c b/profiles/sap/server.c
index dfcf0f1..34a2e38 100644
--- a/profiles/sap/server.c
+++ b/profiles/sap/server.c
@@ -1358,7 +1358,7 @@ int sap_server_register(const char *path, const bdaddr_t *src)
goto sdp_err;
}
- if (add_record_to_server(src, record) < 0) {
+ if (adapter_add_record_to_server(src, record) < 0) {
error("Adding SAP SDP record to the SDP server failed.");
sdp_record_free(record);
goto sdp_err;
diff --git a/src/adapter.c b/src/adapter.c
index fd20d61..8e897ce 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -6307,6 +6307,37 @@ void adapter_start_sdp_server(uint16_t sdp_mtu, uint32_t sdp_flags) {
adapter_foreach(adapter_service_insert, services->data);
}
+int adapter_add_record_to_server(const bdaddr_t *device, sdp_record_t *rec)
+{
+ int chck;
+ struct btd_adapter *adapter;
+
+ chck = add_record_to_server(device, rec);
+
+ // Check if rec handle error occure
+ //
+ // 0 - Ok
+ //
+ // handle == 0xffffffff
+ // -ENOSPC - next handle < 0x10000
+ //
+ // -EEXIST - record already exist
+ if (chck == 0) {
+ // Add rec for each adapter
+ if (bacmp(device, BDADDR_ANY) == 0) {
+ adapter_foreach(adapter_service_insert, rec);
+ return chck;
+ }
+
+ // Add rec for single adapter
+ adapter = adapter_find(device);
+ if (adapter)
+ adapter_service_insert(adapter, rec);
+ }
+
+ return chck;
+}
+
void adapter_sdp_init_services_list(bdaddr_t *device)
{
sdp_list_t *p;
diff --git a/src/adapter.h b/src/adapter.h
index ad01710..3a4b4d9 100644
--- a/src/adapter.h
+++ b/src/adapter.h
@@ -215,4 +215,6 @@ void btd_adapter_for_each_device(struct btd_adapter *adapter,
void (*cb)(struct btd_device *device, void *data),
void *data);
+int adapter_add_record_to_server(const bdaddr_t *device, sdp_record_t *rec);
+
void adapter_sdp_init_services_list(bdaddr_t *device);
diff --git a/src/attrib-server.c b/src/attrib-server.c
index 3f629b0..67631d1 100644
--- a/src/attrib-server.c
+++ b/src/attrib-server.c
@@ -326,7 +326,7 @@ static uint32_t attrib_create_sdp_new(struct gatt_server *server,
"http://www.bluez.org/");
}
- if (add_record_to_server(adapter_get_address(server->adapter), record)
+ if (adapter_add_record_to_server(adapter_get_address(server->adapter), record)
== 0)
return record->handle;
diff --git a/src/profile.c b/src/profile.c
index 90c3535..8e9236c 100644
--- a/src/profile.c
+++ b/src/profile.c
@@ -1220,7 +1220,7 @@ static uint32_t ext_register_record(struct ext_profile *ext,
return 0;
}
- if (add_record_to_server(src, rec) < 0) {
+ if (adapter_add_record_to_server(src, rec) < 0) {
error("Failed to register service record");
sdp_record_free(rec);
return 0;
diff --git a/src/sdpd-database.c b/src/sdpd-database.c
index 7228cd7..335ea0e 100644
--- a/src/sdpd-database.c
+++ b/src/sdpd-database.c
@@ -168,7 +168,6 @@ void sdp_svcdb_set_collectable(sdp_record_t *record, int sock)
*/
void sdp_record_add(const bdaddr_t *device, sdp_record_t *rec)
{
- struct btd_adapter *adapter;
sdp_access_t *dev;
SDPDBG("Adding rec : 0x%lx", (long) rec);
@@ -184,15 +183,6 @@ void sdp_record_add(const bdaddr_t *device, sdp_record_t *rec)
dev->handle = rec->handle;
access_db = sdp_list_insert_sorted(access_db, dev, access_sort);
-
- if (bacmp(device, BDADDR_ANY) == 0) {
- adapter_foreach(adapter_service_insert, rec);
- return;
- }
-
- adapter = adapter_find(device);
- if (adapter)
- adapter_service_insert(adapter, rec);
}
static sdp_list_t *record_locate(uint32_t handle)
--
1.7.9.5
^ permalink raw reply related
* [PATCH 4/4] sdp: Make sdp_record_remove to be handled by adapter
From: Grzegorz Kolodziejczyk @ 2013-08-26 13:07 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1377522441-28671-1-git-send-email-grzegorz.kolodziejczyk@tieto.com>
Make sdp_record_remove to be called from adapter. First remove service
from sdp db then from adapter db without revokes to adapter.
Change-Id: I13a3113909bab2b4494a00ef05e25dc8d113e3ac
---
profiles/audio/a2dp.c | 8 ++++----
profiles/audio/avrcp.c | 4 ++--
profiles/health/hdp.c | 2 +-
profiles/health/hdp_util.c | 2 +-
profiles/network/server.c | 4 ++--
profiles/sap/server.c | 2 +-
src/adapter.c | 40 ++++++++++++++++++++++++++++++++++++++++
src/adapter.h | 1 +
src/attrib-server.c | 6 +++---
src/profile.c | 2 +-
src/sdpd-database.c | 11 -----------
src/sdpd.h | 5 +++++
12 files changed, 61 insertions(+), 26 deletions(-)
diff --git a/profiles/audio/a2dp.c b/profiles/audio/a2dp.c
index 384b143..835fe9a 100644
--- a/profiles/audio/a2dp.c
+++ b/profiles/audio/a2dp.c
@@ -1327,7 +1327,7 @@ void a2dp_remove_sep(struct a2dp_sep *sep)
return;
server->sources = g_slist_remove(server->sources, sep);
if (server->sources == NULL && server->source_record_id) {
- remove_record_from_server(server->source_record_id);
+ adapter_remove_record_from_server(server->source_record_id);
server->source_record_id = 0;
}
} else {
@@ -1335,7 +1335,7 @@ void a2dp_remove_sep(struct a2dp_sep *sep)
return;
server->sinks = g_slist_remove(server->sinks, sep);
if (server->sinks == NULL && server->sink_record_id) {
- remove_record_from_server(server->sink_record_id);
+ adapter_remove_record_from_server(server->sink_record_id);
server->sink_record_id = 0;
}
}
@@ -1944,7 +1944,7 @@ static void a2dp_source_server_remove(struct btd_profile *p,
(GDestroyNotify) a2dp_unregister_sep);
if (server->source_record_id) {
- remove_record_from_server(server->source_record_id);
+ adapter_remove_record_from_server(server->source_record_id);
server->source_record_id = 0;
}
@@ -1989,7 +1989,7 @@ static void a2dp_sink_server_remove(struct btd_profile *p,
g_slist_free_full(server->sinks, (GDestroyNotify) a2dp_unregister_sep);
if (server->sink_record_id) {
- remove_record_from_server(server->sink_record_id);
+ adapter_remove_record_from_server(server->sink_record_id);
server->sink_record_id = 0;
}
diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c
index 6734b0b..825e63c 100644
--- a/profiles/audio/avrcp.c
+++ b/profiles/audio/avrcp.c
@@ -3797,7 +3797,7 @@ static void avrcp_target_server_remove(struct btd_profile *p,
return;
if (server->tg_record_id != 0) {
- remove_record_from_server(server->tg_record_id);
+ adapter_remove_record_from_server(server->tg_record_id);
server->tg_record_id = 0;
}
@@ -3880,7 +3880,7 @@ static void avrcp_controller_server_remove(struct btd_profile *p,
return;
if (server->ct_record_id != 0) {
- remove_record_from_server(server->ct_record_id);
+ adapter_remove_record_from_server(server->ct_record_id);
server->ct_record_id = 0;
}
diff --git a/profiles/health/hdp.c b/profiles/health/hdp.c
index 7f24756..eeac102 100644
--- a/profiles/health/hdp.c
+++ b/profiles/health/hdp.c
@@ -1403,7 +1403,7 @@ void hdp_adapter_unregister(struct btd_adapter *adapter)
hdp_adapter = l->data;
adapters = g_slist_remove(adapters, hdp_adapter);
if (hdp_adapter->sdp_handler > 0)
- remove_record_from_server(hdp_adapter->sdp_handler);
+ adapter_remove_record_from_server(hdp_adapter->sdp_handler);
release_adapter_instance(hdp_adapter);
btd_adapter_unref(hdp_adapter->btd_adapter);
g_free(hdp_adapter);
diff --git a/profiles/health/hdp_util.c b/profiles/health/hdp_util.c
index 30e59bb..34551df 100644
--- a/profiles/health/hdp_util.c
+++ b/profiles/health/hdp_util.c
@@ -693,7 +693,7 @@ gboolean hdp_update_sdp_record(struct hdp_adapter *adapter, GSList *app_list)
sdp_record_t *sdp_record;
if (adapter->sdp_handler > 0)
- remove_record_from_server(adapter->sdp_handler);
+ adapter_remove_record_from_server(adapter->sdp_handler);
if (app_list == NULL) {
adapter->sdp_handler = 0;
diff --git a/profiles/network/server.c b/profiles/network/server.c
index 980dc3a..4efdd74 100644
--- a/profiles/network/server.c
+++ b/profiles/network/server.c
@@ -628,7 +628,7 @@ static void server_disconnect(DBusConnection *conn, void *user_data)
ns->watch_id = 0;
if (ns->record_id) {
- remove_record_from_server(ns->record_id);
+ adapter_remove_record_from_server(ns->record_id);
ns->record_id = 0;
}
@@ -722,7 +722,7 @@ static void server_free(void *data)
server_remove_sessions(ns);
if (ns->record_id)
- remove_record_from_server(ns->record_id);
+ adapter_remove_record_from_server(ns->record_id);
g_free(ns->name);
g_free(ns->bridge);
diff --git a/profiles/sap/server.c b/profiles/sap/server.c
index 34a2e38..f2ccb63 100644
--- a/profiles/sap/server.c
+++ b/profiles/sap/server.c
@@ -1318,7 +1318,7 @@ static void server_remove(struct sap_server *server)
sap_server_remove_conn(server);
- remove_record_from_server(server->record_id);
+ adapter_remove_record_from_server(server->record_id);
if (server->listen_io) {
g_io_channel_shutdown(server->listen_io, TRUE, NULL);
diff --git a/src/adapter.c b/src/adapter.c
index 8e897ce..8a5398f 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -6338,6 +6338,46 @@ int adapter_add_record_to_server(const bdaddr_t *device, sdp_record_t *rec)
return chck;
}
+int adapter_remove_record_from_server(uint32_t handle)
+{
+ int chck;
+ sdp_list_t *p;
+ sdp_record_t *r;
+ sdp_access_t *a;
+ sdp_record_t rh, ah;
+
+ chck = remove_record_from_server(handle);
+
+ if (chck == 0) {
+ // record_locate section
+ rh.handle = handle;
+ p = sdp_list_find(sdp_get_record_list(), &rh, record_sort);
+
+ r = p->data;
+
+ // access_locate section
+ ah.handle = handle;
+ p = sdp_list_find(sdp_get_access_list(), &ah, record_sort);
+
+ if (p == NULL || p->data == NULL)
+ return 0;
+
+ a = p->data;
+
+ // remove from adapter services
+ if (bacmp(&a->device, BDADDR_ANY) != 0) {
+ struct btd_adapter *adapter = adapter_find(&a->device);
+ if (adapter)
+ adapter_service_remove(adapter, r);
+ } else
+ adapter_foreach(adapter_service_remove, r);
+
+ free(a);
+ }
+
+ return chck;
+}
+
void adapter_sdp_init_services_list(bdaddr_t *device)
{
sdp_list_t *p;
diff --git a/src/adapter.h b/src/adapter.h
index 3a4b4d9..4043347 100644
--- a/src/adapter.h
+++ b/src/adapter.h
@@ -216,5 +216,6 @@ void btd_adapter_for_each_device(struct btd_adapter *adapter,
void *data);
int adapter_add_record_to_server(const bdaddr_t *device, sdp_record_t *rec);
+int adapter_remove_record_from_server(uint32_t handle);
void adapter_sdp_init_services_list(bdaddr_t *device);
diff --git a/src/attrib-server.c b/src/attrib-server.c
index 67631d1..89aa4c2 100644
--- a/src/attrib-server.c
+++ b/src/attrib-server.c
@@ -139,10 +139,10 @@ static void gatt_server_free(struct gatt_server *server)
g_slist_free_full(server->clients, (GDestroyNotify) channel_free);
if (server->gatt_sdp_handle > 0)
- remove_record_from_server(server->gatt_sdp_handle);
+ adapter_remove_record_from_server(server->gatt_sdp_handle);
if (server->gap_sdp_handle > 0)
- remove_record_from_server(server->gap_sdp_handle);
+ adapter_remove_record_from_server(server->gap_sdp_handle);
if (server->adapter != NULL)
btd_adapter_unref(server->adapter);
@@ -1380,7 +1380,7 @@ uint32_t attrib_create_sdp(struct btd_adapter *adapter, uint16_t handle,
void attrib_free_sdp(uint32_t sdp_handle)
{
- remove_record_from_server(sdp_handle);
+ adapter_remove_record_from_server(sdp_handle);
}
static uint16_t find_uuid16_avail(struct btd_adapter *adapter, uint16_t nitems)
diff --git a/src/profile.c b/src/profile.c
index 8e9236c..2a0a6ad 100644
--- a/src/profile.c
+++ b/src/profile.c
@@ -1390,7 +1390,7 @@ static void ext_remove_records(struct ext_profile *ext,
ext->records = g_slist_remove(ext->records, r);
- remove_record_from_server(r->handle);
+ adapter_remove_record_from_server(r->handle);
btd_adapter_unref(r->adapter);
g_free(r);
}
diff --git a/src/sdpd-database.c b/src/sdpd-database.c
index 335ea0e..013f0f6 100644
--- a/src/sdpd-database.c
+++ b/src/sdpd-database.c
@@ -41,10 +41,6 @@
static sdp_list_t *service_db;
static sdp_list_t *access_db;
-typedef struct {
- uint32_t handle;
- bdaddr_t device;
-} sdp_access_t;
/*
* Ordering function called when inserting a service record.
@@ -254,13 +250,6 @@ int sdp_record_remove(uint32_t handle)
a = p->data;
- if (bacmp(&a->device, BDADDR_ANY) != 0) {
- struct btd_adapter *adapter = adapter_find(&a->device);
- if (adapter)
- adapter_service_remove(adapter, r);
- } else
- adapter_foreach(adapter_service_remove, r);
-
access_db = sdp_list_remove(access_db, a);
access_free(a);
diff --git a/src/sdpd.h b/src/sdpd.h
index ad384bd..769dc63 100644
--- a/src/sdpd.h
+++ b/src/sdpd.h
@@ -45,6 +45,11 @@ typedef struct request {
int len;
} sdp_req_t;
+typedef struct {
+ uint32_t handle;
+ bdaddr_t device;
+} sdp_access_t;
+
void handle_internal_request(int sk, int mtu, void *data, int len);
void handle_request(int sk, uint8_t *data, int len);
--
1.7.9.5
^ permalink raw reply related
* [PATCH] Bluetooth: Fix error handling for HCI socket options
From: Marcel Holtmann @ 2013-08-26 16:29 UTC (permalink / raw)
To: linux-bluetooth
The HCI sockets for monitor and control do not support any HCI specific
socket options and if tried, an error will be returned. However the
error used is EINVAL and that is not really descriptive. To make it
clear that these sockets are not handling HCI socket options, return
EBADFD instead.
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
---
net/bluetooth/hci_sock.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c
index 49c5c62..f92e913 100644
--- a/net/bluetooth/hci_sock.c
+++ b/net/bluetooth/hci_sock.c
@@ -921,7 +921,7 @@ static int hci_sock_setsockopt(struct socket *sock, int level, int optname,
lock_sock(sk);
if (hci_pi(sk)->channel != HCI_CHANNEL_RAW) {
- err = -EINVAL;
+ err = -EBADFD;
goto done;
}
@@ -1007,7 +1007,7 @@ static int hci_sock_getsockopt(struct socket *sock, int level, int optname,
lock_sock(sk);
if (hci_pi(sk)->channel != HCI_CHANNEL_RAW) {
- err = -EINVAL;
+ err = -EBADFD;
goto done;
}
--
1.8.3.1
^ permalink raw reply related
* [PATCH] Bluetooth: Restrict ioctls to HCI raw channel sockets
From: Marcel Holtmann @ 2013-08-26 16:39 UTC (permalink / raw)
To: linux-bluetooth
The various legacy ioctls used with HCI sockets are limited to raw
channel only. They are not used on the other channels and also have
no meaning there. So return an error if tried to use them.
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
---
net/bluetooth/hci_sock.c | 23 +++++++++++++++++------
1 file changed, 17 insertions(+), 6 deletions(-)
diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c
index f92e913..ab57038 100644
--- a/net/bluetooth/hci_sock.c
+++ b/net/bluetooth/hci_sock.c
@@ -547,6 +547,15 @@ static int hci_sock_ioctl(struct socket *sock, unsigned int cmd,
BT_DBG("cmd %x arg %lx", cmd, arg);
+ lock_sock(sk);
+
+ if (hci_pi(sk)->channel != HCI_CHANNEL_RAW) {
+ err = -EBADFD;
+ goto done;
+ }
+
+ release_sock(sk);
+
switch (cmd) {
case HCIGETDEVLIST:
return hci_get_dev_list(argp);
@@ -591,13 +600,15 @@ static int hci_sock_ioctl(struct socket *sock, unsigned int cmd,
case HCIINQUIRY:
return hci_inquiry(argp);
-
- default:
- lock_sock(sk);
- err = hci_sock_bound_ioctl(sk, cmd, arg);
- release_sock(sk);
- return err;
}
+
+ lock_sock(sk);
+
+ err = hci_sock_bound_ioctl(sk, cmd, arg);
+
+done:
+ release_sock(sk);
+ return err;
}
static int hci_sock_bind(struct socket *sock, struct sockaddr *addr,
--
1.8.3.1
^ permalink raw reply related
* [PATCH] Bluetooth: Report error for HCI reset ioctl when device is down
From: Marcel Holtmann @ 2013-08-27 3:57 UTC (permalink / raw)
To: linux-bluetooth
Even if this is legacy API, there is no reason to not report a proper
error when trying to reset a HCI device that is down.
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
---
net/bluetooth/hci_core.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 8d9b87d..c529e85 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -1341,8 +1341,10 @@ int hci_dev_reset(__u16 dev)
hci_req_lock(hdev);
- if (!test_bit(HCI_UP, &hdev->flags))
+ if (!test_bit(HCI_UP, &hdev->flags)) {
+ ret = -ENETDOWN;
goto done;
+ }
/* Drop queues */
skb_queue_purge(&hdev->rx_q);
--
1.8.3.1
^ permalink raw reply related
* [PATCH 1/2] Bluetooth: Introduce user channel flag for HCI devices
From: Marcel Holtmann @ 2013-08-27 4:40 UTC (permalink / raw)
To: linux-bluetooth
This patch introduces a new user channel flag that allows to give full
control of a HCI device to a user application. The kernel will stay away
from the device and does not allow any further modifications of the
device states.
The existing raw flag is not used since it has a bit of unclear meaning
due to its legacy. Using a new flag makes the code clearer.
A device with the user channel flag set can still be enumerate using the
legacy API, but it does not longer enumerate using the new management
interface used by BlueZ 5 and beyond. This is intentional to not confuse
users of modern systems.
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
---
include/net/bluetooth/hci.h | 1 +
net/bluetooth/hci_core.c | 39 ++++++++++++++++++++++++++++++++++++---
net/bluetooth/hci_sock.c | 15 +++++++++------
net/bluetooth/mgmt.c | 9 +++++++++
4 files changed, 55 insertions(+), 9 deletions(-)
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index aaeaf09..128157d 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -109,6 +109,7 @@ enum {
HCI_SERVICE_CACHE,
HCI_DEBUG_KEYS,
HCI_UNREGISTER,
+ HCI_USER_CHANNEL,
HCI_LE_SCAN,
HCI_SSP_ENABLED,
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index c529e85..541f4d5 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -981,6 +981,11 @@ int hci_inquiry(void __user *arg)
if (!hdev)
return -ENODEV;
+ if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) {
+ err = -EBUSY;
+ goto done;
+ }
+
hci_dev_lock(hdev);
if (inquiry_cache_age(hdev) > INQUIRY_CACHE_AGE_MAX ||
inquiry_cache_empty(hdev) || ir.flags & IREQ_CACHE_FLUSH) {
@@ -1174,7 +1179,8 @@ int hci_dev_open(__u16 dev)
if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks))
set_bit(HCI_RAW, &hdev->flags);
- if (!test_bit(HCI_RAW, &hdev->flags))
+ if (!test_bit(HCI_RAW, &hdev->flags) &&
+ !test_bit(HCI_USER_CHANNEL, &hdev->dev_flags))
ret = __hci_init(hdev);
}
@@ -1185,6 +1191,7 @@ int hci_dev_open(__u16 dev)
set_bit(HCI_UP, &hdev->flags);
hci_notify(hdev, HCI_DEV_UP);
if (!test_bit(HCI_SETUP, &hdev->dev_flags) &&
+ !test_bit(HCI_USER_CHANNEL, &hdev->dev_flags) &&
mgmt_valid_hdev(hdev)) {
hci_dev_lock(hdev);
mgmt_powered(hdev, 1);
@@ -1321,11 +1328,17 @@ int hci_dev_close(__u16 dev)
if (!hdev)
return -ENODEV;
+ if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) {
+ err = -EBUSY;
+ goto done;
+ }
+
if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags))
cancel_delayed_work(&hdev->power_off);
err = hci_dev_do_close(hdev);
+done:
hci_dev_put(hdev);
return err;
}
@@ -1346,6 +1359,11 @@ int hci_dev_reset(__u16 dev)
goto done;
}
+ if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) {
+ ret = -EBUSY;
+ goto done;
+ }
+
/* Drop queues */
skb_queue_purge(&hdev->rx_q);
skb_queue_purge(&hdev->cmd_q);
@@ -1379,10 +1397,15 @@ int hci_dev_reset_stat(__u16 dev)
if (!hdev)
return -ENODEV;
+ if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) {
+ ret = -EBUSY;
+ goto done;
+ }
+
memset(&hdev->stat, 0, sizeof(struct hci_dev_stats));
+done:
hci_dev_put(hdev);
-
return ret;
}
@@ -1399,6 +1422,11 @@ int hci_dev_cmd(unsigned int cmd, void __user *arg)
if (!hdev)
return -ENODEV;
+ if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) {
+ err = -EBUSY;
+ goto done;
+ }
+
switch (cmd) {
case HCISETAUTH:
err = hci_req_sync(hdev, hci_auth_req, dr.dev_opt,
@@ -1457,6 +1485,7 @@ int hci_dev_cmd(unsigned int cmd, void __user *arg)
break;
}
+done:
hci_dev_put(hdev);
return err;
}
@@ -1565,6 +1594,9 @@ static int hci_rfkill_set_block(void *data, bool blocked)
BT_DBG("%p name %s blocked %d", hdev, hdev->name, blocked);
+ if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags))
+ return -EBUSY;
+
if (!blocked)
return 0;
@@ -3451,7 +3483,8 @@ static void hci_rx_work(struct work_struct *work)
hci_send_to_sock(hdev, skb);
}
- if (test_bit(HCI_RAW, &hdev->flags)) {
+ if (test_bit(HCI_RAW, &hdev->flags) ||
+ test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) {
kfree_skb(skb);
continue;
}
diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c
index ab57038..59e68f1 100644
--- a/net/bluetooth/hci_sock.c
+++ b/net/bluetooth/hci_sock.c
@@ -500,6 +500,9 @@ static int hci_sock_bound_ioctl(struct sock *sk, unsigned int cmd,
if (!hdev)
return -EBADFD;
+ if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags))
+ return -EBUSY;
+
switch (cmd) {
case HCISETRAW:
if (!capable(CAP_NET_ADMIN))
@@ -530,19 +533,19 @@ static int hci_sock_bound_ioctl(struct sock *sk, unsigned int cmd,
if (!capable(CAP_NET_ADMIN))
return -EPERM;
return hci_sock_blacklist_del(hdev, (void __user *) arg);
-
- default:
- if (hdev->ioctl)
- return hdev->ioctl(hdev, cmd, arg);
- return -EINVAL;
}
+
+ if (hdev->ioctl)
+ return hdev->ioctl(hdev, cmd, arg);
+
+ return -EINVAL;
}
static int hci_sock_ioctl(struct socket *sock, unsigned int cmd,
unsigned long arg)
{
- struct sock *sk = sock->sk;
void __user *argp = (void __user *) arg;
+ struct sock *sk = sock->sk;
int err;
BT_DBG("cmd %x arg %lx", cmd, arg);
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index fedc539..3070e77 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -339,6 +339,9 @@ static int read_index_list(struct sock *sk, struct hci_dev *hdev, void *data,
if (test_bit(HCI_SETUP, &d->dev_flags))
continue;
+ if (test_bit(HCI_USER_CHANNEL, &d->dev_flags))
+ continue;
+
if (!mgmt_valid_hdev(d))
continue;
@@ -3320,6 +3323,12 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
MGMT_STATUS_INVALID_INDEX);
goto done;
}
+
+ if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) {
+ err = cmd_status(sk, index, opcode,
+ MGMT_STATUS_INVALID_INDEX);
+ goto done;
+ }
}
if (opcode >= ARRAY_SIZE(mgmt_handlers) ||
--
1.8.3.1
^ permalink raw reply related
* [PATCH 2/2] Bluetooth: Introduce new HCI socket channel for user operation
From: Marcel Holtmann @ 2013-08-27 4:40 UTC (permalink / raw)
To: linux-bluetooth
This patch introcuces a new HCI socket channel that allows user
applications to take control over a specific HCI device. The application
gains exclusive access to this device and forces the kernel to stay away
and not manage it. In case of the management interface it will actually
hide the device.
Such operation is useful for security testing tools that need to operate
underneath the Bluetooth stack and need full control over a device. The
advantage here is that the kernel still provides the service of hardware
abstraction and HCI level access. The use of Bluetooth drivers for
hardware access also means that sniffing tools like btmon or hcidump
are still working and the whole set of transaction can be traced with
existing tools.
With the new channel it is possible to send HCI commands, ACL and SCO
data packets and receive HCI events, ACL and SCO packets from the
device. The format follows the well established H:4 protocol.
The new HCI user channel can only be established when a device has been
through its setup routine and is currently powered down. This is
enforced to not cause any problems with current operations. In addition
only one user channel per HCI device is allowed. It is exclusive access
for one user application. Access to this channel is limited to process
with CAP_NET_RAW capability.
Using this new facility does not require any external library or special
ioctl or socket filters. Just create the socket and bind it. After that
the file descriptor is ready to speak H:4 protocol.
struct sockaddr_hci addr;
int fd;
fd = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
memset(&addr, 0, sizeof(addr));
addr.hci_family = AF_BLUETOOTH;
addr.hci_dev = 0;
addr.hci_channel = HCI_CHANNEL_USER;
bind(fd, (struct sockaddr *) &addr, sizeof(addr));
The example shows on how to create a user channel for hci0 device. Error
handling has been left out of the example. However with the limitations
mentioned above it is advised to handle errors. Binding of the user
cahnnel socket can fail for various reasons. Specifically if the device
is currently activated by BlueZ or if the access permissions are not
present.
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
---
include/net/bluetooth/hci.h | 1 +
net/bluetooth/hci_sock.c | 86 ++++++++++++++++++++++++++++++++++++++++++---
2 files changed, 82 insertions(+), 5 deletions(-)
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index 128157d..30c88b5 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -1571,6 +1571,7 @@ struct sockaddr_hci {
#define HCI_DEV_NONE 0xffff
#define HCI_CHANNEL_RAW 0
+#define HCI_CHANNEL_USER 1
#define HCI_CHANNEL_MONITOR 2
#define HCI_CHANNEL_CONTROL 3
diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c
index 59e68f1..c09e976 100644
--- a/net/bluetooth/hci_sock.c
+++ b/net/bluetooth/hci_sock.c
@@ -126,11 +126,20 @@ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb)
if (skb->sk == sk)
continue;
- if (hci_pi(sk)->channel != HCI_CHANNEL_RAW)
- continue;
-
- if (is_filtered_packet(sk, skb))
+ if (hci_pi(sk)->channel == HCI_CHANNEL_RAW) {
+ if (is_filtered_packet(sk, skb))
+ continue;
+ } else if (hci_pi(sk)->channel == HCI_CHANNEL_USER) {
+ if (!bt_cb(skb)->incoming)
+ continue;
+ if (bt_cb(skb)->pkt_type != HCI_EVENT_PKT &&
+ bt_cb(skb)->pkt_type != HCI_ACLDATA_PKT &&
+ bt_cb(skb)->pkt_type != HCI_SCODATA_PKT)
+ continue;
+ } else {
+ /* Don't send frame to other channel types */
continue;
+ }
if (!skb_copy) {
/* Create a private copy with headroom */
@@ -444,6 +453,12 @@ static int hci_sock_release(struct socket *sock)
bt_sock_unlink(&hci_sk_list, sk);
if (hdev) {
+ if (hci_pi(sk)->channel == HCI_CHANNEL_USER) {
+ mgmt_index_added(hdev);
+ clear_bit(HCI_USER_CHANNEL, &hdev->dev_flags);
+ hci_dev_close(hdev->id);
+ }
+
atomic_dec(&hdev->promisc);
hci_dev_put(hdev);
}
@@ -661,6 +676,56 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr,
hci_pi(sk)->hdev = hdev;
break;
+ case HCI_CHANNEL_USER:
+ if (hci_pi(sk)->hdev) {
+ err = -EALREADY;
+ goto done;
+ }
+
+ if (haddr.hci_dev == HCI_DEV_NONE) {
+ err = -EINVAL;
+ goto done;
+ }
+
+ if (!capable(CAP_NET_RAW)) {
+ err = -EPERM;
+ goto done;
+ }
+
+ hdev = hci_dev_get(haddr.hci_dev);
+ if (!hdev) {
+ err = -ENODEV;
+ goto done;
+ }
+
+ if (test_bit(HCI_UP, &hdev->flags) ||
+ test_bit(HCI_INIT, &hdev->flags) ||
+ test_bit(HCI_SETUP, &hdev->dev_flags)) {
+ err = -EBUSY;
+ hci_dev_put(hdev);
+ goto done;
+ }
+
+ if (test_and_set_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) {
+ err = -EUSERS;
+ hci_dev_put(hdev);
+ goto done;
+ }
+
+ mgmt_index_removed(hdev);
+
+ err = hci_dev_open(hdev->id);
+ if (err) {
+ clear_bit(HCI_USER_CHANNEL, &hdev->dev_flags);
+ hci_dev_put(hdev);
+ goto done;
+ }
+
+ atomic_inc(&hdev->promisc);
+
+ hci_pi(sk)->hdev = hdev;
+ break;
+
case HCI_CHANNEL_CONTROL:
if (haddr.hci_dev != HCI_DEV_NONE) {
err = -EINVAL;
@@ -807,6 +872,7 @@ static int hci_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
case HCI_CHANNEL_RAW:
hci_sock_cmsg(sk, msg, skb);
break;
+ case HCI_CHANNEL_USER:
case HCI_CHANNEL_CONTROL:
case HCI_CHANNEL_MONITOR:
sock_recv_timestamp(msg, sk, skb);
@@ -841,6 +907,7 @@ static int hci_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
switch (hci_pi(sk)->channel) {
case HCI_CHANNEL_RAW:
+ case HCI_CHANNEL_USER:
break;
case HCI_CHANNEL_CONTROL:
err = mgmt_control(sk, msg, len);
@@ -877,7 +944,8 @@ static int hci_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
skb_pull(skb, 1);
skb->dev = (void *) hdev;
- if (bt_cb(skb)->pkt_type == HCI_COMMAND_PKT) {
+ if (hci_pi(sk)->channel == HCI_CHANNEL_RAW &&
+ bt_cb(skb)->pkt_type == HCI_COMMAND_PKT) {
u16 opcode = get_unaligned_le16(skb->data);
u16 ogf = hci_opcode_ogf(opcode);
u16 ocf = hci_opcode_ocf(opcode);
@@ -908,6 +976,14 @@ static int hci_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
goto drop;
}
+ if (hci_pi(sk)->channel == HCI_CHANNEL_USER &&
+ bt_cb(skb)->pkt_type != HCI_COMMAND_PKT &&
+ bt_cb(skb)->pkt_type != HCI_ACLDATA_PKT &&
+ bt_cb(skb)->pkt_type != HCI_SCODATA_PKT) {
+ err = -EINVAL;
+ goto drop;
+ }
+
skb_queue_tail(&hdev->raw_q, skb);
queue_work(hdev->workqueue, &hdev->tx_work);
}
--
1.8.3.1
^ permalink raw reply related
* [PATCH] Bluetooth: Use devname:vhci module alias for virtual HCI driver
From: Marcel Holtmann @ 2013-08-27 5:02 UTC (permalink / raw)
To: linux-bluetooth
To allow creating /dev/vhci device node, add the proper module alias for
this driver.
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
---
drivers/bluetooth/hci_vhci.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/bluetooth/hci_vhci.c b/drivers/bluetooth/hci_vhci.c
index d8b7aed..a1ea5b1 100644
--- a/drivers/bluetooth/hci_vhci.c
+++ b/drivers/bluetooth/hci_vhci.c
@@ -309,3 +309,4 @@ MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
MODULE_DESCRIPTION("Bluetooth virtual HCI driver ver " VERSION);
MODULE_VERSION(VERSION);
MODULE_LICENSE("GPL");
+MODULE_ALIAS("devname:vhci");
--
1.8.3.1
^ permalink raw reply related
* Re: [PATCH 2/2] obexd: Add property changed signals for 'org.bluez.obex.Message1'
From: Christian Fetzer @ 2013-08-27 7:09 UTC (permalink / raw)
To: Luiz Augusto von Dentz; +Cc: linux-bluetooth@vger.kernel.org
In-Reply-To: <CABBYNZ+5viOEhXeaGzvO7ZtjCaNrxeFX+c01eqbyURg2_XAdZw@mail.gmail.com>
Hi Luiz,
On 08/26/2013 02:08 PM, Luiz Augusto von Dentz wrote:
> Hi Christian,
>
> On Thu, Aug 22, 2013 at 7:08 PM, Christian Fetzer
> <christian.fetzer@oss.bmw-carit.de> wrote:
>> From: Christian Fetzer <christian.fetzer@bmw-carit.de>
>>
>> This patch adds property changed signal emissions in case message properties
>> change on the server.
>> ---
>> obexd/client/map.c | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++++--
>> 1 file changed, 92 insertions(+), 2 deletions(-)
>>
>> diff --git a/obexd/client/map.c b/obexd/client/map.c
>> index 8864a54..a9a9bcd 100644
>> --- a/obexd/client/map.c
>> +++ b/obexd/client/map.c
>> @@ -792,121 +792,211 @@ static struct map_msg *map_msg_create(struct map_data *data, const char *handle)
>>
>> static void parse_subject(struct map_msg *msg, const char *value)
>> {
>> + if (g_strcmp0(msg->subject, value) == 0)
>> + return;
>> +
>> g_free(msg->subject);
>> msg->subject = g_strdup(value);
>> +
>> + g_dbus_emit_property_changed(conn, msg->path,
>> + MAP_MSG_INTERFACE, "Subject");
>> }
>>
>> static void parse_datetime(struct map_msg *msg, const char *value)
>> {
>> + if (g_strcmp0(msg->timestamp, value) == 0)
>> + return;
>> +
>> g_free(msg->timestamp);
>> msg->timestamp = g_strdup(value);
>> +
>> + g_dbus_emit_property_changed(conn, msg->path,
>> + MAP_MSG_INTERFACE, "Timestamp");
>> }
>>
>> static void parse_sender(struct map_msg *msg, const char *value)
>> {
>> + if (g_strcmp0(msg->sender, value) == 0)
>> + return;
>> +
>> g_free(msg->sender);
>> msg->sender = g_strdup(value);
>> +
>> + g_dbus_emit_property_changed(conn, msg->path,
>> + MAP_MSG_INTERFACE, "Sender");
>> }
>>
>> static void parse_sender_address(struct map_msg *msg, const char *value)
>> {
>> + if (g_strcmp0(msg->sender_address, value) == 0)
>> + return;
>> +
>> g_free(msg->sender_address);
>> msg->sender_address = g_strdup(value);
>> +
>> + g_dbus_emit_property_changed(conn, msg->path,
>> + MAP_MSG_INTERFACE, "SenderAddress");
>> }
>>
>> static void parse_replyto(struct map_msg *msg, const char *value)
>> {
>> + if (g_strcmp0(msg->replyto, value) == 0)
>> + return;
>> +
>> g_free(msg->replyto);
>> msg->replyto = g_strdup(value);
>> +
>> + g_dbus_emit_property_changed(conn, msg->path,
>> + MAP_MSG_INTERFACE, "ReplyTo");
>> }
>>
>> static void parse_recipient(struct map_msg *msg, const char *value)
>> {
>> + if (g_strcmp0(msg->recipient, value) == 0)
>> + return;
>> +
>> g_free(msg->recipient);
>> msg->recipient = g_strdup(value);
>> +
>> + g_dbus_emit_property_changed(conn, msg->path,
>> + MAP_MSG_INTERFACE, "Recipient");
>> }
>>
>> static void parse_recipient_address(struct map_msg *msg, const char *value)
>> {
>> + if (g_strcmp0(msg->recipient_address, value) == 0)
>> + return;
>> +
>> g_free(msg->recipient_address);
>> msg->recipient_address = g_strdup(value);
>> +
>> + g_dbus_emit_property_changed(conn, msg->path,
>> + MAP_MSG_INTERFACE, "RecipientAddress");
>> }
>>
>> static void parse_type(struct map_msg *msg, const char *value)
>> {
>> + if (g_strcmp0(msg->type, value) == 0)
>> + return;
>> +
>> g_free(msg->type);
>> msg->type = g_strdup(value);
>> +
>> + g_dbus_emit_property_changed(conn, msg->path,
>> + MAP_MSG_INTERFACE, "Type");
>> }
>>
>> static void parse_size(struct map_msg *msg, const char *value)
>> {
>> - msg->size = g_ascii_strtoll(value, NULL, 10);
>> + uint64_t size = g_ascii_strtoll(value, NULL, 10);
>> +
>> + if (msg->size == size)
>> + return;
>> +
>> + msg->size = size;
>> +
>> + g_dbus_emit_property_changed(conn, msg->path,
>> + MAP_MSG_INTERFACE, "Size");
>> }
>>
>> static void parse_text(struct map_msg *msg, const char *value)
>> {
>> gboolean flag = strcasecmp(value, "no") != 0;
>>
>> + uint8_t oldflags = msg->flags;
>> if (flag)
>> msg->flags |= MAP_MSG_FLAG_TEXT;
>> else
>> msg->flags &= ~MAP_MSG_FLAG_TEXT;
>>
>> + if (msg->flags != oldflags)
>> + g_dbus_emit_property_changed(conn, msg->path,
>> + MAP_MSG_INTERFACE, "Text");
>> }
>>
>> static void parse_status(struct map_msg *msg, const char *value)
>> {
>> + if (g_strcmp0(msg->status, value) == 0)
>> + return;
>> +
>> g_free(msg->status);
>> msg->status = g_strdup(value);
>> +
>> + g_dbus_emit_property_changed(conn, msg->path,
>> + MAP_MSG_INTERFACE, "Status");
>> }
>>
>> static void parse_attachment_size(struct map_msg *msg, const char *value)
>> {
>> - msg->attachment_size = g_ascii_strtoll(value, NULL, 10);
>> + uint64_t attachment_size = g_ascii_strtoll(value, NULL, 10);
>> +
>> + if (msg->attachment_size == attachment_size)
>> + return;
>> +
>> + msg->attachment_size = attachment_size;
>> +
>> + g_dbus_emit_property_changed(conn, msg->path,
>> + MAP_MSG_INTERFACE, "AttachmentSize");
>> }
>>
>> static void parse_priority(struct map_msg *msg, const char *value)
>> {
>> gboolean flag = strcasecmp(value, "no") != 0;
>>
>> + uint8_t oldflags = msg->flags;
>> if (flag)
>> msg->flags |= MAP_MSG_FLAG_PRIORITY;
>> else
>> msg->flags &= ~MAP_MSG_FLAG_PRIORITY;
>>
>> + if (msg->flags != oldflags)
>> + g_dbus_emit_property_changed(conn, msg->path,
>> + MAP_MSG_INTERFACE, "Priority");
>> }
>>
>> static void parse_read(struct map_msg *msg, const char *value)
>> {
>> gboolean flag = strcasecmp(value, "no") != 0;
>>
>> + uint8_t oldflags = msg->flags;
>> if (flag)
>> msg->flags |= MAP_MSG_FLAG_READ;
>> else
>> msg->flags &= ~MAP_MSG_FLAG_READ;
>>
>> + if (msg->flags != oldflags)
>> + g_dbus_emit_property_changed(conn, msg->path,
>> + MAP_MSG_INTERFACE, "Read");
>> }
>>
>> static void parse_sent(struct map_msg *msg, const char *value)
>> {
>> gboolean flag = strcasecmp(value, "no") != 0;
>>
>> + uint8_t oldflags = msg->flags;
>> if (flag)
>> msg->flags |= MAP_MSG_FLAG_SENT;
>> else
>> msg->flags &= ~MAP_MSG_FLAG_SENT;
>>
>> + if (msg->flags != oldflags)
>> + g_dbus_emit_property_changed(conn, msg->path,
>> + MAP_MSG_INTERFACE, "Sent");
>> }
>>
>> static void parse_protected(struct map_msg *msg, const char *value)
>> {
>> gboolean flag = strcasecmp(value, "no") != 0;
>>
>> + uint8_t oldflags = msg->flags;
>> if (flag)
>> msg->flags |= MAP_MSG_FLAG_PROTECTED;
>> else
>> msg->flags &= ~MAP_MSG_FLAG_PROTECTED;
>>
>> + if (msg->flags != oldflags)
>> + g_dbus_emit_property_changed(conn, msg->path,
>> + MAP_MSG_INTERFACE, "Protected");
>> }
>>
>> static struct map_msg_parser {
>> --
>> 1.8.3.4
>
> Does this make us emit signals when creating the objects? That should
> probably not be necessary, only emit signals once the value really
> changes.
>
>
The property changed signals are not sent for newly created objects.
This is already ensured in g_dbus_emit_property_changed (gdbus/object.c:1709).
Do you prefer an additional check?
Thanks,
Christian
^ permalink raw reply
* hciconfig list commands (patch)
From: Richard Neill @ 2013-08-27 12:11 UTC (permalink / raw)
To: linux-bluetooth
[-- Attachment #1: Type: text/plain, Size: 1842 bytes --]
Dear All,
I'd like to offer a small patch to tools/hciconfig.c (and the man page).
Currently, running the command "hciconfig hci0 commands" creates a list
of supported codes and their names in this format[1]. It's rather tricky
to interpret this format, when trying to discover the command code for a
particular named action, because both lists are very long, and run
consecutively rather than concurrently.
My patch adds an extra command ""hciconfig hci0 commands_list", which
hopefully makes this clearer, emitting output in the format of [2].
I hope this is helpful :-)
If it's necessary to have a legal declaration, then I hereby state for
the record that I, Richard Neill, wrote the attached code, and I release
it under the GPL 2+.
[1] output of "hciconfig hci0 commands"
------------------------
Commands: Octet 0 = 0xff (Bit 0 1 2 3 4 5 6 7)
Octet 1 = 0xff (Bit 0 1 2 3 4 5 6 7)
....
'Inquiry' 'Inquiry Cancel' 'Periodic Inquiry Mode'
'Exit Periodic Inquiry Mode' 'Create Connection' 'Disconnect'
'Add SCO Connection' 'Cancel Create Connection'
'Accept Connection Request' 'Reject Connection Request'
....
------------------------
[2] output of "hciconfig hci0 commands_list"
-------------------------
Byte: Octet: Bit: Command:
0x00 0 0 'Inquiry'
0x01 0 1 'Inquiry Cancel'
0x02 0 2 'Periodic Inquiry Mode'
0x03 0 3 'Exit Periodic Inquiry Mode'
0x04 0 4 'Create Connection'
0x05 0 5 'Disconnect'
0x06 0 6 'Add SCO Connection'
0x07 0 7 'Cancel Create Connection'
....
-------------------------
Best wishes,
Richard
P.S. This is my contribution to bluez, so I hope I'm sending the right
format to the right place. If I could do this better, I'd welcome
feedback off-list.
[-- Attachment #2: hciconfig.1.patch --]
[-- Type: text/x-patch, Size: 103 bytes --]
225a226,228
> .BI commands_list
> Display supported commands in a list format (easier to parse).
> .TP
[-- Attachment #3: hciconfig.c.patch --]
[-- Type: text/x-patch, Size: 1117 bytes --]
1119a1120,1155
> static void cmd_commands_list(int ctl, int hdev, char *opt)
> {
> uint8_t cmds[64];
> char *str;
> int i, n, dd;
>
> dd = hci_open_dev(hdev);
> if (dd < 0) {
> fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
> hdev, strerror(errno), errno);
> exit(1);
> }
>
> if (hci_read_local_commands(dd, cmds, 1000) < 0) {
> fprintf(stderr, "Can't read support commands on hci%d: %s (%d)\n",
> hdev, strerror(errno), errno);
> exit(1);
> }
>
> print_dev_hdr(&di);
> printf ("\tCommands:\n\tByte: Octet: Bit: Command:\n");
> for (i = 0; i < 64; i++) {
> if (!cmds[i]){
> continue;
> }
> for (n = 0; n < 8; n++){
> if (cmds[i] & (1 << n)){
> printf("\t0x%02x %2d %2d '%s'\n",
> (8*i + n), i, n, hci_cmdtostr(8*i + n) );
> }
> }
> printf ("\n");
> }
> hci_close_dev(dd);
> }
>
1926c1962,1963
< { "commands", cmd_commands, 0, "Display supported commands" },
---
> { "commands_list",cmd_commands_list, 0, "Display supported commands (list format)" },
> { "commands",cmd_commands, 0, "Display supported commands" },
^ permalink raw reply
* Re: [PATCH v5 0/6] rfcomm: Implement rfcomm as a proper tty_port
From: Alexander Holler @ 2013-08-27 13:57 UTC (permalink / raw)
To: Peter Hurley
Cc: gustavo, Gianluca Anzolin, marcel, linux-bluetooth, gregkh,
jslaby
In-Reply-To: <52127E0A.6060208@hurleysoftware.com>
Am 19.08.2013 22:20, schrieb Peter Hurley:
> On 07/31/2013 01:50 PM, Peter Hurley wrote:
>> I reviewed these changes and retested. All ok.
>
> Gustavo,
>
> This series fixes a crashing regression from 3.10+ forward.
> Why is this not in linux-next yet?
3.8+ to be exact, just in case someone has to deal with older kernels.
Regards,
Alexander Holler
^ permalink raw reply
* [PATCH] rfcomm: don't release the port in rfcomm_dev_state_change()
From: Gianluca Anzolin @ 2013-08-27 16:28 UTC (permalink / raw)
To: gustavo; +Cc: peter, marcel, linux-bluetooth, gregkh, jslaby, Gianluca Anzolin
When the dlc is closed, rfcomm_dev_state_change() tries to release the
port in the case it cannot get a reference to the tty. However this is
racy and not even needed.
Infact as Peter Hurley points out:
1. Only consider dlcs that are 'stolen' from a connected socket, ie.
reused. Allocated dlcs cannot have been closed prior to port
activate and so for these dlcs a tty reference will always be avail
in rfcomm_dev_state_change() -- except for the conditions covered by
#2b below.
2. If a tty was at some point previously created for this rfcomm, then
either
(a) the tty reference is still avail, so rfcomm_dev_state_change()
will perform a hangup. So nothing to do, or,
(b) the tty reference is no longer avail, and the tty_port will be
destroyed by the last tty_port_put() in rfcomm_tty_cleanup.
Again, no action required.
3. Prior to obtaining the dlc lock in rfcomm_dev_add(),
rfcomm_dev_state_change() will not 'see' a rfcomm_dev so nothing to
do here.
4. After releasing the dlc lock in rfcomm_dev_add(),
rfcomm_dev_state_change() will 'see' an incomplete rfcomm_dev if a
tty reference could not be obtained. Again, the best thing to do here
is nothing. Any future attempted open() will block on
rfcomm_dev_carrier_raised(). The unconnected device will exist until
released by ioctl(RFCOMMRELEASEDEV).
The patch removes the aforementioned code and uses the
tty_port_tty_hangup() helper to hangup the tty.
Signed-off-by: Gianluca Anzolin <gianluca@sottospazio.it>
---
net/bluetooth/rfcomm/tty.c | 35 ++---------------------------------
1 file changed, 2 insertions(+), 33 deletions(-)
diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c
index 6d126fa..84fcf9f 100644
--- a/net/bluetooth/rfcomm/tty.c
+++ b/net/bluetooth/rfcomm/tty.c
@@ -569,7 +569,6 @@ static void rfcomm_dev_data_ready(struct rfcomm_dlc *dlc, struct sk_buff *skb)
static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err)
{
struct rfcomm_dev *dev = dlc->owner;
- struct tty_struct *tty;
if (!dev)
return;
@@ -581,38 +580,8 @@ static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err)
DPM_ORDER_DEV_AFTER_PARENT);
wake_up_interruptible(&dev->port.open_wait);
- } else if (dlc->state == BT_CLOSED) {
- tty = tty_port_tty_get(&dev->port);
- if (!tty) {
- if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) {
- /* Drop DLC lock here to avoid deadlock
- * 1. rfcomm_dev_get will take rfcomm_dev_lock
- * but in rfcomm_dev_add there's lock order:
- * rfcomm_dev_lock -> dlc lock
- * 2. tty_port_put will deadlock if it's
- * the last reference
- *
- * FIXME: when we release the lock anything
- * could happen to dev, even its destruction
- */
- rfcomm_dlc_unlock(dlc);
- if (rfcomm_dev_get(dev->id) == NULL) {
- rfcomm_dlc_lock(dlc);
- return;
- }
-
- if (!test_and_set_bit(RFCOMM_TTY_RELEASED,
- &dev->flags))
- tty_port_put(&dev->port);
-
- tty_port_put(&dev->port);
- rfcomm_dlc_lock(dlc);
- }
- } else {
- tty_hangup(tty);
- tty_kref_put(tty);
- }
- }
+ } else if (dlc->state == BT_CLOSED)
+ tty_port_tty_hangup(&dev->port, false);
}
static void rfcomm_dev_modem_status(struct rfcomm_dlc *dlc, u8 v24_sig)
--
1.8.4
^ permalink raw reply related
* linux-firmware: update Intel Bluetooth firmware patch file
From: Tedd Ho-Jeong An @ 2013-08-27 16:58 UTC (permalink / raw)
To: dwmw2, ben, david.woodhouse; +Cc: linux-bluetooth
From: Tedd Ho-Jeong An <tedd.an@intel.com>
build version: 1334_02
firmware patch version: 2e
This patch fixes the following issues:
- Send invalid SCO connection handle
- Device goes into unexpected states while streaming A2DP audio
- HCI command buffer is overwritten by the following USB autosuspend command
Signed-off-by: Tedd Ho-Jeong An <tedd.an@intel.com>
---
This is 2nd submission with minor commit message changes.
intel/ibt-hw-37.7.10-fw-1.80.2.3.d.bseq | Bin 13315 -> 16516 bytes
1 file changed, 0 insertions(+), 0 deletions(-)
diff --git a/intel/ibt-hw-37.7.10-fw-1.80.2.3.d.bseq b/intel/ibt-hw-37.7.10-fw-1.80.2.3.d.bseq
index 5b87ae1c2ff733038bdff5eb14af89de82a6c0c2..44736e45b6764b8aa3369ec0a2c18f4657e02ef9 100644
GIT binary patch
literal 16516
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
literal 13315
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
Yptzc4re?(&o)EF}xBM_L8n40s1+U%BN&o-=
--
1.8.1.2
--
^ permalink raw reply
* Re: [PATCH v5 0/6] rfcomm: Implement rfcomm as a proper tty_port
From: Peter Hurley @ 2013-08-27 17:50 UTC (permalink / raw)
To: Alexander Holler, gustavo, Gianluca Anzolin, marcel
Cc: linux-bluetooth, gregkh, jslaby
In-Reply-To: <521CB03B.1030003@ahsoftware.de>
On 08/27/2013 09:57 AM, Alexander Holler wrote:
> Am 19.08.2013 22:20, schrieb Peter Hurley:
>> On 07/31/2013 01:50 PM, Peter Hurley wrote:
>
>>> I reviewed these changes and retested. All ok.
>>
>> Gustavo,
>>
>> This series fixes a crashing regression from 3.10+ forward.
>> Why is this not in linux-next yet?
>
> 3.8+ to be exact, just in case someone has to deal with older kernels.
Thanks for the reminder.
This series is too extensive to consider for -stable. Any ideas for
how to address this crash for -stable?
Regards,
Peter Hurley
^ permalink raw reply
* Re: linux-firmware: update Intel Bluetooth firmware patch file
From: Bjørn Mork @ 2013-08-27 19:33 UTC (permalink / raw)
To: Tedd Ho-Jeong An; +Cc: dwmw2, ben, david.woodhouse, linux-bluetooth
In-Reply-To: <2050211.gNpUdmFyHg@tedd-ubuntu>
Tedd Ho-Jeong An <tedd.an@intel.com> writes:
> From: Tedd Ho-Jeong An <tedd.an@intel.com>
>
> build version: 1334_02
> firmware patch version: 2e
>
> This patch fixes the following issues:
> - Send invalid SCO connection handle
> - Device goes into unexpected states while streaming A2DP audio
> - HCI command buffer is overwritten by the following USB autosuspend command
Just wanted to confirm that this firmware fixes the autosuspend issues
I observed. Thanks!
Bjørn
^ permalink raw reply
* Simultaneous BLE connections
From: jonsmirl @ 2013-08-27 21:04 UTC (permalink / raw)
To: linux-bluetooth
How are multiple BLE connections implemented in Bluez? Is it a
function of host memory like IOS with a high limit (1000's)? I see an
old post saying that Bluez only supports one connection at a time.
This has a large impact on host power consumption. With a connection
you know the windows where you have to listen for a peripheral
connection. If instead you use advertising to trigger the connection
the host has to always be scanning since you never know when the
peripheral is going to advertise.
An example system might have twenty BLE sensors that send alerts when
they are tripped. Wouldn't it be better to have 20 long lasting
connections to these sensors? Now the host only has to wake up at the
windows where it is legal for the peripheral to transmit. And the
peripherals can take advantage of their ability to skip transmission
windows.
If instead the peripherals only use advertising the host has to always
be scanning since they don't know when the peripheral might trip.
Doing that consumes a lot of battery power.
--
Jon Smirl
jonsmirl@gmail.com
^ permalink raw reply
* Re: [PATCH 1/4] sdp: Move sdp server init to adapter
From: Frederic Danis @ 2013-08-28 10:01 UTC (permalink / raw)
To: Grzegorz Kolodziejczyk; +Cc: linux-bluetooth
In-Reply-To: <1377522441-28671-2-git-send-email-grzegorz.kolodziejczyk@tieto.com>
Hello Grzegorz,
On 26/08/2013 15:07, Grzegorz Kolodziejczyk wrote:
> Change-Id: Ie8440d59a00750f3d8aec49924d6b209bc6ebe2f
> ---
> src/adapter.c | 11 +++++++++++
> src/adapter.h | 1 +
> src/main.c | 2 +-
> 3 files changed, 13 insertions(+), 1 deletion(-)
>
> diff --git a/src/adapter.c b/src/adapter.c
> index 17f5508..723e3a8 100644
> --- a/src/adapter.c
> +++ b/src/adapter.c
> @@ -6295,3 +6295,14 @@ void adapter_shutdown(void)
> if (!adapter_remaining)
> btd_exit();
> }
> +
> +void adapter_start_sdp_server(uint16_t sdp_mtu, uint32_t sdp_flags) {
Place opening brace of function at the beginning of the next line, please.
Regards
Fred
--
Frederic Danis Open Source Technology Center
frederic.danis@intel.com Intel Corporation
^ permalink raw reply
* Re: [PATCH 3/4] sdp: Make sdp_record_add to be handled by adapter
From: Frederic Danis @ 2013-08-28 10:02 UTC (permalink / raw)
To: Grzegorz Kolodziejczyk; +Cc: linux-bluetooth
In-Reply-To: <1377522441-28671-4-git-send-email-grzegorz.kolodziejczyk@tieto.com>
Hello Grzegorz,
On 26/08/2013 15:07, Grzegorz Kolodziejczyk wrote:
<snip>
> diff --git a/src/adapter.c b/src/adapter.c
> index fd20d61..8e897ce 100644
> --- a/src/adapter.c
> +++ b/src/adapter.c
> @@ -6307,6 +6307,37 @@ void adapter_start_sdp_server(uint16_t sdp_mtu, uint32_t sdp_flags) {
> adapter_foreach(adapter_service_insert, services->data);
> }
>
> +int adapter_add_record_to_server(const bdaddr_t *device, sdp_record_t *rec)
> +{
> + int chck;
> + struct btd_adapter *adapter;
> +
> + chck = add_record_to_server(device, rec);
> +
> + // Check if rec handle error occure
> + //
> + // 0 - Ok
> + //
> + // handle == 0xffffffff
> + // -ENOSPC - next handle < 0x10000
> + //
> + // -EEXIST - record already exist
Please use C89 coding style for comments, like defined for the Linux
kernel. I.e.:
/*
* comment
* comment
*/
> + if (chck == 0) {
> + // Add rec for each adapter
idem here ( /* comment */ )
> + if (bacmp(device, BDADDR_ANY) == 0) {
> + adapter_foreach(adapter_service_insert, rec);
> + return chck;
> + }
> +
> + // Add rec for single adapter
and here
> + adapter = adapter_find(device);
> + if (adapter)
> + adapter_service_insert(adapter, rec);
> + }
> +
> + return chck;
> +}
> +
Regards
Fred
--
Frederic Danis Open Source Technology Center
frederic.danis@intel.com Intel Corporation
^ permalink raw reply
* Re: [PATCH 4/4] sdp: Make sdp_record_remove to be handled by adapter
From: Frederic Danis @ 2013-08-28 10:04 UTC (permalink / raw)
To: Grzegorz Kolodziejczyk; +Cc: linux-bluetooth
In-Reply-To: <1377522441-28671-5-git-send-email-grzegorz.kolodziejczyk@tieto.com>
Hello Grzegorz,
On 26/08/2013 15:07, Grzegorz Kolodziejczyk wrote:
<snip>
> diff --git a/src/adapter.c b/src/adapter.c
> index 8e897ce..8a5398f 100644
> --- a/src/adapter.c
> +++ b/src/adapter.c
> @@ -6338,6 +6338,46 @@ int adapter_add_record_to_server(const bdaddr_t *device, sdp_record_t *rec)
> return chck;
> }
>
> +int adapter_remove_record_from_server(uint32_t handle)
> +{
> + int chck;
> + sdp_list_t *p;
> + sdp_record_t *r;
> + sdp_access_t *a;
> + sdp_record_t rh, ah;
> +
> + chck = remove_record_from_server(handle);
> +
> + if (chck == 0) {
> + // record_locate section
Please use C89 coding style for comments.
> + rh.handle = handle;
> + p = sdp_list_find(sdp_get_record_list(), &rh, record_sort);
> +
> + r = p->data;
> +
> + // access_locate section
idem here
> + ah.handle = handle;
> + p = sdp_list_find(sdp_get_access_list(), &ah, record_sort);
> +
> + if (p == NULL || p->data == NULL)
> + return 0;
> +
> + a = p->data;
> +
> + // remove from adapter services
and here
> + if (bacmp(&a->device, BDADDR_ANY) != 0) {
> + struct btd_adapter *adapter = adapter_find(&a->device);
> + if (adapter)
> + adapter_service_remove(adapter, r);
> + } else
> + adapter_foreach(adapter_service_remove, r);
> +
> + free(a);
> + }
> +
> + return chck;
> +}
> +
Regards
Fred
--
Frederic Danis Open Source Technology Center
frederic.danis@intel.com Intel Corporation
^ permalink raw reply
* How to test/debug bluetooth advertising [with hciconfig]?
From: Richard Neill @ 2013-08-28 10:22 UTC (permalink / raw)
To: linux-bluetooth
Dear All,
I'm trying to get "hello world" working from the shell between 2
Bluetooth 4 dongles, in LE advertising mode. I wonder whether I could
ask for some pointers?
As I understand it, Bluetooth low-energy supports up to 31 bytes of
arbitrary text in the advertising packet. So it should be possible to
set one dongle up in LE-advertising mode, and a second in LE-scan mode,
and to transfer the "hello world" message from one to the other.
I'm rather stuck at the moment. I've discovered that there is an "LE Set
Advertising Data" command, 0xcf (from "hciconfig hci0 commands_list"),
and that "hcitool -i hci0 cmd 0xcf XXX YYY ZZZ" is how to send it. But I
can't find any more documentation for the command set used by hcitool.
Can anyone help, with the commands I need, or by pointing me to the
appropriate documentation/tutorial?
Thanks for your help,
Richard
^ permalink raw reply
* Re: How to test/debug bluetooth advertising [with hciconfig]?
From: Andrei Emeltchenko @ 2013-08-28 10:39 UTC (permalink / raw)
To: Richard Neill; +Cc: linux-bluetooth
In-Reply-To: <521DCF49.5020807@richardneill.org>
Hi Richard,
On Wed, Aug 28, 2013 at 11:22:01AM +0100, Richard Neill wrote:
> Dear All,
>
> I'm trying to get "hello world" working from the shell between 2
> Bluetooth 4 dongles, in LE advertising mode. I wonder whether I
> could ask for some pointers?
>
> As I understand it, Bluetooth low-energy supports up to 31 bytes of
> arbitrary text in the advertising packet. So it should be possible
> to set one dongle up in LE-advertising mode, and a second in LE-scan
> mode, and to transfer the "hello world" message from one to the
> other.
>
> I'm rather stuck at the moment. I've discovered that there is an "LE
> Set Advertising Data" command, 0xcf (from "hciconfig hci0
> commands_list"), and that "hcitool -i hci0 cmd 0xcf XXX YYY ZZZ" is
$ hcitool cmd --help
Usage:
cmd <ogf> <ocf> [parameters]
Check ogf and ocf from the Bluetooth Specification.
Best regards
Andrei Emeltchenko
^ permalink raw reply
* Re: How to test/debug bluetooth advertising [with hciconfig]?
From: Richard Neill @ 2013-08-28 11:09 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <20130828103920.GD15469@aemeltch-MOBL1>
> On Wed, Aug 28, 2013 at 11:22:01AM +0100, Richard Neill wrote:
>> Dear All,
>>
>> I'm trying to get "hello world" working from the shell between 2
>> Bluetooth 4 dongles, in LE advertising mode. I wonder whether I
>> could ask for some pointers?
>
> Check ogf and ocf from the Bluetooth Specification.
Dear Andrei,
Thanks for your reply. That was actually very helpful: by searching
specifically for "ogf ocf Bluetooth specification", I was able to locate
the right part of the relevant document (a 600 page PDF of Bluetooth 4.0
core).
However, before I delve into this in great detail, I wonder whether I am
about to "re-invent the wheel". For example, if I wanted to use "ping"
in a shell-script, I wouldn't normally begin by poking registers in the
ethernet card.
If nobody else has written the utility, I'm happy to do so, (and I'll
publish the results), but I wonder whether it has already been done?
Best wishes,
Richard
P.S. I sent a patch to hciconfig yesterday: was it sufficient to just
post it to this list, or is there anything else I should do in order to
get feedback and to have it considered for acceptance into bluez ?
^ permalink raw reply
* Re: How to test/debug bluetooth advertising [with hciconfig]?
From: Richard Neill @ 2013-08-28 11:10 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <20130828103920.GD15469@aemeltch-MOBL1>
> On Wed, Aug 28, 2013 at 11:22:01AM +0100, Richard Neill wrote:
>> Dear All,
>>
>> I'm trying to get "hello world" working from the shell between 2
>> Bluetooth 4 dongles, in LE advertising mode. I wonder whether I
>> could ask for some pointers?
>
> Check ogf and ocf from the Bluetooth Specification.
Dear Andrei,
Thanks for your reply. That was actually very helpful: by searching
specifically for "ogf ocf Bluetooth specification", I was able to locate
the right part of the relevant document (a 600 page PDF of Bluetooth 4.0
core).
However, before I delve into this in great detail, I wonder whether I am
about to "re-invent the wheel". For example, if I wanted to use "ping"
in a shell-script, I wouldn't normally begin by poking registers in the
ethernet card.
If nobody else has written the utility, I'm happy to do so, (and I'll
publish the results), but I wonder whether it has already been done?
Best wishes,
Richard
P.S. I sent a patch to hciconfig yesterday: was it sufficient to just
post it to this list, or is there anything else I should do in order to
get feedback and to have it considered for acceptance into bluez ?
^ permalink raw reply
* Re: [PATCH v5 0/6] rfcomm: Implement rfcomm as a proper tty_port
From: Alexander Holler @ 2013-08-28 11:24 UTC (permalink / raw)
To: Peter Hurley
Cc: gustavo, Gianluca Anzolin, marcel, linux-bluetooth, gregkh,
jslaby
In-Reply-To: <521CE6E1.2020206@hurleysoftware.com>
Am 27.08.2013 19:50, schrieb Peter Hurley:
> On 08/27/2013 09:57 AM, Alexander Holler wrote:
>> Am 19.08.2013 22:20, schrieb Peter Hurley:
>>> On 07/31/2013 01:50 PM, Peter Hurley wrote:
>>
>>>> I reviewed these changes and retested. All ok.
>>>
>>> Gustavo,
>>>
>>> This series fixes a crashing regression from 3.10+ forward.
>>> Why is this not in linux-next yet?
>>
>> 3.8+ to be exact, just in case someone has to deal with older kernels.
>
> Thanks for the reminder.
>
> This series is too extensive to consider for -stable. Any ideas for
> how to address this crash for -stable?
Besides the BUG_ON() to prevent that something really bad happens, no.
Btw, I've just tested the series with 3.10.9, seems to work.
Thanks a lot,
Alexander Holler
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox