* RE: [RFC] BlueZ D-Bus Sim Access Profile Server API description
From: Nicolas GUILBAUD @ 2010-09-15 7:32 UTC (permalink / raw)
To: Suraj Sumangala, Marcel Holtmann
Cc: Suraj Sumangala, linux-bluetooth, Jothikumar Mothilal
In-Reply-To: <4C906430.90900@Atheros.com>
Hello Marcel and Suraj,
-----Message d'origine-----
De=A0: linux-bluetooth-owner@vger.kernel.org =
[mailto:linux-bluetooth-owner@vger.kernel.org] De la part de Suraj =
Sumangala
Envoy=E9=A0: mercredi 15 septembre 2010 08:14
=C0=A0: Marcel Holtmann
Cc=A0: Suraj Sumangala; linux-bluetooth@vger.kernel.org; Jothikumar =
Mothilal
Objet=A0: Re: [RFC] BlueZ D-Bus Sim Access Profile Server API =
description
>Hi Marcel,
>
>On 9/15/2010 9:14 AM, Marcel Holtmann wrote:
>> Hi Suraj,
>>
>>>
>>> I would really appreciate if you can give me any idea about working
>>> around the above mentioned issues.
>>
>> if we talk about the SAP server role found in a mobile phone, then =
that
>> support clearly needs to interact with the telephony stack. Since =
when
>> SAP is active the telephony stack needs to be suspended and all SIM
>> transaction being forwarded.
>>
>> Currently I would be thinking that the SAP implementation should be =
done
>> inside oFono actually. Since then you have direct access to the
>> hardware. The D-Bus approach just doesn't sound correct to me. I =
could
>> be of course wrong, but I can't wrap my mind around on how you can =
make
>> this work.
>>
>> Even with file descriptor passing this doesn't look like the right
>> approach. If we need a hardware abstraction than we either use oFono =
or
>> we have to create some SAP hardware access abstraction.
>
>The advantage I thought d-bus has would be the generic interface it=20
>could provide. But, if we have a system with direct access to the Sim=20
>access hardware, D-bus could possibly become a bottleneck.
>
>I would really appreciate if others who have worked with Sim Access and =
>OFono can give their comments.
>
>Also, please share some information on regarding the SIM reader=20
>implementation in linux based systems.
>
>>
>> The SAP client role found a carkit is obviously a different story.
>>
>> Regards
>>
>> Marcel
>>
>>
>Regards
>Suraj
I agree with Marcel, because in standard Mobile phone implementation, =
the SIM card is directly connected to the Modem. To access the SIM card, =
AT command (or proprietary commands) are needed, oFono implements both =
of them to access SIM card in Modem side, but in my understanding there =
is no possibilities to disconnect modem (in oFono) due to SIM SAP.
In the case where SIM card is connected to the Application processor =
(Linux side), it's missing the SIM stack (APDU server...). I don't find =
any SIM implementation in Linux, may be we have to specify it.=20
Best Regards,
Nicolas.
^ permalink raw reply
* Re: [RFC] BlueZ D-Bus Sim Access Profile Server API description
From: Suraj Sumangala @ 2010-09-15 6:14 UTC (permalink / raw)
To: Marcel Holtmann
Cc: Suraj Sumangala, linux-bluetooth@vger.kernel.org,
Jothikumar Mothilal
In-Reply-To: <1284522264.2405.89.camel@localhost.localdomain>
Hi Marcel,
On 9/15/2010 9:14 AM, Marcel Holtmann wrote:
> Hi Suraj,
>
>>
>> I would really appreciate if you can give me any idea about working
>> around the above mentioned issues.
>
> if we talk about the SAP server role found in a mobile phone, then that
> support clearly needs to interact with the telephony stack. Since when
> SAP is active the telephony stack needs to be suspended and all SIM
> transaction being forwarded.
>
> Currently I would be thinking that the SAP implementation should be done
> inside oFono actually. Since then you have direct access to the
> hardware. The D-Bus approach just doesn't sound correct to me. I could
> be of course wrong, but I can't wrap my mind around on how you can make
> this work.
>
> Even with file descriptor passing this doesn't look like the right
> approach. If we need a hardware abstraction than we either use oFono or
> we have to create some SAP hardware access abstraction.
The advantage I thought d-bus has would be the generic interface it
could provide. But, if we have a system with direct access to the Sim
access hardware, D-bus could possibly become a bottleneck.
I would really appreciate if others who have worked with Sim Access and
OFono can give their comments.
Also, please share some information on regarding the SIM reader
implementation in linux based systems.
>
> The SAP client role found a carkit is obviously a different story.
>
> Regards
>
> Marcel
>
>
Regards
Suraj
^ permalink raw reply
* Re: [RFC] BlueZ D-Bus Sim Access Profile Server API description
From: Marcel Holtmann @ 2010-09-15 3:44 UTC (permalink / raw)
To: Suraj Sumangala
Cc: Suraj Sumangala, linux-bluetooth@vger.kernel.org,
Jothikumar Mothilal
In-Reply-To: <4C8F160B.4010401@Atheros.com>
Hi Suraj,
> >> Below is the Sim Access Profile server role API discription.
> >> Please let me know your comments.
> >
> > my obvious question is why do we wanna do this over D-Bus and not
> > connect it directly to the hardware via a plugin?
>
> You meant creating something like a tty device and let the hardware
> connect directly to this?
>
> The initial plan was to use a D-Bus file descriptor and transfer the SAP
> packets to the agent over it.
>
> The problem was the SAP packet format is specified by the Bluetooth SAP
> spec. I could not relate it to any generic packet format ( Like HF, HS,
> DUN can be mapped to an AT parser).
>
> I am not sure if any existing card reader implementation could parse it
> to extract SIM card related data (APDU) without additional support for it.
>
> Another reason I thought about D-Bus was because, most of the SAP
> operations are Request/Response type like Reset, SimOn etc.
>
> It mapped better with a Method/Signal communication than packet transfer.
>
> Some of the card reader implementation I checked had proprietary packet
> format. So not sure how we could interface with them.
> >
> > Remember that SIM transaction are timing critical and just relaying them
> > over D-Bus doesn't sound the right approach right now. While of course
> > even bluetoothd can't guarantee and response time, it is clearly better
> > than over D-Bus.
> >
>
> I would really appreciate if you can give me any idea about working
> around the above mentioned issues.
if we talk about the SAP server role found in a mobile phone, then that
support clearly needs to interact with the telephony stack. Since when
SAP is active the telephony stack needs to be suspended and all SIM
transaction being forwarded.
Currently I would be thinking that the SAP implementation should be done
inside oFono actually. Since then you have direct access to the
hardware. The D-Bus approach just doesn't sound correct to me. I could
be of course wrong, but I can't wrap my mind around on how you can make
this work.
Even with file descriptor passing this doesn't look like the right
approach. If we need a hardware abstraction than we either use oFono or
we have to create some SAP hardware access abstraction.
The SAP client role found a carkit is obviously a different story.
Regards
Marcel
^ permalink raw reply
* Re: [PATCHv3 3/3] Bluetooth: check L2CAP length in first ACL fragment
From: Gustavo F. Padovan @ 2010-09-14 18:21 UTC (permalink / raw)
To: Emeltchenko Andrei; +Cc: linux-bluetooth
In-Reply-To: <1284018181-29928-4-git-send-email-Andrei.Emeltchenko.news@gmail.com>
Hi Andrei,
* Emeltchenko Andrei <Andrei.Emeltchenko.news@gmail.com> [2010-09-09 10:43:01 +0300]:
> From: Andrei Emeltchenko <andrei.emeltchenko@nokia.com>
>
> Current Bluetooth code assembles fragments of big L2CAP packets
> in l2cap_recv_acldata and then checks allowed L2CAP size in
> assemled L2CAP packet (pi->imtu < skb->len).
>
> The patch moves allowed L2CAP size check to the early stage when
> we receive the first fragment of L2CAP packet. We do not need to
> reserve and keep L2CAP fragments for bad packets.
>
> Updated version after comments from Mat Martineau <mathewm@codeaurora.org>
>
> Trace below is received when using stress tools sending big
> fragmented L2CAP packets.
> ...
> [ 1712.798492] swapper: page allocation failure. order:4, mode:0x4020
> [ 1712.804809] [<c0031870>] (unwind_backtrace+0x0/0xdc) from [<c00a1f70>]
> (__alloc_pages_nodemask+0x4)
> [ 1712.814666] [<c00a1f70>] (__alloc_pages_nodemask+0x47c/0x4d4) from
> [<c00a1fd8>] (__get_free_pages+)
> [ 1712.824645] [<c00a1fd8>] (__get_free_pages+0x10/0x3c) from [<c026eb5c>]
> (__alloc_skb+0x4c/0xfc)
> [ 1712.833465] [<c026eb5c>] (__alloc_skb+0x4c/0xfc) from [<bf28c738>]
> (l2cap_recv_acldata+0xf0/0x1f8 )
> [ 1712.843322] [<bf28c738>] (l2cap_recv_acldata+0xf0/0x1f8 [l2cap]) from
> [<bf0094ac>] (hci_rx_task+0x)
> ...
>
> Signed-off-by: Andrei Emeltchenko <andrei.emeltchenko@nokia.com>
> ---
> net/bluetooth/l2cap.c | 17 +++++++++++++++++
> 1 files changed, 17 insertions(+), 0 deletions(-)
>
> diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c
> index ce8f5e4..c95fbd8 100644
> --- a/net/bluetooth/l2cap.c
> +++ b/net/bluetooth/l2cap.c
> @@ -4654,6 +4654,8 @@ static int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 fl
>
> if (flags & ACL_START) {
> struct l2cap_hdr *hdr;
> + struct sock *sk;
> + u16 cid;
> int len;
>
> if (conn->rx_len) {
> @@ -4673,6 +4675,7 @@ static int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 fl
>
> hdr = (struct l2cap_hdr *) skb->data;
> len = __le16_to_cpu(hdr->len) + L2CAP_HDR_SIZE;
> + cid = __le16_to_cpu(hdr->cid);
>
> if (len == skb->len) {
> /* Complete frame received */
> @@ -4689,6 +4692,20 @@ static int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 fl
> goto drop;
> }
>
> + sk = l2cap_get_chan_by_scid(&conn->chan_list, cid);
> +
> + if (sk && l2cap_pi(sk)->imtu < len) {
> + BT_ERR("Frame exceeding recv MTU (len %d, MTU %d)",
> + len, l2cap_pi(sk)->imtu);
I think you have to check if the imtu is less than (len -
L2CAP_HDR_SIZE) because we don't count the header in the MTU.
> + conn->rx_len = 0; /* needed? */
It's not needed. rx_len is always zero here.
--
Gustavo F. Padovan
ProFUSION embedded systems - http://profusion.mobi
^ permalink raw reply
* Re: [PATCH] Bluetooth: remove extra newline from debug output
From: Andrei Emeltchenko @ 2010-09-14 13:49 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1283952413-22791-1-git-send-email-Andrei.Emeltchenko.news@gmail.com>
ping
On Wed, Sep 8, 2010 at 4:26 PM, Emeltchenko Andrei
<Andrei.Emeltchenko.news@gmail.com> wrote:
> From: Andrei Emeltchenko <andrei.emeltchenko@nokia.com>
>
>
> Signed-off-by: Andrei Emeltchenko <andrei.emeltchenko@nokia.com>
> ---
> net/bluetooth/rfcomm/core.c | 2 +-
> 1 files changed, 1 insertions(+), 1 deletions(-)
>
> diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c
> index 16b79f3..a7e4f2d 100644
> --- a/net/bluetooth/rfcomm/core.c
> +++ b/net/bluetooth/rfcomm/core.c
> @@ -1698,7 +1698,7 @@ static int rfcomm_recv_frame(struct rfcomm_session *s, struct sk_buff *skb)
> break;
>
> default:
> - BT_ERR("Unknown packet type 0x%02x\n", type);
> + BT_ERR("Unknown packet type 0x%02x", type);
> break;
> }
> kfree_skb(skb);
> --
> 1.7.0.4
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
^ permalink raw reply
* Re: [PATCHv3 0/3] Bluetooth: L2CAP robustness fixes
From: Andrei Emeltchenko @ 2010-09-14 13:48 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1284018181-29928-1-git-send-email-Andrei.Emeltchenko.news@gmail.com>
ping
On Thu, Sep 9, 2010 at 10:42 AM, Emeltchenko Andrei
<Andrei.Emeltchenko.news@gmail.com> wrote:
> From: Andrei Emeltchenko <andrei.emeltchenko@nokia.com>
>
> Fixes increase L2CAP robustness to DOS attacks to BT stack.
>
> Andrei Emeltchenko (3):
> Bluetooth: fix MTU L2CAP configuration parameter
> Bluetooth: check for l2cap header in start fragment
> Bluetooth: check L2CAP length in first ACL fragment
>
> net/bluetooth/l2cap.c | 26 ++++++++++++++++++++++----
> 1 files changed, 22 insertions(+), 4 deletions(-)
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
^ permalink raw reply
* Re: set LE advertise not working...
From: Ville Tervo @ 2010-09-14 11:41 UTC (permalink / raw)
To: ext Mike Tsai
Cc: Claudio Takahasi, Anderson Lizardo, Pavan Savoy, Anderson Briglia,
linux-bluetooth@vger.kernel.org
In-Reply-To: <35B17FE5076C7040809188FBE7913F983F2381E185@SC1EXMB-MBCL.global.atheros.com>
On Fri, Sep 10, 2010 at 11:35:50PM +0200, ext Mike Tsai wrote:
>
> -----Original Message-----
> From: linux-bluetooth-owner@vger.kernel.org [mailto:linux-bluetooth-owner@vger.kernel.org] On Behalf Of Claudio Takahasi
> Sent: Friday, September 10, 2010 2:03 PM
> To: Anderson Lizardo
> Cc: Pavan Savoy; Anderson Briglia; linux-bluetooth@vger.kernel.org
> Subject: Re: set LE advertise not working...
>
> On Fri, Sep 10, 2010 at 5:35 PM, Anderson Lizardo
> <anderson.lizardo@openbossa.org> wrote:
> > On Fri, Sep 10, 2010 at 3:01 PM, Pavan Savoy <pavan_savoy@sify.com> wrote:
> >> No, I don't suppose my controller was in a advertising state.. even if
> >> it was why the return 0x0c? (command disallowed ..)
> >>
> >> I wanted to know whether you have some HCI-VS commands which sort of
> >> enable the LE (AMP/radio/controller) ?
> >> Is it only then the leadv command works? Or does it just works right-away.
> >> I have one of those all in 1 chips.. bt/fm-rx-tx/gps so I am wondering
> >> should I do something to enable BLE and is it some sort of un-spoken
> >> standard?
> >
> > Does LE scan work for this same hardware?
> >
> > Do you have some windows software to test this hardware ? if so, you
> > can try putting one device in advertise mode there , then attempt
> > lescan on the other (from linux)... If it works, most probably the
> > hardware might require some initialization...
> >
> > At least some hardware requires firmware upload and vendor specific
> > one-time initialization which (I think) is out of scope for hciconfig.
> >
> > Regards,
> > --
> > Anderson Lizardo
> > OpenBossa Labs - INdT
> > Manaus - Brazil
> > --
> > 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
> >
>
> > Hi All,
>
> > I noticed that some LE hardwares don't allow to enable scanning or
> > advertising when inquiry scan and/or page scan are enabled.
> > Try to disable it using hciconfig or the D-Bus method before to start
> > LE operations.
>
> > Regards,
> > Claudio.
>
> Hello,
>
> Now I remember, dual mode devices (BR/EDR/LE) are not allowed to work as peripheral (or slave) device. The default advertising mode is "connectable indirect advertising mode". When you try to enable the advertising, you are trying to make this dual mode device as a potential slave to other LE masters and that's why the controller rejects the command.
>
Where is this stated?
> Scan should work though.
>
> I don't think you can ever connect 2 dual mode devices together, you need a single mode device (LE only) as the slave,
Well my two dual mode controllers connect fine with eachother.
--
Ville
^ permalink raw reply
* Re: [RFC] BlueZ D-Bus Sim Access Profile Server API description
From: Suraj Sumangala @ 2010-09-14 6:28 UTC (permalink / raw)
To: Marcel Holtmann
Cc: Suraj Sumangala, linux-bluetooth@vger.kernel.org,
Jothikumar Mothilal
In-Reply-To: <1284442946.2405.50.camel@localhost.localdomain>
Hi Marcel,
On 9/14/2010 11:12 AM, Marcel Holtmann wrote:
> Hi Suraj,
>
>> Below is the Sim Access Profile server role API discription.
>> Please let me know your comments.
>
> my obvious question is why do we wanna do this over D-Bus and not
> connect it directly to the hardware via a plugin?
You meant creating something like a tty device and let the hardware
connect directly to this?
The initial plan was to use a D-Bus file descriptor and transfer the SAP
packets to the agent over it.
The problem was the SAP packet format is specified by the Bluetooth SAP
spec. I could not relate it to any generic packet format ( Like HF, HS,
DUN can be mapped to an AT parser).
I am not sure if any existing card reader implementation could parse it
to extract SIM card related data (APDU) without additional support for it.
Another reason I thought about D-Bus was because, most of the SAP
operations are Request/Response type like Reset, SimOn etc.
It mapped better with a Method/Signal communication than packet transfer.
Some of the card reader implementation I checked had proprietary packet
format. So not sure how we could interface with them.
>
> Remember that SIM transaction are timing critical and just relaying them
> over D-Bus doesn't sound the right approach right now. While of course
> even bluetoothd can't guarantee and response time, it is clearly better
> than over D-Bus.
>
I would really appreciate if you can give me any idea about working
around the above mentioned issues.
> Regards
>
> Marcel
>
>
Regards
Suraj
^ permalink raw reply
* Re: [RFC] BlueZ D-Bus Sim Access Profile Server API description
From: Marcel Holtmann @ 2010-09-14 5:42 UTC (permalink / raw)
To: Suraj Sumangala; +Cc: linux-bluetooth, Jothikumar.Mothilal
In-Reply-To: <1284374584-12282-1-git-send-email-suraj@atheros.com>
Hi Suraj,
> Below is the Sim Access Profile server role API discription.
> Please let me know your comments.
my obvious question is why do we wanna do this over D-Bus and not
connect it directly to the hardware via a plugin?
Remember that SIM transaction are timing critical and just relaying them
over D-Bus doesn't sound the right approach right now. While of course
even bluetoothd can't guarantee and response time, it is clearly better
than over D-Bus.
Regards
Marcel
^ permalink raw reply
* Re: [RFC] BlueZ D-Bus Sim Access Profile Server API description
From: Suraj Sumangala @ 2010-09-14 5:00 UTC (permalink / raw)
To: linux-bluetooth@vger.kernel.org, Jothikumar Mothilal
In-Reply-To: <20100913164718.GA9448@jh-x301>
Hi,
On 9/13/2010 10:17 PM, Johan Hedberg wrote:
> Why would you want to expose these extra SAP profile details to the
> Agent? The bluetoothd side should take care of all details like this
> whenever possible. The only relevant data that needs to be transmitted
> from the Agent to bluetoothd in this case is the ATR and bluetoothd can
> then proceed with creating a proper SAP response packet based on that.
>
>> How do we map the D-Bus error return to the ResultCode?
>
> You'd need to define some sort of mapping table yourself. The list of
> possible result codes is quite short (8 in SAP 1.1) so this shouldn't be
> too hard. E.g. "Error, card not accessible" (0x02) could map to the
> D-Bus error org.bluez.SapError.CardNotAccessible.
The idea was to return the ResultCode as a string from the agent and map
it to the corresponding code in the server.
If we can define error codes for each result, that would be fine.
>
> Johan
Regards
Suraj
^ permalink raw reply
* Re: MP3 over A2DP issue
From: pl bossart @ 2010-09-13 22:09 UTC (permalink / raw)
To: 박찬열; +Cc: linux-bluetooth
In-Reply-To: <001301cb4efe$f723f660$e56be320$%park@samsung.com>
> Could you send me "bluetoothd" log?
I tried again on a different laptop with a clean Fedora install. Same
issue, cannot send mp3 to the headset (worked fine with decode/sbc
encode):
[ume@plb ~]$ gst-launch filesrc location=~/AURAL/Audio/maxwork.mp3 !
mp3parse ! a2dpsink device=00:0B:E4:94:31:9D
Setting pipeline to PAUSED ...
Pipeline is PREROLLING ...
Pipeline is PREROLLED ...
Setting pipeline to PLAYING ...
New clock: GstSystemClock
0:00:00.019779029 2452 0x83cfc98 ERROR avdtpsink
audio/gstavdtpsink.c:1328:gst_avdtp_sink_audioservice_recv:<avdtpsink>
BT_OPEN failed : Invalid argument(22)
0:00:00.019809767 2452 0x83cfc98 ERROR avdtpsink
audio/gstavdtpsink.c:1085:gst_avdtp_sink_configure:<avdtpsink> Error
while receiving device confirmation
0:00:00.020036315 2452 0x83cfc98 ERROR avdtpsink
audio/gstavdtpsink.c:1328:gst_avdtp_sink_audioservice_recv:<avdtpsink>
BT_START_STREAM failed : Success(0)
0:00:00.020050426 2452 0x83cfc98 ERROR avdtpsink
audio/gstavdtpsink.c:937:gst_avdtp_sink_stream_start:<avdtpsink> Error
while stream start confirmation
WARNING: from element
/GstPipeline:pipeline0/GstA2dpSink:a2dpsink0/GstAvdtpSink:avdtpsink:
Internal data flow problem.
Additional debug info:
gstbasesink.c(3436): gst_base_sink_chain_unlocked ():
/GstPipeline:pipeline0/GstA2dpSink:a2dpsink0/GstAvdtpSink:avdtpsink:
Received buffer without a new-segment. Assuming timestamps start from 0.
ERROR: from element /GstPipeline:pipeline0/GstFileSrc:filesrc0:
Internal data flow error.
Additional debug info:
gstbasesrc.c(2559): gst_base_src_loop ():
/GstPipeline:pipeline0/GstFileSrc:filesrc0:
streaming task paused, reason error (-5)
Execution ended after 1757132 ns.
Setting pipeline to PAUSED ...
Setting pipeline to READY ...
Setting pipeline to NULL ...
Freeing pipeline ...
And I see the following messages in /var/log/messages:
Sep 13 16:40:51 localhost kernel: usb 4-2: Product: ThinkPad Bluetooth
with Enhanced Data Rate II
Sep 13 16:40:51 localhost kernel: Bluetooth: Core ver 2.15
Sep 13 16:40:51 localhost kernel: Bluetooth: HCI device and connection
manager initialized
Sep 13 16:40:51 localhost kernel: Bluetooth: HCI socket layer initialized
Sep 13 16:40:51 localhost kernel: Bluetooth: Generic Bluetooth USB
driver ver 0.6
Sep 13 16:40:51 localhost kernel: thinkpad_acpi: rfkill switch
tpacpi_bluetooth_sw: radio is unblocked
Sep 13 16:40:57 localhost bluetoothd[1647]: Bluetooth daemon 4.64
Sep 13 16:40:58 localhost bluetoothd[1651]: Starting SDP server
Sep 13 16:40:58 localhost kernel: Bluetooth: L2CAP ver 2.14
Sep 13 16:40:58 localhost kernel: Bluetooth: L2CAP socket layer initialized
Sep 13 16:40:58 localhost bluetoothd[1651]: Parsing
/etc/bluetooth/network.conf failed: No such file or directory
Sep 13 16:40:58 localhost kernel: Bluetooth: BNEP (Ethernet Emulation) ver 1.3
Sep 13 16:40:58 localhost kernel: Bluetooth: BNEP filters: protocol multicast
Sep 13 16:40:58 localhost bluetoothd[1651]: bridge pan0 created
Sep 13 16:40:58 localhost bluetoothd[1651]: Parsing
/etc/bluetooth/input.conf failed: No such file or directory
Sep 13 16:40:58 localhost bluetoothd[1651]: Parsing
/etc/bluetooth/audio.conf failed: No such file or directory
Sep 13 16:40:58 localhost bluetoothd[1651]: HCI dev 0 registered
Sep 13 16:40:58 localhost kernel: Bluetooth: SCO (Voice Link) ver 0.6
Sep 13 16:40:58 localhost kernel: Bluetooth: SCO socket layer initialized
Sep 13 16:40:58 localhost bluetoothd[1651]: HCI dev 0 up
Sep 13 16:40:58 localhost bluetoothd[1651]: Starting security manager 0
Sep 13 16:40:58 localhost bluetoothd[1651]: Parsing
/etc/bluetooth/serial.conf failed: No such file or directory
Sep 13 16:40:58 localhost kernel: Bluetooth: RFCOMM TTY layer initialized
Sep 13 16:40:58 localhost kernel: Bluetooth: RFCOMM socket layer initialized
Sep 13 16:40:58 localhost kernel: Bluetooth: RFCOMM ver 1.11
Sep 13 16:40:58 localhost bluetoothd[1651]: Adapter
/org/bluez/1647/hci0 has been enabled
Sep 13 16:42:01 localhost bluetoothd[1651]: link_key_request
(sba=00:22:68:EB:F8:5D, dba=00:0B:E4:94:31:9D)
Sep 13 16:42:02 localhost bluetoothd[1651]: link_key_request
(sba=00:22:68:EB:F8:5D, dba=00:0B:E4:94:31:9D)
Sep 13 16:42:04 localhost bluetoothd[1651]: Badly formated or
unrecognized command: AT+CSRSF=1,1,1,1,1,7
Sep 13 16:42:48 localhost bluetoothd[1651]: seid 2 not available or locked
Sep 13 16:42:48 localhost bluetoothd[1651]: seid not opened
No idea if this is due to a bad configuration or a bad headset...
I also see a bunch of bt-usb messages, not sure if this is related
Sep 13 16:40:51 localhost kernel: usbcore: registered new interface driver btusb
Sep 13 16:41:01 localhost kernel: btusb_intr_complete: hci0 urb
f3ff1b00 failed to resubmit (1)
Sep 13 16:41:01 localhost kernel: btusb_bulk_complete: hci0 urb
f3ff1980 failed to resubmit (1)
Sep 13 16:41:01 localhost kernel: btusb_bulk_complete: hci0 urb
f3ff1780 failed to resubmit (1)
Sep 13 16:42:08 localhost kernel: btusb_intr_complete: hci0 urb
f21c0180 failed to resubmit (1)
Sep 13 16:42:08 localhost kernel: btusb_bulk_complete: hci0 urb
f21c0b00 failed to resubmit (1)
Sep 13 16:42:08 localhost kernel: btusb_bulk_complete: hci0 urb
f21c0000 failed to resubmit (1)
Sep 13 16:42:18 localhost kernel: btusb_intr_complete: hci0 urb
f0eb2180 failed to resubmit (1)
Sep 13 16:42:18 localhost kernel: btusb_bulk_complete: hci0 urb
f0eb2a80 failed to resubmit (1)
Sep 13 16:42:18 localhost kernel: btusb_bulk_complete: hci0 urb
f0eb2100 failed to resubmit (1)
Sep 13 16:42:40 localhost kernel: btusb_intr_complete: hci0 urb
f0d99900 failed to resubmit (1)
Sep 13 16:42:40 localhost kernel: btusb_bulk_complete: hci0 urb
f0d99780 failed to resubmit (1)
Sep 13 16:42:40 localhost kernel: btusb_bulk_complete: hci0 urb
f0d99f00 failed to resubmit (1)
Sep 13 16:42:50 localhost kernel: btusb_intr_complete: hci0 urb
f0d82000 failed to resubmit (1)
Sep 13 16:42:50 localhost kernel: btusb_bulk_complete: hci0 urb
f0d82e00 failed to resubmit (1)
Sep 13 16:42:50 localhost kernel: btusb_bulk_complete: hci0 urb
f0d82200 failed to resubmit (1)
Thanks for your feedback,
-Pierre
^ permalink raw reply
* Re: dund
From: Ivan Baidakou @ 2010-09-13 20:47 UTC (permalink / raw)
To: Vladimir Botka; +Cc: linux-bluetooth@vger.kernel.org
In-Reply-To: <20100912163714.09aac1e3@vaio.site>
---=== reply ===---
Hello,
OK, thanks, it solves the synchronization problem, but this is a
particular solution because it doesn't handles such cases as
web-browsing, ftp-uploading (Palm serves as FTP-server for convenient
file uploading from common desktop software) etc. So, there is need to
DUN-functionality. DUNd is deprecated, what comes instead of?
Best regards,
Ivan Baidakou
---=== original message ===---
Date: Sun, 12 Sep 2010 16:37:14 +0200
Subj: Re: dund
From: Vladimir Botka <vbotka@gmail.com>
To: Ivan Baidakou <the.dmol@gmail.com>
On Sun, 12 Sep 2010 11:44:11 +0300
Ivan Baidakou <the.dmol@gmail.com> wrote:
> You haven't real experience, right?
I have.
# pilot-xfer -p bt:00:07:E0:B3:DD:03 -l
Listening for incoming connection on bt:00:07:E0:B3:DD:03...
connected!
Reading list of databases in RAM...
AdditDVSData
ADD050SData
AdditSystemSData
<snip>
Cheers,
-vlado
^ permalink raw reply
* Re: [RFC] BlueZ D-Bus Sim Access Profile Server API description
From: Johan Hedberg @ 2010-09-13 16:47 UTC (permalink / raw)
To: Suraj Sumangala; +Cc: linux-bluetooth, Jothikumar.Mothilal
In-Reply-To: <4C8E40B5.8080701@Atheros.com>
Hi,
On Mon, Sep 13, 2010, Suraj Sumangala wrote:
> The "array{uint8}" is just the ATR response from the card reader. it
> is not a complete Bluetoth SAP response packet. We need to
> encapsulate ATR packet to form a Bluetooth SAP response packet. The
> packet will be roughly.
>
> |Response code|Param1|Param data ( ResultCode )|param2|param data (ATR)|
>
> The ResultCode will have to be mapped as 1 byte result code in the
> SAP response packet sent to the client.
Why would you want to expose these extra SAP profile details to the
Agent? The bluetoothd side should take care of all details like this
whenever possible. The only relevant data that needs to be transmitted
from the Agent to bluetoothd in this case is the ATR and bluetoothd can
then proceed with creating a proper SAP response packet based on that.
> How do we map the D-Bus error return to the ResultCode?
You'd need to define some sort of mapping table yourself. The list of
possible result codes is quite short (8 in SAP 1.1) so this shouldn't be
too hard. E.g. "Error, card not accessible" (0x02) could map to the
D-Bus error org.bluez.SapError.CardNotAccessible.
Johan
^ permalink raw reply
* Re: [RFC] BlueZ D-Bus Sim Access Profile Server API description
From: Suraj Sumangala @ 2010-09-13 15:25 UTC (permalink / raw)
To: Waldemar.Rymarkiewicz@tieto.com
Cc: Suraj Sumangala, linux-bluetooth@vger.kernel.org,
Jothikumar Mothilal
In-Reply-To: <99B09243E1A5DA4898CDD8B700111448097968E69F@EXMB04.eu.tieto.com>
Hi Waldek,
On 9/13/2010 6:17 PM, Waldemar.Rymarkiewicz@tieto.com wrote:
> Hi Suraj,
>
>> The agent will be in a better position to take this decision
>> as it will be interfacing between actual card reader and SAP server.
>>
>> For example, if the agent is connected to an external card
>> reader and it has lost connection with the reader. It can ask
>> for an Immediate disconnection.
>
> What will happen when dbus connection will be lost? SAP server will start immediate disconnection. Right?
Yes,
>
>> If disconnection was triggered by a user action, it can decide
>> to call this API with Graceful as the parameter.
>
> What exactly do you mean "triggerd by a user" ? Does agent interact with the user somehow? In which way, any API for that?
Here we only specifies the interface required by the Agent using SAP
server. The agent implementation will depend on the actual system which
uses SAP server.
For example, if we have use ofono. The agent will be implemented as part
of ofono's implementation. This will be similar to the way Handsfree
Gateway is implemented.
>
>> One candidate would be when the MessageSize is changed.
>> Another one will be with the "SimStatus" property. It makes
>> sense to raise his signal after the server sends the STATUS
>> IND response successfully.
>>
>> But, it is a fact that most of the Properties are set by the
>> Agent itself. So agent will know that they have changed.
>
> Exactly.
>
> Thanks,
> /Waldek
Regards
Suraj
^ permalink raw reply
* Re: [RFC] BlueZ D-Bus Sim Access Profile Server API description
From: Suraj Sumangala @ 2010-09-13 15:18 UTC (permalink / raw)
To: linux-bluetooth, Jothikumar.Mothilal
In-Reply-To: <20100913132014.GA5539@jh-x301>
Hi,
On 9/13/2010 6:50 PM, Johan Hedberg wrote:
> Hi,
>
>> Should we have the method like,
>> struct RequestATR(string command, variant param)
>
> ATR doesn't take any parameters so it'd be just RequestATR()
>
>> where, struct = { string, array{uint8} )
Yep,
>
> What do you need the string for? Wouldn't an array{uint8} suffice? A
> D-Bus error return could be used when the ResultCode in the response
> indicates failure.
The "array{uint8}" is just the ATR response from the card reader. it is
not a complete Bluetoth SAP response packet. We need to encapsulate ATR
packet to form a Bluetooth SAP response packet. The packet will be roughly.
|Response code|Param1|Param data ( ResultCode )|param2|param data (ATR)|
The ResultCode will have to be mapped as 1 byte result code in the SAP
response packet sent to the client.
How do we map the D-Bus error return to the ResultCode?
>
> Johan
> --
> 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
Regards
Suraj
^ permalink raw reply
* [PATCH 10/10] Add proper checks for MediaTransport.SetProperty
From: Luiz Augusto von Dentz @ 2010-09-13 14:15 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1284387337-18822-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.dentz-von@nokia.com>
---
audio/transport.c | 116 +++++++++++++++++++++++++++++++++++++++++++----------
1 files changed, 94 insertions(+), 22 deletions(-)
diff --git a/audio/transport.c b/audio/transport.c
index 6d08677..7387818 100644
--- a/audio/transport.c
+++ b/audio/transport.c
@@ -83,12 +83,23 @@ struct media_transport {
void (*get_properties) (
struct media_transport *transport,
DBusMessageIter *dict);
- DBusMessage *(*set_property) (
+ int (*set_property) (
struct media_transport *transport,
- DBusConnection *conn,
- DBusMessage *msg);
+ const char *property,
+ DBusMessageIter *value);
};
+static inline DBusMessage *invalid_args(DBusMessage *msg)
+{
+ return g_dbus_create_error(msg, ERROR_INTERFACE ".InvalidArguments",
+ "Invalid arguments in method call");
+}
+
+static inline DBusMessage *error_failed(DBusMessage *msg, const char *desc)
+{
+ return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed", "%s", desc);
+}
+
void media_transport_remove(struct media_transport *transport)
{
char *path;
@@ -434,14 +445,10 @@ static DBusMessage *acquire(DBusConnection *conn, DBusMessage *msg,
renderer = media_transport_find_renderer(transport, sender);
if (renderer != NULL)
- return g_dbus_create_error(msg, ERROR_INTERFACE
- ".Failed",
- "Permission denied");
+ return error_failed(msg, strerror(EPERM));
if (media_transport_acquire(transport, accesstype) == FALSE)
- return g_dbus_create_error(msg, ERROR_INTERFACE
- ".Failed",
- "Permission denied");
+ return error_failed(msg, strerror(EPERM));
renderer = media_renderer_create(transport, msg, accesstype);
req = g_new0(struct acquire_request, 1);
@@ -471,9 +478,7 @@ static DBusMessage *release(DBusConnection *conn, DBusMessage *msg,
renderer = media_transport_find_renderer(transport, sender);
if (renderer == NULL)
- return g_dbus_create_error(msg, ERROR_INTERFACE
- ".Failed",
- "Permission denied");
+ return error_failed(msg, strerror(EPERM));
if (g_strcmp0(renderer->accesstype, accesstype) == 0)
media_renderer_remove(renderer);
@@ -481,31 +486,98 @@ static DBusMessage *release(DBusConnection *conn, DBusMessage *msg,
media_transport_release(transport, accesstype);
g_strdelimit(renderer->accesstype, accesstype, ' ');
} else
- return g_dbus_create_error(msg, ERROR_INTERFACE
- ".Failed",
- "Permission denied");
+ return error_failed(msg, strerror(EPERM));
return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
}
-static DBusMessage *set_property_a2dp(struct media_transport *transport,
- DBusConnection *conn, DBusMessage *msg)
+static int set_property_a2dp(struct media_transport *transport,
+ const char *property,
+ DBusMessageIter *value)
{
- return NULL;
+ if (g_strcmp0(property, "Delay") == 0) {
+ if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_UINT16)
+ return -EINVAL;
+ dbus_message_iter_get_basic(value, &transport->delay);
+
+ /* FIXME: send new delay */
+ return 0;
+ }
+
+ return -EINVAL;
}
-static DBusMessage *set_property_headset(struct media_transport *transport,
- DBusConnection *conn, DBusMessage *msg)
+static int set_property_headset(struct media_transport *transport,
+ const char *property,
+ DBusMessageIter *value)
{
- return NULL;
+ if (g_strcmp0(property, "NREC") == 0) {
+ gboolean nrec;
+
+ if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_BOOLEAN)
+ return -EINVAL;
+ dbus_message_iter_get_basic(value, &nrec);
+
+ /* FIXME: set new nrec */
+ return 0;
+ } else if (g_strcmp0(property, "InbandRingtone") == 0) {
+ gboolean inband;
+
+ if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_BOOLEAN)
+ return -EINVAL;
+ dbus_message_iter_get_basic(value, &inband);
+
+ /* FIXME: set new inband */
+ return 0;
+ }
+
+ return -EINVAL;
}
static DBusMessage *set_property(DBusConnection *conn, DBusMessage *msg,
void *data)
{
struct media_transport *transport = data;
+ DBusMessageIter iter;
+ DBusMessageIter value;
+ const char *property, *sender;
+ GSList *l;
+ int err;
+
+ if (!dbus_message_iter_init(msg, &iter))
+ return invalid_args(msg);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+ return invalid_args(msg);
+
+ dbus_message_iter_get_basic(&iter, &property);
+ dbus_message_iter_next(&iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+ return invalid_args(msg);
+ dbus_message_iter_recurse(&iter, &value);
+
+ sender = dbus_message_get_sender(msg);
+ err = -EINVAL;
- return transport->set_property(transport, conn, msg);
+ /* Check if sender has acquired the transport */
+ for (l = transport->renderers; l; l = l->next) {
+ struct media_renderer *renderer = l->data;
+
+ if (g_strcmp0(renderer->name, sender) == 0) {
+ err = transport->set_property(transport, property,
+ &value);
+ break;
+ }
+ }
+
+ if (err < 0) {
+ if (err == -EINVAL)
+ return invalid_args(msg);
+ return error_failed(msg, strerror(-err));
+ }
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
}
static void get_properties_a2dp(struct media_transport *transport,
--
1.7.1
^ permalink raw reply related
* [PATCH 09/10] Remove local cache for nrec and inband
From: Luiz Augusto von Dentz @ 2010-09-13 14:15 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1284387337-18822-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.dentz-von@nokia.com>
Those can be acessed directly from device/headset.
---
audio/transport.c | 11 ++++++-----
1 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/audio/transport.c b/audio/transport.c
index 0da99e8..6d08677 100644
--- a/audio/transport.c
+++ b/audio/transport.c
@@ -72,8 +72,6 @@ struct media_transport {
uint16_t imtu; /* Transport input mtu */
uint16_t omtu; /* Transport output mtu */
uint16_t delay; /* Transport delay (a2dp only) */
- gboolean nrec; /* Transport nrec (hfp only) */
- gboolean inband; /* Transport inband ringtone support (hfp only) */
gboolean read_lock;
gboolean write_lock;
gboolean in_use;
@@ -519,10 +517,13 @@ static void get_properties_a2dp(struct media_transport *transport,
static void get_properties_headset(struct media_transport *transport,
DBusMessageIter *dict)
{
- dict_append_entry(dict, "NREC", DBUS_TYPE_BOOLEAN, &transport->nrec);
+ gboolean nrec, inband;
- dict_append_entry(dict, "InbandRingtone", DBUS_TYPE_BOOLEAN,
- &transport->inband);
+ nrec = headset_get_nrec(transport->device);
+ dict_append_entry(dict, "NREC", DBUS_TYPE_BOOLEAN, &nrec);
+
+ inband = headset_get_inband(transport->device);
+ dict_append_entry(dict, "InbandRingtone", DBUS_TYPE_BOOLEAN, &inband);
}
static DBusMessage *get_properties(DBusConnection *conn, DBusMessage *msg,
--
1.7.1
^ permalink raw reply related
* [PATCH 08/10] Update a2dp transport delay when it changes
From: Luiz Augusto von Dentz @ 2010-09-13 14:15 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1284387337-18822-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.dentz-von@nokia.com>
---
audio/a2dp.c | 31 +++++++++++++++++++++++++++----
audio/media.c | 6 ++++++
audio/media.h | 2 ++
audio/transport.c | 14 ++++++++++++++
audio/transport.h | 2 ++
5 files changed, 51 insertions(+), 4 deletions(-)
diff --git a/audio/a2dp.c b/audio/a2dp.c
index e6daf3b..7d3f25f 100644
--- a/audio/a2dp.c
+++ b/audio/a2dp.c
@@ -44,6 +44,7 @@
#include "source.h"
#include "unix.h"
#include "media.h"
+#include "transport.h"
#include "a2dp.h"
#include "sdpd.h"
@@ -600,9 +601,9 @@ static gboolean endpoint_setconf_ind(struct avdtp *session,
}
static gboolean endpoint_getcap_ind(struct avdtp *session,
- struct avdtp_local_sep *sep,
- gboolean get_all,
- GSList **caps, uint8_t *err, void *user_data)
+ struct avdtp_local_sep *sep,
+ gboolean get_all, GSList **caps,
+ uint8_t *err, void *user_data)
{
struct a2dp_sep *a2dp_sep = user_data;
struct avdtp_service_capability *media_transport, *media_codec;
@@ -1031,6 +1032,28 @@ static gboolean delayreport_ind(struct avdtp *session,
return TRUE;
}
+static gboolean endpoint_delayreport_ind(struct avdtp *session,
+ struct avdtp_local_sep *sep,
+ uint8_t rseid, uint16_t delay,
+ uint8_t *err, void *user_data)
+{
+ struct a2dp_sep *a2dp_sep = user_data;
+ struct media_transport *transport;
+
+ if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+ DBG("Sink %p: DelayReport_Ind", sep);
+ else
+ DBG("Source %p: DelayReport_Ind", sep);
+
+ transport = media_endpoint_get_transport(a2dp_sep->endpoint);
+ if (transport == NULL)
+ return FALSE;
+
+ media_transport_update_delay(transport, delay);
+
+ return TRUE;
+}
+
static void reconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
struct avdtp_stream *stream, struct avdtp_error *err,
void *user_data)
@@ -1115,7 +1138,7 @@ static struct avdtp_sep_ind endpoint_ind = {
.close = close_ind,
.abort = abort_ind,
.reconfigure = reconf_ind,
- .delayreport = delayreport_ind,
+ .delayreport = endpoint_delayreport_ind,
};
static sdp_record_t *a2dp_record(uint8_t type, uint16_t avdtp_ver)
diff --git a/audio/media.c b/audio/media.c
index da9d3ae..19ba1c4 100644
--- a/audio/media.c
+++ b/audio/media.c
@@ -681,3 +681,9 @@ uint8_t media_endpoint_get_codec(struct media_endpoint *endpoint)
{
return endpoint->codec;
}
+
+struct media_transport *media_endpoint_get_transport(
+ struct media_endpoint *endpoint)
+{
+ return endpoint->transport;
+}
diff --git a/audio/media.h b/audio/media.h
index f6d144c..d089103 100644
--- a/audio/media.h
+++ b/audio/media.h
@@ -50,3 +50,5 @@ void media_endpoint_release(struct media_endpoint *endpoint);
struct a2dp_sep *media_endpoint_get_sep(struct media_endpoint *endpoint);
const char *media_endpoint_get_uuid(struct media_endpoint *endpoint);
uint8_t media_endpoint_get_codec(struct media_endpoint *endpoint);
+struct media_transport *media_endpoint_get_transport(
+ struct media_endpoint *endpoint);
diff --git a/audio/transport.c b/audio/transport.c
index c4d78a4..0da99e8 100644
--- a/audio/transport.c
+++ b/audio/transport.c
@@ -669,3 +669,17 @@ const char *media_transport_get_path(struct media_transport *transport)
{
return transport->path;
}
+
+void media_transport_update_delay(struct media_transport *transport,
+ uint16_t delay)
+{
+ /* Check if delay really changed */
+ if (transport->delay == delay)
+ return;
+
+ transport->delay = delay;
+
+ emit_property_changed(transport->conn, transport->path,
+ MEDIA_TRANSPORT_INTERFACE, "Delay",
+ DBUS_TYPE_UINT16, &transport->delay);
+}
diff --git a/audio/transport.h b/audio/transport.h
index a7594f0..f2eb37b 100644
--- a/audio/transport.h
+++ b/audio/transport.h
@@ -32,3 +32,5 @@ struct media_transport *media_transport_create(DBusConnection *conn,
void media_transport_remove(struct media_transport *transport);
const char *media_transport_get_path(struct media_transport *transport);
+void media_transport_update_delay(struct media_transport *transport,
+ uint16_t delay);
--
1.7.1
^ permalink raw reply related
* [PATCH 07/10] Introduce headset_get_inband
From: Luiz Augusto von Dentz @ 2010-09-13 14:15 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1284387337-18822-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.dentz-von@nokia.com>
---
audio/headset.c | 10 ++++++++++
audio/headset.h | 1 +
2 files changed, 11 insertions(+), 0 deletions(-)
diff --git a/audio/headset.c b/audio/headset.c
index cdb3370..dad0716 100644
--- a/audio/headset.c
+++ b/audio/headset.c
@@ -2701,6 +2701,16 @@ gboolean headset_get_nrec(struct audio_device *dev)
return hs->slc->nrec;
}
+gboolean headset_get_inband(struct audio_device *dev)
+{
+ struct headset *hs = dev->headset;
+
+ if (!hs->slc)
+ return TRUE;
+
+ return hs->slc->inband_ring;
+}
+
gboolean headset_get_sco_hci(struct audio_device *dev)
{
return sco_hci;
diff --git a/audio/headset.h b/audio/headset.h
index 8b7c738..29dc02c 100644
--- a/audio/headset.h
+++ b/audio/headset.h
@@ -90,6 +90,7 @@ int headset_get_channel(struct audio_device *dev);
int headset_get_sco_fd(struct audio_device *dev);
gboolean headset_get_nrec(struct audio_device *dev);
+gboolean headset_get_inband(struct audio_device *dev);
gboolean headset_get_sco_hci(struct audio_device *dev);
gboolean headset_is_active(struct audio_device *dev);
--
1.7.1
^ permalink raw reply related
* [PATCH 06/10] Add initial implementation of org.bluez.Media spec
From: Luiz Augusto von Dentz @ 2010-09-13 14:15 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1284387337-18822-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.dentz-von@nokia.com>
---
Makefile.am | 2 +
audio/a2dp.c | 726 ++++++++++++++++++++++++++++++++++++++++++++---------
audio/a2dp.h | 15 ++
audio/avdtp.c | 88 +++----
audio/avdtp.h | 5 +-
audio/manager.c | 54 ++++
audio/manager.h | 1 +
audio/media.c | 683 +++++++++++++++++++++++++++++++++++++++++++++++++
audio/media.h | 52 ++++
audio/sink.c | 179 ++------------
audio/source.c | 174 ++-----------
audio/transport.c | 671 +++++++++++++++++++++++++++++++++++++++++++++++++
audio/transport.h | 34 +++
audio/unix.c | 1 +
14 files changed, 2198 insertions(+), 487 deletions(-)
create mode 100644 audio/media.c
create mode 100644 audio/media.h
create mode 100644 audio/transport.c
create mode 100644 audio/transport.h
diff --git a/Makefile.am b/Makefile.am
index 46f5449..347205e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -139,6 +139,8 @@ builtin_sources += audio/main.c \
audio/avdtp.h audio/avdtp.c \
audio/ipc.h audio/ipc.c \
audio/unix.h audio/unix.c \
+ audio/media.h audio/media.c \
+ audio/transport.h audio/transport.c \
audio/telephony.h
builtin_nodist += audio/telephony.c
diff --git a/audio/a2dp.c b/audio/a2dp.c
index ef0df17..e6daf3b 100644
--- a/audio/a2dp.c
+++ b/audio/a2dp.c
@@ -43,6 +43,7 @@
#include "sink.h"
#include "source.h"
#include "unix.h"
+#include "media.h"
#include "a2dp.h"
#include "sdpd.h"
@@ -60,9 +61,11 @@
#endif
struct a2dp_sep {
+ struct a2dp_server *server;
+ struct media_endpoint *endpoint;
uint8_t type;
uint8_t codec;
- struct avdtp_local_sep *sep;
+ struct avdtp_local_sep *lsep;
struct avdtp *session;
struct avdtp_stream *stream;
guint suspend_timer;
@@ -73,6 +76,7 @@ struct a2dp_sep {
};
struct a2dp_setup_cb {
+ a2dp_select_cb_t select_cb;
a2dp_config_cb_t config_cb;
a2dp_stream_cb_t resume_cb;
a2dp_stream_cb_t suspend_cb;
@@ -84,6 +88,7 @@ struct a2dp_setup {
struct audio_device *dev;
struct avdtp *session;
struct a2dp_sep *sep;
+ struct avdtp_remote_sep *rsep;
struct avdtp_stream *stream;
struct avdtp_error *err;
GSList *client_caps;
@@ -150,11 +155,11 @@ static struct audio_device *a2dp_get_dev(struct avdtp *session)
static gboolean finalize_config(struct a2dp_setup *s)
{
GSList *l;
+ struct avdtp_stream *stream = s->err ? NULL : s->stream;
setup_ref(s);
for (l = s->cb; l != NULL; l = l->next) {
struct a2dp_setup_cb *cb = l->data;
- struct avdtp_stream *stream = s->err ? NULL : s->stream;
if (!cb->config_cb)
continue;
@@ -166,6 +171,7 @@ static gboolean finalize_config(struct a2dp_setup *s)
}
setup_unref(s);
+
return FALSE;
}
@@ -184,6 +190,7 @@ static gboolean finalize_resume(struct a2dp_setup *s)
GSList *l;
setup_ref(s);
+
for (l = s->cb; l != NULL; l = l->next) {
struct a2dp_setup_cb *cb = l->data;
@@ -195,6 +202,7 @@ static gboolean finalize_resume(struct a2dp_setup *s)
}
setup_unref(s);
+
return FALSE;
}
@@ -227,6 +235,25 @@ static gboolean finalize_suspend_errno(struct a2dp_setup *s, int err)
return finalize_suspend(s);
}
+static gboolean finalize_select(struct a2dp_setup *s, GSList *caps)
+{
+ GSList *l;
+
+ setup_ref(s);
+ for (l = s->cb; l != NULL; l = l->next) {
+ struct a2dp_setup_cb *cb = l->data;
+
+ if (cb->select_cb) {
+ cb->select_cb(s->session, s->sep, caps, cb->user_data);
+ cb->select_cb = NULL;
+ setup_unref(s);
+ }
+ }
+
+ setup_unref(s);
+ return FALSE;
+}
+
static struct a2dp_setup *find_setup_by_session(struct avdtp *session)
{
GSList *l;
@@ -276,6 +303,9 @@ static void stream_state_changed(struct avdtp_stream *stream,
sep->session = NULL;
}
+ if (sep->endpoint)
+ media_endpoint_clear_configuration(sep->endpoint);
+
sep->stream = NULL;
}
@@ -505,6 +535,117 @@ static gboolean mpeg_getcap_ind(struct avdtp *session,
return TRUE;
}
+static gboolean endpoint_setconf_ind(struct avdtp *session,
+ struct avdtp_local_sep *sep,
+ struct avdtp_stream *stream,
+ GSList *caps, uint8_t *err,
+ uint8_t *category, void *user_data)
+{
+ struct a2dp_sep *a2dp_sep = user_data;
+ struct audio_device *dev;
+
+ if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+ DBG("Sink %p: Set_Configuration_Ind", sep);
+ else
+ DBG("Source %p: Set_Configuration_Ind", sep);
+
+ dev = a2dp_get_dev(session);
+ if (!dev) {
+ *err = AVDTP_UNSUPPORTED_CONFIGURATION;
+ *category = 0x00;
+ return FALSE;
+ }
+
+ for (; caps != NULL; caps = g_slist_next(caps)) {
+ struct avdtp_service_capability *cap = caps->data;
+ struct avdtp_media_codec_capability *codec;
+ gboolean ret;
+
+ if (cap->category == AVDTP_DELAY_REPORTING &&
+ !a2dp_sep->delay_reporting) {
+ *err = AVDTP_UNSUPPORTED_CONFIGURATION;
+ *category = AVDTP_DELAY_REPORTING;
+ return FALSE;
+ }
+
+ if (cap->category != AVDTP_MEDIA_CODEC)
+ continue;
+
+ codec = (struct avdtp_media_codec_capability *) cap->data;
+
+ if (codec->media_codec_type != a2dp_sep->codec) {
+ *err = AVDTP_UNSUPPORTED_CONFIGURATION;
+ *category = AVDTP_MEDIA_CODEC;
+ return FALSE;
+ }
+
+ ret = media_endpoint_set_configuration(a2dp_sep->endpoint, dev,
+ codec->data, cap->length - sizeof(*codec),
+ NULL, NULL);
+ if (ret)
+ break;
+
+ *err = AVDTP_UNSUPPORTED_CONFIGURATION;
+ *category = AVDTP_MEDIA_CODEC;
+ return FALSE;
+ }
+
+ avdtp_stream_add_cb(session, stream, stream_state_changed, a2dp_sep);
+ a2dp_sep->stream = stream;
+
+ if (a2dp_sep->type == AVDTP_SEP_TYPE_SOURCE)
+ sink_new_stream(dev, session, stream);
+
+ return TRUE;
+}
+
+static gboolean endpoint_getcap_ind(struct avdtp *session,
+ struct avdtp_local_sep *sep,
+ gboolean get_all,
+ GSList **caps, uint8_t *err, void *user_data)
+{
+ struct a2dp_sep *a2dp_sep = user_data;
+ struct avdtp_service_capability *media_transport, *media_codec;
+ struct avdtp_media_codec_capability *codec_caps;
+ uint8_t *capabilities;
+ size_t length;
+
+ if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+ DBG("Sink %p: Get_Capability_Ind", sep);
+ else
+ DBG("Source %p: Get_Capability_Ind", sep);
+
+ *caps = NULL;
+
+ media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT,
+ NULL, 0);
+
+ *caps = g_slist_append(*caps, media_transport);
+
+ length = media_endpoint_get_capabilities(a2dp_sep->endpoint,
+ &capabilities);
+
+ codec_caps = g_malloc0(sizeof(*codec_caps) + length);
+ codec_caps->media_type = AVDTP_MEDIA_TYPE_AUDIO;
+ codec_caps->media_codec_type = a2dp_sep->codec;
+ memcpy(codec_caps->data, capabilities, length);
+
+ media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, codec_caps,
+ sizeof(*codec_caps) + length);
+
+ *caps = g_slist_append(*caps, media_codec);
+ g_free(codec_caps);
+
+ if (get_all) {
+ struct avdtp_service_capability *delay_reporting;
+ delay_reporting = avdtp_service_cap_new(AVDTP_DELAY_REPORTING,
+ NULL, 0);
+ *caps = g_slist_append(*caps, delay_reporting);
+ }
+
+ return TRUE;
+}
+
static void setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
struct avdtp_stream *stream,
struct avdtp_error *err, void *user_data)
@@ -543,6 +684,23 @@ static void setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
else
source_new_stream(dev, session, setup->stream);
+ /* Notify Endpoint */
+ if (a2dp_sep->endpoint) {
+ struct avdtp_service_capability *service;
+ struct avdtp_media_codec_capability *codec;
+
+ service = avdtp_stream_get_codec(stream);
+ codec = (struct avdtp_media_codec_capability *) service->data;
+
+ if (media_endpoint_set_configuration(a2dp_sep->endpoint, dev,
+ codec->data, service->length -
+ sizeof(*codec), NULL, NULL) ==
+ FALSE) {
+ setup->stream = NULL;
+ finalize_config_errno(setup, -EPERM);
+ }
+ }
+
ret = avdtp_open(session, stream);
if (ret < 0) {
error("Error on avdtp_open %s (%d)", strerror(-ret), -ret);
@@ -761,39 +919,11 @@ static gboolean close_ind(struct avdtp *session, struct avdtp_local_sep *sep,
static gboolean a2dp_reconfigure(gpointer data)
{
struct a2dp_setup *setup = data;
- struct avdtp_local_sep *lsep;
- struct avdtp_remote_sep *rsep;
- struct avdtp_service_capability *cap;
- struct avdtp_media_codec_capability *codec_cap = NULL;
- GSList *l;
+ struct a2dp_sep *sep = setup->sep;
int posix_err;
- for (l = setup->client_caps; l != NULL; l = l->next) {
- cap = l->data;
-
- if (cap->category != AVDTP_MEDIA_CODEC)
- continue;
-
- codec_cap = (void *) cap->data;
- break;
- }
-
- if (!codec_cap) {
- error("Cannot find capabilities to reconfigure");
- posix_err = -EINVAL;
- goto failed;
- }
-
- posix_err = avdtp_get_seps(setup->session, AVDTP_SEP_TYPE_SINK,
- codec_cap->media_type,
- codec_cap->media_codec_type,
- &lsep, &rsep);
- if (posix_err < 0) {
- error("No matching ACP and INT SEPs found");
- goto failed;
- }
-
- posix_err = avdtp_set_configuration(setup->session, rsep, lsep,
+ posix_err = avdtp_set_configuration(setup->session, setup->rsep,
+ sep->lsep,
setup->client_caps,
&setup->stream);
if (posix_err < 0) {
@@ -975,6 +1105,19 @@ static struct avdtp_sep_ind mpeg_ind = {
.delayreport = delayreport_ind,
};
+static struct avdtp_sep_ind endpoint_ind = {
+ .get_capability = endpoint_getcap_ind,
+ .set_configuration = endpoint_setconf_ind,
+ .get_configuration = getconf_ind,
+ .open = open_ind,
+ .start = start_ind,
+ .suspend = suspend_ind,
+ .close = close_ind,
+ .abort = abort_ind,
+ .reconfigure = reconf_ind,
+ .delayreport = delayreport_ind,
+};
+
static sdp_record_t *a2dp_record(uint8_t type, uint16_t avdtp_ver)
{
sdp_list_t *svclass_id, *pfseq, *apseq, *root;
@@ -1042,64 +1185,6 @@ static sdp_record_t *a2dp_record(uint8_t type, uint16_t avdtp_ver)
return record;
}
-static struct a2dp_sep *a2dp_add_sep(struct a2dp_server *server, uint8_t type,
- uint8_t codec, gboolean delay_reporting)
-{
- struct a2dp_sep *sep;
- GSList **l;
- uint32_t *record_id;
- sdp_record_t *record;
- struct avdtp_sep_ind *ind;
-
- sep = g_new0(struct a2dp_sep, 1);
-
- ind = (codec == A2DP_CODEC_MPEG12) ? &mpeg_ind : &sbc_ind;
- sep->sep = avdtp_register_sep(&server->src, type,
- AVDTP_MEDIA_TYPE_AUDIO, codec,
- delay_reporting, ind, &cfm, sep);
- if (sep->sep == NULL) {
- g_free(sep);
- return NULL;
- }
-
- sep->codec = codec;
- sep->type = type;
- sep->delay_reporting = delay_reporting;
-
- if (type == AVDTP_SEP_TYPE_SOURCE) {
- l = &server->sources;
- record_id = &server->source_record_id;
- } else {
- l = &server->sinks;
- record_id = &server->sink_record_id;
- }
-
- if (*record_id != 0)
- goto add;
-
- record = a2dp_record(type, server->version);
- if (!record) {
- error("Unable to allocate new service record");
- avdtp_unregister_sep(sep->sep);
- g_free(sep);
- return NULL;
- }
-
- if (add_record_to_server(&server->src, record) < 0) {
- error("Unable to register A2DP service record");\
- sdp_record_free(record);
- avdtp_unregister_sep(sep->sep);
- g_free(sep);
- return NULL;
- }
- *record_id = record->handle;
-
-add:
- *l = g_slist_append(*l, sep);
-
- return sep;
-}
-
static struct a2dp_server *find_server(GSList *list, const bdaddr_t *src)
{
GSList *l;
@@ -1220,22 +1305,23 @@ proceed:
if (source) {
for (i = 0; i < sbc_srcs; i++)
- a2dp_add_sep(server, AVDTP_SEP_TYPE_SOURCE,
- A2DP_CODEC_SBC, delay_reporting);
+ a2dp_add_sep(src, AVDTP_SEP_TYPE_SOURCE,
+ A2DP_CODEC_SBC, delay_reporting, NULL);
for (i = 0; i < mpeg12_srcs; i++)
- a2dp_add_sep(server, AVDTP_SEP_TYPE_SOURCE,
- A2DP_CODEC_MPEG12, delay_reporting);
+ a2dp_add_sep(src, AVDTP_SEP_TYPE_SOURCE,
+ A2DP_CODEC_MPEG12, delay_reporting, NULL);
}
if (sink) {
for (i = 0; i < sbc_sinks; i++)
- a2dp_add_sep(server, AVDTP_SEP_TYPE_SINK,
- A2DP_CODEC_SBC, delay_reporting);
+ a2dp_add_sep(src, AVDTP_SEP_TYPE_SINK,
+ A2DP_CODEC_SBC, delay_reporting, NULL);
for (i = 0; i < mpeg12_sinks; i++)
- a2dp_add_sep(server, AVDTP_SEP_TYPE_SINK,
- A2DP_CODEC_MPEG12, delay_reporting);
+ a2dp_add_sep(src, AVDTP_SEP_TYPE_SINK,
+ A2DP_CODEC_MPEG12, delay_reporting,
+ NULL);
}
return 0;
@@ -1243,7 +1329,7 @@ proceed:
static void a2dp_unregister_sep(struct a2dp_sep *sep)
{
- avdtp_unregister_sep(sep->sep);
+ avdtp_unregister_sep(sep->lsep);
g_free(sep);
}
@@ -1255,20 +1341,14 @@ void a2dp_unregister(const bdaddr_t *src)
if (!server)
return;
- g_slist_foreach(server->sinks, (GFunc) a2dp_unregister_sep, NULL);
+ g_slist_foreach(server->sinks, (GFunc) a2dp_remove_sep, NULL);
g_slist_free(server->sinks);
- g_slist_foreach(server->sources, (GFunc) a2dp_unregister_sep, NULL);
+ g_slist_foreach(server->sources, (GFunc) a2dp_remove_sep, NULL);
g_slist_free(server->sources);
avdtp_exit(src);
- if (server->source_record_id)
- remove_record_from_server(server->source_record_id);
-
- if (server->sink_record_id)
- remove_record_from_server(server->sink_record_id);
-
servers = g_slist_remove(servers, server);
g_free(server);
@@ -1279,6 +1359,100 @@ void a2dp_unregister(const bdaddr_t *src)
connection = NULL;
}
+struct a2dp_sep *a2dp_add_sep(const bdaddr_t *src, uint8_t type,
+ uint8_t codec, gboolean delay_reporting,
+ struct media_endpoint *endpoint)
+{
+ struct a2dp_server *server;
+ struct a2dp_sep *sep;
+ GSList **l;
+ uint32_t *record_id;
+ sdp_record_t *record;
+ struct avdtp_sep_ind *ind;
+
+ server = find_server(servers, src);
+ if (server == NULL)
+ return NULL;
+
+ sep = g_new0(struct a2dp_sep, 1);
+
+ if (endpoint) {
+ ind = &endpoint_ind;
+ goto proceed;
+ }
+
+ ind = (codec == A2DP_CODEC_MPEG12) ? &mpeg_ind : &sbc_ind;
+
+proceed:
+ sep->lsep = avdtp_register_sep(&server->src, type,
+ AVDTP_MEDIA_TYPE_AUDIO, codec,
+ delay_reporting, ind, &cfm, sep);
+ if (sep->lsep == NULL) {
+ g_free(sep);
+ return NULL;
+ }
+
+ sep->server = server;
+ sep->endpoint = endpoint;
+ sep->codec = codec;
+ sep->type = type;
+ sep->delay_reporting = delay_reporting;
+
+ if (type == AVDTP_SEP_TYPE_SOURCE) {
+ l = &server->sources;
+ record_id = &server->source_record_id;
+ } else {
+ l = &server->sinks;
+ record_id = &server->sink_record_id;
+ }
+
+ if (*record_id != 0)
+ goto add;
+
+ record = a2dp_record(type, server->version);
+ if (!record) {
+ error("Unable to allocate new service record");
+ avdtp_unregister_sep(sep->lsep);
+ g_free(sep);
+ return NULL;
+ }
+
+ if (add_record_to_server(&server->src, record) < 0) {
+ error("Unable to register A2DP service record");\
+ sdp_record_free(record);
+ avdtp_unregister_sep(sep->lsep);
+ g_free(sep);
+ return NULL;
+ }
+ *record_id = record->handle;
+
+add:
+ *l = g_slist_append(*l, sep);
+
+ return sep;
+}
+
+void a2dp_remove_sep(struct a2dp_sep *sep)
+{
+ struct a2dp_server *server = sep->server;
+
+ if (sep->type == AVDTP_SEP_TYPE_SOURCE) {
+ server->sources = g_slist_remove(server->sources, sep);
+ if (server->sources == NULL && server->source_record_id) {
+ remove_record_from_server(server->source_record_id);
+ server->source_record_id = 0;
+ }
+ } else {
+ server->sinks = g_slist_remove(server->sinks, sep);
+ if (server->sinks == NULL && server->sink_record_id) {
+ remove_record_from_server(server->sink_record_id);
+ server->sink_record_id = 0;
+ }
+ }
+
+ a2dp_unregister_sep(sep);
+}
+
struct a2dp_sep *a2dp_get(struct avdtp *session,
struct avdtp_remote_sep *rsep)
{
@@ -1317,6 +1491,309 @@ struct a2dp_sep *a2dp_get(struct avdtp *session,
return NULL;
}
+static uint8_t default_bitpool(uint8_t freq, uint8_t mode)
+{
+ switch (freq) {
+ case SBC_SAMPLING_FREQ_16000:
+ case SBC_SAMPLING_FREQ_32000:
+ return 53;
+ case SBC_SAMPLING_FREQ_44100:
+ switch (mode) {
+ case SBC_CHANNEL_MODE_MONO:
+ case SBC_CHANNEL_MODE_DUAL_CHANNEL:
+ return 31;
+ case SBC_CHANNEL_MODE_STEREO:
+ case SBC_CHANNEL_MODE_JOINT_STEREO:
+ return 53;
+ default:
+ error("Invalid channel mode %u", mode);
+ return 53;
+ }
+ case SBC_SAMPLING_FREQ_48000:
+ switch (mode) {
+ case SBC_CHANNEL_MODE_MONO:
+ case SBC_CHANNEL_MODE_DUAL_CHANNEL:
+ return 29;
+ case SBC_CHANNEL_MODE_STEREO:
+ case SBC_CHANNEL_MODE_JOINT_STEREO:
+ return 51;
+ default:
+ error("Invalid channel mode %u", mode);
+ return 51;
+ }
+ default:
+ error("Invalid sampling freq %u", freq);
+ return 53;
+ }
+}
+
+static gboolean select_sbc_params(struct sbc_codec_cap *cap,
+ struct sbc_codec_cap *supported)
+{
+ unsigned int max_bitpool, min_bitpool;
+
+ memset(cap, 0, sizeof(struct sbc_codec_cap));
+
+ cap->cap.media_type = AVDTP_MEDIA_TYPE_AUDIO;
+ cap->cap.media_codec_type = A2DP_CODEC_SBC;
+
+ if (supported->frequency & SBC_SAMPLING_FREQ_44100)
+ cap->frequency = SBC_SAMPLING_FREQ_44100;
+ else if (supported->frequency & SBC_SAMPLING_FREQ_48000)
+ cap->frequency = SBC_SAMPLING_FREQ_48000;
+ else if (supported->frequency & SBC_SAMPLING_FREQ_32000)
+ cap->frequency = SBC_SAMPLING_FREQ_32000;
+ else if (supported->frequency & SBC_SAMPLING_FREQ_16000)
+ cap->frequency = SBC_SAMPLING_FREQ_16000;
+ else {
+ error("No supported frequencies");
+ return FALSE;
+ }
+
+ if (supported->channel_mode & SBC_CHANNEL_MODE_JOINT_STEREO)
+ cap->channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO;
+ else if (supported->channel_mode & SBC_CHANNEL_MODE_STEREO)
+ cap->channel_mode = SBC_CHANNEL_MODE_STEREO;
+ else if (supported->channel_mode & SBC_CHANNEL_MODE_DUAL_CHANNEL)
+ cap->channel_mode = SBC_CHANNEL_MODE_DUAL_CHANNEL;
+ else if (supported->channel_mode & SBC_CHANNEL_MODE_MONO)
+ cap->channel_mode = SBC_CHANNEL_MODE_MONO;
+ else {
+ error("No supported channel modes");
+ return FALSE;
+ }
+
+ if (supported->block_length & SBC_BLOCK_LENGTH_16)
+ cap->block_length = SBC_BLOCK_LENGTH_16;
+ else if (supported->block_length & SBC_BLOCK_LENGTH_12)
+ cap->block_length = SBC_BLOCK_LENGTH_12;
+ else if (supported->block_length & SBC_BLOCK_LENGTH_8)
+ cap->block_length = SBC_BLOCK_LENGTH_8;
+ else if (supported->block_length & SBC_BLOCK_LENGTH_4)
+ cap->block_length = SBC_BLOCK_LENGTH_4;
+ else {
+ error("No supported block lengths");
+ return FALSE;
+ }
+
+ if (supported->subbands & SBC_SUBBANDS_8)
+ cap->subbands = SBC_SUBBANDS_8;
+ else if (supported->subbands & SBC_SUBBANDS_4)
+ cap->subbands = SBC_SUBBANDS_4;
+ else {
+ error("No supported subbands");
+ return FALSE;
+ }
+
+ if (supported->allocation_method & SBC_ALLOCATION_LOUDNESS)
+ cap->allocation_method = SBC_ALLOCATION_LOUDNESS;
+ else if (supported->allocation_method & SBC_ALLOCATION_SNR)
+ cap->allocation_method = SBC_ALLOCATION_SNR;
+
+ min_bitpool = MAX(MIN_BITPOOL, supported->min_bitpool);
+ max_bitpool = MIN(default_bitpool(cap->frequency, cap->channel_mode),
+ supported->max_bitpool);
+
+ cap->min_bitpool = min_bitpool;
+ cap->max_bitpool = max_bitpool;
+
+ return TRUE;
+}
+
+static gboolean select_capabilities(struct avdtp *session,
+ struct avdtp_remote_sep *rsep,
+ GSList **caps)
+{
+ struct avdtp_service_capability *media_transport, *media_codec;
+ struct sbc_codec_cap sbc_cap;
+
+ media_codec = avdtp_get_codec(rsep);
+ if (!media_codec)
+ return FALSE;
+
+ select_sbc_params(&sbc_cap, (struct sbc_codec_cap *) media_codec->data);
+
+ media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT,
+ NULL, 0);
+
+ *caps = g_slist_append(*caps, media_transport);
+
+ media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, &sbc_cap,
+ sizeof(sbc_cap));
+
+ *caps = g_slist_append(*caps, media_codec);
+
+ if (avdtp_get_delay_reporting(rsep)) {
+ struct avdtp_service_capability *delay_reporting;
+ delay_reporting = avdtp_service_cap_new(AVDTP_DELAY_REPORTING,
+ NULL, 0);
+ *caps = g_slist_append(*caps, delay_reporting);
+ }
+
+ return TRUE;
+}
+
+static void select_cb(struct media_endpoint *endpoint, void *ret, int size,
+ void *user_data)
+{
+ struct a2dp_setup *setup = user_data;
+ struct avdtp_service_capability *media_transport, *media_codec;
+ struct avdtp_media_codec_capability *cap;
+ GSList *caps = NULL;
+
+ if (size < 0) {
+ DBG("Endpoint replied an invalid configuration");
+ goto done;
+ }
+
+ media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT,
+ NULL, 0);
+
+ caps = g_slist_append(caps, media_transport);
+
+ cap = g_malloc0(sizeof(*cap) + size);
+ cap->media_type = AVDTP_MEDIA_TYPE_AUDIO;
+ cap->media_codec_type = setup->sep->codec;
+ memcpy(cap->data, ret, size);
+
+ media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, cap,
+ sizeof(*cap) + size);
+
+ caps = g_slist_append(caps, media_codec);
+
+done:
+ finalize_select(setup, caps);
+}
+
+static gboolean auto_select(gpointer data)
+{
+ struct a2dp_setup *setup = data;
+
+ finalize_select(setup, setup->client_caps);
+
+ return FALSE;
+}
+
+static struct a2dp_sep *a2dp_find_sep(struct avdtp *session, GSList *list,
+ const char *sender)
+{
+ for (; list; list = list->next) {
+ struct a2dp_sep *sep = list->data;
+
+ /* Use sender's endpoint if available */
+ if (sender) {
+ const char *name;
+
+ if (sep->endpoint == NULL)
+ continue;
+
+ name = media_endpoint_get_sender(sep->endpoint);
+ if (g_strcmp0(sender, name) != 0)
+ continue;
+ }
+
+ if (avdtp_find_remote_sep(session, sep->lsep) == NULL)
+ continue;
+
+ return sep;
+ }
+
+ return NULL;
+}
+
+static struct a2dp_sep *a2dp_select_sep(struct avdtp *session, uint8_t type,
+ const char *sender)
+{
+ struct a2dp_server *server;
+ struct a2dp_sep *sep;
+ GSList *l;
+ bdaddr_t src;
+
+ avdtp_get_peers(session, &src, NULL);
+ server = find_server(servers, &src);
+ if (!server)
+ return NULL;
+
+ l = type == AVDTP_SEP_TYPE_SINK ? server->sources : server->sinks;
+
+ /* Check sender's seps first */
+ sep = a2dp_find_sep(session, l, sender);
+ if (sep != NULL)
+ return sep;
+
+ return a2dp_find_sep(session, l, NULL);
+}
+
+unsigned int a2dp_select_capabilities(struct avdtp *session,
+ uint8_t type, const char *sender,
+ a2dp_select_cb_t cb,
+ void *user_data)
+{
+ struct a2dp_setup *setup;
+ struct a2dp_setup_cb *cb_data;
+ struct a2dp_sep *sep;
+ struct avdtp_service_capability *service;
+ struct avdtp_media_codec_capability *codec;
+
+ sep = a2dp_select_sep(session, type, sender);
+ if (!sep) {
+ error("Unable to select SEP");
+ return 0;
+ }
+
+ cb_data = g_new0(struct a2dp_setup_cb, 1);
+ cb_data->select_cb = cb;
+ cb_data->user_data = user_data;
+ cb_data->id = ++cb_id;
+
+ setup = find_setup_by_session(session);
+ if (!setup) {
+ setup = g_new0(struct a2dp_setup, 1);
+ setup->session = avdtp_ref(session);
+ setup->dev = a2dp_get_dev(session);
+ setups = g_slist_append(setups, setup);
+ }
+
+ setup_ref(setup);
+ setup->cb = g_slist_append(setup->cb, cb_data);
+ setup->sep = sep;
+ setup->rsep = avdtp_find_remote_sep(session, sep->lsep);
+
+ if (setup->rsep == NULL) {
+ error("Could not find remote sep");
+ goto fail;
+ }
+
+ /* FIXME: Remove auto select when it is not longer possible to register
+ endpoint in the configuration file */
+ if (sep->endpoint == NULL) {
+ if (!select_capabilities(session, setup->rsep,
+ &setup->client_caps)) {
+ error("Unable to auto select remote SEP capabilities");
+ goto fail;
+ }
+
+ g_idle_add(auto_select, setup);
+
+ return cb_data->id;
+ }
+
+ service = avdtp_get_codec(setup->rsep);
+ codec = (struct avdtp_media_codec_capability *) service->data;
+
+ if (media_endpoint_select_configuration(sep->endpoint, codec->data,
+ service->length - sizeof(*codec),
+ select_cb, setup) ==
+ TRUE)
+ return cb_data->id;
+
+fail:
+ setup_unref(setup);
+ cb_id--;
+ return 0;
+
+}
+
unsigned int a2dp_config(struct avdtp *session, struct a2dp_sep *sep,
a2dp_config_cb_t cb, GSList *caps,
void *user_data)
@@ -1326,8 +1803,6 @@ unsigned int a2dp_config(struct avdtp *session, struct a2dp_sep *sep,
struct a2dp_server *server;
struct a2dp_setup *setup;
struct a2dp_sep *tmp;
- struct avdtp_local_sep *lsep;
- struct avdtp_remote_sep *rsep;
struct avdtp_service_capability *cap;
struct avdtp_media_codec_capability *codec_cap = NULL;
int posix_err;
@@ -1355,7 +1830,7 @@ unsigned int a2dp_config(struct avdtp *session, struct a2dp_sep *sep,
if (sep->codec != codec_cap->media_codec_type)
return 0;
- DBG("a2dp_config: selected SEP %p", sep->sep);
+ DBG("a2dp_config: selected SEP %p", sep->lsep);
cb_data = g_new0(struct a2dp_setup_cb, 1);
cb_data->config_cb = cb;
@@ -1376,7 +1851,7 @@ unsigned int a2dp_config(struct avdtp *session, struct a2dp_sep *sep,
setup->stream = sep->stream;
setup->client_caps = caps;
- switch (avdtp_sep_get_state(sep->sep)) {
+ switch (avdtp_sep_get_state(sep->lsep)) {
case AVDTP_STATE_IDLE:
if (sep->type == AVDTP_SEP_TYPE_SOURCE) {
l = server->sources;
@@ -1403,16 +1878,15 @@ unsigned int a2dp_config(struct avdtp *session, struct a2dp_sep *sep,
break;
}
- if (avdtp_get_seps(session, remote_type,
- codec_cap->media_type,
- codec_cap->media_codec_type,
- &lsep, &rsep) < 0) {
+ setup->rsep = avdtp_find_remote_sep(session, sep->lsep);
+ if (setup->rsep == NULL) {
error("No matching ACP and INT SEPs found");
goto failed;
}
- posix_err = avdtp_set_configuration(session, rsep, lsep,
- caps, &setup->stream);
+ posix_err = avdtp_set_configuration(session, setup->rsep,
+ sep->lsep, caps,
+ &setup->stream);
if (posix_err < 0) {
error("avdtp_set_configuration: %s",
strerror(-posix_err));
@@ -1469,7 +1943,7 @@ unsigned int a2dp_resume(struct avdtp *session, struct a2dp_sep *sep,
setup->sep = sep;
setup->stream = sep->stream;
- switch (avdtp_sep_get_state(sep->sep)) {
+ switch (avdtp_sep_get_state(sep->lsep)) {
case AVDTP_STATE_IDLE:
goto failed;
break;
@@ -1528,7 +2002,7 @@ unsigned int a2dp_suspend(struct avdtp *session, struct a2dp_sep *sep,
setup->sep = sep;
setup->stream = sep->stream;
- switch (avdtp_sep_get_state(sep->sep)) {
+ switch (avdtp_sep_get_state(sep->lsep)) {
case AVDTP_STATE_IDLE:
error("a2dp_suspend: no stream to suspend");
goto failed;
@@ -1541,6 +2015,7 @@ unsigned int a2dp_suspend(struct avdtp *session, struct a2dp_sep *sep,
error("avdtp_suspend failed");
goto failed;
}
+ sep->suspending = TRUE;
break;
default:
error("SEP in bad state for suspend");
@@ -1595,7 +2070,7 @@ gboolean a2dp_sep_lock(struct a2dp_sep *sep, struct avdtp *session)
if (sep->locked)
return FALSE;
- DBG("SEP %p locked", sep->sep);
+ DBG("SEP %p locked", sep->lsep);
sep->locked = TRUE;
return TRUE;
@@ -1605,11 +2080,11 @@ gboolean a2dp_sep_unlock(struct a2dp_sep *sep, struct avdtp *session)
{
avdtp_state_t state;
- state = avdtp_sep_get_state(sep->sep);
+ state = avdtp_sep_get_state(sep->lsep);
sep->locked = FALSE;
- DBG("SEP %p unlocked", sep->sep);
+ DBG("SEP %p unlocked", sep->lsep);
if (!sep->stream || state == AVDTP_STATE_IDLE)
return TRUE;
@@ -1671,3 +2146,8 @@ struct a2dp_sep *a2dp_get_sep(struct avdtp *session,
return NULL;
}
+
+struct avdtp_stream *a2dp_sep_get_stream(struct a2dp_sep *sep)
+{
+ return sep->stream;
+}
diff --git a/audio/a2dp.h b/audio/a2dp.h
index fa81776..21fccaa 100644
--- a/audio/a2dp.h
+++ b/audio/a2dp.h
@@ -121,6 +121,10 @@ struct mpeg_codec_cap {
struct a2dp_sep;
+
+typedef void (*a2dp_select_cb_t) (struct avdtp *session,
+ struct a2dp_sep *sep, GSList *caps,
+ void *user_data);
typedef void (*a2dp_config_cb_t) (struct avdtp *session, struct a2dp_sep *sep,
struct avdtp_stream *stream,
struct avdtp_error *err,
@@ -132,7 +136,17 @@ typedef void (*a2dp_stream_cb_t) (struct avdtp *session,
int a2dp_register(DBusConnection *conn, const bdaddr_t *src, GKeyFile *config);
void a2dp_unregister(const bdaddr_t *src);
+struct a2dp_sep *a2dp_add_sep(const bdaddr_t *src, uint8_t type,
+ uint8_t codec, gboolean delay_reporting,
+ struct media_endpoint *endpoint);
+void a2dp_remove_sep(struct a2dp_sep *sep);
+
struct a2dp_sep *a2dp_get(struct avdtp *session, struct avdtp_remote_sep *sep);
+
+unsigned int a2dp_select_capabilities(struct avdtp *session,
+ uint8_t type, const char *sender,
+ a2dp_select_cb_t cb,
+ void *user_data);
unsigned int a2dp_config(struct avdtp *session, struct a2dp_sep *sep,
a2dp_config_cb_t cb, GSList *caps,
void *user_data);
@@ -145,5 +159,6 @@ gboolean a2dp_cancel(struct audio_device *dev, unsigned int id);
gboolean a2dp_sep_lock(struct a2dp_sep *sep, struct avdtp *session);
gboolean a2dp_sep_unlock(struct a2dp_sep *sep, struct avdtp *session);
gboolean a2dp_sep_get_lock(struct a2dp_sep *sep);
+struct avdtp_stream *a2dp_sep_get_stream(struct a2dp_sep *sep);
struct a2dp_sep *a2dp_get_sep(struct avdtp *session,
struct avdtp_stream *stream);
diff --git a/audio/avdtp.c b/audio/avdtp.c
index cc7066f..b9a03f8 100644
--- a/audio/avdtp.c
+++ b/audio/avdtp.c
@@ -1191,22 +1191,36 @@ static struct avdtp_local_sep *find_local_sep_by_seid(struct avdtp_server *serve
return NULL;
}
-static struct avdtp_local_sep *find_local_sep(struct avdtp_server *server,
- uint8_t type,
- uint8_t media_type,
- uint8_t codec)
+struct avdtp_remote_sep *avdtp_find_remote_sep(struct avdtp *session,
+ struct avdtp_local_sep *lsep)
{
GSList *l;
- for (l = server->seps; l != NULL; l = g_slist_next(l)) {
- struct avdtp_local_sep *sep = l->data;
+ if (lsep->info.inuse)
+ return NULL;
+
+ for (l = session->seps; l != NULL; l = g_slist_next(l)) {
+ struct avdtp_remote_sep *sep = l->data;
+ struct avdtp_service_capability *cap;
+ struct avdtp_media_codec_capability *codec_data;
+
+ /* Type must be different: source <-> sink */
+ if (sep->type == lsep->info.type)
+ continue;
+
+ if (sep->media_type != lsep->info.media_type)
+ continue;
- if (sep->info.inuse)
+ if (!sep->codec)
continue;
- if (sep->info.type == type &&
- sep->info.media_type == media_type &&
- sep->codec == codec)
+ cap = sep->codec;
+ codec_data = (void *) cap->data;
+
+ if (codec_data->media_codec_type != lsep->codec)
+ continue;
+
+ if (sep->stream == NULL)
return sep;
}
@@ -3214,49 +3228,6 @@ int avdtp_discover(struct avdtp *session, avdtp_discover_cb_t cb,
return err;
}
-int avdtp_get_seps(struct avdtp *session, uint8_t acp_type, uint8_t media_type,
- uint8_t codec, struct avdtp_local_sep **lsep,
- struct avdtp_remote_sep **rsep)
-{
- GSList *l;
- uint8_t int_type;
-
- int_type = acp_type == AVDTP_SEP_TYPE_SINK ?
- AVDTP_SEP_TYPE_SOURCE : AVDTP_SEP_TYPE_SINK;
-
- *lsep = find_local_sep(session->server, int_type, media_type, codec);
- if (!*lsep)
- return -EINVAL;
-
- for (l = session->seps; l != NULL; l = g_slist_next(l)) {
- struct avdtp_remote_sep *sep = l->data;
- struct avdtp_service_capability *cap;
- struct avdtp_media_codec_capability *codec_data;
-
- if (sep->type != acp_type)
- continue;
-
- if (sep->media_type != media_type)
- continue;
-
- if (!sep->codec)
- continue;
-
- cap = sep->codec;
- codec_data = (void *) cap->data;
-
- if (codec_data->media_codec_type != codec)
- continue;
-
- if (!sep->stream) {
- *rsep = sep;
- return 0;
- }
- }
-
- return -EINVAL;
-}
-
gboolean avdtp_stream_remove_cb(struct avdtp *session,
struct avdtp_stream *stream,
unsigned int id)
@@ -3354,8 +3325,14 @@ int avdtp_set_configuration(struct avdtp *session,
new_stream->lsep = lsep;
new_stream->rseid = rsep->seid;
- if (rsep->delay_reporting && lsep->delay_reporting)
+ if (rsep->delay_reporting && lsep->delay_reporting) {
+ struct avdtp_service_capability *delay_reporting;
+
+ delay_reporting = avdtp_service_cap_new(AVDTP_DELAY_REPORTING,
+ NULL, 0);
+ caps = g_slist_append(caps, delay_reporting);
new_stream->delay_reporting = TRUE;
+ }
g_slist_foreach(caps, copy_capabilities, &new_stream->caps);
@@ -3625,6 +3602,9 @@ int avdtp_unregister_sep(struct avdtp_local_sep *sep)
if (sep->stream)
release_stream(sep->stream, sep->stream->session);
+ DBG("SEP %p unregistered: type:%d codec:%d seid:%d", sep,
+ sep->info.type, sep->codec, sep->info.seid);
+
g_free(sep);
return 0;
diff --git a/audio/avdtp.h b/audio/avdtp.h
index 3fe682b..bf10a2f 100644
--- a/audio/avdtp.h
+++ b/audio/avdtp.h
@@ -290,9 +290,8 @@ struct avdtp_local_sep *avdtp_register_sep(const bdaddr_t *src, uint8_t type,
void *user_data);
/* Find a matching pair of local and remote SEP ID's */
-int avdtp_get_seps(struct avdtp *session, uint8_t type, uint8_t media,
- uint8_t codec, struct avdtp_local_sep **lsep,
- struct avdtp_remote_sep **rsep);
+struct avdtp_remote_sep *avdtp_find_remote_sep(struct avdtp *session,
+ struct avdtp_local_sep *lsep);
int avdtp_unregister_sep(struct avdtp_local_sep *sep);
diff --git a/audio/manager.c b/audio/manager.c
index 87e7a2a..816c807 100644
--- a/audio/manager.c
+++ b/audio/manager.c
@@ -61,6 +61,7 @@
#include "device.h"
#include "error.h"
#include "avdtp.h"
+#include "media.h"
#include "a2dp.h"
#include "headset.h"
#include "gateway.h"
@@ -72,6 +73,10 @@
#include "telephony.h"
#include "unix.h"
+#ifndef DBUS_TYPE_UNIX_FD
+#define DBUS_TYPE_UNIX_FD -1
+#endif
+
typedef enum {
HEADSET = 1 << 0,
GATEWAY = 1 << 1,
@@ -115,6 +120,7 @@ static struct enabled_interfaces enabled = {
.source = FALSE,
.control = TRUE,
.socket = TRUE,
+ .media = FALSE
};
static struct audio_adapter *find_adapter(GSList *list,
@@ -1015,6 +1021,38 @@ static void avrcp_server_remove(struct btd_adapter *adapter)
audio_adapter_unref(adp);
}
+static int media_server_probe(struct btd_adapter *adapter)
+{
+ struct audio_adapter *adp;
+ const gchar *path = adapter_get_path(adapter);
+ bdaddr_t src;
+
+ DBG("path %s", path);
+
+ adp = audio_adapter_get(adapter);
+ if (!adp)
+ return -EINVAL;
+
+ adapter_get_address(adapter, &src);
+
+ return media_register(connection, path, &src);
+}
+
+static void media_server_remove(struct btd_adapter *adapter)
+{
+ struct audio_adapter *adp;
+ const gchar *path = adapter_get_path(adapter);
+
+ DBG("path %s", path);
+
+ adp = find_adapter(adapters, adapter);
+ if (!adp)
+ return;
+
+ media_unregister(path);
+ audio_adapter_unref(adp);
+}
+
static struct btd_device_driver audio_driver = {
.name = "audio",
.uuids = BTD_UUIDS(HSP_HS_UUID, HFP_HS_UUID, HSP_AG_UUID, HFP_AG_UUID,
@@ -1048,6 +1086,12 @@ static struct btd_adapter_driver avrcp_server_driver = {
.remove = avrcp_server_remove,
};
+static struct btd_adapter_driver media_server_driver = {
+ .name = "media",
+ .probe = media_server_probe,
+ .remove = media_server_remove,
+};
+
int audio_manager_init(DBusConnection *conn, GKeyFile *conf,
gboolean *enable_sco)
{
@@ -1078,6 +1122,8 @@ int audio_manager_init(DBusConnection *conn, GKeyFile *conf,
enabled.control = TRUE;
else if (g_str_equal(list[i], "Socket"))
enabled.socket = TRUE;
+ else if (g_str_equal(list[i], "Media"))
+ enabled.media = TRUE;
}
g_strfreev(list);
@@ -1096,6 +1142,8 @@ int audio_manager_init(DBusConnection *conn, GKeyFile *conf,
enabled.control = FALSE;
else if (g_str_equal(list[i], "Socket"))
enabled.socket = FALSE;
+ else if (g_str_equal(list[i], "Media"))
+ enabled.media = FALSE;
}
g_strfreev(list);
@@ -1126,6 +1174,9 @@ proceed:
if (enabled.socket)
unix_init();
+ if (enabled.media)
+ btd_register_adapter_driver(&media_server_driver);
+
if (enabled.headset) {
telephony_init();
btd_register_adapter_driver(&headset_server_driver);
@@ -1164,6 +1215,9 @@ void audio_manager_exit(void)
if (enabled.socket)
unix_exit();
+ if (enabled.media)
+ btd_unregister_adapter_driver(&media_server_driver);
+
if (enabled.headset) {
btd_unregister_adapter_driver(&headset_server_driver);
telephony_exit();
diff --git a/audio/manager.h b/audio/manager.h
index c79b761..0bf7663 100644
--- a/audio/manager.h
+++ b/audio/manager.h
@@ -30,6 +30,7 @@ struct enabled_interfaces {
gboolean source;
gboolean control;
gboolean socket;
+ gboolean media;
};
int audio_manager_init(DBusConnection *conn, GKeyFile *config,
diff --git a/audio/media.c b/audio/media.c
new file mode 100644
index 0000000..da9d3ae
--- /dev/null
+++ b/audio/media.c
@@ -0,0 +1,683 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2007 Nokia Corporation
+ * Copyright (C) 2004-2009 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+
+#include <glib.h>
+#include <gdbus.h>
+
+#include "../src/adapter.h"
+#include "../src/dbus-common.h"
+
+#include "log.h"
+#include "error.h"
+#include "device.h"
+#include "avdtp.h"
+#include "media.h"
+#include "transport.h"
+#include "a2dp.h"
+#include "headset.h"
+
+#ifndef DBUS_TYPE_UNIX_FD
+#define DBUS_TYPE_UNIX_FD -1
+#endif
+
+#define MEDIA_INTERFACE "org.bluez.Media"
+#define MEDIA_ENDPOINT_INTERFACE "org.bluez.MediaEndpoint"
+
+#define REQUEST_TIMEOUT (3 * 1000) /* 3 seconds */
+
+struct media_adapter {
+ bdaddr_t src; /* Adapter address */
+ char *path; /* Adapter path */
+ DBusConnection *conn; /* Adapter connection */
+ GSList *endpoints; /* Endpoints list */
+};
+
+struct endpoint_request {
+ DBusMessage *msg;
+ DBusPendingCall *call;
+ media_endpoint_cb_t cb;
+ void *user_data;
+};
+
+struct media_endpoint {
+ struct a2dp_sep *sep;
+ char *sender; /* Endpoint DBus bus id */
+ char *path; /* Endpoint object path */
+ char *uuid; /* Endpoint property UUID */
+ uint8_t codec; /* Endpoint codec */
+ uint8_t *capabilities; /* Endpoint property capabilities */
+ size_t size; /* Endpoint capabilities size */
+ guint hs_watch;
+ guint watch;
+ struct endpoint_request *request;
+ struct media_transport *transport;
+ struct media_adapter *adapter;
+};
+
+static GSList *adapters = NULL;
+
+static void endpoint_request_free(struct endpoint_request *request)
+{
+ if (request->call)
+ dbus_pending_call_cancel(request->call);
+
+ dbus_message_unref(request->msg);
+ g_free(request);
+}
+
+static void media_endpoint_remove(struct media_endpoint *endpoint)
+{
+ struct media_adapter *adapter = endpoint->adapter;
+
+ info("Endpoint unregistered: sender=%s path=%s", endpoint->sender,
+ endpoint->path);
+
+ adapter->endpoints = g_slist_remove(adapter->endpoints, endpoint);
+
+ if (endpoint->sep)
+ a2dp_remove_sep(endpoint->sep);
+
+ if (endpoint->hs_watch)
+ headset_remove_state_cb(endpoint->hs_watch);
+
+ if (endpoint->request)
+ endpoint_request_free(endpoint->request);
+
+ if (endpoint->transport)
+ media_transport_remove(endpoint->transport);
+
+ g_dbus_remove_watch(adapter->conn, endpoint->watch);
+ g_free(endpoint->capabilities);
+ g_free(endpoint->sender);
+ g_free(endpoint->path);
+ g_free(endpoint->uuid);
+ g_free(endpoint);
+}
+
+static void media_endpoint_exit(DBusConnection *connection, void *user_data)
+{
+ struct media_endpoint *endpoint = user_data;
+
+ endpoint->watch = 0;
+ media_endpoint_remove(endpoint);
+}
+
+static void headset_state_changed(struct audio_device *dev,
+ headset_state_t old_state,
+ headset_state_t new_state,
+ void *user_data)
+{
+ struct media_endpoint *endpoint = user_data;
+
+ DBG("");
+
+ switch (new_state) {
+ case HEADSET_STATE_DISCONNECTED:
+ if (old_state != HEADSET_STATE_CONNECTING)
+ media_endpoint_clear_configuration(endpoint);
+ case HEADSET_STATE_CONNECTING:
+ break;
+ case HEADSET_STATE_CONNECTED:
+ if (old_state != HEADSET_STATE_PLAY_IN_PROGRESS &&
+ old_state != HEADSET_STATE_PLAYING)
+ media_endpoint_set_configuration(endpoint, dev, NULL,
+ 0, NULL, NULL);
+ break;
+ case HEADSET_STATE_PLAY_IN_PROGRESS:
+ break;
+ case HEADSET_STATE_PLAYING:
+ break;
+ }
+}
+
+static struct media_endpoint *media_endpoint_create(struct media_adapter *adapter,
+ const char *sender,
+ const char *path,
+ const char *uuid,
+ gboolean delay_reporting,
+ uint8_t codec,
+ uint8_t *capabilities,
+ int size)
+{
+ struct media_endpoint *endpoint;
+
+ endpoint = g_new0(struct media_endpoint, 1);
+ endpoint->sender = g_strdup(sender);
+ endpoint->path = g_strdup(path);
+ endpoint->uuid = g_strdup(uuid);
+ endpoint->codec = codec;
+ endpoint->capabilities = g_new(uint8_t, size);
+ memcpy(endpoint->capabilities, capabilities, size);
+ endpoint->size = size;
+ endpoint->adapter = adapter;
+
+ if (g_strcmp0(uuid, A2DP_SOURCE_UUID) == 0) {
+ endpoint->sep = a2dp_add_sep(&adapter->src,
+ AVDTP_SEP_TYPE_SOURCE, codec,
+ delay_reporting, endpoint);
+ if (endpoint->sep == NULL)
+ goto failed;
+ } else if (g_strcmp0(uuid, A2DP_SINK_UUID) == 0) {
+ endpoint->sep = a2dp_add_sep(&adapter->src,
+ AVDTP_SEP_TYPE_SINK, codec,
+ delay_reporting, endpoint);
+ if (endpoint->sep == NULL)
+ goto failed;
+ } else if (g_strcmp0(uuid, HFP_AG_UUID) == 0 ||
+ g_strcmp0(uuid, HSP_AG_UUID) == 0)
+ endpoint->hs_watch = headset_add_state_cb(headset_state_changed,
+ endpoint);
+ else
+ goto failed;
+
+ endpoint->watch = g_dbus_add_disconnect_watch(adapter->conn, sender,
+ media_endpoint_exit, endpoint,
+ NULL);
+
+ adapter->endpoints = g_slist_append(adapter->endpoints, endpoint);
+ info("Endpoint registered: sender=%s path=%s", sender, path);
+
+ return endpoint;
+
+failed:
+ g_free(endpoint);
+ return NULL;
+}
+
+static struct media_endpoint *media_adapter_find_endpoint(
+ struct media_adapter *adapter,
+ const char *sender,
+ const char *path,
+ const char *uuid)
+{
+ GSList *l;
+
+ for (l = adapter->endpoints; l; l = l->next) {
+ struct media_endpoint *endpoint = l->data;
+
+ if (sender && g_strcmp0(endpoint->sender, sender) != 0)
+ continue;
+
+ if (path && g_strcmp0(endpoint->path, path) != 0)
+ continue;
+
+ if (uuid && g_strcmp0(endpoint->uuid, uuid) != 0)
+ continue;
+
+ return endpoint;
+ }
+
+ return NULL;
+}
+
+const char *media_endpoint_get_sender(struct media_endpoint *endpoint)
+{
+ return endpoint->sender;
+}
+
+static int parse_properties(DBusMessageIter *props, const char **uuid,
+ gboolean *delay_reporting, uint8_t *codec,
+ uint8_t **capabilities, int *size)
+{
+ while (dbus_message_iter_get_arg_type(props) == DBUS_TYPE_DICT_ENTRY) {
+ const char *key;
+ DBusMessageIter value, entry;
+ int var;
+
+ dbus_message_iter_recurse(props, &entry);
+ dbus_message_iter_get_basic(&entry, &key);
+
+ dbus_message_iter_next(&entry);
+ dbus_message_iter_recurse(&entry, &value);
+
+ var = dbus_message_iter_get_arg_type(&value);
+ if (strcasecmp(key, "UUID") == 0) {
+ if (var != DBUS_TYPE_STRING)
+ return -EINVAL;
+ dbus_message_iter_get_basic(&value, uuid);
+ } else if (strcasecmp(key, "Codec") == 0) {
+ if (var != DBUS_TYPE_BYTE)
+ return -EINVAL;
+ dbus_message_iter_get_basic(&value, codec);
+ } else if (strcasecmp(key, "DelayReporting") == 0) {
+ if (var != DBUS_TYPE_BOOLEAN)
+ return -EINVAL;
+ dbus_message_iter_get_basic(&value, delay_reporting);
+ } else if (strcasecmp(key, "Capabilities") == 0) {
+ DBusMessageIter array;
+
+ if (var != DBUS_TYPE_ARRAY)
+ return -EINVAL;
+
+ dbus_message_iter_recurse(&value, &array);
+ dbus_message_iter_get_fixed_array(&array, capabilities,
+ size);
+ }
+
+ dbus_message_iter_next(props);
+ }
+
+ return 0;
+}
+
+static DBusMessage *register_endpoint(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct media_adapter *adapter = data;
+ DBusMessageIter args, props;
+ const char *sender, *path, *uuid = NULL;
+ gboolean delay_reporting;
+ uint8_t codec;
+ uint8_t *capabilities;
+ int size;
+
+ sender = dbus_message_get_sender(msg);
+
+ dbus_message_iter_init(msg, &args);
+
+ dbus_message_iter_get_basic(&args, &path);
+ dbus_message_iter_next(&args);
+
+ if (media_adapter_find_endpoint(adapter, sender, path, NULL) != NULL)
+ return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed",
+ "Endpoint already registered");
+
+ dbus_message_iter_recurse(&args, &props);
+ if (dbus_message_iter_get_arg_type(&props) != DBUS_TYPE_DICT_ENTRY)
+ return g_dbus_create_error(msg, ERROR_INTERFACE
+ ".Failed", "Invalid argument");
+
+ if (parse_properties(&props, &uuid, &delay_reporting, &codec,
+ &capabilities, &size) || uuid == NULL)
+ return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed",
+ "Invalid argument");
+
+ if (media_endpoint_create(adapter, sender, path, uuid, delay_reporting,
+ codec, capabilities, size) == FALSE)
+ return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed",
+ "Invalid argument");
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *unregister_endpoint(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct media_adapter *adapter = data;
+ struct media_endpoint *endpoint;
+ const char *sender, *path;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID))
+ return NULL;
+
+ sender = dbus_message_get_sender(msg);
+
+ endpoint = media_adapter_find_endpoint(adapter, sender, path, NULL);
+ if (endpoint == NULL)
+ return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed",
+ "Endpoint not registered");
+
+ media_endpoint_remove(endpoint);
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static GDBusMethodTable media_methods[] = {
+ { "RegisterEndpoint", "oa{sv}", "", register_endpoint },
+ { "UnregisterEndpoint", "o", "", unregister_endpoint },
+ { },
+};
+
+static void path_free(void *data)
+{
+ struct media_adapter *adapter = data;
+
+ g_slist_foreach(adapter->endpoints, (GFunc) media_endpoint_release,
+ NULL);
+ g_slist_free(adapter->endpoints);
+
+ dbus_connection_unref(adapter->conn);
+
+ adapters = g_slist_remove(adapters, adapter);
+
+ g_free(adapter->path);
+ g_free(adapter);
+}
+
+int media_register(DBusConnection *conn, const char *path, const bdaddr_t *src)
+{
+ struct media_adapter *adapter;
+
+ if (DBUS_TYPE_UNIX_FD < 0)
+ return -EPERM;
+
+ adapter = g_new0(struct media_adapter, 1);
+ adapter->conn = dbus_connection_ref(conn);
+ bacpy(&adapter->src, src);
+ adapter->path = g_strdup(path);
+
+ if (!g_dbus_register_interface(conn, path, MEDIA_INTERFACE,
+ media_methods, NULL, NULL,
+ adapter, path_free)) {
+ error("D-Bus failed to register %s path", path);
+ path_free(adapter);
+ return -1;
+ }
+
+ adapters = g_slist_append(adapters, adapter);
+
+ return 0;
+}
+
+void media_unregister(const char *path)
+{
+ GSList *l;
+
+ for (l = adapters; l; l = l->next) {
+ struct media_adapter *adapter = l->data;
+
+ if (g_strcmp0(path, adapter->path) == 0) {
+ g_dbus_unregister_interface(adapter->conn, path,
+ MEDIA_INTERFACE);
+ return;
+ }
+ }
+}
+
+size_t media_endpoint_get_capabilities(struct media_endpoint *endpoint,
+ uint8_t **capabilities)
+{
+ *capabilities = endpoint->capabilities;
+ return endpoint->size;
+}
+
+static void endpoint_reply(DBusPendingCall *call, void *user_data)
+{
+ struct media_endpoint *endpoint = user_data;
+ struct endpoint_request *request = endpoint->request;
+ DBusMessage *reply;
+ DBusError err;
+ gboolean value;
+ void *ret = NULL;
+ int size = -1;
+
+ /* steal_reply will always return non-NULL since the callback
+ * is only called after a reply has been received */
+ reply = dbus_pending_call_steal_reply(call);
+
+ dbus_error_init(&err);
+ if (dbus_set_error_from_message(&err, reply)) {
+ error("Endpoint replied with an error: %s",
+ err.name);
+
+ /* Clear endpoint configuration in case of NO_REPLY error */
+ if (dbus_error_has_name(&err, DBUS_ERROR_NO_REPLY)) {
+ media_endpoint_clear_configuration(endpoint);
+ dbus_message_unref(reply);
+ dbus_error_free(&err);
+ return;
+ }
+
+ dbus_error_free(&err);
+ goto done;
+ }
+
+ dbus_error_init(&err);
+ if (dbus_message_is_method_call(request->msg, MEDIA_ENDPOINT_INTERFACE,
+ "SelectConfiguration")) {
+ DBusMessageIter args, array;
+ uint8_t *configuration;
+
+ dbus_message_iter_init(reply, &args);
+
+ dbus_message_iter_recurse(&args, &array);
+
+ dbus_message_iter_get_fixed_array(&array, &configuration, &size);
+
+ ret = configuration;
+ goto done;
+ } else if (!dbus_message_get_args(reply, &err, DBUS_TYPE_INVALID)) {
+ error("Wrong reply signature: %s", err.message);
+ dbus_error_free(&err);
+ goto done;
+ }
+
+ size = 1;
+ value = TRUE;
+ ret = &value;
+
+done:
+ dbus_message_unref(reply);
+
+ request->cb(endpoint, ret, size, request->user_data);
+
+ endpoint_request_free(request);
+ endpoint->request = NULL;
+}
+
+static gboolean media_endpoint_async_call(DBusConnection *conn,
+ DBusMessage *msg,
+ struct media_endpoint *endpoint,
+ media_endpoint_cb_t cb,
+ void *user_data)
+{
+ struct endpoint_request *request;
+
+ if (endpoint->request)
+ return FALSE;
+
+ request = g_new0(struct endpoint_request, 1);
+
+ /* Timeout should be less than avdtp request timeout (4 seconds) */
+ if (dbus_connection_send_with_reply(conn, msg, &request->call,
+ REQUEST_TIMEOUT) == FALSE) {
+ error("D-Bus send failed");
+ g_free(request);
+ return FALSE;
+ }
+
+ dbus_pending_call_set_notify(request->call, endpoint_reply, endpoint, NULL);
+
+ request->msg = msg;
+ request->cb = cb;
+ request->user_data = user_data;
+ endpoint->request = request;
+
+ DBG("Calling %s: name = %s path = %s", dbus_message_get_member(msg),
+ dbus_message_get_destination(msg),
+ dbus_message_get_path(msg));
+
+ return TRUE;
+}
+
+gboolean media_endpoint_set_configuration(struct media_endpoint *endpoint,
+ struct audio_device *device,
+ uint8_t *configuration, size_t size,
+ media_endpoint_cb_t cb,
+ void *user_data)
+{
+ DBusConnection *conn;
+ DBusMessage *msg, *reply;
+ DBusError err;
+ const char *path;
+
+ if (endpoint->transport != NULL || endpoint->request != NULL)
+ return FALSE;
+
+ conn = endpoint->adapter->conn;
+
+ endpoint->transport = media_transport_create(conn, endpoint, device,
+ configuration, size);
+ if (endpoint->transport == NULL)
+ return FALSE;
+
+ msg = dbus_message_new_method_call(endpoint->sender, endpoint->path,
+ MEDIA_ENDPOINT_INTERFACE,
+ "SetConfiguration");
+ if (msg == NULL) {
+ error("Couldn't allocate D-Bus message");
+ return FALSE;
+ }
+
+ path = media_transport_get_path(endpoint->transport);
+ dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
+ &configuration, size,
+ DBUS_TYPE_INVALID);
+
+ if (cb != NULL)
+ return media_endpoint_async_call(conn, msg, endpoint, cb, user_data);
+
+ dbus_error_init(&err);
+
+ DBG("Calling %s: name = %s path = %s", dbus_message_get_member(msg),
+ dbus_message_get_destination(msg),
+ dbus_message_get_path(msg));
+
+ /* FIXME: remove once we can reply setconf asynchronously */
+ reply = dbus_connection_send_with_reply_and_block(conn, msg,
+ REQUEST_TIMEOUT, &err);
+
+ dbus_message_unref(msg);
+
+ if (reply) {
+ dbus_message_unref(reply);
+ return TRUE;
+ }
+
+ if (dbus_error_is_set(&err)) {
+ error("Endpoint replied with an error: %s", err.name);
+
+ if (dbus_error_has_name(&err, DBUS_ERROR_NO_REPLY))
+ media_endpoint_clear_configuration(endpoint);
+
+ dbus_error_free(&err);
+ }
+
+ return FALSE;
+}
+
+gboolean media_endpoint_select_configuration(struct media_endpoint *endpoint,
+ uint8_t *capabilities,
+ size_t length,
+ media_endpoint_cb_t cb,
+ void *user_data)
+{
+ DBusConnection *conn;
+ DBusMessage *msg;
+
+ if (endpoint->request != NULL)
+ return FALSE;
+
+ conn = endpoint->adapter->conn;
+
+ msg = dbus_message_new_method_call(endpoint->sender, endpoint->path,
+ MEDIA_ENDPOINT_INTERFACE,
+ "SelectConfiguration");
+ if (msg == NULL) {
+ error("Couldn't allocate D-Bus message");
+ return FALSE;
+ }
+
+ dbus_message_append_args(msg, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
+ &capabilities, length,
+ DBUS_TYPE_INVALID);
+
+ return media_endpoint_async_call(conn, msg, endpoint, cb, user_data);
+}
+
+void media_endpoint_clear_configuration(struct media_endpoint *endpoint)
+{
+ DBusConnection *conn;
+ DBusMessage *msg;
+
+ if (endpoint->transport == NULL)
+ return;
+
+ if (endpoint->request) {
+ endpoint_request_free(endpoint->request);
+ endpoint->request = NULL;
+ }
+
+ conn = endpoint->adapter->conn;
+
+ media_transport_remove(endpoint->transport);
+ endpoint->transport = NULL;
+
+ msg = dbus_message_new_method_call(endpoint->sender, endpoint->path,
+ MEDIA_ENDPOINT_INTERFACE,
+ "ClearConfiguration");
+ if (msg == NULL) {
+ error("Couldn't allocate D-Bus message");
+ return;
+ }
+
+ g_dbus_send_message(conn, msg);
+}
+
+void media_endpoint_release(struct media_endpoint *endpoint)
+{
+ DBusMessage *msg;
+
+ DBG("sender=%s path=%s", endpoint->sender, endpoint->path);
+
+ /* already exit */
+ if (endpoint->watch == 0)
+ return;
+
+ msg = dbus_message_new_method_call(endpoint->sender, endpoint->path,
+ MEDIA_ENDPOINT_INTERFACE,
+ "Release");
+ if (msg == NULL) {
+ error("Couldn't allocate D-Bus message");
+ return;
+ }
+
+ g_dbus_send_message(endpoint->adapter->conn, msg);
+
+ media_endpoint_remove(endpoint);
+}
+
+struct a2dp_sep *media_endpoint_get_sep(struct media_endpoint *endpoint)
+{
+ return endpoint->sep;
+}
+
+const char *media_endpoint_get_uuid(struct media_endpoint *endpoint)
+{
+ return endpoint->uuid;
+}
+
+uint8_t media_endpoint_get_codec(struct media_endpoint *endpoint)
+{
+ return endpoint->codec;
+}
diff --git a/audio/media.h b/audio/media.h
new file mode 100644
index 0000000..f6d144c
--- /dev/null
+++ b/audio/media.h
@@ -0,0 +1,52 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2007 Nokia Corporation
+ * Copyright (C) 2004-2009 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+struct media_endpoint;
+
+typedef void (*media_endpoint_cb_t) (struct media_endpoint *endpoint,
+ void *ret, int size, void *user_data);
+
+int media_register(DBusConnection *conn, const char *path, const bdaddr_t *src);
+void media_unregister(const char *path);
+
+const char *media_endpoint_get_sender(struct media_endpoint *endpoint);
+
+size_t media_endpoint_get_capabilities(struct media_endpoint *endpoint,
+ uint8_t **capabilities);
+gboolean media_endpoint_set_configuration(struct media_endpoint *endpoint,
+ struct audio_device *device,
+ uint8_t *configuration, size_t size,
+ media_endpoint_cb_t cb,
+ void *user_data);
+gboolean media_endpoint_select_configuration(struct media_endpoint *endpoint,
+ uint8_t *capabilities,
+ size_t length,
+ media_endpoint_cb_t cb,
+ void *user_data);
+void media_endpoint_clear_configuration(struct media_endpoint *endpoint);
+void media_endpoint_release(struct media_endpoint *endpoint);
+
+struct a2dp_sep *media_endpoint_get_sep(struct media_endpoint *endpoint);
+const char *media_endpoint_get_uuid(struct media_endpoint *endpoint);
+uint8_t media_endpoint_get_codec(struct media_endpoint *endpoint);
diff --git a/audio/sink.c b/audio/sink.c
index f4dce28..67cffee 100644
--- a/audio/sink.c
+++ b/audio/sink.c
@@ -40,6 +40,7 @@
#include "device.h"
#include "avdtp.h"
+#include "media.h"
#include "a2dp.h"
#include "error.h"
#include "sink.h"
@@ -343,146 +344,30 @@ static void stream_setup_complete(struct avdtp *session, struct a2dp_sep *sep,
}
}
-static uint8_t default_bitpool(uint8_t freq, uint8_t mode)
+static void select_complete(struct avdtp *session, struct a2dp_sep *sep,
+ GSList *caps, void *user_data)
{
- switch (freq) {
- case SBC_SAMPLING_FREQ_16000:
- case SBC_SAMPLING_FREQ_32000:
- return 53;
- case SBC_SAMPLING_FREQ_44100:
- switch (mode) {
- case SBC_CHANNEL_MODE_MONO:
- case SBC_CHANNEL_MODE_DUAL_CHANNEL:
- return 31;
- case SBC_CHANNEL_MODE_STEREO:
- case SBC_CHANNEL_MODE_JOINT_STEREO:
- return 53;
- default:
- error("Invalid channel mode %u", mode);
- return 53;
- }
- case SBC_SAMPLING_FREQ_48000:
- switch (mode) {
- case SBC_CHANNEL_MODE_MONO:
- case SBC_CHANNEL_MODE_DUAL_CHANNEL:
- return 29;
- case SBC_CHANNEL_MODE_STEREO:
- case SBC_CHANNEL_MODE_JOINT_STEREO:
- return 51;
- default:
- error("Invalid channel mode %u", mode);
- return 51;
- }
- default:
- error("Invalid sampling freq %u", freq);
- return 53;
- }
-}
-
-static gboolean select_sbc_params(struct sbc_codec_cap *cap,
- struct sbc_codec_cap *supported)
-{
- unsigned int max_bitpool, min_bitpool;
-
- memset(cap, 0, sizeof(struct sbc_codec_cap));
-
- cap->cap.media_type = AVDTP_MEDIA_TYPE_AUDIO;
- cap->cap.media_codec_type = A2DP_CODEC_SBC;
-
- if (supported->frequency & SBC_SAMPLING_FREQ_44100)
- cap->frequency = SBC_SAMPLING_FREQ_44100;
- else if (supported->frequency & SBC_SAMPLING_FREQ_48000)
- cap->frequency = SBC_SAMPLING_FREQ_48000;
- else if (supported->frequency & SBC_SAMPLING_FREQ_32000)
- cap->frequency = SBC_SAMPLING_FREQ_32000;
- else if (supported->frequency & SBC_SAMPLING_FREQ_16000)
- cap->frequency = SBC_SAMPLING_FREQ_16000;
- else {
- error("No supported frequencies");
- return FALSE;
- }
-
- if (supported->channel_mode & SBC_CHANNEL_MODE_JOINT_STEREO)
- cap->channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO;
- else if (supported->channel_mode & SBC_CHANNEL_MODE_STEREO)
- cap->channel_mode = SBC_CHANNEL_MODE_STEREO;
- else if (supported->channel_mode & SBC_CHANNEL_MODE_DUAL_CHANNEL)
- cap->channel_mode = SBC_CHANNEL_MODE_DUAL_CHANNEL;
- else if (supported->channel_mode & SBC_CHANNEL_MODE_MONO)
- cap->channel_mode = SBC_CHANNEL_MODE_MONO;
- else {
- error("No supported channel modes");
- return FALSE;
- }
-
- if (supported->block_length & SBC_BLOCK_LENGTH_16)
- cap->block_length = SBC_BLOCK_LENGTH_16;
- else if (supported->block_length & SBC_BLOCK_LENGTH_12)
- cap->block_length = SBC_BLOCK_LENGTH_12;
- else if (supported->block_length & SBC_BLOCK_LENGTH_8)
- cap->block_length = SBC_BLOCK_LENGTH_8;
- else if (supported->block_length & SBC_BLOCK_LENGTH_4)
- cap->block_length = SBC_BLOCK_LENGTH_4;
- else {
- error("No supported block lengths");
- return FALSE;
- }
-
- if (supported->subbands & SBC_SUBBANDS_8)
- cap->subbands = SBC_SUBBANDS_8;
- else if (supported->subbands & SBC_SUBBANDS_4)
- cap->subbands = SBC_SUBBANDS_4;
- else {
- error("No supported subbands");
- return FALSE;
- }
-
- if (supported->allocation_method & SBC_ALLOCATION_LOUDNESS)
- cap->allocation_method = SBC_ALLOCATION_LOUDNESS;
- else if (supported->allocation_method & SBC_ALLOCATION_SNR)
- cap->allocation_method = SBC_ALLOCATION_SNR;
-
- min_bitpool = MAX(MIN_BITPOOL, supported->min_bitpool);
- max_bitpool = MIN(default_bitpool(cap->frequency, cap->channel_mode),
- supported->max_bitpool);
-
- cap->min_bitpool = min_bitpool;
- cap->max_bitpool = max_bitpool;
-
- return TRUE;
-}
-
-static gboolean select_capabilities(struct avdtp *session,
- struct avdtp_remote_sep *rsep,
- GSList **caps)
-{
- struct avdtp_service_capability *media_transport, *media_codec;
- struct sbc_codec_cap sbc_cap;
-
- media_codec = avdtp_get_codec(rsep);
- if (!media_codec)
- return FALSE;
-
- select_sbc_params(&sbc_cap, (struct sbc_codec_cap *) media_codec->data);
-
- media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT,
- NULL, 0);
-
- *caps = g_slist_append(*caps, media_transport);
+ struct sink *sink = user_data;
+ struct pending_request *pending;
+ int id;
- media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, &sbc_cap,
- sizeof(sbc_cap));
+ pending = sink->connect;
+ pending->id = 0;
- *caps = g_slist_append(*caps, media_codec);
+ id = a2dp_config(session, sep, stream_setup_complete, caps, sink);
+ if (id == 0)
+ goto failed;
- if (avdtp_get_delay_reporting(rsep)) {
- struct avdtp_service_capability *delay_reporting;
- delay_reporting = avdtp_service_cap_new(AVDTP_DELAY_REPORTING,
- NULL, 0);
- *caps = g_slist_append(*caps, delay_reporting);
- }
+ pending->id = id;
+ return;
- return TRUE;
+failed:
+ if (pending->msg)
+ error_failed(pending->conn, pending->msg, "Stream setup failed");
+ pending_request_free(sink->dev, pending);
+ sink->connect = NULL;
+ avdtp_unref(sink->session);
+ sink->session = NULL;
}
static void discovery_complete(struct avdtp *session, GSList *seps, struct avdtp_error *err,
@@ -490,10 +375,6 @@ static void discovery_complete(struct avdtp *session, GSList *seps, struct avdtp
{
struct sink *sink = user_data;
struct pending_request *pending;
- struct avdtp_local_sep *lsep;
- struct avdtp_remote_sep *rsep;
- struct a2dp_sep *sep;
- GSList *caps = NULL;
int id;
pending = sink->connect;
@@ -515,24 +396,8 @@ static void discovery_complete(struct avdtp *session, GSList *seps, struct avdtp
DBG("Discovery complete");
- if (avdtp_get_seps(session, AVDTP_SEP_TYPE_SINK, AVDTP_MEDIA_TYPE_AUDIO,
- A2DP_CODEC_SBC, &lsep, &rsep) < 0) {
- error("No matching ACP and INT SEPs found");
- goto failed;
- }
-
- if (!select_capabilities(session, rsep, &caps)) {
- error("Unable to select remote SEP capabilities");
- goto failed;
- }
-
- sep = a2dp_get(session, rsep);
- if (!sep) {
- error("Unable to get a local source SEP");
- goto failed;
- }
-
- id = a2dp_config(sink->session, sep, stream_setup_complete, caps, sink);
+ id = a2dp_select_capabilities(sink->session, AVDTP_SEP_TYPE_SINK, NULL,
+ select_complete, sink);
if (id == 0)
goto failed;
diff --git a/audio/source.c b/audio/source.c
index 35d8136..01173f5 100644
--- a/audio/source.c
+++ b/audio/source.c
@@ -41,6 +41,7 @@
#include "device.h"
#include "avdtp.h"
+#include "media.h"
#include "a2dp.h"
#include "error.h"
#include "source.h"
@@ -310,140 +311,34 @@ static void stream_setup_complete(struct avdtp *session, struct a2dp_sep *sep,
}
}
-static uint8_t default_bitpool(uint8_t freq, uint8_t mode)
+static void select_complete(struct avdtp *session, struct a2dp_sep *sep,
+ GSList *caps, void *user_data)
{
- switch (freq) {
- case SBC_SAMPLING_FREQ_16000:
- case SBC_SAMPLING_FREQ_32000:
- return 53;
- case SBC_SAMPLING_FREQ_44100:
- switch (mode) {
- case SBC_CHANNEL_MODE_MONO:
- case SBC_CHANNEL_MODE_DUAL_CHANNEL:
- return 31;
- case SBC_CHANNEL_MODE_STEREO:
- case SBC_CHANNEL_MODE_JOINT_STEREO:
- return 53;
- default:
- error("Invalid channel mode %u", mode);
- return 53;
- }
- case SBC_SAMPLING_FREQ_48000:
- switch (mode) {
- case SBC_CHANNEL_MODE_MONO:
- case SBC_CHANNEL_MODE_DUAL_CHANNEL:
- return 29;
- case SBC_CHANNEL_MODE_STEREO:
- case SBC_CHANNEL_MODE_JOINT_STEREO:
- return 51;
- default:
- error("Invalid channel mode %u", mode);
- return 51;
- }
- default:
- error("Invalid sampling freq %u", freq);
- return 53;
- }
-}
-
-static gboolean select_sbc_params(struct sbc_codec_cap *cap,
- struct sbc_codec_cap *supported)
-{
- unsigned int max_bitpool, min_bitpool;
-
- memset(cap, 0, sizeof(struct sbc_codec_cap));
-
- cap->cap.media_type = AVDTP_MEDIA_TYPE_AUDIO;
- cap->cap.media_codec_type = A2DP_CODEC_SBC;
-
- if (supported->frequency & SBC_SAMPLING_FREQ_44100)
- cap->frequency = SBC_SAMPLING_FREQ_44100;
- else if (supported->frequency & SBC_SAMPLING_FREQ_48000)
- cap->frequency = SBC_SAMPLING_FREQ_48000;
- else if (supported->frequency & SBC_SAMPLING_FREQ_32000)
- cap->frequency = SBC_SAMPLING_FREQ_32000;
- else if (supported->frequency & SBC_SAMPLING_FREQ_16000)
- cap->frequency = SBC_SAMPLING_FREQ_16000;
- else {
- error("No supported frequencies");
- return FALSE;
- }
-
- if (supported->channel_mode & SBC_CHANNEL_MODE_JOINT_STEREO)
- cap->channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO;
- else if (supported->channel_mode & SBC_CHANNEL_MODE_STEREO)
- cap->channel_mode = SBC_CHANNEL_MODE_STEREO;
- else if (supported->channel_mode & SBC_CHANNEL_MODE_DUAL_CHANNEL)
- cap->channel_mode = SBC_CHANNEL_MODE_DUAL_CHANNEL;
- else if (supported->channel_mode & SBC_CHANNEL_MODE_MONO)
- cap->channel_mode = SBC_CHANNEL_MODE_MONO;
- else {
- error("No supported channel modes");
- return FALSE;
- }
-
- if (supported->block_length & SBC_BLOCK_LENGTH_16)
- cap->block_length = SBC_BLOCK_LENGTH_16;
- else if (supported->block_length & SBC_BLOCK_LENGTH_12)
- cap->block_length = SBC_BLOCK_LENGTH_12;
- else if (supported->block_length & SBC_BLOCK_LENGTH_8)
- cap->block_length = SBC_BLOCK_LENGTH_8;
- else if (supported->block_length & SBC_BLOCK_LENGTH_4)
- cap->block_length = SBC_BLOCK_LENGTH_4;
- else {
- error("No supported block lengths");
- return FALSE;
- }
-
- if (supported->subbands & SBC_SUBBANDS_8)
- cap->subbands = SBC_SUBBANDS_8;
- else if (supported->subbands & SBC_SUBBANDS_4)
- cap->subbands = SBC_SUBBANDS_4;
- else {
- error("No supported subbands");
- return FALSE;
- }
-
- if (supported->allocation_method & SBC_ALLOCATION_LOUDNESS)
- cap->allocation_method = SBC_ALLOCATION_LOUDNESS;
- else if (supported->allocation_method & SBC_ALLOCATION_SNR)
- cap->allocation_method = SBC_ALLOCATION_SNR;
-
- min_bitpool = MAX(MIN_BITPOOL, supported->min_bitpool);
- max_bitpool = MIN(default_bitpool(cap->frequency, cap->channel_mode),
- supported->max_bitpool);
-
- cap->min_bitpool = min_bitpool;
- cap->max_bitpool = max_bitpool;
-
- return TRUE;
-}
-
-static gboolean select_capabilities(struct avdtp *session,
- struct avdtp_remote_sep *rsep,
- GSList **caps)
-{
- struct avdtp_service_capability *media_transport, *media_codec;
- struct sbc_codec_cap sbc_cap;
-
- media_codec = avdtp_get_codec(rsep);
- if (!media_codec)
- return FALSE;
-
- select_sbc_params(&sbc_cap, (struct sbc_codec_cap *) media_codec->data);
+ struct source *source = user_data;
+ struct pending_request *pending;
+ int id;
- media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT,
- NULL, 0);
+ pending = source->connect;
- *caps = g_slist_append(*caps, media_transport);
+ pending->id = 0;
- media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, &sbc_cap,
- sizeof(sbc_cap));
+ if (caps == NULL)
+ goto failed;
- *caps = g_slist_append(*caps, media_codec);
+ id = a2dp_config(session, sep, stream_setup_complete, caps, source);
+ if (id == 0)
+ goto failed;
+ pending->id = id;
+ return;
- return TRUE;
+failed:
+ if (pending->msg)
+ error_failed(pending->conn, pending->msg, "Stream setup failed");
+ pending_request_free(source->dev, pending);
+ source->connect = NULL;
+ avdtp_unref(source->session);
+ source->session = NULL;
}
static void discovery_complete(struct avdtp *session, GSList *seps, struct avdtp_error *err,
@@ -451,10 +346,6 @@ static void discovery_complete(struct avdtp *session, GSList *seps, struct avdtp
{
struct source *source = user_data;
struct pending_request *pending;
- struct avdtp_local_sep *lsep;
- struct avdtp_remote_sep *rsep;
- struct a2dp_sep *sep;
- GSList *caps = NULL;
int id;
pending = source->connect;
@@ -476,25 +367,8 @@ static void discovery_complete(struct avdtp *session, GSList *seps, struct avdtp
DBG("Discovery complete");
- if (avdtp_get_seps(session, AVDTP_SEP_TYPE_SOURCE, AVDTP_MEDIA_TYPE_AUDIO,
- A2DP_CODEC_SBC, &lsep, &rsep) < 0) {
- error("No matching ACP and INT SEPs found");
- goto failed;
- }
-
- if (!select_capabilities(session, rsep, &caps)) {
- error("Unable to select remote SEP capabilities");
- goto failed;
- }
-
- sep = a2dp_get(session, rsep);
- if (!sep) {
- error("Unable to get a local sink SEP");
- goto failed;
- }
-
- id = a2dp_config(source->session, sep, stream_setup_complete, caps,
- source);
+ id = a2dp_select_capabilities(source->session, AVDTP_SEP_TYPE_SOURCE, NULL,
+ select_complete, source);
if (id == 0)
goto failed;
diff --git a/audio/transport.c b/audio/transport.c
new file mode 100644
index 0000000..c4d78a4
--- /dev/null
+++ b/audio/transport.c
@@ -0,0 +1,671 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2007 Nokia Corporation
+ * Copyright (C) 2004-2009 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+
+#include <glib.h>
+#include <gdbus.h>
+
+#include "../src/adapter.h"
+#include "../src/dbus-common.h"
+
+#include "log.h"
+#include "error.h"
+#include "device.h"
+#include "avdtp.h"
+#include "media.h"
+#include "transport.h"
+#include "a2dp.h"
+#include "headset.h"
+
+#define MEDIA_TRANSPORT_INTERFACE "org.bluez.MediaTransport"
+
+struct acquire_request {
+ DBusMessage *msg;
+ guint id;
+ struct media_renderer *renderer;
+};
+
+struct media_renderer {
+ struct media_transport *transport;
+ struct acquire_request *request;
+ char *name;
+ char *accesstype;
+ guint watch;
+};
+
+struct media_transport {
+ DBusConnection *conn;
+ char *path; /* Transport object path */
+ struct audio_device *device; /* Transport device */
+ struct avdtp *session; /* Signalling session (a2dp only) */
+ struct media_endpoint *endpoint; /* Transport endpoint */
+ GSList *renderers; /* Transport renderers */
+ uint8_t *configuration; /* Transport configuration */
+ int size; /* Transport configuration size */
+ int fd; /* Transport file descriptor */
+ uint16_t imtu; /* Transport input mtu */
+ uint16_t omtu; /* Transport output mtu */
+ uint16_t delay; /* Transport delay (a2dp only) */
+ gboolean nrec; /* Transport nrec (hfp only) */
+ gboolean inband; /* Transport inband ringtone support (hfp only) */
+ gboolean read_lock;
+ gboolean write_lock;
+ gboolean in_use;
+ guint (*resume) (struct media_transport *transport,
+ struct media_renderer *renderer);
+ void (*suspend) (struct media_transport *transport);
+ void (*cancel) (struct media_transport *transport,
+ guint id);
+ void (*get_properties) (
+ struct media_transport *transport,
+ DBusMessageIter *dict);
+ DBusMessage *(*set_property) (
+ struct media_transport *transport,
+ DBusConnection *conn,
+ DBusMessage *msg);
+};
+
+void media_transport_remove(struct media_transport *transport)
+{
+ char *path;
+
+ path = g_strdup(transport->path);
+
+ g_dbus_unregister_interface(transport->conn, path,
+ MEDIA_TRANSPORT_INTERFACE);
+
+ g_free(path);
+}
+
+static void acquire_request_free(struct acquire_request *req)
+{
+ struct media_renderer *renderer = req->renderer;
+ struct media_transport *transport = renderer->transport;
+
+ if (req->id)
+ transport->cancel(renderer->transport, req->id);
+
+ if (req->msg)
+ dbus_message_unref(req->msg);
+
+ renderer->request = NULL;
+ g_free(req);
+}
+
+static gboolean media_transport_release(struct media_transport *transport,
+ const char *accesstype)
+{
+ if (g_strstr_len(accesstype, -1, "r") != NULL) {
+ transport->read_lock = FALSE;
+ DBG("Transport %s: read lock released", transport->path);
+ }
+
+ if (g_strstr_len(accesstype, -1, "w") != NULL) {
+ transport->write_lock = FALSE;
+ DBG("Transport %s: write lock released", transport->path);
+ }
+
+ return TRUE;
+}
+
+static void media_renderer_remove(struct media_renderer *renderer)
+{
+ struct media_transport *transport = renderer->transport;
+
+ media_transport_release(transport, renderer->accesstype);
+
+ if (renderer->watch)
+ g_dbus_remove_watch(transport->conn, renderer->watch);
+
+ if (renderer->request) {
+ DBusMessage *reply = g_dbus_create_error(renderer->request->msg,
+ ERROR_INTERFACE ".Failed",
+ "%s", strerror(EIO));
+
+ g_dbus_send_message(transport->conn, reply);
+
+ acquire_request_free(renderer->request);
+ }
+
+ transport->renderers = g_slist_remove(transport->renderers, renderer);
+
+ /* Suspend if the is no longer any renderer */
+ if (transport->renderers == NULL)
+ transport->suspend(transport);
+
+ DBG("Renderer removed: sender=%s accesstype=%s", renderer->name,
+ renderer->accesstype);
+
+ g_free(renderer->name);
+ g_free(renderer->accesstype);
+ g_free(renderer);
+}
+
+static gboolean media_transport_set_fd(struct media_transport *transport,
+ int fd, uint16_t imtu, uint16_t omtu)
+{
+ if (transport->fd == fd)
+ return TRUE;
+
+ transport->fd = fd;
+ transport->imtu = imtu;
+ transport->omtu = omtu;
+
+ info("%s: fd(%d) ready", transport->path, fd);
+
+ emit_property_changed(transport->conn, transport->path,
+ MEDIA_TRANSPORT_INTERFACE, "IMTU",
+ DBUS_TYPE_UINT16, &transport->imtu);
+
+ emit_property_changed(transport->conn, transport->path,
+ MEDIA_TRANSPORT_INTERFACE, "OMTU",
+ DBUS_TYPE_UINT16, &transport->omtu);
+
+ return TRUE;
+}
+
+static gboolean remove_renderer(gpointer data)
+{
+ media_renderer_remove(data);
+
+ return FALSE;
+}
+
+static void a2dp_resume_complete(struct avdtp *session,
+ struct avdtp_error *err, void *user_data)
+{
+ struct media_renderer *renderer = user_data;
+ struct acquire_request *req = renderer->request;
+ struct media_transport *transport = renderer->transport;
+ struct a2dp_sep *sep = media_endpoint_get_sep(transport->endpoint);
+ struct avdtp_stream *stream;
+ int fd;
+ uint16_t imtu, omtu;
+
+ req->id = 0;
+
+ if (err)
+ goto fail;
+
+ stream = a2dp_sep_get_stream(sep);
+ if (stream == NULL)
+ goto fail;
+
+ if (avdtp_stream_get_transport(stream, &fd, &imtu, &omtu, NULL) ==
+ FALSE)
+ goto fail;
+
+ media_transport_set_fd(transport, fd, imtu, omtu);
+
+ if (g_dbus_send_reply(transport->conn, req->msg,
+ DBUS_TYPE_UNIX_FD, &fd,
+ DBUS_TYPE_INVALID) == FALSE)
+ goto fail;
+
+ return;
+
+fail:
+ /* Let the stream state change before removing the renderer */
+ g_idle_add(remove_renderer, renderer);
+}
+
+static guint resume_a2dp(struct media_transport *transport,
+ struct media_renderer *renderer)
+{
+ struct media_endpoint *endpoint = transport->endpoint;
+ struct audio_device *device = transport->device;
+ struct a2dp_sep *sep = media_endpoint_get_sep(endpoint);
+
+ if (transport->session == NULL) {
+ transport->session = avdtp_get(&device->src, &device->dst);
+ if (transport->session == NULL)
+ return 0;
+ }
+
+ if (transport->in_use == TRUE)
+ goto done;
+
+ transport->in_use = a2dp_sep_lock(sep, transport->session);
+ if (transport->in_use == FALSE)
+ return 0;
+
+done:
+ return a2dp_resume(transport->session, sep, a2dp_resume_complete,
+ renderer);
+}
+
+static void suspend_a2dp(struct media_transport *transport)
+{
+ struct media_endpoint *endpoint = transport->endpoint;
+ struct a2dp_sep *sep = media_endpoint_get_sep(endpoint);
+
+ a2dp_sep_unlock(sep, transport->session);
+ transport->in_use = FALSE;
+}
+
+static void cancel_a2dp(struct media_transport *transport, guint id)
+{
+ a2dp_cancel(transport->device, id);
+}
+
+static void headset_resume_complete(struct audio_device *dev, void *user_data)
+{
+ struct media_renderer *renderer = user_data;
+ struct acquire_request *req = renderer->request;
+ struct media_transport *transport = renderer->transport;
+ int fd;
+
+ req->id = 0;
+
+ if (dev == NULL)
+ goto fail;
+
+ fd = headset_get_sco_fd(dev);
+ if (fd < 0)
+ goto fail;
+
+ media_transport_set_fd(transport, fd, 48, 48);
+
+ if (g_dbus_send_reply(transport->conn, req->msg,
+ DBUS_TYPE_UNIX_FD, &fd,
+ DBUS_TYPE_INVALID) == FALSE)
+ goto fail;
+
+ return;
+
+fail:
+ media_renderer_remove(renderer);
+}
+
+static guint resume_headset(struct media_transport *transport,
+ struct media_renderer *renderer)
+{
+ struct audio_device *device = transport->device;
+
+ if (transport->in_use == TRUE)
+ goto done;
+
+ transport->in_use = headset_lock(device, HEADSET_LOCK_READ |
+ HEADSET_LOCK_WRITE);
+ if (transport->in_use == FALSE)
+ return 0;
+
+done:
+ return headset_request_stream(device, headset_resume_complete,
+ renderer);
+}
+
+static void suspend_headset(struct media_transport *transport)
+{
+ struct audio_device *device = transport->device;
+
+ headset_unlock(device, HEADSET_LOCK_READ | HEADSET_LOCK_WRITE);
+ transport->in_use = FALSE;
+}
+
+static void cancel_headset(struct media_transport *transport, guint id)
+{
+ headset_cancel_stream(transport->device, id);
+}
+
+static void media_renderer_exit(DBusConnection *connection, void *user_data)
+{
+ struct media_renderer *renderer = user_data;
+
+ renderer->watch = 0;
+ if (renderer->request != NULL)
+ acquire_request_free(renderer->request);
+
+ media_renderer_remove(renderer);
+}
+
+static gboolean media_transport_acquire(struct media_transport *transport,
+ const char *accesstype)
+{
+ gboolean read_lock = FALSE, write_lock = FALSE;
+
+ if (g_strstr_len(accesstype, -1, "r") != NULL) {
+ if (transport->read_lock == TRUE)
+ return FALSE;
+ read_lock = TRUE;
+ }
+
+ if (g_strstr_len(accesstype, -1, "w") != NULL) {
+ if (transport->write_lock == TRUE)
+ return FALSE;
+ write_lock = TRUE;
+ }
+
+ /* Check invalid accesstype */
+ if (read_lock == FALSE && write_lock == FALSE)
+ return FALSE;
+
+ if (read_lock) {
+ transport->read_lock = read_lock;
+ DBG("Transport %s: read lock acquired", transport->path);
+ }
+
+ if (write_lock) {
+ transport->write_lock = write_lock;
+ DBG("Transport %s: write lock acquired", transport->path);
+ }
+
+
+ return TRUE;
+}
+
+static struct media_renderer *media_renderer_create(
+ struct media_transport *transport,
+ DBusMessage *msg,
+ const char *accesstype)
+{
+ struct media_renderer *renderer;
+
+ renderer = g_new0(struct media_renderer, 1);
+ renderer->transport = transport;
+ renderer->name = g_strdup(dbus_message_get_sender(msg));
+ renderer->accesstype = g_strdup(accesstype);
+ renderer->watch = g_dbus_add_disconnect_watch(transport->conn,
+ renderer->name,
+ media_renderer_exit,
+ renderer, NULL);
+ transport->renderers = g_slist_append(transport->renderers, renderer);
+
+ DBG("Renderer created: sender=%s accesstype=%s", renderer->name,
+ accesstype);
+
+ return renderer;
+}
+
+static struct media_renderer *media_transport_find_renderer(
+ struct media_transport *transport,
+ const char *name)
+{
+ GSList *l;
+
+ for (l = transport->renderers; l; l = l->next) {
+ struct media_renderer *renderer = l->data;
+
+ if (g_strcmp0(renderer->name, name) == 0)
+ return renderer;
+ }
+
+ return NULL;
+}
+
+static DBusMessage *acquire(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct media_transport *transport = data;
+ struct media_renderer *renderer;
+ struct acquire_request *req;
+ const char *accesstype, *sender;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &accesstype,
+ DBUS_TYPE_INVALID))
+ return NULL;
+
+ sender = dbus_message_get_sender(msg);
+
+ renderer = media_transport_find_renderer(transport, sender);
+ if (renderer != NULL)
+ return g_dbus_create_error(msg, ERROR_INTERFACE
+ ".Failed",
+ "Permission denied");
+
+ if (media_transport_acquire(transport, accesstype) == FALSE)
+ return g_dbus_create_error(msg, ERROR_INTERFACE
+ ".Failed",
+ "Permission denied");
+
+ renderer = media_renderer_create(transport, msg, accesstype);
+ req = g_new0(struct acquire_request, 1);
+ req->msg = dbus_message_ref(msg);
+ req->renderer = renderer;
+ req->id = transport->resume(transport, renderer);
+ renderer->request = req;
+ if (req->id == 0)
+ media_renderer_remove(renderer);
+
+ return NULL;
+}
+
+static DBusMessage *release(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct media_transport *transport = data;
+ struct media_renderer *renderer;
+ const char *accesstype, *sender;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &accesstype,
+ DBUS_TYPE_INVALID))
+ return NULL;
+
+ sender = dbus_message_get_sender(msg);
+
+ renderer = media_transport_find_renderer(transport, sender);
+ if (renderer == NULL)
+ return g_dbus_create_error(msg, ERROR_INTERFACE
+ ".Failed",
+ "Permission denied");
+
+ if (g_strcmp0(renderer->accesstype, accesstype) == 0)
+ media_renderer_remove(renderer);
+ else if (g_strstr_len(renderer->accesstype, -1, accesstype) != NULL) {
+ media_transport_release(transport, accesstype);
+ g_strdelimit(renderer->accesstype, accesstype, ' ');
+ } else
+ return g_dbus_create_error(msg, ERROR_INTERFACE
+ ".Failed",
+ "Permission denied");
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *set_property_a2dp(struct media_transport *transport,
+ DBusConnection *conn, DBusMessage *msg)
+{
+ return NULL;
+}
+
+static DBusMessage *set_property_headset(struct media_transport *transport,
+ DBusConnection *conn, DBusMessage *msg)
+{
+ return NULL;
+}
+
+static DBusMessage *set_property(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct media_transport *transport = data;
+
+ return transport->set_property(transport, conn, msg);
+}
+
+static void get_properties_a2dp(struct media_transport *transport,
+ DBusMessageIter *dict)
+{
+ dict_append_entry(dict, "Delay", DBUS_TYPE_UINT16, &transport->delay);
+}
+
+static void get_properties_headset(struct media_transport *transport,
+ DBusMessageIter *dict)
+{
+ dict_append_entry(dict, "NREC", DBUS_TYPE_BOOLEAN, &transport->nrec);
+
+ dict_append_entry(dict, "InbandRingtone", DBUS_TYPE_BOOLEAN,
+ &transport->inband);
+}
+
+static DBusMessage *get_properties(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct media_transport *transport = data;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusMessageIter dict;
+ const char *uuid;
+ uint8_t codec;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+ /* Device */
+ dict_append_entry(&dict, "Device", DBUS_TYPE_OBJECT_PATH,
+ &transport->device->path);
+
+ dict_append_entry(&dict, "ReadLock", DBUS_TYPE_BOOLEAN,
+ &transport->read_lock);
+
+ dict_append_entry(&dict, "WriteLock", DBUS_TYPE_BOOLEAN,
+ &transport->write_lock);
+
+ dict_append_entry(&dict, "IMTU", DBUS_TYPE_UINT16,
+ &transport->imtu);
+
+ dict_append_entry(&dict, "OMTU", DBUS_TYPE_UINT16,
+ &transport->omtu);
+
+ uuid = media_endpoint_get_uuid(transport->endpoint);
+ dict_append_entry(&dict, "UUID", DBUS_TYPE_STRING, &uuid);
+
+ codec = media_endpoint_get_codec(transport->endpoint);
+ dict_append_entry(&dict, "Codec", DBUS_TYPE_BYTE, &codec);
+
+ dict_append_array(&dict, "Configuration", DBUS_TYPE_BYTE,
+ &transport->configuration, transport->size);
+
+ if (transport->get_properties)
+ transport->get_properties(transport, &dict);
+
+ dbus_message_iter_close_container(&iter, &dict);
+
+ return reply;
+}
+
+static GDBusMethodTable transport_methods[] = {
+ { "GetProperties", "", "a{sv}", get_properties },
+ { "Acquire", "s", "h", acquire,
+ G_DBUS_METHOD_FLAG_ASYNC},
+ { "Release", "s", "", release },
+ { "SetProperty", "sv", "", set_property },
+ { },
+};
+
+static GDBusSignalTable transport_signals[] = {
+ { "PropertyChanged", "sv" },
+ { }
+};
+
+static void media_transport_free(void *data)
+{
+ struct media_transport *transport = data;
+
+ g_slist_foreach(transport->renderers, (GFunc) media_renderer_remove,
+ NULL);
+ g_slist_free(transport->renderers);
+
+ if (transport->session)
+ avdtp_unref(transport->session);
+
+ if (transport->conn)
+ dbus_connection_unref(transport->conn);
+
+ g_free(transport->configuration);
+ g_free(transport->path);
+ g_free(transport);
+}
+
+struct media_transport *media_transport_create(DBusConnection *conn,
+ struct media_endpoint *endpoint,
+ struct audio_device *device,
+ uint8_t *configuration,
+ size_t size)
+{
+ struct media_transport *transport;
+ const char *uuid;
+ static int fd = 0;
+
+ transport = g_new0(struct media_transport, 1);
+ transport->conn = dbus_connection_ref(conn);
+ transport->device = device;
+ transport->endpoint = endpoint;
+ transport->configuration = g_new(uint8_t, size);
+ memcpy(transport->configuration, configuration, size);
+ transport->size = size;
+ transport->path = g_strdup_printf("%s/fd%d", device->path, fd++);
+ transport->fd = -1;
+
+ uuid = media_endpoint_get_uuid(endpoint);
+ if (g_strcmp0(uuid, A2DP_SOURCE_UUID) == 0 ||
+ g_strcmp0(uuid, A2DP_SINK_UUID) == 0) {
+ transport->resume = resume_a2dp;
+ transport->suspend = suspend_a2dp;
+ transport->cancel = cancel_a2dp;
+ transport->get_properties = get_properties_a2dp;
+ transport->set_property = set_property_a2dp;
+ } else if (g_strcmp0(uuid, HFP_AG_UUID) == 0 ||
+ g_strcmp0(uuid, HSP_AG_UUID) == 0) {
+ transport->resume = resume_headset;
+ transport->suspend = suspend_headset;
+ transport->cancel = cancel_headset;
+ transport->get_properties = get_properties_headset;
+ transport->set_property = set_property_headset;
+ } else
+ goto fail;
+
+ if (g_dbus_register_interface(transport->conn, transport->path,
+ MEDIA_TRANSPORT_INTERFACE,
+ transport_methods, transport_signals, NULL,
+ transport, media_transport_free) == FALSE) {
+ error("Could not register transport %s", transport->path);
+ goto fail;
+ }
+
+ return transport;
+
+fail:
+ media_transport_free(transport);
+ return NULL;
+}
+
+const char *media_transport_get_path(struct media_transport *transport)
+{
+ return transport->path;
+}
diff --git a/audio/transport.h b/audio/transport.h
new file mode 100644
index 0000000..a7594f0
--- /dev/null
+++ b/audio/transport.h
@@ -0,0 +1,34 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2007 Nokia Corporation
+ * Copyright (C) 2004-2009 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+struct media_transport;
+
+struct media_transport *media_transport_create(DBusConnection *conn,
+ struct media_endpoint *endpoint,
+ struct audio_device *device,
+ uint8_t *configuration,
+ size_t size);
+
+void media_transport_remove(struct media_transport *transport);
+const char *media_transport_get_path(struct media_transport *transport);
diff --git a/audio/unix.c b/audio/unix.c
index cf2704c..ad822bd 100644
--- a/audio/unix.c
+++ b/audio/unix.c
@@ -44,6 +44,7 @@
#include "device.h"
#include "manager.h"
#include "avdtp.h"
+#include "media.h"
#include "a2dp.h"
#include "headset.h"
#include "sink.h"
--
1.7.1
^ permalink raw reply related
* [PATCH 05/10] Add simple-endpoint test script
From: Luiz Augusto von Dentz @ 2010-09-13 14:15 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1284387337-18822-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.dentz-von@nokia.com>
---
test/simple-endpoint | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 126 insertions(+), 0 deletions(-)
create mode 100755 test/simple-endpoint
diff --git a/test/simple-endpoint b/test/simple-endpoint
new file mode 100755
index 0000000..e09a528
--- /dev/null
+++ b/test/simple-endpoint
@@ -0,0 +1,126 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+import dbus.service
+import dbus.mainloop.glib
+import gobject
+
+A2DP_SOURCE_UUID = "0000110A-0000-1000-8000-00805F9B34FB"
+A2DP_SINK_UUID = "0000110B-0000-1000-8000-00805F9B34FB"
+HFP_AG_UUID = "0000111F-0000-1000-8000-00805F9B34FB"
+HSP_AG_UUID = "00001112-0000-1000-8000-00805F9B34FB"
+
+SBC_CODEC = dbus.Byte(0x00)
+#Channel Modes: Mono DualChannel Stereo JointStereo
+#Frequencies: 16Khz 32Khz 44.1Khz 48Khz
+#Subbands: 4 8
+#Blocks: 4 8 12 16
+#Bitpool Range: 2-64
+SBC_CAPABILITIES = dbus.Array([dbus.Byte(0xff), dbus.Byte(0xff), dbus.Byte(2), dbus.Byte(64)])
+# JointStereo 44.1Khz Subbands: Blocks: 16 Bitpool Range: 2-32
+SBC_CONFIGURATION = dbus.Array([dbus.Byte(0x21), dbus.Byte(0x15), dbus.Byte(2), dbus.Byte(32)])
+
+MP3_CODEC = dbus.Byte(0x01)
+#Channel Modes: Mono DualChannel Stereo JointStereo
+#Frequencies: 32Khz 44.1Khz 48Khz
+#CRC: YES
+#Layer: 3
+#Bit Rate: All except Free format
+#VBR: Yes
+#Payload Format: RFC-2250
+MP3_CAPABILITIES = dbus.Array([dbus.Byte(0x3f), dbus.Byte(0x07), dbus.Byte(0xff), dbus.Byte(0xfe)])
+# JointStereo 44.1Khz Layer: 3 Bit Rate: VBR Format: RFC-2250
+MP3_CONFIGURATION = dbus.Array([dbus.Byte(0x21), dbus.Byte(0x02), dbus.Byte(0x00), dbus.Byte(0x80)])
+
+PCM_CODEC = dbus.Byte(0x00)
+PCM_CONFIGURATION = dbus.Array([], signature="ay")
+
+class Rejected(dbus.DBusException):
+ _dbus_error_name = "org.bluez.Error.Rejected"
+
+class Endpoint(dbus.service.Object):
+ exit_on_release = True
+ configuration = SBC_CONFIGURATION
+
+ def set_exit_on_release(self, exit_on_release):
+ self.exit_on_release = exit_on_release
+
+ def default_configuration(self, configuration):
+ self.configuration = configuration
+
+ @dbus.service.method("org.bluez.MediaEndpoint",
+ in_signature="", out_signature="")
+ def Release(self):
+ print "Release"
+ if self.exit_on_release:
+ mainloop.quit()
+
+ @dbus.service.method("org.bluez.MediaEndpoint",
+ in_signature="", out_signature="")
+ def ClearConfiguration(self):
+ print "ClearConfiguration"
+
+ @dbus.service.method("org.bluez.MediaEndpoint",
+ in_signature="oay", out_signature="")
+ def SetConfiguration(self, transport, config):
+ print "SetConfiguration (%s, %s)" % (transport, config)
+ return
+
+ @dbus.service.method("org.bluez.MediaEndpoint",
+ in_signature="ay", out_signature="ay")
+ def SelectConfiguration(self, caps):
+ print "SelectConfiguration (%s)" % (caps)
+ return self.configuration
+
+if __name__ == '__main__':
+ dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+ bus = dbus.SystemBus()
+ manager = dbus.Interface(bus.get_object("org.bluez", "/"),
+ "org.bluez.Manager")
+
+ if len(sys.argv) > 1:
+ path = manager.FindAdapter(sys.argv[1])
+ else:
+ path = manager.DefaultAdapter()
+
+ media = dbus.Interface(bus.get_object("org.bluez", path),
+ "org.bluez.Media")
+
+ path = "/test/endpoint"
+ endpoint = Endpoint(bus, path)
+ mainloop = gobject.MainLoop()
+
+ properties = dbus.Dictionary({ "UUID" : A2DP_SOURCE_UUID,
+ "Codec" : SBC_CODEC,
+ "DelayReporting" : True,
+ "Capabilities" : SBC_CAPABILITIES })
+
+ if len(sys.argv) > 2:
+ if sys.argv[2] == "sbcsink":
+ properties = dbus.Dictionary({ "UUID" : A2DP_SINK_UUID,
+ "Codec" : SBC_CODEC,
+ "DelayReporting" : True,
+ "Capabilities" : SBC_CAPABILITIES })
+ if sys.argv[2] == "mp3source":
+ properties = dbus.Dictionary({ "UUID" : A2DP_SOURCE_UUID,
+ "Codec" : MP3_CODEC,
+ "Capabilities" : MP3_CAPABILITIES })
+ endpoint.default_configuration(MP3_CONFIGURATION)
+ if sys.argv[2] == "mp3sink":
+ properties = dbus.Dictionary({ "UUID" : A2DP_SINK_UUID,
+ "Codec" : MP3_CODEC,
+ "Capabilities" : MP3_CAPABILITIES })
+ endpoint.default_configuration(MP3_CONFIGURATION)
+ if sys.argv[2] == "hfpag" or sys.argv[2] == "hspag":
+ properties = dbus.Dictionary({ "UUID" : HFP_AG_UUID,
+ "Codec" : PCM_CODEC,
+ "Capabilities" : PCM_CONFIGURATION })
+ endpoint.default_configuration(dbus.Array([]))
+
+ print properties
+
+ media.RegisterEndpoint(path, properties)
+
+ mainloop.run()
--
1.7.1
^ permalink raw reply related
* [PATCH 04/10] Add support for media transport in gstreamer plugin
From: Luiz Augusto von Dentz @ 2010-09-13 14:15 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1284387337-18822-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.dentz-von@nokia.com>
---
Makefile.am | 4 +-
audio/a2dp-codecs.h | 116 ++++++++++
audio/gsta2dpsink.c | 33 +++-
audio/gsta2dpsink.h | 1 +
audio/gstavdtpsink.c | 609 +++++++++++++++++++++++++++++++++++++++++++++++++-
audio/gstavdtpsink.h | 6 +
6 files changed, 757 insertions(+), 12 deletions(-)
create mode 100644 audio/a2dp-codecs.h
diff --git a/Makefile.am b/Makefile.am
index a8829d9..46f5449 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -307,9 +307,9 @@ audio_libgstbluetooth_la_SOURCES = audio/gstbluetooth.c audio/gstpragma.h \
audio/rtp.h audio/ipc.h audio/ipc.c
audio_libgstbluetooth_la_LDFLAGS = -module -avoid-version
audio_libgstbluetooth_la_LIBADD = sbc/libsbc.la lib/libbluetooth.la \
- @GSTREAMER_LIBS@ -lgstaudio-0.10 -lgstrtp-0.10
+ @DBUS_LIBS@ @GSTREAMER_LIBS@ -lgstaudio-0.10 -lgstrtp-0.10
audio_libgstbluetooth_la_CFLAGS = -fvisibility=hidden -fno-strict-aliasing \
- $(AM_CFLAGS) @GSTREAMER_CFLAGS@
+ $(AM_CFLAGS) @DBUS_CFLAGS@ @GSTREAMER_CFLAGS@
endif
endif
diff --git a/audio/a2dp-codecs.h b/audio/a2dp-codecs.h
new file mode 100644
index 0000000..e44634e
--- /dev/null
+++ b/audio/a2dp-codecs.h
@@ -0,0 +1,116 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * 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
+ *
+ */
+
+#define A2DP_CODEC_SBC 0x00
+#define A2DP_CODEC_MPEG12 0x01
+#define A2DP_CODEC_MPEG24 0x02
+#define A2DP_CODEC_ATRAC 0x03
+
+#define SBC_SAMPLING_FREQ_16000 (1 << 3)
+#define SBC_SAMPLING_FREQ_32000 (1 << 2)
+#define SBC_SAMPLING_FREQ_44100 (1 << 1)
+#define SBC_SAMPLING_FREQ_48000 1
+
+#define SBC_CHANNEL_MODE_MONO (1 << 3)
+#define SBC_CHANNEL_MODE_DUAL_CHANNEL (1 << 2)
+#define SBC_CHANNEL_MODE_STEREO (1 << 1)
+#define SBC_CHANNEL_MODE_JOINT_STEREO 1
+
+#define SBC_BLOCK_LENGTH_4 (1 << 3)
+#define SBC_BLOCK_LENGTH_8 (1 << 2)
+#define SBC_BLOCK_LENGTH_12 (1 << 1)
+#define SBC_BLOCK_LENGTH_16 1
+
+#define SBC_SUBBANDS_4 (1 << 1)
+#define SBC_SUBBANDS_8 1
+
+#define SBC_ALLOCATION_SNR (1 << 1)
+#define SBC_ALLOCATION_LOUDNESS 1
+
+#define MPEG_CHANNEL_MODE_MONO (1 << 3)
+#define MPEG_CHANNEL_MODE_DUAL_CHANNEL (1 << 2)
+#define MPEG_CHANNEL_MODE_STEREO (1 << 1)
+#define MPEG_CHANNEL_MODE_JOINT_STEREO 1
+
+#define MPEG_LAYER_MP1 (1 << 2)
+#define MPEG_LAYER_MP2 (1 << 1)
+#define MPEG_LAYER_MP3 1
+
+#define MPEG_SAMPLING_FREQ_16000 (1 << 5)
+#define MPEG_SAMPLING_FREQ_22050 (1 << 4)
+#define MPEG_SAMPLING_FREQ_24000 (1 << 3)
+#define MPEG_SAMPLING_FREQ_32000 (1 << 2)
+#define MPEG_SAMPLING_FREQ_44100 (1 << 1)
+#define MPEG_SAMPLING_FREQ_48000 1
+
+#define MAX_BITPOOL 64
+#define MIN_BITPOOL 2
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+typedef struct {
+ uint8_t channel_mode:4;
+ uint8_t frequency:4;
+ uint8_t allocation_method:2;
+ uint8_t subbands:2;
+ uint8_t block_length:4;
+ uint8_t min_bitpool;
+ uint8_t max_bitpool;
+} __attribute__ ((packed)) a2dp_sbc_t;
+
+typedef struct {
+ uint8_t channel_mode:4;
+ uint8_t crc:1;
+ uint8_t layer:3;
+ uint8_t frequency:6;
+ uint8_t mpf:1;
+ uint8_t rfa:1;
+ uint16_t bitrate;
+} __attribute__ ((packed)) a2dp_mpeg_t;
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+typedef struct {
+ uint8_t frequency:4;
+ uint8_t channel_mode:4;
+ uint8_t block_length:4;
+ uint8_t subbands:2;
+ uint8_t allocation_method:2;
+ uint8_t min_bitpool;
+ uint8_t max_bitpool;
+} __attribute__ ((packed)) a2dp_sbc_t;
+
+typedef struct {
+ uint8_t layer:3;
+ uint8_t crc:1;
+ uint8_t channel_mode:4;
+ uint8_t rfa:1;
+ uint8_t mpf:1;
+ uint8_t frequency:6;
+ uint16_t bitrate;
+} __attribute__ ((packed)) a2dp_mpeg_t;
+
+#else
+#error "Unknown byte order"
+#endif
diff --git a/audio/gsta2dpsink.c b/audio/gsta2dpsink.c
index 492fc63..ac63424 100644
--- a/audio/gsta2dpsink.c
+++ b/audio/gsta2dpsink.c
@@ -44,7 +44,8 @@ GST_DEBUG_CATEGORY_STATIC(gst_a2dp_sink_debug);
enum {
PROP_0,
PROP_DEVICE,
- PROP_AUTOCONNECT
+ PROP_AUTOCONNECT,
+ PROP_TRANSPORT
};
GST_BOILERPLATE(GstA2dpSink, gst_a2dp_sink, GstBin, GST_TYPE_BIN);
@@ -175,6 +176,16 @@ static void gst_a2dp_sink_set_property(GObject *object, guint prop_id,
self->device = g_value_dup_string(value);
break;
+ case PROP_TRANSPORT:
+ if (self->sink != NULL)
+ gst_avdtp_sink_set_transport(self->sink,
+ g_value_get_string(value));
+
+ if (self->transport != NULL)
+ g_free(self->transport);
+ self->transport = g_value_dup_string(value);
+ break;
+
case PROP_AUTOCONNECT:
self->autoconnect = g_value_get_boolean(value);
@@ -193,7 +204,7 @@ static void gst_a2dp_sink_get_property(GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec)
{
GstA2dpSink *self = GST_A2DP_SINK(object);
- gchar *device;
+ gchar *device, *transport;
switch (prop_id) {
case PROP_DEVICE:
@@ -210,6 +221,13 @@ static void gst_a2dp_sink_get_property(GObject *object, guint prop_id,
g_value_set_boolean(value, self->autoconnect);
break;
+ case PROP_TRANSPORT:
+ if (self->sink != NULL) {
+ transport = gst_avdtp_sink_get_transport(self->sink);
+ if (transport != NULL)
+ g_value_take_string(value, transport);
+ }
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
@@ -285,6 +303,10 @@ static GstStateChangeReturn gst_a2dp_sink_change_state(GstElement *element,
gst_avdtp_sink_set_device(self->sink,
self->device);
+ if (self->transport != NULL)
+ gst_avdtp_sink_set_transport(self->sink,
+ self->transport);
+
g_object_set(G_OBJECT(self->sink), "auto-connect",
self->autoconnect, NULL);
@@ -365,6 +387,11 @@ static void gst_a2dp_sink_class_init(GstA2dpSinkClass *klass)
"Automatically attempt to connect to device",
DEFAULT_AUTOCONNECT, G_PARAM_READWRITE));
+ g_object_class_install_property(object_class, PROP_TRANSPORT,
+ g_param_spec_string("transport", "Transport",
+ "Use configured transport",
+ NULL, G_PARAM_READWRITE));
+
GST_DEBUG_CATEGORY_INIT(gst_a2dp_sink_debug, "a2dpsink", 0,
"A2DP sink element");
}
@@ -437,6 +464,7 @@ static gboolean gst_a2dp_sink_init_avdtp_sink(GstA2dpSink *self)
self->sink = GST_AVDTP_SINK(sink);
self->sink_is_in_bin = TRUE;
g_object_set(G_OBJECT(self->sink), "device", self->device, NULL);
+ g_object_set(G_OBJECT(self->sink), "transport", self->transport, NULL);
gst_element_set_state(sink, GST_STATE_PAUSED);
@@ -674,6 +702,7 @@ static void gst_a2dp_sink_init(GstA2dpSink *self,
self->fakesink = NULL;
self->rtp = NULL;
self->device = NULL;
+ self->transport = NULL;
self->autoconnect = DEFAULT_AUTOCONNECT;
self->capsfilter = NULL;
self->newseg_event = NULL;
diff --git a/audio/gsta2dpsink.h b/audio/gsta2dpsink.h
index 3ff4e45..3c4510e 100644
--- a/audio/gsta2dpsink.h
+++ b/audio/gsta2dpsink.h
@@ -53,6 +53,7 @@ struct _GstA2dpSink {
GstElement *fakesink;
gchar *device;
+ gchar *transport;
gboolean autoconnect;
gboolean sink_is_in_bin;
diff --git a/audio/gstavdtpsink.c b/audio/gstavdtpsink.c
index b8c8832..95c4811 100644
--- a/audio/gstavdtpsink.c
+++ b/audio/gstavdtpsink.c
@@ -37,8 +37,11 @@
#include <gst/rtp/gstrtpbuffer.h>
+#include <dbus/dbus.h>
+
#include "ipc.h"
#include "rtp.h"
+#include "a2dp-codecs.h"
#include "gstpragma.h"
#include "gstavdtpsink.h"
@@ -61,11 +64,20 @@ GST_DEBUG_CATEGORY_STATIC(avdtp_sink_debug);
g_mutex_unlock(s->sink_lock); \
} G_STMT_END
+#ifndef DBUS_TYPE_UNIX_FD
+#define DBUS_TYPE_UNIX_FD -1
+#endif
struct bluetooth_data {
struct bt_get_capabilities_rsp *caps; /* Bluetooth device caps */
guint link_mtu;
+ DBusConnection *conn;
+ guint8 codec; /* Bluetooth transport configuration */
+ gchar *uuid;
+ guint8 *config;
+ gint config_size;
+
gchar buffer[BUFFER_SIZE]; /* Codec transfer buffer */
};
@@ -75,7 +87,8 @@ struct bluetooth_data {
enum {
PROP_0,
PROP_DEVICE,
- PROP_AUTOCONNECT
+ PROP_AUTOCONNECT,
+ PROP_TRANSPORT
};
GST_BOILERPLATE(GstAvdtpSink, gst_avdtp_sink, GstBaseSink,
@@ -127,6 +140,23 @@ static void gst_avdtp_sink_base_init(gpointer g_class)
gst_element_class_set_details(element_class, &avdtp_sink_details);
}
+static void gst_avdtp_sink_transport_release(GstAvdtpSink *self)
+{
+ DBusMessage *msg;
+ const char *access_type = "w";
+
+ msg = dbus_message_new_method_call("org.bluez", self->transport,
+ "org.bluez.MediaTransport",
+ "Release");
+
+ dbus_message_append_args(msg, DBUS_TYPE_STRING, &access_type,
+ DBUS_TYPE_INVALID);
+
+ dbus_connection_send(self->data->conn, msg, NULL);
+
+ dbus_message_unref(msg);
+}
+
static gboolean gst_avdtp_sink_stop(GstBaseSink *basesink)
{
GstAvdtpSink *self = GST_AVDTP_SINK(basesink);
@@ -151,6 +181,10 @@ static gboolean gst_avdtp_sink_stop(GstBaseSink *basesink)
}
if (self->data) {
+ if (self->transport)
+ gst_avdtp_sink_transport_release(self);
+ if (self->data->conn)
+ dbus_connection_unref(self->data->conn);
g_free(self->data);
self->data = NULL;
}
@@ -178,6 +212,9 @@ static void gst_avdtp_sink_finalize(GObject *object)
if (self->device)
g_free(self->device);
+ if (self->transport)
+ g_free(self->transport);
+
g_mutex_free(self->sink_lock);
G_OBJECT_CLASS(parent_class)->finalize(object);
@@ -198,6 +235,13 @@ static void gst_avdtp_sink_set_property(GObject *object, guint prop_id,
case PROP_AUTOCONNECT:
sink->autoconnect = g_value_get_boolean(value);
break;
+
+ case PROP_TRANSPORT:
+ if (sink->transport)
+ g_free(sink->transport);
+ sink->transport = g_value_dup_string(value);
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
@@ -217,6 +261,11 @@ static void gst_avdtp_sink_get_property(GObject *object, guint prop_id,
case PROP_AUTOCONNECT:
g_value_set_boolean(value, sink->autoconnect);
break;
+
+ case PROP_TRANSPORT:
+ g_value_set_string(value, sink->transport);
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
@@ -370,6 +419,10 @@ static gboolean gst_avdtp_sink_conf_recv_stream_fd(
GIOFlags flags;
gsize read;
+ /* Proceed if stream was already acquired */
+ if (self->stream != NULL)
+ goto proceed;
+
ret = gst_avdtp_sink_bluetooth_recvmsg_fd(self);
if (ret < 0)
return FALSE;
@@ -380,6 +433,7 @@ static gboolean gst_avdtp_sink_conf_recv_stream_fd(
return FALSE;
}
+proceed:
/* set stream socket to nonblock */
GST_LOG_OBJECT(self, "setting stream socket to nonblock");
flags = g_io_channel_get_flags(self->stream);
@@ -733,6 +787,325 @@ static GstStructure *gst_avdtp_sink_parse_mpeg_caps(
return structure;
}
+static GstStructure *gst_avdtp_sink_parse_sbc_raw(GstAvdtpSink *self)
+{
+ a2dp_sbc_t *sbc = (a2dp_sbc_t *) self->data->config;
+ GstStructure *structure;
+ GValue *value;
+ GValue *list;
+ gboolean mono, stereo;
+
+ structure = gst_structure_empty_new("audio/x-sbc");
+ value = g_value_init(g_new0(GValue, 1), G_TYPE_STRING);
+
+ /* mode */
+ list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST);
+ if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_MONO) {
+ g_value_set_static_string(value, "mono");
+ gst_value_list_prepend_value(list, value);
+ }
+ if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO) {
+ g_value_set_static_string(value, "stereo");
+ gst_value_list_prepend_value(list, value);
+ }
+ if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL) {
+ g_value_set_static_string(value, "dual");
+ gst_value_list_prepend_value(list, value);
+ }
+ if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_JOINT_STEREO) {
+ g_value_set_static_string(value, "joint");
+ gst_value_list_prepend_value(list, value);
+ }
+ g_value_unset(value);
+ if (list) {
+ gst_structure_set_value(structure, "mode", list);
+ g_free(list);
+ list = NULL;
+ }
+
+ /* subbands */
+ list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST);
+ value = g_value_init(value, G_TYPE_INT);
+ if (sbc->subbands & BT_A2DP_SUBBANDS_4) {
+ g_value_set_int(value, 4);
+ gst_value_list_prepend_value(list, value);
+ }
+ if (sbc->subbands & BT_A2DP_SUBBANDS_8) {
+ g_value_set_int(value, 8);
+ gst_value_list_prepend_value(list, value);
+ }
+ g_value_unset(value);
+ if (list) {
+ gst_structure_set_value(structure, "subbands", list);
+ g_free(list);
+ list = NULL;
+ }
+
+ /* blocks */
+ value = g_value_init(value, G_TYPE_INT);
+ list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST);
+ if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_16) {
+ g_value_set_int(value, 16);
+ gst_value_list_prepend_value(list, value);
+ }
+ if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_12) {
+ g_value_set_int(value, 12);
+ gst_value_list_prepend_value(list, value);
+ }
+ if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_8) {
+ g_value_set_int(value, 8);
+ gst_value_list_prepend_value(list, value);
+ }
+ if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_4) {
+ g_value_set_int(value, 4);
+ gst_value_list_prepend_value(list, value);
+ }
+ g_value_unset(value);
+ if (list) {
+ gst_structure_set_value(structure, "blocks", list);
+ g_free(list);
+ list = NULL;
+ }
+
+ /* allocation */
+ g_value_init(value, G_TYPE_STRING);
+ list = g_value_init(g_new0(GValue,1), GST_TYPE_LIST);
+ if (sbc->allocation_method & BT_A2DP_ALLOCATION_LOUDNESS) {
+ g_value_set_static_string(value, "loudness");
+ gst_value_list_prepend_value(list, value);
+ }
+ if (sbc->allocation_method & BT_A2DP_ALLOCATION_SNR) {
+ g_value_set_static_string(value, "snr");
+ gst_value_list_prepend_value(list, value);
+ }
+ g_value_unset(value);
+ if (list) {
+ gst_structure_set_value(structure, "allocation", list);
+ g_free(list);
+ list = NULL;
+ }
+
+ /* rate */
+ g_value_init(value, G_TYPE_INT);
+ list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST);
+ if (sbc->frequency & BT_SBC_SAMPLING_FREQ_48000) {
+ g_value_set_int(value, 48000);
+ gst_value_list_prepend_value(list, value);
+ }
+ if (sbc->frequency & BT_SBC_SAMPLING_FREQ_44100) {
+ g_value_set_int(value, 44100);
+ gst_value_list_prepend_value(list, value);
+ }
+ if (sbc->frequency & BT_SBC_SAMPLING_FREQ_32000) {
+ g_value_set_int(value, 32000);
+ gst_value_list_prepend_value(list, value);
+ }
+ if (sbc->frequency & BT_SBC_SAMPLING_FREQ_16000) {
+ g_value_set_int(value, 16000);
+ gst_value_list_prepend_value(list, value);
+ }
+ g_value_unset(value);
+ if (list) {
+ gst_structure_set_value(structure, "rate", list);
+ g_free(list);
+ list = NULL;
+ }
+
+ /* bitpool */
+ value = g_value_init(value, GST_TYPE_INT_RANGE);
+ gst_value_set_int_range(value,
+ MIN(sbc->min_bitpool, TEMPLATE_MAX_BITPOOL),
+ MIN(sbc->max_bitpool, TEMPLATE_MAX_BITPOOL));
+ gst_structure_set_value(structure, "bitpool", value);
+ g_value_unset(value);
+
+ /* channels */
+ mono = FALSE;
+ stereo = FALSE;
+ if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_MONO)
+ mono = TRUE;
+ if ((sbc->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO) ||
+ (sbc->channel_mode &
+ BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL) ||
+ (sbc->channel_mode &
+ BT_A2DP_CHANNEL_MODE_JOINT_STEREO))
+ stereo = TRUE;
+
+ if (mono && stereo) {
+ g_value_init(value, GST_TYPE_INT_RANGE);
+ gst_value_set_int_range(value, 1, 2);
+ } else {
+ g_value_init(value, G_TYPE_INT);
+ if (mono)
+ g_value_set_int(value, 1);
+ else if (stereo)
+ g_value_set_int(value, 2);
+ else {
+ GST_ERROR_OBJECT(self,
+ "Unexpected number of channels");
+ g_value_set_int(value, 0);
+ }
+ }
+
+ gst_structure_set_value(structure, "channels", value);
+ g_free(value);
+
+ return structure;
+}
+
+static GstStructure *gst_avdtp_sink_parse_mpeg_raw(GstAvdtpSink *self)
+{
+ a2dp_mpeg_t *mpeg = (a2dp_mpeg_t *) self->data->config;
+ GstStructure *structure;
+ GValue *value;
+ GValue *list;
+ gboolean valid_layer = FALSE;
+ gboolean mono, stereo;
+
+ GST_LOG_OBJECT(self, "parsing mpeg caps");
+
+ structure = gst_structure_empty_new("audio/mpeg");
+ value = g_new0(GValue, 1);
+ g_value_init(value, G_TYPE_INT);
+
+ list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST);
+ g_value_set_int(value, 1);
+ gst_value_list_prepend_value(list, value);
+ g_value_set_int(value, 2);
+ gst_value_list_prepend_value(list, value);
+ gst_structure_set_value(structure, "mpegversion", list);
+ g_free(list);
+
+ /* layer */
+ GST_LOG_OBJECT(self, "setting mpeg layer");
+ list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST);
+ if (mpeg->layer & BT_MPEG_LAYER_1) {
+ g_value_set_int(value, 1);
+ gst_value_list_prepend_value(list, value);
+ valid_layer = TRUE;
+ }
+ if (mpeg->layer & BT_MPEG_LAYER_2) {
+ g_value_set_int(value, 2);
+ gst_value_list_prepend_value(list, value);
+ valid_layer = TRUE;
+ }
+ if (mpeg->layer & BT_MPEG_LAYER_3) {
+ g_value_set_int(value, 3);
+ gst_value_list_prepend_value(list, value);
+ valid_layer = TRUE;
+ }
+ if (list) {
+ gst_structure_set_value(structure, "layer", list);
+ g_free(list);
+ list = NULL;
+ }
+
+ if (!valid_layer) {
+ gst_structure_free(structure);
+ g_free(value);
+ return NULL;
+ }
+
+ /* rate */
+ GST_LOG_OBJECT(self, "setting mpeg rate");
+ list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST);
+ if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_48000) {
+ g_value_set_int(value, 48000);
+ gst_value_list_prepend_value(list, value);
+ }
+ if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_44100) {
+ g_value_set_int(value, 44100);
+ gst_value_list_prepend_value(list, value);
+ }
+ if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_32000) {
+ g_value_set_int(value, 32000);
+ gst_value_list_prepend_value(list, value);
+ }
+ if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_24000) {
+ g_value_set_int(value, 24000);
+ gst_value_list_prepend_value(list, value);
+ }
+ if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_22050) {
+ g_value_set_int(value, 22050);
+ gst_value_list_prepend_value(list, value);
+ }
+ if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_16000) {
+ g_value_set_int(value, 16000);
+ gst_value_list_prepend_value(list, value);
+ }
+ g_value_unset(value);
+ if (list) {
+ gst_structure_set_value(structure, "rate", list);
+ g_free(list);
+ list = NULL;
+ }
+
+ /* channels */
+ GST_LOG_OBJECT(self, "setting mpeg channels");
+ mono = FALSE;
+ stereo = FALSE;
+ if (mpeg->channel_mode & BT_A2DP_CHANNEL_MODE_MONO)
+ mono = TRUE;
+ if ((mpeg->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO) ||
+ (mpeg->channel_mode &
+ BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL) ||
+ (mpeg->channel_mode &
+ BT_A2DP_CHANNEL_MODE_JOINT_STEREO))
+ stereo = TRUE;
+
+ if (mono && stereo) {
+ g_value_init(value, GST_TYPE_INT_RANGE);
+ gst_value_set_int_range(value, 1, 2);
+ } else {
+ g_value_init(value, G_TYPE_INT);
+ if (mono)
+ g_value_set_int(value, 1);
+ else if (stereo)
+ g_value_set_int(value, 2);
+ else {
+ GST_ERROR_OBJECT(self,
+ "Unexpected number of channels");
+ g_value_set_int(value, 0);
+ }
+ }
+ gst_structure_set_value(structure, "channels", value);
+ g_free(value);
+
+ return structure;
+}
+
+static gboolean gst_avdtp_sink_update_config(GstAvdtpSink *self)
+{
+ GstStructure *structure;
+ gchar *tmp;
+
+ switch (self->data->codec) {
+ case A2DP_CODEC_SBC:
+ structure = gst_avdtp_sink_parse_sbc_raw(self);
+ break;
+ case A2DP_CODEC_MPEG12:
+ structure = gst_avdtp_sink_parse_mpeg_raw(self);
+ break;
+ default:
+ GST_ERROR_OBJECT(self, "Unsupported configuration");
+ return FALSE;
+ }
+
+ if (structure == NULL)
+ return FALSE;
+
+ if (self->dev_caps != NULL)
+ gst_caps_unref(self->dev_caps);
+
+ self->dev_caps = gst_caps_new_full(structure, NULL);
+
+ tmp = gst_caps_to_string(self->dev_caps);
+ GST_DEBUG_OBJECT(self, "Transport configuration: %s", tmp);
+ g_free(tmp);
+
+ return TRUE;
+}
+
static gboolean gst_avdtp_sink_update_caps(GstAvdtpSink *self)
{
sbc_capabilities_t *sbc;
@@ -743,6 +1116,9 @@ static gboolean gst_avdtp_sink_update_caps(GstAvdtpSink *self)
GST_LOG_OBJECT(self, "updating device caps");
+ if (self->data->config_size != 0 && self->data->config != NULL)
+ return gst_avdtp_sink_update_config(self);
+
sbc = (void *) gst_avdtp_find_caps(self, BT_A2DP_SBC_SINK);
mpeg = (void *) gst_avdtp_find_caps(self, BT_A2DP_MPEG12_SINK);
@@ -870,6 +1246,192 @@ static gboolean gst_avdtp_sink_event(GstBaseSink *basesink,
return TRUE;
}
+static gboolean gst_avdtp_sink_transport_parse_property(GstAvdtpSink *self,
+ DBusMessageIter *i)
+{
+ const char *key;
+ DBusMessageIter variant_i;
+
+ if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_STRING) {
+ GST_ERROR_OBJECT(self, "Property name not a string.");
+ return FALSE;
+ }
+
+ dbus_message_iter_get_basic(i, &key);
+
+ if (!dbus_message_iter_next(i)) {
+ GST_ERROR_OBJECT(self, "Property value missing");
+ return FALSE;
+ }
+
+ if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_VARIANT) {
+ GST_ERROR_OBJECT(self, "Property value not a variant.");
+ return FALSE;
+ }
+
+ dbus_message_iter_recurse(i, &variant_i);
+
+ switch (dbus_message_iter_get_arg_type(&variant_i)) {
+ case DBUS_TYPE_BYTE: {
+ uint8_t value;
+ dbus_message_iter_get_basic(&variant_i, &value);
+
+ if (g_str_equal(key, "Codec") == TRUE)
+ self->data->codec = value;
+
+ break;
+ }
+ case DBUS_TYPE_UINT16: {
+ uint16_t value;
+ dbus_message_iter_get_basic(&variant_i, &value);
+
+ if (g_str_equal(key, "OMTU") == TRUE)
+ self->data->link_mtu = value;
+
+ break;
+ }
+ case DBUS_TYPE_STRING: {
+ const char *value;
+ dbus_message_iter_get_basic(&variant_i, &value);
+
+ if (g_str_equal(key, "UUID") == TRUE) {
+ g_free(self->data->uuid);
+ self->data->uuid = g_strdup(value);
+ }
+
+ break;
+ }
+ case DBUS_TYPE_ARRAY: {
+ DBusMessageIter array_i;
+ char *value;
+ int size;
+
+ dbus_message_iter_recurse(&variant_i, &array_i);
+ dbus_message_iter_get_fixed_array(&array_i, &value, &size);
+
+ if (g_str_equal(key, "Configuration")) {
+ g_free(self->data->config);
+ self->data->config = g_new0(guint8, size);
+ self->data->config_size = size;
+ memcpy(self->data->config, value, size);
+ }
+
+ break;
+ }
+ }
+
+ return TRUE;
+}
+
+static gboolean gst_avdtp_sink_transport_acquire(GstAvdtpSink *self)
+{
+ DBusMessage *msg, *reply;
+ DBusError err;
+ const char *access_type = "w";
+ int fd;
+
+ dbus_error_init(&err);
+
+ if (self->data->conn == NULL)
+ self->data->conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
+
+ msg = dbus_message_new_method_call("org.bluez", self->transport,
+ "org.bluez.MediaTransport",
+ "Acquire");
+
+ dbus_message_append_args(msg, DBUS_TYPE_STRING, &access_type,
+ DBUS_TYPE_INVALID);
+
+ reply = dbus_connection_send_with_reply_and_block(self->data->conn,
+ msg, -1, &err);
+
+ if (dbus_error_is_set(&err))
+ goto fail;
+
+ if (dbus_message_get_args(reply, &err, DBUS_TYPE_UNIX_FD, &fd,
+ DBUS_TYPE_INVALID) == FALSE)
+ goto fail;
+
+ dbus_message_unref(reply);
+
+ self->stream = g_io_channel_unix_new(fd);
+ g_io_channel_set_close_on_unref(self->stream, TRUE);
+ GST_DEBUG_OBJECT(self, "stream_fd=%d", fd);
+
+ return TRUE;
+
+fail:
+ GST_ERROR_OBJECT(self, "Failed to acquire transport stream: %s",
+ err.message);
+
+ dbus_error_free(&err);
+
+ if (reply)
+ dbus_message_unref(msg);
+
+ return FALSE;
+}
+
+static gboolean gst_avdtp_sink_transport_get_properties(GstAvdtpSink *self)
+{
+ DBusMessage *msg, *reply;
+ DBusMessageIter arg_i, ele_i;
+ DBusError err;
+
+ dbus_error_init(&err);
+
+ /* Transport need to be acquire first to make sure the MTUs are
+ available */
+ if (gst_avdtp_sink_transport_acquire(self) == FALSE)
+ return FALSE;
+
+ msg = dbus_message_new_method_call("org.bluez", self->transport,
+ "org.bluez.MediaTransport",
+ "GetProperties");
+ reply = dbus_connection_send_with_reply_and_block(self->data->conn,
+ msg, -1, &err);
+
+ if (dbus_error_is_set(&err) || reply == NULL) {
+ GST_ERROR_OBJECT(self, "Failed to get transport properties: %s",
+ err.message);
+ goto fail;
+ }
+
+ if (!dbus_message_iter_init(reply, &arg_i)) {
+ GST_ERROR_OBJECT(self, "GetProperties reply has no arguments.");
+ goto fail;
+ }
+
+ if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_ARRAY) {
+ GST_ERROR_OBJECT(self, "GetProperties argument is not an array.");
+ goto fail;
+ }
+
+ dbus_message_iter_recurse(&arg_i, &ele_i);
+ while (dbus_message_iter_get_arg_type(&ele_i) != DBUS_TYPE_INVALID) {
+
+ if (dbus_message_iter_get_arg_type(&ele_i) ==
+ DBUS_TYPE_DICT_ENTRY) {
+ DBusMessageIter dict_i;
+
+ dbus_message_iter_recurse(&ele_i, &dict_i);
+
+ gst_avdtp_sink_transport_parse_property(self, &dict_i);
+ }
+
+ if (!dbus_message_iter_next(&ele_i))
+ break;
+ }
+
+ return gst_avdtp_sink_update_caps(self);
+
+fail:
+ dbus_message_unref(msg);
+ dbus_message_unref(reply);
+ return FALSE;
+
+}
+
static gboolean gst_avdtp_sink_start(GstBaseSink *basesink)
{
GstAvdtpSink *self = GST_AVDTP_SINK(basesink);
@@ -878,6 +1440,16 @@ static gboolean gst_avdtp_sink_start(GstBaseSink *basesink)
GST_INFO_OBJECT(self, "start");
+ self->data = g_new0(struct bluetooth_data, 1);
+
+ self->stream = NULL;
+ self->stream_caps = NULL;
+ self->mp3_using_crc = -1;
+ self->channel_mode = -1;
+
+ if (self->transport != NULL)
+ return gst_avdtp_sink_transport_get_properties(self);
+
self->watch_id = 0;
sk = bt_audio_service_open();
@@ -892,13 +1464,6 @@ static gboolean gst_avdtp_sink_start(GstBaseSink *basesink)
self->watch_id = g_io_add_watch(self->server, G_IO_HUP | G_IO_ERR |
G_IO_NVAL, server_callback, self);
- self->data = g_new0(struct bluetooth_data, 1);
-
- self->stream = NULL;
- self->stream_caps = NULL;
- self->mp3_using_crc = -1;
- self->channel_mode = -1;
-
if (!gst_avdtp_sink_get_capabilities(self)) {
GST_ERROR_OBJECT(self, "failed to get capabilities "
"from device");
@@ -920,6 +1485,9 @@ static gboolean gst_avdtp_sink_stream_start(GstAvdtpSink *self)
struct bt_new_stream_ind *ind = (void *) buf;
GIOError io_error;
+ if (self->transport != NULL)
+ return gst_avdtp_sink_conf_recv_stream_fd(self);
+
memset(req, 0, sizeof(buf));
req->h.type = BT_REQUEST;
req->h.name = BT_START_STREAM;
@@ -1051,6 +1619,10 @@ static gboolean gst_avdtp_sink_configure(GstAvdtpSink *self,
GST_DEBUG_OBJECT(self, "configuring device with caps: %s", temp);
g_free(temp);
+ /* Transport already configured */
+ if (self->transport != NULL)
+ return TRUE;
+
structure = gst_caps_get_structure(caps, 0);
if (gst_structure_has_name(structure, "audio/x-sbc"))
@@ -1237,6 +1809,12 @@ static void gst_avdtp_sink_class_init(GstAvdtpSinkClass *klass)
"to device", DEFAULT_AUTOCONNECT,
G_PARAM_READWRITE));
+ g_object_class_install_property(object_class, PROP_TRANSPORT,
+ g_param_spec_string("transport",
+ "Transport",
+ "Use configured transport",
+ NULL, G_PARAM_READWRITE));
+
GST_DEBUG_CATEGORY_INIT(avdtp_sink_debug, "avdtpsink", 0,
"A2DP headset sink element");
}
@@ -1245,6 +1823,7 @@ static void gst_avdtp_sink_init(GstAvdtpSink *self,
GstAvdtpSinkClass *klass)
{
self->device = NULL;
+ self->transport = NULL;
self->data = NULL;
self->stream = NULL;
@@ -1400,11 +1979,25 @@ void gst_avdtp_sink_set_device(GstAvdtpSink *self, const gchar *dev)
self->device = g_strdup(dev);
}
+void gst_avdtp_sink_set_transport(GstAvdtpSink *self, const gchar *trans)
+{
+ if (self->transport != NULL)
+ g_free(self->transport);
+
+ GST_LOG_OBJECT(self, "Setting transport: %s", trans);
+ self->transport = g_strdup(trans);
+}
+
gchar *gst_avdtp_sink_get_device(GstAvdtpSink *self)
{
return g_strdup(self->device);
}
+gchar *gst_avdtp_sink_get_transport(GstAvdtpSink *self)
+{
+ return g_strdup(self->transport);
+}
+
void gst_avdtp_sink_set_crc(GstAvdtpSink *self, gboolean crc)
{
gint new_crc;
diff --git a/audio/gstavdtpsink.h b/audio/gstavdtpsink.h
index eb78248..888169f 100644
--- a/audio/gstavdtpsink.h
+++ b/audio/gstavdtpsink.h
@@ -51,6 +51,7 @@ struct _GstAvdtpSink {
GstBaseSink sink;
gchar *device;
+ gchar *transport;
GIOChannel *stream;
struct bluetooth_data *data;
@@ -86,8 +87,13 @@ guint gst_avdtp_sink_get_link_mtu(GstAvdtpSink *sink);
void gst_avdtp_sink_set_device(GstAvdtpSink *sink,
const gchar* device);
+void gst_avdtp_sink_set_transport(GstAvdtpSink *sink,
+ const gchar *transport);
+
gchar *gst_avdtp_sink_get_device(GstAvdtpSink *sink);
+gchar *gst_avdtp_sink_get_transport(GstAvdtpSink *sink);
+
gboolean gst_avdtp_sink_plugin_init(GstPlugin *plugin);
void gst_avdtp_sink_set_crc(GstAvdtpSink *self, gboolean crc);
--
1.7.1
^ permalink raw reply related
* [PATCH 03/10] Add option to enable/disable unix ipc via audio.conf
From: Luiz Augusto von Dentz @ 2010-09-13 14:15 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1284387337-18822-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto Von Dentz <luiz.dentz-von@nokia.com>
---
audio/main.c | 9 ---------
audio/manager.c | 12 ++++++++++++
audio/manager.h | 1 +
3 files changed, 13 insertions(+), 9 deletions(-)
diff --git a/audio/main.c b/audio/main.c
index 9d316ec..745c307 100644
--- a/audio/main.c
+++ b/audio/main.c
@@ -42,7 +42,6 @@
#include "plugin.h"
#include "log.h"
#include "device.h"
-#include "unix.h"
#include "headset.h"
#include "manager.h"
#include "gateway.h"
@@ -151,11 +150,6 @@ static int audio_init(void)
config = load_config_file(CONFIGDIR "/audio.conf");
- if (unix_init() < 0) {
- error("Unable to setup unix socket");
- goto failed;
- }
-
if (audio_manager_init(connection, config, &enable_sco) < 0)
goto failed;
@@ -174,7 +168,6 @@ static int audio_init(void)
failed:
audio_manager_exit();
- unix_exit();
if (connection) {
dbus_connection_unref(connection);
@@ -194,8 +187,6 @@ static void audio_exit(void)
audio_manager_exit();
- unix_exit();
-
dbus_connection_unref(connection);
}
diff --git a/audio/manager.c b/audio/manager.c
index 3db5987..87e7a2a 100644
--- a/audio/manager.c
+++ b/audio/manager.c
@@ -70,6 +70,7 @@
#include "manager.h"
#include "sdpd.h"
#include "telephony.h"
+#include "unix.h"
typedef enum {
HEADSET = 1 << 0,
@@ -113,6 +114,7 @@ static struct enabled_interfaces enabled = {
.sink = TRUE,
.source = FALSE,
.control = TRUE,
+ .socket = TRUE,
};
static struct audio_adapter *find_adapter(GSList *list,
@@ -1074,6 +1076,8 @@ int audio_manager_init(DBusConnection *conn, GKeyFile *conf,
enabled.source = TRUE;
else if (g_str_equal(list[i], "Control"))
enabled.control = TRUE;
+ else if (g_str_equal(list[i], "Socket"))
+ enabled.socket = TRUE;
}
g_strfreev(list);
@@ -1090,6 +1094,8 @@ int audio_manager_init(DBusConnection *conn, GKeyFile *conf,
enabled.source = FALSE;
else if (g_str_equal(list[i], "Control"))
enabled.control = FALSE;
+ else if (g_str_equal(list[i], "Socket"))
+ enabled.socket = FALSE;
}
g_strfreev(list);
@@ -1117,6 +1123,9 @@ int audio_manager_init(DBusConnection *conn, GKeyFile *conf,
max_connected_headsets = i;
proceed:
+ if (enabled.socket)
+ unix_init();
+
if (enabled.headset) {
telephony_init();
btd_register_adapter_driver(&headset_server_driver);
@@ -1152,6 +1161,9 @@ void audio_manager_exit(void)
config = NULL;
}
+ if (enabled.socket)
+ unix_exit();
+
if (enabled.headset) {
btd_unregister_adapter_driver(&headset_server_driver);
telephony_exit();
diff --git a/audio/manager.h b/audio/manager.h
index 90fe6f0..c79b761 100644
--- a/audio/manager.h
+++ b/audio/manager.h
@@ -29,6 +29,7 @@ struct enabled_interfaces {
gboolean sink;
gboolean source;
gboolean control;
+ gboolean socket;
};
int audio_manager_init(DBusConnection *conn, GKeyFile *config,
--
1.7.1
^ permalink raw reply related
* [PATCH 02/10] Add rule to enabling talking to org.bluez.MediaEndpoint
From: Luiz Augusto von Dentz @ 2010-09-13 14:15 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1284387337-18822-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto Von Dentz <luiz.dentz-von@nokia.com>
---
src/bluetooth.conf | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/src/bluetooth.conf b/src/bluetooth.conf
index 56e7a83..354e5e0 100644
--- a/src/bluetooth.conf
+++ b/src/bluetooth.conf
@@ -12,6 +12,7 @@
<allow send_destination="org.bluez"/>
<allow send_interface="org.bluez.Agent"/>
<allow send_interface="org.bluez.HandsfreeAgent"/>
+ <allow send_interface="org.bluez.MediaEndpoint"/>
</policy>
<policy at_console="true">
--
1.7.1
^ permalink raw reply related
* [PATCH 01/10] Add media API documentation
From: Luiz Augusto von Dentz @ 2010-09-13 14:15 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1284387337-18822-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.dentz-von@nokia.com>
Media API is a replacement for the internal audio IPC which is no longer
necessary as DBus 1.3 and newer are capable of transfering file
descriptors.
---
doc/media-api.txt | 169 +++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 169 insertions(+), 0 deletions(-)
create mode 100644 doc/media-api.txt
diff --git a/doc/media-api.txt b/doc/media-api.txt
new file mode 100644
index 0000000..94fc62f
--- /dev/null
+++ b/doc/media-api.txt
@@ -0,0 +1,169 @@
+BlueZ D-Bus Media API description
+*********************************
+
+Media hierarchy
+===============
+
+Service org.bluez
+Interface org.bluez.Media
+Object path [variable prefix]/{hci0,hci1,...}
+
+Methods void RegisterEndpoint(object endpoint, dict properties)
+
+ Register a local end point to sender, the sender can
+ register as many end points as it likes.
+
+ Note: If the sender disconnects the end points are
+ automatically unregistered.
+
+ possible properties:
+
+ string UUID:
+
+ UUID of the profile which the endpoint
+ is for.
+
+ byte Codec:
+
+ Assigned mumber of codec that the
+ endpoint implements. The values should
+ match the profile specification which
+ is indicated by the UUID.
+
+ array{byte} Capabilities:
+
+ Capabilities blob, it is used as it is
+ so the size and byte order must match.
+
+ void UnregisterEndpoint(object endpoint)
+
+ Unregister sender end point.
+
+MediaEndpoint hierarchy
+=======================
+
+Service unique name
+Interface org.bluez.MediaEndpoint
+Object path freely definable
+
+Methods void SetConfiguration(object transport, array{byte} configuration)
+
+ Set configuration for the transport.
+
+ array{byte} SelectConfiguration(array{byte} capabilities)
+
+ Select preferable configuration from the supported
+ capabilities.
+
+ Returns a configuration which can be used to setup
+ a transport.
+
+ Note: There is no need to cache the selected
+ configuration since on success the configuration is
+ send back as parameter of SetConfiguration.
+
+ void ClearConfiguration()
+
+ Clear any configuration set.
+
+ void Release()
+
+ This method gets called when the service daemon
+ unregisters the endpoint. An endpoint can use it to do
+ cleanup tasks. There is no need to unregister the
+ endpoint, because when this method gets called it has
+ already been unregistered.
+
+MediaTransport hierarchy
+========================
+
+Service org.bluez
+Interface org.bluez.MediaTransport
+Object path [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/fdX
+
+Methods dict GetProperties()
+
+ Returns all properties for the interface. See the
+ properties section for available properties.
+
+ fd Acquire(string accesstype)
+
+ Acquire transport file descriptor.
+
+ possible accesstype:
+
+ "r" : Read only access
+
+ "w" : Write only access
+
+ "rw": Read and write access
+
+ void Release(string accesstype)
+
+ Releases file descriptor.
+
+ void SetProperty(string name, variant value)
+
+ Changes the value of the specified property. Only
+ properties that are listed a read-write can be changed.
+
+ On success this will emit a PropertyChanged signal.
+
+Signals void PropertyChanged(string name, variant value)
+
+ This signal indicates a changed value of the given
+ property.
+
+Properties object Device [readonly]
+
+ Device object which the transport is connected to.
+
+ boolean ReadLock [readonly]
+
+ Transport read lock.
+
+ boolean WriteLock [readonly]
+
+ Transport read lock.
+
+ uint16 IMTU [readonly]
+
+ Transport input MTU.
+
+ uint16 OMTU [readonly]
+
+ Transport output MTU.
+
+ string UUID [readonly]
+
+ UUID of the profile which the transport is for.
+
+ byte Codec [readonly]
+
+ Assigned mumber of codec that the transport support.
+ The values should match the profile specification which
+ is indicated by the UUID.
+
+ array{byte} Configuration [readonly]
+
+ Configuration blob, it is used as it is so the size and
+ byte order must match.
+
+ uint16 Delay [readwrite]
+
+ Optional. Transport delay in 1/10 of milisecond, this
+ property is only writeable when the transport was
+ acquired by the sender.
+
+ boolean NREC [readwrite]
+
+ Optional. Indicates if echo cancelling and noise
+ reduction functions are active in the transport, this
+ property is only writeable when the transport was
+ acquired by the sender.
+
+ boolean InbandRingtone [readwrite]
+
+ Optional. Indicates if the transport support sending
+ ringtones, this property is only writeable when the
+ transport was acquired by the sender.
--
1.7.1
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox