Linux bluetooth development
 help / color / mirror / Atom feed
* Re: MAP implementation status
From: Luiz Augusto von Dentz @ 2010-09-10 18:26 UTC (permalink / raw)
  To: Gustavo F. Padovan; +Cc: Radoslaw Jablonski, linux-bluetooth@vger.kernel.org
In-Reply-To: <20100910181259.GA19693@vigoh>

Hi,

On Fri, Sep 10, 2010 at 9:12 PM, Gustavo F. Padovan
<padovan@profusion.mobi> wrote:
> Hi Radoslaw,
>
> * Radoslaw Jablonski <ext-jablonski.radoslaw@nokia.com> [2010-09-09 15:46:33 +0300]:
>
>>
>>   Hi all,
>> We plan to start implementation of Message Access Profile Server as
>> obexd plugin. Before we start with coding part I need to check if
>> someone else started implementing MAP Server, MNS Server of Map client.
>> If some code is already available, then we could combine our forces to
>> add MAP support for OBEXD.
>
> We had a Google Summer of Code Project on MAP this year. Vinicius was
> the mentor, he can talk about.

iirc it was mainly a MAP client, the other parts that are missing is:

- MAP plugin + drivers
- Backend infrastructure
- Backends

-- 
Luiz Augusto von Dentz
Computer Engineer

^ permalink raw reply

* Re: [PATCH v2 1/2] Bluetooth: Implement LE Set Advertise Enable cmd
From: Gustavo F. Padovan @ 2010-09-10 18:20 UTC (permalink / raw)
  To: Anderson Briglia; +Cc: linux-bluetooth
In-Reply-To: <1284131696-26117-1-git-send-email-anderson.briglia@openbossa.org>

Hi Anderson,

* Anderson Briglia <anderson.briglia@openbossa.org> [2010-09-10 11:14:55 -0400]:

> This patch implements LE Set Advertise Enable command for dual mode and
> Low Energy hci controllers. It also adds new HCI flags in order to
> indicate the Advertising state for userland applications and kernel
> itself.
> 
> Signed-off-by: Anderson Briglia <anderson.briglia@openbossa.org>
> ---
>  include/net/bluetooth/hci.h |    7 +++++++
>  net/bluetooth/hci_event.c   |   27 +++++++++++++++++++++++++++
>  2 files changed, 34 insertions(+), 0 deletions(-)
> 
> diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
> index bcbdd6d..a28e92b 100644
> --- a/include/net/bluetooth/hci.h
> +++ b/include/net/bluetooth/hci.h
> @@ -76,6 +76,8 @@ enum {
>  	HCI_INQUIRY,
>  
>  	HCI_RAW,
> +
> +	HCI_LE_ADV,
>  };
>  
>  /* HCI ioctl defines */
> @@ -593,6 +595,11 @@ struct hci_rp_read_bd_addr {
>  	bdaddr_t bdaddr;
>  } __packed;
>  
> +/* --- HCI LE Commands --- */
> +#define HCI_OP_LE_SET_ADVERTISE_ENABLE	0x200a
> +	#define ADVERTISE_ENABLED	0x01
> +	#define ADVERTISE_DISABLED	0x00

Just make these LE_ADVERTISE_ENABLED and LE_ADVERTISE_DISABLED then we
should be fine with this patch.

-- 
Gustavo F. Padovan
ProFUSION embedded systems - http://profusion.mobi

^ permalink raw reply

* Re: set LE advertise not working...
From: Anderson Briglia @ 2010-09-10 18:14 UTC (permalink / raw)
  To: Pavan Savoy; +Cc: linux-bluetooth
In-Reply-To: <AANLkTin02sOL89MuUhWe1_nOg4umytfJKRfc=Z_dhpRq@mail.gmail.com>

On 09/10/2010 12:53 PM, Pavan Savoy wrote:
> Nope, I haven't applied the kernel patches...
> But my kernel diff to the latest kernel is only regarding the "block"
> and "unblock" to black list devices....
>
> do I need some patches in kernel for enabling/disabling BLE too ?
> I thought it just sends out the commands to controller...
>    

You are right. You don't need to have the kernel patches.
I did some tests here and everything goes ok.
Check the hcidump output below:

2010-09-10 14:13:19.032099 < HCI Command: Unknown (0x08|0x000a) plen 1
   0000: 01                                                .
2010-09-10 14:13:19.070320 > HCI Event: Command Complete (0x0e) plen 4
     Unknown (0x08|0x000a) ncmd 2
     0000: 00

I just did a hciconfig hci0 leadv

Regards,

Anderson Briglia
> On Fri, Sep 10, 2010 at 8:40 AM, Anderson Briglia
> <anderson.briglia@openbossa.org>  wrote:
>    
>> Hi Pavan,
>>
>> Try to make a hciconfig reset before setting leadv.
>> Are you using that kernel patches I send before, right?
>>
>> Regards,
>>
>> Anderson Briglia
>>
>> On 09/09/2010 03:58 PM, Pavan Savoy wrote:
>>      
>>> I have an LE enabled BT controller, and I happened to send across a
>>> simple "leadv" from the recently updated hciconfig
>>> I got back the following data
>>> "4 e 5 1 a 20 c 3a" and hence
>>> "Can't set advertise mode on hci0: Success (0)"
>>> and 0xC I guess by the BT spec means the command is disallowed (for
>>> reasons unknown?....)
>>>
>>> So I'm just wondering, am I supposed to run some HCI-VS commands to
>>> enable LE? I mean I should ask my vendor I agree, but anyone who has
>>> got it working.. do they run some VS commands before enabling LE
>>> advertising?
>>> --
>>> To unsubscribe from this list: send the line "unsubscribe linux-bluetooth"
>>> in
>>> the body of a message to majordomo@vger.kernel.org
>>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>>>
>>>        
>>
>>      


^ permalink raw reply

* Re: MAP implementation status
From: Gustavo F. Padovan @ 2010-09-10 18:12 UTC (permalink / raw)
  To: Radoslaw Jablonski; +Cc: linux-bluetooth@vger.kernel.org
In-Reply-To: <4C88D729.7050803@nokia.com>

Hi Radoslaw,

* Radoslaw Jablonski <ext-jablonski.radoslaw@nokia.com> [2010-09-09 15:46:33 +0300]:

> 
>   Hi all,
> We plan to start implementation of Message Access Profile Server as 
> obexd plugin. Before we start with coding part I need to check if 
> someone else started implementing MAP Server, MNS Server of Map client.
> If some code is already available, then we could combine our forces to 
> add MAP support for OBEXD.

We had a Google Summer of Code Project on MAP this year. Vinicius was
the mentor, he can talk about.

-- 
Gustavo F. Padovan
ProFUSION embedded systems - http://profusion.mobi

^ permalink raw reply

* RE: set LE advertise not working...
From: Mike Tsai @ 2010-09-10 17:49 UTC (permalink / raw)
  To: Pavan Savoy, Anderson Briglia; +Cc: linux-bluetooth@vger.kernel.org
In-Reply-To: <AANLkTin02sOL89MuUhWe1_nOg4umytfJKRfc=Z_dhpRq@mail.gmail.com>

Is it possible that BT controller is not in standby state. i.e. it could be already in scanning or advertising sate by default?

Mike


-----Original Message-----
From: linux-bluetooth-owner@vger.kernel.org [mailto:linux-bluetooth-owner@vger.kernel.org] On Behalf Of Pavan Savoy
Sent: Friday, September 10, 2010 9:53 AM
To: Anderson Briglia
Cc: linux-bluetooth@vger.kernel.org
Subject: Re: set LE advertise not working...

Nope, I haven't applied the kernel patches...
But my kernel diff to the latest kernel is only regarding the "block"
and "unblock" to black list devices....

do I need some patches in kernel for enabling/disabling BLE too ?
I thought it just sends out the commands to controller...

On Fri, Sep 10, 2010 at 8:40 AM, Anderson Briglia
<anderson.briglia@openbossa.org> wrote:
> Hi Pavan,
>
> Try to make a hciconfig reset before setting leadv.
> Are you using that kernel patches I send before, right?
>
> Regards,
>
> Anderson Briglia
>
> On 09/09/2010 03:58 PM, Pavan Savoy wrote:
>>
>> I have an LE enabled BT controller, and I happened to send across a
>> simple "leadv" from the recently updated hciconfig
>> I got back the following data
>> "4 e 5 1 a 20 c 3a" and hence
>> "Can't set advertise mode on hci0: Success (0)"
>> and 0xC I guess by the BT spec means the command is disallowed (for
>> reasons unknown?....)
>>
>> So I'm just wondering, am I supposed to run some HCI-VS commands to
>> enable LE? I mean I should ask my vendor I agree, but anyone who has
>> got it working.. do they run some VS commands before enabling LE
>> advertising?
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-bluetooth"
>> in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>>
>
>
--
To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH] Removing role field from ORG tag
From: Johan Hedberg @ 2010-09-10 16:55 UTC (permalink / raw)
  To: Rafal Michalski; +Cc: linux-bluetooth
In-Reply-To: <1284127974-8230-1-git-send-email-michalski.raf@gmail.com>

Hi Rafal,

On Fri, Sep 10, 2010, Rafal Michalski wrote:
> Previously role field was under ORG tag. Now it's single field under
> ROLE tag.
> ---
>  plugins/phonebook-tracker.c |    2 +-
>  plugins/vcard.c             |   12 ++++++------
>  plugins/vcard.h             |    2 +-
>  3 files changed, 8 insertions(+), 8 deletions(-)

The patch has been pushed upstream. Thanks.

Johan

^ permalink raw reply

* Re: Possible regression with skb_clone() in 2.6.36
From: Mat Martineau @ 2010-09-10 16:53 UTC (permalink / raw)
  To: Gustavo F. Padovan; +Cc: linux-bluetooth, netdev, linux-kernel, marcel, davem
In-Reply-To: <1283988727-1456-1-git-send-email-padovan@profusion.mobi>


Gustavo -

I'm not sure why the streaming code used to work, but this does not 
look like an skb_clone() problem.  Your patch to remove the 
skb_clone() call in l2cap_streaming_send() addresses the root cause of 
this crash.

On Wed, 8 Sep 2010, Gustavo F. Padovan wrote:

> I've been experiencing some problems when running the L2CAP Streaming mode in
> 2.6.36. The system quickly runs in an Out Of Memory condition and crash. That
> wasn't happening before, so I think we may have a regression here (I didn't
> find where yet). The crash log is below.
>
> The following patch does not fix the regression, but shows that removing the
> skb_clone() call from l2cap_streaming_send() we workaround the problem. The
> patch is good anyway because it saves memory and time.
>
> By now I have no idea on how to fix this.
>
> <snip>

This has to do with the sk->sk_wmem_alloc accounting that controls the 
amount of write buffer space used on the socket.

When the L2CAP streaming mode socket segments its data, it allocates 
memory using sock_alloc_send_skb() (via bt_skb_send_alloc()).  Before 
that allocation call returns, skb_set_owner_w() is called on the new 
skb.  This adds to sk->sk_wmem_alloc and sets skb->destructor so that 
sk->sk_wmem_alloc is correctly updated when the skb is freed.

When that skb is cloned, the clone is not "owned" by the write buffer. 
The clone's destructor is set to NULL in __skb_clone().  The version 
of l2cap_streaming_send() that runs out of memory is passing the 
non-owned skb clone down to the HCI layer.  The original skb (the one 
that's "owned by w") is immediately freed, which adjusts 
sk->sk_wmem_alloc back down - the socket thinks it has unlimited write 
buffer space.  As a result, bt_skb_send_alloc() never blocks waiting 
for buffer space (or returns EAGAIN for nonblocking writes) and the 
HCI send queue keeps growing.

This isn't a problem for the ERTM sends, because the original skbs are 
kept in the ERTM tx queue until they are acked.  Once they're acked, 
the write buffer space is freed and additional skbs can be allocated.

Regards,

--
Mat Martineau
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum


^ permalink raw reply

* Re: set LE advertise not working...
From: Pavan Savoy @ 2010-09-10 16:53 UTC (permalink / raw)
  To: Anderson Briglia; +Cc: linux-bluetooth
In-Reply-To: <4C8A3537.2010606@openbossa.org>

Nope, I haven't applied the kernel patches...
But my kernel diff to the latest kernel is only regarding the "block"
and "unblock" to black list devices....

do I need some patches in kernel for enabling/disabling BLE too ?
I thought it just sends out the commands to controller...

On Fri, Sep 10, 2010 at 8:40 AM, Anderson Briglia
<anderson.briglia@openbossa.org> wrote:
> Hi Pavan,
>
> Try to make a hciconfig reset before setting leadv.
> Are you using that kernel patches I send before, right?
>
> Regards,
>
> Anderson Briglia
>
> On 09/09/2010 03:58 PM, Pavan Savoy wrote:
>>
>> I have an LE enabled BT controller, and I happened to send across a
>> simple "leadv" from the recently updated hciconfig
>> I got back the following data
>> "4 e 5 1 a 20 c 3a" and hence
>> "Can't set advertise mode on hci0: Success (0)"
>> and 0xC I guess by the BT spec means the command is disallowed (for
>> reasons unknown?....)
>>
>> So I'm just wondering, am I supposed to run some HCI-VS commands to
>> enable LE? I mean I should ask my vendor I agree, but anyone who has
>> got it working.. do they run some VS commands before enabling LE
>> advertising?
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-bluetooth"
>> in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>>
>
>

^ permalink raw reply

* [PATCH v2 2/2] Bluetooth: Implement LE Set Scan Enable cmd
From: Anderson Briglia @ 2010-09-10 15:14 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Anderson Briglia
In-Reply-To: <1284131696-26117-1-git-send-email-anderson.briglia@openbossa.org>

This patch implements LE Set Scan Enable command for dual
mode and Low Energy hci controllers. It also adds new HCI flags
in order to indicate the LE Scanning state for userland applications
and kernel itself.

Signed-off-by: Anderson Briglia <anderson.briglia@openbossa.org>
---
 include/net/bluetooth/hci.h |    5 +++++
 net/bluetooth/hci_event.c   |   27 +++++++++++++++++++++++++++
 2 files changed, 32 insertions(+), 0 deletions(-)

diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index a28e92b..752c942 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -78,6 +78,7 @@ enum {
 	HCI_RAW,
 
 	HCI_LE_ADV,
+	HCI_LE_SCAN,
 };
 
 /* HCI ioctl defines */
@@ -600,6 +601,10 @@ struct hci_rp_read_bd_addr {
 	#define ADVERTISE_ENABLED	0x01
 	#define ADVERTISE_DISABLED	0x00
 
+#define HCI_OP_LE_SET_SCAN_ENABLE	0x200c
+	#define LE_SCAN_ENABLED		0x01
+	#define LE_SCAN_DISABLED	0x00
+
 /* ---- HCI Events ---- */
 #define HCI_EV_INQUIRY_COMPLETE		0x01
 
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 51e2776..c4bfbdc 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -845,6 +845,29 @@ static void hci_cc_le_set_advertise(struct hci_dev *hdev, struct sk_buff *skb)
 	hci_req_complete(hdev, status);
 }
 
+static void hci_cc_le_set_scan(struct hci_dev *hdev, struct sk_buff *skb)
+{
+	__u8 status = *((__u8 *) skb->data);
+	void *sent;
+
+	BT_DBG("%s status 0x%x", hdev->name, status);
+
+	sent = hci_sent_cmd_data(hdev, HCI_OP_LE_SET_SCAN_ENABLE);
+	if (!sent)
+		return;
+
+	if (!status) {
+		__u8 param = *((__u8 *) sent);
+
+		if (param & LE_SCAN_ENABLED)
+			set_bit(HCI_LE_SCAN, &hdev->flags);
+		else
+			clear_bit(HCI_LE_SCAN, &hdev->flags);
+	}
+
+	hci_req_complete(hdev, status);
+}
+
 static inline void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
 {
 	__u8 status = *((__u8 *) skb->data);
@@ -1337,6 +1360,10 @@ static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *sk
 		hci_cc_le_set_advertise(hdev, skb);
 		break;
 
+	case HCI_OP_LE_SET_SCAN_ENABLE:
+		hci_cc_le_set_scan(hdev, skb);
+		break;
+
 	default:
 		BT_DBG("%s opcode 0x%x", hdev->name, opcode);
 		break;
-- 
1.7.0.4


^ permalink raw reply related

* [PATCH v2 1/2] Bluetooth: Implement LE Set Advertise Enable cmd
From: Anderson Briglia @ 2010-09-10 15:14 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Anderson Briglia

This patch implements LE Set Advertise Enable command for dual mode and
Low Energy hci controllers. It also adds new HCI flags in order to
indicate the Advertising state for userland applications and kernel
itself.

Signed-off-by: Anderson Briglia <anderson.briglia@openbossa.org>
---
 include/net/bluetooth/hci.h |    7 +++++++
 net/bluetooth/hci_event.c   |   27 +++++++++++++++++++++++++++
 2 files changed, 34 insertions(+), 0 deletions(-)

diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index bcbdd6d..a28e92b 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -76,6 +76,8 @@ enum {
 	HCI_INQUIRY,
 
 	HCI_RAW,
+
+	HCI_LE_ADV,
 };
 
 /* HCI ioctl defines */
@@ -593,6 +595,11 @@ struct hci_rp_read_bd_addr {
 	bdaddr_t bdaddr;
 } __packed;
 
+/* --- HCI LE Commands --- */
+#define HCI_OP_LE_SET_ADVERTISE_ENABLE	0x200a
+	#define ADVERTISE_ENABLED	0x01
+	#define ADVERTISE_DISABLED	0x00
+
 /* ---- HCI Events ---- */
 #define HCI_EV_INQUIRY_COMPLETE		0x01
 
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index bfef5ba..51e2776 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -822,6 +822,29 @@ static void hci_cs_exit_sniff_mode(struct hci_dev *hdev, __u8 status)
 	hci_dev_unlock(hdev);
 }
 
+static void hci_cc_le_set_advertise(struct hci_dev *hdev, struct sk_buff *skb)
+{
+	__u8 status = *((__u8 *) skb->data);
+	void *sent;
+
+	BT_DBG("%s status 0x%x", hdev->name, status);
+
+	sent = hci_sent_cmd_data(hdev, HCI_OP_LE_SET_ADVERTISE_ENABLE);
+	if (!sent)
+		return;
+
+	if (!status) {
+		__u8 param = *((__u8 *) sent);
+
+		if (param & ADVERTISE_ENABLED)
+			set_bit(HCI_LE_ADV, &hdev->flags);
+		else
+			clear_bit(HCI_LE_ADV, &hdev->flags);
+	}
+
+	hci_req_complete(hdev, status);
+}
+
 static inline void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
 {
 	__u8 status = *((__u8 *) skb->data);
@@ -1310,6 +1333,10 @@ static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *sk
 		hci_cc_read_bd_addr(hdev, skb);
 		break;
 
+	case HCI_OP_LE_SET_ADVERTISE_ENABLE:
+		hci_cc_le_set_advertise(hdev, skb);
+		break;
+
 	default:
 		BT_DBG("%s opcode 0x%x", hdev->name, opcode);
 		break;
-- 
1.7.0.4


^ permalink raw reply related

* [PATCH] Removing role field from ORG tag
From: Rafal Michalski @ 2010-09-10 14:12 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Rafal Michalski

Previously role field was under ORG tag. Now it's single field under
ROLE tag.
---
 plugins/phonebook-tracker.c |    2 +-
 plugins/vcard.c             |   12 ++++++------
 plugins/vcard.h             |    2 +-
 3 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/plugins/phonebook-tracker.c b/plugins/phonebook-tracker.c
index 41df840..43c2e28 100644
--- a/plugins/phonebook-tracker.c
+++ b/plugins/phonebook-tracker.c
@@ -863,7 +863,7 @@ add_entry:
 	contact->photo = g_strdup(reply[21]);
 	contact->company = g_strdup(reply[22]);
 	contact->department = g_strdup(reply[23]);
-	contact->title = g_strdup(reply[24]);
+	contact->role = g_strdup(reply[24]);
 	contact->uid = g_strdup(reply[32]);
 
 	set_call_type(contact, reply[COL_DATE], reply[COL_SENT],
diff --git a/plugins/vcard.c b/plugins/vcard.c
index 4a77eb3..e277ced 100644
--- a/plugins/vcard.c
+++ b/plugins/vcard.c
@@ -357,9 +357,6 @@ static gboolean org_fields_present(struct phonebook_contact *contact)
 	if (contact->department && strlen(contact->department))
 		return TRUE;
 
-	if (contact->title && strlen(contact->title))
-		return TRUE;
-
 	return FALSE;
 }
 
@@ -371,8 +368,8 @@ static void vcard_printf_org(GString *vcards,
 		return;
 	}
 
-	vcard_printf(vcards, "ORG:%s;%s;%s", contact->company,
-				contact->department, contact->title);
+	vcard_printf(vcards, "ORG:%s;%s", contact->company,
+				contact->department);
 }
 
 static void vcard_printf_address(GString *vcards, uint8_t format,
@@ -539,6 +536,9 @@ void phonebook_add_contact(GString *vcards, struct phonebook_contact *contact,
 	if (filter & FILTER_ORG)
 		vcard_printf_org(vcards, contact);
 
+	if (filter & FILTER_ROLE)
+		vcard_printf_tag(vcards, format, "ROLE", NULL, contact->role);
+
 	if (filter & FILTER_X_IRMC_CALL_DATETIME)
 		vcard_printf_datetime(vcards, contact);
 
@@ -596,7 +596,7 @@ void phonebook_contact_free(struct phonebook_contact *contact)
 	g_free(contact->photo);
 	g_free(contact->company);
 	g_free(contact->department);
-	g_free(contact->title);
+	g_free(contact->role);
 	g_free(contact->datetime);
 	g_free(contact);
 }
diff --git a/plugins/vcard.h b/plugins/vcard.h
index 4d134c2..0ed51b9 100644
--- a/plugins/vcard.h
+++ b/plugins/vcard.h
@@ -78,7 +78,7 @@ struct phonebook_contact {
 	char *photo;
 	char *company;
 	char *department;
-	char *title;
+	char *role;
 	char *datetime;
 	int calltype;
 };
-- 
1.6.3.3


^ permalink raw reply related

* Re: set LE advertise not working...
From: Anderson Briglia @ 2010-09-10 13:40 UTC (permalink / raw)
  To: Pavan Savoy; +Cc: linux-bluetooth
In-Reply-To: <AANLkTikJFwwjs53_2F6NuBd-e00p1AnYahPMwYNgrveE@mail.gmail.com>

Hi Pavan,

Try to make a hciconfig reset before setting leadv.
Are you using that kernel patches I send before, right?

Regards,

Anderson Briglia

On 09/09/2010 03:58 PM, Pavan Savoy wrote:
> I have an LE enabled BT controller, and I happened to send across a
> simple "leadv" from the recently updated hciconfig
> I got back the following data
> "4 e 5 1 a 20 c 3a" and hence
> "Can't set advertise mode on hci0: Success (0)"
> and 0xC I guess by the BT spec means the command is disallowed (for
> reasons unknown?....)
>
> So I'm just wondering, am I supposed to run some HCI-VS commands to
> enable LE? I mean I should ask my vendor I agree, but anyone who has
> got it working.. do they run some VS commands before enabling LE
> advertising?
> --
> To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>    


^ permalink raw reply

* Re: Request for input regarding new driver in MFD for GPS_Bluetooth_FM controller CG2900
From: Alan Cox @ 2010-09-10 13:38 UTC (permalink / raw)
  To: pghatwork
  Cc: par-gunnar.p.hjalmdahl, linux-kernel, linux-bluetooth,
	linus.walleij
In-Reply-To: <AANLkTikTBdr4U1bPtfXWhgLgdvWDMPJrFjBCktPL40dg@mail.gmail.com>

>  - Is it OK to dynamically use the channels either from Kernel or User space?

Quite a few devices do this. In most cases it makes no sense to burden
the user with channel management so its only natural that opening the tty
or direct interfaces should "just work"

^ permalink raw reply

* Request for input regarding new driver in MFD for GPS_Bluetooth_FM controller CG2900
From: Par-Gunnar Hjalmdahl @ 2010-09-10 12:53 UTC (permalink / raw)
  To: linux-kernel, linux-bluetooth; +Cc: linus.walleij

Hi,

Me and my colleagues here at ST-Ericsson SA are working on a Linux
driver for our connectivity combo chip CG2900
(http://www.stericsson.com/product/223489.jsp).
CG2900 is a controller containing GPS, Bluetooth, and FM radio (also
called GBF-controller), which can be connected to the platform using
UART or SPI as transport. The communication towards the different
functionalities is done via so-called HCI H:4 channels, where the
first byte in the message determines the channel being used for the
particular message. Channels 1, 2, 3, and 4 are used for Bluetooth
(Command, ACL, SCO, and Event) according to Bluetooth specification
while 8 is used for FM radio and 9 is used for GPS. There are also a
few other channels used for various debug purposes.

Since there are multiple functionalities we thought that a good place
to put this driver is as a MFD driver. I hope you agree?
As can be seen below there is also a driver for the BlueZ stack, cg2900_hci.

The architecture of the driver is as follows:
 - cg2900_core: Implements driver API. Keeps track of opened channels,
registered chip handlers, and transport used. When first user
registers it requests startup of chip. When last user deregisters it
requests shutdown of chip. Uses Bluetooth HCI Local Version
Information to determine chip connected.
 - cg2900_chip: Implements chip specific features for the CG2900.
Downloads patches and settings during chip startup.
 - stlc2690_chip: As cg2900_chip but for STLC2690 controller (BT/FM combo).
 - cg2900_uart: Registers as a driver for the TTY Line Discipline
N_HCI. When transport is opened from User Space, e.g. from hciattach,
it registers as transport to cg2900_core.
 - cg2900_spi: Registers to the SPI driver. When being probed it
registers as transport to cg2900_core.
 - cg2900_char_device: Exposes all channels as character devices so
they can be used by user space stacks.
 - cg2900_hci: Registers to BlueZ (i.e. not a MFD driver) and to the
CG2900 MFD driver. Acts as a connector for Bluetooth functionality
between BlueZ and CG2900 MFD driver.

The API as it is now contains functions for:
 - register
 - deregister
 - alloc_skb
 - write
 - reset
 - get_chip_revision

For a user the normal way to work would be:
 - Register to the driver. The user supplies a name to the channel for
look-up and a set of callbacks for receiving data and detecting reset.
The register function returns, if successful, a pointer to a device
structure used when calling the other functions.
 - Send data to the chip using the write function, supplying device
structure and sk_buffer.
 - Receive data from the chip through the read callback, which
supplies device structure and sk_buffer.
 - Perform reset of the chip of a lock-up is detected by the stack.
 - Deregister from the driver.

Compared to other MFD drivers there are some major differences:
 - The CG2900 driver does not use the MFD core functions, such as
mfd_add_devices, but that could be changed quite easily by replacing
register and deregister with users using the MFD device probe instead.
This could however collide with the next difference.
 - The CG2900 driver is made with freedom of choice and usage in mind.
It is possible to dynamically open and close channels and even
transports. This means that you could open a channel first from Kernel
space (using API), then it could be closed and another user could open
it from User space (using char dev). From what I've heard this is not
very popular among the Linux community, but I really like the way it
works. Note that any channel can only have one user at a time.

I know that there are also other HCI combo chips with Linux drivers,
e.g by TI. If there is a wish we could modify this driver from cg2900
to e.g. hci_combo and make it neutral inside cg2900_core from which
chip is actually being used. This way any manufacturer could use it as
a generic framework for their multifunction HCI combo devices.


So if you've been able to read through this long message I would like
you to think through and voice your opinion about what I've presented
above. Especially:
 - Shall I use the MFD functions instead of our own API?
 - Is it OK to dynamically use the channels either from Kernel or User space?
 - Would you prefer this to be a ST-Ericsson CG2900 chip driver only
or would you like an HCI-Combo framework where you could add your own
chips?

Best regards,

Pär-Gunnar Hjälmdahl
Senior Staff Engineer
ST-Ericsson SA
Lund, Sweden

^ permalink raw reply

* Re: [PATCH] Fix dict_append_array to accept types other than string
From: Johan Hedberg @ 2010-09-10 11:58 UTC (permalink / raw)
  To: Luiz Augusto von Dentz; +Cc: linux-bluetooth
In-Reply-To: <1284119403-9367-1-git-send-email-luiz.dentz@gmail.com>

Hi Luiz,

On Fri, Sep 10, 2010, Luiz Augusto von Dentz wrote:
> From: Luiz Augusto von Dentz <luiz.dentz-von@nokia.com>
> 
> ---
>  src/dbus-common.c |   16 +++++++++++-----
>  1 files changed, 11 insertions(+), 5 deletions(-)

The patch is now upstream, with the following minor change that I did
for you:

> +		const char ***str_array = (const char ***) val;

Since val is void * there's no need to do the type cast (the original
code didn't do it either).

Johan

^ permalink raw reply

* [PATCH] Fix dict_append_array to accept types other than string
From: Luiz Augusto von Dentz @ 2010-09-10 11:50 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.dentz-von@nokia.com>

---
 src/dbus-common.c |   16 +++++++++++-----
 1 files changed, 11 insertions(+), 5 deletions(-)

diff --git a/src/dbus-common.c b/src/dbus-common.c
index 0484450..5e4813c 100644
--- a/src/dbus-common.c
+++ b/src/dbus-common.c
@@ -210,8 +210,6 @@ static void append_array_variant(DBusMessageIter *iter, int type, void *val,
 	DBusMessageIter variant, array;
 	char type_sig[2] = { type, '\0' };
 	char array_sig[3] = { DBUS_TYPE_ARRAY, type, '\0' };
-	const char ***str_array = val;
-	int i;
 
 	dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
 						array_sig, &variant);
@@ -219,9 +217,17 @@ static void append_array_variant(DBusMessageIter *iter, int type, void *val,
 	dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY,
 						type_sig, &array);
 
-	for (i = 0; i < n_elements; i++)
-		dbus_message_iter_append_basic(&array, type,
-						&((*str_array)[i]));
+	if (dbus_type_is_fixed(type) == TRUE) {
+		dbus_message_iter_append_fixed_array(&array, type, val,
+							n_elements);
+	} else if (type == DBUS_TYPE_STRING || type == DBUS_TYPE_OBJECT_PATH) {
+		const char ***str_array = (const char ***) val;
+		int i;
+
+		for (i = 0; i < n_elements; i++)
+			dbus_message_iter_append_basic(&array, type,
+							&((*str_array)[i]));
+	}
 
 	dbus_message_iter_close_container(&variant, &array);
 
-- 
1.7.1


^ permalink raw reply related

* Re: Possible regression with skb_clone() in 2.6.36
From: Jarek Poplawski @ 2010-09-10 10:15 UTC (permalink / raw)
  To: Gustavo F. Padovan; +Cc: netdev, linux-kernel, marcel, davem, linux-bluetooth
In-Reply-To: <1283988727-1456-1-git-send-email-padovan@profusion.mobi>

On 2010-09-09 01:32, Gustavo F. Padovan wrote:
> I've been experiencing some problems when running the L2CAP Streaming mode in
> 2.6.36. The system quickly runs in an Out Of Memory condition and crash. That
> wasn't happening before, so I think we may have a regression here (I didn't
> find where yet). The crash log is below.
> 
> The following patch does not fix the regression, but shows that removing the
> skb_clone() call from l2cap_streaming_send() we workaround the problem. The
> patch is good anyway because it saves memory and time.
> 
> By now I have no idea on how to fix this.

My suggestion is to check the length of conn->data_q in hci_send_acl()
while this happens, and maybe try to set some limit here.

Jarek P.

^ permalink raw reply

* [PATCH 3/3] btio: Add ifndef/endif guard for btio.h
From: Zhenhua Zhang @ 2010-09-10  2:49 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1284086984-17072-1-git-send-email-zhenhua.zhang@intel.com>

To avoid circular inclusion of include file.
---
 btio/btio.h |    3 +++
 1 files changed, 3 insertions(+), 0 deletions(-)

diff --git a/btio/btio.h b/btio/btio.h
index 81fda8e..d373ed1 100644
--- a/btio/btio.h
+++ b/btio/btio.h
@@ -21,6 +21,8 @@
  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  *
  */
+#ifndef BT_IO_H
+#define BT_IO_H
 
 #include <glib.h>
 
@@ -92,3 +94,4 @@ GIOChannel *bt_io_listen(BtIOType type, BtIOConnect connect,
 				GDestroyNotify destroy, GError **err,
 				BtIOOption opt1, ...);
 
+#endif
-- 
1.7.0.4


^ permalink raw reply related

* [PATCH 2/3] btio: Seperate btio.[ch] into btio directory
From: Zhenhua Zhang @ 2010-09-10  2:49 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1284086984-17072-1-git-send-email-zhenhua.zhang@intel.com>

Seperate btio.[ch] from src directory to btio sub-folder.
---
 Makefile.am    |    8 +-
 Makefile.tools |    2 +-
 btio/btio.c    | 1299 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 btio/btio.h    |   94 ++++
 src/btio.c     | 1299 --------------------------------------------------------
 src/btio.h     |   94 ----
 6 files changed, 1399 insertions(+), 1397 deletions(-)
 create mode 100644 btio/btio.c
 create mode 100644 btio/btio.h
 delete mode 100644 src/btio.c
 delete mode 100644 src/btio.h

diff --git a/Makefile.am b/Makefile.am
index 7895be2..a8829d9 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -102,6 +102,8 @@ endif
 gdbus_sources = gdbus/gdbus.h gdbus/mainloop.c gdbus/watch.c \
 					gdbus/object.c gdbus/polkit.c
 
+btio_sources = btio/btio.h btio/btio.c
+
 builtin_modules =
 builtin_sources =
 builtin_nodist =
@@ -216,14 +218,14 @@ endif
 sbin_PROGRAMS += src/bluetoothd
 
 src_bluetoothd_SOURCES = $(gdbus_sources) $(builtin_sources) \
-			$(attrib_sources) \
+			$(attrib_sources) $(btio_sources) \
 			$(mcap_sources) \
 			src/main.c src/log.h src/log.c \
 			src/security.c src/rfkill.c src/hcid.h src/sdpd.h \
 			src/sdpd-server.c src/sdpd-request.c \
 			src/sdpd-service.c src/sdpd-database.c \
 			src/attrib-server.h src/attrib-server.c \
-			src/sdp-xml.h src/sdp-xml.c src/btio.h src/btio.c \
+			src/sdp-xml.h src/sdp-xml.c \
 			src/textfile.h src/textfile.c \
 			src/glib-helper.h src/glib-helper.c \
 			src/oui.h src/oui.c src/uinput.h src/ppoll.h \
@@ -357,7 +359,7 @@ AM_CFLAGS = @DBUS_CFLAGS@ @GLIB_CFLAGS@ @CAPNG_CFLAGS@ \
 
 INCLUDES = -I$(builddir)/lib -I$(builddir)/src -I$(srcdir)/src \
 			-I$(srcdir)/audio -I$(srcdir)/sbc -I$(srcdir)/gdbus \
-			-I$(srcdir)/attrib
+			-I$(srcdir)/attrib -I$(srcdir)/btio
 
 if MCAP
 	INCLUDES += -I$(builddir)/health
diff --git a/Makefile.tools b/Makefile.tools
index 1c46542..14ecf31 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -170,7 +170,7 @@ test_bdaddr_LDADD = lib/libbluetooth.la
 
 test_agent_LDADD = @DBUS_LIBS@
 
-test_btiotest_SOURCES = test/btiotest.c src/btio.h src/btio.c
+test_btiotest_SOURCES = test/btiotest.c btio/btio.h btio/btio.c
 test_btiotest_LDADD = @GLIB_LIBS@ lib/libbluetooth.la
 
 test_test_textfile_SOURCES = test/test-textfile.c src/textfile.h src/textfile.c
diff --git a/btio/btio.c b/btio/btio.c
new file mode 100644
index 0000000..8b273ca
--- /dev/null
+++ b/btio/btio.c
@@ -0,0 +1,1299 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2009-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2009-2010  Nokia Corporation
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+#include <stdarg.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <poll.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/l2cap.h>
+#include <bluetooth/rfcomm.h>
+#include <bluetooth/sco.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#include <glib.h>
+
+#include "btio.h"
+
+#define ERROR_FAILED(gerr, str, err) \
+		g_set_error(gerr, BT_IO_ERROR, BT_IO_ERROR_FAILED, \
+				str ": %s (%d)", strerror(err), err)
+
+#define DEFAULT_DEFER_TIMEOUT 30
+
+struct set_opts {
+	bdaddr_t src;
+	bdaddr_t dst;
+	int defer;
+	int sec_level;
+	uint8_t channel;
+	uint16_t psm;
+	uint16_t mtu;
+	uint16_t imtu;
+	uint16_t omtu;
+	int master;
+	uint8_t mode;
+};
+
+struct connect {
+	BtIOConnect connect;
+	gpointer user_data;
+	GDestroyNotify destroy;
+};
+
+struct accept {
+	BtIOConnect connect;
+	gpointer user_data;
+	GDestroyNotify destroy;
+};
+
+struct server {
+	BtIOConnect connect;
+	BtIOConfirm confirm;
+	gpointer user_data;
+	GDestroyNotify destroy;
+};
+
+static void server_remove(struct server *server)
+{
+	if (server->destroy)
+		server->destroy(server->user_data);
+	g_free(server);
+}
+
+static void connect_remove(struct connect *conn)
+{
+	if (conn->destroy)
+		conn->destroy(conn->user_data);
+	g_free(conn);
+}
+
+static void accept_remove(struct accept *accept)
+{
+	if (accept->destroy)
+		accept->destroy(accept->user_data);
+	g_free(accept);
+}
+
+static gboolean check_nval(GIOChannel *io)
+{
+	struct pollfd fds;
+
+	memset(&fds, 0, sizeof(fds));
+	fds.fd = g_io_channel_unix_get_fd(io);
+	fds.events = POLLNVAL;
+
+	if (poll(&fds, 1, 0) > 0 && (fds.revents & POLLNVAL))
+		return TRUE;
+
+	return FALSE;
+}
+
+static gboolean accept_cb(GIOChannel *io, GIOCondition cond,
+							gpointer user_data)
+{
+	struct accept *accept = user_data;
+	GError *err = NULL;
+
+	/* If the user aborted this accept attempt */
+	if ((cond & G_IO_NVAL) || check_nval(io))
+		return FALSE;
+
+	if (cond & (G_IO_HUP | G_IO_ERR))
+		g_set_error(&err, BT_IO_ERROR, BT_IO_ERROR_DISCONNECTED,
+				"HUP or ERR on socket");
+
+	accept->connect(io, err, accept->user_data);
+
+	g_clear_error(&err);
+
+	return FALSE;
+}
+
+static gboolean connect_cb(GIOChannel *io, GIOCondition cond,
+							gpointer user_data)
+{
+	struct connect *conn = user_data;
+	GError *gerr = NULL;
+
+	/* If the user aborted this connect attempt */
+	if ((cond & G_IO_NVAL) || check_nval(io))
+		return FALSE;
+
+	if (cond & G_IO_OUT) {
+		int err = 0, sock = g_io_channel_unix_get_fd(io);
+		socklen_t len = sizeof(err);
+
+		if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &len) < 0)
+			err = errno;
+
+		if (err)
+			g_set_error(&gerr, BT_IO_ERROR,
+					BT_IO_ERROR_CONNECT_FAILED, "%s (%d)",
+					strerror(err), err);
+	} else if (cond & (G_IO_HUP | G_IO_ERR))
+		g_set_error(&gerr, BT_IO_ERROR, BT_IO_ERROR_CONNECT_FAILED,
+				"HUP or ERR on socket");
+
+	conn->connect(io, gerr, conn->user_data);
+
+	if (gerr)
+		g_error_free(gerr);
+
+	return FALSE;
+}
+
+static gboolean server_cb(GIOChannel *io, GIOCondition cond,
+							gpointer user_data)
+{
+	struct server *server = user_data;
+	int srv_sock, cli_sock;
+	GIOChannel *cli_io;
+
+	/* If the user closed the server */
+	if ((cond & G_IO_NVAL) || check_nval(io))
+		return FALSE;
+
+	srv_sock = g_io_channel_unix_get_fd(io);
+
+	cli_sock = accept(srv_sock, NULL, NULL);
+	if (cli_sock < 0)
+		return TRUE;
+
+	cli_io = g_io_channel_unix_new(cli_sock);
+
+	g_io_channel_set_close_on_unref(cli_io, TRUE);
+	g_io_channel_set_flags(cli_io, G_IO_FLAG_NONBLOCK, NULL);
+
+	if (server->confirm)
+		server->confirm(cli_io, server->user_data);
+	else
+		server->connect(cli_io, NULL, server->user_data);
+
+	g_io_channel_unref(cli_io);
+
+	return TRUE;
+}
+
+static void server_add(GIOChannel *io, BtIOConnect connect,
+				BtIOConfirm confirm, gpointer user_data,
+				GDestroyNotify destroy)
+{
+	struct server *server;
+	GIOCondition cond;
+
+	server = g_new0(struct server, 1);
+	server->connect = connect;
+	server->confirm = confirm;
+	server->user_data = user_data;
+	server->destroy = destroy;
+
+	cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+	g_io_add_watch_full(io, G_PRIORITY_DEFAULT, cond, server_cb, server,
+					(GDestroyNotify) server_remove);
+}
+
+static void connect_add(GIOChannel *io, BtIOConnect connect,
+				gpointer user_data, GDestroyNotify destroy)
+{
+	struct connect *conn;
+	GIOCondition cond;
+
+	conn = g_new0(struct connect, 1);
+	conn->connect = connect;
+	conn->user_data = user_data;
+	conn->destroy = destroy;
+
+	cond = G_IO_OUT | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+	g_io_add_watch_full(io, G_PRIORITY_DEFAULT, cond, connect_cb, conn,
+					(GDestroyNotify) connect_remove);
+}
+
+static void accept_add(GIOChannel *io, BtIOConnect connect, gpointer user_data,
+							GDestroyNotify destroy)
+{
+	struct accept *accept;
+	GIOCondition cond;
+
+	accept = g_new0(struct accept, 1);
+	accept->connect = connect;
+	accept->user_data = user_data;
+	accept->destroy = destroy;
+
+	cond = G_IO_OUT | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+	g_io_add_watch_full(io, G_PRIORITY_DEFAULT, cond, accept_cb, accept,
+					(GDestroyNotify) accept_remove);
+}
+
+static int l2cap_bind(int sock, const bdaddr_t *src, uint16_t psm, GError **err)
+{
+	struct sockaddr_l2 addr;
+
+	memset(&addr, 0, sizeof(addr));
+	addr.l2_family = AF_BLUETOOTH;
+	bacpy(&addr.l2_bdaddr, src);
+	addr.l2_psm = htobs(psm);
+
+	if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		ERROR_FAILED(err, "l2cap_bind", errno);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int l2cap_connect(int sock, const bdaddr_t *dst, uint16_t psm)
+{
+	int err;
+	struct sockaddr_l2 addr;
+
+	memset(&addr, 0, sizeof(addr));
+	addr.l2_family = AF_BLUETOOTH;
+	bacpy(&addr.l2_bdaddr, dst);
+	addr.l2_psm = htobs(psm);
+
+	err = connect(sock, (struct sockaddr *) &addr, sizeof(addr));
+	if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS))
+		return err;
+
+	return 0;
+}
+
+static int l2cap_set_master(int sock, int master)
+{
+	int flags;
+	socklen_t len;
+
+	len = sizeof(flags);
+	if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags, &len) < 0)
+		return -errno;
+
+	if (master) {
+		if (flags & L2CAP_LM_MASTER)
+			return 0;
+		flags |= L2CAP_LM_MASTER;
+	} else {
+		if (!(flags & L2CAP_LM_MASTER))
+			return 0;
+		flags &= ~L2CAP_LM_MASTER;
+	}
+
+	if (setsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags, sizeof(flags)) < 0)
+		return -errno;
+
+	return 0;
+}
+
+static int rfcomm_set_master(int sock, int master)
+{
+	int flags;
+	socklen_t len;
+
+	len = sizeof(flags);
+	if (getsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &flags, &len) < 0)
+		return -errno;
+
+	if (master) {
+		if (flags & RFCOMM_LM_MASTER)
+			return 0;
+		flags |= RFCOMM_LM_MASTER;
+	} else {
+		if (!(flags & RFCOMM_LM_MASTER))
+			return 0;
+		flags &= ~RFCOMM_LM_MASTER;
+	}
+
+	if (setsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &flags, sizeof(flags)) < 0)
+		return -errno;
+
+	return 0;
+}
+
+static int l2cap_set_lm(int sock, int level)
+{
+	int lm_map[] = {
+		0,
+		L2CAP_LM_AUTH,
+		L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT,
+		L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT | L2CAP_LM_SECURE,
+	}, opt = lm_map[level];
+
+	if (setsockopt(sock, SOL_L2CAP, L2CAP_LM, &opt, sizeof(opt)) < 0)
+		return -errno;
+
+	return 0;
+}
+
+static int rfcomm_set_lm(int sock, int level)
+{
+	int lm_map[] = {
+		0,
+		RFCOMM_LM_AUTH,
+		RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT,
+		RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT | RFCOMM_LM_SECURE,
+	}, opt = lm_map[level];
+
+	if (setsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &opt, sizeof(opt)) < 0)
+		return -errno;
+
+	return 0;
+}
+
+static gboolean set_sec_level(int sock, BtIOType type, int level, GError **err)
+{
+	struct bt_security sec;
+	int ret;
+
+	if (level < BT_SECURITY_LOW || level > BT_SECURITY_HIGH) {
+		g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+				"Valid security level range is %d-%d",
+				BT_SECURITY_LOW, BT_SECURITY_HIGH);
+		return FALSE;
+	}
+
+	memset(&sec, 0, sizeof(sec));
+	sec.level = level;
+
+	if (setsockopt(sock, SOL_BLUETOOTH, BT_SECURITY, &sec,
+							sizeof(sec)) == 0)
+		return TRUE;
+
+	if (errno != ENOPROTOOPT) {
+		ERROR_FAILED(err, "setsockopt(BT_SECURITY)", errno);
+		return FALSE;
+	}
+
+	if (type == BT_IO_L2CAP)
+		ret = l2cap_set_lm(sock, level);
+	else
+		ret = rfcomm_set_lm(sock, level);
+
+	if (ret < 0) {
+		ERROR_FAILED(err, "setsockopt(LM)", -ret);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static int l2cap_get_lm(int sock, int *sec_level)
+{
+	int opt;
+	socklen_t len;
+
+	len = sizeof(opt);
+	if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &opt, &len) < 0)
+		return -errno;
+
+	*sec_level = 0;
+
+	if (opt & L2CAP_LM_AUTH)
+		*sec_level = BT_SECURITY_LOW;
+	if (opt & L2CAP_LM_ENCRYPT)
+		*sec_level = BT_SECURITY_MEDIUM;
+	if (opt & L2CAP_LM_SECURE)
+		*sec_level = BT_SECURITY_HIGH;
+
+	return 0;
+}
+
+static int rfcomm_get_lm(int sock, int *sec_level)
+{
+	int opt;
+	socklen_t len;
+
+	len = sizeof(opt);
+	if (getsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &opt, &len) < 0)
+		return -errno;
+
+	*sec_level = 0;
+
+	if (opt & RFCOMM_LM_AUTH)
+		*sec_level = BT_SECURITY_LOW;
+	if (opt & RFCOMM_LM_ENCRYPT)
+		*sec_level = BT_SECURITY_MEDIUM;
+	if (opt & RFCOMM_LM_SECURE)
+		*sec_level = BT_SECURITY_HIGH;
+
+	return 0;
+}
+
+static gboolean get_sec_level(int sock, BtIOType type, int *level,
+								GError **err)
+{
+	struct bt_security sec;
+	socklen_t len;
+	int ret;
+
+	memset(&sec, 0, sizeof(sec));
+	len = sizeof(sec);
+	if (getsockopt(sock, SOL_BLUETOOTH, BT_SECURITY, &sec, &len) == 0) {
+		*level = sec.level;
+		return TRUE;
+	}
+
+	if (errno != ENOPROTOOPT) {
+		ERROR_FAILED(err, "getsockopt(BT_SECURITY)", errno);
+		return FALSE;
+	}
+
+	if (type == BT_IO_L2CAP)
+		ret = l2cap_get_lm(sock, level);
+	else
+		ret = rfcomm_get_lm(sock, level);
+
+	if (ret < 0) {
+		ERROR_FAILED(err, "getsockopt(LM)", -ret);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean l2cap_set(int sock, int sec_level, uint16_t imtu, uint16_t omtu,
+					uint8_t mode, int master, GError **err)
+{
+	if (imtu || omtu || mode) {
+		struct l2cap_options l2o;
+		socklen_t len;
+
+		memset(&l2o, 0, sizeof(l2o));
+		len = sizeof(l2o);
+		if (getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o,
+								&len) < 0) {
+			ERROR_FAILED(err, "getsockopt(L2CAP_OPTIONS)", errno);
+			return FALSE;
+		}
+
+		if (imtu)
+			l2o.imtu = imtu;
+		if (omtu)
+			l2o.omtu = omtu;
+		if (mode)
+			l2o.mode = mode;
+
+		if (setsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o,
+							sizeof(l2o)) < 0) {
+			ERROR_FAILED(err, "setsockopt(L2CAP_OPTIONS)", errno);
+			return FALSE;
+		}
+	}
+
+	if (master >= 0 && l2cap_set_master(sock, master) < 0) {
+		ERROR_FAILED(err, "l2cap_set_master", errno);
+		return FALSE;
+	}
+
+	if (sec_level && !set_sec_level(sock, BT_IO_L2CAP, sec_level, err))
+		return FALSE;
+
+	return TRUE;
+}
+
+static int rfcomm_bind(int sock,
+		const bdaddr_t *src, uint8_t channel, GError **err)
+{
+	struct sockaddr_rc addr;
+
+	memset(&addr, 0, sizeof(addr));
+	addr.rc_family = AF_BLUETOOTH;
+	bacpy(&addr.rc_bdaddr, src);
+	addr.rc_channel = channel;
+
+	if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		ERROR_FAILED(err, "rfcomm_bind", errno);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int rfcomm_connect(int sock, const bdaddr_t *dst, uint8_t channel)
+{
+	int err;
+	struct sockaddr_rc addr;
+
+	memset(&addr, 0, sizeof(addr));
+	addr.rc_family = AF_BLUETOOTH;
+	bacpy(&addr.rc_bdaddr, dst);
+	addr.rc_channel = channel;
+
+	err = connect(sock, (struct sockaddr *) &addr, sizeof(addr));
+	if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS))
+		return err;
+
+	return 0;
+}
+
+static gboolean rfcomm_set(int sock, int sec_level, int master, GError **err)
+{
+	if (sec_level && !set_sec_level(sock, BT_IO_RFCOMM, sec_level, err))
+		return FALSE;
+
+	if (master >= 0 && rfcomm_set_master(sock, master) < 0) {
+		ERROR_FAILED(err, "rfcomm_set_master", errno);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static int sco_bind(int sock, const bdaddr_t *src, GError **err)
+{
+	struct sockaddr_sco addr;
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sco_family = AF_BLUETOOTH;
+	bacpy(&addr.sco_bdaddr, src);
+
+	if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		ERROR_FAILED(err, "sco_bind", errno);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int sco_connect(int sock, const bdaddr_t *dst)
+{
+	struct sockaddr_sco addr;
+	int err;
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sco_family = AF_BLUETOOTH;
+	bacpy(&addr.sco_bdaddr, dst);
+
+	err = connect(sock, (struct sockaddr *) &addr, sizeof(addr));
+	if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS))
+		return err;
+
+	return 0;
+}
+
+static gboolean sco_set(int sock, uint16_t mtu, GError **err)
+{
+	struct sco_options sco_opt;
+	socklen_t len;
+
+	if (!mtu)
+		return TRUE;
+
+	len = sizeof(sco_opt);
+	memset(&sco_opt, 0, len);
+	if (getsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt, &len) < 0) {
+		ERROR_FAILED(err, "getsockopt(SCO_OPTIONS)", errno);
+		return FALSE;
+	}
+
+	sco_opt.mtu = mtu;
+	if (setsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt,
+						sizeof(sco_opt)) < 0) {
+		ERROR_FAILED(err, "setsockopt(SCO_OPTIONS)", errno);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean parse_set_opts(struct set_opts *opts, GError **err,
+						BtIOOption opt1, va_list args)
+{
+	BtIOOption opt = opt1;
+	const char *str;
+
+	memset(opts, 0, sizeof(*opts));
+
+	/* Set defaults */
+	opts->defer = DEFAULT_DEFER_TIMEOUT;
+	opts->master = -1;
+	opts->sec_level = BT_IO_SEC_MEDIUM;
+	opts->mode = L2CAP_MODE_BASIC;
+
+	while (opt != BT_IO_OPT_INVALID) {
+		switch (opt) {
+		case BT_IO_OPT_SOURCE:
+			str = va_arg(args, const char *);
+			if (strncasecmp(str, "hci", 3) == 0)
+				hci_devba(atoi(str + 3), &opts->src);
+			else
+				str2ba(str, &opts->src);
+			break;
+		case BT_IO_OPT_SOURCE_BDADDR:
+			bacpy(&opts->src, va_arg(args, const bdaddr_t *));
+			break;
+		case BT_IO_OPT_DEST:
+			str2ba(va_arg(args, const char *), &opts->dst);
+			break;
+		case BT_IO_OPT_DEST_BDADDR:
+			bacpy(&opts->dst, va_arg(args, const bdaddr_t *));
+			break;
+		case BT_IO_OPT_DEFER_TIMEOUT:
+			opts->defer = va_arg(args, int);
+			break;
+		case BT_IO_OPT_SEC_LEVEL:
+			opts->sec_level = va_arg(args, int);
+			break;
+		case BT_IO_OPT_CHANNEL:
+			opts->channel = va_arg(args, int);
+			break;
+		case BT_IO_OPT_PSM:
+			opts->psm = va_arg(args, int);
+			break;
+		case BT_IO_OPT_MTU:
+			opts->mtu = va_arg(args, int);
+			opts->imtu = opts->mtu;
+			opts->omtu = opts->mtu;
+			break;
+		case BT_IO_OPT_OMTU:
+			opts->omtu = va_arg(args, int);
+			if (!opts->mtu)
+				opts->mtu = opts->omtu;
+			break;
+		case BT_IO_OPT_IMTU:
+			opts->imtu = va_arg(args, int);
+			if (!opts->mtu)
+				opts->mtu = opts->imtu;
+			break;
+		case BT_IO_OPT_MASTER:
+			opts->master = va_arg(args, gboolean);
+			break;
+		case BT_IO_OPT_MODE:
+			opts->mode = va_arg(args, int);
+			break;
+		default:
+			g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+					"Unknown option %d", opt);
+			return FALSE;
+		}
+
+		opt = va_arg(args, int);
+	}
+
+	return TRUE;
+}
+
+static gboolean get_peers(int sock, struct sockaddr *src, struct sockaddr *dst,
+				socklen_t len, GError **err)
+{
+	socklen_t olen;
+
+	memset(src, 0, len);
+	olen = len;
+	if (getsockname(sock, src, &olen) < 0) {
+		ERROR_FAILED(err, "getsockname", errno);
+		return FALSE;
+	}
+
+	memset(dst, 0, len);
+	olen = len;
+	if (getpeername(sock, dst, &olen) < 0) {
+		ERROR_FAILED(err, "getpeername", errno);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static int l2cap_get_info(int sock, uint16_t *handle, uint8_t *dev_class)
+{
+	struct l2cap_conninfo info;
+	socklen_t len;
+
+	len = sizeof(info);
+	if (getsockopt(sock, SOL_L2CAP, L2CAP_CONNINFO, &info, &len) < 0)
+		return -errno;
+
+	if (handle)
+		*handle = info.hci_handle;
+
+	if (dev_class)
+		memcpy(dev_class, info.dev_class, 3);
+
+	return 0;
+}
+
+static gboolean l2cap_get(int sock, GError **err, BtIOOption opt1,
+								va_list args)
+{
+	BtIOOption opt = opt1;
+	struct sockaddr_l2 src, dst;
+	struct l2cap_options l2o;
+	int flags;
+	uint8_t dev_class[3];
+	uint16_t handle;
+	socklen_t len;
+
+	len = sizeof(l2o);
+	memset(&l2o, 0, len);
+	if (getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &len) < 0) {
+		ERROR_FAILED(err, "getsockopt(L2CAP_OPTIONS)", errno);
+		return FALSE;
+	}
+
+	if (!get_peers(sock, (struct sockaddr *) &src,
+				(struct sockaddr *) &dst, sizeof(src), err))
+		return FALSE;
+
+	while (opt != BT_IO_OPT_INVALID) {
+		switch (opt) {
+		case BT_IO_OPT_SOURCE:
+			ba2str(&src.l2_bdaddr, va_arg(args, char *));
+			break;
+		case BT_IO_OPT_SOURCE_BDADDR:
+			bacpy(va_arg(args, bdaddr_t *), &src.l2_bdaddr);
+			break;
+		case BT_IO_OPT_DEST:
+			ba2str(&dst.l2_bdaddr, va_arg(args, char *));
+			break;
+		case BT_IO_OPT_DEST_BDADDR:
+			bacpy(va_arg(args, bdaddr_t *), &dst.l2_bdaddr);
+			break;
+		case BT_IO_OPT_DEFER_TIMEOUT:
+			len = sizeof(int);
+			if (getsockopt(sock, SOL_BLUETOOTH, BT_DEFER_SETUP,
+					va_arg(args, int *), &len) < 0) {
+				ERROR_FAILED(err, "getsockopt(DEFER_SETUP)",
+									errno);
+				return FALSE;
+			}
+			break;
+		case BT_IO_OPT_SEC_LEVEL:
+			if (!get_sec_level(sock, BT_IO_L2CAP,
+						va_arg(args, int *), err))
+				return FALSE;
+			break;
+		case BT_IO_OPT_PSM:
+			*(va_arg(args, uint16_t *)) = src.l2_psm ?
+						src.l2_psm : dst.l2_psm;
+			break;
+		case BT_IO_OPT_OMTU:
+			*(va_arg(args, uint16_t *)) = l2o.omtu;
+			break;
+		case BT_IO_OPT_IMTU:
+			*(va_arg(args, uint16_t *)) = l2o.imtu;
+			break;
+		case BT_IO_OPT_MASTER:
+			len = sizeof(flags);
+			if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags,
+								&len) < 0) {
+				ERROR_FAILED(err, "getsockopt(L2CAP_LM)",
+									errno);
+				return FALSE;
+			}
+			*(va_arg(args, gboolean *)) =
+				(flags & L2CAP_LM_MASTER) ? TRUE : FALSE;
+			break;
+		case BT_IO_OPT_HANDLE:
+			if (l2cap_get_info(sock, &handle, dev_class) < 0) {
+				ERROR_FAILED(err, "L2CAP_CONNINFO", errno);
+				return FALSE;
+			}
+			*(va_arg(args, uint16_t *)) = handle;
+			break;
+		case BT_IO_OPT_CLASS:
+			if (l2cap_get_info(sock, &handle, dev_class) < 0) {
+				ERROR_FAILED(err, "L2CAP_CONNINFO", errno);
+				return FALSE;
+			}
+			memcpy(va_arg(args, uint8_t *), dev_class, 3);
+			break;
+		default:
+			g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+					"Unknown option %d", opt);
+			return FALSE;
+		}
+
+		opt = va_arg(args, int);
+	}
+
+	return TRUE;
+}
+
+static int rfcomm_get_info(int sock, uint16_t *handle, uint8_t *dev_class)
+{
+	struct rfcomm_conninfo info;
+	socklen_t len;
+
+	len = sizeof(info);
+	if (getsockopt(sock, SOL_RFCOMM, RFCOMM_CONNINFO, &info, &len) < 0)
+		return -errno;
+
+	if (handle)
+		*handle = info.hci_handle;
+
+	if (dev_class)
+		memcpy(dev_class, info.dev_class, 3);
+
+	return 0;
+}
+
+static gboolean rfcomm_get(int sock, GError **err, BtIOOption opt1,
+								va_list args)
+{
+	BtIOOption opt = opt1;
+	struct sockaddr_rc src, dst;
+	int flags;
+	socklen_t len;
+	uint8_t dev_class[3];
+	uint16_t handle;
+
+	if (!get_peers(sock, (struct sockaddr *) &src,
+				(struct sockaddr *) &dst, sizeof(src), err))
+		return FALSE;
+
+	while (opt != BT_IO_OPT_INVALID) {
+		switch (opt) {
+		case BT_IO_OPT_SOURCE:
+			ba2str(&src.rc_bdaddr, va_arg(args, char *));
+			break;
+		case BT_IO_OPT_SOURCE_BDADDR:
+			bacpy(va_arg(args, bdaddr_t *), &src.rc_bdaddr);
+			break;
+		case BT_IO_OPT_DEST:
+			ba2str(&dst.rc_bdaddr, va_arg(args, char *));
+			break;
+		case BT_IO_OPT_DEST_BDADDR:
+			bacpy(va_arg(args, bdaddr_t *), &dst.rc_bdaddr);
+			break;
+		case BT_IO_OPT_DEFER_TIMEOUT:
+			len = sizeof(int);
+			if (getsockopt(sock, SOL_BLUETOOTH, BT_DEFER_SETUP,
+					va_arg(args, int *), &len) < 0) {
+				ERROR_FAILED(err, "getsockopt(DEFER_SETUP)",
+									errno);
+				return FALSE;
+			}
+			break;
+		case BT_IO_OPT_SEC_LEVEL:
+			if (!get_sec_level(sock, BT_IO_RFCOMM,
+						va_arg(args, int *), err))
+				return FALSE;
+			break;
+		case BT_IO_OPT_CHANNEL:
+			*(va_arg(args, uint8_t *)) = src.rc_channel ?
+					src.rc_channel : dst.rc_channel;
+			break;
+		case BT_IO_OPT_SOURCE_CHANNEL:
+			*(va_arg(args, uint8_t *)) = src.rc_channel;
+			break;
+		case BT_IO_OPT_DEST_CHANNEL:
+			*(va_arg(args, uint8_t *)) = dst.rc_channel;
+			break;
+		case BT_IO_OPT_MASTER:
+			len = sizeof(flags);
+			if (getsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &flags,
+								&len) < 0) {
+				ERROR_FAILED(err, "getsockopt(RFCOMM_LM)",
+									errno);
+				return FALSE;
+			}
+			*(va_arg(args, gboolean *)) =
+				(flags & RFCOMM_LM_MASTER) ? TRUE : FALSE;
+			break;
+		case BT_IO_OPT_HANDLE:
+			if (rfcomm_get_info(sock, &handle, dev_class) < 0) {
+				ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
+				return FALSE;
+			}
+			*(va_arg(args, uint16_t *)) = handle;
+			break;
+		case BT_IO_OPT_CLASS:
+			if (rfcomm_get_info(sock, &handle, dev_class) < 0) {
+				ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
+				return FALSE;
+			}
+			memcpy(va_arg(args, uint8_t *), dev_class, 3);
+			break;
+		default:
+			g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+					"Unknown option %d", opt);
+			return FALSE;
+		}
+
+		opt = va_arg(args, int);
+	}
+
+	return TRUE;
+}
+
+static int sco_get_info(int sock, uint16_t *handle, uint8_t *dev_class)
+{
+	struct sco_conninfo info;
+	socklen_t len;
+
+	len = sizeof(info);
+	if (getsockopt(sock, SOL_SCO, SCO_CONNINFO, &info, &len) < 0)
+		return -errno;
+
+	if (handle)
+		*handle = info.hci_handle;
+
+	if (dev_class)
+		memcpy(dev_class, info.dev_class, 3);
+
+	return 0;
+}
+
+static gboolean sco_get(int sock, GError **err, BtIOOption opt1, va_list args)
+{
+	BtIOOption opt = opt1;
+	struct sockaddr_sco src, dst;
+	struct sco_options sco_opt;
+	socklen_t len;
+	uint8_t dev_class[3];
+	uint16_t handle;
+
+	len = sizeof(sco_opt);
+	memset(&sco_opt, 0, len);
+	if (getsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt, &len) < 0) {
+		ERROR_FAILED(err, "getsockopt(SCO_OPTIONS)", errno);
+		return FALSE;
+	}
+
+	if (!get_peers(sock, (struct sockaddr *) &src,
+				(struct sockaddr *) &dst, sizeof(src), err))
+		return FALSE;
+
+	while (opt != BT_IO_OPT_INVALID) {
+		switch (opt) {
+		case BT_IO_OPT_SOURCE:
+			ba2str(&src.sco_bdaddr, va_arg(args, char *));
+			break;
+		case BT_IO_OPT_SOURCE_BDADDR:
+			bacpy(va_arg(args, bdaddr_t *), &src.sco_bdaddr);
+			break;
+		case BT_IO_OPT_DEST:
+			ba2str(&dst.sco_bdaddr, va_arg(args, char *));
+			break;
+		case BT_IO_OPT_DEST_BDADDR:
+			bacpy(va_arg(args, bdaddr_t *), &dst.sco_bdaddr);
+			break;
+		case BT_IO_OPT_MTU:
+		case BT_IO_OPT_IMTU:
+		case BT_IO_OPT_OMTU:
+			*(va_arg(args, uint16_t *)) = sco_opt.mtu;
+			break;
+		case BT_IO_OPT_HANDLE:
+			if (sco_get_info(sock, &handle, dev_class) < 0) {
+				ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
+				return FALSE;
+			}
+			*(va_arg(args, uint16_t *)) = handle;
+			break;
+		case BT_IO_OPT_CLASS:
+			if (sco_get_info(sock, &handle, dev_class) < 0) {
+				ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
+				return FALSE;
+			}
+			memcpy(va_arg(args, uint8_t *), dev_class, 3);
+			break;
+		default:
+			g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+					"Unknown option %d", opt);
+			return FALSE;
+		}
+
+		opt = va_arg(args, int);
+	}
+
+	return TRUE;
+}
+
+static gboolean get_valist(GIOChannel *io, BtIOType type, GError **err,
+						BtIOOption opt1, va_list args)
+{
+	int sock;
+
+	sock = g_io_channel_unix_get_fd(io);
+
+	switch (type) {
+	case BT_IO_L2RAW:
+	case BT_IO_L2CAP:
+		return l2cap_get(sock, err, opt1, args);
+	case BT_IO_RFCOMM:
+		return rfcomm_get(sock, err, opt1, args);
+	case BT_IO_SCO:
+		return sco_get(sock, err, opt1, args);
+	}
+
+	g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+			"Unknown BtIO type %d", type);
+	return FALSE;
+}
+
+gboolean bt_io_accept(GIOChannel *io, BtIOConnect connect, gpointer user_data,
+					GDestroyNotify destroy, GError **err)
+{
+	int sock;
+	char c;
+	struct pollfd pfd;
+
+	sock = g_io_channel_unix_get_fd(io);
+
+	memset(&pfd, 0, sizeof(pfd));
+	pfd.fd = sock;
+	pfd.events = POLLOUT;
+
+	if (poll(&pfd, 1, 0) < 0) {
+		ERROR_FAILED(err, "poll", errno);
+		return FALSE;
+	}
+
+	if (!(pfd.revents & POLLOUT)) {
+		int ret;
+		ret = read(sock, &c, 1);
+	}
+
+	accept_add(io, connect, user_data, destroy);
+
+	return TRUE;
+}
+
+gboolean bt_io_set(GIOChannel *io, BtIOType type, GError **err,
+							BtIOOption opt1, ...)
+{
+	va_list args;
+	gboolean ret;
+	struct set_opts opts;
+	int sock;
+
+	va_start(args, opt1);
+	ret = parse_set_opts(&opts, err, opt1, args);
+	va_end(args);
+
+	if (!ret)
+		return ret;
+
+	sock = g_io_channel_unix_get_fd(io);
+
+	switch (type) {
+	case BT_IO_L2RAW:
+	case BT_IO_L2CAP:
+		return l2cap_set(sock, opts.sec_level, opts.imtu, opts.omtu,
+						opts.mode, opts.master, err);
+	case BT_IO_RFCOMM:
+		return rfcomm_set(sock, opts.sec_level, opts.master, err);
+	case BT_IO_SCO:
+		return sco_set(sock, opts.mtu, err);
+	}
+
+	g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+			"Unknown BtIO type %d", type);
+	return FALSE;
+}
+
+gboolean bt_io_get(GIOChannel *io, BtIOType type, GError **err,
+							BtIOOption opt1, ...)
+{
+	va_list args;
+	gboolean ret;
+
+	va_start(args, opt1);
+	ret = get_valist(io, type, err, opt1, args);
+	va_end(args);
+
+	return ret;
+}
+
+static GIOChannel *create_io(BtIOType type, gboolean server,
+					struct set_opts *opts, GError **err)
+{
+	int sock;
+	GIOChannel *io;
+
+	switch (type) {
+	case BT_IO_L2RAW:
+		sock = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP);
+		if (sock < 0) {
+			ERROR_FAILED(err, "socket(RAW, L2CAP)", errno);
+			return NULL;
+		}
+		if (l2cap_bind(sock, &opts->src,
+					server ? opts->psm : 0, err) < 0)
+			goto failed;
+		if (!l2cap_set(sock, opts->sec_level, 0, 0, 0, -1, err))
+			goto failed;
+		break;
+	case BT_IO_L2CAP:
+		sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
+		if (sock < 0) {
+			ERROR_FAILED(err, "socket(SEQPACKET, L2CAP)", errno);
+			return NULL;
+		}
+		if (l2cap_bind(sock, &opts->src,
+					server ? opts->psm : 0, err) < 0)
+			goto failed;
+		if (!l2cap_set(sock, opts->sec_level, opts->imtu, opts->omtu,
+						opts->mode, opts->master, err))
+			goto failed;
+		break;
+	case BT_IO_RFCOMM:
+		sock = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
+		if (sock < 0) {
+			ERROR_FAILED(err, "socket(STREAM, RFCOMM)", errno);
+			return NULL;
+		}
+		if (rfcomm_bind(sock, &opts->src,
+					server ? opts->channel : 0, err) < 0)
+			goto failed;
+		if (!rfcomm_set(sock, opts->sec_level, opts->master, err))
+			goto failed;
+		break;
+	case BT_IO_SCO:
+		sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
+		if (sock < 0) {
+			ERROR_FAILED(err, "socket(SEQPACKET, SCO)", errno);
+			return NULL;
+		}
+		if (sco_bind(sock, &opts->src, err) < 0)
+			goto failed;
+		if (!sco_set(sock, opts->mtu, err))
+			goto failed;
+		break;
+	default:
+		g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+				"Unknown BtIO type %d", type);
+		return NULL;
+	}
+
+	io = g_io_channel_unix_new(sock);
+
+	g_io_channel_set_close_on_unref(io, TRUE);
+	g_io_channel_set_flags(io, G_IO_FLAG_NONBLOCK, NULL);
+
+	return io;
+
+failed:
+	close(sock);
+
+	return NULL;
+}
+
+GIOChannel *bt_io_connect(BtIOType type, BtIOConnect connect,
+				gpointer user_data, GDestroyNotify destroy,
+				GError **gerr, BtIOOption opt1, ...)
+{
+	GIOChannel *io;
+	va_list args;
+	struct set_opts opts;
+	int err, sock;
+	gboolean ret;
+
+	va_start(args, opt1);
+	ret = parse_set_opts(&opts, gerr, opt1, args);
+	va_end(args);
+
+	if (ret == FALSE)
+		return NULL;
+
+	io = create_io(type, FALSE, &opts, gerr);
+	if (io == NULL)
+		return NULL;
+
+	sock = g_io_channel_unix_get_fd(io);
+
+	switch (type) {
+	case BT_IO_L2RAW:
+		err = l2cap_connect(sock, &opts.dst, 0);
+		break;
+	case BT_IO_L2CAP:
+		err = l2cap_connect(sock, &opts.dst, opts.psm);
+		break;
+	case BT_IO_RFCOMM:
+		err = rfcomm_connect(sock, &opts.dst, opts.channel);
+		break;
+	case BT_IO_SCO:
+		err = sco_connect(sock, &opts.dst);
+		break;
+	default:
+		g_set_error(gerr, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+						"Unknown BtIO type %d", type);
+		return NULL;
+	}
+
+	if (err < 0) {
+		g_set_error(gerr, BT_IO_ERROR, BT_IO_ERROR_CONNECT_FAILED,
+				"connect: %s (%d)", strerror(-err), -err);
+		g_io_channel_unref(io);
+		return NULL;
+	}
+
+	connect_add(io, connect, user_data, destroy);
+
+	return io;
+}
+
+GIOChannel *bt_io_listen(BtIOType type, BtIOConnect connect,
+				BtIOConfirm confirm, gpointer user_data,
+				GDestroyNotify destroy, GError **err,
+				BtIOOption opt1, ...)
+{
+	GIOChannel *io;
+	va_list args;
+	struct set_opts opts;
+	int sock;
+	gboolean ret;
+
+	if (type == BT_IO_L2RAW) {
+		g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+				"Server L2CAP RAW sockets not supported");
+		return NULL;
+	}
+
+	va_start(args, opt1);
+	ret = parse_set_opts(&opts, err, opt1, args);
+	va_end(args);
+
+	if (ret == FALSE)
+		return NULL;
+
+	io = create_io(type, TRUE, &opts, err);
+	if (io == NULL)
+		return NULL;
+
+	sock = g_io_channel_unix_get_fd(io);
+
+	if (confirm)
+		setsockopt(sock, SOL_BLUETOOTH, BT_DEFER_SETUP, &opts.defer,
+							sizeof(opts.defer));
+
+	if (listen(sock, 5) < 0) {
+		ERROR_FAILED(err, "listen", errno);
+		g_io_channel_unref(io);
+		return NULL;
+	}
+
+	server_add(io, connect, confirm, user_data, destroy);
+
+	return io;
+}
+
+GQuark bt_io_error_quark(void)
+{
+	return g_quark_from_static_string("bt-io-error-quark");
+}
diff --git a/btio/btio.h b/btio/btio.h
new file mode 100644
index 0000000..81fda8e
--- /dev/null
+++ b/btio/btio.h
@@ -0,0 +1,94 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2009-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2009-2010  Nokia Corporation
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <glib.h>
+
+typedef enum {
+	BT_IO_ERROR_DISCONNECTED,
+	BT_IO_ERROR_CONNECT_FAILED,
+	BT_IO_ERROR_FAILED,
+	BT_IO_ERROR_INVALID_ARGS,
+} BtIOError;
+
+#define BT_IO_ERROR bt_io_error_quark()
+
+GQuark bt_io_error_quark(void);
+
+typedef enum {
+	BT_IO_L2RAW,
+	BT_IO_L2CAP,
+	BT_IO_RFCOMM,
+	BT_IO_SCO,
+} BtIOType;
+
+typedef enum {
+	BT_IO_OPT_INVALID = 0,
+	BT_IO_OPT_SOURCE,
+	BT_IO_OPT_SOURCE_BDADDR,
+	BT_IO_OPT_DEST,
+	BT_IO_OPT_DEST_BDADDR,
+	BT_IO_OPT_DEFER_TIMEOUT,
+	BT_IO_OPT_SEC_LEVEL,
+	BT_IO_OPT_CHANNEL,
+	BT_IO_OPT_SOURCE_CHANNEL,
+	BT_IO_OPT_DEST_CHANNEL,
+	BT_IO_OPT_PSM,
+	BT_IO_OPT_MTU,
+	BT_IO_OPT_OMTU,
+	BT_IO_OPT_IMTU,
+	BT_IO_OPT_MASTER,
+	BT_IO_OPT_HANDLE,
+	BT_IO_OPT_CLASS,
+	BT_IO_OPT_MODE,
+} BtIOOption;
+
+typedef enum {
+	BT_IO_SEC_SDP = 0,
+	BT_IO_SEC_LOW,
+	BT_IO_SEC_MEDIUM,
+	BT_IO_SEC_HIGH,
+} BtIOSecLevel;
+
+typedef void (*BtIOConfirm)(GIOChannel *io, gpointer user_data);
+
+typedef void (*BtIOConnect)(GIOChannel *io, GError *err, gpointer user_data);
+
+gboolean bt_io_accept(GIOChannel *io, BtIOConnect connect, gpointer user_data,
+					GDestroyNotify destroy, GError **err);
+
+gboolean bt_io_set(GIOChannel *io, BtIOType type, GError **err,
+						BtIOOption opt1, ...);
+
+gboolean bt_io_get(GIOChannel *io, BtIOType type, GError **err,
+						BtIOOption opt1, ...);
+
+GIOChannel *bt_io_connect(BtIOType type, BtIOConnect connect,
+				gpointer user_data, GDestroyNotify destroy,
+				GError **err, BtIOOption opt1, ...);
+
+GIOChannel *bt_io_listen(BtIOType type, BtIOConnect connect,
+				BtIOConfirm confirm, gpointer user_data,
+				GDestroyNotify destroy, GError **err,
+				BtIOOption opt1, ...);
+
diff --git a/src/btio.c b/src/btio.c
deleted file mode 100644
index 8b273ca..0000000
--- a/src/btio.c
+++ /dev/null
@@ -1,1299 +0,0 @@
-/*
- *
- *  BlueZ - Bluetooth protocol stack for Linux
- *
- *  Copyright (C) 2009-2010  Marcel Holtmann <marcel@holtmann.org>
- *  Copyright (C) 2009-2010  Nokia Corporation
- *
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- *
- */
-#include <stdarg.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <errno.h>
-#include <poll.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/l2cap.h>
-#include <bluetooth/rfcomm.h>
-#include <bluetooth/sco.h>
-#include <bluetooth/hci.h>
-#include <bluetooth/hci_lib.h>
-
-#include <glib.h>
-
-#include "btio.h"
-
-#define ERROR_FAILED(gerr, str, err) \
-		g_set_error(gerr, BT_IO_ERROR, BT_IO_ERROR_FAILED, \
-				str ": %s (%d)", strerror(err), err)
-
-#define DEFAULT_DEFER_TIMEOUT 30
-
-struct set_opts {
-	bdaddr_t src;
-	bdaddr_t dst;
-	int defer;
-	int sec_level;
-	uint8_t channel;
-	uint16_t psm;
-	uint16_t mtu;
-	uint16_t imtu;
-	uint16_t omtu;
-	int master;
-	uint8_t mode;
-};
-
-struct connect {
-	BtIOConnect connect;
-	gpointer user_data;
-	GDestroyNotify destroy;
-};
-
-struct accept {
-	BtIOConnect connect;
-	gpointer user_data;
-	GDestroyNotify destroy;
-};
-
-struct server {
-	BtIOConnect connect;
-	BtIOConfirm confirm;
-	gpointer user_data;
-	GDestroyNotify destroy;
-};
-
-static void server_remove(struct server *server)
-{
-	if (server->destroy)
-		server->destroy(server->user_data);
-	g_free(server);
-}
-
-static void connect_remove(struct connect *conn)
-{
-	if (conn->destroy)
-		conn->destroy(conn->user_data);
-	g_free(conn);
-}
-
-static void accept_remove(struct accept *accept)
-{
-	if (accept->destroy)
-		accept->destroy(accept->user_data);
-	g_free(accept);
-}
-
-static gboolean check_nval(GIOChannel *io)
-{
-	struct pollfd fds;
-
-	memset(&fds, 0, sizeof(fds));
-	fds.fd = g_io_channel_unix_get_fd(io);
-	fds.events = POLLNVAL;
-
-	if (poll(&fds, 1, 0) > 0 && (fds.revents & POLLNVAL))
-		return TRUE;
-
-	return FALSE;
-}
-
-static gboolean accept_cb(GIOChannel *io, GIOCondition cond,
-							gpointer user_data)
-{
-	struct accept *accept = user_data;
-	GError *err = NULL;
-
-	/* If the user aborted this accept attempt */
-	if ((cond & G_IO_NVAL) || check_nval(io))
-		return FALSE;
-
-	if (cond & (G_IO_HUP | G_IO_ERR))
-		g_set_error(&err, BT_IO_ERROR, BT_IO_ERROR_DISCONNECTED,
-				"HUP or ERR on socket");
-
-	accept->connect(io, err, accept->user_data);
-
-	g_clear_error(&err);
-
-	return FALSE;
-}
-
-static gboolean connect_cb(GIOChannel *io, GIOCondition cond,
-							gpointer user_data)
-{
-	struct connect *conn = user_data;
-	GError *gerr = NULL;
-
-	/* If the user aborted this connect attempt */
-	if ((cond & G_IO_NVAL) || check_nval(io))
-		return FALSE;
-
-	if (cond & G_IO_OUT) {
-		int err = 0, sock = g_io_channel_unix_get_fd(io);
-		socklen_t len = sizeof(err);
-
-		if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &len) < 0)
-			err = errno;
-
-		if (err)
-			g_set_error(&gerr, BT_IO_ERROR,
-					BT_IO_ERROR_CONNECT_FAILED, "%s (%d)",
-					strerror(err), err);
-	} else if (cond & (G_IO_HUP | G_IO_ERR))
-		g_set_error(&gerr, BT_IO_ERROR, BT_IO_ERROR_CONNECT_FAILED,
-				"HUP or ERR on socket");
-
-	conn->connect(io, gerr, conn->user_data);
-
-	if (gerr)
-		g_error_free(gerr);
-
-	return FALSE;
-}
-
-static gboolean server_cb(GIOChannel *io, GIOCondition cond,
-							gpointer user_data)
-{
-	struct server *server = user_data;
-	int srv_sock, cli_sock;
-	GIOChannel *cli_io;
-
-	/* If the user closed the server */
-	if ((cond & G_IO_NVAL) || check_nval(io))
-		return FALSE;
-
-	srv_sock = g_io_channel_unix_get_fd(io);
-
-	cli_sock = accept(srv_sock, NULL, NULL);
-	if (cli_sock < 0)
-		return TRUE;
-
-	cli_io = g_io_channel_unix_new(cli_sock);
-
-	g_io_channel_set_close_on_unref(cli_io, TRUE);
-	g_io_channel_set_flags(cli_io, G_IO_FLAG_NONBLOCK, NULL);
-
-	if (server->confirm)
-		server->confirm(cli_io, server->user_data);
-	else
-		server->connect(cli_io, NULL, server->user_data);
-
-	g_io_channel_unref(cli_io);
-
-	return TRUE;
-}
-
-static void server_add(GIOChannel *io, BtIOConnect connect,
-				BtIOConfirm confirm, gpointer user_data,
-				GDestroyNotify destroy)
-{
-	struct server *server;
-	GIOCondition cond;
-
-	server = g_new0(struct server, 1);
-	server->connect = connect;
-	server->confirm = confirm;
-	server->user_data = user_data;
-	server->destroy = destroy;
-
-	cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
-	g_io_add_watch_full(io, G_PRIORITY_DEFAULT, cond, server_cb, server,
-					(GDestroyNotify) server_remove);
-}
-
-static void connect_add(GIOChannel *io, BtIOConnect connect,
-				gpointer user_data, GDestroyNotify destroy)
-{
-	struct connect *conn;
-	GIOCondition cond;
-
-	conn = g_new0(struct connect, 1);
-	conn->connect = connect;
-	conn->user_data = user_data;
-	conn->destroy = destroy;
-
-	cond = G_IO_OUT | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
-	g_io_add_watch_full(io, G_PRIORITY_DEFAULT, cond, connect_cb, conn,
-					(GDestroyNotify) connect_remove);
-}
-
-static void accept_add(GIOChannel *io, BtIOConnect connect, gpointer user_data,
-							GDestroyNotify destroy)
-{
-	struct accept *accept;
-	GIOCondition cond;
-
-	accept = g_new0(struct accept, 1);
-	accept->connect = connect;
-	accept->user_data = user_data;
-	accept->destroy = destroy;
-
-	cond = G_IO_OUT | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
-	g_io_add_watch_full(io, G_PRIORITY_DEFAULT, cond, accept_cb, accept,
-					(GDestroyNotify) accept_remove);
-}
-
-static int l2cap_bind(int sock, const bdaddr_t *src, uint16_t psm, GError **err)
-{
-	struct sockaddr_l2 addr;
-
-	memset(&addr, 0, sizeof(addr));
-	addr.l2_family = AF_BLUETOOTH;
-	bacpy(&addr.l2_bdaddr, src);
-	addr.l2_psm = htobs(psm);
-
-	if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-		ERROR_FAILED(err, "l2cap_bind", errno);
-		return -1;
-	}
-
-	return 0;
-}
-
-static int l2cap_connect(int sock, const bdaddr_t *dst, uint16_t psm)
-{
-	int err;
-	struct sockaddr_l2 addr;
-
-	memset(&addr, 0, sizeof(addr));
-	addr.l2_family = AF_BLUETOOTH;
-	bacpy(&addr.l2_bdaddr, dst);
-	addr.l2_psm = htobs(psm);
-
-	err = connect(sock, (struct sockaddr *) &addr, sizeof(addr));
-	if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS))
-		return err;
-
-	return 0;
-}
-
-static int l2cap_set_master(int sock, int master)
-{
-	int flags;
-	socklen_t len;
-
-	len = sizeof(flags);
-	if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags, &len) < 0)
-		return -errno;
-
-	if (master) {
-		if (flags & L2CAP_LM_MASTER)
-			return 0;
-		flags |= L2CAP_LM_MASTER;
-	} else {
-		if (!(flags & L2CAP_LM_MASTER))
-			return 0;
-		flags &= ~L2CAP_LM_MASTER;
-	}
-
-	if (setsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags, sizeof(flags)) < 0)
-		return -errno;
-
-	return 0;
-}
-
-static int rfcomm_set_master(int sock, int master)
-{
-	int flags;
-	socklen_t len;
-
-	len = sizeof(flags);
-	if (getsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &flags, &len) < 0)
-		return -errno;
-
-	if (master) {
-		if (flags & RFCOMM_LM_MASTER)
-			return 0;
-		flags |= RFCOMM_LM_MASTER;
-	} else {
-		if (!(flags & RFCOMM_LM_MASTER))
-			return 0;
-		flags &= ~RFCOMM_LM_MASTER;
-	}
-
-	if (setsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &flags, sizeof(flags)) < 0)
-		return -errno;
-
-	return 0;
-}
-
-static int l2cap_set_lm(int sock, int level)
-{
-	int lm_map[] = {
-		0,
-		L2CAP_LM_AUTH,
-		L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT,
-		L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT | L2CAP_LM_SECURE,
-	}, opt = lm_map[level];
-
-	if (setsockopt(sock, SOL_L2CAP, L2CAP_LM, &opt, sizeof(opt)) < 0)
-		return -errno;
-
-	return 0;
-}
-
-static int rfcomm_set_lm(int sock, int level)
-{
-	int lm_map[] = {
-		0,
-		RFCOMM_LM_AUTH,
-		RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT,
-		RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT | RFCOMM_LM_SECURE,
-	}, opt = lm_map[level];
-
-	if (setsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &opt, sizeof(opt)) < 0)
-		return -errno;
-
-	return 0;
-}
-
-static gboolean set_sec_level(int sock, BtIOType type, int level, GError **err)
-{
-	struct bt_security sec;
-	int ret;
-
-	if (level < BT_SECURITY_LOW || level > BT_SECURITY_HIGH) {
-		g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-				"Valid security level range is %d-%d",
-				BT_SECURITY_LOW, BT_SECURITY_HIGH);
-		return FALSE;
-	}
-
-	memset(&sec, 0, sizeof(sec));
-	sec.level = level;
-
-	if (setsockopt(sock, SOL_BLUETOOTH, BT_SECURITY, &sec,
-							sizeof(sec)) == 0)
-		return TRUE;
-
-	if (errno != ENOPROTOOPT) {
-		ERROR_FAILED(err, "setsockopt(BT_SECURITY)", errno);
-		return FALSE;
-	}
-
-	if (type == BT_IO_L2CAP)
-		ret = l2cap_set_lm(sock, level);
-	else
-		ret = rfcomm_set_lm(sock, level);
-
-	if (ret < 0) {
-		ERROR_FAILED(err, "setsockopt(LM)", -ret);
-		return FALSE;
-	}
-
-	return TRUE;
-}
-
-static int l2cap_get_lm(int sock, int *sec_level)
-{
-	int opt;
-	socklen_t len;
-
-	len = sizeof(opt);
-	if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &opt, &len) < 0)
-		return -errno;
-
-	*sec_level = 0;
-
-	if (opt & L2CAP_LM_AUTH)
-		*sec_level = BT_SECURITY_LOW;
-	if (opt & L2CAP_LM_ENCRYPT)
-		*sec_level = BT_SECURITY_MEDIUM;
-	if (opt & L2CAP_LM_SECURE)
-		*sec_level = BT_SECURITY_HIGH;
-
-	return 0;
-}
-
-static int rfcomm_get_lm(int sock, int *sec_level)
-{
-	int opt;
-	socklen_t len;
-
-	len = sizeof(opt);
-	if (getsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &opt, &len) < 0)
-		return -errno;
-
-	*sec_level = 0;
-
-	if (opt & RFCOMM_LM_AUTH)
-		*sec_level = BT_SECURITY_LOW;
-	if (opt & RFCOMM_LM_ENCRYPT)
-		*sec_level = BT_SECURITY_MEDIUM;
-	if (opt & RFCOMM_LM_SECURE)
-		*sec_level = BT_SECURITY_HIGH;
-
-	return 0;
-}
-
-static gboolean get_sec_level(int sock, BtIOType type, int *level,
-								GError **err)
-{
-	struct bt_security sec;
-	socklen_t len;
-	int ret;
-
-	memset(&sec, 0, sizeof(sec));
-	len = sizeof(sec);
-	if (getsockopt(sock, SOL_BLUETOOTH, BT_SECURITY, &sec, &len) == 0) {
-		*level = sec.level;
-		return TRUE;
-	}
-
-	if (errno != ENOPROTOOPT) {
-		ERROR_FAILED(err, "getsockopt(BT_SECURITY)", errno);
-		return FALSE;
-	}
-
-	if (type == BT_IO_L2CAP)
-		ret = l2cap_get_lm(sock, level);
-	else
-		ret = rfcomm_get_lm(sock, level);
-
-	if (ret < 0) {
-		ERROR_FAILED(err, "getsockopt(LM)", -ret);
-		return FALSE;
-	}
-
-	return TRUE;
-}
-
-static gboolean l2cap_set(int sock, int sec_level, uint16_t imtu, uint16_t omtu,
-					uint8_t mode, int master, GError **err)
-{
-	if (imtu || omtu || mode) {
-		struct l2cap_options l2o;
-		socklen_t len;
-
-		memset(&l2o, 0, sizeof(l2o));
-		len = sizeof(l2o);
-		if (getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o,
-								&len) < 0) {
-			ERROR_FAILED(err, "getsockopt(L2CAP_OPTIONS)", errno);
-			return FALSE;
-		}
-
-		if (imtu)
-			l2o.imtu = imtu;
-		if (omtu)
-			l2o.omtu = omtu;
-		if (mode)
-			l2o.mode = mode;
-
-		if (setsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o,
-							sizeof(l2o)) < 0) {
-			ERROR_FAILED(err, "setsockopt(L2CAP_OPTIONS)", errno);
-			return FALSE;
-		}
-	}
-
-	if (master >= 0 && l2cap_set_master(sock, master) < 0) {
-		ERROR_FAILED(err, "l2cap_set_master", errno);
-		return FALSE;
-	}
-
-	if (sec_level && !set_sec_level(sock, BT_IO_L2CAP, sec_level, err))
-		return FALSE;
-
-	return TRUE;
-}
-
-static int rfcomm_bind(int sock,
-		const bdaddr_t *src, uint8_t channel, GError **err)
-{
-	struct sockaddr_rc addr;
-
-	memset(&addr, 0, sizeof(addr));
-	addr.rc_family = AF_BLUETOOTH;
-	bacpy(&addr.rc_bdaddr, src);
-	addr.rc_channel = channel;
-
-	if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-		ERROR_FAILED(err, "rfcomm_bind", errno);
-		return -1;
-	}
-
-	return 0;
-}
-
-static int rfcomm_connect(int sock, const bdaddr_t *dst, uint8_t channel)
-{
-	int err;
-	struct sockaddr_rc addr;
-
-	memset(&addr, 0, sizeof(addr));
-	addr.rc_family = AF_BLUETOOTH;
-	bacpy(&addr.rc_bdaddr, dst);
-	addr.rc_channel = channel;
-
-	err = connect(sock, (struct sockaddr *) &addr, sizeof(addr));
-	if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS))
-		return err;
-
-	return 0;
-}
-
-static gboolean rfcomm_set(int sock, int sec_level, int master, GError **err)
-{
-	if (sec_level && !set_sec_level(sock, BT_IO_RFCOMM, sec_level, err))
-		return FALSE;
-
-	if (master >= 0 && rfcomm_set_master(sock, master) < 0) {
-		ERROR_FAILED(err, "rfcomm_set_master", errno);
-		return FALSE;
-	}
-
-	return TRUE;
-}
-
-static int sco_bind(int sock, const bdaddr_t *src, GError **err)
-{
-	struct sockaddr_sco addr;
-
-	memset(&addr, 0, sizeof(addr));
-	addr.sco_family = AF_BLUETOOTH;
-	bacpy(&addr.sco_bdaddr, src);
-
-	if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-		ERROR_FAILED(err, "sco_bind", errno);
-		return -1;
-	}
-
-	return 0;
-}
-
-static int sco_connect(int sock, const bdaddr_t *dst)
-{
-	struct sockaddr_sco addr;
-	int err;
-
-	memset(&addr, 0, sizeof(addr));
-	addr.sco_family = AF_BLUETOOTH;
-	bacpy(&addr.sco_bdaddr, dst);
-
-	err = connect(sock, (struct sockaddr *) &addr, sizeof(addr));
-	if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS))
-		return err;
-
-	return 0;
-}
-
-static gboolean sco_set(int sock, uint16_t mtu, GError **err)
-{
-	struct sco_options sco_opt;
-	socklen_t len;
-
-	if (!mtu)
-		return TRUE;
-
-	len = sizeof(sco_opt);
-	memset(&sco_opt, 0, len);
-	if (getsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt, &len) < 0) {
-		ERROR_FAILED(err, "getsockopt(SCO_OPTIONS)", errno);
-		return FALSE;
-	}
-
-	sco_opt.mtu = mtu;
-	if (setsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt,
-						sizeof(sco_opt)) < 0) {
-		ERROR_FAILED(err, "setsockopt(SCO_OPTIONS)", errno);
-		return FALSE;
-	}
-
-	return TRUE;
-}
-
-static gboolean parse_set_opts(struct set_opts *opts, GError **err,
-						BtIOOption opt1, va_list args)
-{
-	BtIOOption opt = opt1;
-	const char *str;
-
-	memset(opts, 0, sizeof(*opts));
-
-	/* Set defaults */
-	opts->defer = DEFAULT_DEFER_TIMEOUT;
-	opts->master = -1;
-	opts->sec_level = BT_IO_SEC_MEDIUM;
-	opts->mode = L2CAP_MODE_BASIC;
-
-	while (opt != BT_IO_OPT_INVALID) {
-		switch (opt) {
-		case BT_IO_OPT_SOURCE:
-			str = va_arg(args, const char *);
-			if (strncasecmp(str, "hci", 3) == 0)
-				hci_devba(atoi(str + 3), &opts->src);
-			else
-				str2ba(str, &opts->src);
-			break;
-		case BT_IO_OPT_SOURCE_BDADDR:
-			bacpy(&opts->src, va_arg(args, const bdaddr_t *));
-			break;
-		case BT_IO_OPT_DEST:
-			str2ba(va_arg(args, const char *), &opts->dst);
-			break;
-		case BT_IO_OPT_DEST_BDADDR:
-			bacpy(&opts->dst, va_arg(args, const bdaddr_t *));
-			break;
-		case BT_IO_OPT_DEFER_TIMEOUT:
-			opts->defer = va_arg(args, int);
-			break;
-		case BT_IO_OPT_SEC_LEVEL:
-			opts->sec_level = va_arg(args, int);
-			break;
-		case BT_IO_OPT_CHANNEL:
-			opts->channel = va_arg(args, int);
-			break;
-		case BT_IO_OPT_PSM:
-			opts->psm = va_arg(args, int);
-			break;
-		case BT_IO_OPT_MTU:
-			opts->mtu = va_arg(args, int);
-			opts->imtu = opts->mtu;
-			opts->omtu = opts->mtu;
-			break;
-		case BT_IO_OPT_OMTU:
-			opts->omtu = va_arg(args, int);
-			if (!opts->mtu)
-				opts->mtu = opts->omtu;
-			break;
-		case BT_IO_OPT_IMTU:
-			opts->imtu = va_arg(args, int);
-			if (!opts->mtu)
-				opts->mtu = opts->imtu;
-			break;
-		case BT_IO_OPT_MASTER:
-			opts->master = va_arg(args, gboolean);
-			break;
-		case BT_IO_OPT_MODE:
-			opts->mode = va_arg(args, int);
-			break;
-		default:
-			g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-					"Unknown option %d", opt);
-			return FALSE;
-		}
-
-		opt = va_arg(args, int);
-	}
-
-	return TRUE;
-}
-
-static gboolean get_peers(int sock, struct sockaddr *src, struct sockaddr *dst,
-				socklen_t len, GError **err)
-{
-	socklen_t olen;
-
-	memset(src, 0, len);
-	olen = len;
-	if (getsockname(sock, src, &olen) < 0) {
-		ERROR_FAILED(err, "getsockname", errno);
-		return FALSE;
-	}
-
-	memset(dst, 0, len);
-	olen = len;
-	if (getpeername(sock, dst, &olen) < 0) {
-		ERROR_FAILED(err, "getpeername", errno);
-		return FALSE;
-	}
-
-	return TRUE;
-}
-
-static int l2cap_get_info(int sock, uint16_t *handle, uint8_t *dev_class)
-{
-	struct l2cap_conninfo info;
-	socklen_t len;
-
-	len = sizeof(info);
-	if (getsockopt(sock, SOL_L2CAP, L2CAP_CONNINFO, &info, &len) < 0)
-		return -errno;
-
-	if (handle)
-		*handle = info.hci_handle;
-
-	if (dev_class)
-		memcpy(dev_class, info.dev_class, 3);
-
-	return 0;
-}
-
-static gboolean l2cap_get(int sock, GError **err, BtIOOption opt1,
-								va_list args)
-{
-	BtIOOption opt = opt1;
-	struct sockaddr_l2 src, dst;
-	struct l2cap_options l2o;
-	int flags;
-	uint8_t dev_class[3];
-	uint16_t handle;
-	socklen_t len;
-
-	len = sizeof(l2o);
-	memset(&l2o, 0, len);
-	if (getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &len) < 0) {
-		ERROR_FAILED(err, "getsockopt(L2CAP_OPTIONS)", errno);
-		return FALSE;
-	}
-
-	if (!get_peers(sock, (struct sockaddr *) &src,
-				(struct sockaddr *) &dst, sizeof(src), err))
-		return FALSE;
-
-	while (opt != BT_IO_OPT_INVALID) {
-		switch (opt) {
-		case BT_IO_OPT_SOURCE:
-			ba2str(&src.l2_bdaddr, va_arg(args, char *));
-			break;
-		case BT_IO_OPT_SOURCE_BDADDR:
-			bacpy(va_arg(args, bdaddr_t *), &src.l2_bdaddr);
-			break;
-		case BT_IO_OPT_DEST:
-			ba2str(&dst.l2_bdaddr, va_arg(args, char *));
-			break;
-		case BT_IO_OPT_DEST_BDADDR:
-			bacpy(va_arg(args, bdaddr_t *), &dst.l2_bdaddr);
-			break;
-		case BT_IO_OPT_DEFER_TIMEOUT:
-			len = sizeof(int);
-			if (getsockopt(sock, SOL_BLUETOOTH, BT_DEFER_SETUP,
-					va_arg(args, int *), &len) < 0) {
-				ERROR_FAILED(err, "getsockopt(DEFER_SETUP)",
-									errno);
-				return FALSE;
-			}
-			break;
-		case BT_IO_OPT_SEC_LEVEL:
-			if (!get_sec_level(sock, BT_IO_L2CAP,
-						va_arg(args, int *), err))
-				return FALSE;
-			break;
-		case BT_IO_OPT_PSM:
-			*(va_arg(args, uint16_t *)) = src.l2_psm ?
-						src.l2_psm : dst.l2_psm;
-			break;
-		case BT_IO_OPT_OMTU:
-			*(va_arg(args, uint16_t *)) = l2o.omtu;
-			break;
-		case BT_IO_OPT_IMTU:
-			*(va_arg(args, uint16_t *)) = l2o.imtu;
-			break;
-		case BT_IO_OPT_MASTER:
-			len = sizeof(flags);
-			if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags,
-								&len) < 0) {
-				ERROR_FAILED(err, "getsockopt(L2CAP_LM)",
-									errno);
-				return FALSE;
-			}
-			*(va_arg(args, gboolean *)) =
-				(flags & L2CAP_LM_MASTER) ? TRUE : FALSE;
-			break;
-		case BT_IO_OPT_HANDLE:
-			if (l2cap_get_info(sock, &handle, dev_class) < 0) {
-				ERROR_FAILED(err, "L2CAP_CONNINFO", errno);
-				return FALSE;
-			}
-			*(va_arg(args, uint16_t *)) = handle;
-			break;
-		case BT_IO_OPT_CLASS:
-			if (l2cap_get_info(sock, &handle, dev_class) < 0) {
-				ERROR_FAILED(err, "L2CAP_CONNINFO", errno);
-				return FALSE;
-			}
-			memcpy(va_arg(args, uint8_t *), dev_class, 3);
-			break;
-		default:
-			g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-					"Unknown option %d", opt);
-			return FALSE;
-		}
-
-		opt = va_arg(args, int);
-	}
-
-	return TRUE;
-}
-
-static int rfcomm_get_info(int sock, uint16_t *handle, uint8_t *dev_class)
-{
-	struct rfcomm_conninfo info;
-	socklen_t len;
-
-	len = sizeof(info);
-	if (getsockopt(sock, SOL_RFCOMM, RFCOMM_CONNINFO, &info, &len) < 0)
-		return -errno;
-
-	if (handle)
-		*handle = info.hci_handle;
-
-	if (dev_class)
-		memcpy(dev_class, info.dev_class, 3);
-
-	return 0;
-}
-
-static gboolean rfcomm_get(int sock, GError **err, BtIOOption opt1,
-								va_list args)
-{
-	BtIOOption opt = opt1;
-	struct sockaddr_rc src, dst;
-	int flags;
-	socklen_t len;
-	uint8_t dev_class[3];
-	uint16_t handle;
-
-	if (!get_peers(sock, (struct sockaddr *) &src,
-				(struct sockaddr *) &dst, sizeof(src), err))
-		return FALSE;
-
-	while (opt != BT_IO_OPT_INVALID) {
-		switch (opt) {
-		case BT_IO_OPT_SOURCE:
-			ba2str(&src.rc_bdaddr, va_arg(args, char *));
-			break;
-		case BT_IO_OPT_SOURCE_BDADDR:
-			bacpy(va_arg(args, bdaddr_t *), &src.rc_bdaddr);
-			break;
-		case BT_IO_OPT_DEST:
-			ba2str(&dst.rc_bdaddr, va_arg(args, char *));
-			break;
-		case BT_IO_OPT_DEST_BDADDR:
-			bacpy(va_arg(args, bdaddr_t *), &dst.rc_bdaddr);
-			break;
-		case BT_IO_OPT_DEFER_TIMEOUT:
-			len = sizeof(int);
-			if (getsockopt(sock, SOL_BLUETOOTH, BT_DEFER_SETUP,
-					va_arg(args, int *), &len) < 0) {
-				ERROR_FAILED(err, "getsockopt(DEFER_SETUP)",
-									errno);
-				return FALSE;
-			}
-			break;
-		case BT_IO_OPT_SEC_LEVEL:
-			if (!get_sec_level(sock, BT_IO_RFCOMM,
-						va_arg(args, int *), err))
-				return FALSE;
-			break;
-		case BT_IO_OPT_CHANNEL:
-			*(va_arg(args, uint8_t *)) = src.rc_channel ?
-					src.rc_channel : dst.rc_channel;
-			break;
-		case BT_IO_OPT_SOURCE_CHANNEL:
-			*(va_arg(args, uint8_t *)) = src.rc_channel;
-			break;
-		case BT_IO_OPT_DEST_CHANNEL:
-			*(va_arg(args, uint8_t *)) = dst.rc_channel;
-			break;
-		case BT_IO_OPT_MASTER:
-			len = sizeof(flags);
-			if (getsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &flags,
-								&len) < 0) {
-				ERROR_FAILED(err, "getsockopt(RFCOMM_LM)",
-									errno);
-				return FALSE;
-			}
-			*(va_arg(args, gboolean *)) =
-				(flags & RFCOMM_LM_MASTER) ? TRUE : FALSE;
-			break;
-		case BT_IO_OPT_HANDLE:
-			if (rfcomm_get_info(sock, &handle, dev_class) < 0) {
-				ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
-				return FALSE;
-			}
-			*(va_arg(args, uint16_t *)) = handle;
-			break;
-		case BT_IO_OPT_CLASS:
-			if (rfcomm_get_info(sock, &handle, dev_class) < 0) {
-				ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
-				return FALSE;
-			}
-			memcpy(va_arg(args, uint8_t *), dev_class, 3);
-			break;
-		default:
-			g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-					"Unknown option %d", opt);
-			return FALSE;
-		}
-
-		opt = va_arg(args, int);
-	}
-
-	return TRUE;
-}
-
-static int sco_get_info(int sock, uint16_t *handle, uint8_t *dev_class)
-{
-	struct sco_conninfo info;
-	socklen_t len;
-
-	len = sizeof(info);
-	if (getsockopt(sock, SOL_SCO, SCO_CONNINFO, &info, &len) < 0)
-		return -errno;
-
-	if (handle)
-		*handle = info.hci_handle;
-
-	if (dev_class)
-		memcpy(dev_class, info.dev_class, 3);
-
-	return 0;
-}
-
-static gboolean sco_get(int sock, GError **err, BtIOOption opt1, va_list args)
-{
-	BtIOOption opt = opt1;
-	struct sockaddr_sco src, dst;
-	struct sco_options sco_opt;
-	socklen_t len;
-	uint8_t dev_class[3];
-	uint16_t handle;
-
-	len = sizeof(sco_opt);
-	memset(&sco_opt, 0, len);
-	if (getsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt, &len) < 0) {
-		ERROR_FAILED(err, "getsockopt(SCO_OPTIONS)", errno);
-		return FALSE;
-	}
-
-	if (!get_peers(sock, (struct sockaddr *) &src,
-				(struct sockaddr *) &dst, sizeof(src), err))
-		return FALSE;
-
-	while (opt != BT_IO_OPT_INVALID) {
-		switch (opt) {
-		case BT_IO_OPT_SOURCE:
-			ba2str(&src.sco_bdaddr, va_arg(args, char *));
-			break;
-		case BT_IO_OPT_SOURCE_BDADDR:
-			bacpy(va_arg(args, bdaddr_t *), &src.sco_bdaddr);
-			break;
-		case BT_IO_OPT_DEST:
-			ba2str(&dst.sco_bdaddr, va_arg(args, char *));
-			break;
-		case BT_IO_OPT_DEST_BDADDR:
-			bacpy(va_arg(args, bdaddr_t *), &dst.sco_bdaddr);
-			break;
-		case BT_IO_OPT_MTU:
-		case BT_IO_OPT_IMTU:
-		case BT_IO_OPT_OMTU:
-			*(va_arg(args, uint16_t *)) = sco_opt.mtu;
-			break;
-		case BT_IO_OPT_HANDLE:
-			if (sco_get_info(sock, &handle, dev_class) < 0) {
-				ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
-				return FALSE;
-			}
-			*(va_arg(args, uint16_t *)) = handle;
-			break;
-		case BT_IO_OPT_CLASS:
-			if (sco_get_info(sock, &handle, dev_class) < 0) {
-				ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
-				return FALSE;
-			}
-			memcpy(va_arg(args, uint8_t *), dev_class, 3);
-			break;
-		default:
-			g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-					"Unknown option %d", opt);
-			return FALSE;
-		}
-
-		opt = va_arg(args, int);
-	}
-
-	return TRUE;
-}
-
-static gboolean get_valist(GIOChannel *io, BtIOType type, GError **err,
-						BtIOOption opt1, va_list args)
-{
-	int sock;
-
-	sock = g_io_channel_unix_get_fd(io);
-
-	switch (type) {
-	case BT_IO_L2RAW:
-	case BT_IO_L2CAP:
-		return l2cap_get(sock, err, opt1, args);
-	case BT_IO_RFCOMM:
-		return rfcomm_get(sock, err, opt1, args);
-	case BT_IO_SCO:
-		return sco_get(sock, err, opt1, args);
-	}
-
-	g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-			"Unknown BtIO type %d", type);
-	return FALSE;
-}
-
-gboolean bt_io_accept(GIOChannel *io, BtIOConnect connect, gpointer user_data,
-					GDestroyNotify destroy, GError **err)
-{
-	int sock;
-	char c;
-	struct pollfd pfd;
-
-	sock = g_io_channel_unix_get_fd(io);
-
-	memset(&pfd, 0, sizeof(pfd));
-	pfd.fd = sock;
-	pfd.events = POLLOUT;
-
-	if (poll(&pfd, 1, 0) < 0) {
-		ERROR_FAILED(err, "poll", errno);
-		return FALSE;
-	}
-
-	if (!(pfd.revents & POLLOUT)) {
-		int ret;
-		ret = read(sock, &c, 1);
-	}
-
-	accept_add(io, connect, user_data, destroy);
-
-	return TRUE;
-}
-
-gboolean bt_io_set(GIOChannel *io, BtIOType type, GError **err,
-							BtIOOption opt1, ...)
-{
-	va_list args;
-	gboolean ret;
-	struct set_opts opts;
-	int sock;
-
-	va_start(args, opt1);
-	ret = parse_set_opts(&opts, err, opt1, args);
-	va_end(args);
-
-	if (!ret)
-		return ret;
-
-	sock = g_io_channel_unix_get_fd(io);
-
-	switch (type) {
-	case BT_IO_L2RAW:
-	case BT_IO_L2CAP:
-		return l2cap_set(sock, opts.sec_level, opts.imtu, opts.omtu,
-						opts.mode, opts.master, err);
-	case BT_IO_RFCOMM:
-		return rfcomm_set(sock, opts.sec_level, opts.master, err);
-	case BT_IO_SCO:
-		return sco_set(sock, opts.mtu, err);
-	}
-
-	g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-			"Unknown BtIO type %d", type);
-	return FALSE;
-}
-
-gboolean bt_io_get(GIOChannel *io, BtIOType type, GError **err,
-							BtIOOption opt1, ...)
-{
-	va_list args;
-	gboolean ret;
-
-	va_start(args, opt1);
-	ret = get_valist(io, type, err, opt1, args);
-	va_end(args);
-
-	return ret;
-}
-
-static GIOChannel *create_io(BtIOType type, gboolean server,
-					struct set_opts *opts, GError **err)
-{
-	int sock;
-	GIOChannel *io;
-
-	switch (type) {
-	case BT_IO_L2RAW:
-		sock = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP);
-		if (sock < 0) {
-			ERROR_FAILED(err, "socket(RAW, L2CAP)", errno);
-			return NULL;
-		}
-		if (l2cap_bind(sock, &opts->src,
-					server ? opts->psm : 0, err) < 0)
-			goto failed;
-		if (!l2cap_set(sock, opts->sec_level, 0, 0, 0, -1, err))
-			goto failed;
-		break;
-	case BT_IO_L2CAP:
-		sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
-		if (sock < 0) {
-			ERROR_FAILED(err, "socket(SEQPACKET, L2CAP)", errno);
-			return NULL;
-		}
-		if (l2cap_bind(sock, &opts->src,
-					server ? opts->psm : 0, err) < 0)
-			goto failed;
-		if (!l2cap_set(sock, opts->sec_level, opts->imtu, opts->omtu,
-						opts->mode, opts->master, err))
-			goto failed;
-		break;
-	case BT_IO_RFCOMM:
-		sock = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
-		if (sock < 0) {
-			ERROR_FAILED(err, "socket(STREAM, RFCOMM)", errno);
-			return NULL;
-		}
-		if (rfcomm_bind(sock, &opts->src,
-					server ? opts->channel : 0, err) < 0)
-			goto failed;
-		if (!rfcomm_set(sock, opts->sec_level, opts->master, err))
-			goto failed;
-		break;
-	case BT_IO_SCO:
-		sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
-		if (sock < 0) {
-			ERROR_FAILED(err, "socket(SEQPACKET, SCO)", errno);
-			return NULL;
-		}
-		if (sco_bind(sock, &opts->src, err) < 0)
-			goto failed;
-		if (!sco_set(sock, opts->mtu, err))
-			goto failed;
-		break;
-	default:
-		g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-				"Unknown BtIO type %d", type);
-		return NULL;
-	}
-
-	io = g_io_channel_unix_new(sock);
-
-	g_io_channel_set_close_on_unref(io, TRUE);
-	g_io_channel_set_flags(io, G_IO_FLAG_NONBLOCK, NULL);
-
-	return io;
-
-failed:
-	close(sock);
-
-	return NULL;
-}
-
-GIOChannel *bt_io_connect(BtIOType type, BtIOConnect connect,
-				gpointer user_data, GDestroyNotify destroy,
-				GError **gerr, BtIOOption opt1, ...)
-{
-	GIOChannel *io;
-	va_list args;
-	struct set_opts opts;
-	int err, sock;
-	gboolean ret;
-
-	va_start(args, opt1);
-	ret = parse_set_opts(&opts, gerr, opt1, args);
-	va_end(args);
-
-	if (ret == FALSE)
-		return NULL;
-
-	io = create_io(type, FALSE, &opts, gerr);
-	if (io == NULL)
-		return NULL;
-
-	sock = g_io_channel_unix_get_fd(io);
-
-	switch (type) {
-	case BT_IO_L2RAW:
-		err = l2cap_connect(sock, &opts.dst, 0);
-		break;
-	case BT_IO_L2CAP:
-		err = l2cap_connect(sock, &opts.dst, opts.psm);
-		break;
-	case BT_IO_RFCOMM:
-		err = rfcomm_connect(sock, &opts.dst, opts.channel);
-		break;
-	case BT_IO_SCO:
-		err = sco_connect(sock, &opts.dst);
-		break;
-	default:
-		g_set_error(gerr, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-						"Unknown BtIO type %d", type);
-		return NULL;
-	}
-
-	if (err < 0) {
-		g_set_error(gerr, BT_IO_ERROR, BT_IO_ERROR_CONNECT_FAILED,
-				"connect: %s (%d)", strerror(-err), -err);
-		g_io_channel_unref(io);
-		return NULL;
-	}
-
-	connect_add(io, connect, user_data, destroy);
-
-	return io;
-}
-
-GIOChannel *bt_io_listen(BtIOType type, BtIOConnect connect,
-				BtIOConfirm confirm, gpointer user_data,
-				GDestroyNotify destroy, GError **err,
-				BtIOOption opt1, ...)
-{
-	GIOChannel *io;
-	va_list args;
-	struct set_opts opts;
-	int sock;
-	gboolean ret;
-
-	if (type == BT_IO_L2RAW) {
-		g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-				"Server L2CAP RAW sockets not supported");
-		return NULL;
-	}
-
-	va_start(args, opt1);
-	ret = parse_set_opts(&opts, err, opt1, args);
-	va_end(args);
-
-	if (ret == FALSE)
-		return NULL;
-
-	io = create_io(type, TRUE, &opts, err);
-	if (io == NULL)
-		return NULL;
-
-	sock = g_io_channel_unix_get_fd(io);
-
-	if (confirm)
-		setsockopt(sock, SOL_BLUETOOTH, BT_DEFER_SETUP, &opts.defer,
-							sizeof(opts.defer));
-
-	if (listen(sock, 5) < 0) {
-		ERROR_FAILED(err, "listen", errno);
-		g_io_channel_unref(io);
-		return NULL;
-	}
-
-	server_add(io, connect, confirm, user_data, destroy);
-
-	return io;
-}
-
-GQuark bt_io_error_quark(void)
-{
-	return g_quark_from_static_string("bt-io-error-quark");
-}
diff --git a/src/btio.h b/src/btio.h
deleted file mode 100644
index 81fda8e..0000000
--- a/src/btio.h
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- *
- *  BlueZ - Bluetooth protocol stack for Linux
- *
- *  Copyright (C) 2009-2010  Marcel Holtmann <marcel@holtmann.org>
- *  Copyright (C) 2009-2010  Nokia Corporation
- *
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- *
- */
-
-#include <glib.h>
-
-typedef enum {
-	BT_IO_ERROR_DISCONNECTED,
-	BT_IO_ERROR_CONNECT_FAILED,
-	BT_IO_ERROR_FAILED,
-	BT_IO_ERROR_INVALID_ARGS,
-} BtIOError;
-
-#define BT_IO_ERROR bt_io_error_quark()
-
-GQuark bt_io_error_quark(void);
-
-typedef enum {
-	BT_IO_L2RAW,
-	BT_IO_L2CAP,
-	BT_IO_RFCOMM,
-	BT_IO_SCO,
-} BtIOType;
-
-typedef enum {
-	BT_IO_OPT_INVALID = 0,
-	BT_IO_OPT_SOURCE,
-	BT_IO_OPT_SOURCE_BDADDR,
-	BT_IO_OPT_DEST,
-	BT_IO_OPT_DEST_BDADDR,
-	BT_IO_OPT_DEFER_TIMEOUT,
-	BT_IO_OPT_SEC_LEVEL,
-	BT_IO_OPT_CHANNEL,
-	BT_IO_OPT_SOURCE_CHANNEL,
-	BT_IO_OPT_DEST_CHANNEL,
-	BT_IO_OPT_PSM,
-	BT_IO_OPT_MTU,
-	BT_IO_OPT_OMTU,
-	BT_IO_OPT_IMTU,
-	BT_IO_OPT_MASTER,
-	BT_IO_OPT_HANDLE,
-	BT_IO_OPT_CLASS,
-	BT_IO_OPT_MODE,
-} BtIOOption;
-
-typedef enum {
-	BT_IO_SEC_SDP = 0,
-	BT_IO_SEC_LOW,
-	BT_IO_SEC_MEDIUM,
-	BT_IO_SEC_HIGH,
-} BtIOSecLevel;
-
-typedef void (*BtIOConfirm)(GIOChannel *io, gpointer user_data);
-
-typedef void (*BtIOConnect)(GIOChannel *io, GError *err, gpointer user_data);
-
-gboolean bt_io_accept(GIOChannel *io, BtIOConnect connect, gpointer user_data,
-					GDestroyNotify destroy, GError **err);
-
-gboolean bt_io_set(GIOChannel *io, BtIOType type, GError **err,
-						BtIOOption opt1, ...);
-
-gboolean bt_io_get(GIOChannel *io, BtIOType type, GError **err,
-						BtIOOption opt1, ...);
-
-GIOChannel *bt_io_connect(BtIOType type, BtIOConnect connect,
-				gpointer user_data, GDestroyNotify destroy,
-				GError **err, BtIOOption opt1, ...);
-
-GIOChannel *bt_io_listen(BtIOType type, BtIOConnect connect,
-				BtIOConfirm confirm, gpointer user_data,
-				GDestroyNotify destroy, GError **err,
-				BtIOOption opt1, ...);
-
-- 
1.7.0.4


^ permalink raw reply related

* [PATCH 1/3] btio: Remove blank line at EOF
From: Zhenhua Zhang @ 2010-09-10  2:49 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1284086984-17072-1-git-send-email-zhenhua.zhang@intel.com>

---
 src/btio.c |    1 -
 1 files changed, 0 insertions(+), 1 deletions(-)

diff --git a/src/btio.c b/src/btio.c
index 93079ae..8b273ca 100644
--- a/src/btio.c
+++ b/src/btio.c
@@ -1297,4 +1297,3 @@ GQuark bt_io_error_quark(void)
 {
 	return g_quark_from_static_string("bt-io-error-quark");
 }
-
-- 
1.7.0.4


^ permalink raw reply related

* [PATCH 0/3] bluez: isolate btio.[ch] and do some cleanup
From: Zhenhua Zhang @ 2010-09-10  2:49 UTC (permalink / raw)
  To: linux-bluetooth

Hi,

Similar with previous patch threads. These patches move bluez/src/btio.[ch] out from source directory and make it identical with obex's btio.[ch]. So we can freely copy them among bluez, obex and ofono.

Best Regards,
Zhenhua


^ permalink raw reply

* [PATCH 3/3] btio: Replace void * with gpointer
From: Zhenhua Zhang @ 2010-09-10  2:47 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1284086847-16429-1-git-send-email-zhenhua.zhang@intel.com>

TO make it consistent with bluez btio.c.
---
 btio/btio.c |   24 ++++++++++++------------
 btio/btio.h |   10 +++++-----
 2 files changed, 17 insertions(+), 17 deletions(-)

diff --git a/btio/btio.c b/btio/btio.c
index 42a3bcd..8b273ca 100644
--- a/btio/btio.c
+++ b/btio/btio.c
@@ -62,20 +62,20 @@ struct set_opts {
 
 struct connect {
 	BtIOConnect connect;
-	void *user_data;
+	gpointer user_data;
 	GDestroyNotify destroy;
 };
 
 struct accept {
 	BtIOConnect connect;
-	void *user_data;
+	gpointer user_data;
 	GDestroyNotify destroy;
 };
 
 struct server {
 	BtIOConnect connect;
 	BtIOConfirm confirm;
-	void *user_data;
+	gpointer user_data;
 	GDestroyNotify destroy;
 };
 
@@ -115,7 +115,7 @@ static gboolean check_nval(GIOChannel *io)
 }
 
 static gboolean accept_cb(GIOChannel *io, GIOCondition cond,
-							void *user_data)
+							gpointer user_data)
 {
 	struct accept *accept = user_data;
 	GError *err = NULL;
@@ -136,7 +136,7 @@ static gboolean accept_cb(GIOChannel *io, GIOCondition cond,
 }
 
 static gboolean connect_cb(GIOChannel *io, GIOCondition cond,
-							void *user_data)
+							gpointer user_data)
 {
 	struct connect *conn = user_data;
 	GError *gerr = NULL;
@@ -169,7 +169,7 @@ static gboolean connect_cb(GIOChannel *io, GIOCondition cond,
 }
 
 static gboolean server_cb(GIOChannel *io, GIOCondition cond,
-							void *user_data)
+							gpointer user_data)
 {
 	struct server *server = user_data;
 	int srv_sock, cli_sock;
@@ -201,7 +201,7 @@ static gboolean server_cb(GIOChannel *io, GIOCondition cond,
 }
 
 static void server_add(GIOChannel *io, BtIOConnect connect,
-				BtIOConfirm confirm, void *user_data,
+				BtIOConfirm confirm, gpointer user_data,
 				GDestroyNotify destroy)
 {
 	struct server *server;
@@ -219,7 +219,7 @@ static void server_add(GIOChannel *io, BtIOConnect connect,
 }
 
 static void connect_add(GIOChannel *io, BtIOConnect connect,
-				void *user_data, GDestroyNotify destroy)
+				gpointer user_data, GDestroyNotify destroy)
 {
 	struct connect *conn;
 	GIOCondition cond;
@@ -234,7 +234,7 @@ static void connect_add(GIOChannel *io, BtIOConnect connect,
 					(GDestroyNotify) connect_remove);
 }
 
-static void accept_add(GIOChannel *io, BtIOConnect connect, void *user_data,
+static void accept_add(GIOChannel *io, BtIOConnect connect, gpointer user_data,
 							GDestroyNotify destroy)
 {
 	struct accept *accept;
@@ -1046,7 +1046,7 @@ static gboolean get_valist(GIOChannel *io, BtIOType type, GError **err,
 	return FALSE;
 }
 
-gboolean bt_io_accept(GIOChannel *io, BtIOConnect connect, void *user_data,
+gboolean bt_io_accept(GIOChannel *io, BtIOConnect connect, gpointer user_data,
 					GDestroyNotify destroy, GError **err)
 {
 	int sock;
@@ -1195,7 +1195,7 @@ failed:
 }
 
 GIOChannel *bt_io_connect(BtIOType type, BtIOConnect connect,
-				void *user_data, GDestroyNotify destroy,
+				gpointer user_data, GDestroyNotify destroy,
 				GError **gerr, BtIOOption opt1, ...)
 {
 	GIOChannel *io;
@@ -1249,7 +1249,7 @@ GIOChannel *bt_io_connect(BtIOType type, BtIOConnect connect,
 }
 
 GIOChannel *bt_io_listen(BtIOType type, BtIOConnect connect,
-				BtIOConfirm confirm, void *user_data,
+				BtIOConfirm confirm, gpointer user_data,
 				GDestroyNotify destroy, GError **err,
 				BtIOOption opt1, ...)
 {
diff --git a/btio/btio.h b/btio/btio.h
index e9dcc9f..d373ed1 100644
--- a/btio/btio.h
+++ b/btio/btio.h
@@ -72,11 +72,11 @@ typedef enum {
 	BT_IO_SEC_HIGH,
 } BtIOSecLevel;
 
-typedef void (*BtIOConfirm)(GIOChannel *io, void *user_data);
+typedef void (*BtIOConfirm)(GIOChannel *io, gpointer user_data);
 
-typedef void (*BtIOConnect)(GIOChannel *io, GError *err, void *user_data);
+typedef void (*BtIOConnect)(GIOChannel *io, GError *err, gpointer user_data);
 
-gboolean bt_io_accept(GIOChannel *io, BtIOConnect connect, void *user_data,
+gboolean bt_io_accept(GIOChannel *io, BtIOConnect connect, gpointer user_data,
 					GDestroyNotify destroy, GError **err);
 
 gboolean bt_io_set(GIOChannel *io, BtIOType type, GError **err,
@@ -86,11 +86,11 @@ gboolean bt_io_get(GIOChannel *io, BtIOType type, GError **err,
 						BtIOOption opt1, ...);
 
 GIOChannel *bt_io_connect(BtIOType type, BtIOConnect connect,
-				void *user_data, GDestroyNotify destroy,
+				gpointer user_data, GDestroyNotify destroy,
 				GError **err, BtIOOption opt1, ...);
 
 GIOChannel *bt_io_listen(BtIOType type, BtIOConnect connect,
-				BtIOConfirm confirm, void *user_data,
+				BtIOConfirm confirm, gpointer user_data,
 				GDestroyNotify destroy, GError **err,
 				BtIOOption opt1, ...);
 
-- 
1.7.0.4


^ permalink raw reply related

* [PATCH 2/3] btio: Seperate btio.[ch] into btio directory
From: Zhenhua Zhang @ 2010-09-10  2:47 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1284086847-16429-1-git-send-email-zhenhua.zhang@intel.com>

Seperate btio.[ch] from src directory to btio sub-folder.
---
 Makefile.am |   19 +-
 btio/btio.c | 1299 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 btio/btio.h |   97 +++++
 src/btio.c  | 1299 -----------------------------------------------------------
 src/btio.h  |   97 -----
 5 files changed, 1406 insertions(+), 1405 deletions(-)
 create mode 100644 btio/btio.c
 create mode 100644 btio/btio.h
 delete mode 100644 src/btio.c
 delete mode 100644 src/btio.h

diff --git a/Makefile.am b/Makefile.am
index e6940bc..175747c 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -19,6 +19,8 @@ gwobex_sources = gwobex/gw-obex.h gwobex/gw-obex.c \
 			gwobex/obex-xfer.h gwobex/obex-xfer.c \
 			gwobex/utils.h gwobex/utils.c gwobex/log.h
 
+btio_sources = btio/btio.h btio/btio.c
+
 libexec_PROGRAMS =
 
 if SERVER
@@ -66,11 +68,10 @@ builtin_nodist += plugins/phonebook.c
 
 libexec_PROGRAMS += src/obexd
 
-src_obexd_SOURCES = $(gdbus_sources) $(builtin_sources) \
+src_obexd_SOURCES = $(gdbus_sources) $(builtin_sources) $(btio_sources) \
 			src/main.c src/obexd.h src/plugin.h src/plugin.c \
-			src/log.h src/log.c src/btio.h src/btio.c \
-			src/dbus.h src/manager.c src/obex.h src/obex.c \
-			src/obex-priv.h \
+			src/log.h src/log.c src/dbus.h src/manager.c \
+			src/obex.h src/obex.c src/obex-priv.h \
 			src/mimetype.h src/mimetype.c \
 			src/service.h src/service.c \
 			src/transport.h src/transport.c \
@@ -108,13 +109,12 @@ service_in_files += client/obex-client.service.in
 
 libexec_PROGRAMS += client/obex-client
 
-client_obex_client_SOURCES = $(gdbus_sources) $(gwobex_sources) client/main.c \
-				client/session.h client/session.c \
+client_obex_client_SOURCES = $(gdbus_sources) $(gwobex_sources) $(btio_sources) \
+				client/main.c client/session.h client/session.c \
 				src/log.h src/log.c \
 				client/pbap.h client/pbap.c \
 				client/sync.h client/sync.c \
-				client/transfer.h client/transfer.c \
-				src/btio.c src/btio.h
+				client/transfer.h client/transfer.c
 
 client_obex_client_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ @OPENOBEX_LIBS@ @BLUEZ_LIBS@
 endif
@@ -127,7 +127,8 @@ AM_CFLAGS = @OPENOBEX_CFLAGS@ @BLUEZ_CFLAGS@ @EBOOK_CFLAGS@ \
 			-DOBEX_PLUGIN_BUILTIN -DPLUGINDIR=\""$(plugindir)"\"
 
 INCLUDES = -I$(builddir)/src -I$(srcdir)/src -I$(srcdir)/plugins \
-				-I$(srcdir)/gdbus -I$(srcdir)/gwobex
+				-I$(srcdir)/gdbus -I$(srcdir)/gwobex \
+				-I$(srcdir)/btio
 
 CLEANFILES = $(service_DATA) $(builtin_files)
 
diff --git a/btio/btio.c b/btio/btio.c
new file mode 100644
index 0000000..42a3bcd
--- /dev/null
+++ b/btio/btio.c
@@ -0,0 +1,1299 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2009-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2009-2010  Nokia Corporation
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+#include <stdarg.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <poll.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/l2cap.h>
+#include <bluetooth/rfcomm.h>
+#include <bluetooth/sco.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#include <glib.h>
+
+#include "btio.h"
+
+#define ERROR_FAILED(gerr, str, err) \
+		g_set_error(gerr, BT_IO_ERROR, BT_IO_ERROR_FAILED, \
+				str ": %s (%d)", strerror(err), err)
+
+#define DEFAULT_DEFER_TIMEOUT 30
+
+struct set_opts {
+	bdaddr_t src;
+	bdaddr_t dst;
+	int defer;
+	int sec_level;
+	uint8_t channel;
+	uint16_t psm;
+	uint16_t mtu;
+	uint16_t imtu;
+	uint16_t omtu;
+	int master;
+	uint8_t mode;
+};
+
+struct connect {
+	BtIOConnect connect;
+	void *user_data;
+	GDestroyNotify destroy;
+};
+
+struct accept {
+	BtIOConnect connect;
+	void *user_data;
+	GDestroyNotify destroy;
+};
+
+struct server {
+	BtIOConnect connect;
+	BtIOConfirm confirm;
+	void *user_data;
+	GDestroyNotify destroy;
+};
+
+static void server_remove(struct server *server)
+{
+	if (server->destroy)
+		server->destroy(server->user_data);
+	g_free(server);
+}
+
+static void connect_remove(struct connect *conn)
+{
+	if (conn->destroy)
+		conn->destroy(conn->user_data);
+	g_free(conn);
+}
+
+static void accept_remove(struct accept *accept)
+{
+	if (accept->destroy)
+		accept->destroy(accept->user_data);
+	g_free(accept);
+}
+
+static gboolean check_nval(GIOChannel *io)
+{
+	struct pollfd fds;
+
+	memset(&fds, 0, sizeof(fds));
+	fds.fd = g_io_channel_unix_get_fd(io);
+	fds.events = POLLNVAL;
+
+	if (poll(&fds, 1, 0) > 0 && (fds.revents & POLLNVAL))
+		return TRUE;
+
+	return FALSE;
+}
+
+static gboolean accept_cb(GIOChannel *io, GIOCondition cond,
+							void *user_data)
+{
+	struct accept *accept = user_data;
+	GError *err = NULL;
+
+	/* If the user aborted this accept attempt */
+	if ((cond & G_IO_NVAL) || check_nval(io))
+		return FALSE;
+
+	if (cond & (G_IO_HUP | G_IO_ERR))
+		g_set_error(&err, BT_IO_ERROR, BT_IO_ERROR_DISCONNECTED,
+				"HUP or ERR on socket");
+
+	accept->connect(io, err, accept->user_data);
+
+	g_clear_error(&err);
+
+	return FALSE;
+}
+
+static gboolean connect_cb(GIOChannel *io, GIOCondition cond,
+							void *user_data)
+{
+	struct connect *conn = user_data;
+	GError *gerr = NULL;
+
+	/* If the user aborted this connect attempt */
+	if ((cond & G_IO_NVAL) || check_nval(io))
+		return FALSE;
+
+	if (cond & G_IO_OUT) {
+		int err = 0, sock = g_io_channel_unix_get_fd(io);
+		socklen_t len = sizeof(err);
+
+		if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &len) < 0)
+			err = errno;
+
+		if (err)
+			g_set_error(&gerr, BT_IO_ERROR,
+					BT_IO_ERROR_CONNECT_FAILED, "%s (%d)",
+					strerror(err), err);
+	} else if (cond & (G_IO_HUP | G_IO_ERR))
+		g_set_error(&gerr, BT_IO_ERROR, BT_IO_ERROR_CONNECT_FAILED,
+				"HUP or ERR on socket");
+
+	conn->connect(io, gerr, conn->user_data);
+
+	if (gerr)
+		g_error_free(gerr);
+
+	return FALSE;
+}
+
+static gboolean server_cb(GIOChannel *io, GIOCondition cond,
+							void *user_data)
+{
+	struct server *server = user_data;
+	int srv_sock, cli_sock;
+	GIOChannel *cli_io;
+
+	/* If the user closed the server */
+	if ((cond & G_IO_NVAL) || check_nval(io))
+		return FALSE;
+
+	srv_sock = g_io_channel_unix_get_fd(io);
+
+	cli_sock = accept(srv_sock, NULL, NULL);
+	if (cli_sock < 0)
+		return TRUE;
+
+	cli_io = g_io_channel_unix_new(cli_sock);
+
+	g_io_channel_set_close_on_unref(cli_io, TRUE);
+	g_io_channel_set_flags(cli_io, G_IO_FLAG_NONBLOCK, NULL);
+
+	if (server->confirm)
+		server->confirm(cli_io, server->user_data);
+	else
+		server->connect(cli_io, NULL, server->user_data);
+
+	g_io_channel_unref(cli_io);
+
+	return TRUE;
+}
+
+static void server_add(GIOChannel *io, BtIOConnect connect,
+				BtIOConfirm confirm, void *user_data,
+				GDestroyNotify destroy)
+{
+	struct server *server;
+	GIOCondition cond;
+
+	server = g_new0(struct server, 1);
+	server->connect = connect;
+	server->confirm = confirm;
+	server->user_data = user_data;
+	server->destroy = destroy;
+
+	cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+	g_io_add_watch_full(io, G_PRIORITY_DEFAULT, cond, server_cb, server,
+					(GDestroyNotify) server_remove);
+}
+
+static void connect_add(GIOChannel *io, BtIOConnect connect,
+				void *user_data, GDestroyNotify destroy)
+{
+	struct connect *conn;
+	GIOCondition cond;
+
+	conn = g_new0(struct connect, 1);
+	conn->connect = connect;
+	conn->user_data = user_data;
+	conn->destroy = destroy;
+
+	cond = G_IO_OUT | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+	g_io_add_watch_full(io, G_PRIORITY_DEFAULT, cond, connect_cb, conn,
+					(GDestroyNotify) connect_remove);
+}
+
+static void accept_add(GIOChannel *io, BtIOConnect connect, void *user_data,
+							GDestroyNotify destroy)
+{
+	struct accept *accept;
+	GIOCondition cond;
+
+	accept = g_new0(struct accept, 1);
+	accept->connect = connect;
+	accept->user_data = user_data;
+	accept->destroy = destroy;
+
+	cond = G_IO_OUT | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+	g_io_add_watch_full(io, G_PRIORITY_DEFAULT, cond, accept_cb, accept,
+					(GDestroyNotify) accept_remove);
+}
+
+static int l2cap_bind(int sock, const bdaddr_t *src, uint16_t psm, GError **err)
+{
+	struct sockaddr_l2 addr;
+
+	memset(&addr, 0, sizeof(addr));
+	addr.l2_family = AF_BLUETOOTH;
+	bacpy(&addr.l2_bdaddr, src);
+	addr.l2_psm = htobs(psm);
+
+	if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		ERROR_FAILED(err, "l2cap_bind", errno);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int l2cap_connect(int sock, const bdaddr_t *dst, uint16_t psm)
+{
+	int err;
+	struct sockaddr_l2 addr;
+
+	memset(&addr, 0, sizeof(addr));
+	addr.l2_family = AF_BLUETOOTH;
+	bacpy(&addr.l2_bdaddr, dst);
+	addr.l2_psm = htobs(psm);
+
+	err = connect(sock, (struct sockaddr *) &addr, sizeof(addr));
+	if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS))
+		return err;
+
+	return 0;
+}
+
+static int l2cap_set_master(int sock, int master)
+{
+	int flags;
+	socklen_t len;
+
+	len = sizeof(flags);
+	if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags, &len) < 0)
+		return -errno;
+
+	if (master) {
+		if (flags & L2CAP_LM_MASTER)
+			return 0;
+		flags |= L2CAP_LM_MASTER;
+	} else {
+		if (!(flags & L2CAP_LM_MASTER))
+			return 0;
+		flags &= ~L2CAP_LM_MASTER;
+	}
+
+	if (setsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags, sizeof(flags)) < 0)
+		return -errno;
+
+	return 0;
+}
+
+static int rfcomm_set_master(int sock, int master)
+{
+	int flags;
+	socklen_t len;
+
+	len = sizeof(flags);
+	if (getsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &flags, &len) < 0)
+		return -errno;
+
+	if (master) {
+		if (flags & RFCOMM_LM_MASTER)
+			return 0;
+		flags |= RFCOMM_LM_MASTER;
+	} else {
+		if (!(flags & RFCOMM_LM_MASTER))
+			return 0;
+		flags &= ~RFCOMM_LM_MASTER;
+	}
+
+	if (setsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &flags, sizeof(flags)) < 0)
+		return -errno;
+
+	return 0;
+}
+
+static int l2cap_set_lm(int sock, int level)
+{
+	int lm_map[] = {
+		0,
+		L2CAP_LM_AUTH,
+		L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT,
+		L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT | L2CAP_LM_SECURE,
+	}, opt = lm_map[level];
+
+	if (setsockopt(sock, SOL_L2CAP, L2CAP_LM, &opt, sizeof(opt)) < 0)
+		return -errno;
+
+	return 0;
+}
+
+static int rfcomm_set_lm(int sock, int level)
+{
+	int lm_map[] = {
+		0,
+		RFCOMM_LM_AUTH,
+		RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT,
+		RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT | RFCOMM_LM_SECURE,
+	}, opt = lm_map[level];
+
+	if (setsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &opt, sizeof(opt)) < 0)
+		return -errno;
+
+	return 0;
+}
+
+static gboolean set_sec_level(int sock, BtIOType type, int level, GError **err)
+{
+	struct bt_security sec;
+	int ret;
+
+	if (level < BT_SECURITY_LOW || level > BT_SECURITY_HIGH) {
+		g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+				"Valid security level range is %d-%d",
+				BT_SECURITY_LOW, BT_SECURITY_HIGH);
+		return FALSE;
+	}
+
+	memset(&sec, 0, sizeof(sec));
+	sec.level = level;
+
+	if (setsockopt(sock, SOL_BLUETOOTH, BT_SECURITY, &sec,
+							sizeof(sec)) == 0)
+		return TRUE;
+
+	if (errno != ENOPROTOOPT) {
+		ERROR_FAILED(err, "setsockopt(BT_SECURITY)", errno);
+		return FALSE;
+	}
+
+	if (type == BT_IO_L2CAP)
+		ret = l2cap_set_lm(sock, level);
+	else
+		ret = rfcomm_set_lm(sock, level);
+
+	if (ret < 0) {
+		ERROR_FAILED(err, "setsockopt(LM)", -ret);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static int l2cap_get_lm(int sock, int *sec_level)
+{
+	int opt;
+	socklen_t len;
+
+	len = sizeof(opt);
+	if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &opt, &len) < 0)
+		return -errno;
+
+	*sec_level = 0;
+
+	if (opt & L2CAP_LM_AUTH)
+		*sec_level = BT_SECURITY_LOW;
+	if (opt & L2CAP_LM_ENCRYPT)
+		*sec_level = BT_SECURITY_MEDIUM;
+	if (opt & L2CAP_LM_SECURE)
+		*sec_level = BT_SECURITY_HIGH;
+
+	return 0;
+}
+
+static int rfcomm_get_lm(int sock, int *sec_level)
+{
+	int opt;
+	socklen_t len;
+
+	len = sizeof(opt);
+	if (getsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &opt, &len) < 0)
+		return -errno;
+
+	*sec_level = 0;
+
+	if (opt & RFCOMM_LM_AUTH)
+		*sec_level = BT_SECURITY_LOW;
+	if (opt & RFCOMM_LM_ENCRYPT)
+		*sec_level = BT_SECURITY_MEDIUM;
+	if (opt & RFCOMM_LM_SECURE)
+		*sec_level = BT_SECURITY_HIGH;
+
+	return 0;
+}
+
+static gboolean get_sec_level(int sock, BtIOType type, int *level,
+								GError **err)
+{
+	struct bt_security sec;
+	socklen_t len;
+	int ret;
+
+	memset(&sec, 0, sizeof(sec));
+	len = sizeof(sec);
+	if (getsockopt(sock, SOL_BLUETOOTH, BT_SECURITY, &sec, &len) == 0) {
+		*level = sec.level;
+		return TRUE;
+	}
+
+	if (errno != ENOPROTOOPT) {
+		ERROR_FAILED(err, "getsockopt(BT_SECURITY)", errno);
+		return FALSE;
+	}
+
+	if (type == BT_IO_L2CAP)
+		ret = l2cap_get_lm(sock, level);
+	else
+		ret = rfcomm_get_lm(sock, level);
+
+	if (ret < 0) {
+		ERROR_FAILED(err, "getsockopt(LM)", -ret);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean l2cap_set(int sock, int sec_level, uint16_t imtu, uint16_t omtu,
+					uint8_t mode, int master, GError **err)
+{
+	if (imtu || omtu || mode) {
+		struct l2cap_options l2o;
+		socklen_t len;
+
+		memset(&l2o, 0, sizeof(l2o));
+		len = sizeof(l2o);
+		if (getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o,
+								&len) < 0) {
+			ERROR_FAILED(err, "getsockopt(L2CAP_OPTIONS)", errno);
+			return FALSE;
+		}
+
+		if (imtu)
+			l2o.imtu = imtu;
+		if (omtu)
+			l2o.omtu = omtu;
+		if (mode)
+			l2o.mode = mode;
+
+		if (setsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o,
+							sizeof(l2o)) < 0) {
+			ERROR_FAILED(err, "setsockopt(L2CAP_OPTIONS)", errno);
+			return FALSE;
+		}
+	}
+
+	if (master >= 0 && l2cap_set_master(sock, master) < 0) {
+		ERROR_FAILED(err, "l2cap_set_master", errno);
+		return FALSE;
+	}
+
+	if (sec_level && !set_sec_level(sock, BT_IO_L2CAP, sec_level, err))
+		return FALSE;
+
+	return TRUE;
+}
+
+static int rfcomm_bind(int sock,
+		const bdaddr_t *src, uint8_t channel, GError **err)
+{
+	struct sockaddr_rc addr;
+
+	memset(&addr, 0, sizeof(addr));
+	addr.rc_family = AF_BLUETOOTH;
+	bacpy(&addr.rc_bdaddr, src);
+	addr.rc_channel = channel;
+
+	if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		ERROR_FAILED(err, "rfcomm_bind", errno);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int rfcomm_connect(int sock, const bdaddr_t *dst, uint8_t channel)
+{
+	int err;
+	struct sockaddr_rc addr;
+
+	memset(&addr, 0, sizeof(addr));
+	addr.rc_family = AF_BLUETOOTH;
+	bacpy(&addr.rc_bdaddr, dst);
+	addr.rc_channel = channel;
+
+	err = connect(sock, (struct sockaddr *) &addr, sizeof(addr));
+	if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS))
+		return err;
+
+	return 0;
+}
+
+static gboolean rfcomm_set(int sock, int sec_level, int master, GError **err)
+{
+	if (sec_level && !set_sec_level(sock, BT_IO_RFCOMM, sec_level, err))
+		return FALSE;
+
+	if (master >= 0 && rfcomm_set_master(sock, master) < 0) {
+		ERROR_FAILED(err, "rfcomm_set_master", errno);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static int sco_bind(int sock, const bdaddr_t *src, GError **err)
+{
+	struct sockaddr_sco addr;
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sco_family = AF_BLUETOOTH;
+	bacpy(&addr.sco_bdaddr, src);
+
+	if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		ERROR_FAILED(err, "sco_bind", errno);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int sco_connect(int sock, const bdaddr_t *dst)
+{
+	struct sockaddr_sco addr;
+	int err;
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sco_family = AF_BLUETOOTH;
+	bacpy(&addr.sco_bdaddr, dst);
+
+	err = connect(sock, (struct sockaddr *) &addr, sizeof(addr));
+	if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS))
+		return err;
+
+	return 0;
+}
+
+static gboolean sco_set(int sock, uint16_t mtu, GError **err)
+{
+	struct sco_options sco_opt;
+	socklen_t len;
+
+	if (!mtu)
+		return TRUE;
+
+	len = sizeof(sco_opt);
+	memset(&sco_opt, 0, len);
+	if (getsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt, &len) < 0) {
+		ERROR_FAILED(err, "getsockopt(SCO_OPTIONS)", errno);
+		return FALSE;
+	}
+
+	sco_opt.mtu = mtu;
+	if (setsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt,
+						sizeof(sco_opt)) < 0) {
+		ERROR_FAILED(err, "setsockopt(SCO_OPTIONS)", errno);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean parse_set_opts(struct set_opts *opts, GError **err,
+						BtIOOption opt1, va_list args)
+{
+	BtIOOption opt = opt1;
+	const char *str;
+
+	memset(opts, 0, sizeof(*opts));
+
+	/* Set defaults */
+	opts->defer = DEFAULT_DEFER_TIMEOUT;
+	opts->master = -1;
+	opts->sec_level = BT_IO_SEC_MEDIUM;
+	opts->mode = L2CAP_MODE_BASIC;
+
+	while (opt != BT_IO_OPT_INVALID) {
+		switch (opt) {
+		case BT_IO_OPT_SOURCE:
+			str = va_arg(args, const char *);
+			if (strncasecmp(str, "hci", 3) == 0)
+				hci_devba(atoi(str + 3), &opts->src);
+			else
+				str2ba(str, &opts->src);
+			break;
+		case BT_IO_OPT_SOURCE_BDADDR:
+			bacpy(&opts->src, va_arg(args, const bdaddr_t *));
+			break;
+		case BT_IO_OPT_DEST:
+			str2ba(va_arg(args, const char *), &opts->dst);
+			break;
+		case BT_IO_OPT_DEST_BDADDR:
+			bacpy(&opts->dst, va_arg(args, const bdaddr_t *));
+			break;
+		case BT_IO_OPT_DEFER_TIMEOUT:
+			opts->defer = va_arg(args, int);
+			break;
+		case BT_IO_OPT_SEC_LEVEL:
+			opts->sec_level = va_arg(args, int);
+			break;
+		case BT_IO_OPT_CHANNEL:
+			opts->channel = va_arg(args, int);
+			break;
+		case BT_IO_OPT_PSM:
+			opts->psm = va_arg(args, int);
+			break;
+		case BT_IO_OPT_MTU:
+			opts->mtu = va_arg(args, int);
+			opts->imtu = opts->mtu;
+			opts->omtu = opts->mtu;
+			break;
+		case BT_IO_OPT_OMTU:
+			opts->omtu = va_arg(args, int);
+			if (!opts->mtu)
+				opts->mtu = opts->omtu;
+			break;
+		case BT_IO_OPT_IMTU:
+			opts->imtu = va_arg(args, int);
+			if (!opts->mtu)
+				opts->mtu = opts->imtu;
+			break;
+		case BT_IO_OPT_MASTER:
+			opts->master = va_arg(args, gboolean);
+			break;
+		case BT_IO_OPT_MODE:
+			opts->mode = va_arg(args, int);
+			break;
+		default:
+			g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+					"Unknown option %d", opt);
+			return FALSE;
+		}
+
+		opt = va_arg(args, int);
+	}
+
+	return TRUE;
+}
+
+static gboolean get_peers(int sock, struct sockaddr *src, struct sockaddr *dst,
+				socklen_t len, GError **err)
+{
+	socklen_t olen;
+
+	memset(src, 0, len);
+	olen = len;
+	if (getsockname(sock, src, &olen) < 0) {
+		ERROR_FAILED(err, "getsockname", errno);
+		return FALSE;
+	}
+
+	memset(dst, 0, len);
+	olen = len;
+	if (getpeername(sock, dst, &olen) < 0) {
+		ERROR_FAILED(err, "getpeername", errno);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static int l2cap_get_info(int sock, uint16_t *handle, uint8_t *dev_class)
+{
+	struct l2cap_conninfo info;
+	socklen_t len;
+
+	len = sizeof(info);
+	if (getsockopt(sock, SOL_L2CAP, L2CAP_CONNINFO, &info, &len) < 0)
+		return -errno;
+
+	if (handle)
+		*handle = info.hci_handle;
+
+	if (dev_class)
+		memcpy(dev_class, info.dev_class, 3);
+
+	return 0;
+}
+
+static gboolean l2cap_get(int sock, GError **err, BtIOOption opt1,
+								va_list args)
+{
+	BtIOOption opt = opt1;
+	struct sockaddr_l2 src, dst;
+	struct l2cap_options l2o;
+	int flags;
+	uint8_t dev_class[3];
+	uint16_t handle;
+	socklen_t len;
+
+	len = sizeof(l2o);
+	memset(&l2o, 0, len);
+	if (getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &len) < 0) {
+		ERROR_FAILED(err, "getsockopt(L2CAP_OPTIONS)", errno);
+		return FALSE;
+	}
+
+	if (!get_peers(sock, (struct sockaddr *) &src,
+				(struct sockaddr *) &dst, sizeof(src), err))
+		return FALSE;
+
+	while (opt != BT_IO_OPT_INVALID) {
+		switch (opt) {
+		case BT_IO_OPT_SOURCE:
+			ba2str(&src.l2_bdaddr, va_arg(args, char *));
+			break;
+		case BT_IO_OPT_SOURCE_BDADDR:
+			bacpy(va_arg(args, bdaddr_t *), &src.l2_bdaddr);
+			break;
+		case BT_IO_OPT_DEST:
+			ba2str(&dst.l2_bdaddr, va_arg(args, char *));
+			break;
+		case BT_IO_OPT_DEST_BDADDR:
+			bacpy(va_arg(args, bdaddr_t *), &dst.l2_bdaddr);
+			break;
+		case BT_IO_OPT_DEFER_TIMEOUT:
+			len = sizeof(int);
+			if (getsockopt(sock, SOL_BLUETOOTH, BT_DEFER_SETUP,
+					va_arg(args, int *), &len) < 0) {
+				ERROR_FAILED(err, "getsockopt(DEFER_SETUP)",
+									errno);
+				return FALSE;
+			}
+			break;
+		case BT_IO_OPT_SEC_LEVEL:
+			if (!get_sec_level(sock, BT_IO_L2CAP,
+						va_arg(args, int *), err))
+				return FALSE;
+			break;
+		case BT_IO_OPT_PSM:
+			*(va_arg(args, uint16_t *)) = src.l2_psm ?
+						src.l2_psm : dst.l2_psm;
+			break;
+		case BT_IO_OPT_OMTU:
+			*(va_arg(args, uint16_t *)) = l2o.omtu;
+			break;
+		case BT_IO_OPT_IMTU:
+			*(va_arg(args, uint16_t *)) = l2o.imtu;
+			break;
+		case BT_IO_OPT_MASTER:
+			len = sizeof(flags);
+			if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags,
+								&len) < 0) {
+				ERROR_FAILED(err, "getsockopt(L2CAP_LM)",
+									errno);
+				return FALSE;
+			}
+			*(va_arg(args, gboolean *)) =
+				(flags & L2CAP_LM_MASTER) ? TRUE : FALSE;
+			break;
+		case BT_IO_OPT_HANDLE:
+			if (l2cap_get_info(sock, &handle, dev_class) < 0) {
+				ERROR_FAILED(err, "L2CAP_CONNINFO", errno);
+				return FALSE;
+			}
+			*(va_arg(args, uint16_t *)) = handle;
+			break;
+		case BT_IO_OPT_CLASS:
+			if (l2cap_get_info(sock, &handle, dev_class) < 0) {
+				ERROR_FAILED(err, "L2CAP_CONNINFO", errno);
+				return FALSE;
+			}
+			memcpy(va_arg(args, uint8_t *), dev_class, 3);
+			break;
+		default:
+			g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+					"Unknown option %d", opt);
+			return FALSE;
+		}
+
+		opt = va_arg(args, int);
+	}
+
+	return TRUE;
+}
+
+static int rfcomm_get_info(int sock, uint16_t *handle, uint8_t *dev_class)
+{
+	struct rfcomm_conninfo info;
+	socklen_t len;
+
+	len = sizeof(info);
+	if (getsockopt(sock, SOL_RFCOMM, RFCOMM_CONNINFO, &info, &len) < 0)
+		return -errno;
+
+	if (handle)
+		*handle = info.hci_handle;
+
+	if (dev_class)
+		memcpy(dev_class, info.dev_class, 3);
+
+	return 0;
+}
+
+static gboolean rfcomm_get(int sock, GError **err, BtIOOption opt1,
+								va_list args)
+{
+	BtIOOption opt = opt1;
+	struct sockaddr_rc src, dst;
+	int flags;
+	socklen_t len;
+	uint8_t dev_class[3];
+	uint16_t handle;
+
+	if (!get_peers(sock, (struct sockaddr *) &src,
+				(struct sockaddr *) &dst, sizeof(src), err))
+		return FALSE;
+
+	while (opt != BT_IO_OPT_INVALID) {
+		switch (opt) {
+		case BT_IO_OPT_SOURCE:
+			ba2str(&src.rc_bdaddr, va_arg(args, char *));
+			break;
+		case BT_IO_OPT_SOURCE_BDADDR:
+			bacpy(va_arg(args, bdaddr_t *), &src.rc_bdaddr);
+			break;
+		case BT_IO_OPT_DEST:
+			ba2str(&dst.rc_bdaddr, va_arg(args, char *));
+			break;
+		case BT_IO_OPT_DEST_BDADDR:
+			bacpy(va_arg(args, bdaddr_t *), &dst.rc_bdaddr);
+			break;
+		case BT_IO_OPT_DEFER_TIMEOUT:
+			len = sizeof(int);
+			if (getsockopt(sock, SOL_BLUETOOTH, BT_DEFER_SETUP,
+					va_arg(args, int *), &len) < 0) {
+				ERROR_FAILED(err, "getsockopt(DEFER_SETUP)",
+									errno);
+				return FALSE;
+			}
+			break;
+		case BT_IO_OPT_SEC_LEVEL:
+			if (!get_sec_level(sock, BT_IO_RFCOMM,
+						va_arg(args, int *), err))
+				return FALSE;
+			break;
+		case BT_IO_OPT_CHANNEL:
+			*(va_arg(args, uint8_t *)) = src.rc_channel ?
+					src.rc_channel : dst.rc_channel;
+			break;
+		case BT_IO_OPT_SOURCE_CHANNEL:
+			*(va_arg(args, uint8_t *)) = src.rc_channel;
+			break;
+		case BT_IO_OPT_DEST_CHANNEL:
+			*(va_arg(args, uint8_t *)) = dst.rc_channel;
+			break;
+		case BT_IO_OPT_MASTER:
+			len = sizeof(flags);
+			if (getsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &flags,
+								&len) < 0) {
+				ERROR_FAILED(err, "getsockopt(RFCOMM_LM)",
+									errno);
+				return FALSE;
+			}
+			*(va_arg(args, gboolean *)) =
+				(flags & RFCOMM_LM_MASTER) ? TRUE : FALSE;
+			break;
+		case BT_IO_OPT_HANDLE:
+			if (rfcomm_get_info(sock, &handle, dev_class) < 0) {
+				ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
+				return FALSE;
+			}
+			*(va_arg(args, uint16_t *)) = handle;
+			break;
+		case BT_IO_OPT_CLASS:
+			if (rfcomm_get_info(sock, &handle, dev_class) < 0) {
+				ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
+				return FALSE;
+			}
+			memcpy(va_arg(args, uint8_t *), dev_class, 3);
+			break;
+		default:
+			g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+					"Unknown option %d", opt);
+			return FALSE;
+		}
+
+		opt = va_arg(args, int);
+	}
+
+	return TRUE;
+}
+
+static int sco_get_info(int sock, uint16_t *handle, uint8_t *dev_class)
+{
+	struct sco_conninfo info;
+	socklen_t len;
+
+	len = sizeof(info);
+	if (getsockopt(sock, SOL_SCO, SCO_CONNINFO, &info, &len) < 0)
+		return -errno;
+
+	if (handle)
+		*handle = info.hci_handle;
+
+	if (dev_class)
+		memcpy(dev_class, info.dev_class, 3);
+
+	return 0;
+}
+
+static gboolean sco_get(int sock, GError **err, BtIOOption opt1, va_list args)
+{
+	BtIOOption opt = opt1;
+	struct sockaddr_sco src, dst;
+	struct sco_options sco_opt;
+	socklen_t len;
+	uint8_t dev_class[3];
+	uint16_t handle;
+
+	len = sizeof(sco_opt);
+	memset(&sco_opt, 0, len);
+	if (getsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt, &len) < 0) {
+		ERROR_FAILED(err, "getsockopt(SCO_OPTIONS)", errno);
+		return FALSE;
+	}
+
+	if (!get_peers(sock, (struct sockaddr *) &src,
+				(struct sockaddr *) &dst, sizeof(src), err))
+		return FALSE;
+
+	while (opt != BT_IO_OPT_INVALID) {
+		switch (opt) {
+		case BT_IO_OPT_SOURCE:
+			ba2str(&src.sco_bdaddr, va_arg(args, char *));
+			break;
+		case BT_IO_OPT_SOURCE_BDADDR:
+			bacpy(va_arg(args, bdaddr_t *), &src.sco_bdaddr);
+			break;
+		case BT_IO_OPT_DEST:
+			ba2str(&dst.sco_bdaddr, va_arg(args, char *));
+			break;
+		case BT_IO_OPT_DEST_BDADDR:
+			bacpy(va_arg(args, bdaddr_t *), &dst.sco_bdaddr);
+			break;
+		case BT_IO_OPT_MTU:
+		case BT_IO_OPT_IMTU:
+		case BT_IO_OPT_OMTU:
+			*(va_arg(args, uint16_t *)) = sco_opt.mtu;
+			break;
+		case BT_IO_OPT_HANDLE:
+			if (sco_get_info(sock, &handle, dev_class) < 0) {
+				ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
+				return FALSE;
+			}
+			*(va_arg(args, uint16_t *)) = handle;
+			break;
+		case BT_IO_OPT_CLASS:
+			if (sco_get_info(sock, &handle, dev_class) < 0) {
+				ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
+				return FALSE;
+			}
+			memcpy(va_arg(args, uint8_t *), dev_class, 3);
+			break;
+		default:
+			g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+					"Unknown option %d", opt);
+			return FALSE;
+		}
+
+		opt = va_arg(args, int);
+	}
+
+	return TRUE;
+}
+
+static gboolean get_valist(GIOChannel *io, BtIOType type, GError **err,
+						BtIOOption opt1, va_list args)
+{
+	int sock;
+
+	sock = g_io_channel_unix_get_fd(io);
+
+	switch (type) {
+	case BT_IO_L2RAW:
+	case BT_IO_L2CAP:
+		return l2cap_get(sock, err, opt1, args);
+	case BT_IO_RFCOMM:
+		return rfcomm_get(sock, err, opt1, args);
+	case BT_IO_SCO:
+		return sco_get(sock, err, opt1, args);
+	}
+
+	g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+			"Unknown BtIO type %d", type);
+	return FALSE;
+}
+
+gboolean bt_io_accept(GIOChannel *io, BtIOConnect connect, void *user_data,
+					GDestroyNotify destroy, GError **err)
+{
+	int sock;
+	char c;
+	struct pollfd pfd;
+
+	sock = g_io_channel_unix_get_fd(io);
+
+	memset(&pfd, 0, sizeof(pfd));
+	pfd.fd = sock;
+	pfd.events = POLLOUT;
+
+	if (poll(&pfd, 1, 0) < 0) {
+		ERROR_FAILED(err, "poll", errno);
+		return FALSE;
+	}
+
+	if (!(pfd.revents & POLLOUT)) {
+		int ret;
+		ret = read(sock, &c, 1);
+	}
+
+	accept_add(io, connect, user_data, destroy);
+
+	return TRUE;
+}
+
+gboolean bt_io_set(GIOChannel *io, BtIOType type, GError **err,
+							BtIOOption opt1, ...)
+{
+	va_list args;
+	gboolean ret;
+	struct set_opts opts;
+	int sock;
+
+	va_start(args, opt1);
+	ret = parse_set_opts(&opts, err, opt1, args);
+	va_end(args);
+
+	if (!ret)
+		return ret;
+
+	sock = g_io_channel_unix_get_fd(io);
+
+	switch (type) {
+	case BT_IO_L2RAW:
+	case BT_IO_L2CAP:
+		return l2cap_set(sock, opts.sec_level, opts.imtu, opts.omtu,
+						opts.mode, opts.master, err);
+	case BT_IO_RFCOMM:
+		return rfcomm_set(sock, opts.sec_level, opts.master, err);
+	case BT_IO_SCO:
+		return sco_set(sock, opts.mtu, err);
+	}
+
+	g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+			"Unknown BtIO type %d", type);
+	return FALSE;
+}
+
+gboolean bt_io_get(GIOChannel *io, BtIOType type, GError **err,
+							BtIOOption opt1, ...)
+{
+	va_list args;
+	gboolean ret;
+
+	va_start(args, opt1);
+	ret = get_valist(io, type, err, opt1, args);
+	va_end(args);
+
+	return ret;
+}
+
+static GIOChannel *create_io(BtIOType type, gboolean server,
+					struct set_opts *opts, GError **err)
+{
+	int sock;
+	GIOChannel *io;
+
+	switch (type) {
+	case BT_IO_L2RAW:
+		sock = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP);
+		if (sock < 0) {
+			ERROR_FAILED(err, "socket(RAW, L2CAP)", errno);
+			return NULL;
+		}
+		if (l2cap_bind(sock, &opts->src,
+					server ? opts->psm : 0, err) < 0)
+			goto failed;
+		if (!l2cap_set(sock, opts->sec_level, 0, 0, 0, -1, err))
+			goto failed;
+		break;
+	case BT_IO_L2CAP:
+		sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
+		if (sock < 0) {
+			ERROR_FAILED(err, "socket(SEQPACKET, L2CAP)", errno);
+			return NULL;
+		}
+		if (l2cap_bind(sock, &opts->src,
+					server ? opts->psm : 0, err) < 0)
+			goto failed;
+		if (!l2cap_set(sock, opts->sec_level, opts->imtu, opts->omtu,
+						opts->mode, opts->master, err))
+			goto failed;
+		break;
+	case BT_IO_RFCOMM:
+		sock = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
+		if (sock < 0) {
+			ERROR_FAILED(err, "socket(STREAM, RFCOMM)", errno);
+			return NULL;
+		}
+		if (rfcomm_bind(sock, &opts->src,
+					server ? opts->channel : 0, err) < 0)
+			goto failed;
+		if (!rfcomm_set(sock, opts->sec_level, opts->master, err))
+			goto failed;
+		break;
+	case BT_IO_SCO:
+		sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
+		if (sock < 0) {
+			ERROR_FAILED(err, "socket(SEQPACKET, SCO)", errno);
+			return NULL;
+		}
+		if (sco_bind(sock, &opts->src, err) < 0)
+			goto failed;
+		if (!sco_set(sock, opts->mtu, err))
+			goto failed;
+		break;
+	default:
+		g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+				"Unknown BtIO type %d", type);
+		return NULL;
+	}
+
+	io = g_io_channel_unix_new(sock);
+
+	g_io_channel_set_close_on_unref(io, TRUE);
+	g_io_channel_set_flags(io, G_IO_FLAG_NONBLOCK, NULL);
+
+	return io;
+
+failed:
+	close(sock);
+
+	return NULL;
+}
+
+GIOChannel *bt_io_connect(BtIOType type, BtIOConnect connect,
+				void *user_data, GDestroyNotify destroy,
+				GError **gerr, BtIOOption opt1, ...)
+{
+	GIOChannel *io;
+	va_list args;
+	struct set_opts opts;
+	int err, sock;
+	gboolean ret;
+
+	va_start(args, opt1);
+	ret = parse_set_opts(&opts, gerr, opt1, args);
+	va_end(args);
+
+	if (ret == FALSE)
+		return NULL;
+
+	io = create_io(type, FALSE, &opts, gerr);
+	if (io == NULL)
+		return NULL;
+
+	sock = g_io_channel_unix_get_fd(io);
+
+	switch (type) {
+	case BT_IO_L2RAW:
+		err = l2cap_connect(sock, &opts.dst, 0);
+		break;
+	case BT_IO_L2CAP:
+		err = l2cap_connect(sock, &opts.dst, opts.psm);
+		break;
+	case BT_IO_RFCOMM:
+		err = rfcomm_connect(sock, &opts.dst, opts.channel);
+		break;
+	case BT_IO_SCO:
+		err = sco_connect(sock, &opts.dst);
+		break;
+	default:
+		g_set_error(gerr, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+						"Unknown BtIO type %d", type);
+		return NULL;
+	}
+
+	if (err < 0) {
+		g_set_error(gerr, BT_IO_ERROR, BT_IO_ERROR_CONNECT_FAILED,
+				"connect: %s (%d)", strerror(-err), -err);
+		g_io_channel_unref(io);
+		return NULL;
+	}
+
+	connect_add(io, connect, user_data, destroy);
+
+	return io;
+}
+
+GIOChannel *bt_io_listen(BtIOType type, BtIOConnect connect,
+				BtIOConfirm confirm, void *user_data,
+				GDestroyNotify destroy, GError **err,
+				BtIOOption opt1, ...)
+{
+	GIOChannel *io;
+	va_list args;
+	struct set_opts opts;
+	int sock;
+	gboolean ret;
+
+	if (type == BT_IO_L2RAW) {
+		g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+				"Server L2CAP RAW sockets not supported");
+		return NULL;
+	}
+
+	va_start(args, opt1);
+	ret = parse_set_opts(&opts, err, opt1, args);
+	va_end(args);
+
+	if (ret == FALSE)
+		return NULL;
+
+	io = create_io(type, TRUE, &opts, err);
+	if (io == NULL)
+		return NULL;
+
+	sock = g_io_channel_unix_get_fd(io);
+
+	if (confirm)
+		setsockopt(sock, SOL_BLUETOOTH, BT_DEFER_SETUP, &opts.defer,
+							sizeof(opts.defer));
+
+	if (listen(sock, 5) < 0) {
+		ERROR_FAILED(err, "listen", errno);
+		g_io_channel_unref(io);
+		return NULL;
+	}
+
+	server_add(io, connect, confirm, user_data, destroy);
+
+	return io;
+}
+
+GQuark bt_io_error_quark(void)
+{
+	return g_quark_from_static_string("bt-io-error-quark");
+}
diff --git a/btio/btio.h b/btio/btio.h
new file mode 100644
index 0000000..e9dcc9f
--- /dev/null
+++ b/btio/btio.h
@@ -0,0 +1,97 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2009-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2009-2010  Nokia Corporation
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+#ifndef BT_IO_H
+#define BT_IO_H
+
+#include <glib.h>
+
+typedef enum {
+	BT_IO_ERROR_DISCONNECTED,
+	BT_IO_ERROR_CONNECT_FAILED,
+	BT_IO_ERROR_FAILED,
+	BT_IO_ERROR_INVALID_ARGS,
+} BtIOError;
+
+#define BT_IO_ERROR bt_io_error_quark()
+
+GQuark bt_io_error_quark(void);
+
+typedef enum {
+	BT_IO_L2RAW,
+	BT_IO_L2CAP,
+	BT_IO_RFCOMM,
+	BT_IO_SCO,
+} BtIOType;
+
+typedef enum {
+	BT_IO_OPT_INVALID = 0,
+	BT_IO_OPT_SOURCE,
+	BT_IO_OPT_SOURCE_BDADDR,
+	BT_IO_OPT_DEST,
+	BT_IO_OPT_DEST_BDADDR,
+	BT_IO_OPT_DEFER_TIMEOUT,
+	BT_IO_OPT_SEC_LEVEL,
+	BT_IO_OPT_CHANNEL,
+	BT_IO_OPT_SOURCE_CHANNEL,
+	BT_IO_OPT_DEST_CHANNEL,
+	BT_IO_OPT_PSM,
+	BT_IO_OPT_MTU,
+	BT_IO_OPT_OMTU,
+	BT_IO_OPT_IMTU,
+	BT_IO_OPT_MASTER,
+	BT_IO_OPT_HANDLE,
+	BT_IO_OPT_CLASS,
+	BT_IO_OPT_MODE,
+} BtIOOption;
+
+typedef enum {
+	BT_IO_SEC_SDP = 0,
+	BT_IO_SEC_LOW,
+	BT_IO_SEC_MEDIUM,
+	BT_IO_SEC_HIGH,
+} BtIOSecLevel;
+
+typedef void (*BtIOConfirm)(GIOChannel *io, void *user_data);
+
+typedef void (*BtIOConnect)(GIOChannel *io, GError *err, void *user_data);
+
+gboolean bt_io_accept(GIOChannel *io, BtIOConnect connect, void *user_data,
+					GDestroyNotify destroy, GError **err);
+
+gboolean bt_io_set(GIOChannel *io, BtIOType type, GError **err,
+						BtIOOption opt1, ...);
+
+gboolean bt_io_get(GIOChannel *io, BtIOType type, GError **err,
+						BtIOOption opt1, ...);
+
+GIOChannel *bt_io_connect(BtIOType type, BtIOConnect connect,
+				void *user_data, GDestroyNotify destroy,
+				GError **err, BtIOOption opt1, ...);
+
+GIOChannel *bt_io_listen(BtIOType type, BtIOConnect connect,
+				BtIOConfirm confirm, void *user_data,
+				GDestroyNotify destroy, GError **err,
+				BtIOOption opt1, ...);
+
+#endif
diff --git a/src/btio.c b/src/btio.c
deleted file mode 100644
index 42a3bcd..0000000
--- a/src/btio.c
+++ /dev/null
@@ -1,1299 +0,0 @@
-/*
- *
- *  BlueZ - Bluetooth protocol stack for Linux
- *
- *  Copyright (C) 2009-2010  Marcel Holtmann <marcel@holtmann.org>
- *  Copyright (C) 2009-2010  Nokia Corporation
- *
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- *
- */
-#include <stdarg.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <errno.h>
-#include <poll.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/l2cap.h>
-#include <bluetooth/rfcomm.h>
-#include <bluetooth/sco.h>
-#include <bluetooth/hci.h>
-#include <bluetooth/hci_lib.h>
-
-#include <glib.h>
-
-#include "btio.h"
-
-#define ERROR_FAILED(gerr, str, err) \
-		g_set_error(gerr, BT_IO_ERROR, BT_IO_ERROR_FAILED, \
-				str ": %s (%d)", strerror(err), err)
-
-#define DEFAULT_DEFER_TIMEOUT 30
-
-struct set_opts {
-	bdaddr_t src;
-	bdaddr_t dst;
-	int defer;
-	int sec_level;
-	uint8_t channel;
-	uint16_t psm;
-	uint16_t mtu;
-	uint16_t imtu;
-	uint16_t omtu;
-	int master;
-	uint8_t mode;
-};
-
-struct connect {
-	BtIOConnect connect;
-	void *user_data;
-	GDestroyNotify destroy;
-};
-
-struct accept {
-	BtIOConnect connect;
-	void *user_data;
-	GDestroyNotify destroy;
-};
-
-struct server {
-	BtIOConnect connect;
-	BtIOConfirm confirm;
-	void *user_data;
-	GDestroyNotify destroy;
-};
-
-static void server_remove(struct server *server)
-{
-	if (server->destroy)
-		server->destroy(server->user_data);
-	g_free(server);
-}
-
-static void connect_remove(struct connect *conn)
-{
-	if (conn->destroy)
-		conn->destroy(conn->user_data);
-	g_free(conn);
-}
-
-static void accept_remove(struct accept *accept)
-{
-	if (accept->destroy)
-		accept->destroy(accept->user_data);
-	g_free(accept);
-}
-
-static gboolean check_nval(GIOChannel *io)
-{
-	struct pollfd fds;
-
-	memset(&fds, 0, sizeof(fds));
-	fds.fd = g_io_channel_unix_get_fd(io);
-	fds.events = POLLNVAL;
-
-	if (poll(&fds, 1, 0) > 0 && (fds.revents & POLLNVAL))
-		return TRUE;
-
-	return FALSE;
-}
-
-static gboolean accept_cb(GIOChannel *io, GIOCondition cond,
-							void *user_data)
-{
-	struct accept *accept = user_data;
-	GError *err = NULL;
-
-	/* If the user aborted this accept attempt */
-	if ((cond & G_IO_NVAL) || check_nval(io))
-		return FALSE;
-
-	if (cond & (G_IO_HUP | G_IO_ERR))
-		g_set_error(&err, BT_IO_ERROR, BT_IO_ERROR_DISCONNECTED,
-				"HUP or ERR on socket");
-
-	accept->connect(io, err, accept->user_data);
-
-	g_clear_error(&err);
-
-	return FALSE;
-}
-
-static gboolean connect_cb(GIOChannel *io, GIOCondition cond,
-							void *user_data)
-{
-	struct connect *conn = user_data;
-	GError *gerr = NULL;
-
-	/* If the user aborted this connect attempt */
-	if ((cond & G_IO_NVAL) || check_nval(io))
-		return FALSE;
-
-	if (cond & G_IO_OUT) {
-		int err = 0, sock = g_io_channel_unix_get_fd(io);
-		socklen_t len = sizeof(err);
-
-		if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &len) < 0)
-			err = errno;
-
-		if (err)
-			g_set_error(&gerr, BT_IO_ERROR,
-					BT_IO_ERROR_CONNECT_FAILED, "%s (%d)",
-					strerror(err), err);
-	} else if (cond & (G_IO_HUP | G_IO_ERR))
-		g_set_error(&gerr, BT_IO_ERROR, BT_IO_ERROR_CONNECT_FAILED,
-				"HUP or ERR on socket");
-
-	conn->connect(io, gerr, conn->user_data);
-
-	if (gerr)
-		g_error_free(gerr);
-
-	return FALSE;
-}
-
-static gboolean server_cb(GIOChannel *io, GIOCondition cond,
-							void *user_data)
-{
-	struct server *server = user_data;
-	int srv_sock, cli_sock;
-	GIOChannel *cli_io;
-
-	/* If the user closed the server */
-	if ((cond & G_IO_NVAL) || check_nval(io))
-		return FALSE;
-
-	srv_sock = g_io_channel_unix_get_fd(io);
-
-	cli_sock = accept(srv_sock, NULL, NULL);
-	if (cli_sock < 0)
-		return TRUE;
-
-	cli_io = g_io_channel_unix_new(cli_sock);
-
-	g_io_channel_set_close_on_unref(cli_io, TRUE);
-	g_io_channel_set_flags(cli_io, G_IO_FLAG_NONBLOCK, NULL);
-
-	if (server->confirm)
-		server->confirm(cli_io, server->user_data);
-	else
-		server->connect(cli_io, NULL, server->user_data);
-
-	g_io_channel_unref(cli_io);
-
-	return TRUE;
-}
-
-static void server_add(GIOChannel *io, BtIOConnect connect,
-				BtIOConfirm confirm, void *user_data,
-				GDestroyNotify destroy)
-{
-	struct server *server;
-	GIOCondition cond;
-
-	server = g_new0(struct server, 1);
-	server->connect = connect;
-	server->confirm = confirm;
-	server->user_data = user_data;
-	server->destroy = destroy;
-
-	cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
-	g_io_add_watch_full(io, G_PRIORITY_DEFAULT, cond, server_cb, server,
-					(GDestroyNotify) server_remove);
-}
-
-static void connect_add(GIOChannel *io, BtIOConnect connect,
-				void *user_data, GDestroyNotify destroy)
-{
-	struct connect *conn;
-	GIOCondition cond;
-
-	conn = g_new0(struct connect, 1);
-	conn->connect = connect;
-	conn->user_data = user_data;
-	conn->destroy = destroy;
-
-	cond = G_IO_OUT | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
-	g_io_add_watch_full(io, G_PRIORITY_DEFAULT, cond, connect_cb, conn,
-					(GDestroyNotify) connect_remove);
-}
-
-static void accept_add(GIOChannel *io, BtIOConnect connect, void *user_data,
-							GDestroyNotify destroy)
-{
-	struct accept *accept;
-	GIOCondition cond;
-
-	accept = g_new0(struct accept, 1);
-	accept->connect = connect;
-	accept->user_data = user_data;
-	accept->destroy = destroy;
-
-	cond = G_IO_OUT | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
-	g_io_add_watch_full(io, G_PRIORITY_DEFAULT, cond, accept_cb, accept,
-					(GDestroyNotify) accept_remove);
-}
-
-static int l2cap_bind(int sock, const bdaddr_t *src, uint16_t psm, GError **err)
-{
-	struct sockaddr_l2 addr;
-
-	memset(&addr, 0, sizeof(addr));
-	addr.l2_family = AF_BLUETOOTH;
-	bacpy(&addr.l2_bdaddr, src);
-	addr.l2_psm = htobs(psm);
-
-	if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-		ERROR_FAILED(err, "l2cap_bind", errno);
-		return -1;
-	}
-
-	return 0;
-}
-
-static int l2cap_connect(int sock, const bdaddr_t *dst, uint16_t psm)
-{
-	int err;
-	struct sockaddr_l2 addr;
-
-	memset(&addr, 0, sizeof(addr));
-	addr.l2_family = AF_BLUETOOTH;
-	bacpy(&addr.l2_bdaddr, dst);
-	addr.l2_psm = htobs(psm);
-
-	err = connect(sock, (struct sockaddr *) &addr, sizeof(addr));
-	if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS))
-		return err;
-
-	return 0;
-}
-
-static int l2cap_set_master(int sock, int master)
-{
-	int flags;
-	socklen_t len;
-
-	len = sizeof(flags);
-	if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags, &len) < 0)
-		return -errno;
-
-	if (master) {
-		if (flags & L2CAP_LM_MASTER)
-			return 0;
-		flags |= L2CAP_LM_MASTER;
-	} else {
-		if (!(flags & L2CAP_LM_MASTER))
-			return 0;
-		flags &= ~L2CAP_LM_MASTER;
-	}
-
-	if (setsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags, sizeof(flags)) < 0)
-		return -errno;
-
-	return 0;
-}
-
-static int rfcomm_set_master(int sock, int master)
-{
-	int flags;
-	socklen_t len;
-
-	len = sizeof(flags);
-	if (getsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &flags, &len) < 0)
-		return -errno;
-
-	if (master) {
-		if (flags & RFCOMM_LM_MASTER)
-			return 0;
-		flags |= RFCOMM_LM_MASTER;
-	} else {
-		if (!(flags & RFCOMM_LM_MASTER))
-			return 0;
-		flags &= ~RFCOMM_LM_MASTER;
-	}
-
-	if (setsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &flags, sizeof(flags)) < 0)
-		return -errno;
-
-	return 0;
-}
-
-static int l2cap_set_lm(int sock, int level)
-{
-	int lm_map[] = {
-		0,
-		L2CAP_LM_AUTH,
-		L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT,
-		L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT | L2CAP_LM_SECURE,
-	}, opt = lm_map[level];
-
-	if (setsockopt(sock, SOL_L2CAP, L2CAP_LM, &opt, sizeof(opt)) < 0)
-		return -errno;
-
-	return 0;
-}
-
-static int rfcomm_set_lm(int sock, int level)
-{
-	int lm_map[] = {
-		0,
-		RFCOMM_LM_AUTH,
-		RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT,
-		RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT | RFCOMM_LM_SECURE,
-	}, opt = lm_map[level];
-
-	if (setsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &opt, sizeof(opt)) < 0)
-		return -errno;
-
-	return 0;
-}
-
-static gboolean set_sec_level(int sock, BtIOType type, int level, GError **err)
-{
-	struct bt_security sec;
-	int ret;
-
-	if (level < BT_SECURITY_LOW || level > BT_SECURITY_HIGH) {
-		g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-				"Valid security level range is %d-%d",
-				BT_SECURITY_LOW, BT_SECURITY_HIGH);
-		return FALSE;
-	}
-
-	memset(&sec, 0, sizeof(sec));
-	sec.level = level;
-
-	if (setsockopt(sock, SOL_BLUETOOTH, BT_SECURITY, &sec,
-							sizeof(sec)) == 0)
-		return TRUE;
-
-	if (errno != ENOPROTOOPT) {
-		ERROR_FAILED(err, "setsockopt(BT_SECURITY)", errno);
-		return FALSE;
-	}
-
-	if (type == BT_IO_L2CAP)
-		ret = l2cap_set_lm(sock, level);
-	else
-		ret = rfcomm_set_lm(sock, level);
-
-	if (ret < 0) {
-		ERROR_FAILED(err, "setsockopt(LM)", -ret);
-		return FALSE;
-	}
-
-	return TRUE;
-}
-
-static int l2cap_get_lm(int sock, int *sec_level)
-{
-	int opt;
-	socklen_t len;
-
-	len = sizeof(opt);
-	if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &opt, &len) < 0)
-		return -errno;
-
-	*sec_level = 0;
-
-	if (opt & L2CAP_LM_AUTH)
-		*sec_level = BT_SECURITY_LOW;
-	if (opt & L2CAP_LM_ENCRYPT)
-		*sec_level = BT_SECURITY_MEDIUM;
-	if (opt & L2CAP_LM_SECURE)
-		*sec_level = BT_SECURITY_HIGH;
-
-	return 0;
-}
-
-static int rfcomm_get_lm(int sock, int *sec_level)
-{
-	int opt;
-	socklen_t len;
-
-	len = sizeof(opt);
-	if (getsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &opt, &len) < 0)
-		return -errno;
-
-	*sec_level = 0;
-
-	if (opt & RFCOMM_LM_AUTH)
-		*sec_level = BT_SECURITY_LOW;
-	if (opt & RFCOMM_LM_ENCRYPT)
-		*sec_level = BT_SECURITY_MEDIUM;
-	if (opt & RFCOMM_LM_SECURE)
-		*sec_level = BT_SECURITY_HIGH;
-
-	return 0;
-}
-
-static gboolean get_sec_level(int sock, BtIOType type, int *level,
-								GError **err)
-{
-	struct bt_security sec;
-	socklen_t len;
-	int ret;
-
-	memset(&sec, 0, sizeof(sec));
-	len = sizeof(sec);
-	if (getsockopt(sock, SOL_BLUETOOTH, BT_SECURITY, &sec, &len) == 0) {
-		*level = sec.level;
-		return TRUE;
-	}
-
-	if (errno != ENOPROTOOPT) {
-		ERROR_FAILED(err, "getsockopt(BT_SECURITY)", errno);
-		return FALSE;
-	}
-
-	if (type == BT_IO_L2CAP)
-		ret = l2cap_get_lm(sock, level);
-	else
-		ret = rfcomm_get_lm(sock, level);
-
-	if (ret < 0) {
-		ERROR_FAILED(err, "getsockopt(LM)", -ret);
-		return FALSE;
-	}
-
-	return TRUE;
-}
-
-static gboolean l2cap_set(int sock, int sec_level, uint16_t imtu, uint16_t omtu,
-					uint8_t mode, int master, GError **err)
-{
-	if (imtu || omtu || mode) {
-		struct l2cap_options l2o;
-		socklen_t len;
-
-		memset(&l2o, 0, sizeof(l2o));
-		len = sizeof(l2o);
-		if (getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o,
-								&len) < 0) {
-			ERROR_FAILED(err, "getsockopt(L2CAP_OPTIONS)", errno);
-			return FALSE;
-		}
-
-		if (imtu)
-			l2o.imtu = imtu;
-		if (omtu)
-			l2o.omtu = omtu;
-		if (mode)
-			l2o.mode = mode;
-
-		if (setsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o,
-							sizeof(l2o)) < 0) {
-			ERROR_FAILED(err, "setsockopt(L2CAP_OPTIONS)", errno);
-			return FALSE;
-		}
-	}
-
-	if (master >= 0 && l2cap_set_master(sock, master) < 0) {
-		ERROR_FAILED(err, "l2cap_set_master", errno);
-		return FALSE;
-	}
-
-	if (sec_level && !set_sec_level(sock, BT_IO_L2CAP, sec_level, err))
-		return FALSE;
-
-	return TRUE;
-}
-
-static int rfcomm_bind(int sock,
-		const bdaddr_t *src, uint8_t channel, GError **err)
-{
-	struct sockaddr_rc addr;
-
-	memset(&addr, 0, sizeof(addr));
-	addr.rc_family = AF_BLUETOOTH;
-	bacpy(&addr.rc_bdaddr, src);
-	addr.rc_channel = channel;
-
-	if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-		ERROR_FAILED(err, "rfcomm_bind", errno);
-		return -1;
-	}
-
-	return 0;
-}
-
-static int rfcomm_connect(int sock, const bdaddr_t *dst, uint8_t channel)
-{
-	int err;
-	struct sockaddr_rc addr;
-
-	memset(&addr, 0, sizeof(addr));
-	addr.rc_family = AF_BLUETOOTH;
-	bacpy(&addr.rc_bdaddr, dst);
-	addr.rc_channel = channel;
-
-	err = connect(sock, (struct sockaddr *) &addr, sizeof(addr));
-	if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS))
-		return err;
-
-	return 0;
-}
-
-static gboolean rfcomm_set(int sock, int sec_level, int master, GError **err)
-{
-	if (sec_level && !set_sec_level(sock, BT_IO_RFCOMM, sec_level, err))
-		return FALSE;
-
-	if (master >= 0 && rfcomm_set_master(sock, master) < 0) {
-		ERROR_FAILED(err, "rfcomm_set_master", errno);
-		return FALSE;
-	}
-
-	return TRUE;
-}
-
-static int sco_bind(int sock, const bdaddr_t *src, GError **err)
-{
-	struct sockaddr_sco addr;
-
-	memset(&addr, 0, sizeof(addr));
-	addr.sco_family = AF_BLUETOOTH;
-	bacpy(&addr.sco_bdaddr, src);
-
-	if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-		ERROR_FAILED(err, "sco_bind", errno);
-		return -1;
-	}
-
-	return 0;
-}
-
-static int sco_connect(int sock, const bdaddr_t *dst)
-{
-	struct sockaddr_sco addr;
-	int err;
-
-	memset(&addr, 0, sizeof(addr));
-	addr.sco_family = AF_BLUETOOTH;
-	bacpy(&addr.sco_bdaddr, dst);
-
-	err = connect(sock, (struct sockaddr *) &addr, sizeof(addr));
-	if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS))
-		return err;
-
-	return 0;
-}
-
-static gboolean sco_set(int sock, uint16_t mtu, GError **err)
-{
-	struct sco_options sco_opt;
-	socklen_t len;
-
-	if (!mtu)
-		return TRUE;
-
-	len = sizeof(sco_opt);
-	memset(&sco_opt, 0, len);
-	if (getsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt, &len) < 0) {
-		ERROR_FAILED(err, "getsockopt(SCO_OPTIONS)", errno);
-		return FALSE;
-	}
-
-	sco_opt.mtu = mtu;
-	if (setsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt,
-						sizeof(sco_opt)) < 0) {
-		ERROR_FAILED(err, "setsockopt(SCO_OPTIONS)", errno);
-		return FALSE;
-	}
-
-	return TRUE;
-}
-
-static gboolean parse_set_opts(struct set_opts *opts, GError **err,
-						BtIOOption opt1, va_list args)
-{
-	BtIOOption opt = opt1;
-	const char *str;
-
-	memset(opts, 0, sizeof(*opts));
-
-	/* Set defaults */
-	opts->defer = DEFAULT_DEFER_TIMEOUT;
-	opts->master = -1;
-	opts->sec_level = BT_IO_SEC_MEDIUM;
-	opts->mode = L2CAP_MODE_BASIC;
-
-	while (opt != BT_IO_OPT_INVALID) {
-		switch (opt) {
-		case BT_IO_OPT_SOURCE:
-			str = va_arg(args, const char *);
-			if (strncasecmp(str, "hci", 3) == 0)
-				hci_devba(atoi(str + 3), &opts->src);
-			else
-				str2ba(str, &opts->src);
-			break;
-		case BT_IO_OPT_SOURCE_BDADDR:
-			bacpy(&opts->src, va_arg(args, const bdaddr_t *));
-			break;
-		case BT_IO_OPT_DEST:
-			str2ba(va_arg(args, const char *), &opts->dst);
-			break;
-		case BT_IO_OPT_DEST_BDADDR:
-			bacpy(&opts->dst, va_arg(args, const bdaddr_t *));
-			break;
-		case BT_IO_OPT_DEFER_TIMEOUT:
-			opts->defer = va_arg(args, int);
-			break;
-		case BT_IO_OPT_SEC_LEVEL:
-			opts->sec_level = va_arg(args, int);
-			break;
-		case BT_IO_OPT_CHANNEL:
-			opts->channel = va_arg(args, int);
-			break;
-		case BT_IO_OPT_PSM:
-			opts->psm = va_arg(args, int);
-			break;
-		case BT_IO_OPT_MTU:
-			opts->mtu = va_arg(args, int);
-			opts->imtu = opts->mtu;
-			opts->omtu = opts->mtu;
-			break;
-		case BT_IO_OPT_OMTU:
-			opts->omtu = va_arg(args, int);
-			if (!opts->mtu)
-				opts->mtu = opts->omtu;
-			break;
-		case BT_IO_OPT_IMTU:
-			opts->imtu = va_arg(args, int);
-			if (!opts->mtu)
-				opts->mtu = opts->imtu;
-			break;
-		case BT_IO_OPT_MASTER:
-			opts->master = va_arg(args, gboolean);
-			break;
-		case BT_IO_OPT_MODE:
-			opts->mode = va_arg(args, int);
-			break;
-		default:
-			g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-					"Unknown option %d", opt);
-			return FALSE;
-		}
-
-		opt = va_arg(args, int);
-	}
-
-	return TRUE;
-}
-
-static gboolean get_peers(int sock, struct sockaddr *src, struct sockaddr *dst,
-				socklen_t len, GError **err)
-{
-	socklen_t olen;
-
-	memset(src, 0, len);
-	olen = len;
-	if (getsockname(sock, src, &olen) < 0) {
-		ERROR_FAILED(err, "getsockname", errno);
-		return FALSE;
-	}
-
-	memset(dst, 0, len);
-	olen = len;
-	if (getpeername(sock, dst, &olen) < 0) {
-		ERROR_FAILED(err, "getpeername", errno);
-		return FALSE;
-	}
-
-	return TRUE;
-}
-
-static int l2cap_get_info(int sock, uint16_t *handle, uint8_t *dev_class)
-{
-	struct l2cap_conninfo info;
-	socklen_t len;
-
-	len = sizeof(info);
-	if (getsockopt(sock, SOL_L2CAP, L2CAP_CONNINFO, &info, &len) < 0)
-		return -errno;
-
-	if (handle)
-		*handle = info.hci_handle;
-
-	if (dev_class)
-		memcpy(dev_class, info.dev_class, 3);
-
-	return 0;
-}
-
-static gboolean l2cap_get(int sock, GError **err, BtIOOption opt1,
-								va_list args)
-{
-	BtIOOption opt = opt1;
-	struct sockaddr_l2 src, dst;
-	struct l2cap_options l2o;
-	int flags;
-	uint8_t dev_class[3];
-	uint16_t handle;
-	socklen_t len;
-
-	len = sizeof(l2o);
-	memset(&l2o, 0, len);
-	if (getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &len) < 0) {
-		ERROR_FAILED(err, "getsockopt(L2CAP_OPTIONS)", errno);
-		return FALSE;
-	}
-
-	if (!get_peers(sock, (struct sockaddr *) &src,
-				(struct sockaddr *) &dst, sizeof(src), err))
-		return FALSE;
-
-	while (opt != BT_IO_OPT_INVALID) {
-		switch (opt) {
-		case BT_IO_OPT_SOURCE:
-			ba2str(&src.l2_bdaddr, va_arg(args, char *));
-			break;
-		case BT_IO_OPT_SOURCE_BDADDR:
-			bacpy(va_arg(args, bdaddr_t *), &src.l2_bdaddr);
-			break;
-		case BT_IO_OPT_DEST:
-			ba2str(&dst.l2_bdaddr, va_arg(args, char *));
-			break;
-		case BT_IO_OPT_DEST_BDADDR:
-			bacpy(va_arg(args, bdaddr_t *), &dst.l2_bdaddr);
-			break;
-		case BT_IO_OPT_DEFER_TIMEOUT:
-			len = sizeof(int);
-			if (getsockopt(sock, SOL_BLUETOOTH, BT_DEFER_SETUP,
-					va_arg(args, int *), &len) < 0) {
-				ERROR_FAILED(err, "getsockopt(DEFER_SETUP)",
-									errno);
-				return FALSE;
-			}
-			break;
-		case BT_IO_OPT_SEC_LEVEL:
-			if (!get_sec_level(sock, BT_IO_L2CAP,
-						va_arg(args, int *), err))
-				return FALSE;
-			break;
-		case BT_IO_OPT_PSM:
-			*(va_arg(args, uint16_t *)) = src.l2_psm ?
-						src.l2_psm : dst.l2_psm;
-			break;
-		case BT_IO_OPT_OMTU:
-			*(va_arg(args, uint16_t *)) = l2o.omtu;
-			break;
-		case BT_IO_OPT_IMTU:
-			*(va_arg(args, uint16_t *)) = l2o.imtu;
-			break;
-		case BT_IO_OPT_MASTER:
-			len = sizeof(flags);
-			if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags,
-								&len) < 0) {
-				ERROR_FAILED(err, "getsockopt(L2CAP_LM)",
-									errno);
-				return FALSE;
-			}
-			*(va_arg(args, gboolean *)) =
-				(flags & L2CAP_LM_MASTER) ? TRUE : FALSE;
-			break;
-		case BT_IO_OPT_HANDLE:
-			if (l2cap_get_info(sock, &handle, dev_class) < 0) {
-				ERROR_FAILED(err, "L2CAP_CONNINFO", errno);
-				return FALSE;
-			}
-			*(va_arg(args, uint16_t *)) = handle;
-			break;
-		case BT_IO_OPT_CLASS:
-			if (l2cap_get_info(sock, &handle, dev_class) < 0) {
-				ERROR_FAILED(err, "L2CAP_CONNINFO", errno);
-				return FALSE;
-			}
-			memcpy(va_arg(args, uint8_t *), dev_class, 3);
-			break;
-		default:
-			g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-					"Unknown option %d", opt);
-			return FALSE;
-		}
-
-		opt = va_arg(args, int);
-	}
-
-	return TRUE;
-}
-
-static int rfcomm_get_info(int sock, uint16_t *handle, uint8_t *dev_class)
-{
-	struct rfcomm_conninfo info;
-	socklen_t len;
-
-	len = sizeof(info);
-	if (getsockopt(sock, SOL_RFCOMM, RFCOMM_CONNINFO, &info, &len) < 0)
-		return -errno;
-
-	if (handle)
-		*handle = info.hci_handle;
-
-	if (dev_class)
-		memcpy(dev_class, info.dev_class, 3);
-
-	return 0;
-}
-
-static gboolean rfcomm_get(int sock, GError **err, BtIOOption opt1,
-								va_list args)
-{
-	BtIOOption opt = opt1;
-	struct sockaddr_rc src, dst;
-	int flags;
-	socklen_t len;
-	uint8_t dev_class[3];
-	uint16_t handle;
-
-	if (!get_peers(sock, (struct sockaddr *) &src,
-				(struct sockaddr *) &dst, sizeof(src), err))
-		return FALSE;
-
-	while (opt != BT_IO_OPT_INVALID) {
-		switch (opt) {
-		case BT_IO_OPT_SOURCE:
-			ba2str(&src.rc_bdaddr, va_arg(args, char *));
-			break;
-		case BT_IO_OPT_SOURCE_BDADDR:
-			bacpy(va_arg(args, bdaddr_t *), &src.rc_bdaddr);
-			break;
-		case BT_IO_OPT_DEST:
-			ba2str(&dst.rc_bdaddr, va_arg(args, char *));
-			break;
-		case BT_IO_OPT_DEST_BDADDR:
-			bacpy(va_arg(args, bdaddr_t *), &dst.rc_bdaddr);
-			break;
-		case BT_IO_OPT_DEFER_TIMEOUT:
-			len = sizeof(int);
-			if (getsockopt(sock, SOL_BLUETOOTH, BT_DEFER_SETUP,
-					va_arg(args, int *), &len) < 0) {
-				ERROR_FAILED(err, "getsockopt(DEFER_SETUP)",
-									errno);
-				return FALSE;
-			}
-			break;
-		case BT_IO_OPT_SEC_LEVEL:
-			if (!get_sec_level(sock, BT_IO_RFCOMM,
-						va_arg(args, int *), err))
-				return FALSE;
-			break;
-		case BT_IO_OPT_CHANNEL:
-			*(va_arg(args, uint8_t *)) = src.rc_channel ?
-					src.rc_channel : dst.rc_channel;
-			break;
-		case BT_IO_OPT_SOURCE_CHANNEL:
-			*(va_arg(args, uint8_t *)) = src.rc_channel;
-			break;
-		case BT_IO_OPT_DEST_CHANNEL:
-			*(va_arg(args, uint8_t *)) = dst.rc_channel;
-			break;
-		case BT_IO_OPT_MASTER:
-			len = sizeof(flags);
-			if (getsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &flags,
-								&len) < 0) {
-				ERROR_FAILED(err, "getsockopt(RFCOMM_LM)",
-									errno);
-				return FALSE;
-			}
-			*(va_arg(args, gboolean *)) =
-				(flags & RFCOMM_LM_MASTER) ? TRUE : FALSE;
-			break;
-		case BT_IO_OPT_HANDLE:
-			if (rfcomm_get_info(sock, &handle, dev_class) < 0) {
-				ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
-				return FALSE;
-			}
-			*(va_arg(args, uint16_t *)) = handle;
-			break;
-		case BT_IO_OPT_CLASS:
-			if (rfcomm_get_info(sock, &handle, dev_class) < 0) {
-				ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
-				return FALSE;
-			}
-			memcpy(va_arg(args, uint8_t *), dev_class, 3);
-			break;
-		default:
-			g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-					"Unknown option %d", opt);
-			return FALSE;
-		}
-
-		opt = va_arg(args, int);
-	}
-
-	return TRUE;
-}
-
-static int sco_get_info(int sock, uint16_t *handle, uint8_t *dev_class)
-{
-	struct sco_conninfo info;
-	socklen_t len;
-
-	len = sizeof(info);
-	if (getsockopt(sock, SOL_SCO, SCO_CONNINFO, &info, &len) < 0)
-		return -errno;
-
-	if (handle)
-		*handle = info.hci_handle;
-
-	if (dev_class)
-		memcpy(dev_class, info.dev_class, 3);
-
-	return 0;
-}
-
-static gboolean sco_get(int sock, GError **err, BtIOOption opt1, va_list args)
-{
-	BtIOOption opt = opt1;
-	struct sockaddr_sco src, dst;
-	struct sco_options sco_opt;
-	socklen_t len;
-	uint8_t dev_class[3];
-	uint16_t handle;
-
-	len = sizeof(sco_opt);
-	memset(&sco_opt, 0, len);
-	if (getsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt, &len) < 0) {
-		ERROR_FAILED(err, "getsockopt(SCO_OPTIONS)", errno);
-		return FALSE;
-	}
-
-	if (!get_peers(sock, (struct sockaddr *) &src,
-				(struct sockaddr *) &dst, sizeof(src), err))
-		return FALSE;
-
-	while (opt != BT_IO_OPT_INVALID) {
-		switch (opt) {
-		case BT_IO_OPT_SOURCE:
-			ba2str(&src.sco_bdaddr, va_arg(args, char *));
-			break;
-		case BT_IO_OPT_SOURCE_BDADDR:
-			bacpy(va_arg(args, bdaddr_t *), &src.sco_bdaddr);
-			break;
-		case BT_IO_OPT_DEST:
-			ba2str(&dst.sco_bdaddr, va_arg(args, char *));
-			break;
-		case BT_IO_OPT_DEST_BDADDR:
-			bacpy(va_arg(args, bdaddr_t *), &dst.sco_bdaddr);
-			break;
-		case BT_IO_OPT_MTU:
-		case BT_IO_OPT_IMTU:
-		case BT_IO_OPT_OMTU:
-			*(va_arg(args, uint16_t *)) = sco_opt.mtu;
-			break;
-		case BT_IO_OPT_HANDLE:
-			if (sco_get_info(sock, &handle, dev_class) < 0) {
-				ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
-				return FALSE;
-			}
-			*(va_arg(args, uint16_t *)) = handle;
-			break;
-		case BT_IO_OPT_CLASS:
-			if (sco_get_info(sock, &handle, dev_class) < 0) {
-				ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
-				return FALSE;
-			}
-			memcpy(va_arg(args, uint8_t *), dev_class, 3);
-			break;
-		default:
-			g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-					"Unknown option %d", opt);
-			return FALSE;
-		}
-
-		opt = va_arg(args, int);
-	}
-
-	return TRUE;
-}
-
-static gboolean get_valist(GIOChannel *io, BtIOType type, GError **err,
-						BtIOOption opt1, va_list args)
-{
-	int sock;
-
-	sock = g_io_channel_unix_get_fd(io);
-
-	switch (type) {
-	case BT_IO_L2RAW:
-	case BT_IO_L2CAP:
-		return l2cap_get(sock, err, opt1, args);
-	case BT_IO_RFCOMM:
-		return rfcomm_get(sock, err, opt1, args);
-	case BT_IO_SCO:
-		return sco_get(sock, err, opt1, args);
-	}
-
-	g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-			"Unknown BtIO type %d", type);
-	return FALSE;
-}
-
-gboolean bt_io_accept(GIOChannel *io, BtIOConnect connect, void *user_data,
-					GDestroyNotify destroy, GError **err)
-{
-	int sock;
-	char c;
-	struct pollfd pfd;
-
-	sock = g_io_channel_unix_get_fd(io);
-
-	memset(&pfd, 0, sizeof(pfd));
-	pfd.fd = sock;
-	pfd.events = POLLOUT;
-
-	if (poll(&pfd, 1, 0) < 0) {
-		ERROR_FAILED(err, "poll", errno);
-		return FALSE;
-	}
-
-	if (!(pfd.revents & POLLOUT)) {
-		int ret;
-		ret = read(sock, &c, 1);
-	}
-
-	accept_add(io, connect, user_data, destroy);
-
-	return TRUE;
-}
-
-gboolean bt_io_set(GIOChannel *io, BtIOType type, GError **err,
-							BtIOOption opt1, ...)
-{
-	va_list args;
-	gboolean ret;
-	struct set_opts opts;
-	int sock;
-
-	va_start(args, opt1);
-	ret = parse_set_opts(&opts, err, opt1, args);
-	va_end(args);
-
-	if (!ret)
-		return ret;
-
-	sock = g_io_channel_unix_get_fd(io);
-
-	switch (type) {
-	case BT_IO_L2RAW:
-	case BT_IO_L2CAP:
-		return l2cap_set(sock, opts.sec_level, opts.imtu, opts.omtu,
-						opts.mode, opts.master, err);
-	case BT_IO_RFCOMM:
-		return rfcomm_set(sock, opts.sec_level, opts.master, err);
-	case BT_IO_SCO:
-		return sco_set(sock, opts.mtu, err);
-	}
-
-	g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-			"Unknown BtIO type %d", type);
-	return FALSE;
-}
-
-gboolean bt_io_get(GIOChannel *io, BtIOType type, GError **err,
-							BtIOOption opt1, ...)
-{
-	va_list args;
-	gboolean ret;
-
-	va_start(args, opt1);
-	ret = get_valist(io, type, err, opt1, args);
-	va_end(args);
-
-	return ret;
-}
-
-static GIOChannel *create_io(BtIOType type, gboolean server,
-					struct set_opts *opts, GError **err)
-{
-	int sock;
-	GIOChannel *io;
-
-	switch (type) {
-	case BT_IO_L2RAW:
-		sock = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP);
-		if (sock < 0) {
-			ERROR_FAILED(err, "socket(RAW, L2CAP)", errno);
-			return NULL;
-		}
-		if (l2cap_bind(sock, &opts->src,
-					server ? opts->psm : 0, err) < 0)
-			goto failed;
-		if (!l2cap_set(sock, opts->sec_level, 0, 0, 0, -1, err))
-			goto failed;
-		break;
-	case BT_IO_L2CAP:
-		sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
-		if (sock < 0) {
-			ERROR_FAILED(err, "socket(SEQPACKET, L2CAP)", errno);
-			return NULL;
-		}
-		if (l2cap_bind(sock, &opts->src,
-					server ? opts->psm : 0, err) < 0)
-			goto failed;
-		if (!l2cap_set(sock, opts->sec_level, opts->imtu, opts->omtu,
-						opts->mode, opts->master, err))
-			goto failed;
-		break;
-	case BT_IO_RFCOMM:
-		sock = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
-		if (sock < 0) {
-			ERROR_FAILED(err, "socket(STREAM, RFCOMM)", errno);
-			return NULL;
-		}
-		if (rfcomm_bind(sock, &opts->src,
-					server ? opts->channel : 0, err) < 0)
-			goto failed;
-		if (!rfcomm_set(sock, opts->sec_level, opts->master, err))
-			goto failed;
-		break;
-	case BT_IO_SCO:
-		sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
-		if (sock < 0) {
-			ERROR_FAILED(err, "socket(SEQPACKET, SCO)", errno);
-			return NULL;
-		}
-		if (sco_bind(sock, &opts->src, err) < 0)
-			goto failed;
-		if (!sco_set(sock, opts->mtu, err))
-			goto failed;
-		break;
-	default:
-		g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-				"Unknown BtIO type %d", type);
-		return NULL;
-	}
-
-	io = g_io_channel_unix_new(sock);
-
-	g_io_channel_set_close_on_unref(io, TRUE);
-	g_io_channel_set_flags(io, G_IO_FLAG_NONBLOCK, NULL);
-
-	return io;
-
-failed:
-	close(sock);
-
-	return NULL;
-}
-
-GIOChannel *bt_io_connect(BtIOType type, BtIOConnect connect,
-				void *user_data, GDestroyNotify destroy,
-				GError **gerr, BtIOOption opt1, ...)
-{
-	GIOChannel *io;
-	va_list args;
-	struct set_opts opts;
-	int err, sock;
-	gboolean ret;
-
-	va_start(args, opt1);
-	ret = parse_set_opts(&opts, gerr, opt1, args);
-	va_end(args);
-
-	if (ret == FALSE)
-		return NULL;
-
-	io = create_io(type, FALSE, &opts, gerr);
-	if (io == NULL)
-		return NULL;
-
-	sock = g_io_channel_unix_get_fd(io);
-
-	switch (type) {
-	case BT_IO_L2RAW:
-		err = l2cap_connect(sock, &opts.dst, 0);
-		break;
-	case BT_IO_L2CAP:
-		err = l2cap_connect(sock, &opts.dst, opts.psm);
-		break;
-	case BT_IO_RFCOMM:
-		err = rfcomm_connect(sock, &opts.dst, opts.channel);
-		break;
-	case BT_IO_SCO:
-		err = sco_connect(sock, &opts.dst);
-		break;
-	default:
-		g_set_error(gerr, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-						"Unknown BtIO type %d", type);
-		return NULL;
-	}
-
-	if (err < 0) {
-		g_set_error(gerr, BT_IO_ERROR, BT_IO_ERROR_CONNECT_FAILED,
-				"connect: %s (%d)", strerror(-err), -err);
-		g_io_channel_unref(io);
-		return NULL;
-	}
-
-	connect_add(io, connect, user_data, destroy);
-
-	return io;
-}
-
-GIOChannel *bt_io_listen(BtIOType type, BtIOConnect connect,
-				BtIOConfirm confirm, void *user_data,
-				GDestroyNotify destroy, GError **err,
-				BtIOOption opt1, ...)
-{
-	GIOChannel *io;
-	va_list args;
-	struct set_opts opts;
-	int sock;
-	gboolean ret;
-
-	if (type == BT_IO_L2RAW) {
-		g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
-				"Server L2CAP RAW sockets not supported");
-		return NULL;
-	}
-
-	va_start(args, opt1);
-	ret = parse_set_opts(&opts, err, opt1, args);
-	va_end(args);
-
-	if (ret == FALSE)
-		return NULL;
-
-	io = create_io(type, TRUE, &opts, err);
-	if (io == NULL)
-		return NULL;
-
-	sock = g_io_channel_unix_get_fd(io);
-
-	if (confirm)
-		setsockopt(sock, SOL_BLUETOOTH, BT_DEFER_SETUP, &opts.defer,
-							sizeof(opts.defer));
-
-	if (listen(sock, 5) < 0) {
-		ERROR_FAILED(err, "listen", errno);
-		g_io_channel_unref(io);
-		return NULL;
-	}
-
-	server_add(io, connect, confirm, user_data, destroy);
-
-	return io;
-}
-
-GQuark bt_io_error_quark(void)
-{
-	return g_quark_from_static_string("bt-io-error-quark");
-}
diff --git a/src/btio.h b/src/btio.h
deleted file mode 100644
index e9dcc9f..0000000
--- a/src/btio.h
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- *
- *  BlueZ - Bluetooth protocol stack for Linux
- *
- *  Copyright (C) 2009-2010  Marcel Holtmann <marcel@holtmann.org>
- *  Copyright (C) 2009-2010  Nokia Corporation
- *
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- *
- */
-#ifndef BT_IO_H
-#define BT_IO_H
-
-#include <glib.h>
-
-typedef enum {
-	BT_IO_ERROR_DISCONNECTED,
-	BT_IO_ERROR_CONNECT_FAILED,
-	BT_IO_ERROR_FAILED,
-	BT_IO_ERROR_INVALID_ARGS,
-} BtIOError;
-
-#define BT_IO_ERROR bt_io_error_quark()
-
-GQuark bt_io_error_quark(void);
-
-typedef enum {
-	BT_IO_L2RAW,
-	BT_IO_L2CAP,
-	BT_IO_RFCOMM,
-	BT_IO_SCO,
-} BtIOType;
-
-typedef enum {
-	BT_IO_OPT_INVALID = 0,
-	BT_IO_OPT_SOURCE,
-	BT_IO_OPT_SOURCE_BDADDR,
-	BT_IO_OPT_DEST,
-	BT_IO_OPT_DEST_BDADDR,
-	BT_IO_OPT_DEFER_TIMEOUT,
-	BT_IO_OPT_SEC_LEVEL,
-	BT_IO_OPT_CHANNEL,
-	BT_IO_OPT_SOURCE_CHANNEL,
-	BT_IO_OPT_DEST_CHANNEL,
-	BT_IO_OPT_PSM,
-	BT_IO_OPT_MTU,
-	BT_IO_OPT_OMTU,
-	BT_IO_OPT_IMTU,
-	BT_IO_OPT_MASTER,
-	BT_IO_OPT_HANDLE,
-	BT_IO_OPT_CLASS,
-	BT_IO_OPT_MODE,
-} BtIOOption;
-
-typedef enum {
-	BT_IO_SEC_SDP = 0,
-	BT_IO_SEC_LOW,
-	BT_IO_SEC_MEDIUM,
-	BT_IO_SEC_HIGH,
-} BtIOSecLevel;
-
-typedef void (*BtIOConfirm)(GIOChannel *io, void *user_data);
-
-typedef void (*BtIOConnect)(GIOChannel *io, GError *err, void *user_data);
-
-gboolean bt_io_accept(GIOChannel *io, BtIOConnect connect, void *user_data,
-					GDestroyNotify destroy, GError **err);
-
-gboolean bt_io_set(GIOChannel *io, BtIOType type, GError **err,
-						BtIOOption opt1, ...);
-
-gboolean bt_io_get(GIOChannel *io, BtIOType type, GError **err,
-						BtIOOption opt1, ...);
-
-GIOChannel *bt_io_connect(BtIOType type, BtIOConnect connect,
-				void *user_data, GDestroyNotify destroy,
-				GError **err, BtIOOption opt1, ...);
-
-GIOChannel *bt_io_listen(BtIOType type, BtIOConnect connect,
-				BtIOConfirm confirm, void *user_data,
-				GDestroyNotify destroy, GError **err,
-				BtIOOption opt1, ...);
-
-#endif
-- 
1.7.0.4


^ permalink raw reply related

* [PATCH 1/3] btio: Remove blank line at EOF
From: Zhenhua Zhang @ 2010-09-10  2:47 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1284086847-16429-1-git-send-email-zhenhua.zhang@intel.com>

---
 src/btio.c |    1 -
 1 files changed, 0 insertions(+), 1 deletions(-)

diff --git a/src/btio.c b/src/btio.c
index 485eec6..42a3bcd 100644
--- a/src/btio.c
+++ b/src/btio.c
@@ -1297,4 +1297,3 @@ GQuark bt_io_error_quark(void)
 {
 	return g_quark_from_static_string("bt-io-error-quark");
 }
-
-- 
1.7.0.4


^ permalink raw reply related

* [PATCH 0/3] obex: isolate btio.[ch] and do some cleanup
From: Zhenhua Zhang @ 2010-09-10  2:47 UTC (permalink / raw)
  To: linux-bluetooth

Hi,

These patches move obexd/src/btio.[ch] out from source directory and make it identical with bluez's btio.[ch]. So we can freely copy them among bluez, obex and ofono.

Best Regards,
Zhenhua


^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox