Linux bluetooth development
 help / color / mirror / Atom feed
* who uses HCI_VENDOR_PKT
From: Pavan Savoy @ 2010-04-29 22:24 UTC (permalink / raw)
  To: linux-bluetooth

Hi,

Can I get an example usage of HCI_VENDOR_PKT ?
I want to check it against the packet format that I have come across which starts with 0x8 and with 0x9 unlike the 0xff mentioned in the HCI_VENDOR_PKT.

So, any device/links to formats ? 



^ permalink raw reply

* Re: [PATCH] hid/hid-sony: fix sony_set_operational_bt
From: Bastien Nocera @ 2010-04-29 23:23 UTC (permalink / raw)
  To: Antonio Ospite; +Cc: linux-input, linux-bluetooth
In-Reply-To: <1272578374-9250-1-git-send-email-ospite@studenti.unina.it>

On Thu, 2010-04-29 at 23:59 +0200, Antonio Ospite wrote:
> Don't send the report type as part of the data, this prevents the
> controller from going into the operational state at all.
> 
> This is completely equivalent to what the code originally meant to accomplish:
> as per in net/bluetooth/hidp/core.c::hidp_output_raw_report(), by using
> HID_FEATURE_REPORT here, what will be actually sent is
> (HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_FEATURE) which is exactly 0x53.
> 
> Signed-off-by: Antonio Ospite <ospite@studenti.unina.it>

My mistake there. I should have tested the patch at run-time, and not
trusted my copy-paste skills :(

Signed-off-by: Bastien Nocera <hadess@hadess.net>



^ permalink raw reply

* [PATCH] Health Device Profile API
From: Elvis Pfutzenreuter @ 2010-04-30  1:44 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: epx

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain, Size: 5232 bytes --]

Initial Health API proposal. Includes Adapter, Device and HealthAgent API
description. Proposed API can address Health source and sink roles.

Adapter methods exported on Health interface allow HealthAgent
registration. Device methods exported on Health interface allow
applications to actively created MCAP data channels and get informations
of the services exported by the endpoints.

HealthAgent allows applications to receive multiple data streams encoded
on IEEE 11073-20601 Personal Health Data Exchange Protocol.
---
 doc/health-api.txt |  141 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 141 insertions(+), 0 deletions(-)
 create mode 100644 doc/health-api.txt

diff --git a/doc/health-api.txt b/doc/health-api.txt
new file mode 100644
index 0000000..9be3d6c
--- /dev/null
+++ b/doc/health-api.txt
@@ -0,0 +1,141 @@
+BlueZ D-Bus Health API description
+*********************************
+
+Copyright (C) 2010 Elvis Pfützenreuter <epx@signove.com>
+
+Health Adapter hierarchy
+===============
+
+Service		org.bluez
+Interface	org.bluez.Health
+Object path	[variable prefix]/{hci0,hci1,...}
+
+This object hierarchy intend to be used by applications which want to enable
+a Health source or sink end point. The most common usage scenario is the sink
+role. Multi-channel protocol internals are hidden from the application interface.
+
+FD passing concept is used to allow applications to handle data channels.
+
+Methods:
+	uint32 RegisterRole(dict options, object path)
+
+		Register an HDP role to the adapter and assign the health agent
+		that shall be responsible by address data channel connections.
+		Returns an identification for the HDP context.
+
+		Records and channels are automatically closed when the agent
+		leaves the bus. Mandatory and optional options are described
+		below:
+			Optional parameters(dinamically assigned when not
+			informed):
+			uint16 CPSM: control channel
+			uint16 DPSM: data channel
+
+			Mandatory parameters:
+			array{dict EndPoint} EndPoints: Array of end points.
+				Possible EndPoint properties:
+				uint8 MDEPId: Mandatory MCAP Data End Point
+					Identification
+				uint16 DataType: Mandatory Endpoint data type
+				string Role: "source" or "sink"
+				string Description: Optional description
+
+	UnregisterRole(uint32 id)
+
+		Removes an HDP role and kills all connections related to it.
+
+Health Agent hierarchy
+===============
+
+Service		unique name
+Interface	org.bluez.HealthAgent
+Object path	freely definable
+
+Agent responsible by address data channels connections. Either active or
+passive connections are notified. FD passing mechanism is used to allows
+the HealthAgent to access directly the data encoded on IEEE 11073-20601
+Personal Health Data Exchange Protocol.
+
+Methods:
+	void NewMDLConnection(filedescriptor fd, dict properties)
+
+		This method gets called when a new data channel connection
+		is established. It addresses host and remote initiated
+		connections.
+
+		Properties:
+			uint8 LocalMDEPId:
+			uint8 RemoteMDEPId:
+			uint16 MDLId:
+			boolean Passive: Device initiated connection
+			boolean Reconnection: Previous established data
+				channel reestablished
+
+	void Release()
+
+		This method gets called whenever the service daemon unregisters
+		the agent or whenever the Adapter where the HealthAgent
+		registers itself is removed.
+
+Health Device hierarchy
+===============
+
+Service		org.bluez
+Interface	org.bluez.Health
+Object path	[variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
+
+The methods described below can be used by applications that need
+to manage data channel connections or get properties of service
+exported by the remote device.
+
+Methods:
+
+	void Connect(uint8 local_mdepid, uint8 remote_mdepid, uint16 mdlid)
+
+		Method used by applications that need to actively create
+		a data channel connection. When the remote connects back
+		in the data channel PSM, the connection is reported to
+		the registered HealthAgent.
+
+		mdlid greater than zero, means that the host is attempt
+		to reestablish a previous session. Zero means that new
+		session shall be established.
+
+		If the reconnection is not possible, a new connection
+		will be created (automatic fallback). The MDL ID may
+		change; the application needs to check it when receiving
+		Agent connection data.
+
+	void Disconnect(uint16  mdlid)
+
+		Closes the give data channel but keeps the MDL ID valid for
+		future reconnections.
+
+	void DeleteConnection(uint16 mdlid)
+
+		Closes the MDL connection and invalidates the MDL ID
+
+	dict GetProperties()
+
+		Returns the device properties.
+
+Signals:
+
+	PropertiesChanged()
+
+Properties:
+		array{MDEPs}: array of remote data end points. Attributes of
+			a MDEP entry are:
+				uint8 MDEPId
+				uint16 DataType
+				string Role: "source" or "sink"
+				string Description: optional  description
+
+		array{MDLs}: array of valid data channels. Mandatory
+			MDLs attributes are:
+			uint16 MDLId: Data channel identification
+			uint8 LocalMDEPId: Local data channel identification
+			uint8 RemoteMDEPId: Remote data channel identification
+
+		boolean Connected: true when the MCAP control channel is
+					established
-- 
1.7.0.4


^ permalink raw reply related

* More about proposed Health Device Profile API
From: Elvis Pfützenreuter @ 2010-04-30  2:28 UTC (permalink / raw)
  To: linux-bluetooth
  Cc: claudio.takahasi, Jose Antonio Santos Cadenas,
	Santiago Carot Nemesio, Vinicius Costa Gomes, raul.herbster

A very simple session graph that illustrates the proposed API can be seen at http://epx.com.br/health_bluez.png.

This API proposal was discussed jointly with Claudio Takahashi, Vinicius Gomes, Raul Herbster and others, and it is based on previous work on MCAP API specification by Jose Cadenas and Santiago Nemesio, which have been CCed.

The main guidelines of our current proposal are:

a) MCAP is just a protocol, which does not specify SDP records for its own needs. The HDP profile specifies the necessary SDP record, so we expose an API for HDP, not for MCAP.

b) Avoid exposing MCAP protocol details to the API client, except where unavoidable. Several MCAP parameters like PSM control channel may be left undefined by the client, and the implementation would fill them with reasonable values.

c) MCAP control connection (MCL) is completely taken care of. The application only deals with MCAP data channels (MDLs), if any.

d) The application should not need to handle SDP records, which are quite complicated in the HDP case. When it registers a HDP role, enough information is passed so a SDP record may be published automatically.

When a remote HDP device is present, its SDP record would be interpreted automatically, so the application can discover easily the MDEP IDs and data types offered by remote device.

e) The API allows for active connection, but still the application must register the HDP role beforehand. This ensures that a proper SDP record will have been published. (HDP sinks, the most common role a BlueZ-running device will play, require the SDP record).

f) HDP would be implemented inside bluetoothd as a plugin.

g) HDP itself does not specify the data channels' protocol (it is normally the IEEE 11073-20601, but there may be others in the future). The API client would receive L2CAP sockets for each data channel, and select()/recv()/send() them directly.

h) The application would be responsible by keeping the relationship between MDL IDs and IEEE sessions, if it wants to enjoy the reconnection feature. Also, it needs to be prepared to have a MDL socket closed, receive another one and continue the session over it.

If the application does not want to deal with reconnection, it may ask for MDL deletion upon socket closure, or just take the new socket as a brand new MDL.

The HDP plugin would keep the necessary state information (local and remote MDEP IDs) to allow easy (and transparent) reconnection.


Cadenas and Nemesio have already sent patches with an initial MCAP implementation, some time ago. We hope to build on their initial effort and collaborate with them in order to have HDP implemented as soon as possible and offering a very comfortable API for the clients.

--
Elvis

^ permalink raw reply

* Re: Modem emulator and DUN server side for oFono and BlueZ
From: Gustavo F. Padovan @ 2010-04-30  8:03 UTC (permalink / raw)
  To: ofono; +Cc: linux-bluetooth@vger.kernel.org
In-Reply-To: <33AB447FBD802F4E932063B962385B351D9FE1FA@shsmsx501.ccr.corp.intel.com>

Hi Zhenhua,

* Zhang, Zhenhua <zhenhua.zhang@intel.com> [2010-04-27 15:53:54 +0800]:

> Hi,
> 
> I am now working on modem emulator and one usage is for DUN server role. Since Padovan is working on client role, it's good to share my rough thinking for server side implementation. Here are the simple steps I have:
> 
> 1. Create an oFono emulator atom in oFono. It's the emulator manager that could create DUN, HFP AG or SPP type emulators. It exposes dbus methods like CreateEmulator, DestroyEmulator, GetProperty, etc.
> 
> 2. DUN agent server in BlueZ watch oFono and call CreateEmulator and pass the file descriptor to oFono. This server could further implement HFP AG and SPP connection.


Shouldn't the emulator register itself on the BlueZ agent server?


-- 
Gustavo F. Padovan
http://padovan.org
_______________________________________________
ofono mailing list
ofono@ofono.org
http://lists.ofono.org/listinfo/ofono

^ permalink raw reply

* Re: More about proposed Health Device Profile API
From: José Antonio Santos Cadenas @ 2010-04-30 10:59 UTC (permalink / raw)
  To: Elvis Pfützenreuter
  Cc: linux-bluetooth, claudio.takahasi, scarot, Vinicius Costa Gomes,
	raul.herbster, Pedro de las Heras Quirós
In-Reply-To: <8FDD2957-B97E-4646-B164-4B00E7910399@signove.com>

Hi all,

El Friday 30 April 2010 04:28:45 Elvis Pfützenreuter escribió:
> A very simple session graph that illustrates the proposed API can be seen
>  at http://epx.com.br/health_bluez.png.
> 
> This API proposal was discussed jointly with Claudio Takahashi, Vinicius
>  Gomes, Raul Herbster and others, and it is based on previous work on MCAP
>  API specification by Jose Cadenas and Santiago Nemesio, which have been
>  CCed.

That sounds great. We are happy that our ideas are good for you. We spent a 
lot of time on its desing. If you wish, we can have a meeting early next week 
via IRC or any other way in order to disscuss this api. We have already begun 
working in HDP so it will be very interesting to discuss this API with you. We 
have a quite mature implementation of MCAP. As you know, at the current moment 
we are having an internal reorganiation so we have publish an alternative git 
with the last mcap code (still working on it). Please, feel free to download 
and test it. All comments are wellcome.

git://193.147.51.48/git/mcap_devel.git

If you are interesting in the meeting please consider that in Madrid monday is 
holiday, so the first day that we can talk to you is Tuesday 4th.

> 
> The main guidelines of our current proposal are:
> 
> a) MCAP is just a protocol, which does not specify SDP records for its own
>  needs. The HDP profile specifies the necessary SDP record, so we expose an
>  API for HDP, not for MCAP.
> 
> b) Avoid exposing MCAP protocol details to the API client, except where
>  unavoidable. Several MCAP parameters like PSM control channel may be left
>  undefined by the client, and the implementation would fill them with
>  reasonable values.
> 
> c) MCAP control connection (MCL) is completely taken care of. The
>  application only deals with MCAP data channels (MDLs), if any.
> 
> d) The application should not need to handle SDP records, which are quite
>  complicated in the HDP case. When it registers a HDP role, enough
>  information is passed so a SDP record may be published automatically.
> 
> When a remote HDP device is present, its SDP record would be interpreted
>  automatically, so the application can discover easily the MDEP IDs and
>  data types offered by remote device.
> 
> e) The API allows for active connection, but still the application must
>  register the HDP role beforehand. This ensures that a proper SDP record
>  will have been published. (HDP sinks, the most common role a BlueZ-running
>  device will play, require the SDP record).
> 
> f) HDP would be implemented inside bluetoothd as a plugin.
> 
> g) HDP itself does not specify the data channels' protocol (it is normally
>  the IEEE 11073-20601, but there may be others in the future). The API
>  client would receive L2CAP sockets for each data channel, and
>  select()/recv()/send() them directly.
> 
> h) The application would be responsible by keeping the relationship between
>  MDL IDs and IEEE sessions, if it wants to enjoy the reconnection feature.
>  Also, it needs to be prepared to have a MDL socket closed, receive another
>  one and continue the session over it.
> 
> If the application does not want to deal with reconnection, it may ask for
>  MDL deletion upon socket closure, or just take the new socket as a brand
>  new MDL.
> 
> The HDP plugin would keep the necessary state information (local and remote
>  MDEP IDs) to allow easy (and transparent) reconnection.

This is more or less what we think. We believe that is better to talk it by 
IRC. By now he have a sequence diagram with the interaction of HDP with the 
clients (agents or managers).

http://193.147.51.48/hdp_sequence.png

> 
> 
> Cadenas and Nemesio have already sent patches with an initial MCAP
>  implementation, some time ago. We hope to build on their initial effort
>  and collaborate with them in order to have HDP implemented as soon as
>  possible and offering a very comfortable API for the clients.

The patches are now obsolete, because we made a lot of changes on it. We 
haven't sent it yet because we are still waiting for comments about the 
architecture proposed in last patches. It will be better to discard those 
patches. We can also comment/explain the MCAP arquitecture if we have a 
meeting. We are planning to send new patches with all the changes next week. 

> 

Regards

Jose and Santiago

PS: Also CCing Pedro de-las-Heras who is also working with us.

> --
> Elvis
> 

^ permalink raw reply

* Re: More about proposed Health Device Profile API
From: José Antonio Santos Cadenas @ 2010-04-30 11:05 UTC (permalink / raw)
  To: Elvis Pfützenreuter
  Cc: linux-bluetooth, claudio.takahasi, scarot, Vinicius Costa Gomes,
	raul.herbster, Pedro de las Heras Quirós
In-Reply-To: <201004301259.45561.jcaden@libresoft.es>

Sorry, Santiago's address was wrong.

Please answer to this mail.

El Friday 30 April 2010 12:59:45 José Antonio Santos Cadenas escribió:
> Hi all,
> 
> El Friday 30 April 2010 04:28:45 Elvis Pfützenreuter escribió:
> > A very simple session graph that illustrates the proposed API can be seen
> >  at http://epx.com.br/health_bluez.png.
> >
> > This API proposal was discussed jointly with Claudio Takahashi, Vinicius
> >  Gomes, Raul Herbster and others, and it is based on previous work on
> > MCAP API specification by Jose Cadenas and Santiago Nemesio, which have
> > been CCed.
> 
> That sounds great. We are happy that our ideas are good for you. We spent a
> lot of time on its desing. If you wish, we can have a meeting early next
>  week via IRC or any other way in order to disscuss this api. We have
>  already begun working in HDP so it will be very interesting to discuss
>  this API with you. We have a quite mature implementation of MCAP. As you
>  know, at the current moment we are having an internal reorganiation so we
>  have publish an alternative git with the last mcap code (still working on
>  it). Please, feel free to download and test it. All comments are wellcome.
> 
> git://193.147.51.48/git/mcap_devel.git
> 
> If you are interesting in the meeting please consider that in Madrid monday
>  is holiday, so the first day that we can talk to you is Tuesday 4th.
> 
> > The main guidelines of our current proposal are:
> >
> > a) MCAP is just a protocol, which does not specify SDP records for its
> > own needs. The HDP profile specifies the necessary SDP record, so we
> > expose an API for HDP, not for MCAP.
> >
> > b) Avoid exposing MCAP protocol details to the API client, except where
> >  unavoidable. Several MCAP parameters like PSM control channel may be
> > left undefined by the client, and the implementation would fill them with
> > reasonable values.
> >
> > c) MCAP control connection (MCL) is completely taken care of. The
> >  application only deals with MCAP data channels (MDLs), if any.
> >
> > d) The application should not need to handle SDP records, which are quite
> >  complicated in the HDP case. When it registers a HDP role, enough
> >  information is passed so a SDP record may be published automatically.
> >
> > When a remote HDP device is present, its SDP record would be interpreted
> >  automatically, so the application can discover easily the MDEP IDs and
> >  data types offered by remote device.
> >
> > e) The API allows for active connection, but still the application must
> >  register the HDP role beforehand. This ensures that a proper SDP record
> >  will have been published. (HDP sinks, the most common role a
> > BlueZ-running device will play, require the SDP record).
> >
> > f) HDP would be implemented inside bluetoothd as a plugin.
> >
> > g) HDP itself does not specify the data channels' protocol (it is
> > normally the IEEE 11073-20601, but there may be others in the future).
> > The API client would receive L2CAP sockets for each data channel, and
> >  select()/recv()/send() them directly.
> >
> > h) The application would be responsible by keeping the relationship
> > between MDL IDs and IEEE sessions, if it wants to enjoy the reconnection
> > feature. Also, it needs to be prepared to have a MDL socket closed,
> > receive another one and continue the session over it.
> >
> > If the application does not want to deal with reconnection, it may ask
> > for MDL deletion upon socket closure, or just take the new socket as a
> > brand new MDL.
> >
> > The HDP plugin would keep the necessary state information (local and
> > remote MDEP IDs) to allow easy (and transparent) reconnection.
> 
> This is more or less what we think. We believe that is better to talk it by
> IRC. By now he have a sequence diagram with the interaction of HDP with the
> clients (agents or managers).
> 
> http://193.147.51.48/hdp_sequence.png
> 
> > Cadenas and Nemesio have already sent patches with an initial MCAP
> >  implementation, some time ago. We hope to build on their initial effort
> >  and collaborate with them in order to have HDP implemented as soon as
> >  possible and offering a very comfortable API for the clients.
> 
> The patches are now obsolete, because we made a lot of changes on it. We
> haven't sent it yet because we are still waiting for comments about the
> architecture proposed in last patches. It will be better to discard those
> patches. We can also comment/explain the MCAP arquitecture if we have a
> meeting. We are planning to send new patches with all the changes next
>  week.
> 
> 
> 
> Regards
> 
> Jose and Santiago
> 
> PS: Also CCing Pedro de-las-Heras who is also working with us.
> 
> > --
> > Elvis
> 

^ permalink raw reply

* [PATCH] hid/hid-sony: fix sony_set_operational_bt
From: Antonio Ospite @ 2010-04-30 19:53 UTC (permalink / raw)
  To: linux-input; +Cc: Antonio Ospite, cbe-oss-dev, linux-bluetooth, hadess

Don't send the report type as part of the data, this prevents the
controller from going into the operational state at all.

This is completely equivalent to what the code originally meant to accomplish:
as per in net/bluetooth/hidp/core.c::hidp_output_raw_report(), by using
HID_FEATURE_REPORT here, what will be actually sent is
(HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_FEATURE) which is exactly 0x53.

Signed-off-by: Antonio Ospite <ospite@studenti.unina.it>
---

As this change makes sony_set_operational_bt work while it was non working
before, it might be good to have it in 2.6.34.

Thanks,
   Antonio Ospite
   http://ao2.it

 drivers/hid/hid-sony.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index 7502a4b..402d557 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -76,7 +76,7 @@ static int sony_set_operational_usb(struct hid_device *hdev)
 
 static int sony_set_operational_bt(struct hid_device *hdev)
 {
-	unsigned char buf[] = { 0x53, 0xf4,  0x42, 0x03, 0x00, 0x00 };
+	unsigned char buf[] = { 0xf4,  0x42, 0x03, 0x00, 0x00 };
 	return hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT);
 }
 
-- 
1.7.1


^ permalink raw reply related

* Re: [PATCH] Health Device Profile API
From: Luiz Augusto von Dentz @ 2010-05-03 13:51 UTC (permalink / raw)
  To: Elvis Pfutzenreuter; +Cc: linux-bluetooth
In-Reply-To: <1272591852-4961-1-git-send-email-epx@signove.com>

Hi Elvis,

On Fri, Apr 30, 2010 at 4:44 AM, Elvis Pfutzenreuter <epx@signove.com> wrote:
> Initial Health API proposal. Includes Adapter, Device and HealthAgent API
> description. Proposed API can address Health source and sink roles.
>
> Adapter methods exported on Health interface allow HealthAgent
> registration. Device methods exported on Health interface allow
> applications to actively created MCAP data channels and get informations
> of the services exported by the endpoints.
>
> HealthAgent allows applications to receive multiple data streams encoded
> on IEEE 11073-20601 Personal Health Data Exchange Protocol.
> ---
>  doc/health-api.txt |  141 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 files changed, 141 insertions(+), 0 deletions(-)
>  create mode 100644 doc/health-api.txt
>
> diff --git a/doc/health-api.txt b/doc/health-api.txt
> new file mode 100644
> index 0000000..9be3d6c
> --- /dev/null
> +++ b/doc/health-api.txt
> @@ -0,0 +1,141 @@
> +BlueZ D-Bus Health API description
> +*********************************
> +
> +Copyright (C) 2010 Elvis Pfützenreuter <epx@signove.com>
> +
> +Health Adapter hierarchy
> +===============
> +
> +Service                org.bluez
> +Interface      org.bluez.Health
> +Object path    [variable prefix]/{hci0,hci1,...}
> +
> +This object hierarchy intend to be used by applications which want to enable
> +a Health source or sink end point. The most common usage scenario is the sink
> +role. Multi-channel protocol internals are hidden from the application interface.
> +
> +FD passing concept is used to allow applications to handle data channels.
> +
> +Methods:
> +       uint32 RegisterRole(dict options, object path)
> +
> +               Register an HDP role to the adapter and assign the health agent
> +               that shall be responsible by address data channel connections.
> +               Returns an identification for the HDP context.
> +
> +               Records and channels are automatically closed when the agent
> +               leaves the bus. Mandatory and optional options are described
> +               below:
> +                       Optional parameters(dinamically assigned when not
> +                       informed):
> +                       uint16 CPSM: control channel
> +                       uint16 DPSM: data channel
> +
> +                       Mandatory parameters:
> +                       array{dict EndPoint} EndPoints: Array of end points.
> +                               Possible EndPoint properties:
> +                               uint8 MDEPId: Mandatory MCAP Data End Point
> +                                       Identification
> +                               uint16 DataType: Mandatory Endpoint data type
> +                               string Role: "source" or "sink"
> +                               string Description: Optional description
> +
> +       UnregisterRole(uint32 id)
> +
> +               Removes an HDP role and kills all connections related to it.

I guess you wont be reusing the path for multiple roles, so I guess
you can unregister by path which IMHO are a better identifier in case
of D-Bus.

> +Health Agent hierarchy
> +===============
> +
> +Service                unique name
> +Interface      org.bluez.HealthAgent
> +Object path    freely definable
> +
> +Agent responsible by address data channels connections. Either active or
> +passive connections are notified. FD passing mechanism is used to allows
> +the HealthAgent to access directly the data encoded on IEEE 11073-20601
> +Personal Health Data Exchange Protocol.
> +
> +Methods:
> +       void NewMDLConnection(filedescriptor fd, dict properties)
> +
> +               This method gets called when a new data channel connection
> +               is established. It addresses host and remote initiated
> +               connections.
> +
> +               Properties:
> +                       uint8 LocalMDEPId:
> +                       uint8 RemoteMDEPId:
> +                       uint16 MDLId:
> +                       boolean Passive: Device initiated connection
> +                       boolean Reconnection: Previous established data
> +                               channel reestablished
> +
> +       void Release()
> +
> +               This method gets called whenever the service daemon unregisters
> +               the agent or whenever the Adapter where the HealthAgent
> +               registers itself is removed.

You might want to pass the MTU of the connection if the fd represent a
l2cap socket and probably inform the device path so the agent know who
is connecting, or even better if you have path to represent the
connection see the next connect for more details.

> +Health Device hierarchy
> +===============
> +
> +Service                org.bluez
> +Interface      org.bluez.Health
> +Object path    [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
> +
> +The methods described below can be used by applications that need
> +to manage data channel connections or get properties of service
> +exported by the remote device.
> +
> +Methods:
> +
> +       void Connect(uint8 local_mdepid, uint8 remote_mdepid, uint16 mdlid)
> +
> +               Method used by applications that need to actively create
> +               a data channel connection. When the remote connects back
> +               in the data channel PSM, the connection is reported to
> +               the registered HealthAgent.
> +
> +               mdlid greater than zero, means that the host is attempt
> +               to reestablish a previous session. Zero means that new
> +               session shall be established.
> +
> +               If the reconnection is not possible, a new connection
> +               will be created (automatic fallback). The MDL ID may
> +               change; the application needs to check it when receiving
> +               Agent connection data.
> +
> +       void Disconnect(uint16  mdlid)
> +
> +               Closes the give data channel but keeps the MDL ID valid for
> +               future reconnections.
> +
> +       void DeleteConnection(uint16 mdlid)
> +
> +               Closes the MDL connection and invalidates the MDL ID
> +
> +       dict GetProperties()
> +
> +               Returns the device properties.
> +
> +Signals:
> +
> +       PropertiesChanged()
> +
> +Properties:
> +               array{MDEPs}: array of remote data end points. Attributes of
> +                       a MDEP entry are:
> +                               uint8 MDEPId
> +                               uint16 DataType
> +                               string Role: "source" or "sink"
> +                               string Description: optional  description
> +
> +               array{MDLs}: array of valid data channels. Mandatory
> +                       MDLs attributes are:
> +                       uint16 MDLId: Data channel identification
> +                       uint8 LocalMDEPId: Local data channel identification
> +                       uint8 RemoteMDEPId: Remote data channel identification
> +
> +               boolean Connected: true when the MCAP control channel is
> +                                       established

I guess it would be better if Connect takes a dict in this case, so we
can easily omit options if necessary, also I would suggest to return a
new path representing the connection, which can be reused, that one
has Connect/Disconnect and mdl properties, the reason for that is to
avoid array of structs which are not very nice to parse and confusing
to debug.

In case of MDEP we might want to have two interfaces
org.bluez.HealthSink and org.bluez.HealthSource, this should fix the
reusing the interface name with different methods in adapter path and
device path, internally the code can take care of matching local ones
with remotes ones, well at least we did that for a2dp sink/source
although it is a completely different profile I think it might be
useful to have a similar logic in this case otherwise the ui (if there
is any in this case) has to figure out a lot of details to connect,
again in a2dp there is also sink/source concept and support for
multiples remote/local seps but we try to hide as much as possible in
the API.

-- 
Luiz Augusto von Dentz
Computer Engineer

^ permalink raw reply

* Re: [PATCH] Health Device Profile API
From: Elvis Pfützenreuter @ 2010-05-03 14:10 UTC (permalink / raw)
  To: Luiz Augusto von Dentz; +Cc: linux-bluetooth
In-Reply-To: <o2k2d5a2c101005030651ke836a891q7544e54e89201777@mail.gmail.com>

Thanks for the feedback! I will work on this and submit a new proposal tomorrow.


^ permalink raw reply

* Re: [PATCH] Health Device Profile API
From: Elvis Pfützenreuter @ 2010-05-03 14:23 UTC (permalink / raw)
  To: Luiz Augusto von Dentz; +Cc: linux-bluetooth
In-Reply-To: <o2k2d5a2c101005030651ke836a891q7544e54e89201777@mail.gmail.com>

In the case of HDP, the same device may support two different roles (source and sink) on a single SDP record. For example, a data concentrator that collects data from sensors and forwards to a PC will have a number of source and sink MDEPs, all of them under a single SDP record and a single "listening" L2CAP socket for MCAP control channel. When a remoty party connects, only the data channels that match are connected (both of same data type, one side source, other side sink).

That's why we chose a single interface, a generic RegisterRole(), and role appears again in EndPoint dict.

> In case of MDEP we might want to have two interfaces
> org.bluez.HealthSink and org.bluez.HealthSource, this should fix the
> reusing the interface name with different methods in adapter path and
> device path, internally the code can take care of matching local ones
> with remotes ones, well at least we did that for a2dp sink/source
> although it is a completely different profile I think it might be
> useful to have a similar logic in this case otherwise the ui (if there
> is any in this case) has to figure out a lot of details to connect,
> again in a2dp there is also sink/source concept and support for
> multiples remote/local seps but we try to hide as much as possible in
> the API.
> 
> -- 
> Luiz Augusto von Dentz
> Computer Engineer


^ permalink raw reply

* Re: [PATCH] hid/hid-sony: fix sony_set_operational_bt
From: Jiri Kosina @ 2010-05-03 15:19 UTC (permalink / raw)
  To: Bastien Nocera; +Cc: Antonio Ospite, linux-input, linux-bluetooth
In-Reply-To: <1272583410.2582.441.camel@localhost.localdomain>

On Fri, 30 Apr 2010, Bastien Nocera wrote:

> > Don't send the report type as part of the data, this prevents the
> > controller from going into the operational state at all.
> > 
> > This is completely equivalent to what the code originally meant to accomplish:
> > as per in net/bluetooth/hidp/core.c::hidp_output_raw_report(), by using
> > HID_FEATURE_REPORT here, what will be actually sent is
> > (HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_FEATURE) which is exactly 0x53.
> > 
> > Signed-off-by: Antonio Ospite <ospite@studenti.unina.it>
> Signed-off-by: Bastien Nocera <hadess@hadess.net>

Applied, thanks.

-- 
Jiri Kosina
SUSE Labs, Novell Inc.

^ permalink raw reply

* Bluez wiki
From: A. Craig West @ 2010-05-03 17:35 UTC (permalink / raw)
  To: linux-bluetooth

I'm not sure who to report this to, but the wiki site is down, and has
been for most of the weekend. I am hoping there is somebody on the
list who is able to fix it...
-Craig

^ permalink raw reply

* [PATCH 0/2] Groundwork for Sixaxis paring into bluez.
From: Antonio Ospite @ 2010-05-03 20:15 UTC (permalink / raw)
  To: linux-input; +Cc: Antonio Ospite, Bastien Nocera, linux-bluetooth, cbe-oss-dev

Hi,

These changes are in preparation of seamless integration of the Sixaxis
pairing procedure into bluez. It was about time :)

The first one cleans up the quirks handling.

The second one adds a sysfs interface to set and get the device and master
bdaddr, this is needed in order to setup the host for pairing. This kernel
interface is a mere workaround for the lack of features of hidraw[1], if
someone wants to add the missing features to hidraw I will be happy to drop
this one, otherwise consider queuing these for 2.6.35 if it's too late for
2.6.34.

Thanks,
   Antonio

[1] http://www.spinics.net/lists/linux-input/msg08311.html


Antonio Ospite (2):
  hid/hid-sony: Apply sixaxis quirks only to sixaxis
  hid/hid-sony: get and set Sixaxis bdaddr via sysfs

 drivers/hid/hid-sony.c |  170 +++++++++++++++++++++++++++++++++++++++++++----
 1 files changed, 155 insertions(+), 15 deletions(-)

-- 
Antonio Ospite
http://ao2.it

PGP public key ID: 0x4553B001

A: Because it messes up the order in which people normally read text.
   See http://en.wikipedia.org/wiki/Posting_style
Q: Why is top-posting such a bad thing?
A: Top-posting.
Q: What is the most annoying thing in e-mail?

^ permalink raw reply

* [PATCH 1/2] hid/hid-sony: Apply sixaxis quirks only to sixaxis
From: Antonio Ospite @ 2010-05-03 20:15 UTC (permalink / raw)
  To: linux-input; +Cc: Antonio Ospite, Bastien Nocera, linux-bluetooth, cbe-oss-dev
In-Reply-To: <1272917756-31098-1-git-send-email-ospite@studenti.unina.it>

Be more explicit and avoid calling sony_set_operational_usb() when we
have USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE.

While at it, rename the sony_set_operational routines to
sixaxis_set_operational as they are sixaxis specific.

This is also in preparation for the sysfs interface to set and get bdaddr
over usb and for some other Sixaxis report fixup.

Signed-off-by: Antonio Ospite <ospite@studenti.unina.it>
---
 drivers/hid/hid-sony.c |   28 ++++++++++++++--------------
 1 files changed, 14 insertions(+), 14 deletions(-)

diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index 402d557..d61f268 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -24,7 +24,9 @@
 
 #include "hid-ids.h"
 
-#define VAIO_RDESC_CONSTANT 0x0001
+#define VAIO_RDESC_CONSTANT     (1 << 0)
+#define SIXAXIS_CONTROLLER_USB  (1 << 1)
+#define SIXAXIS_CONTROLLER_BT   (1 << 2)
 
 struct sony_sc {
 	unsigned long quirks;
@@ -49,7 +51,7 @@ static void sony_report_fixup(struct hid_device *hdev, __u8 *rdesc,
  * to "operational".  Without this, the ps3 controller will not report any
  * events.
  */
-static int sony_set_operational_usb(struct hid_device *hdev)
+static int sixaxis_set_operational_usb(struct hid_device *hdev)
 {
 	struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
 	struct usb_device *dev = interface_to_usbdev(intf);
@@ -74,7 +76,7 @@ static int sony_set_operational_usb(struct hid_device *hdev)
 	return ret;
 }
 
-static int sony_set_operational_bt(struct hid_device *hdev)
+static int sixaxis_set_operational_bt(struct hid_device *hdev)
 {
 	unsigned char buf[] = { 0xf4,  0x42, 0x03, 0x00, 0x00 };
 	return hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT);
@@ -108,16 +110,12 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
 		goto err_free;
 	}
 
-	switch (hdev->bus) {
-	case BUS_USB:
-		ret = sony_set_operational_usb(hdev);
-		break;
-	case BUS_BLUETOOTH:
-		ret = sony_set_operational_bt(hdev);
-		break;
-	default:
+	if (sc->quirks & SIXAXIS_CONTROLLER_USB)
+		ret = sixaxis_set_operational_usb(hdev);
+	else if (sc->quirks & SIXAXIS_CONTROLLER_BT)
+		ret = sixaxis_set_operational_bt(hdev);
+	else
 		ret = 0;
-	}
 
 	if (ret < 0)
 		goto err_stop;
@@ -137,8 +135,10 @@ static void sony_remove(struct hid_device *hdev)
 }
 
 static const struct hid_device_id sony_devices[] = {
-	{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
-	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER),
+		.driver_data = SIXAXIS_CONTROLLER_USB },
+	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER),
+		.driver_data = SIXAXIS_CONTROLLER_BT },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE),
 		.driver_data = VAIO_RDESC_CONSTANT },
 	{ }
-- 
1.7.1


^ permalink raw reply related

* [PATCH 2/2] hid/hid-sony: get and set Sixaxis bdaddr via sysfs
From: Antonio Ospite @ 2010-05-03 20:15 UTC (permalink / raw)
  To: linux-input; +Cc: Antonio Ospite, Bastien Nocera, linux-bluetooth, cbe-oss-dev
In-Reply-To: <1272917756-31098-1-git-send-email-ospite@studenti.unina.it>

Expose to userspace a simple way to get device bdaddr, and get/set master
bdaddr on Sixaxis controller.

Right now userspace softwares which manage pairing the controller with a
bluetooth adapter, rely on libusb and hence have to detach and reattach
usbhid, which is not very nice.

Signed-off-by: Antonio Ospite <ospite@studenti.unina.it>
---

As said, ideally this should be done with an interface such as hidraw, but its
limitations prevent us from doing it the right way; and I am not going to
touch hidraw myself anytime soon.

Regards,
   Antonio

 drivers/hid/hid-sony.c |  148 +++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 145 insertions(+), 3 deletions(-)

diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index d61f268..1b611ec 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -47,6 +47,131 @@ static void sony_report_fixup(struct hid_device *hdev, __u8 *rdesc,
 }
 
 /*
+ * Show and set the master bdaddr for PS3 controller, without disconnecting
+ * the device.
+ */
+static ssize_t show_sixaxis_master_bdaddr(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct usb_interface *intf = to_usb_interface(dev);
+	struct usb_device *udev = interface_to_usbdev(intf);
+	__u16 ifnum = intf->cur_altsetting->desc.bInterfaceNumber;
+	int ret;
+	unsigned char *mbuf = kmalloc(9, GFP_KERNEL);
+
+	if (!mbuf)
+		return -ENOMEM;
+
+	ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+				 HID_REQ_GET_REPORT,
+				 USB_DIR_IN | USB_TYPE_CLASS |
+				 USB_RECIP_INTERFACE,
+				 (3 << 8) | 0xf5, ifnum, mbuf, 8,
+				 USB_CTRL_GET_TIMEOUT);
+	if (ret < 0)
+		dev_err(dev, "%s failed to get master bdaddr, ret: %d\n",
+				__func__, ret);
+	else
+		/* 18 is strlen("00:00:00:00:00:00\n") */
+		ret = snprintf(buf, 18, "%02x:%02x:%02x:%02x:%02x:%02x\n",
+				mbuf[2], mbuf[3], mbuf[4],
+				mbuf[5], mbuf[6], mbuf[7]);
+
+	kfree(mbuf);
+
+	return ret;
+}
+
+static ssize_t store_sixaxis_master_bdaddr(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct usb_interface *intf = to_usb_interface(dev);
+	struct usb_device *udev = interface_to_usbdev(intf);
+	__u16 ifnum = intf->cur_altsetting->desc.bInterfaceNumber;
+	int ret;
+	unsigned char *mbuf = kmalloc(9, GFP_KERNEL);
+
+	mbuf[0] = 0x01;
+	mbuf[1] = 0x00;
+	ret = sscanf(buf, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
+			&mbuf[2], &mbuf[3], &mbuf[4],
+			&mbuf[5], &mbuf[6], &mbuf[7]);
+	if (ret != 6) {
+		dev_err(dev, "%s failed, ret: %d\n", __func__, ret);
+		return -EINVAL;
+	}
+
+	ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+				 HID_REQ_SET_REPORT,
+				 USB_DIR_OUT | USB_TYPE_CLASS |
+				 USB_RECIP_INTERFACE,
+				 (3 << 8) | 0xf5, ifnum, mbuf, 8,
+				 USB_CTRL_GET_TIMEOUT);
+
+	kfree(mbuf);
+
+	if (ret < 0) {
+		dev_err(dev, "%s failed to set master bdaddr, ret: %d\n",
+				__func__, ret);
+		return ret;
+	}
+
+	return count;
+}
+
+/*
+ * Show the bdaddr for PS3 controller, without disconnecting the device.
+ */
+static ssize_t show_sixaxis_bdaddr(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct usb_interface *intf = to_usb_interface(dev);
+	struct usb_device *udev = interface_to_usbdev(intf);
+	__u16 ifnum = intf->cur_altsetting->desc.bInterfaceNumber;
+	int ret;
+	unsigned char *mbuf = kmalloc(18, GFP_KERNEL);
+
+	if (!mbuf)
+		return -ENOMEM;
+
+	ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+				 HID_REQ_GET_REPORT,
+				 USB_DIR_IN | USB_TYPE_CLASS |
+				 USB_RECIP_INTERFACE,
+				 (3 << 8) | 0xf2, ifnum, mbuf, 17,
+				 USB_CTRL_GET_TIMEOUT);
+	if (ret < 0)
+		dev_err(dev, "%s failed to get device bdaddr, ret: %d\n",
+				__func__, ret);
+	else
+		/* 18 is strlen("00:00:00:00:00:00\n") */
+		ret = snprintf(buf, 18, "%02x:%02x:%02x:%02x:%02x:%02x\n",
+				mbuf[4], mbuf[5], mbuf[6],
+				mbuf[7], mbuf[8], mbuf[9]);
+
+	kfree(mbuf);
+
+	return ret;
+}
+
+static DEVICE_ATTR(sixaxis_master_bdaddr, S_IWUSR|S_IRUGO,
+		show_sixaxis_master_bdaddr, store_sixaxis_master_bdaddr);
+
+static DEVICE_ATTR(sixaxis_bdaddr, S_IRUGO,
+		show_sixaxis_bdaddr, NULL);
+
+static struct attribute *sixaxis_attributes[] = {
+	&dev_attr_sixaxis_master_bdaddr.attr,
+	&dev_attr_sixaxis_bdaddr.attr,
+	NULL
+};
+
+static const struct attribute_group sixaxis_attr_group = {
+	.attrs = sixaxis_attributes,
+};
+
+
+/*
  * Sending HID_REQ_GET_REPORT changes the operation mode of the ps3 controller
  * to "operational".  Without this, the ps3 controller will not report any
  * events.
@@ -57,8 +182,9 @@ static int sixaxis_set_operational_usb(struct hid_device *hdev)
 	struct usb_device *dev = interface_to_usbdev(intf);
 	__u16 ifnum = intf->cur_altsetting->desc.bInterfaceNumber;
 	int ret;
-	char *buf = kmalloc(18, GFP_KERNEL);
+	unsigned char *buf = kmalloc(18, GFP_KERNEL);
 
+	dev_info(&hdev->dev, "Calling %s\n", __func__);
 	if (!buf)
 		return -ENOMEM;
 
@@ -70,6 +196,9 @@ static int sixaxis_set_operational_usb(struct hid_device *hdev)
 				 USB_CTRL_GET_TIMEOUT);
 	if (ret < 0)
 		dev_err(&hdev->dev, "can't set operational mode\n");
+	else
+		dev_info(&hdev->dev, "Sony PS3 Controller bdaddr: %02x:%02x:%02x:%02x:%02x:%02x\n",
+				buf[4], buf[5], buf[6], buf[7], buf[8], buf[9]);
 
 	kfree(buf);
 
@@ -110,9 +239,17 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
 		goto err_free;
 	}
 
-	if (sc->quirks & SIXAXIS_CONTROLLER_USB)
+	if (sc->quirks & SIXAXIS_CONTROLLER_USB) {
+		struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
+		ret = sysfs_create_group(&intf->dev.kobj, &sixaxis_attr_group);
+		if (ret < 0) {
+			dev_err(&hdev->dev,
+				"cannot register sixaxis sysfs hooks\n");
+			goto err_stop;
+		}
+
 		ret = sixaxis_set_operational_usb(hdev);
-	else if (sc->quirks & SIXAXIS_CONTROLLER_BT)
+	} else if (sc->quirks & SIXAXIS_CONTROLLER_BT)
 		ret = sixaxis_set_operational_bt(hdev);
 	else
 		ret = 0;
@@ -130,6 +267,11 @@ err_free:
 
 static void sony_remove(struct hid_device *hdev)
 {
+	struct sony_sc *sc = hid_get_drvdata(hdev);
+	if (sc->quirks & SIXAXIS_CONTROLLER_USB) {
+		struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
+		sysfs_remove_group(&intf->dev.kobj, &sixaxis_attr_group);
+	}
 	hid_hw_stop(hdev);
 	kfree(hid_get_drvdata(hdev));
 }
-- 
1.7.1


^ permalink raw reply related

* [PATCH] Health Device Profile API
From: Elvis Pfützenreuter @ 2010-05-03 23:22 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: epx

From: Elvis Pfutzenreuter <epx@signove.com>

Initial Health API proposal. Includes Adapter, Device and HealthAgent API
description. Proposed API can address Health source and sink roles.

Adapter methods exported on Health interface allow HealthAgent
registration. Device methods exported on Health interface allow
applications to actively created MCAP data channels and get informations
of the services exported by the endpoints.

HealthAgent allows applications to receive multiple data streams encoded
on IEEE 11073-20601 Personal Health Data Exchange Protocol.

v2 changes: added HealthData connection object interface
	    assumes at most one HDP set of roles per application
---
 doc/health-api.txt |  187 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 187 insertions(+), 0 deletions(-)
 create mode 100644 doc/health-api.txt

diff --git a/doc/health-api.txt b/doc/health-api.txt
new file mode 100644
index 0000000..49c043d
--- /dev/null
+++ b/doc/health-api.txt
@@ -0,0 +1,187 @@
+BlueZ D-Bus Health API description
+*********************************
+
+Copyright (C) 2010 Elvis Pfutzenreuter <epx@signove.com>
+
+Health Adapter hierarchy
+========================
+
+Service		org.bluez
+Interface	org.bluez.Health
+Object path	[variable prefix]/{hci0,hci1,...}
+
+This object hierarchy intend to be used by applications which want to enable
+a Health source or sink end point. The most common usage scenario is the sink
+role. Multi-channel protocol internals are hidden from the application interface.
+
+Methods:
+	void RegisterRole(dict options, object path)
+
+		Register an HDP role to the adapter and assign the health agent
+		that shall be responsible by address data channel connections.
+
+		Records and channels are automatically closed when the agent
+		leaves the bus. Mandatory and optional options are described
+		below:
+			Optional parameters (dinamically assigned when not
+			informed):
+			uint16 CPSM: control channel PSM
+			uint16 DPSM: data channel PSM
+
+			Mandatory parameters:
+			array{dict EndPoint} EndPoints: Array of end points.
+				Possible EndPoint properties:
+				uint8 MDEPId: Mandatory MCAP Data End Point
+					Identification
+				uint16 DataType: Mandatory Endpoint data type
+				string Role: "source" or "sink"
+				string Description: Optional description
+
+	UnregisterRole()
+
+		Removes an HDP role and kills all connections related to it.
+
+
+Health Agent hierarchy
+======================
+
+Service		unique name
+Interface	org.bluez.HealthAgent
+Object path	freely definable
+
+Agent responsible by address data channels connections. Either active or
+passive connections are notified. FD passing mechanism is used to allows
+the HealthAgent to access directly the data encoded on IEEE 11073-20601
+Personal Health Data Exchange Protocol.
+
+Methods:
+	void NewDataConnection(dict properties)
+
+		This method gets called when a new data channel connection
+		is established. It addresses host and remote initiated
+		connections.
+
+		Properties:
+			string device_path: the Health Device object path
+			string connection_path: the HealthData object path
+			boolean Passive: Device initiated connection
+			boolean Reconnection: Previous established data
+				channel reestablished
+
+	void Release()
+
+		This method gets called whenever the service daemon unregisters
+		the agent or whenever the Adapter where the HealthAgent
+		registers itself is removed.
+
+
+Health Device hierarchy
+=======================
+
+Service		org.bluez
+Interface	org.bluez.Health
+Object path	[variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
+
+The methods described below can be used by applications that need
+to manage data channel connections or get properties of service
+exported by the remote device.
+
+Methods:
+
+	string Connect(dict parameters)
+
+		Method used by applications that need to actively create
+		a data channel connection. When the remote connects back
+		in the data channel PSM, the connection is reported to
+		the registered HealthAgent.
+
+		Returns an HDP Connection object path. The same path
+		will be received again via Agent, when connection is
+		established.
+
+		If the reconnection is not possible, a new connection
+		will be created (automatic fallback). The MDL ID may
+		change; the application needs to check HealthData object
+		MDLId property when connection is notified via Agent.
+
+		Mandatory parameters:
+			uint8 local_mdepid: local MDEP ID (*)
+			uint8 remote_mdepid: remote MDEP ID
+		Optional parameters:
+			uint16 mdlid: MDL ID, if trying reconnection
+
+		(*) Was defined by application when it called RegisterRole().
+
+	dict GetProperties()
+
+		Returns the device properties.
+
+Signals:
+
+	PropertiesChanged()
+
+Properties:
+		array{MDEPs}: array of remote data end points. Attributes of
+			a MDEP entry are:
+				uint8 MDEPId
+				uint16 DataType
+				string Role: "source" or "sink"
+				string Description: optional description
+
+		array{MDLs}: array of open data channels. Mandatory
+			MDLs attributes are:
+			uint16 MDLId: Data channel identification
+			string path: HealthData object path
+
+		boolean Connected: true when the MCAP control channel is
+					established
+
+
+Health Device Data Connection hierarchy
+=======================================
+
+Service		org.bluez
+Interface	org.bluez.HealthData
+Object path	[variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/YYYYYYYYYYY
+
+A HealthData object can be created either by active connection of by
+receiving a connection via HealthAgent.
+
+FD passing concept is used to allow applications to handle data channels.
+
+A HealthData object exists only while the data connection is up. If the
+connection is broken, the object is removed. The application may
+(and should) handle this situation, by trying a reconnection.
+If the connection was not deleted, the MDL ID outlives the respective
+HealthData object. (Another sign of connection breakage is file
+descriptor closure.)
+
+The methods described below can be used by applications to manage
+a single data channel or get its properties.
+
+Methods:
+
+	filedescriptor GetFD()
+
+		Obtains the raw filedescriptor for this data channel
+		(most likely a L2CAP socket.)
+
+	void Disconnect()
+
+		Closes this data channel and destroys the object,
+		but keeps the MDL ID valid for future reconnections.
+
+	void DeleteConnection()
+
+		Closes this data channel, destroys the object
+		 and invalidates the MDL ID.
+
+	dict GetProperties()
+
+		Returns the device properties.
+
+Properties:
+		uint8 LocalMDEPId: Local data channel identification
+		uint8 RemoteMDEPId: Remote data channel identification
+		uint16 MDLId: data channel ID
+		uint16 MTU: L2CAP channel message MTU
-- 
1.7.0.4


^ permalink raw reply related

* How to use a2dp avrcp controls
From: John Frankish @ 2010-05-04  5:18 UTC (permalink / raw)
  To: linux-bluetooth@vger.kernel.org

Hi,

Although I can pair with and play music to a set of a2dp headphones without problems, I cannot figure out how to enable the "track skip" and similar remote controls on the headphones.

Is this something that can be done easily - if so, could somebody point me to the documentation?

If not, are there music playing applications (rhythmbox or similar) that already enable the remote controls on headphones?

Note that I see appropriate echoes in dmesg when I press the "track skip" button, so the message is getting from the headphones to my laptop...

Thanks
John

^ permalink raw reply

* Re: [PATCH] Health Device Profile API
From: Santiago Carot-Nemesio @ 2010-05-04  7:50 UTC (permalink / raw)
  To: Elvis Pfützenreuter; +Cc: linux-bluetooth
In-Reply-To: <1272928958-8073-1-git-send-email-epx@signove.com>

Hello Elvis,

We think that we are doing the same work in HDP. We are working in the
API for that pugin since last months but we prefer finish MCAP
implementation before fixing HDP API.

We saw your latest patches and next there are our comments. It seems a
lot with our first prototype implementation.
There are some functionalities that we like but we miss other else. We
are going to send an e-mail with the API that we propose.
We expect that we can work together to get a HDP API early. Remember
that we can talk in #openhealth if you want, just tell us when you can
attend ;)

El lun, 03-05-2010 a las 20:22 -0300, Elvis Pfützenreuter escribió:
> From: Elvis Pfutzenreuter <epx@signove.com>
> 
> Initial Health API proposal. Includes Adapter, Device and HealthAgent API
> description. Proposed API can address Health source and sink roles.
> 
> Adapter methods exported on Health interface allow HealthAgent
> registration. Device methods exported on Health interface allow
> applications to actively created MCAP data channels and get informations
> of the services exported by the endpoints.
> 
> HealthAgent allows applications to receive multiple data streams encoded
> on IEEE 11073-20601 Personal Health Data Exchange Protocol.
> 
> v2 changes: added HealthData connection object interface
> 	    assumes at most one HDP set of roles per application
> ---
>  doc/health-api.txt |  187 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 files changed, 187 insertions(+), 0 deletions(-)
>  create mode 100644 doc/health-api.txt
> 
> diff --git a/doc/health-api.txt b/doc/health-api.txt
> new file mode 100644
> index 0000000..49c043d
> --- /dev/null
> +++ b/doc/health-api.txt
> @@ -0,0 +1,187 @@
> +BlueZ D-Bus Health API description
> +*********************************
> +
> +Copyright (C) 2010 Elvis Pfutzenreuter <epx@signove.com>
> +
> +Health Adapter hierarchy
> +========================
> +
> +Service		org.bluez
> +Interface	org.bluez.Health
> +Object path	[variable prefix]/{hci0,hci1,...}
> +
> +This object hierarchy intend to be used by applications which want to enable
> +a Health source or sink end point. The most common usage scenario is the sink
> +role. Multi-channel protocol internals are hidden from the application interface.
> +
> +Methods:
> +	void RegisterRole(dict options, object path)
> +
> +		Register an HDP role to the adapter and assign the health agent
> +		that shall be responsible by address data channel connections.
> +
> +		Records and channels are automatically closed when the agent
> +		leaves the bus. Mandatory and optional options are described
> +		below:
> +			Optional parameters (dinamically assigned when not
> +			informed):
> +			uint16 CPSM: control channel PSM
> +			uint16 DPSM: data channel PSM
> +
> +			Mandatory parameters:
> +			array{dict EndPoint} EndPoints: Array of end points.
> +				Possible EndPoint properties:
> +				uint8 MDEPId: Mandatory MCAP Data End Point
> +					Identification
> +				uint16 DataType: Mandatory Endpoint data type
> +				string Role: "source" or "sink"
> +				string Description: Optional description
> +
> +	UnregisterRole()
> +
> +		Removes an HDP role and kills all connections related to it.
> +

We believe that it is better that applications doesn't need to provide
parameters that can be managed internally by HDP plugin such as MDEPs.
Then applications could get an avaible MDEP provided by HDP instead of
provide it. This way HDP doesn't have to check that the mdep provided is
a valid mdep and that mdep is not registered two times with different
roles.
We have provided an abstraction based in HDP sessions becasue there may
exist different HDP sessions running in the same host. Your API doesn't
seem to take in count that possibility.

> +
> +Health Agent hierarchy
> +======================
> +
> +Service		unique name
> +Interface	org.bluez.HealthAgent
> +Object path	freely definable
> +
> +Agent responsible by address data channels connections. Either active or
> +passive connections are notified. FD passing mechanism is used to allows
> +the HealthAgent to access directly the data encoded on IEEE 11073-20601
> +Personal Health Data Exchange Protocol.
> +
> +Methods:
> +	void NewDataConnection(dict properties)
> +
> +		This method gets called when a new data channel connection
> +		is established. It addresses host and remote initiated
> +		connections.
> +
> +		Properties:
> +			string device_path: the Health Device object path
> +			string connection_path: the HealthData object path
> +			boolean Passive: Device initiated connection
> +			boolean Reconnection: Previous established data
> +				channel reestablished
> +
> +	void Release()
> +
> +		This method gets called whenever the service daemon unregisters
> +		the agent or whenever the Adapter where the HealthAgent
> +		registers itself is removed.
> +
> +

Above callbacks reflect same ideas that we have, but we propose a
session oriented agent (we call HdpAgent in the e-mail) that
applications should provide when they connect to a HdpSession. Once
applications are connected to a session, HDP will notify them using that
d-bus object.

> +Health Device hierarchy
> +=======================
> +
> +Service		org.bluez
> +Interface	org.bluez.Health
> +Object path	[variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
> +
> +The methods described below can be used by applications that need
> +to manage data channel connections or get properties of service
> +exported by the remote device.
> +
You can't rely in remote HDP record for Source devices (it is not
mandatory for them to registry it).  we dont understand why you need to
provide a local mdep for the connection of the data channel, only the
remote mdep is required for do that.

> +Methods:
> +
> +	string Connect(dict parameters)
> +
> +		Method used by applications that need to actively create
> +		a data channel connection. When the remote connects back
> +		in the data channel PSM, the connection is reported to
> +		the registered HealthAgent.
> +
> +		Returns an HDP Connection object path. The same path
> +		will be received again via Agent, when connection is
> +		established.
> +
> +		If the reconnection is not possible, a new connection
> +		will be created (automatic fallback). The MDL ID may
> +		change; the application needs to check HealthData object
> +		MDLId property when connection is notified via Agent.
> +
> +		Mandatory parameters:
> +			uint8 local_mdepid: local MDEP ID (*)
> +			uint8 remote_mdepid: remote MDEP ID
> +		Optional parameters:
> +			uint16 mdlid: MDL ID, if trying reconnection

Reconnections may be transparent for applications, we believe that HDP
knows when a data channels is re-connected. (see our API for more
details)

> +
> +		(*) Was defined by application when it called RegisterRole().
> +
> +	dict GetProperties()
> +
> +		Returns the device properties.
> +
> +Signals:
> +
> +	PropertiesChanged()

We think that it is unnecessary because properties should not change
when a HDP session is running.

> +
> +Properties:
> +		array{MDEPs}: array of remote data end points. Attributes of
> +			a MDEP entry are:
> +				uint8 MDEPId
> +				uint16 DataType
> +				string Role: "source" or "sink"
> +				string Description: optional description

We like this. we think that it is helpful for to get remote information
from a remote device.

> +
> +		array{MDLs}: array of open data channels. Mandatory
> +			MDLs attributes are:
> +			uint16 MDLId: Data channel identification
> +			string path: HealthData object path

I think that above method is not necessary because an HdpAgent always is
notified when a new data channel is created or destroyed, but we are
open to discuss.

> +
> +		boolean Connected: true when the MCAP control channel is
> +					established
> +
> +
> +Health Device Data Connection hierarchy
> +=======================================
> +
> +Service		org.bluez
> +Interface	org.bluez.HealthData
> +Object path	[variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/YYYYYYYYYYY
> +
> +A HealthData object can be created either by active connection of by
> +receiving a connection via HealthAgent.
> +
> +FD passing concept is used to allow applications to handle data channels.
> +
> +A HealthData object exists only while the data connection is up. If the
> +connection is broken, the object is removed. The application may
> +(and should) handle this situation, by trying a reconnection.
> +If the connection was not deleted, the MDL ID outlives the respective
> +HealthData object. (Another sign of connection breakage is file
> +descriptor closure.)
> +
We believe that connections and reconnections should be transparents to
application, for example, if data channel is closed, next time that an
application want to get the data channel fd to send data, HDP could
re-connected the previously closed data channel.
We have done that because under some conditions, devices can close an
MCL for power saving and upper application shouldn't be affected (you
can see 11073-20601 state machine for more details or HDP white paper)

> +The methods described below can be used by applications to manage
> +a single data channel or get its properties.
> +
> +Methods:
> +
> +	filedescriptor GetFD()
> +
> +		Obtains the raw filedescriptor for this data channel
> +		(most likely a L2CAP socket.)
> +
> +	void Disconnect()
> +
> +		Closes this data channel and destroys the object,
> +		but keeps the MDL ID valid for future reconnections.
> +
> +	void DeleteConnection()
> +
> +		Closes this data channel, destroys the object
> +		 and invalidates the MDL ID.

This is more or less that we have thought, but taking in count that
there may exist more Hdp session running on the same host.
> +
> +	dict GetProperties()
> +
> +		Returns the device properties.
> +
> +Properties:
> +		uint8 LocalMDEPId: Local data channel identification
> +		uint8 RemoteMDEPId: Remote data channel identification
> +		uint16 MDLId: data channel ID
> +		uint16 MTU: L2CAP channel message MTU

Regards.
Jose and Santiago



^ permalink raw reply

* HDP proposed API
From: Santiago Carot-Nemesio @ 2010-05-04  7:55 UTC (permalink / raw)
  To: linux-bluetooth@vger.kernel.org

Hello,
Next is the API wich we have been working last weeks.

Regards.


BlueZ D-Bus HDP API description
***********************************

Authors:
Santiago Carot-Nemesio		sancane@gmail.com
Jose Antonio Santos-Cadenas	santoscadenas@gmail.com


Health Device Profile hierarchy
===============================

Service		org.bluez
Interface	org.bluez.Hdp
Object path	[variable prefix]/{hci0,hci1,...}

Methods		object CreateSession(object path, byte data_spec)

			Returns the object path for the new HDP session.
			The path parameter is the path of the remote object
			with the callbacks to nofity events (see
			org.bluez.HdpAgent at the end of this document)
			The data_spec is the data exchange specficication
			(see section 5.2.10 of the specification document)

			Possible errors: org.bluez.Error.InvalidArguments

		void DeleteSession(object path)

			Delete the HDP session identified by the object path.

			Possible errors: org.bluez.Error.InvalidArguments
					 org.bluez.Error.NotFound


--------------------------------------------------------------------------------

Service		org.bluez
Interface	org.bluez.HdpSession
Object path	[variable prefix]/{hci0,hci1,...}/{hdp0,hdp1,...}

		uint8 AllocateMdep(uint8 role)

			This method allocates a logical end point. Valids values
			for MDEP roles are:
				* 0x00 For source
				* 0x01 For sink

			The return value is the allocated MDEP.

		void AddFeature(uint8 mdepid, uint16 dtype, string dscr)

			Add a new entry in the HDP supported feature list with
			the role given in the MDEP id.

			If the new feature has the same data type as other
			previusly added feature for the same MDEP id, the old
			feature will be overwritten.

		void Start()

			Starts the associated MCAP layer and set up the SDP
			registry with all supported features.

			Possible errors: org.bluez.Error.HdpError

		object Connect(string btaddr)

			Connect with the device and returns the object path of
			the remote device.
			To call this method, the session must be started.

			Possible errors: org.bluez.Error.InvalidArguments
					 org.bluez.Error.HdpError

		void Disconnect(object device, boolean delete)

			Disconnect from the remote device. If delete is true, any
			status will also be deleted. Otherwise, the status will
			be keeped for allowing future reconnections.
			To call this method, the session must be started.

			Possible errors: org.bluez.Error.InvalidArguments
					 org.bluez.Error.NotFound
					 org.bluez.Error.HdpError

--------------------------------------------------------------------------------

Service		org.bluez
Interface	org.bluez.HdpDevice
Object path	[variable prefix]/{hci0,hci1,...}/{hdp0,hdp1,...}/dev_XX_XX_XX_XX_XX_XX

		boolean Echo(array{byte})

			Sends an echo petition to the remote device. Return True
			if response matches with the buffer sended. If some error
			is detected False value is returned and the associated
			MCL is closed.

		uint16 OpenDc(byte mdepid, byte config)

			Creates a new data channel with the indicated config
			to the remote MCAP Data End Point (MDEP).
			The configuration should indicate the channel quality of
			service.
			Returns the data channel id.

			Possible errors: org.bluez.Error.InvalidArguments
					 org.bluez.Error.HdpError

		file_descriptor GetDcFd(uint16 mdlid)

			Gets a file descriptor where data can be readed or
			writed for receive or sent by the data channel.
			Returns the file descriptor

			Possible errors: org.bluez.Error.InvalidArguments
					 org.bluez.Error.NotFound
					 org.bluez.Error.HdpError

		void DeleteDc(uint16 mdlid)

			Deletes a data channel so it will not be available for
			use.

			Possible errors: org.bluez.Error.InvalidArguments
					 org.bluez.Error.NotFound
					 org.bluez.Error.HdpError

		void DeleteAllDc()

			Deletes all data channels so it will not be available
			for use. Typically this function is called when the
			connection with the remote device will be closed
			permanently

			Possible errors: org.bluez.Error.HdpError

		uint16 FirstRelDc()

			Returns the identificator of the first reliable data
			channel connected for this device.

			Possible errors: org.bluez.Error.HdpError

HDPAgent hierarchy
==================

Service         unique name
Interface       org.bluez.HdpAgent
Object path     freely definable

		void DeviceConnected(object path)

			This method is called whenever a new device connection
			has been established over the control channel of the
			current HDP session. The objectpath contains the object
			path of the remote device.

		void DeviceDisconnected(object path)

			This method is called when a remote device is
			disconnected definitively. Any future reconnections
			will fail. Also all data channels associated to this
			device will be closed.

		void CreatedDc(object path, uint16 mdlid, filedescriptor fd)

			This method is called when a new data channel is created
			The path contains the object path of the device whith
			the new connection is created, the mdlid the data
			channel identificator and the fd is the file descriptor
			where the data can be readed or writed.

		void DeletedDc(object path, uint16 mdlid)

			This method is called when a data channel is closed.
			After this call the data channel will not be valid and
			can be reused for future created data channels.

		void DataReady(object path, uint16 mdlid, filedescriptor fd)

			This method is called when there is new data that can be
			readed in a data channel


^ permalink raw reply

* Re: [PATCH] Added support for deleting all MDLS in MCAP
From: José Antonio Santos Cadenas @ 2010-05-04  8:20 UTC (permalink / raw)
  To: Santiago Carot Nemesio; +Cc: linux-bluetooth
In-Reply-To: <201004091344.41686.jcaden@libresoft.es>

Hi all,

El Friday 09 April 2010 13:44:41 José Antonio Santos Cadenas escribió:
> Also fixed some bugs in mcl state transitions
> 
> Signed-off-by: Jose Antonio Santos Cadenas <santoscadenas@gmail.com>
> Reviewed-by: Santiago Carot Nemesio <sancane@gmail.com>
> ---
>  mcap/mcap.c     |  133
> +++++++++++++++++++++++++++++++++++++++---------------- mcap/mcap_lib.h | 
>   2 +
>  2 files changed, 97 insertions(+), 38 deletions(-)
> 
> diff --git a/mcap/mcap.c b/mcap/mcap.c
> index 28c586c..e76c565 100644
> --- a/mcap/mcap.c
> +++ b/mcap/mcap.c
> @@ -33,7 +33,6 @@
>  #include "mcap.h"
>  #include "mcap_lib.h"
> 
> -//#define STATE2STR(_mcl) state2str(_mcl->state)
>  #define MCAP_ERROR mcap_error_quark()
>  #define SET_DEFAULT_MCL_CB(__mcl) do {				\
>  	__mcl->cb->mdl_connected = default_mdl_connected_cb;	\
> @@ -71,7 +70,8 @@ typedef enum {
>  typedef enum {
>  	MDL_WAITING,
>  	MDL_CONNECTED,
> -	MDL_CLOSED
> +	MDL_DELETING,
> +	MDL_CLOSED,
>  } MDLState;
> 
>  struct mcap_mcl_cb {
> @@ -79,7 +79,7 @@ struct mcap_mcl_cb {
>  	mcap_mdl_event_cb 		mdl_closed;	/* Remote device has closed an 
mdl */
>  	mcap_mdl_event_cb 		mdl_deleted;	/* Remote device deleted an 
mdl */
>  	mcap_remote_mdl_conn_req_cb	mdl_conn_req;	/* Remote deive requested
> create an mdl */ -	mcap_remote_mdl_reconn_req_cb 	mdl_reconn_req;	/*
> Remote device requested reconnect previus mdl */
> +	mcap_remote_mdl_reconn_req_cb 	mdl_reconn_req;	/* Remote device
> requested reconnect previous mdl */ gpointer			user_data;	/* 
user data */
>  };
> 
> @@ -240,6 +240,9 @@ static void update_mcl_state(struct mcap_mcl *mcl)
>  	GSList *l;
>  	struct mcap_mdl *mdl;
> 
> +	if (mcl->state == MCL_PENDING)
> +		return;
> +
>  	for (l = mcl->mdls; l; l = l->next) {
>  		mdl = l->data;
> 
> @@ -280,6 +283,12 @@ static void mcap_send_std_opcode(struct mcap_mcl *mcl,
> const uint8_t *buf, return;
>  	}
> 
> +	if (mcl->state == MCL_PENDING) {
> +		g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED,
> +			"Not Std Op. Codes can be sent in PENDING State");
> +		return;
> +	}
> +
>  	if (mcap_send_data(g_io_channel_unix_get_fd(mcl->cc), buf, size) < 0)
>  		g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED,
>  				    "Data can't be sent, write error");
> @@ -394,17 +403,10 @@ static gint compare_mdl(gconstpointer a,
> gconstpointer b) static gboolean wait_response_timer(gpointer data)
>  {
>  	struct mcap_mcl *mcl = data;
> -	struct mcap_mdl_op_cb *con = mcl->priv_data;
> -	struct mcap_mdl *mdl = con->mdl;
> 
>  	GError *gerr = NULL;
> 
>  	RELEASE_TIMER(mcl);
> -	mcl->mdls = g_slist_remove(mcl->mdls, mdl);
> -	g_free(mdl);
> -
> -	mcl->req = MCL_AVAILABLE;
> -	update_mcl_state(mcl);
> 
>  	g_set_error(&gerr, MCAP_ERROR, MCAP_ERROR_FAILED,
>  					"Timeout waiting response");
> @@ -494,6 +496,26 @@ void mcap_req_mdl_reconnect(struct mcap_mdl *mdl,
>  	con->cb.op = reconnect_cb;
>  	con->user_data = user_data;
> 
> +	mcl->state = MCL_ACTIVE;
> +	mcl->lcmd = cmd;
> +	mcl->req = MCL_WAITING_RSP;
> +	mcl->priv_data = con;
> +
> +	mcl->tid = g_timeout_add_seconds(RESPONSE_TIMER, wait_response_timer,
> mcl); +}
> +
> +static void send_delete_req(GError **err, struct mcap_mcl *mcl,
> +				struct mcap_mdl_op_cb *con, uint16_t mdlid)
> +{
> +	uint8_t *cmd;
> +
> +	cmd = create_req(MCAP_MD_DELETE_MDL_REQ, mdlid);
> +	mcap_send_std_opcode(mcl, cmd, sizeof(mcap_md_req), err);
> +	if (*err) {
> +		g_free(cmd);
> +		return;
> +	}
> +
>  	mcl->lcmd = cmd;
>  	mcl->req = MCL_WAITING_RSP;
>  	mcl->priv_data = con;
> @@ -501,13 +523,43 @@ void mcap_req_mdl_reconnect(struct mcap_mdl *mdl,
>  	mcl->tid = g_timeout_add_seconds(RESPONSE_TIMER, wait_response_timer,
> mcl); }
> 
> +void mcap_req_mdl_delete_all(struct mcap_mcl *mcl, GError **err,
> +			mcap_mdl_del_cb delete_cb, gpointer user_data)
> +{
> +	GSList *l;
> +	struct mcap_mdl *mdl;
> +	struct mcap_mdl_op_cb *con;
> +
> +	debug("MCL in state: %d", mcl->state);
> +	if (!mcl->mdls) {
> +		g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED,
> +				"There are not MDLs created");
> +		return;
> +	}
> +
> +	for (l = mcl->mdls; l; l = l->next) {
> +		mdl = l->data;
> +		if (mdl->state != MDL_WAITING)
> +			mdl->state = MDL_DELETING;
> +	}
> +
> +	con = g_new0(struct mcap_mdl_op_cb, 1);
> +	con->mdl = NULL;
> +	con->cb.del = delete_cb;
> +	con->user_data = user_data;
> +
> +	send_delete_req(err, mcl, con, MCAP_ALL_MDLIDS);
> +	if (*err)
> +		g_free(con);
> +	debug("exiting MCL in state: %d", mcl->state);
> +}
> +
>  void mcap_req_mdl_deletion(struct mcap_mdl *mdl, GError **err,
>  			mcap_mdl_del_cb delete_cb, gpointer user_data)
>  {
>  	struct mcap_mcl *mcl= mdl->mcl;
>  	struct mcap_mdl_op_cb *con;
>  	GSList *l;
> -	uint8_t *cmd;
> 
>  	l = g_slist_find(mcl->mdls, mdl);
> 
> @@ -519,28 +571,19 @@ void mcap_req_mdl_deletion(struct mcap_mdl *mdl,
> GError **err,
> 
>  	if (mdl->state == MDL_WAITING) {
>  		g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED,
> -					"Not valid petition in this mdl state");
> +							"Mdl is not created");
>  		return;
>  	}
> +	mdl->state = MDL_DELETING;
> 
>  	con = g_new0(struct mcap_mdl_op_cb, 1);
>  	con->mdl = mdl;
>  	con->cb.del = delete_cb;
>  	con->user_data = user_data;
> 
> -	cmd = create_req(MCAP_MD_DELETE_MDL_REQ, mdl->mdlid);
> -	mcap_send_std_opcode(mcl, cmd, sizeof(mcap_md_req), err);
> -	if (*err) {
> +	send_delete_req(err, mcl, con, mdl->mdlid);
> +	if (*err)
>  		g_free(con);
> -		g_free(cmd);
> -		return;
> -	}
> -
> -	mcl->lcmd = cmd;
> -	mcl->req = MCL_WAITING_RSP;
> -	mcl->priv_data = con;
> -
> -	mcl->tid = g_timeout_add_seconds(RESPONSE_TIMER, wait_response_timer,
> mcl); }
> 
>  void mcap_mdl_abort(struct mcap_mdl *mdl, GError **err,
> @@ -688,7 +731,7 @@ static gboolean parse_set_opts(struct mcap_mcl_cb
> *mcl_cb, GError **err, cb = va_arg(args, int);
>  	}
> 
> -	/* Set new callbacks set */
> +	/* Set new callbacks */
>  	if (c->mdl_connected)
>  		mcl_cb->mdl_connected = c->mdl_connected;
>  	if (c->mdl_closed)
> @@ -825,7 +868,7 @@ static void process_md_create_mdl_req(struct mcap_mcl
> *mcl, uint8_t *cmd, int le if ((cfga != 0) && (cfga != conf)) {
>  		/* Remote device set default configuration but upper profile */
>  		/* has changed it. Protocol Error: force closing the MCL by */
> -		/* using remote device using UNESPECIFIED_ERROR response*/
> +		/* using remote device using UNESPECIFIED_ERROR response */
>  		send4B_cmd(mcl, MCAP_MD_CREATE_MDL_RSP, MCAP_UNESPECIFIED_ERROR,
>  								mdl_id);
>  		return;
> @@ -844,8 +887,8 @@ static void process_md_create_mdl_req(struct mcap_mcl
> *mcl, uint8_t *cmd, int le shutdown_mdl(mdl);
>  		mcl->cb->mdl_closed(mdl, mcl->cb->user_data);
>  	}
> -	mdl->state = MDL_WAITING;
>  	mdl->mdep_id = mdep_id;
> +	mdl->state = MDL_WAITING;
>  	mcl->mdls = g_slist_insert_sorted(mcl->mdls, mdl, compare_mdl);
> 
>  	mcl->state = MCL_PENDING;
> @@ -934,7 +977,7 @@ static void process_md_abort_mdl_req(struct mcap_mcl
> *mcl, uint8_t *cmd, int len g_free(del);
>  	send4B_cmd(mcl, MCAP_MD_ABORT_MDL_RSP, MCAP_SUCCESS, mdl_id);
>  }
> -/* Functions used to process responses */
> +
>  static gboolean check_err_rsp(uint16_t rmdl, uint16_t smdl, uint8_t rc,
>  					int rlen, int len, GError **gerr)
>  {
> @@ -1089,15 +1132,26 @@ static void mcap_delete_mdl(gpointer elem, gpointer
> user_data) {
>  	struct mcap_mdl *mdl = elem;
>  	gboolean notify = *(gboolean *)user_data;
> -	if (mdl->state == MDL_CONNECTED) {
> -		debug("MDL %d already connected, closing it", mdl->mdlid);
> -		shutdown_mdl(mdl);
> -	}
> +
> +	shutdown_mdl(mdl);
>  	if (notify)
>  		mdl->mcl->cb->mdl_deleted(mdl, mdl->mcl->cb->user_data);
>  	g_free(mdl);
>  }
> 
> +static void restore_mdl(gpointer elem, gpointer data)
> +{
> +	struct mcap_mdl *mdl = elem;
> +
> +	if (mdl->state == MDL_DELETING) {
> +		if (mdl->dc)
> +			mdl->state = MDL_CONNECTED;
> +		else
> +			mdl->state = MDL_CLOSED;
> +	} else if (mdl->state == MDL_CLOSED)
> +		mdl->mcl->cb->mdl_closed(mdl, mdl->mcl->cb->user_data);
> +}
> +
>  static gboolean process_md_delete_mdl_rsp(struct mcap_mcl *mcl, uint8_t
> *cmd, int len)
>  {
> @@ -1124,6 +1178,10 @@ static gboolean process_md_delete_mdl_rsp(struct
> mcap_mcl *mcl, uint8_t *cmd, mcl->lcmd = NULL;
>  	mcl->req = MCL_AVAILABLE;
>  	if (gerr) {
> +		if (mdl)
> +			restore_mdl(mdl, NULL);
> +		else
> +			g_slist_foreach(mcl->mdls, restore_mdl, NULL);
>  		deleted_cb(gerr, user_data);
>  		g_error_free(gerr);
>  		return close;
> @@ -1158,8 +1216,8 @@ static void process_md_delete_mdl_req(struct mcap_mcl
> *mcl, mcap_md_req *req) notify = FALSE;
>  		g_slist_foreach(mcl->mdls, mcap_delete_mdl, &notify);
>  		g_slist_free(mcl->mdls);
> -		mcl->state = MCL_CONNECTED;
>  		mcl->mdls = NULL;
> +		mcl->state = MCL_CONNECTED;
>  		/* NULL mdl means ALL_MDLS */
>  		mcl->cb->mdl_deleted(NULL, mcl->cb->user_data);
>  		goto resp;
> @@ -1178,11 +1236,10 @@ static void process_md_delete_mdl_req(struct
> mcap_mcl *mcl, mcap_md_req *req) }
>  	}
> 
> -	if (!mdl) {
> +	if (!mdl || (mdl->state == MDL_WAITING)) {
>  		send4B_cmd(mcl, MCAP_MD_DELETE_MDL_RSP, MCAP_INVALID_MDL, mdlid);
>  		return;
>  	}
> -
>  	mcl->mdls = g_slist_remove(mcl->mdls, mdl);
>  	update_mcl_state(mcl);
>  	notify = TRUE;
> @@ -1400,16 +1457,16 @@ static gboolean mdl_closing_cb(GIOChannel *chan,
> GIOCondition cond, gpointer dat {
> 
>  	struct mcap_mdl *mdl = data;
> -	gboolean open;
> +	gboolean notify;
> 
>  	debug("Close MDL %d", mdl->mdlid);
> 
> -	open = (mdl->state == MDL_CONNECTED);
> +	notify = (mdl->state == MDL_CONNECTED);
>  	shutdown_mdl(mdl);
> 
>  	update_mcl_state(mdl->mcl);
> 
> -	if (open)
> +	if (notify)
>  		/*Callback to upper layer */
>  		mdl->mcl->cb->mdl_closed(mdl, mdl->mcl->cb->user_data);
> 
> diff --git a/mcap/mcap_lib.h b/mcap/mcap_lib.h
> index 530f03a..867a53d 100644
> --- a/mcap/mcap_lib.h
> +++ b/mcap/mcap_lib.h
> @@ -92,6 +92,8 @@ void mcap_req_mdl_creation(struct mcap_mcl *mcl,
>  void mcap_req_mdl_reconnect(struct mcap_mdl *mdl, GError **err,
>  				mcap_mdl_operation_cb reconnect_cb,
>  				gpointer user_data);
> +void mcap_req_mdl_delete_all(struct mcap_mcl *mcl, GError **err,
> +			mcap_mdl_del_cb delete_cb, gpointer user_data);
>  void mcap_req_mdl_deletion(struct mcap_mdl *mdl, GError **err,
>  			mcap_mdl_del_cb delete_cb, gpointer user_data);
>  void mcap_mdl_connect(struct mcap_mdl *mdl,


Please, dismiss this patches we are sending new ones shortly.

Regards

^ permalink raw reply

* [PATCH] MCAP implementation for BlueZ
From: Santiago Carot-Nemesio @ 2010-05-04  8:43 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Santiago Carot-Nemesio

This patch enables MCAP in BlueZ.
Current implementation provides support for standard op. codes. Clock
Synchronization protocol is not yet supported.
---
 Makefile.am     |   11 +-
 acinclude.m4    |    6 +
 mcap/mcap.c     | 2146 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 mcap/mcap.h     |  176 +++++
 mcap/mcap_lib.h |  162 +++++
 5 files changed, 2500 insertions(+), 1 deletions(-)
 create mode 100644 mcap/mcap.c
 create mode 100644 mcap/mcap.h
 create mode 100644 mcap/mcap_lib.h

diff --git a/Makefile.am b/Makefile.am
index f991121..bc88155 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -101,6 +101,7 @@ gdbus_sources = gdbus/gdbus.h gdbus/mainloop.c gdbus/object.c gdbus/watch.c
 builtin_modules =
 builtin_sources =
 builtin_nodist =
+mcap_sources =
 
 if PNATPLUGIN
 builtin_modules += pnat
@@ -168,6 +169,10 @@ builtin_modules += service
 builtin_sources += plugins/service.c
 endif
 
+if MCAP
+mcap_sources += mcap/mcap_lib.h mcap/mcap.h mcap/mcap.c
+endif
+
 builtin_modules += hciops
 builtin_sources += plugins/hciops.c
 
@@ -196,7 +201,8 @@ src_bluetoothd_SOURCES = $(gdbus_sources) $(builtin_sources) \
 			src/adapter.h src/adapter.c \
 			src/device.h src/device.c \
 			src/dbus-common.c src/dbus-common.h \
-			src/dbus-hci.h src/dbus-hci.c
+			src/dbus-hci.h src/dbus-hci.c \
+			$(mcap_sources)
 src_bluetoothd_LDADD = lib/libbluetooth.la @GLIB_LIBS@ @DBUS_LIBS@ \
 							@CAPNG_LIBS@ -ldl
 src_bluetoothd_LDFLAGS = -Wl,--export-dynamic \
@@ -318,6 +324,9 @@ 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
 
+if MCAP
+INCLUDES += -I$(builddir)/mcap
+endif
 
 pkgconfigdir = $(libdir)/pkgconfig
 
diff --git a/acinclude.m4 b/acinclude.m4
index f7bb047..b512cfb 100644
--- a/acinclude.m4
+++ b/acinclude.m4
@@ -167,6 +167,7 @@ AC_DEFUN([AC_ARG_BLUEZ], [
 	serial_enable=yes
 	network_enable=yes
 	service_enable=yes
+	mcap_enable=no
 	pnat_enable=no
 	tracer_enable=no
 	tools_enable=yes
@@ -215,6 +216,10 @@ AC_DEFUN([AC_ARG_BLUEZ], [
 		service_enable=${enableval}
 	])
 
+	AC_ARG_ENABLE(mcap, AC_HELP_STRING([--enable-mcap], [enable mcap support]), [
+			mcap_enable=${enableval}
+	])
+
 	AC_ARG_ENABLE(pnat, AC_HELP_STRING([--enable-pnat], [enable pnat plugin]), [
 		pnat_enable=${enableval}
 	])
@@ -325,6 +330,7 @@ AC_DEFUN([AC_ARG_BLUEZ], [
 	AM_CONDITIONAL(SERIALPLUGIN, test "${serial_enable}" = "yes")
 	AM_CONDITIONAL(NETWORKPLUGIN, test "${network_enable}" = "yes")
 	AM_CONDITIONAL(SERVICEPLUGIN, test "${service_enable}" = "yes")
+	AM_CONDITIONAL(MCAP, test "${mcap_enable}" = "yes")
 	AM_CONDITIONAL(ECHOPLUGIN, test "no" = "yes")
 	AM_CONDITIONAL(PNATPLUGIN, test "${pnat_enable}" = "yes")
 	AM_CONDITIONAL(TRACER, test "${tracer_enable}" = "yes")
diff --git a/mcap/mcap.c b/mcap/mcap.c
new file mode 100644
index 0000000..5068054
--- /dev/null
+++ b/mcap/mcap.c
@@ -0,0 +1,2146 @@
+/*
+ *
+ *  MCAP for BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ *
+ *  Authors:
+ *  Santiago Carot-Nemesio <sancane at gmail.com>
+ *  Jose Antonio Santos-Cadenas <santoscadenas at gmail.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <gdbus.h>
+#include "adapter.h"
+#include "logging.h"
+#include "btio.h"
+#include "error.h"
+
+#include <netinet/in.h>
+
+#include "mcap.h"
+#include "mcap_lib.h"
+
+#define RESPONSE_TIMER	2	/* seconds */
+#define MAX_CACHED	10	/* 10 devices */
+
+#define MCAP_ERROR mcap_error_quark()
+
+#define RELEASE_TIMER(__mcl) do {	\
+	g_source_remove(__mcl->tid);	\
+	__mcl->tid = 0;			\
+} while(0)
+
+#define MCAP_IS_STD_OPCODE(_oc)	(_oc >= MCAP_ERROR_RSP &&	\
+				 _oc <= MCAP_MD_DELETE_MDL_RSP)
+
+typedef enum {
+	MCL_CONNECTED,
+	MCL_PENDING,
+	MCL_ACTIVE,
+	MCL_IDLE
+} MCLState;
+
+typedef enum {
+	MCL_ACCEPTOR,
+	MCL_INITIATOR,
+} MCLRole;
+
+typedef enum {
+	MCL_AVAILABLE,
+	MCL_WAITING_RSP,
+} MCAPCtrl;
+
+typedef enum {
+	MDL_WAITING,
+	MDL_CONNECTED,
+	MDL_DELETING,
+	MDL_CLOSED,
+} MDLState;
+
+struct mcap_mdl_cb {
+	mcap_mdl_event_cb 		mdl_connected;	/* Remote device has created a mdl */
+	mcap_mdl_event_cb 		mdl_closed;	/* Remote device has closed a mdl */
+	mcap_mdl_event_cb 		mdl_deleted;	/* Remote device deletion of a mdl */
+	mcap_mdl_event_cb		mdl_aborted;	/* Remote device aborted the mdl creation */
+	mcap_remote_mdl_conn_req_cb	mdl_conn_req;	/* Remote deive requested a creation of a mdl */
+	mcap_remote_mdl_reconn_req_cb 	mdl_reconn_req;	/* Remote device requested reconnection of a mdl */
+	gpointer			user_data;	/* user data */
+};
+
+struct mcap_session {
+	bdaddr_t			src;			/* Source address */
+	GIOChannel			*ccio;			/* Control Channel IO */
+	GIOChannel			*dcio;			/* Data Channel IO */
+	GSList				*mcls;			/* MCAP session list */
+	GSList				*cached;		/* List with all cached MCLs (MAX_CACHED macro) */
+	BtIOSecLevel			sec;			/* Security level */
+	mcap_mcl_event_cb		mcl_connected_cb;	/* New MCL connected */
+	mcap_mcl_event_cb		mcl_reconnected_cb;	/* Old MCL has been reconnected */
+	mcap_mcl_event_cb		mcl_disconnected_cb;	/* MCL disconnected */
+	mcap_mcl_event_cb		mcl_uncached_cb;	/* MCL has been removed from MCAP cache */
+	gpointer			user_data;		/* Data to be provided in a callbacks */
+};
+
+struct mcap_mcl {
+	struct mcap_session	*ms;		/* MCAP session where this MCL belongs */
+	bdaddr_t		addr;		/* device address */
+	GIOChannel		*cc;		/* MCAP Control Channel IO */
+	guint			wid;		/* MCL Watcher id */
+	GSList			*mdls;		/* List of Data Channels shorted by mdlid */
+	MCLState		state;		/* current MCL State */
+	MCLRole			role;		/* initiator or aceptor of this MCL*/
+	MCAPCtrl		req;		/* Request control flag */
+	void			*priv_data;	/* Temporal data to manage responses */
+	struct mcap_mdl_cb	*cb;		/* MDL callbacks */
+	guint			tid;		/* Timer id for waiting for a resposne */
+	uint8_t			*lcmd;		/* Last command sent */
+	guint			ref;		/* References counter */
+	uint8_t			ctrl;		/* MCL control flag */
+};
+
+#define	MCAP_CTRL_CACHED	0x01	/* MCL is cached */
+#define	MCAP_CTRL_STD_OP	0x02	/* Support for standard op codes */
+#define	MCAP_CTRL_SYNC_OP	0x04	/* Support for synchronization commands */
+#define	MCAP_CTRL_CONN		0x08	/* MCL is in connectcting process */
+#define	MCAP_CTRL_FREE		0x10	/* MCL is marked as releasable */
+#define	MCAP_CTRL_NOCACHE	0x20	/* MCL is marked as not cacheable */
+
+struct mcap_mdl {
+	struct mcap_mcl		*mcl;		/* MCAP mcl for this mdl */
+	GIOChannel		*dc;		/* MCAP Data Channel IO */
+	guint			wid;		/* MDL Watcher id */
+	uint16_t		mdlid;		/* MDL id */
+	uint8_t			mdep_id;	/* MCAP Data End Point */
+	MDLState		state;		/* MDL state */
+};
+
+struct connect_mcl {
+	struct mcap_mcl		*mcl;		/* MCL for this operation */
+	mcap_mcl_connect_cb	connect_cb;	/* Connect callback */
+	gpointer		user_data;	/* Callback user data */
+};
+
+typedef union {
+	mcap_mdl_operation_cb 		op;
+	mcap_mdl_operation_conf_cb	op_conf;
+	mcap_mdl_del_cb			del;
+} mcap_cb_type;
+
+struct mcap_mdl_op_cb {
+	struct mcap_mdl		*mdl;		/* MDL for this operation */
+	mcap_cb_type		cb;		/* Operation callback */
+	gpointer		user_data;	/* Callback user data */
+};
+
+static void mcap_notify_error(struct mcap_mcl *mcl, GError *err);
+static int mcap_send_data(int sock, const uint8_t *buf, uint32_t size);
+static int send4B_cmd(struct mcap_mcl *mcl, uint8_t oc,
+					uint8_t rc, uint16_t mdl);
+static int send5B_cmd(struct mcap_mcl *mcl, uint8_t oc, uint8_t rc,
+					uint16_t mdl, uint8_t param);
+
+/* MCAP finite state machine functions */
+static void proc_req_connected(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len);
+static void proc_req_pending(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len);
+static void proc_req_active(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len);
+
+static void (*proc_req[])(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len) = {
+	proc_req_connected,
+	proc_req_pending,
+	proc_req_active
+};
+
+static void mcap_park_mcl(struct mcap_mcl *mcl);
+static void mcap_unpark_mcl(struct mcap_mcl *mcl);
+
+struct mcap_mcl *mcap_mcl_ref(struct mcap_mcl *mcl);
+void mcap_mcl_unref(struct mcap_mcl *mcl);
+
+static void default_mdl_connected_cb(struct mcap_mdl *mdl, gpointer data)
+{
+	debug("MCAP Unmanaged mdl connection");
+}
+static void default_mdl_closed_cb(struct mcap_mdl *mdl, gpointer data)
+{
+	debug("MCAP Unmanaged mdl clsoed");
+}
+static void default_mdl_deleted_cb(struct mcap_mdl *mdl, gpointer data)
+{
+	debug("MCAP Unmanaged mdl deleted");
+}
+static uint8_t default_mdl_conn_req_cb(struct mcap_mcl *mcl,
+						uint8_t mdepid, uint16_t mdlid,
+						uint8_t *conf, gpointer data)
+{
+	debug("MCAP mdl remote connection aborted");
+	/* Due to this callback is not managed this request won't be supported */
+	return MCAP_REQUEST_NOT_SUPPORTED;
+}
+static uint8_t default_mdl_reconn_req_cb(struct mcap_mdl *mdl,
+						gpointer data)
+{
+	debug("MCAP mdl remote reconnection aborted");
+	/* Due to this callback is not managed this request won't be supported */
+	return MCAP_REQUEST_NOT_SUPPORTED;
+}
+
+static void set_default_cb(struct mcap_mcl *mcl)
+{
+	if (!mcl->cb)
+		mcl->cb = g_new0(struct mcap_mdl_cb, 1);
+
+	mcl->cb->mdl_connected = default_mdl_connected_cb;
+	mcl->cb->mdl_closed = default_mdl_closed_cb;
+	mcl->cb->mdl_deleted = default_mdl_deleted_cb;
+	mcl->cb->mdl_conn_req = default_mdl_conn_req_cb;
+	mcl->cb->mdl_reconn_req = default_mdl_reconn_req_cb;
+}
+
+static GQuark mcap_error_quark(void)
+{
+	return g_quark_from_static_string("mcap-error-quark");
+}
+
+static char *error2str(uint8_t rc)
+{
+	switch (rc) {
+	case MCAP_SUCCESS:
+		return "Success";
+	case MCAP_INVALID_OP_CODE:
+		return "Invalid Op Code";
+	case MCAP_INVALID_PARAM_VALUE:
+		return "Ivalid Parameter Value";
+	case MCAP_INVALID_MDEP:
+		return "Invalid MDEP";
+	case MCAP_MDEP_BUSY:
+		return "MDEP Busy";
+	case MCAP_INVALID_MDL:
+		return "Invalid MDL";
+	case MCAP_MDL_BUSY:
+		return "MDL Busy";
+	case MCAP_INVALID_OPERATION:
+		return "Invalid Operation";
+	case MCAP_RESOURCE_UNAVAILABLE:
+		return "Resource Unavailable";
+	case MCAP_UNESPECIFIED_ERROR:
+		return "Unspecified Error";
+	case MCAP_REQUEST_NOT_SUPPORTED:
+		return "Request Not Supported";
+	case MCAP_CONFIGURATION_REJECTED:
+		return "Configuration Rejected";
+	default:
+		return "Unknown Response Code";
+	}
+}
+
+static struct mcap_mcl *find_mcl(GSList *list, const bdaddr_t *addr)
+{
+	GSList *l;
+	struct mcap_mcl *mcl;
+
+	for (l = list; l; l = l->next) {
+		mcl = l->data;
+
+		if (!bacmp(&mcl->addr, addr))
+			return mcl;
+	}
+
+	return NULL;
+}
+
+static void update_mcl_state(struct mcap_mcl *mcl)
+{
+	GSList *l;
+	struct mcap_mdl *mdl;
+
+	if (mcl->state == MCL_PENDING)
+		return;
+
+	for (l = mcl->mdls; l; l = l->next) {
+		mdl = l->data;
+
+		if (mdl->state == MDL_CONNECTED) {
+			mcl->state = MCL_ACTIVE;
+			return;
+		}
+	}
+
+	mcl->state = MCL_CONNECTED;
+}
+
+static int mcap_send_data(int sock, const uint8_t *buf, uint32_t size)
+{
+	uint32_t sent = 0;
+
+	while (sent < size) {
+		int n = send(sock, buf + sent, size - sent, 0);
+		if (n < 0)
+			return -1;
+		sent += n;
+	}
+	return 0;
+}
+
+static void mcap_send_std_opcode(struct mcap_mcl *mcl, uint8_t *cmd,
+						uint32_t size, GError **err)
+{
+	if (mcl->state == MCL_IDLE) {
+		g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED,
+			"MCL is not connected");
+		return;
+	}
+
+	if (mcl->req != MCL_AVAILABLE) {
+		g_set_error(err, MCAP_ERROR, MCAP_ERROR_RESOURCE_UNAVAILABLE,
+					"Pending request");
+		return;
+	}
+
+	if (!(mcl->ctrl & MCAP_CTRL_STD_OP)) {
+		g_set_error(err, MCAP_ERROR, MCAP_ERROR_REQUEST_NOT_SUPPORTED,
+				    "Remote does not support standard opcodes");
+		return;
+	}
+
+	if (mcl->state == MCL_PENDING) {
+		g_set_error(err, MCAP_ERROR, MCAP_ERROR_INVALID_OPERATION,
+			"Not Std Op. Codes can be sent in PENDING State");
+		return;
+	}
+
+	if (mcap_send_data(g_io_channel_unix_get_fd(mcl->cc), cmd, size) < 0) {
+		g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED,
+				    "Command can't be sent, write error");
+		return;
+	}
+
+	mcl->lcmd = cmd;
+	mcl->req = MCL_WAITING_RSP;
+}
+
+static int send4B_cmd(struct mcap_mcl *mcl, uint8_t oc, uint8_t rc,
+								uint16_t mdl)
+{
+	uint8_t *rsp;
+	mcap4B_rsp *rsp_err;
+	int sent;
+
+
+	rsp = g_malloc0(sizeof(mcap4B_rsp));
+
+	rsp_err = (mcap4B_rsp *)rsp;
+	rsp_err->op = oc;
+	rsp_err->rc = rc;
+	rsp_err->mdl = htons (mdl);
+
+	sent = mcap_send_data(g_io_channel_unix_get_fd(mcl->cc),
+					rsp,
+					sizeof(mcap4B_rsp));
+	g_free(rsp);
+	return sent;
+}
+
+static int send5B_cmd(struct mcap_mcl *mcl, uint8_t oc, uint8_t rc,
+						uint16_t mdl, uint8_t param)
+{
+	uint8_t *rsp;
+	mcap5B_rsp *suc;
+	int sent;
+
+	rsp = g_malloc0(sizeof(mcap5B_rsp));
+
+	suc = (mcap5B_rsp *)rsp;
+	suc->op = oc;
+	suc->rc = rc;
+	suc->mdl = htons(mdl);
+	suc->param = param;
+
+	sent = mcap_send_data(g_io_channel_unix_get_fd(mcl->cc), rsp,
+							sizeof(mcap5B_rsp));
+	g_free(rsp);
+	return sent;
+}
+
+static uint16_t generate_mdlid(struct mcap_mcl *mcl)
+{
+	uint16_t mdlid = MCAP_MDLID_INITIAL;
+	struct mcap_mdl *mdl;
+	GSList *l;
+
+	for (l = mcl->mdls; l; l = l->next) {
+		mdl = l->data;
+		if (mdlid < mdl->mdlid)
+			break;
+		else
+			mdlid = mdl->mdlid + 1;
+	}
+
+	if (mdlid > MCAP_MDLID_FINAL)
+		return 0;
+
+	return mdlid;
+}
+
+static uint8_t *create_req(uint8_t op, uint16_t mdl_id)
+{
+	uint8_t *req;
+	mcap_md_req *req_cmd;
+
+	req = g_malloc0(sizeof(mcap_md_req));
+
+	req_cmd = (mcap_md_req *)req;
+	req_cmd->op = op;
+	req_cmd->mdl = htons(mdl_id);
+
+	return req;
+}
+
+static uint8_t *create_mdl_req(uint16_t mdl_id, uint8_t mdep, uint8_t conf)
+{
+	uint8_t *req;
+	mcap_md_create_mdl_req *req_mdl;
+
+	req = g_malloc0(sizeof(mcap_md_create_mdl_req));
+
+	req_mdl = (mcap_md_create_mdl_req *)req;
+	req_mdl->op = MCAP_MD_CREATE_MDL_REQ;
+	req_mdl->mdl = htons(mdl_id);
+	req_mdl->mdep = mdep;
+	req_mdl->conf = conf;
+
+	return req;
+}
+
+static gint compare_mdl(gconstpointer a, gconstpointer b)
+{
+	const struct mcap_mdl *mdla = a;
+	const struct mcap_mdl *mdlb = b;
+
+	if (mdla->mdlid == mdlb->mdlid)
+		return 0;
+	else if (mdla->mdlid < mdlb->mdlid)
+		return -1;
+	else
+		return 1;
+}
+
+static gboolean wait_response_timer(gpointer data)
+{
+	struct mcap_mcl *mcl = data;
+
+	GError *gerr = NULL;
+
+	RELEASE_TIMER(mcl);
+
+	g_set_error(&gerr, MCAP_ERROR, MCAP_ERROR_FAILED,
+					"Timeout waiting response");
+
+	mcap_notify_error(mcl, gerr);
+
+	g_error_free(gerr);
+	mcl->ms->mcl_disconnected_cb(mcl, mcl->ms->user_data);
+	mcap_park_mcl(mcl);
+	return FALSE;
+}
+
+void mcap_req_mdl_creation(struct mcap_mcl *mcl,
+				uint8_t mdepid,
+				uint8_t conf,
+				GError **err,
+				mcap_mdl_operation_conf_cb connect_cb,
+				gpointer user_data)
+{
+	struct mcap_mdl *mdl;
+	struct mcap_mdl_op_cb *con;
+	uint8_t *cmd = NULL;
+	uint16_t id;
+
+	id = generate_mdlid(mcl);
+	if (!id) {
+		g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED,
+					"Not more mdlids available");
+		return;
+	}
+
+	mdl = g_new0(struct mcap_mdl, 1);
+	mdl->mcl = mcl;
+	mdl->mdlid = id;
+	mdl->mdep_id = mdepid;
+	mdl->state = MDL_WAITING;
+
+	con = g_new0(struct mcap_mdl_op_cb, 1);
+	con->mdl = mdl;
+	con->cb.op_conf = connect_cb;
+	con->user_data = user_data;
+
+	cmd = create_mdl_req(id, mdepid, conf);
+	mcap_send_std_opcode(mcl, cmd, sizeof(mcap_md_create_mdl_req), err);
+	if (*err) {
+		g_free(mdl);
+		g_free(con);
+		g_free(cmd);
+		return;
+	}
+
+	mcl->state = MCL_ACTIVE;
+	mcl->priv_data = con;
+
+	mcl->mdls = g_slist_insert_sorted(mcl->mdls, mdl, compare_mdl);
+	mcl->tid = g_timeout_add_seconds(RESPONSE_TIMER, wait_response_timer, mcl);
+}
+
+void mcap_req_mdl_reconnect(struct mcap_mdl *mdl,
+				GError **err,
+				mcap_mdl_operation_cb reconnect_cb,
+				gpointer user_data)
+{
+	struct mcap_mdl_op_cb *con;
+	struct mcap_mcl *mcl = mdl->mcl;
+	uint8_t *cmd;
+
+	if (mdl->state != MDL_CLOSED) {
+		g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED,
+					"MDL is not closed");
+		return;
+	}
+	con = g_new0(struct mcap_mdl_op_cb, 1);
+
+	cmd = create_req(MCAP_MD_RECONNECT_MDL_REQ, mdl->mdlid);
+	mcap_send_std_opcode(mcl, cmd, sizeof(mcap_md_req), err);
+	if (*err) {
+		g_free(con);
+		g_free(cmd);
+		return;
+	}
+
+	mdl->state = MDL_WAITING;
+
+	con->mdl = mdl;
+	con->cb.op = reconnect_cb;
+	con->user_data = user_data;
+
+	mcl->state = MCL_ACTIVE;
+	mcl->priv_data = con;
+
+	mcl->tid = g_timeout_add_seconds(RESPONSE_TIMER, wait_response_timer, mcl);
+}
+
+static void send_delete_req(GError **err, struct mcap_mcl *mcl,
+				struct mcap_mdl_op_cb *con, uint16_t mdlid)
+{
+	uint8_t *cmd;
+
+	cmd = create_req(MCAP_MD_DELETE_MDL_REQ, mdlid);
+	mcap_send_std_opcode(mcl, cmd, sizeof(mcap_md_req), err);
+	if (*err) {
+		g_free(cmd);
+		return;
+	}
+
+	mcl->priv_data = con;
+
+	mcl->tid = g_timeout_add_seconds(RESPONSE_TIMER, wait_response_timer, mcl);
+}
+
+void mcap_req_mdl_delete_all(struct mcap_mcl *mcl, GError **err,
+			mcap_mdl_del_cb delete_cb, gpointer user_data)
+{
+	GSList *l;
+	struct mcap_mdl *mdl;
+	struct mcap_mdl_op_cb *con;
+
+	debug("MCL in state: %d", mcl->state);
+	if (!mcl->mdls) {
+		g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED,
+				"There are not MDLs created");
+		return;
+	}
+
+	for (l = mcl->mdls; l; l = l->next) {
+		mdl = l->data;
+		if (mdl->state != MDL_WAITING)
+			mdl->state = MDL_DELETING;
+	}
+
+	con = g_new0(struct mcap_mdl_op_cb, 1);
+	con->mdl = NULL;
+	con->cb.del = delete_cb;
+	con->user_data = user_data;
+
+	send_delete_req(err, mcl, con, MCAP_ALL_MDLIDS);
+	if (*err)
+		g_free(con);
+}
+
+void mcap_req_mdl_deletion(struct mcap_mdl *mdl, GError **err,
+			mcap_mdl_del_cb delete_cb, gpointer user_data)
+{
+	struct mcap_mcl *mcl= mdl->mcl;
+	struct mcap_mdl_op_cb *con;
+	GSList *l;
+
+	l = g_slist_find(mcl->mdls, mdl);
+
+	if (!l) {
+		g_set_error(err, MCAP_ERROR, MCAP_ERROR_INVALID_MDL,
+					"%s" , error2str(MCAP_INVALID_MDEP));
+		return;
+	}
+
+	if (mdl->state == MDL_WAITING) {
+		g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED,
+							"Mdl is not created");
+		return;
+	}
+	mdl->state = MDL_DELETING;
+
+	con = g_new0(struct mcap_mdl_op_cb, 1);
+	con->mdl = mdl;
+	con->cb.del = delete_cb;
+	con->user_data = user_data;
+
+	send_delete_req(err, mcl, con, mdl->mdlid);
+	if (*err)
+		g_free(con);
+}
+
+void mcap_mdl_abort(struct mcap_mdl *mdl, GError **err,
+			mcap_mdl_del_cb abort_cb, gpointer user_data)
+{
+	struct mcap_mdl_op_cb *con;
+	struct mcap_mcl *mcl = mdl->mcl;
+	uint8_t *cmd;
+
+	if (mdl->state != MDL_WAITING) {
+		g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED,
+							"Mdl in invalid state");
+		return;
+	}
+
+	con = g_new0(struct mcap_mdl_op_cb, 1);
+	cmd = create_req(MCAP_MD_ABORT_MDL_REQ, mdl->mdlid);
+	mcap_send_std_opcode(mcl, cmd, sizeof(mcap_md_req), err);
+	if (*err) {
+		g_free(con);
+		g_free(cmd);
+		return;
+	}
+
+	con->mdl = mdl;
+	con->cb.del = abort_cb;
+	con->user_data = user_data;
+
+	mcl->priv_data = con;
+
+	mcl->tid = g_timeout_add_seconds(RESPONSE_TIMER, wait_response_timer, mcl);
+}
+
+int mcap_mdl_get_fd(struct mcap_mdl *mdl)
+{
+	if ((!mdl) || (mdl->state != MDL_CONNECTED))
+		return -1;
+
+	return g_io_channel_unix_get_fd(mdl->dc);
+}
+
+uint16_t mcap_mdl_get_mdlid(struct mcap_mdl *mdl)
+{
+	if (!mdl)
+		return MCAP_MDLID_RESERVED;
+
+	return mdl->mdlid;
+}
+
+static void shutdown_mdl(struct mcap_mdl *mdl)
+{
+	mdl->state = MDL_CLOSED;
+
+	g_source_remove(mdl->wid);
+
+	if (mdl->dc) {
+		g_io_channel_shutdown(mdl->dc, TRUE, NULL);
+		g_io_channel_unref(mdl->dc);
+		mdl->dc = NULL;
+	}
+}
+
+static void mcap_free_mdls(struct mcap_mcl *mcl, gboolean park)
+{
+	GSList *l;
+	struct mcap_mdl *mdl;
+
+	if (!mcl->mdls)
+		return;
+
+	for (l = mcl->mdls; l; l = l->next) {
+		mdl = l->data;
+		shutdown_mdl(mdl);
+		if (!park)
+			g_free(mdl);
+	}
+
+	if (!park) {
+		g_slist_free(mcl->mdls);
+		mcl->mdls = NULL;
+	}
+}
+
+static void mcap_mcl_free(struct mcap_mcl *mcl, gboolean save)
+{
+	gboolean store = ((!(mcl->ctrl & MCAP_CTRL_FREE)) && save);
+
+	if (mcl->tid) {
+		RELEASE_TIMER(mcl);
+	}
+
+	if (mcl->cc) {
+		g_io_channel_shutdown(mcl->cc, TRUE, NULL);
+		g_io_channel_unref(mcl->cc);
+		mcl->cc = NULL;
+	}
+
+	g_source_remove(mcl->wid);
+	if (mcl->lcmd) {
+		g_free(mcl->lcmd);
+		mcl->lcmd = NULL;
+	}
+
+	if (mcl->priv_data) {
+		g_free(mcl->priv_data);
+		mcl->priv_data = NULL;
+	}
+
+	mcap_free_mdls(mcl, store);
+
+	if (mcl->cb && !store) {
+		g_free(mcl->cb);
+		mcl->cb = NULL;
+	}
+
+	mcl->state = MCL_IDLE;
+
+	if (store)
+		return;
+
+	g_free(mcl);
+}
+
+static void mcap_mcl_shutdown(struct mcap_mcl *mcl)
+{
+	mcap_mcl_free(mcl, TRUE);
+}
+
+static void mcap_mcl_release(struct mcap_mcl *mcl)
+{
+	mcap_mcl_free(mcl, FALSE);
+}
+
+static void mcap_mcl_check_del(struct mcap_mcl *mcl)
+{
+	if (mcl->ctrl & MCAP_CTRL_CACHED)
+		mcap_mcl_shutdown(mcl);
+	else
+		mcap_mcl_unref(mcl);
+}
+
+static void mcap_park_mcl(struct mcap_mcl *mcl)
+{
+	GSList *l;
+	struct mcap_mcl *last;
+	int len;
+
+	if (mcl->ctrl & MCAP_CTRL_CACHED)
+		return;
+
+	mcl->ms->mcls = g_slist_remove(mcl->ms->mcls, mcl);
+
+	if ((mcl->ctrl & MCAP_CTRL_NOCACHE) || (mcl->ref < 2)) {
+		mcap_mcl_unref(mcl);
+		return;
+	}
+
+	debug("Caching MCL");
+
+	len = g_slist_length(mcl->ms->cached);
+	if (len == MAX_CACHED) {
+		/* Remove the latest cached mcl */
+		l = g_slist_last(mcl->ms->cached);
+		last = l->data;
+		mcl->ms->cached = g_slist_remove(mcl->ms->cached, last);
+		last->ctrl &= ~MCAP_CTRL_CACHED;
+		if (last->ctrl & MCAP_CTRL_CONN) {
+			/* If connection process is not success this MCL will be
+			 * freed next time that mcap_mcl_free is invoked */
+			last->ctrl |= MCAP_CTRL_FREE;
+		} else {
+			last->ms->mcl_uncached_cb(last, last->ms->user_data);
+			mcap_mcl_unref(last);
+		}
+	}
+
+	mcl->ms->cached = g_slist_prepend(mcl->ms->cached, mcl);
+	mcl->ctrl |= MCAP_CTRL_CACHED;
+	mcap_mcl_shutdown(mcl);
+}
+
+static void mcap_unpark_mcl(struct mcap_mcl *mcl)
+{
+	if (!(mcl->ctrl & MCAP_CTRL_CACHED))
+		return;
+
+	debug("Got MCL from cache");
+
+	mcl->ms->cached = g_slist_remove(mcl->ms->cached, mcl);
+	mcl->ms->mcls = g_slist_prepend(mcl->ms->mcls, mcl);
+	mcl->ctrl &= ~MCAP_CTRL_CACHED;
+	mcl->ctrl &= ~MCAP_CTRL_FREE;
+}
+
+void mcap_close_mcl(struct mcap_mcl *mcl, gboolean cache)
+{
+	if (!mcl)
+		return;
+
+	if (mcl->cc) {
+		g_io_channel_shutdown(mcl->cc, TRUE, NULL);
+		g_io_channel_unref(mcl->cc);
+		mcl->cc = NULL;
+	}
+
+	mcl->state = MCL_IDLE;
+
+	if (!cache)
+		mcl->ctrl |= MCAP_CTRL_NOCACHE;
+}
+
+struct mcap_mcl *mcap_mcl_ref(struct mcap_mcl *mcl)
+{
+	mcl->ref++;
+
+	debug("mcap_mcl_ref(%p): ref=%d", mcl, mcl->ref);
+
+	return mcl;
+}
+
+void mcap_mcl_unref(struct mcap_mcl *mcl)
+{
+	mcl->ref--;
+
+	debug("mcap_mcl_unref(%p): ref=%d", mcl, mcl->ref);
+
+	if (mcl->ref > 0)
+		return;
+
+	mcap_mcl_release(mcl);
+}
+
+static gboolean parse_set_opts(struct mcap_mdl_cb *mdl_cb, GError **err,
+						McapMclCb cb1, va_list args)
+{
+	McapMclCb cb = cb1;
+	struct mcap_mdl_cb *c;
+
+	c = g_new0(struct mcap_mdl_cb, 1);
+
+	while (cb != MCAP_MDL_CB_INVALID) {
+		switch (cb) {
+		case MCAP_MDL_CB_CONNECTED:
+			c->mdl_connected = va_arg(args,
+				mcap_mdl_event_cb);
+			break;
+		case MCAP_MDL_CB_CLOSED:
+			c->mdl_closed = va_arg(args,
+				mcap_mdl_event_cb);
+			break;
+		case MCAP_MDL_CB_DELETED:
+			c->mdl_deleted = va_arg(args,
+				mcap_mdl_event_cb);
+			break;
+		case MCAP_MDL_CB_ABORTED:
+			c->mdl_aborted = va_arg(args,
+				mcap_mdl_event_cb);
+			break;
+		case MCAP_MDL_CB_REMOTE_CONN_REQ:
+			c->mdl_conn_req = va_arg(args,
+				mcap_remote_mdl_conn_req_cb);
+			break;
+		case MCAP_MDL_CB_REMOTE_RECONN_REQ:
+			c->mdl_reconn_req = va_arg(args,
+				mcap_remote_mdl_reconn_req_cb);
+			break;
+		default:
+			g_set_error(err, MCAP_ERROR, MCAP_ERROR_INVALID_ARGS,
+				"Unknown option %d", cb);
+			return FALSE;
+		}
+		cb = va_arg(args, int);
+	}
+
+	/* Set new callbacks */
+	if (c->mdl_connected)
+		mdl_cb->mdl_connected = c->mdl_connected;
+	if (c->mdl_closed)
+		mdl_cb->mdl_closed = c->mdl_closed;
+	if (c->mdl_deleted)
+		mdl_cb->mdl_deleted = c->mdl_deleted;
+	if (c->mdl_aborted)
+		mdl_cb->mdl_aborted = c->mdl_aborted;
+	if (c->mdl_conn_req)
+		mdl_cb->mdl_conn_req = c->mdl_conn_req;
+	if (c->mdl_reconn_req)
+		mdl_cb->mdl_reconn_req = c->mdl_reconn_req;
+
+	g_free(c);
+	return TRUE;
+}
+
+void mcap_mcl_set_cb(struct mcap_mcl *mcl, GError **gerr,
+					gpointer user_data, McapMclCb cb1, ...)
+{
+	va_list args;
+	gboolean ret;
+
+	va_start(args, cb1);
+	ret = parse_set_opts(mcl->cb, gerr, cb1, args);
+	va_end(args);
+
+	if (!ret)
+		return;
+
+	mcl->cb->user_data = user_data;
+	return;
+}
+
+bdaddr_t mcap_mcl_get_addr(struct mcap_mcl *mcl)
+{
+	return mcl->addr;
+}
+
+static void proc_sync_cmd(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len)
+{
+	/*
+	 * Current MCAP implementation does not support the
+	 * Clock Synchronization Protocol
+	 */
+
+	send4B_cmd(mcl, cmd[0], MCAP_REQUEST_NOT_SUPPORTED, MCAP_MDLID_RESERVED);
+#if 0
+	switch (cmd[0]) {
+	case MCAP_MD_SYNC_CAP_REQ:
+		debug("TODO: received MCAP_MD_SYNC_CAP_REQ: %d",
+			MCAP_MD_SYNC_CAP_REQ);
+		break;
+	case MCAP_MD_SYNC_CAP_RSP:
+		debug("TODO: received MCAP_MD_SYNC_CAP_RSP: %d",
+			MCAP_MD_SYNC_CAP_RSP);
+		break;
+	case MCAP_MD_SYNC_SET_REQ:
+		debug("TODO: received MCAP_MD_SYNC_SET_REQ: %d",
+			MCAP_MD_SYNC_SET_REQ);
+		break;
+	case MCAP_MD_SYNC_SET_RSP:
+		debug("TODO: received MCAP_MD_SYNC_SET_RSP: %d",
+			MCAP_MD_SYNC_SET_RSP);
+		break;
+	case MCAP_MD_SYNC_INFO_IND:
+		debug("TODO: received MCAP_MD_SYNC_INFO_IND :%d",
+			MCAP_MD_SYNC_INFO_IND);
+		break;
+        }
+#endif
+}
+
+static void error_cmd_rsp(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len)
+{
+	uint16_t mdlr;
+
+	if (cmd[0] <= MCAP_MD_DELETE_MDL_RSP) {
+		/* Standard Op Code request is invalid in current state */
+		error("Invalid cmd received (op code = %d) in state %d",
+							cmd[0],	mcl->state);
+		/* Get mdlid sended to generate appropiate response if it is possible */
+		mdlr = len < sizeof(mcap_md_req) ? MCAP_MDLID_RESERVED :
+						ntohs(((mcap_md_req *)cmd)->mdl);
+		send4B_cmd(mcl, cmd[0]+1, MCAP_INVALID_OPERATION, mdlr);
+	} else {
+		error("Unknown cmd request received (op code = %d) in state %d",
+							cmd[0], mcl->state);
+		send4B_cmd(mcl, MCAP_ERROR_RSP, MCAP_INVALID_OP_CODE,
+							MCAP_MDLID_RESERVED);
+	}
+}
+
+static struct mcap_mdl *get_mdl(struct mcap_mcl *mcl, uint16_t mdlid)
+{
+	GSList *l;
+	struct mcap_mdl *mdl;
+
+	for (l = mcl->mdls; l; l = l->next) {
+		mdl = l->data;
+		if (mdlid == mdl->mdlid)
+			return mdl;
+	}
+
+	return NULL;
+}
+
+static void mcap_delete_mdl(gpointer elem, gpointer user_data)
+{
+	struct mcap_mdl *mdl = elem;
+	gboolean notify = *(gboolean *)user_data;
+
+	shutdown_mdl(mdl);
+	if (notify)
+		mdl->mcl->cb->mdl_deleted(mdl, mdl->mcl->cb->user_data);
+	g_free(mdl);
+}
+
+/* Functions used to process request */
+
+static void process_md_create_mdl_req(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len)
+{
+	mcap_md_create_mdl_req *req;
+	struct mcap_mdl *mdl;
+	uint16_t mdl_id;
+	uint8_t mdep_id;
+	uint8_t cfga, conf;
+	uint8_t rsp;
+
+	if (len != sizeof(mcap_md_create_mdl_req)) {
+		send4B_cmd(mcl, MCAP_MD_CREATE_MDL_RSP,
+				MCAP_INVALID_PARAM_VALUE, MCAP_MDLID_RESERVED);
+		return;
+	}
+
+	req = (mcap_md_create_mdl_req *)cmd;
+
+	mdl_id = ntohs(req->mdl);
+	if ((mdl_id < MCAP_MDLID_INITIAL) || (mdl_id > MCAP_MDLID_FINAL)) {
+		send4B_cmd(mcl, MCAP_MD_CREATE_MDL_RSP, MCAP_INVALID_MDL, mdl_id);
+		return;
+	}
+
+	mdep_id = req->mdep;
+	if (mdep_id > MCAP_MDEPID_FINAL) {
+		send4B_cmd(mcl, MCAP_MD_CREATE_MDL_RSP, MCAP_INVALID_MDEP, mdl_id);
+		return;
+	}
+
+	cfga = conf = req->conf;
+	/* Callback to upper layer */
+	rsp = mcl->cb->mdl_conn_req(mcl, mdep_id, mdl_id, &conf,
+							mcl->cb->user_data);
+
+	if ((cfga != 0) && (cfga != conf)) {
+		/* Remote device set default configuration but upper profile */
+		/* has changed it. Protocol Error: force closing the MCL by */
+		/* using remote device using UNESPECIFIED_ERROR response */
+		send4B_cmd(mcl, MCAP_MD_CREATE_MDL_RSP, MCAP_UNESPECIFIED_ERROR,
+								mdl_id);
+		return;
+	}
+	if (rsp != MCAP_SUCCESS) {
+		send4B_cmd(mcl, MCAP_MD_CREATE_MDL_RSP, rsp, mdl_id);
+		return;
+	}
+
+	mdl = get_mdl(mcl, mdl_id);
+	if (!mdl) {
+		mdl = g_malloc0(sizeof(struct mcap_mdl));
+		mdl->mcl = mcl;
+		mdl->mdlid = mdl_id;
+	} else if (mdl->state == MDL_CONNECTED) {
+		shutdown_mdl(mdl);
+		//mcl->cb->mdl_closed(mdl, mcl->cb->user_data);
+	}
+	mdl->mdep_id = mdep_id;
+	mdl->state = MDL_WAITING;
+	mcl->mdls = g_slist_insert_sorted(mcl->mdls, mdl, compare_mdl);
+
+	mcl->state = MCL_PENDING;
+	send5B_cmd(mcl, MCAP_MD_CREATE_MDL_RSP, MCAP_SUCCESS, mdl_id, conf);
+}
+
+static void process_md_reconnect_mdl_req(struct mcap_mcl *mcl, uint8_t *cmd,
+								uint32_t len)
+{
+	mcap_md_req *req;
+	struct mcap_mdl *mdl;
+	uint16_t mdl_id;
+	uint8_t rsp;
+
+	if (len != sizeof(mcap_md_req)) {
+		send4B_cmd(mcl, MCAP_MD_RECONNECT_MDL_RSP,
+				MCAP_INVALID_PARAM_VALUE, MCAP_MDLID_RESERVED);
+		return;
+	}
+
+	req = (mcap_md_req *)cmd;
+	mdl_id = ntohs(req->mdl);
+
+	mdl = get_mdl(mcl, mdl_id);
+	if (!mdl) {
+		send4B_cmd(mcl, MCAP_MD_RECONNECT_MDL_RSP,
+						MCAP_INVALID_MDL, mdl_id);
+		return;
+	}
+
+	/* Callback to upper layer */
+	rsp = mcl->cb->mdl_reconn_req(mdl, mcl->cb->user_data);
+	if (rsp != MCAP_SUCCESS) {
+		send4B_cmd(mcl, MCAP_MD_CREATE_MDL_RSP, rsp, mdl_id);
+		return;
+	}
+
+	if (mdl->state == MDL_CONNECTED)
+		shutdown_mdl(mdl);
+
+	mdl->state = MDL_WAITING;
+	mcl->state = MCL_PENDING;
+	send4B_cmd(mcl, MCAP_MD_RECONNECT_MDL_RSP, MCAP_SUCCESS, mdl_id);
+}
+
+static void process_md_abort_mdl_req(struct mcap_mcl *mcl, uint8_t *cmd,
+								uint32_t len)
+{
+	mcap_md_req *req;
+	GSList *l;
+	struct mcap_mdl *mdl, *del;
+	uint16_t mdl_id;
+
+	if (len != sizeof(mcap_md_req)) {
+		send4B_cmd(mcl, MCAP_MD_ABORT_MDL_RSP,
+				MCAP_INVALID_PARAM_VALUE, MCAP_MDLID_RESERVED);
+		return;
+	}
+
+	req = (mcap_md_req *)cmd;
+	mdl_id = ntohs(req->mdl);
+	mcl->state = MCL_CONNECTED;
+	for (l = mcl->mdls; l; l = l->next) {
+		mdl = l->data;
+		if ((mdl_id == mdl->mdlid) && (mdl->state == MDL_WAITING)) {
+			del = mdl;
+			if (mcl->state != MCL_CONNECTED)
+				break;
+			continue;
+		}
+		if ((mdl->state == MDL_CONNECTED) && (mcl->state != MCL_ACTIVE))
+			mcl->state = MCL_ACTIVE;
+
+		if ((del) && (mcl->state == MCL_ACTIVE))
+			break;
+	}
+
+	if (!del) {
+		send4B_cmd(mcl, MCAP_MD_ABORT_MDL_RSP, MCAP_INVALID_MDL, mdl_id);
+		return;
+	}
+
+	mcl->cb->mdl_aborted(del, mcl->cb->user_data);
+
+	mcl->mdls = g_slist_remove(mcl->mdls, del);
+	g_free(del);
+	send4B_cmd(mcl, MCAP_MD_ABORT_MDL_RSP, MCAP_SUCCESS, mdl_id);
+}
+
+static void process_md_delete_mdl_req(struct mcap_mcl *mcl, uint8_t *cmd,
+								uint32_t len)
+{
+	mcap_md_req *req;
+	struct mcap_mdl *mdl, *aux;
+	uint16_t mdlid;
+	gboolean notify;
+	GSList *l;
+
+	debug("process MCAP_MD_DELETE_MDL_REQ");
+
+	if (len != sizeof(mcap_md_req)) {
+		send4B_cmd(mcl, MCAP_MD_ABORT_MDL_RSP,
+				MCAP_INVALID_PARAM_VALUE, MCAP_MDLID_RESERVED);
+		return;
+	}
+
+	req = (mcap_md_req *)cmd;
+	mdlid = ntohs(req->mdl);
+	if (mdlid == MCAP_ALL_MDLIDS) {
+		notify = FALSE;
+		g_slist_foreach(mcl->mdls, mcap_delete_mdl, &notify);
+		g_slist_free(mcl->mdls);
+		mcl->mdls = NULL;
+		mcl->state = MCL_CONNECTED;
+		/* NULL mdl means ALL_MDLS */
+		mcl->cb->mdl_deleted(NULL, mcl->cb->user_data);
+		goto resp;
+	}
+
+	if ((mdlid < MCAP_MDLID_INITIAL) || (mdlid > MCAP_MDLID_FINAL)) {
+		send4B_cmd(mcl, MCAP_MD_CREATE_MDL_RSP, MCAP_INVALID_MDL, mdlid);
+		return;
+	}
+
+	for (l=mcl->mdls, mdl = NULL; l; l = l->next) {
+		aux = l->data;
+		if (aux->mdlid == mdlid) {
+			mdl = aux;
+			break;
+		}
+	}
+
+	if (!mdl || (mdl->state == MDL_WAITING)) {
+		send4B_cmd(mcl, MCAP_MD_DELETE_MDL_RSP, MCAP_INVALID_MDL, mdlid);
+		return;
+	}
+	mcl->mdls = g_slist_remove(mcl->mdls, mdl);
+	update_mcl_state(mcl);
+	notify = TRUE;
+	mcap_delete_mdl(mdl, &notify);
+resp:
+	send4B_cmd(mcl, MCAP_MD_DELETE_MDL_RSP, MCAP_SUCCESS, mdlid);
+}
+
+static gboolean check_err_rsp(uint16_t rmdl, uint16_t smdl, uint8_t rc,
+				uint32_t rlen, uint32_t len, GError **gerr)
+{
+	gboolean close = FALSE;
+	char *msg;
+	gint err = MCAP_ERROR_FAILED;
+
+	if (rmdl != smdl) {
+		msg = "MDLID received doesn't match with MDLID sended";
+		close = TRUE;
+		goto fail;
+	}
+
+	if ((rc != MCAP_SUCCESS) && (rc <= MCAP_CONFIGURATION_REJECTED)) {
+		msg = error2str(rc);
+		err = rc;
+		goto fail;
+	}
+
+	if (rlen < len) {
+		msg = "Protocol error";
+		close = TRUE;
+		goto fail;
+	}
+	return FALSE;
+fail:
+	g_set_error(gerr, MCAP_ERROR, err, "%s", msg);
+	return close;
+}
+
+static gboolean process_md_create_mdl_rsp(struct mcap_mcl *mcl,
+						uint8_t *cmd, uint32_t len)
+{
+	struct mcap_mdl_op_cb *conn = mcl->priv_data;
+	struct mcap_mdl *mdl = conn->mdl;
+	mcap_mdl_operation_conf_cb connect_cb = conn->cb.op_conf;
+	gpointer user_data = conn->user_data;
+	uint16_t mdlid;
+	mcap5B_rsp *rsp = (mcap5B_rsp *) cmd;
+	mcap_md_create_mdl_req *cmdlast;
+	GError *gerr = NULL;
+	gboolean close = FALSE;
+
+	g_free(mcl->priv_data);
+	mcl->priv_data = NULL;
+
+	cmdlast = (mcap_md_create_mdl_req *) mcl->lcmd;
+	mdlid = ntohs(cmdlast->mdl);
+	rsp->mdl = ntohs(rsp->mdl);
+
+	g_free(mcl->lcmd);
+	mcl->lcmd = NULL;
+	mcl->req = MCL_AVAILABLE;
+	close = check_err_rsp(rsp->mdl, mdlid, rsp->rc, 0, 0, &gerr);
+
+	if (gerr)
+		goto fail;
+
+	if (len < 5) {
+		g_set_error(&gerr, MCAP_ERROR, MCAP_ERROR_FAILED,
+							"Protocol error");
+		close = TRUE;
+		goto fail;
+	}
+
+	/* Check if preferences changed */
+	if ((cmdlast->conf != 0x00) && (rsp->param != cmdlast->conf)) {
+		g_set_error(&gerr, MCAP_ERROR, MCAP_ERROR_FAILED,
+							"Configutation changed");
+		close = TRUE;
+		goto fail;
+	}
+
+	connect_cb(mdl, rsp->param, gerr, user_data);
+	return close;
+fail:
+	connect_cb(NULL, 0, gerr, user_data);
+	mcl->mdls = g_slist_remove(mcl->mdls, mdl);
+	g_free(mdl);
+	g_error_free(gerr);
+	return close;
+}
+
+static gboolean process_md_reconnect_mdl_rsp(struct mcap_mcl *mcl,
+						uint8_t *cmd, uint32_t len)
+{
+	struct mcap_mdl_op_cb *reconn = mcl->priv_data;
+	struct mcap_mdl *mdl = reconn->mdl;
+	mcap_mdl_operation_cb reconn_cb = reconn->cb.op;
+	gpointer user_data = reconn->user_data;
+	mcap4B_rsp *rsp = (mcap4B_rsp *) cmd;
+	mcap_md_req *cmdlast = (mcap_md_req *) mcl->lcmd;
+	uint16_t mdlid = ntohs(cmdlast->mdl);
+	GError *gerr = NULL;
+	gboolean close = FALSE;
+
+	g_free(mcl->priv_data);
+	mcl->priv_data = NULL;
+
+	rsp->mdl = ntohs(rsp->mdl);
+
+	close = check_err_rsp(rsp->mdl, mdlid, rsp->rc, len,
+						sizeof(mcap4B_rsp), &gerr);
+
+	g_free(mcl->lcmd);
+	mcl->lcmd = NULL;
+	mcl->req = MCL_AVAILABLE;
+
+	reconn_cb(mdl, gerr, user_data);
+	if (!gerr)
+		return close;
+
+	g_error_free(gerr);
+	shutdown_mdl(mdl);
+
+	if (rsp->rc != MCAP_INVALID_MDL)
+		return close;
+
+	/* Remove cached mdlid */
+	mcl->mdls = g_slist_remove(mcl->mdls, mdl);
+	mcl->cb->mdl_deleted(mdl, mcl->cb->user_data);
+	g_free(mdl);
+
+	return close;
+}
+
+static gboolean process_md_abort_mdl_rsp(struct mcap_mcl *mcl,
+						uint8_t *cmd, uint32_t len)
+{
+	struct mcap_mdl_op_cb *abrt = mcl->priv_data;
+	struct mcap_mdl *mdl = abrt->mdl;
+	mcap_mdl_del_cb abrt_cb = abrt->cb.del;
+	gpointer user_data = abrt->user_data;
+	mcap4B_rsp *rsp = (mcap4B_rsp *) cmd;
+	mcap_md_req *cmdlast = (mcap_md_req *) mcl->lcmd;
+	uint16_t mdlid = ntohs(cmdlast->mdl);
+	GError *gerr = NULL;
+	gboolean close = FALSE;
+
+	g_free(mcl->priv_data);
+	mcl->priv_data = NULL;
+
+	rsp->mdl = ntohs(rsp->mdl);
+
+	close = check_err_rsp(rsp->mdl, mdlid, rsp->rc, len,
+						sizeof(mcap4B_rsp), &gerr);
+	g_free(mcl->lcmd);
+	mcl->lcmd = NULL;
+	mcl->req = MCL_AVAILABLE;
+
+	if (gerr) {
+		abrt_cb(gerr, user_data);
+		g_error_free(gerr);
+		return close;
+	}
+
+	mcl->mdls = g_slist_remove(mcl->mdls, mdl);
+	g_free(mdl);
+	update_mcl_state(mcl);
+	abrt_cb(gerr, user_data);
+	return close;
+}
+
+static void restore_mdl(gpointer elem, gpointer data)
+{
+	struct mcap_mdl *mdl = elem;
+
+	if (mdl->state == MDL_DELETING) {
+		if (mdl->dc)
+			mdl->state = MDL_CONNECTED;
+		else
+			mdl->state = MDL_CLOSED;
+	} else if (mdl->state == MDL_CLOSED)
+		mdl->mcl->cb->mdl_closed(mdl, mdl->mcl->cb->user_data);
+}
+
+static gboolean process_md_delete_mdl_rsp(struct mcap_mcl *mcl, uint8_t *cmd,
+								uint32_t len)
+{
+	struct mcap_mdl_op_cb *del = mcl->priv_data;
+	struct mcap_mdl *mdl = del->mdl;
+	mcap_mdl_del_cb deleted_cb = del->cb.del;
+	gpointer user_data = del->user_data;
+	mcap4B_rsp *rsp = (mcap4B_rsp *) cmd;
+	mcap_md_req *cmdlast = (mcap_md_req *) mcl->lcmd;
+	uint16_t mdlid = ntohs(cmdlast->mdl);
+	GError *gerr = NULL;
+	gboolean close = FALSE;
+	gboolean notify = FALSE;
+
+	g_free(mcl->priv_data);
+	mcl->priv_data = NULL;
+
+	rsp->mdl = ntohs(rsp->mdl);
+
+	close = check_err_rsp(rsp->mdl, mdlid, rsp->rc, len,
+						sizeof(mcap4B_rsp), &gerr);
+
+	g_free(mcl->lcmd);
+	mcl->lcmd = NULL;
+	mcl->req = MCL_AVAILABLE;
+	if (gerr) {
+		if (mdl)
+			restore_mdl(mdl, NULL);
+		else
+			g_slist_foreach(mcl->mdls, restore_mdl, NULL);
+		deleted_cb(gerr, user_data);
+		g_error_free(gerr);
+		return close;
+	}
+
+	if (mdlid == MCAP_ALL_MDLIDS) {
+		g_slist_foreach(mcl->mdls, mcap_delete_mdl, &notify);
+		g_slist_free(mcl->mdls);
+		mcl->mdls = NULL;
+		mcl->state = MCL_CONNECTED;
+		goto end;
+	}
+
+	mcl->mdls = g_slist_remove(mcl->mdls, mdl);
+	update_mcl_state(mcl);
+	mcap_delete_mdl(mdl, &notify);
+end:
+	deleted_cb(gerr, user_data);
+	return close;
+}
+
+/* Function used to process commands depending of MCL state */
+
+static void proc_req_connected(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len)
+{
+	switch (cmd[0]) {
+	case MCAP_MD_CREATE_MDL_REQ:
+		process_md_create_mdl_req(mcl, cmd, len);
+		break;
+	case MCAP_MD_RECONNECT_MDL_REQ:
+		process_md_reconnect_mdl_req(mcl, cmd, len);
+		break;
+	case MCAP_MD_DELETE_MDL_REQ:
+		process_md_delete_mdl_req(mcl, cmd, len);
+		break;
+	default:
+		error_cmd_rsp(mcl, cmd, len);
+	}
+}
+
+static void proc_req_pending(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len)
+{
+	if (cmd[0] == MCAP_MD_ABORT_MDL_REQ)
+		process_md_abort_mdl_req(mcl, cmd, len);
+	else
+		error_cmd_rsp(mcl, cmd, len);
+}
+
+static void proc_req_active(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len)
+{
+	switch (cmd[0]) {
+	case MCAP_MD_CREATE_MDL_REQ:
+		process_md_create_mdl_req(mcl, cmd, len);
+		break;
+	case MCAP_MD_RECONNECT_MDL_REQ:
+		process_md_reconnect_mdl_req(mcl, cmd, len);
+		break;
+	case MCAP_MD_DELETE_MDL_REQ:
+		process_md_delete_mdl_req(mcl, cmd, len);
+		break;
+	default:
+		error_cmd_rsp(mcl, cmd, len);
+	}
+}
+
+static void mcap_notify_error(struct mcap_mcl *mcl, GError *err)
+{
+	struct mcap_mdl_op_cb *con = mcl->priv_data;
+
+	if (!con || !mcl->lcmd)
+		return;
+
+	switch (mcl->lcmd[0]){
+	case MCAP_MD_CREATE_MDL_REQ:
+		con->cb.op_conf(NULL, 0, err, con->user_data);
+		break;
+	case MCAP_MD_ABORT_MDL_REQ:
+	case MCAP_MD_DELETE_MDL_REQ:
+		con->cb.del(err, con->user_data);
+		break;
+	case MCAP_MD_RECONNECT_MDL_REQ:
+		con->cb.op(NULL, err, con->user_data);
+		break;
+	}
+
+	g_free(mcl->priv_data);
+	mcl->priv_data = NULL;
+}
+
+static gboolean check_rsp(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len)
+{
+	mcap4B_rsp *rsp;
+	GError *gerr = NULL;
+
+	/* Check if the response matches with the last request */
+	if ((cmd[0] != MCAP_ERROR_RSP) && ((mcl->lcmd[0] + 1) != cmd[0]))
+		goto close_mcl;
+
+	if (len < 4)
+		goto close_mcl;
+
+	rsp = (mcap4B_rsp *)cmd;
+
+	if (rsp->rc == MCAP_REQUEST_NOT_SUPPORTED) {
+		debug("Remote does not support opcodes");
+		g_set_error(&gerr, MCAP_ERROR, MCAP_ERROR_REQUEST_NOT_SUPPORTED,
+						"%s", error2str(rsp->rc));
+		mcap_notify_error(mcl, gerr);
+		g_error_free(gerr);
+
+		g_free(mcl->lcmd);
+		mcl->lcmd = NULL;
+		mcl->ctrl &= ~MCAP_CTRL_STD_OP;
+		mcl->req = MCL_AVAILABLE;
+		update_mcl_state(mcl);
+		return FALSE;
+	}
+
+	if (rsp->rc == MCAP_UNESPECIFIED_ERROR)
+		goto close_mcl;
+
+	return TRUE;
+close_mcl:
+	if (rsp->rc == MCAP_UNESPECIFIED_ERROR)
+		g_set_error(&gerr, MCAP_ERROR, MCAP_ERROR_UNESPECIFIED_ERROR,
+						"%s", error2str(rsp->rc));
+	else
+		g_set_error(&gerr, MCAP_ERROR, MCAP_ERROR_FAILED,
+						"Protocol error");
+	mcap_notify_error(mcl, gerr);
+	g_error_free(gerr);
+	mcl->ms->mcl_disconnected_cb(mcl, mcl->ms->user_data);
+	mcap_park_mcl(mcl);
+	return FALSE;
+}
+
+static void proc_response(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len)
+{
+	gboolean close;
+	RELEASE_TIMER(mcl);
+
+	if (!check_rsp(mcl, cmd, len))
+		return;
+
+	switch (cmd[0]) {
+	case MCAP_ERROR_RSP:
+		error("received MCAP_ERROR_RSP:%d",MCAP_ERROR_RSP);
+		close = TRUE;
+		break;
+	case MCAP_MD_CREATE_MDL_RSP:
+		close = process_md_create_mdl_rsp(mcl, cmd, len);
+		break;
+	case MCAP_MD_RECONNECT_MDL_RSP:
+		close = process_md_reconnect_mdl_rsp(mcl, cmd, len);
+		break;
+	case MCAP_MD_ABORT_MDL_RSP:
+		close = process_md_abort_mdl_rsp(mcl, cmd, len);
+		break;
+	case MCAP_MD_DELETE_MDL_RSP:
+		debug("received MCAP_MD_DELETE_MDL_RSP");
+		close = process_md_delete_mdl_rsp(mcl, cmd, len);
+		break;
+	default:
+		debug("Unknown cmd response received (op code = %d)",cmd[0]);
+		close = TRUE;
+		break;
+	}
+
+	if (close) {
+		mcl->ms->mcl_disconnected_cb(mcl, mcl->ms->user_data);
+		mcap_park_mcl(mcl);
+	}
+}
+
+static void rsend_req(struct mcap_mcl *mcl)
+{
+	uint8_t *cmd = mcl->lcmd;
+	int len;
+
+	if(!cmd)
+		return;
+
+	switch (cmd[0]) {
+	case MCAP_MD_RECONNECT_MDL_REQ:
+	case MCAP_MD_ABORT_MDL_REQ:
+	case MCAP_MD_DELETE_MDL_REQ:
+		len = 3;
+		break;
+	case MCAP_MD_CREATE_MDL_REQ:
+		len = 5;
+		break;
+	default:
+		return;
+	}
+
+	mcap_send_data(g_io_channel_unix_get_fd(mcl->cc), cmd, len);
+}
+
+static void proc_cmd(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len)
+{
+	if ((cmd[0] >= MCAP_MD_SYNC_CAP_REQ) && (cmd[0] <= MCAP_MD_SYNC_INFO_IND)) {
+		proc_sync_cmd(mcl, cmd, len);
+		return;
+	}
+
+	if (!(mcl->ctrl & MCAP_CTRL_STD_OP)) {
+		/* In case the remote device doesn't work corerctly */
+		error("Remote device does not support opcodes, cmd ignored");
+		return;
+	}
+
+	if (mcl->req == MCL_WAITING_RSP) {
+		if (cmd[0] & 0x01) {
+			/* Request arrived when a response is expected */
+			if (mcl->role == MCL_INITIATOR)
+				/* ignore */
+				return;
+			proc_req[mcl->state](mcl, cmd, len);
+			/* Initiator will ignore our last request => re-send */
+			rsend_req(mcl);
+			return;
+		}
+		proc_response(mcl, cmd, len);
+	} else if (cmd[0] & 0x01)
+		proc_req[mcl->state](mcl, cmd, len);
+}
+
+static gboolean mdl_event_cb(GIOChannel *chan, GIOCondition cond, gpointer data)
+{
+
+	struct mcap_mdl *mdl = data;
+	gboolean notify;
+
+	debug("Close MDL %d", mdl->mdlid);
+
+	notify = (mdl->state == MDL_CONNECTED);
+	shutdown_mdl(mdl);
+
+	update_mcl_state(mdl->mcl);
+
+	if (notify)
+		/*Callback to upper layer */
+		mdl->mcl->cb->mdl_closed(mdl, mdl->mcl->cb->user_data);
+
+	return FALSE;
+}
+
+static void mcap_connect_mdl_cb(GIOChannel *chan, GError *conn_err,
+							gpointer data)
+{
+	struct mcap_mdl_op_cb *con = data;
+	struct mcap_mdl *mdl = con->mdl;
+	mcap_mdl_operation_cb cb = con->cb.op;
+	gpointer user_data = con->user_data;
+
+	g_free(con);
+	debug("mdl connect callback");
+
+	if (conn_err) {
+		debug("ERROR: mdl connect callback");
+		mdl->state = MDL_CLOSED;
+		g_io_channel_unref(mdl->dc);
+		mdl->dc = NULL;
+		cb(mdl, conn_err, user_data);
+		return;
+	}
+
+	mdl->state = MDL_CONNECTED;
+	mdl->wid = g_io_add_watch(mdl->dc, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+				(GIOFunc) mdl_event_cb, mdl);
+
+	cb(mdl, conn_err, user_data);
+}
+
+void mcap_mdl_connect(struct mcap_mdl *mdl, BtIOType BtType, uint16_t dcpsm,
+	GError **err, mcap_mdl_operation_cb connect_cb, gpointer user_data)
+{
+	struct mcap_mdl_op_cb *con;
+
+	if (mdl->state != MDL_WAITING) {
+		g_set_error(err, MCAP_ERROR, MCAP_ERROR_INVALID_MDL,
+					"%s", error2str(MCAP_INVALID_MDL));
+		return;
+	}
+
+	con = g_new0(struct mcap_mdl_op_cb, 1);
+	con->mdl = mdl;
+	con->cb.op = connect_cb;
+	con->user_data = user_data;
+
+	/* TODO: Check if BtIOType is ERTM or Streaming before continue */
+
+	mdl->dc = bt_io_connect(BtType, mcap_connect_mdl_cb, con,
+				NULL, err,
+				BT_IO_OPT_SOURCE_BDADDR, &mdl->mcl->ms->src,
+				BT_IO_OPT_DEST_BDADDR, &mdl->mcl->addr,
+				BT_IO_OPT_PSM, dcpsm,
+				BT_IO_OPT_MTU, MCAP_DC_MTU,
+				BT_IO_OPT_SEC_LEVEL, mdl->mcl->ms->sec,
+				BT_IO_OPT_INVALID);
+	if (*err) {
+		debug("MDL Connection error");
+		mdl->state = MDL_CLOSED;
+		g_free(con);
+	}
+}
+
+static gboolean mcl_control_cb(GIOChannel *chan, GIOCondition cond, gpointer data)
+{
+
+	struct mcap_mcl *mcl = data;
+	int sk, len;
+	uint8_t buf[MCAP_CC_MTU];
+
+	if (cond & G_IO_NVAL) {
+		error("Revise G_IO_NVAL");
+		goto fail;
+	}
+
+	if (cond & (G_IO_ERR | G_IO_HUP))
+		goto fail;
+
+	sk = g_io_channel_unix_get_fd(chan);
+	len = recv(sk, buf, sizeof(buf), 0);
+	if (len < 0)
+		goto fail;
+
+	proc_cmd(mcl, buf, (uint32_t)len);
+	return TRUE;
+fail:
+	if (mcl->state != MCL_IDLE)
+		mcl->ms->mcl_disconnected_cb(mcl, mcl->ms->user_data);
+	mcap_park_mcl(mcl);
+	return FALSE;
+}
+
+static void mcap_connect_mcl_cb(GIOChannel *chan, GError *conn_err,
+							gpointer user_data)
+{
+	char dstaddr[18];
+	struct connect_mcl *con = user_data;
+	struct mcap_mcl *aux, *mcl = con->mcl;
+	mcap_mcl_connect_cb connect_cb = con->connect_cb;
+	gpointer data = con->user_data;
+	GError *gerr = NULL;
+
+	g_free(con);
+
+	mcl->ctrl &= ~MCAP_CTRL_CONN;
+
+	if (conn_err) {
+		if (mcl->ctrl & MCAP_CTRL_FREE)
+			mcl->ms->mcl_uncached_cb(mcl, mcl->ms->user_data);
+		mcap_mcl_check_del(mcl);
+		connect_cb(NULL, conn_err, data);
+		return;
+	}
+
+	ba2str(&mcl->addr, dstaddr);
+
+	aux = find_mcl(mcl->ms->mcls, &mcl->addr);
+	if (aux) {
+		/* Double MCL connection case */
+		if (aux != mcl) {
+			/* This MCL was not in cache */
+			mcap_mcl_unref(mcl);
+		}
+		error("MCL error: Device %s is already connected", dstaddr);
+		g_set_error(&gerr, MCAP_ERROR, MCAP_ERROR_ALREADY_EXISTS,
+				"MCL %s is already connected", dstaddr);
+		connect_cb(NULL, gerr, data);
+		g_error_free(gerr);
+		return;
+	}
+
+	mcl->state = MCL_CONNECTED;
+	mcl->role = MCL_INITIATOR;
+	mcl->req = MCL_AVAILABLE;
+	mcl->ctrl |= MCAP_CTRL_STD_OP;
+
+	if (mcl->ctrl & MCAP_CTRL_CACHED)
+		mcap_unpark_mcl(mcl);
+	else
+		mcl->ms->mcls = g_slist_prepend(mcl->ms->mcls, mcl);
+
+	mcl->wid = g_io_add_watch(mcl->cc, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+				(GIOFunc) mcl_control_cb, mcl);
+	connect_cb(mcl, gerr, data);
+
+	if (mcl->ref == 1) {
+		mcl->ms->mcls = g_slist_remove(mcl->ms->mcls, mcl);
+		mcap_mcl_unref(mcl);
+	}
+}
+
+void mcap_create_mcl(struct mcap_session *ms,
+				const bdaddr_t *addr,
+				uint16_t ccpsm,
+				GError **err,
+				mcap_mcl_connect_cb connect_cb,
+				gpointer user_data)
+{
+	struct mcap_mcl *mcl;
+	struct connect_mcl *con;
+
+	mcl = find_mcl(ms->mcls, addr);
+	if (mcl) {
+		g_set_error(err, MCAP_ERROR, MCAP_ERROR_ALREADY_EXISTS,
+					"MCL is already connected.");
+		return;
+	}
+
+	mcl = find_mcl(ms->cached, addr);
+	if (!mcl) {
+		mcl = g_new0(struct mcap_mcl, 1);
+		mcl->ms = ms;
+		mcl->state = MCL_IDLE;
+		bacpy(&mcl->addr, addr);
+		set_default_cb(mcl);
+		mcl = mcap_mcl_ref(mcl);
+	} else
+		mcl->ctrl |= MCAP_CTRL_CONN;
+
+	con = g_new0(struct connect_mcl, 1);
+	con->mcl = mcl;
+	con->connect_cb = connect_cb;
+	con->user_data = user_data;
+
+	mcl->cc = bt_io_connect(BT_IO_L2CAP, mcap_connect_mcl_cb, con,
+				NULL, err,
+				BT_IO_OPT_SOURCE_BDADDR, &ms->src,
+				BT_IO_OPT_DEST_BDADDR, addr,
+				BT_IO_OPT_PSM, ccpsm,
+				BT_IO_OPT_MTU, MCAP_CC_MTU,
+				BT_IO_OPT_SEC_LEVEL, ms->sec,
+				BT_IO_OPT_INVALID);
+	if (*err) {
+		g_free(con);
+		mcl->ctrl &= ~MCAP_CTRL_CONN;
+		if (mcl->ctrl & MCAP_CTRL_FREE)
+			mcl->ms->mcl_uncached_cb(mcl, mcl->ms->user_data);
+		mcap_mcl_check_del(mcl);
+	}
+}
+
+static void connect_dc_event_cb(GIOChannel *chan, GError *err, gpointer user_data)
+{
+	struct mcap_mdl *mdl = user_data;
+	struct mcap_mcl *mcl = mdl->mcl;
+
+	mdl->state = MDL_CONNECTED;
+	mdl->dc = g_io_channel_ref(chan);
+	mdl->wid = g_io_add_watch(mdl->dc, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+						(GIOFunc) mdl_event_cb, mdl);
+
+	mcl->state = MCL_ACTIVE;
+	mcl->cb->mdl_connected(mdl, mcl->cb->user_data);
+}
+
+static void confirm_dc_event_cb(GIOChannel *chan, gpointer user_data)
+{
+	struct mcap_session *ms = user_data;
+	struct mcap_mcl *mcl;
+	struct mcap_mdl *mdl;
+	GError *err = NULL;
+	bdaddr_t dst;
+	GSList *l;
+
+	bt_io_get(chan, BT_IO_L2CAP, &err,
+			BT_IO_OPT_DEST_BDADDR, &dst,
+			BT_IO_OPT_INVALID);
+	if (err) {
+		error("%s", err->message);
+		g_error_free(err);
+		goto drop;
+	}
+
+	mcl = find_mcl(ms->mcls, &dst);
+	if (!mcl || (mcl->state != MCL_PENDING))
+		goto drop;
+
+	for (l = mcl->mdls; l; l = l->next) {
+		mdl = l->data;
+		if (mdl->state == MDL_WAITING) {
+			if (!bt_io_accept(chan, connect_dc_event_cb, mdl, NULL,
+									&err)) {
+				error("MDL accept error %s", err->message);
+				g_error_free(err);
+				goto drop;
+			}
+			return;
+		}
+	}
+drop:
+	g_io_channel_shutdown(chan, TRUE, NULL);
+}
+
+static void connect_mcl_event_cb(GIOChannel *chan, GError *err, gpointer user_data)
+{
+	struct mcap_mcl *mcl = user_data;
+	gboolean reconn;
+
+	if (err) {
+		mcap_mcl_check_del(mcl);
+		return;
+	}
+
+	mcl->state = MCL_CONNECTED;
+	mcl->role = MCL_ACCEPTOR;
+	mcl->req = MCL_AVAILABLE;
+	mcl->cc = g_io_channel_ref(chan);
+	mcl->ctrl |= MCAP_CTRL_STD_OP;
+
+	reconn = (mcl->ctrl & MCAP_CTRL_CACHED);
+	if (reconn)
+		mcap_unpark_mcl(mcl);
+	else
+		mcl->ms->mcls = g_slist_prepend(mcl->ms->mcls, mcl);
+
+	mcl->wid = g_io_add_watch(mcl->cc,
+			G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+			(GIOFunc) mcl_control_cb, mcl);
+
+	/* Callback to report new MCL */
+	if (reconn)
+		mcl->ms->mcl_reconnected_cb(mcl, mcl->ms->user_data);
+	else
+		mcl->ms->mcl_connected_cb(mcl, mcl->ms->user_data);
+
+	if (mcl->ref == 1) {
+		mcl->ms->mcls = g_slist_remove(mcl->ms->mcls, mcl);
+		mcap_mcl_unref(mcl);
+	}
+}
+
+static void confirm_mcl_event_cb(GIOChannel *chan, gpointer user_data)
+{
+	struct mcap_session *ms = user_data;
+	struct mcap_mcl *mcl;
+	bdaddr_t dst;
+	char address[18], srcstr[18];
+	GError *err = NULL;
+
+	bt_io_get(chan, BT_IO_L2CAP, &err,
+			BT_IO_OPT_DEST_BDADDR, &dst,
+			BT_IO_OPT_DEST, address,
+			BT_IO_OPT_INVALID);
+	if (err) {
+		error("%s", err->message);
+		g_error_free(err);
+		goto drop;
+	}
+
+	ba2str(&ms->src, srcstr);
+	mcl = find_mcl(ms->mcls, &dst);
+	if (mcl) {
+		error("Control channel already created with %s on adapter %s",
+				address, srcstr);
+		goto drop;
+	}
+
+	mcl = find_mcl(ms->cached, &dst);
+	if (!mcl) {
+		mcl = g_new0(struct mcap_mcl, 1);
+		mcl->ms = ms;
+		bacpy(&mcl->addr, &dst);
+		set_default_cb(mcl);
+		mcl = mcap_mcl_ref(mcl);
+	}
+
+	if (!bt_io_accept(chan, connect_mcl_event_cb, mcl, NULL, &err)) {
+		error("mcap accept error: %s", err->message);
+		if (!(mcl->ctrl & MCAP_CTRL_CACHED))
+			mcap_mcl_unref(mcl);
+		g_error_free(err);
+		goto drop;
+	}
+
+	return;
+drop:
+	g_io_channel_shutdown(chan, TRUE, NULL);
+}
+
+struct mcap_session *mcap_create_session(struct btd_adapter *btd_adapter,
+					BtIOSecLevel sec,
+					uint16_t ccpsm,
+					uint16_t dcpsm,
+					GError **gerr,
+					mcap_mcl_event_cb mcl_connected,
+					mcap_mcl_event_cb mcl_reconnected,
+					mcap_mcl_event_cb mcl_disconnected,
+					mcap_mcl_event_cb mcl_uncached,
+					gpointer user_data)
+{
+	struct mcap_session *ms;
+
+	if (sec < BT_IO_SEC_MEDIUM) {
+		g_set_error(gerr, MCAP_ERROR, MCAP_ERROR_INVALID_ARGS,
+				"Security level can't be minor of %d",
+				BT_IO_SEC_MEDIUM);
+		return NULL;
+	}
+
+	if (!(mcl_connected && mcl_reconnected &&
+			mcl_disconnected && mcl_uncached)) {
+		g_set_error(gerr, MCAP_ERROR, MCAP_ERROR_INVALID_ARGS,
+				"The callbacks can't be null");
+		return NULL;
+	}
+
+	ms = g_new0(struct mcap_session, 1);
+
+	adapter_get_address(btd_adapter, &ms->src);
+
+	ms->sec = sec;
+	ms->mcl_connected_cb = mcl_connected;
+	ms->mcl_reconnected_cb = mcl_reconnected;
+	ms->mcl_disconnected_cb = mcl_disconnected;
+	ms->mcl_uncached_cb = mcl_uncached;
+	ms->user_data = user_data;
+
+	/* Listen incoming connections in control channel */
+	ms->ccio = bt_io_listen(BT_IO_L2CAP, NULL, confirm_mcl_event_cb, ms,
+				NULL, gerr,
+				BT_IO_OPT_SOURCE_BDADDR, &ms->src,
+				BT_IO_OPT_PSM, ccpsm,
+				BT_IO_OPT_MTU, MCAP_CC_MTU,
+				BT_IO_OPT_SEC_LEVEL, sec,
+				BT_IO_OPT_INVALID);
+	if (!ms->ccio) {
+		error("%s", (*gerr)->message);
+		g_free(ms);
+		return NULL;
+	}
+
+	/* Listen incoming connections in data channels */
+	ms->dcio = bt_io_listen(BT_IO_L2CAP, NULL, confirm_dc_event_cb, ms,
+				NULL, gerr,
+				BT_IO_OPT_SOURCE_BDADDR, &ms->src,
+				BT_IO_OPT_PSM, dcpsm,
+				BT_IO_OPT_MTU, MCAP_DC_MTU,
+				BT_IO_OPT_SEC_LEVEL, sec,
+				BT_IO_OPT_INVALID);
+	if (!ms->dcio) {
+		g_io_channel_shutdown(ms->ccio, TRUE, NULL);
+		g_io_channel_unref(ms->ccio);
+		ms->ccio = NULL;
+		error("%s", (*gerr)->message);
+		g_free(ms);
+		return NULL;
+	}
+
+	return ms;
+}
+
+void mcap_close_session(struct mcap_session *ms)
+{
+	GSList *l;
+
+	if (!ms)
+		return;
+
+	if (ms->ccio) {
+		g_io_channel_shutdown(ms->ccio, TRUE, NULL);
+		g_io_channel_unref(ms->ccio);
+		ms->ccio = NULL;
+	}
+
+	if (ms->dcio) {
+		g_io_channel_shutdown(ms->dcio, TRUE, NULL);
+		g_io_channel_unref(ms->dcio);
+		ms->dcio = NULL;
+	}
+
+	for (l = ms->mcls; l; l = l->next) {
+		mcap_mcl_shutdown(l->data);
+		mcap_mcl_unref(l->data);
+	}
+	g_slist_free(ms->mcls);
+	ms->mcls = NULL;
+
+	for (l = ms->cached; l; l = l->next)
+		mcap_mcl_unref(l->data);
+	g_slist_free(ms->cached);
+	ms->cached = NULL;
+
+	g_free(ms);
+}
+
+uint16_t mcap_get_ctrl_psm(struct mcap_session *ms, GError **err)
+{
+	uint16_t lpsm;
+
+	if (!(ms && ms->ccio)) {
+		g_set_error(err, MCAP_ERROR, MCAP_ERROR_INVALID_ARGS,
+			"Invalid MCAP session");
+		return 0;
+	}
+
+	bt_io_get(ms->ccio, BT_IO_L2CAP, err,
+			BT_IO_OPT_PSM, &lpsm,
+			BT_IO_OPT_INVALID);
+	if (*err)
+		return 0;
+	return lpsm;
+}
+
+uint16_t mcap_get_data_psm(struct mcap_session *ms, GError **err)
+{
+	uint16_t lpsm;
+
+	if (!(ms && ms->dcio)) {
+		g_set_error(err, MCAP_ERROR, MCAP_ERROR_INVALID_ARGS,
+			"Invalid MCAP session");
+		return 0;
+	}
+
+	bt_io_get(ms->dcio, BT_IO_L2CAP, err,
+			BT_IO_OPT_PSM, &lpsm,
+			BT_IO_OPT_INVALID);
+	if (*err)
+		return 0;
+	return lpsm;
+}
diff --git a/mcap/mcap.h b/mcap/mcap.h
new file mode 100644
index 0000000..598e6ce
--- /dev/null
+++ b/mcap/mcap.h
@@ -0,0 +1,176 @@
+/*
+ *
+ *  MCAP for BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ *
+ *  Authors:
+ *  Santiago Carot-Nemesio <sancane at gmail.com>
+ *  Jose Antonio Santos-Cadenas <santoscadenas at gmail.com>
+ *
+ *  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 __MCAP_H
+#define __MCAP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define MCAP_VERSION	0x0100	/* current version 01.00 */
+
+/* bytes to get MCAP Supported Procedures */
+#define MCAP_SUP_PROC	0x06
+
+/* maximum transmission unit for channels */
+#define MCAP_CC_MTU	48
+#define MCAP_DC_MTU	L2CAP_DEFAULT_MTU
+
+
+/* MCAP Standard Op Codes */
+#define MCAP_ERROR_RSP			0x00
+#define MCAP_MD_CREATE_MDL_REQ		0x01
+#define MCAP_MD_CREATE_MDL_RSP		0x02
+#define MCAP_MD_RECONNECT_MDL_REQ	0x03
+#define MCAP_MD_RECONNECT_MDL_RSP	0x04
+#define MCAP_MD_ABORT_MDL_REQ		0x05
+#define MCAP_MD_ABORT_MDL_RSP		0x06
+#define MCAP_MD_DELETE_MDL_REQ		0x07
+#define MCAP_MD_DELETE_MDL_RSP		0x08
+/*RESERVED                              0x09*/
+/*RESERVED                              0x10*/
+
+/* MCAP Clock Sync Op Codes */
+#define MCAP_MD_SYNC_CAP_REQ		0x11
+#define MCAP_MD_SYNC_CAP_RSP		0x12
+#define MCAP_MD_SYNC_SET_REQ		0x13
+#define MCAP_MD_SYNC_SET_RSP            0x14
+#define MCAP_MD_SYNC_INFO_IND		0x15
+/*RESERVED				0x16*/
+/*RESERVED                              0x17*/
+/*RESERVED                              0x18*/
+/*RESERVED                              0x19*/
+/*RESERVED                              0x20*/
+
+/* MCAP Response codes */
+#define MCAP_SUCCESS			0x00
+#define MCAP_INVALID_OP_CODE		0x01
+#define MCAP_INVALID_PARAM_VALUE	0x02
+#define MCAP_INVALID_MDEP		0x03
+#define MCAP_MDEP_BUSY			0x04
+#define MCAP_INVALID_MDL		0x05
+#define MCAP_MDL_BUSY			0x06
+#define MCAP_INVALID_OPERATION		0x07
+#define MCAP_RESOURCE_UNAVAILABLE	0x08
+#define MCAP_UNESPECIFIED_ERROR		0x09
+#define MCAP_REQUEST_NOT_SUPPORTED	0x0A
+#define MCAP_CONFIGURATION_REJECTED	0x0B
+/*RESERVED				0x0C-0xFF*/
+
+
+/* MDL IDs */
+#define MCAP_MDLID_RESERVED		0x0000
+#define MCAP_MDLID_INITIAL		0x0001
+#define MCAP_MDLID_FINAL		0xFEFF
+/*RESERVED				0xFF00-0xFFFE*/
+#define MCAP_ALL_MDLIDS			0xFFFF
+
+/* MDEP IDs */
+#define MCAP_MDEPID_INITIAL		0x00
+#define MCAP_MDEPID_FINAL		0x7F
+/*RESERVED				0x80-0xFF*/
+
+
+/*
+ * MCAP Request Packet Format
+ */
+
+typedef struct {
+	uint8_t         op;
+        uint16_t	mdl;
+	uint8_t		mdep;
+	uint8_t		conf;
+} __attribute__ ((packed)) mcap_md_create_mdl_req;
+
+typedef struct {
+        uint8_t         op;
+        uint16_t        mdl;
+} __attribute__ ((packed)) mcap_md_req;
+
+
+/*
+ * MCAP Response Packet Format
+ */
+
+typedef struct {
+        uint8_t         op;
+        uint8_t         rc;
+	uint16_t        mdl;
+} __attribute__ ((packed)) mcap4B_rsp;
+
+typedef struct {
+        uint8_t         op;
+        uint8_t         rc;
+        uint16_t        mdl;
+        uint8_t         param;
+} __attribute__ ((packed)) mcap5B_rsp;
+
+
+/*
+ * MCAP Clock Synchronization Protocol
+ */
+typedef struct {
+        uint8_t         op;
+        uint16_t        timest;
+} __attribute__ ((packed)) mcap_md_sync_cap_req;
+
+typedef struct {
+        uint8_t         op;
+	uint8_t         rc;
+	uint8_t         btclock;
+        uint16_t        sltime;
+	uint16_t        timestnr;
+	uint16_t        timestna;
+} __attribute__ ((packed)) mcap_md_sync_cap_rsp;
+
+typedef struct {
+        uint8_t         op;
+	uint8_t         timestui;
+        uint32_t        btclock;
+	uint64_t        timestst;
+} __attribute__ ((packed)) mcap_md_sync_set_req;
+
+typedef struct {
+        uint8_t         op;
+	uint8_t         rc;
+        uint32_t        btclock;
+	uint64_t        timestst;
+	uint16_t        timestsa;
+} __attribute__ ((packed)) mcap_md_sync_set_rsp;
+
+typedef struct {
+        uint8_t         op;
+        uint32_t        btclock;
+	uint64_t        timestst;
+	uint16_t        timestsa;
+} __attribute__ ((packed)) mcap_md_sync_info_ind;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __MCAP_H */
diff --git a/mcap/mcap_lib.h b/mcap/mcap_lib.h
new file mode 100644
index 0000000..fd3eb74
--- /dev/null
+++ b/mcap/mcap_lib.h
@@ -0,0 +1,162 @@
+/*
+ *
+ *  MCAP for BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ *
+ *  Authors:
+ *  Santiago Carot-Nemesio <sancane at gmail.com>
+ *  Jose Antonio Santos-Cadenas <santoscadenas at gmail.com>
+ *
+ *  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 __MCAP_LIB_H
+#define __MCAP_LIB_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <bluetooth/l2cap.h>
+#include "btio.h"
+#include "mcap.h"
+
+typedef enum {
+/* MCAP Error Response Codes */
+	MCAP_ERROR_INVALID_OP_CODE = 1,
+	MCAP_ERROR_INVALID_PARAM_VALUE,
+	MCAP_ERROR_INVALID_MDEP,
+	MCAP_ERROR_MDEP_BUSY,
+	MCAP_ERROR_INVALID_MDL,
+	MCAP_ERROR_MDL_BUSY,
+	MCAP_ERROR_INVALID_OPERATION,
+	MCAP_ERROR_RESOURCE_UNAVAILABLE,
+	MCAP_ERROR_UNESPECIFIED_ERROR,
+	MCAP_ERROR_REQUEST_NOT_SUPPORTED,
+	MCAP_ERROR_CONFIGURATION_REJECTED,
+/* MCAP Internal Errors */
+	MCAP_ERROR_INVALID_ARGS,
+	MCAP_ERROR_ALREADY_EXISTS,
+	MCAP_ERROR_FAILED,
+} McapError;
+
+typedef enum {
+	MCAP_MDL_CB_INVALID,
+	MCAP_MDL_CB_CONNECTED,		/* mcap_mdl_event_cb */
+	MCAP_MDL_CB_CLOSED,		/* mcap_mdl_event_cb */
+	MCAP_MDL_CB_DELETED,		/* mcap_mdl_event_cb */
+	MCAP_MDL_CB_ABORTED,		/* mcap_mdl_event_cb */
+	MCAP_MDL_CB_REMOTE_CONN_REQ,	/* mcap_remote_mdl_conn_req_cb */
+	MCAP_MDL_CB_REMOTE_RECONN_REQ,	/* mcap_remote_mdl_reconn_req_cb */
+} McapMclCb;
+
+struct mcap_session;
+struct mcap_mcl;
+struct mcap_mdl;
+
+/************ Callbacks ************/
+
+/* mdl callbacks */
+
+typedef void (* mcap_mdl_event_cb) (struct mcap_mdl *mdl, gpointer data);
+typedef void (* mcap_mdl_operation_conf_cb) (struct mcap_mdl *mdl, uint8_t conf,
+						GError *err, gpointer data);
+typedef void (* mcap_mdl_operation_cb) (struct mcap_mdl *mdl, GError *err,
+						gpointer data);
+typedef void (* mcap_mdl_del_cb) (GError *err, gpointer data);
+
+/* Next function should return an MCAP appropiate response code */
+typedef uint8_t (* mcap_remote_mdl_conn_req_cb) (struct mcap_mcl *mcl,
+						uint8_t mdepid, uint16_t mdlid,
+						uint8_t *conf, gpointer data);
+typedef uint8_t (* mcap_remote_mdl_reconn_req_cb) (struct mcap_mdl *mdl,
+						gpointer data);
+
+/* mcl callbacks */
+
+typedef void (* mcap_mcl_event_cb) (struct mcap_mcl *mcl, gpointer data);
+typedef void (* mcap_mcl_connect_cb) (struct mcap_mcl *mcl, GError *err,
+								gpointer data);
+
+/************ Operations ************/
+
+/* Mdl operations*/
+
+void mcap_req_mdl_creation(struct mcap_mcl *mcl,
+				uint8_t mdepid,
+				uint8_t conf,
+				GError **err,
+				mcap_mdl_operation_conf_cb connect_cb,
+				gpointer user_data);
+void mcap_req_mdl_reconnect(struct mcap_mdl *mdl, GError **err,
+				mcap_mdl_operation_cb reconnect_cb,
+				gpointer user_data);
+void mcap_req_mdl_delete_all(struct mcap_mcl *mcl, GError **err,
+			mcap_mdl_del_cb delete_cb, gpointer user_data);
+void mcap_req_mdl_deletion(struct mcap_mdl *mdl, GError **err,
+			mcap_mdl_del_cb delete_cb, gpointer user_data);
+void mcap_mdl_connect(struct mcap_mdl *mdl,
+			BtIOType BtType,
+			uint16_t dcpsm,
+			GError **err,
+			mcap_mdl_operation_cb connect_cb,
+			gpointer user_data);
+void mcap_mdl_abort(struct mcap_mdl *mdl, GError **err,
+			mcap_mdl_del_cb abort_cb, gpointer user_data);
+
+int mcap_mdl_get_fd(struct mcap_mdl *mdl);
+uint16_t mcap_mdl_get_mdlid(struct mcap_mdl *mdl);
+
+/* Mcl operations*/
+
+void mcap_create_mcl(struct mcap_session *ms,
+				const bdaddr_t *addr,
+				uint16_t ccpsm,
+				GError **err,
+				mcap_mcl_connect_cb connect_cb,
+				gpointer user_data);
+void mcap_close_mcl(struct mcap_mcl *mcl, gboolean cache);
+void mcap_mcl_set_cb(struct mcap_mcl *mcl, GError **gerr,
+					gpointer user_data, McapMclCb cb1, ...);
+bdaddr_t mcap_mcl_get_addr(struct mcap_mcl *mcl);
+
+struct mcap_mcl *mcap_mcl_ref(struct mcap_mcl *mcl);
+void mcap_mcl_unref(struct mcap_mcl *mcl);
+
+/* MCAP main operations */
+
+struct mcap_session *mcap_create_session(struct btd_adapter *btd_adapter,
+					BtIOSecLevel sec, uint16_t ccpsm,
+					uint16_t dcpsm,
+					GError **gerr,
+					mcap_mcl_event_cb mcl_connected,
+					mcap_mcl_event_cb mcl_reconnected,
+					mcap_mcl_event_cb mcl_disconnected,
+					mcap_mcl_event_cb mcl_uncached,
+					gpointer user_data);
+
+void mcap_close_session(struct mcap_session *ms);
+
+uint16_t mcap_get_ctrl_psm(struct mcap_session *ms, GError **err);
+uint16_t mcap_get_data_psm(struct mcap_session *ms, GError **err);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __MCAP_LIB_H */
+
-- 
1.6.3.3


^ permalink raw reply related

* Re: HDP proposed API
From: Luiz Augusto von Dentz @ 2010-05-04 10:04 UTC (permalink / raw)
  To: Santiago Carot-Nemesio; +Cc: linux-bluetooth@vger.kernel.org
In-Reply-To: <1272959741.2182.123.camel@mosquito>

Hi,

On Tue, May 4, 2010 at 10:55 AM, Santiago Carot-Nemesio
<scarot@libresoft.es> wrote:
> Hello,
> Next is the API wich we have been working last weeks.
>
> Regards.
>
>
> BlueZ D-Bus HDP API description
> ***********************************
>
> Authors:
> Santiago Carot-Nemesio          sancane@gmail.com
> Jose Antonio Santos-Cadenas     santoscadenas@gmail.com
>
>
> Health Device Profile hierarchy
> ===============================
>
> Service         org.bluez
> Interface       org.bluez.Hdp
> Object path     [variable prefix]/{hci0,hci1,...}
>
> Methods         object CreateSession(object path, byte data_spec)
>
>                        Returns the object path for the new HDP session.
>                        The path parameter is the path of the remote object
>                        with the callbacks to nofity events (see
>                        org.bluez.HdpAgent at the end of this document)
>                        The data_spec is the data exchange specficication
>                        (see section 5.2.10 of the specification document)
>
>                        Possible errors: org.bluez.Error.InvalidArguments
>
>                void DeleteSession(object path)
>
>                        Delete the HDP session identified by the object path.
>
>                        Possible errors: org.bluez.Error.InvalidArguments
>                                         org.bluez.Error.NotFound
>
>
> --------------------------------------------------------------------------------
>
> Service         org.bluez
> Interface       org.bluez.HdpSession
> Object path     [variable prefix]/{hci0,hci1,...}/{hdp0,hdp1,...}
>
>                uint8 AllocateMdep(uint8 role)
>
>                        This method allocates a logical end point. Valids values
>                        for MDEP roles are:
>                                * 0x00 For source
>                                * 0x01 For sink
>
>                        The return value is the allocated MDEP.
>
>                void AddFeature(uint8 mdepid, uint16 dtype, string dscr)
>
>                        Add a new entry in the HDP supported feature list with
>                        the role given in the MDEP id.
>
>                        If the new feature has the same data type as other
>                        previusly added feature for the same MDEP id, the old
>                        feature will be overwritten.
>
>                void Start()
>
>                        Starts the associated MCAP layer and set up the SDP
>                        registry with all supported features.
>
>                        Possible errors: org.bluez.Error.HdpError
>
>                object Connect(string btaddr)
>
>                        Connect with the device and returns the object path of
>                        the remote device.
>                        To call this method, the session must be started.
>
>                        Possible errors: org.bluez.Error.InvalidArguments
>                                         org.bluez.Error.HdpError
>
>                void Disconnect(object device, boolean delete)
>
>                        Disconnect from the remote device. If delete is true, any
>                        status will also be deleted. Otherwise, the status will
>                        be keeped for allowing future reconnections.
>                        To call this method, the session must be started.
>
>                        Possible errors: org.bluez.Error.InvalidArguments
>                                         org.bluez.Error.NotFound
>                                         org.bluez.Error.HdpError
>
> --------------------------------------------------------------------------------
>
> Service         org.bluez
> Interface       org.bluez.HdpDevice
> Object path     [variable prefix]/{hci0,hci1,...}/{hdp0,hdp1,...}/dev_XX_XX_XX_XX_XX_XX
>
>                boolean Echo(array{byte})
>
>                        Sends an echo petition to the remote device. Return True
>                        if response matches with the buffer sended. If some error
>                        is detected False value is returned and the associated
>                        MCL is closed.
>
>                uint16 OpenDc(byte mdepid, byte config)
>
>                        Creates a new data channel with the indicated config
>                        to the remote MCAP Data End Point (MDEP).
>                        The configuration should indicate the channel quality of
>                        service.
>                        Returns the data channel id.
>
>                        Possible errors: org.bluez.Error.InvalidArguments
>                                         org.bluez.Error.HdpError
>
>                file_descriptor GetDcFd(uint16 mdlid)
>
>                        Gets a file descriptor where data can be readed or
>                        writed for receive or sent by the data channel.
>                        Returns the file descriptor
>
>                        Possible errors: org.bluez.Error.InvalidArguments
>                                         org.bluez.Error.NotFound
>                                         org.bluez.Error.HdpError
>
>                void DeleteDc(uint16 mdlid)
>
>                        Deletes a data channel so it will not be available for
>                        use.
>
>                        Possible errors: org.bluez.Error.InvalidArguments
>                                         org.bluez.Error.NotFound
>                                         org.bluez.Error.HdpError
>
>                void DeleteAllDc()
>
>                        Deletes all data channels so it will not be available
>                        for use. Typically this function is called when the
>                        connection with the remote device will be closed
>                        permanently
>
>                        Possible errors: org.bluez.Error.HdpError
>
>                uint16 FirstRelDc()
>
>                        Returns the identificator of the first reliable data
>                        channel connected for this device.
>
>                        Possible errors: org.bluez.Error.HdpError
>
> HDPAgent hierarchy
> ==================
>
> Service         unique name
> Interface       org.bluez.HdpAgent
> Object path     freely definable
>
>                void DeviceConnected(object path)
>
>                        This method is called whenever a new device connection
>                        has been established over the control channel of the
>                        current HDP session. The objectpath contains the object
>                        path of the remote device.
>
>                void DeviceDisconnected(object path)
>
>                        This method is called when a remote device is
>                        disconnected definitively. Any future reconnections
>                        will fail. Also all data channels associated to this
>                        device will be closed.
>
>                void CreatedDc(object path, uint16 mdlid, filedescriptor fd)
>
>                        This method is called when a new data channel is created
>                        The path contains the object path of the device whith
>                        the new connection is created, the mdlid the data
>                        channel identificator and the fd is the file descriptor
>                        where the data can be readed or writed.
>
>                void DeletedDc(object path, uint16 mdlid)
>
>                        This method is called when a data channel is closed.
>                        After this call the data channel will not be valid and
>                        can be reused for future created data channels.
>
>                void DataReady(object path, uint16 mdlid, filedescriptor fd)
>
>                        This method is called when there is new data that can be
>                        readed in a data channel
>
> --
> 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
>

This sounds a little too complicated for me, my suggestion for you
guys is to first discuss a python script for the supported use cases
to figure out what is the best approach here, a tool written in C
would be nice too, so you will notice how much logic will got
duplicate if someone has to do the same thing as the script does. Also
I must say that this API goes against the design of our current 4.x
API, since it creates its own device representation.


-- 
Luiz Augusto von Dentz
Computer Engineer

^ permalink raw reply

* Re: HDP proposed API
From: José Antonio Santos Cadenas @ 2010-05-04 10:13 UTC (permalink / raw)
  To: Luiz Augusto von Dentz
  Cc: Santiago Carot-Nemesio, linux-bluetooth@vger.kernel.org
In-Reply-To: <m2u2d5a2c101005040304ke238595n54b8584dd78b8f37@mail.gmail.com>

Hi,

El Tuesday 04 May 2010 12:04:31 Luiz Augusto von Dentz escribió:
> Hi,
> 
> On Tue, May 4, 2010 at 10:55 AM, Santiago Carot-Nemesio
> 
> <scarot@libresoft.es> wrote:
> > Hello,
> > Next is the API wich we have been working last weeks.
> > 
> > Regards.
> > 
> > 
> > BlueZ D-Bus HDP API description
> > ***********************************
> > 
> > Authors:
> > Santiago Carot-Nemesio          sancane@gmail.com
> > Jose Antonio Santos-Cadenas     santoscadenas@gmail.com
> > 
> > 
> > Health Device Profile hierarchy
> > ===============================
> > 
> > Service         org.bluez
> > Interface       org.bluez.Hdp
> > Object path     [variable prefix]/{hci0,hci1,...}
> > 
> > Methods         object CreateSession(object path, byte data_spec)
> > 
> >                        Returns the object path for the new HDP session.
> >                        The path parameter is the path of the remote
> > object with the callbacks to nofity events (see
> >                        org.bluez.HdpAgent at the end of this document)
> >                        The data_spec is the data exchange specficication
> >                        (see section 5.2.10 of the specification document)
> > 
> >                        Possible errors: org.bluez.Error.InvalidArguments
> > 
> >                void DeleteSession(object path)
> > 
> >                        Delete the HDP session identified by the object
> > path.
> > 
> >                        Possible errors: org.bluez.Error.InvalidArguments
> >                                         org.bluez.Error.NotFound
> > 
> > 
> > -------------------------------------------------------------------------
> > -------
> > 
> > Service         org.bluez
> > Interface       org.bluez.HdpSession
> > Object path     [variable prefix]/{hci0,hci1,...}/{hdp0,hdp1,...}
> > 
> >                uint8 AllocateMdep(uint8 role)
> > 
> >                        This method allocates a logical end point. Valids
> > values for MDEP roles are:
> >                                * 0x00 For source
> >                                * 0x01 For sink
> > 
> >                        The return value is the allocated MDEP.
> > 
> >                void AddFeature(uint8 mdepid, uint16 dtype, string dscr)
> > 
> >                        Add a new entry in the HDP supported feature list
> > with the role given in the MDEP id.
> > 
> >                        If the new feature has the same data type as other
> >                        previusly added feature for the same MDEP id, the
> > old feature will be overwritten.
> > 
> >                void Start()
> > 
> >                        Starts the associated MCAP layer and set up the
> > SDP registry with all supported features.
> > 
> >                        Possible errors: org.bluez.Error.HdpError
> > 
> >                object Connect(string btaddr)
> > 
> >                        Connect with the device and returns the object
> > path of the remote device.
> >                        To call this method, the session must be started.
> > 
> >                        Possible errors: org.bluez.Error.InvalidArguments
> >                                         org.bluez.Error.HdpError
> > 
> >                void Disconnect(object device, boolean delete)
> > 
> >                        Disconnect from the remote device. If delete is
> > true, any status will also be deleted. Otherwise, the status will be
> > keeped for allowing future reconnections. To call this method, the
> > session must be started.
> > 
> >                        Possible errors: org.bluez.Error.InvalidArguments
> >                                         org.bluez.Error.NotFound
> >                                         org.bluez.Error.HdpError
> > 
> > -------------------------------------------------------------------------
> > -------
> > 
> > Service         org.bluez
> > Interface       org.bluez.HdpDevice
> > Object path     [variable
> > prefix]/{hci0,hci1,...}/{hdp0,hdp1,...}/dev_XX_XX_XX_XX_XX_XX
> > 
> >                boolean Echo(array{byte})
> > 
> >                        Sends an echo petition to the remote device.
> > Return True if response matches with the buffer sended. If some error is
> > detected False value is returned and the associated MCL is closed.
> > 
> >                uint16 OpenDc(byte mdepid, byte config)
> > 
> >                        Creates a new data channel with the indicated
> > config to the remote MCAP Data End Point (MDEP). The configuration
> > should indicate the channel quality of service.
> >                        Returns the data channel id.
> > 
> >                        Possible errors: org.bluez.Error.InvalidArguments
> >                                         org.bluez.Error.HdpError
> > 
> >                file_descriptor GetDcFd(uint16 mdlid)
> > 
> >                        Gets a file descriptor where data can be readed or
> >                        writed for receive or sent by the data channel.
> >                        Returns the file descriptor
> > 
> >                        Possible errors: org.bluez.Error.InvalidArguments
> >                                         org.bluez.Error.NotFound
> >                                         org.bluez.Error.HdpError
> > 
> >                void DeleteDc(uint16 mdlid)
> > 
> >                        Deletes a data channel so it will not be available
> > for use.
> > 
> >                        Possible errors: org.bluez.Error.InvalidArguments
> >                                         org.bluez.Error.NotFound
> >                                         org.bluez.Error.HdpError
> > 
> >                void DeleteAllDc()
> > 
> >                        Deletes all data channels so it will not be
> > available for use. Typically this function is called when the connection
> > with the remote device will be closed permanently
> > 
> >                        Possible errors: org.bluez.Error.HdpError
> > 
> >                uint16 FirstRelDc()
> > 
> >                        Returns the identificator of the first reliable
> > data channel connected for this device.
> > 
> >                        Possible errors: org.bluez.Error.HdpError
> > 
> > HDPAgent hierarchy
> > ==================
> > 
> > Service         unique name
> > Interface       org.bluez.HdpAgent
> > Object path     freely definable
> > 
> >                void DeviceConnected(object path)
> > 
> >                        This method is called whenever a new device
> > connection has been established over the control channel of the current
> > HDP session. The objectpath contains the object path of the remote
> > device.
> > 
> >                void DeviceDisconnected(object path)
> > 
> >                        This method is called when a remote device is
> >                        disconnected definitively. Any future
> > reconnections will fail. Also all data channels associated to this
> > device will be closed.
> > 
> >                void CreatedDc(object path, uint16 mdlid, filedescriptor
> > fd)
> > 
> >                        This method is called when a new data channel is
> > created The path contains the object path of the device whith the new
> > connection is created, the mdlid the data channel identificator and the
> > fd is the file descriptor where the data can be readed or writed.
> > 
> >                void DeletedDc(object path, uint16 mdlid)
> > 
> >                        This method is called when a data channel is
> > closed. After this call the data channel will not be valid and can be
> > reused for future created data channels.
> > 
> >                void DataReady(object path, uint16 mdlid, filedescriptor
> > fd)
> > 
> >                        This method is called when there is new data that
> > can be readed in a data channel
> > 
> > --
> > 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
> 
> This sounds a little too complicated for me, my suggestion for you
> guys is to first discuss a python script for the supported use cases
> to figure out what is the best approach here, 

We will prepare a program that shows this usage, we are going to send it in a 
few hours. It is normal not to understand this the first time because HDP is 
quite estrange.

> a tool written in C
> would be nice too, so you will notice how much logic will got
> duplicate if someone has to do the same thing as the script does. Also
> I must say that this API goes against the design of our current 4.x
> API, since it creates its own device representation.

We know, but since HDP devices are associated to a HDP session it is necessary 
to create a new type (maybe the name choosed is not a good idea :), we try to 
create and object that handles an mcl, a connection between two HDP sessions 
in different devices)

Regards.

^ permalink raw reply

* Re: HDP proposed API
From: José Antonio Santos Cadenas @ 2010-05-04 10:54 UTC (permalink / raw)
  To: Luiz Augusto von Dentz
  Cc: Santiago Carot-Nemesio, linux-bluetooth@vger.kernel.org
In-Reply-To: <m2u2d5a2c101005040304ke238595n54b8584dd78b8f37@mail.gmail.com>

Hi all,

Next a simple example program in python using the api that we proposed in this thread.

We still use the name Device but maybe a better name can be used.

Regards.

----------
#!/usr/bin/python
# -*- coding: utf-8 -*-

import dbus
import dbus.service
from dbus.mainloop.glib import DBusGMainLoop
import gobject
import os

DBusGMainLoop(set_as_default=True)
loop = gobject.MainLoop()

obj_path = "/python/tester/hdp"

bus = dbus.SystemBus()

# This object receives the information from the daemon
class HDP(dbus.service.Object):
	def __init__(self, bus, obj_path):
		self.bus = bus
		dbus.service.Object.__init__(self, self.bus, obj_path)
	@dbus.service.method('org.bluez.HdpAgent',
			     in_signature='o', out_signature='',
			     sender_keyword='sender')
	def DeviceConnected(self, path, sender=None):
		print "Connected new device %s: (sender %s)" % (path, sender)
	@dbus.service.method('org.bluez.HdpAgent',
			     in_signature='o', out_signature='',
			     sender_keyword='sender')
	def DeviceDisconnected(self, path, sender=None):
		print "Device %s: disconnected (sender: %s)" % (path, sender)
	@dbus.service.method('org.bluez.HdpAgent',
			     in_signature='oq', out_signature='',
			     sender_keyword='sender')
	def DeletedDc(self, dev, mdlid, sender=None):
		print "Data channel: %d deleted on dev %s (sender %s)" % \
						(mdlid, dev, sender)
	@dbus.service.method('org.bluez.HdpAgent',
			     in_signature='oqh', out_signature='',
			     sender_keyword='sender')
	def CreatedDc(self, dev, mdlid, fd, sender=None):
		print "Data channel: %d created on dev %s: fd: %d " % \
					        (mdlid, dev, fd, sender)

hdp_obj = HDP(bus, obj_path);

manager = dbus.Interface(bus.get_object("org.bluez", "/"),
						"org.bluez.Manager")
hdp = dbus.Interface(bus.get_object("org.bluez", manager.DefaultAdapter()),
						"org.bluez.Hdp")

data_spec = 1
session_path = hdp.CreateSession(obj_path, data_spec)

session = dbus.Interface(bus.get_object("org.bluez", session_path),
						"org.bluez.HdpSession")

# Configuration of the session, adding suported features
source_role = 0 
mdep = session.AllocateMdep(source_role)
session.AddFeature(mdep, 4100, "Pulse oximeter")

sink_role = 1 # Sink role
mdep2 = session.AllocateMdep(sink_role)
session.AddFeature(mdep2, 4111, "Scale")
session.AddFeature(mdep2, 4100, "Pulse oximeter sink")

# Now start the session
# Starts a MCAP session and register in the SDP
session.Start()

# *****************************************************************************
# Now, if you start the main loop, the HDP events will be notified in HDP class
# *****************************************************************************

# Connect to a remote device
addr = "XX:XX:XX:XX:XX:XX"
dev_path = session.Connect(addr)
dev = dbus.Interface(bus.get_object("org.bluez", dev_path),
						"org.bluez.HDPDevice")

dcid = dev.OpenDc(rem_mdep, conf)
fd = dev.GetDcFd(dcid)

# You can use the fd for write or read data.
# If the read/write operation fails, reconnection will be performed getting
# the file descriptor again

while some_condition:
	try:
		os.write(fd, "some_data")
	except:
		fd = dev.GetDcFd(dcid) # Implicit reconeciton of dc

dev.DeleteDc(dcid) # Close data channel
dev.DeleteAllDc() # Close all data channels for this mcl (device+session)

if power_save:
	session.Disconnect(dev_path, False)
	# Close the connection with the remote devide for power save,
	# Reconnections will be possible
else:
	session.Disconnect(dev_path, True)
	# Close the connection with the remote devide forever and clean state
	# Reconnections will NOT be possible

hdp.DeleteSession(session_path)
# Close the session, all connections and data channels will be closed

^ 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