From: Andrei Emeltchenko <[email protected]>
This is initial code implementing socket HAL. OPP currently works with send/receive files. Probaly
other profiles works as well, not tested yet.
Changes:
* v3: Fixed coding style with write/send between file descriptors.
* v2: Following Marcel comments changed way copying between file descriptors works, added SDP record
for OPP and now it is possible to send files through GUI. Merged one patch with structures with actual user.
* v1: Rebased and use static src address, hal_fd removed from structure and closed after sent to framework,
added connect calls and SDP parsing, added cleanup_rfcomm function, minor fixes.
* RFC Initial
TODO:
* Add SDP record for PBAP and other profiles
* Use splice() (requires bionic change first)
For tracking rfcomm sockets I use structure rfslot which has following
fields:
- real_sock - real RFCOMM socket
- fd - fd to communicate with Android framework
create_rfslot sets hal_fd which is fd passed to Android framework with CMSG
Andrei Emeltchenko (16):
android/hal-sock: Add debug flag printing
android/socket: Use static local adapter address
android/socket: Add connect signal to socket
android/socket: Define structs and implement listen
android/socket: Implement socket accepted event
android/socket: Implement Android RFCOMM stack events
android/socket: Implement RFCOMM events
android/socket: Implement accept signal over Android fd
android/socket: Write channel to Android fd
android/socket: Implement socket connect HAL method
android/socket: Parse SDP response and connect
android/socket: Implement HAL connect call
android/socket: Send RFCOMM channel to framework
android/socket: Send connect signal on connect
android/socket: Close file descriptor after sending
android/socket: Add SDP record for OPP profile
android/hal-msg.h | 2 +
android/hal-sock.c | 8 +-
android/socket.c | 605 +++++++++++++++++++++++++++++++++++++++++++++++++++-
android/socket.h | 7 +
4 files changed, 614 insertions(+), 8 deletions(-)
--
1.7.10.4
Hi Johan,
On Mon, Nov 18, 2013 at 10:55:35AM +0200, Johan Hedberg wrote:
> Hi Andrei,
>
> On Mon, Nov 18, 2013, Andrei Emeltchenko wrote:
> > This defines structures for socket HAL. We need to emulate Android
> > sockets by sending connect/accept signals over file descriptor.
> > Handle HAL socket listen call. Create RFCOMM socket and wait for events.
> > ---
> > android/socket.c | 118 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
> > 1 file changed, 116 insertions(+), 2 deletions(-)
> >
> > diff --git a/android/socket.c b/android/socket.c
> > index e580036..276c78c 100644
> > --- a/android/socket.c
> > +++ b/android/socket.c
> > @@ -27,8 +27,12 @@
> >
> > #include <glib.h>
> > #include <stdbool.h>
> > +#include <unistd.h>
> > +#include <errno.h>
> >
> > #include "lib/bluetooth.h"
> > +#include "btio/btio.h"
> > +#include "lib/sdp.h"
> > #include "log.h"
> > #include "hal-msg.h"
> > #include "hal-ipc.h"
> > @@ -37,13 +41,123 @@
> >
> > static bdaddr_t adapter_addr;
> >
> > -static int handle_listen(void *buf)
> > +/* Simple list of RFCOMM server sockets */
> > +GList *rfcomm_srv_list = NULL;
> > +
> > +/* Simple list of RFCOMM connected sockets */
> > +GList *rfcomm_connected_list = NULL;
>
> Didn't I suggest "servers" and "connections" as names for these?
>
> > +struct rfcomm_slot {
>
> The term "slot" seems a bit confusing to me. You're using this struct as
> context for both server sockets and connected sockets. Would the name
> struct rfcomm_socket make more sense? Feel free to suggest something
> else too.
rfcomm_socket is ok, what is a good name instead of rfslot? rfsock?
Best regards
Andrei Emeltchenko
Hi,
On Mon, Nov 18, 2013 at 10:26:19AM +0200, Andrei Emeltchenko wrote:
> From: Andrei Emeltchenko <[email protected]>
>
> This adds SDP record for OPP shown below:
>
> Service Name: OBEX Object Push
> Service RecHandle: 0x10002
> Service Class ID List:
> "OBEX Object Push" (0x1105)
> Protocol Descriptor List:
> "RFCOMM" (0x0003)
> Channel: 9
> "OBEX" (0x0008)
> Profile Descriptor List:
> "OBEX Object Push" (0x1105)
> Version: 0x0100
> ---
> android/socket.c | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 94 insertions(+)
>
> diff --git a/android/socket.c b/android/socket.c
> index 6b50014..989b876 100644
> --- a/android/socket.c
> +++ b/android/socket.c
> @@ -36,6 +36,7 @@
> #include "lib/sdp_lib.h"
> #include "src/sdp-client.h"
>
> +#include "bluetooth.h"
> #include "log.h"
> #include "hal-msg.h"
> #include "hal-ipc.h"
> @@ -43,6 +44,9 @@
> #include "utils.h"
> #include "socket.h"
>
> +/* Use Object Transfer for all services */
> +#define SVC_HINT_OBEX 0x10
> +
> static bdaddr_t adapter_addr;
>
> /* Simple list of RFCOMM server sockets */
> @@ -56,6 +60,7 @@ struct rfcomm_slot {
> int real_sock; /* real RFCOMM socket */
> int channel; /* RFCOMM channel */
> bdaddr_t dst;
> + uint32_t service_handle;
> };
>
> static struct rfcomm_slot *create_rfslot(int sock, int *hal_fd)
> @@ -86,6 +91,9 @@ static void cleanup_rfslot(struct rfcomm_slot *rfslot)
> if (rfslot->real_sock > 0)
> close(rfslot->real_sock);
>
> + if (rfslot->service_handle)
> + bt_adapter_remove_record(rfslot->service_handle);
> +
> g_free(rfslot);
> }
>
> @@ -108,6 +116,90 @@ static struct {
> { {0} }
> };
>
> +static sdp_record_t *create_opp_record(uint8_t chan)
> +{
> + sdp_list_t *svclass_id, *pfseq, *apseq, *root;
> + uuid_t root_uuid, opush_uuid, rfcomm_uuid, obex_uuid;
> + sdp_profile_desc_t profile[1];
> + sdp_list_t *aproto, *proto[2];
> + uint8_t formats[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0xff };
> + void *dtds[sizeof(formats)], *values[sizeof(formats)];
> + unsigned int i;
> + uint8_t dtd = SDP_UINT8;
> + sdp_data_t *sflist;
> + sdp_data_t *channel;
> + sdp_record_t *record;
> +
> + record = sdp_record_alloc();
> + if (!record)
> + return NULL;
> +
> + sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
> + root = sdp_list_append(0, &root_uuid);
> + sdp_set_browse_groups(record, root);
> +
> + sdp_uuid16_create(&opush_uuid, OBEX_OBJPUSH_SVCLASS_ID);
> + svclass_id = sdp_list_append(0, &opush_uuid);
> + sdp_set_service_classes(record, svclass_id);
> +
> + sdp_uuid16_create(&profile[0].uuid, OBEX_OBJPUSH_PROFILE_ID);
> + profile[0].version = 0x0100;
> + pfseq = sdp_list_append(0, profile);
> + sdp_set_profile_descs(record, pfseq);
> +
> + sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
> + proto[0] = sdp_list_append(0, &rfcomm_uuid);
> + channel = sdp_data_alloc(SDP_UINT8, &chan);
> + proto[0] = sdp_list_append(proto[0], channel);
> + apseq = sdp_list_append(apseq, proto[0]);
fixed myself to:
@@ -161,7 +161,7 @@ static sdp_record_t *create_opp_record(uint8_t chan)
proto[0] = sdp_list_append(0, &rfcomm_uuid);
channel = sdp_data_alloc(SDP_UINT8, &chan);
proto[0] = sdp_list_append(proto[0], channel);
- apseq = sdp_list_append(apseq, proto[0]);
+ apseq = sdp_list_append(0, proto[0]);
sdp_uuid16_create(&obex_uuid, OBEX_UUID);
proto[1] = sdp_list_append(0, &obex_uuid);
Best regards
Andrei Emeltchenko
Hi Andrei,
On Mon, Nov 18, 2013, Andrei Emeltchenko wrote:
> This defines structures for socket HAL. We need to emulate Android
> sockets by sending connect/accept signals over file descriptor.
> Handle HAL socket listen call. Create RFCOMM socket and wait for events.
> ---
> android/socket.c | 118 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 116 insertions(+), 2 deletions(-)
>
> diff --git a/android/socket.c b/android/socket.c
> index e580036..276c78c 100644
> --- a/android/socket.c
> +++ b/android/socket.c
> @@ -27,8 +27,12 @@
>
> #include <glib.h>
> #include <stdbool.h>
> +#include <unistd.h>
> +#include <errno.h>
>
> #include "lib/bluetooth.h"
> +#include "btio/btio.h"
> +#include "lib/sdp.h"
> #include "log.h"
> #include "hal-msg.h"
> #include "hal-ipc.h"
> @@ -37,13 +41,123 @@
>
> static bdaddr_t adapter_addr;
>
> -static int handle_listen(void *buf)
> +/* Simple list of RFCOMM server sockets */
> +GList *rfcomm_srv_list = NULL;
> +
> +/* Simple list of RFCOMM connected sockets */
> +GList *rfcomm_connected_list = NULL;
Didn't I suggest "servers" and "connections" as names for these?
> +struct rfcomm_slot {
The term "slot" seems a bit confusing to me. You're using this struct as
context for both server sockets and connected sockets. Would the name
struct rfcomm_socket make more sense? Feel free to suggest something
else too.
> +
> + rfslot = g_malloc0(sizeof(*rfslot));
rfslot = g_new0(struct rfcom_slot, 1);
> +static void cleanup_rfslot(struct rfcomm_slot *rfslot)
> +{
> + DBG("Cleanup: rfslot: %p fd %d real_sock %d chan %u",
> + rfslot, rfslot->fd, rfslot->real_sock, rfslot->channel);
Since DBG() will anyway print the function name having an explicit
"Cleanup:" in the log message seems redundant.
> +static struct {
> + uint8_t uuid[16];
> + uint8_t channel;
> +} uuid_to_chan[] = {
> + { .uuid = {
> + 0x00, 0x00, 0x11, 0x2F, 0x00, 0x00, 0x10, 0x00,
> + 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB
> + },
Why doesn't this first entry have a channel? Please indent the stuff
between { and } with an extra tab.
> +#define PBAP_DEFAULT_CHANNEL 15
I'd rather have these default channel definitions in a separate place,
probably in the beginning of the C-file where you can quickly see them.
Having them inline like this doesn't really provide any value at all
compared to just hard-coding them in the places where you have
.channel = ...
> + { .uuid = {
> + 0x00, 0x00, 0x11, 0x05, 0x00, 0x00, 0x10, 0x00,
> + 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB
> + },
> +#define OPP_DEFAULT_CHANNEL 9
> + .channel = OPP_DEFAULT_CHANNEL },
Is this the missing channel from the first entry? Looks misplaced to me.
> +static int get_rfcomm_default_chan(const uint8_t *uuid)
> +{
> + int i;
> +
> + for (i = 0; uuid_to_chan[i].channel; i++) {
> + if (!memcmp(uuid_to_chan[i].uuid, uuid, 16))
> + return uuid_to_chan[i].channel;
> + }
>
> return -1;
Maybe return -ENOENT?
> +static void accept_cb(GIOChannel *io, GError *err, gpointer user_data)
> +{
> +}
> +
> +static int handle_listen(void *buf)
> +{
> + struct hal_cmd_sock_listen *cmd = buf;
> + struct rfcomm_slot *rfslot;
> + GIOChannel *io;
> + GError *err = NULL;
> + int hal_fd = -1;
Why do you need to pre-initialize hal_fd? If create_rfslot() can fail
can won't you detect that?
> + int chan;
> +
> + DBG("");
> +
> + chan = get_rfcomm_default_chan(cmd->uuid);
> + if (chan < 0)
> + return -1;
Maybe "return chan;"?
Johan
Hi Andrei,
On Mon, Nov 18, 2013, Andrei Emeltchenko wrote:
> ---
> android/socket.c | 3 +++
> 1 file changed, 3 insertions(+)
The first two patches have been applied.
Johan
Hi Andrei,
On Mon, Nov 18, 2013, Andrei Emeltchenko wrote:
> Connect signal is used to pass information to framework that socket
> is accepted.
> ---
> android/hal-msg.h | 2 ++
> android/socket.h | 7 +++++++
> 2 files changed, 9 insertions(+)
>
> diff --git a/android/hal-msg.h b/android/hal-msg.h
> index 44fd5c8..9e3a81f 100644
> --- a/android/hal-msg.h
> +++ b/android/hal-msg.h
> @@ -232,6 +232,8 @@ struct hal_cmd_sock_connect {
> uint8_t flags;
> } __attribute__((packed));
>
> +/* Bluetooth Hidhost HAL api */
> +
This part seems unrelated to this patch?
Johan
From: Andrei Emeltchenko <[email protected]>
This adds SDP record for OPP shown below:
Service Name: OBEX Object Push
Service RecHandle: 0x10002
Service Class ID List:
"OBEX Object Push" (0x1105)
Protocol Descriptor List:
"RFCOMM" (0x0003)
Channel: 9
"OBEX" (0x0008)
Profile Descriptor List:
"OBEX Object Push" (0x1105)
Version: 0x0100
---
android/socket.c | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 94 insertions(+)
diff --git a/android/socket.c b/android/socket.c
index 6b50014..989b876 100644
--- a/android/socket.c
+++ b/android/socket.c
@@ -36,6 +36,7 @@
#include "lib/sdp_lib.h"
#include "src/sdp-client.h"
+#include "bluetooth.h"
#include "log.h"
#include "hal-msg.h"
#include "hal-ipc.h"
@@ -43,6 +44,9 @@
#include "utils.h"
#include "socket.h"
+/* Use Object Transfer for all services */
+#define SVC_HINT_OBEX 0x10
+
static bdaddr_t adapter_addr;
/* Simple list of RFCOMM server sockets */
@@ -56,6 +60,7 @@ struct rfcomm_slot {
int real_sock; /* real RFCOMM socket */
int channel; /* RFCOMM channel */
bdaddr_t dst;
+ uint32_t service_handle;
};
static struct rfcomm_slot *create_rfslot(int sock, int *hal_fd)
@@ -86,6 +91,9 @@ static void cleanup_rfslot(struct rfcomm_slot *rfslot)
if (rfslot->real_sock > 0)
close(rfslot->real_sock);
+ if (rfslot->service_handle)
+ bt_adapter_remove_record(rfslot->service_handle);
+
g_free(rfslot);
}
@@ -108,6 +116,90 @@ static struct {
{ {0} }
};
+static sdp_record_t *create_opp_record(uint8_t chan)
+{
+ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+ uuid_t root_uuid, opush_uuid, rfcomm_uuid, obex_uuid;
+ sdp_profile_desc_t profile[1];
+ sdp_list_t *aproto, *proto[2];
+ uint8_t formats[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0xff };
+ void *dtds[sizeof(formats)], *values[sizeof(formats)];
+ unsigned int i;
+ uint8_t dtd = SDP_UINT8;
+ sdp_data_t *sflist;
+ sdp_data_t *channel;
+ sdp_record_t *record;
+
+ record = sdp_record_alloc();
+ if (!record)
+ return NULL;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(0, &root_uuid);
+ sdp_set_browse_groups(record, root);
+
+ sdp_uuid16_create(&opush_uuid, OBEX_OBJPUSH_SVCLASS_ID);
+ svclass_id = sdp_list_append(0, &opush_uuid);
+ sdp_set_service_classes(record, svclass_id);
+
+ sdp_uuid16_create(&profile[0].uuid, OBEX_OBJPUSH_PROFILE_ID);
+ profile[0].version = 0x0100;
+ pfseq = sdp_list_append(0, profile);
+ sdp_set_profile_descs(record, pfseq);
+
+ sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+ proto[0] = sdp_list_append(0, &rfcomm_uuid);
+ channel = sdp_data_alloc(SDP_UINT8, &chan);
+ proto[0] = sdp_list_append(proto[0], channel);
+ apseq = sdp_list_append(apseq, proto[0]);
+
+ sdp_uuid16_create(&obex_uuid, OBEX_UUID);
+ proto[1] = sdp_list_append(0, &obex_uuid);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ aproto = sdp_list_append(0, apseq);
+ sdp_set_access_protos(record, aproto);
+
+ for (i = 0; i < sizeof(formats); i++) {
+ dtds[i] = &dtd;
+ values[i] = &formats[i];
+ }
+ sflist = sdp_seq_alloc(dtds, values, sizeof(formats));
+ sdp_attr_add(record, SDP_ATTR_SUPPORTED_FORMATS_LIST, sflist);
+
+ sdp_set_info_attr(record, "OBEX Object Push", 0, 0);
+
+ sdp_data_free(channel);
+ sdp_list_free(proto[0], 0);
+ sdp_list_free(proto[1], 0);
+ sdp_list_free(apseq, 0);
+ sdp_list_free(aproto, 0);
+
+ return record;
+}
+
+static uint32_t sdp_service_register(uint8_t chan)
+{
+ sdp_record_t *record;
+
+ switch (chan) {
+ case OPP_DEFAULT_CHANNEL:
+ record = create_opp_record(chan);
+ break;
+ default:
+ DBG("Not implemented");
+ return 0;
+ }
+
+ if (bt_adapter_add_record(record, SVC_HINT_OBEX) < 0) {
+ error("Failed to register on SDP record");
+ sdp_record_free(record);
+ return 0;
+ }
+
+ return record->handle;
+}
+
static int bt_sock_send_fd(int sock_fd, const void *buf, int len, int send_fd)
{
ssize_t ret;
@@ -371,6 +463,8 @@ static int handle_listen(void *buf)
return -1;
}
+ rfslot->service_handle = sdp_service_register(chan);
+
DBG("real_sock %d fd %d hal_fd %d",
rfslot->real_sock, rfslot->fd, hal_fd);
--
1.7.10.4
From: Andrei Emeltchenko <[email protected]>
Framework expects channel to be send.
---
android/socket.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/android/socket.c b/android/socket.c
index 063681e..39935e8 100644
--- a/android/socket.c
+++ b/android/socket.c
@@ -477,6 +477,11 @@ static void sdp_search_cb(sdp_list_t *recs, int err, gpointer data)
goto fail;
}
+ if (write(rfslot->fd, &chan, sizeof(chan)) != sizeof(chan)) {
+ error("Error sending RFCOMM channel");
+ goto fail;
+ }
+
rfslot->real_sock = g_io_channel_unix_get_fd(io);
rfslot->channel = chan;
rfcomm_connected_list = g_list_append(rfcomm_connected_list, rfslot);
--
1.7.10.4
From: Andrei Emeltchenko <[email protected]>
---
android/socket.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/android/socket.c b/android/socket.c
index 1bcaa23..6b50014 100644
--- a/android/socket.c
+++ b/android/socket.c
@@ -560,6 +560,7 @@ void bt_sock_handle_cmd(int sk, uint8_t opcode, void *buf, uint16_t len)
break;
ipc_send(sk, HAL_SERVICE_ID_SOCK, opcode, 0, NULL, fd);
+ close(fd);
return;
case HAL_OP_SOCK_CONNECT:
fd = handle_connect(buf);
@@ -567,6 +568,7 @@ void bt_sock_handle_cmd(int sk, uint8_t opcode, void *buf, uint16_t len)
break;
ipc_send(sk, HAL_SERVICE_ID_SOCK, opcode, 0, NULL, fd);
+ close(fd);
return;
default:
DBG("Unhandled command, opcode 0x%x", opcode);
--
1.7.10.4
From: Andrei Emeltchenko <[email protected]>
Android framework expects connect signal to be sent when
remote device is connected.
---
android/socket.c | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/android/socket.c b/android/socket.c
index 39935e8..1bcaa23 100644
--- a/android/socket.c
+++ b/android/socket.c
@@ -377,6 +377,33 @@ static int handle_listen(void *buf)
return hal_fd;
}
+static ssize_t sock_send_connect(struct rfcomm_slot *rfslot, bdaddr_t *bdaddr)
+{
+ struct hal_sock_connect_signal cmd;
+ ssize_t len;
+
+ DBG("");
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.size = sizeof(cmd);
+ bdaddr2android(bdaddr, cmd.bdaddr);
+ cmd.channel = rfslot->channel;
+ cmd.status = 0;
+
+ len = write(rfslot->fd, &cmd, sizeof(cmd));
+ if (len < 0) {
+ error("%s", strerror(errno));
+ return len;
+ }
+
+ if (len != (ssize_t) sizeof(cmd)) {
+ error("Error sending connect signal");
+ return -1;
+ }
+
+ return len;
+}
+
static void connect_cb(GIOChannel *io, GError *conn_err, gpointer user_data)
{
struct rfcomm_slot *rfslot = user_data;
@@ -407,6 +434,9 @@ static void connect_cb(GIOChannel *io, GError *conn_err, gpointer user_data)
rfslot->fd, rfslot->real_sock, rfslot->channel,
g_io_channel_unix_get_fd(io));
+ if (sock_send_connect(rfslot, &dst) < 0)
+ goto fail;
+
/* Handle events from Android */
io_stack = g_io_channel_unix_new(rfslot->fd);
g_io_add_watch(io_stack, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
--
1.7.10.4
From: Andrei Emeltchenko <[email protected]>
Parse SDP response, find RFCOMM channel and connect.
---
android/socket.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 63 insertions(+)
diff --git a/android/socket.c b/android/socket.c
index c22e4ed..dde000d 100644
--- a/android/socket.c
+++ b/android/socket.c
@@ -377,9 +377,72 @@ static int handle_listen(void *buf)
return hal_fd;
}
+static void connect_cb(GIOChannel *io, GError *err, gpointer user_data)
+{
+}
+
static void sdp_search_cb(sdp_list_t *recs, int err, gpointer data)
{
+ struct rfcomm_slot *rfslot = data;
+ GError *gerr = NULL;
+ sdp_list_t *list;
+ GIOChannel *io;
+ int chan;
+
DBG("");
+
+ if (err < 0) {
+ error("Unable to get SDP record: %s", strerror(-err));
+ goto fail;
+ }
+
+ if (!recs || !recs->data) {
+ error("No SDP records found");
+ goto fail;
+ }
+
+ for (list = recs; list != NULL; list = list->next) {
+ sdp_record_t *rec = list->data;
+ sdp_list_t *protos;
+
+ if (sdp_get_access_protos(rec, &protos) < 0) {
+ error("Unable to get proto list");
+ goto fail;
+ }
+
+ chan = sdp_get_proto_port(protos, RFCOMM_UUID);
+
+ sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free,
+ NULL);
+ sdp_list_free(protos, NULL);
+ }
+
+ if (chan <= 0) {
+ error("Could not get RFCOMM channel %d", chan);
+ goto fail;
+ }
+
+ DBG("Got RFCOMM channel %d", chan);
+
+ io = bt_io_connect(connect_cb, rfslot, NULL, &gerr,
+ BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+ BT_IO_OPT_DEST_BDADDR, &rfslot->dst,
+ BT_IO_OPT_CHANNEL, chan,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+ BT_IO_OPT_INVALID);
+ if (!io) {
+ error("Failed connect: %s", gerr->message);
+ g_error_free(gerr);
+ goto fail;
+ }
+
+ rfslot->real_sock = g_io_channel_unix_get_fd(io);
+ rfslot->channel = chan;
+ rfcomm_connected_list = g_list_append(rfcomm_connected_list, rfslot);
+ return;
+
+fail:
+ cleanup_rfslot(rfslot);
}
static int handle_connect(void *buf)
--
1.7.10.4
From: Andrei Emeltchenko <[email protected]>
HAL connect uses similar event handlers like listen call.
---
android/socket.c | 43 ++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 42 insertions(+), 1 deletion(-)
diff --git a/android/socket.c b/android/socket.c
index dde000d..063681e 100644
--- a/android/socket.c
+++ b/android/socket.c
@@ -377,8 +377,49 @@ static int handle_listen(void *buf)
return hal_fd;
}
-static void connect_cb(GIOChannel *io, GError *err, gpointer user_data)
+static void connect_cb(GIOChannel *io, GError *conn_err, gpointer user_data)
{
+ struct rfcomm_slot *rfslot = user_data;
+ GIOChannel *io_stack;
+ GError *io_err = NULL;
+ bdaddr_t dst;
+ char address[18];
+ int chan = -1;
+
+ bt_io_get(io, &io_err,
+ BT_IO_OPT_DEST_BDADDR, &dst,
+ BT_IO_OPT_INVALID);
+ if (io_err) {
+ error("%s", io_err->message);
+ g_error_free(io_err);
+ goto fail;
+ }
+
+ if (conn_err) {
+ error("%s", conn_err->message);
+ goto fail;
+ }
+
+ ba2str(&dst, address);
+ DBG("Connected to %s rfslot %p chan %d", address, rfslot, chan);
+
+ DBG("rfslot: fd %d real_sock %d chan %u sock %d",
+ rfslot->fd, rfslot->real_sock, rfslot->channel,
+ g_io_channel_unix_get_fd(io));
+
+ /* Handle events from Android */
+ io_stack = g_io_channel_unix_new(rfslot->fd);
+ g_io_add_watch(io_stack, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ sock_stack_event_cb, rfslot);
+ g_io_channel_unref(io_stack);
+
+ /* Handle rfcomm events */
+ g_io_add_watch(io, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+ sock_rfcomm_event_cb, rfslot);
+
+ return;
+fail:
+ cleanup_rfslot(rfslot);
}
static void sdp_search_cb(sdp_list_t *recs, int err, gpointer data)
--
1.7.10.4
From: Andrei Emeltchenko <[email protected]>
Android expects to get accept signal over file descriptor which was
set during listen HAL call.
---
android/socket.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 57 insertions(+)
diff --git a/android/socket.c b/android/socket.c
index eba50f6..4886158 100644
--- a/android/socket.c
+++ b/android/socket.c
@@ -37,6 +37,7 @@
#include "hal-msg.h"
#include "hal-ipc.h"
#include "ipc.h"
+#include "utils.h"
#include "socket.h"
static bdaddr_t adapter_addr;
@@ -103,6 +104,45 @@ static struct {
{ {0} }
};
+static int bt_sock_send_fd(int sock_fd, const void *buf, int len, int send_fd)
+{
+ ssize_t ret;
+ struct msghdr msg;
+ struct cmsghdr *cmsg;
+ struct iovec iv;
+ char msgbuf[CMSG_SPACE(1)];
+
+ DBG("len %d sock_fd %d send_fd %d", len, sock_fd, send_fd);
+
+ if (sock_fd == -1 || send_fd == -1)
+ return -1;
+
+ memset(&msg, 0, sizeof(msg));
+
+ msg.msg_control = msgbuf;
+ msg.msg_controllen = sizeof(msgbuf);
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(send_fd));
+ memcpy(CMSG_DATA(cmsg), &send_fd, sizeof(send_fd));
+
+ iv.iov_base = (unsigned char *) buf;
+ iv.iov_len = len;
+
+ msg.msg_iov = &iv;
+ msg.msg_iovlen = 1;
+
+ ret = sendmsg(sock_fd, &msg, MSG_NOSIGNAL);
+ if (ret < 0) {
+ error("sendmsg(): sock_fd %d send_fd %d: %s",
+ sock_fd, send_fd, strerror(errno));
+ return ret;
+ }
+
+ return ret;
+}
+
static int get_rfcomm_default_chan(const uint8_t *uuid)
{
int i;
@@ -226,6 +266,21 @@ static gboolean sock_rfcomm_event_cb(GIOChannel *io, GIOCondition cond,
return TRUE;
}
+static void sock_send_accept(struct rfcomm_slot *rfslot, bdaddr_t *bdaddr,
+ int fd_accepted)
+{
+ struct hal_sock_connect_signal cmd;
+
+ DBG("");
+
+ cmd.size = sizeof(cmd);
+ bdaddr2android(bdaddr, cmd.bdaddr);
+ cmd.channel = rfslot->channel;
+ cmd.status = 0;
+
+ bt_sock_send_fd(rfslot->fd, &cmd, sizeof(cmd), fd_accepted);
+}
+
static void accept_cb(GIOChannel *io, GError *err, gpointer user_data)
{
struct rfcomm_slot *rfslot = user_data;
@@ -257,6 +312,8 @@ static void accept_cb(GIOChannel *io, GError *err, gpointer user_data)
rfcomm_connected_list =
g_list_append(rfcomm_connected_list, rfslot_acc);
+ sock_send_accept(rfslot, &dst, hal_fd);
+
/* Handle events from Android */
io_stack = g_io_channel_unix_new(rfslot_acc->fd);
g_io_add_watch(io_stack, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
--
1.7.10.4
From: Andrei Emeltchenko <[email protected]>
When we get accepted event we create rfcomm slot and start listening
for events from Android framework and from RFCOMM real socket.
---
android/socket.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 52 insertions(+)
diff --git a/android/socket.c b/android/socket.c
index 276c78c..c9ee32f 100644
--- a/android/socket.c
+++ b/android/socket.c
@@ -115,8 +115,60 @@ static int get_rfcomm_default_chan(const uint8_t *uuid)
return -1;
}
+static gboolean sock_stack_event_cb(GIOChannel *io, GIOCondition cond,
+ gpointer data)
+{
+ return TRUE;
+}
+
+static gboolean sock_rfcomm_event_cb(GIOChannel *io, GIOCondition cond,
+ gpointer data)
+{
+ return TRUE;
+}
+
static void accept_cb(GIOChannel *io, GError *err, gpointer user_data)
{
+ struct rfcomm_slot *rfslot = user_data;
+ struct rfcomm_slot *rfslot_acc;
+ GIOChannel *io_stack;
+ bdaddr_t dst;
+ char address[18];
+ int sock_acc;
+ int hal_fd = -1;
+
+ bt_io_get(io, &err,
+ BT_IO_OPT_DEST_BDADDR, &dst,
+ BT_IO_OPT_INVALID);
+ if (err) {
+ error("%s", err->message);
+ g_io_channel_shutdown(io, TRUE, NULL);
+ return;
+ }
+
+ ba2str(&dst, address);
+ DBG("Incoming connection from %s rfslot %p", address, rfslot);
+
+ DBG("rfslot: fd %d real_sock %d chan %u sock %d",
+ rfslot->fd, rfslot->real_sock, rfslot->channel,
+ g_io_channel_unix_get_fd(io));
+
+ sock_acc = g_io_channel_unix_get_fd(io);
+ rfslot_acc = create_rfslot(sock_acc, &hal_fd);
+ rfcomm_connected_list =
+ g_list_append(rfcomm_connected_list, rfslot_acc);
+
+ /* Handle events from Android */
+ io_stack = g_io_channel_unix_new(rfslot_acc->fd);
+ g_io_add_watch(io_stack, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ sock_stack_event_cb, rfslot_acc);
+ g_io_channel_unref(io_stack);
+
+ /* Handle rfcomm events */
+ g_io_add_watch(io, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+ sock_rfcomm_event_cb, rfslot_acc);
+
+ DBG("rfslot %p rfslot_acc %p", rfslot, rfslot_acc);
}
static int handle_listen(void *buf)
--
1.7.10.4
From: Andrei Emeltchenko <[email protected]>
Copy data from RFCOMM socket to Android framework. Consider splice
in the future.
---
android/socket.c | 38 ++++++++++++++++++++++++++++++++++++++
1 file changed, 38 insertions(+)
diff --git a/android/socket.c b/android/socket.c
index 027b519..eba50f6 100644
--- a/android/socket.c
+++ b/android/socket.c
@@ -185,6 +185,44 @@ static gboolean sock_stack_event_cb(GIOChannel *io, GIOCondition cond,
static gboolean sock_rfcomm_event_cb(GIOChannel *io, GIOCondition cond,
gpointer data)
{
+ struct rfcomm_slot *rfslot = data;
+ unsigned char buf[1024];
+ int len, sent;
+
+ DBG("rfslot: fd %d real_sock %d chan %u sock %d",
+ rfslot->fd, rfslot->real_sock, rfslot->channel,
+ g_io_channel_unix_get_fd(io));
+
+ if (!g_list_find(rfcomm_connected_list, rfslot)) {
+ error("rfslot %p not found in the list", rfslot);
+ return FALSE;
+ }
+
+ if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) {
+ error("Socket error: sock %d cond %d",
+ g_io_channel_unix_get_fd(io), cond);
+ rfcomm_connected_list = g_list_remove(rfcomm_connected_list,
+ rfslot);
+ cleanup_rfslot(rfslot);
+ return FALSE;
+ }
+
+ len = read(rfslot->real_sock, buf, sizeof(buf));
+ if (len <= 0) {
+ error("read(): %s", strerror(errno));
+ return FALSE;
+ }
+
+ DBG("read %d bytes, write to fd %d", len, rfslot->fd);
+
+ sent = try_write_all(rfslot->fd, buf, len);
+ if (sent < 0) {
+ error("write(): %s", strerror(errno));
+ return FALSE;
+ }
+
+ DBG("Written %d bytes", sent);
+
return TRUE;
}
--
1.7.10.4
From: Andrei Emeltchenko <[email protected]>
---
android/socket.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/android/socket.c b/android/socket.c
index c283c5f..e580036 100644
--- a/android/socket.c
+++ b/android/socket.c
@@ -35,6 +35,7 @@
#include "ipc.h"
#include "socket.h"
+static bdaddr_t adapter_addr;
static int handle_listen(void *buf)
{
@@ -81,6 +82,8 @@ bool bt_socket_register(int sk, const bdaddr_t *addr)
{
DBG("");
+ bacpy(&adapter_addr, addr);
+
return true;
}
--
1.7.10.4
From: Andrei Emeltchenko <[email protected]>
Android framework expects to receive channel number as int.
---
android/socket.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/android/socket.c b/android/socket.c
index 4886158..a800a77 100644
--- a/android/socket.c
+++ b/android/socket.c
@@ -360,6 +360,13 @@ static int handle_listen(void *buf)
rfslot->real_sock = g_io_channel_unix_get_fd(io);
rfcomm_srv_list = g_list_append(rfcomm_srv_list, rfslot);
+ /* TODO: Check this */
+ if (write(rfslot->fd, &chan, sizeof(chan)) != sizeof(chan)) {
+ error("Error sending RFCOMM channel");
+ cleanup_rfslot(rfslot);
+ return -1;
+ }
+
DBG("real_sock %d fd %d hal_fd %d",
rfslot->real_sock, rfslot->fd, hal_fd);
--
1.7.10.4
From: Andrei Emeltchenko <[email protected]>
---
android/hal-sock.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/android/hal-sock.c b/android/hal-sock.c
index b7bc88e..eafa451 100644
--- a/android/hal-sock.c
+++ b/android/hal-sock.c
@@ -55,8 +55,8 @@ static bt_status_t sock_listen(btsock_type_t type, const char *service_name,
return BT_STATUS_PARM_INVALID;
}
- DBG("uuid %s chan %d sock %p type %d service_name %s",
- btuuid2str(uuid), chan, sock, type, service_name);
+ DBG("uuid %s chan %d sock %p type %d service_name %s flags 0x%02x",
+ btuuid2str(uuid), chan, sock, type, service_name, flags);
switch (type) {
case BTSOCK_RFCOMM:
@@ -82,8 +82,8 @@ static bt_status_t sock_connect(const bt_bdaddr_t *bdaddr, btsock_type_t type,
return BT_STATUS_PARM_INVALID;
}
- DBG("uuid %s chan %d sock %p type %d", btuuid2str(uuid), chan, sock,
- type);
+ DBG("uuid %s chan %d sock %p type %d flags 0x%02x",
+ btuuid2str(uuid), chan, sock, type, flags);
if (type != BTSOCK_RFCOMM) {
error("Socket type %u not supported", type);
--
1.7.10.4
From: Andrei Emeltchenko <[email protected]>
Handle events from Android framework. Write everything to real RFCOMM
socket. Consider splice() in the future.
---
android/socket.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 61 insertions(+)
diff --git a/android/socket.c b/android/socket.c
index c9ee32f..027b519 100644
--- a/android/socket.c
+++ b/android/socket.c
@@ -115,9 +115,70 @@ static int get_rfcomm_default_chan(const uint8_t *uuid)
return -1;
}
+static int try_write_all(int fd, unsigned char *buf, int len)
+{
+ int sent = 0;
+
+ while (len > 0) {
+ int written;
+
+ written = write(fd, buf, len);
+ if (written < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ return -1;
+ }
+
+ if (!written)
+ return 0;
+
+ len -= written; buf += written; sent += written;
+ }
+
+ return sent;
+}
+
static gboolean sock_stack_event_cb(GIOChannel *io, GIOCondition cond,
gpointer data)
{
+ struct rfcomm_slot *rfslot = data;
+ unsigned char buf[1024];
+ int len, sent;
+
+ DBG("rfslot: fd %d real_sock %d chan %u sock %d",
+ rfslot->fd, rfslot->real_sock, rfslot->channel,
+ g_io_channel_unix_get_fd(io));
+
+ if (!g_list_find(rfcomm_connected_list, rfslot)) {
+ error("rfslot %p not found in the list", rfslot);
+ return FALSE;
+ }
+
+ if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) {
+ error("Socket error: sock %d cond %d",
+ g_io_channel_unix_get_fd(io), cond);
+ rfcomm_connected_list = g_list_remove(rfcomm_connected_list,
+ rfslot);
+ cleanup_rfslot(rfslot);
+ return FALSE;
+ }
+
+ len = read(rfslot->fd, buf, sizeof(buf));
+ if (len <= 0) {
+ error("read(): %s", strerror(errno));
+ return FALSE;
+ }
+
+ DBG("read %d bytes write to %d", len, rfslot->real_sock);
+
+ sent = try_write_all(rfslot->real_sock, buf, len);
+ if (sent < 0) {
+ error("write(): %s", strerror(errno));
+ return FALSE;
+ }
+
+ DBG("Written %d bytes", sent);
+
return TRUE;
}
--
1.7.10.4
From: Andrei Emeltchenko <[email protected]>
Connect signal is used to pass information to framework that socket
is accepted.
---
android/hal-msg.h | 2 ++
android/socket.h | 7 +++++++
2 files changed, 9 insertions(+)
diff --git a/android/hal-msg.h b/android/hal-msg.h
index 44fd5c8..9e3a81f 100644
--- a/android/hal-msg.h
+++ b/android/hal-msg.h
@@ -232,6 +232,8 @@ struct hal_cmd_sock_connect {
uint8_t flags;
} __attribute__((packed));
+/* Bluetooth Hidhost HAL api */
+
#define HAL_OP_HIDHOST_CONNECT 0x01
struct hal_cmd_hidhost_connect {
uint8_t bdaddr[6];
diff --git a/android/socket.h b/android/socket.h
index 7aa5574..ba56c9b 100644
--- a/android/socket.h
+++ b/android/socket.h
@@ -21,6 +21,13 @@
*
*/
+struct hal_sock_connect_signal {
+ short size;
+ uint8_t bdaddr[6];
+ int channel;
+ int status;
+} __attribute__((packed));
+
void bt_sock_handle_cmd(int sk, uint8_t opcode, void *buf, uint16_t len);
bool bt_socket_register(int sk, const bdaddr_t *addr);
--
1.7.10.4
From: Andrei Emeltchenko <[email protected]>
First step is to query remote device for RFCOMM channel.
---
android/socket.c | 34 ++++++++++++++++++++++++++++++++--
1 file changed, 32 insertions(+), 2 deletions(-)
diff --git a/android/socket.c b/android/socket.c
index a800a77..c22e4ed 100644
--- a/android/socket.c
+++ b/android/socket.c
@@ -33,6 +33,9 @@
#include "lib/bluetooth.h"
#include "btio/btio.h"
#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+#include "src/sdp-client.h"
+
#include "log.h"
#include "hal-msg.h"
#include "hal-ipc.h"
@@ -52,6 +55,7 @@ struct rfcomm_slot {
int fd; /* descriptor for communication with Java framework */
int real_sock; /* real RFCOMM socket */
int channel; /* RFCOMM channel */
+ bdaddr_t dst;
};
static struct rfcomm_slot *create_rfslot(int sock, int *hal_fd)
@@ -373,11 +377,37 @@ static int handle_listen(void *buf)
return hal_fd;
}
+static void sdp_search_cb(sdp_list_t *recs, int err, gpointer data)
+{
+ DBG("");
+}
+
static int handle_connect(void *buf)
{
- DBG("Not implemented");
+ struct hal_cmd_sock_connect *cmd = buf;
+ struct rfcomm_slot *rfslot;
+ bdaddr_t dst;
+ uuid_t uuid;
+ int hal_fd = -1;
- return -1;
+ DBG("");
+
+ android2bdaddr(cmd->bdaddr, &dst);
+ rfslot = create_rfslot(-1, &hal_fd);
+ bacpy(&rfslot->dst, &dst);
+
+ memset(&uuid, 0, sizeof(uuid));
+ uuid.type = SDP_UUID128;
+ memcpy(&uuid.value.uuid128, cmd->uuid, sizeof(uint128_t));
+
+ if (bt_search_service(&adapter_addr, &dst, &uuid, sdp_search_cb, rfslot,
+ NULL) < 0) {
+ error("Failed to search SDP records");
+ cleanup_rfslot(rfslot);
+ return -1;
+ }
+
+ return hal_fd;
}
void bt_sock_handle_cmd(int sk, uint8_t opcode, void *buf, uint16_t len)
--
1.7.10.4
From: Andrei Emeltchenko <[email protected]>
This defines structures for socket HAL. We need to emulate Android
sockets by sending connect/accept signals over file descriptor.
Handle HAL socket listen call. Create RFCOMM socket and wait for events.
---
android/socket.c | 118 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 116 insertions(+), 2 deletions(-)
diff --git a/android/socket.c b/android/socket.c
index e580036..276c78c 100644
--- a/android/socket.c
+++ b/android/socket.c
@@ -27,8 +27,12 @@
#include <glib.h>
#include <stdbool.h>
+#include <unistd.h>
+#include <errno.h>
#include "lib/bluetooth.h"
+#include "btio/btio.h"
+#include "lib/sdp.h"
#include "log.h"
#include "hal-msg.h"
#include "hal-ipc.h"
@@ -37,13 +41,123 @@
static bdaddr_t adapter_addr;
-static int handle_listen(void *buf)
+/* Simple list of RFCOMM server sockets */
+GList *rfcomm_srv_list = NULL;
+
+/* Simple list of RFCOMM connected sockets */
+GList *rfcomm_connected_list = NULL;
+
+struct rfcomm_slot {
+ int fd; /* descriptor for communication with Java framework */
+ int real_sock; /* real RFCOMM socket */
+ int channel; /* RFCOMM channel */
+};
+
+static struct rfcomm_slot *create_rfslot(int sock, int *hal_fd)
{
- DBG("Not implemented");
+ int fds[2] = {-1, -1};
+ struct rfcomm_slot *rfslot;
+
+ if (socketpair(AF_LOCAL, SOCK_STREAM, 0, fds) < 0) {
+ error("socketpair(): %s", strerror(errno));
+ return NULL;
+ }
+
+ rfslot = g_malloc0(sizeof(*rfslot));
+ rfslot->fd = fds[0];
+ *hal_fd = fds[1];
+ rfslot->real_sock = sock;
+
+ return rfslot;
+}
+
+static void cleanup_rfslot(struct rfcomm_slot *rfslot)
+{
+ DBG("Cleanup: rfslot: %p fd %d real_sock %d chan %u",
+ rfslot, rfslot->fd, rfslot->real_sock, rfslot->channel);
+
+ if (rfslot->fd > 0)
+ close(rfslot->fd);
+ if (rfslot->real_sock > 0)
+ close(rfslot->real_sock);
+
+ g_free(rfslot);
+}
+
+static struct {
+ uint8_t uuid[16];
+ uint8_t channel;
+} uuid_to_chan[] = {
+ { .uuid = {
+ 0x00, 0x00, 0x11, 0x2F, 0x00, 0x00, 0x10, 0x00,
+ 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB
+ },
+#define PBAP_DEFAULT_CHANNEL 15
+ .channel = PBAP_DEFAULT_CHANNEL },
+ { .uuid = {
+ 0x00, 0x00, 0x11, 0x05, 0x00, 0x00, 0x10, 0x00,
+ 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB
+ },
+#define OPP_DEFAULT_CHANNEL 9
+ .channel = OPP_DEFAULT_CHANNEL },
+ { {0} }
+};
+
+static int get_rfcomm_default_chan(const uint8_t *uuid)
+{
+ int i;
+
+ for (i = 0; uuid_to_chan[i].channel; i++) {
+ if (!memcmp(uuid_to_chan[i].uuid, uuid, 16))
+ return uuid_to_chan[i].channel;
+ }
return -1;
}
+static void accept_cb(GIOChannel *io, GError *err, gpointer user_data)
+{
+}
+
+static int handle_listen(void *buf)
+{
+ struct hal_cmd_sock_listen *cmd = buf;
+ struct rfcomm_slot *rfslot;
+ GIOChannel *io;
+ GError *err = NULL;
+ int hal_fd = -1;
+ int chan;
+
+ DBG("");
+
+ chan = get_rfcomm_default_chan(cmd->uuid);
+ if (chan < 0)
+ return -1;
+
+ DBG("rfcomm channel %d", chan);
+
+ rfslot = create_rfslot(-1, &hal_fd);
+
+ io = bt_io_listen(accept_cb, NULL, rfslot, NULL, &err,
+ BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+ BT_IO_OPT_CHANNEL, chan,
+ BT_IO_OPT_INVALID);
+ if (!io) {
+ error("Failed listen: %s", err->message);
+ g_error_free(err);
+ cleanup_rfslot(rfslot);
+ return -1;
+ }
+
+ rfslot->real_sock = g_io_channel_unix_get_fd(io);
+ rfcomm_srv_list = g_list_append(rfcomm_srv_list, rfslot);
+
+ DBG("real_sock %d fd %d hal_fd %d",
+ rfslot->real_sock, rfslot->fd, hal_fd);
+
+ return hal_fd;
+}
+
static int handle_connect(void *buf)
{
DBG("Not implemented");
--
1.7.10.4