2011-11-22 14:01:22

by Frederic Danis

[permalink] [raw]
Subject: [RFC 0/2] Add org.bluez.Telephony interface

Hello,

I am working on this set of patches.

Replace telephony drivers for HeadSet/HandsFree Profiles by a new DBus
interface (org.bluez.Telephony).
External applications, which should implement AT parsing and telephony
part of HeadSet or HandsFree Profiles, will have to register a telephony
agent using this new interface.


First patch add the new org.bluez.Telephony interface and remove
functions related to AT parsing from audio/headset.c.

Second patch remove the telephony drivers (dummy, maemo5, maemo6
and ofono), functions used by telephony drivers and no more needed
methods of org.bluez.Headset interface.


What I would like to do to continue is:
- move rfcomm servers from audio/manager.c to audio/telephony.c, so
servers are started only when a telephony agent is registered. This
should also replace audio/gateway.c.
- pass MediaTransport object in NewConnection method so telephony agent
will be able to set NREC and ringtone, which can in turn notify Pulse
Audio of property changed.


Any comments appreciated

Regards

Fred

Frédéric Danis (2):
audio: Move tel drivers to DBus interface
audio: Remove tel drivers

Makefile.am | 17 +-
audio/headset.c | 1517 +-------------------------------
audio/headset.h | 7 +-
audio/manager.c | 4 +-
audio/telephony-dummy.c | 433 ---------
audio/telephony-maemo5.c | 2104 --------------------------------------------
audio/telephony-maemo6.c | 2201 ----------------------------------------------
audio/telephony-ofono.c | 1638 ----------------------------------
audio/telephony.c | 494 +++++++++++
audio/telephony.h | 196 +----
audio/transport.c | 10 +
audio/unix.c | 2 +
doc/audio-api.txt | 99 +++
13 files changed, 631 insertions(+), 8091 deletions(-)
delete mode 100644 audio/telephony-dummy.c
delete mode 100644 audio/telephony-maemo5.c
delete mode 100644 audio/telephony-maemo6.c
delete mode 100644 audio/telephony-ofono.c
create mode 100644 audio/telephony.c



2011-11-24 06:46:26

by Denis Kenzior

[permalink] [raw]
Subject: Re: [RFC 0/2] Add org.bluez.Telephony interface

Hi Anderson,

>
> Phone Alert Status has a method to silent the ringer during a incoming
> call. It also allows to change to/from "silent" profile. Alert
> notification allows to enable/disable notifications of incoming
> calls/SMS.
>
> All of this is platform specific, therefore if we are going this route
> for BlueZ 5, we need a more complete telephony interface that allows
> to implement platform specific telephony bits outside BlueZ.
>

These are indeed highly platform specific, moreover, they are UI/UX
specific.

>> or should be implemented into telephony daemon.
>
> Maybe myself or Claudio are not clear, but I think the
> "org.bluez.Telephony" interface name were supposed to mean a generic
> telephony interface for BlueZ (so any Telephony related component
> could use), but for me looks like it is just a HFP/HSP "proxy"
> interface.
>
> In other words, if we wanted to move the Phone Alert Status or Alert
> Notification to e.g. ofono, your interface (as is) would be not
> suitable to connect to a LE socket.

It sounds like moving these to oFono is not a good idea in the first
place, since oFono is not responsible for the UI, ringtone generation,
etc.

The question is whether you want to reserve .Telephony interface for
some LE specific bits (in which case we can use something like
VoiceTelephony or HandsfreeTelephony for the current proposal) or try
not expose the LE bits over D-Bus at all (e.g. using bluez plugins.)

Regards,
-Denis

2011-11-24 15:54:59

by Anderson Lizardo

[permalink] [raw]
Subject: Re: [RFC 0/2] Add org.bluez.Telephony interface

Hi Frederic,

On Thu, Nov 24, 2011 at 11:26 AM, Frederic Danis
<[email protected]> wrote:
>> Besides that, I think your proposed Telephony API is too tied to
>> HFP/HSP. I think it is interesting to have a more generic API so that
>> other telephony related profiles (e.g. LE ones) could reuse it without
>> requiring their own agent based API.
>>
>> Regards,
>
> With this new D-Bus telephony drivers there will be no more need of
> telephony related code into BlueZ, which will move to platform telephony
> daemon.
>
> My understanding for those LE profiles are, as they are related to
> telephony, that they should listen for D-Bus signals (simple way, read only)

Phone Alert Status has a method to silent the ringer during a incoming
call. It also allows to change to/from "silent" profile. Alert
notification allows to enable/disable notifications of incoming
calls/SMS.

All of this is platform specific, therefore if we are going this route
for BlueZ 5, we need a more complete telephony interface that allows
to implement platform specific telephony bits outside BlueZ.

> or should be implemented into telephony daemon.

Maybe myself or Claudio are not clear, but I think the
"org.bluez.Telephony" interface name were supposed to mean a generic
telephony interface for BlueZ (so any Telephony related component
could use), but for me looks like it is just a HFP/HSP "proxy"
interface.

In other words, if we wanted to move the Phone Alert Status or Alert
Notification to e.g. ofono, your interface (as is) would be not
suitable to connect to a LE socket.

Best Regards,
--
Anderson Lizardo
Instituto Nokia de Tecnologia - INdT
Manaus - Brazil

2011-11-24 15:26:30

by Frederic Danis

[permalink] [raw]
Subject: Re: [RFC 0/2] Add org.bluez.Telephony interface

Hello Lizardo,

Le 23/11/2011 12:06, Anderson Lizardo a ?crit :
> Hi Frederic,
>
> On Wed, Nov 23, 2011 at 5:58 AM, Frederic Danis
> <[email protected]> wrote:
>> The new telephony D-Bus interface is just in charge of performing SDP
>> publication and RFComm connection. All Telephony related code is managed by
>> the agent (i.e. oFono).
>
> How t
>
>> I take a look to the Phone Alert Status and Alert Notification and I thought
>> that this should be implemented in the agent, or in a BlueZ service
>> accessible by the agent through D-Bus.
>
> I'm wondering what exactly has been discussed/decided on the BlueZ summit.
>
> Are we supposed to move everything "platform specific" to agents and
> remove all drivers from BlueZ? Are we okay with have BlueZ depending
> on multiple agents?
>
Yes, this is what I understand for BlueZ 5.

> Besides that, I think your proposed Telephony API is too tied to
> HFP/HSP. I think it is interesting to have a more generic API so that
> other telephony related profiles (e.g. LE ones) could reuse it without
> requiring their own agent based API.
>
> Regards,

With this new D-Bus telephony drivers there will be no more need of
telephony related code into BlueZ, which will move to platform telephony
daemon.

My understanding for those LE profiles are, as they are related to
telephony, that they should listen for D-Bus signals (simple way, read
only) or should be implemented into telephony daemon.

Regards

Fred

--
Frederic Danis Open Source Technology Centre
[email protected] Intel Corporation


2011-11-23 11:06:59

by Anderson Lizardo

[permalink] [raw]
Subject: Re: [RFC 0/2] Add org.bluez.Telephony interface

Hi Frederic,

On Wed, Nov 23, 2011 at 5:58 AM, Frederic Danis
<[email protected]> wrote:
> The new telephony D-Bus interface is just in charge of performing SDP
> publication and RFComm connection. All Telephony related code is managed by
> the agent (i.e. oFono).

How t

> I take a look to the Phone Alert Status and Alert Notification and I thought
> that this should be implemented in the agent, or in a BlueZ service
> accessible by the agent through D-Bus.

I'm wondering what exactly has been discussed/decided on the BlueZ summit.

Are we supposed to move everything "platform specific" to agents and
remove all drivers from BlueZ? Are we okay with have BlueZ depending
on multiple agents?

Besides that, I think your proposed Telephony API is too tied to
HFP/HSP. I think it is interesting to have a more generic API so that
other telephony related profiles (e.g. LE ones) could reuse it without
requiring their own agent based API.

Regards,
--
Anderson Lizardo
Instituto Nokia de Tecnologia - INdT
Manaus - Brazil

2011-11-23 09:58:28

by Frederic Danis

[permalink] [raw]
Subject: Re: [RFC 0/2] Add org.bluez.Telephony interface

Hello Claudio,

Le 22/11/2011 17:41, Claudio Takahasi a écrit :
> Hi Frederic,
>
> On Tue, Nov 22, 2011 at 12:58 PM, Frederic Danis
> <[email protected]> wrote:
>> Hello Andrei,
>>
>> Le 22/11/2011 15:15, Andrei Emeltchenko a écrit :
>>>>
>>>> First patch add the new org.bluez.Telephony interface and remove
>>>> functions related to AT parsing from audio/headset.c.
>>>>
>>>> Second patch remove the telephony drivers (dummy, maemo5, maemo6
>>>
>>> is this really needed?
>>>
>>> Best regards
>>> Andrei Emeltchenko
>>>
>>
>> During latest BlueZ conf, it has been decided that telephony drivers should
>> be replaced by the telephony D-Bus interface for BlueZ 5.
>
> We will need also platform specific services/plugins to implement BLE
> GATT profiles. Phone Alert Status and Alert Notification need to get/set to
> platform specific information, such as incoming call, mute once, alert
> level, ...
>
> BT SIG designed BR/EDR and BLE GATT profiles avoiding(trying) features
> "overlap". However, from the development point of view the low layer will be
> shared by both. So, please keep the code flexible enough to be used by
> more than one profile.

The new telephony D-Bus interface is just in charge of performing SDP
publication and RFComm connection. All Telephony related code is managed
by the agent (i.e. oFono).

I take a look to the Phone Alert Status and Alert Notification and I
thought that this should be implemented in the agent, or in a BlueZ
service accessible by the agent through D-Bus.

Regards

Fred

--
Frederic Danis Open Source Technology Centre
[email protected] Intel Corporation


2011-11-22 16:41:06

by Claudio Takahasi

[permalink] [raw]
Subject: Re: [RFC 0/2] Add org.bluez.Telephony interface

Hi Frederic,

On Tue, Nov 22, 2011 at 12:58 PM, Frederic Danis
<[email protected]> wrote:
> Hello Andrei,
>
> Le 22/11/2011 15:15, Andrei Emeltchenko a écrit :
>>>
>>> First patch add the new org.bluez.Telephony interface and remove
>>> functions related to AT parsing from audio/headset.c.
>>>
>>> Second patch remove the telephony drivers (dummy, maemo5, maemo6
>>
>> is this really needed?
>>
>> Best regards
>> Andrei Emeltchenko
>>
>
> During latest BlueZ conf, it has been decided that telephony drivers should
> be replaced by the telephony D-Bus interface for BlueZ 5.

We will need also platform specific services/plugins to implement BLE
GATT profiles. Phone Alert Status and Alert Notification need to get/set to
platform specific information, such as incoming call, mute once, alert
level, ...

BT SIG designed BR/EDR and BLE GATT profiles avoiding(trying) features
"overlap". However, from the development point of view the low layer will be
shared by both. So, please keep the code flexible enough to be used by
more than one profile.

BR,
Claudio.

2011-11-22 15:58:24

by Frederic Danis

[permalink] [raw]
Subject: Re: [RFC 0/2] Add org.bluez.Telephony interface

Hello Andrei,

Le 22/11/2011 15:15, Andrei Emeltchenko a ?crit :
>> First patch add the new org.bluez.Telephony interface and remove
>> functions related to AT parsing from audio/headset.c.
>>
>> Second patch remove the telephony drivers (dummy, maemo5, maemo6
>
> is this really needed?
>
> Best regards
> Andrei Emeltchenko
>

During latest BlueZ conf, it has been decided that telephony drivers
should be replaced by the telephony D-Bus interface for BlueZ 5.

Regards

Fred

--
Frederic Danis Open Source Technology Centre
[email protected] Intel Corporation


2011-11-22 14:22:04

by Johan Hedberg

[permalink] [raw]
Subject: Re: [RFC 1/2] audio: Move tel drivers to DBus interface

Hi Fr?d?ric,

On Tue, Nov 22, 2011, Fr?d?ric Danis wrote:
> +Telephony hierarchy [experiemental]
> +===================
> +
> +Service org.bluez
> +Interface org.bluez.Telephony
> +Object path [variable prefix]/{hci0,hci1,...}
> +
> +Methods void RegisterAgent(object path, dict properties)
> +
> + Register a TelephonyAgent to sender, the sender can
> + register as many agents as it likes.
> +
> + Note: If the sender disconnects its agents are
> + automatically unregistered.
> +
> + possible properties:
> +
> + string UUID:
> +
> + UUID of the profile which the agent is
> + for.
> +
> + uint16 Version:
> +
> + Version of the profile which the agent
> + implements.
> +
> + byte Features:
> +
> + Agent supported features as defined in
> + profile spec e.g. HFP.

The HFP spec uses a 16-bit integer for Features in SDP so I believe we
should too.

> +
> + Possible Errors: org.bluez.Error.InvalidArguments
> +
> +
> + void UnregisterAgent(object path)
> +
> + Unregister sender agent.
> +
> +TelephonyAgent hierarchy
> +========================
> +
> +Service unique name
> +Interface org.bluez.TelephonyAgent
> +Object path freely definable
> +
> +ethods void NewConnection(filedescriptor fd, dict properties)

M missing :)

> +
> + This method gets called whenever a new connection
> + has been established. This method assumes that DBus
> + daemon with file descriptor passing capability is
> + being used.
> +
> + The agent should only return successfully once the
> + establishment of the service level connection (SLC)
> + has been completed. In the case of Handsfree this
> + means that BRSF exchange has been performed and
> + necessary initialization has been done.
> +
> + If Endpoint is set the agent is responsible to
> + create an object implementing org.bluez.MediaTransport
> + and notify the Endpoint using org.bluez.MediaEndpoint.
> +
> + possible properties:
> +
> + strict Device:
> +
> + BlueZ remote device object.
> +
> + string UUID:
> +
> + Profile UUID of the connection.
> +
> + uint16 Version:
> +
> + Remote profile version.
> +
> + byte Features:
> +
> + Remote profile features.

Same thing here about the Features integer size.

Johan

2011-11-22 14:15:11

by Andrei Emeltchenko

[permalink] [raw]
Subject: Re: [RFC 0/2] Add org.bluez.Telephony interface

Hi Fr?d?ric,

On Tue, Nov 22, 2011 at 03:01:22PM +0100, Fr?d?ric Danis wrote:
> Hello,
>
> I am working on this set of patches.
>
> Replace telephony drivers for HeadSet/HandsFree Profiles by a new DBus
> interface (org.bluez.Telephony).
> External applications, which should implement AT parsing and telephony
> part of HeadSet or HandsFree Profiles, will have to register a telephony
> agent using this new interface.
>
>
> First patch add the new org.bluez.Telephony interface and remove
> functions related to AT parsing from audio/headset.c.
>
> Second patch remove the telephony drivers (dummy, maemo5, maemo6

is this really needed?

Best regards
Andrei Emeltchenko

> and ofono), functions used by telephony drivers and no more needed
> methods of org.bluez.Headset interface.
>
>
> What I would like to do to continue is:
> - move rfcomm servers from audio/manager.c to audio/telephony.c, so
> servers are started only when a telephony agent is registered. This
> should also replace audio/gateway.c.
> - pass MediaTransport object in NewConnection method so telephony agent
> will be able to set NREC and ringtone, which can in turn notify Pulse
> Audio of property changed.
>
>
> Any comments appreciated
>
> Regards
>
> Fred
>
> Fr?d?ric Danis (2):
> audio: Move tel drivers to DBus interface
> audio: Remove tel drivers
>
> Makefile.am | 17 +-
> audio/headset.c | 1517 +-------------------------------
> audio/headset.h | 7 +-
> audio/manager.c | 4 +-
> audio/telephony-dummy.c | 433 ---------
> audio/telephony-maemo5.c | 2104 --------------------------------------------
> audio/telephony-maemo6.c | 2201 ----------------------------------------------
> audio/telephony-ofono.c | 1638 ----------------------------------
> audio/telephony.c | 494 +++++++++++
> audio/telephony.h | 196 +----
> audio/transport.c | 10 +
> audio/unix.c | 2 +
> doc/audio-api.txt | 99 +++
> 13 files changed, 631 insertions(+), 8091 deletions(-)
> delete mode 100644 audio/telephony-dummy.c
> delete mode 100644 audio/telephony-maemo5.c
> delete mode 100644 audio/telephony-maemo6.c
> delete mode 100644 audio/telephony-ofono.c
> create mode 100644 audio/telephony.c
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

2011-11-22 14:01:23

by Frederic Danis

[permalink] [raw]
Subject: [RFC 1/2] audio: Move tel drivers to DBus interface

---
Makefile.am | 13 +-
audio/headset.c | 614 ++---------------------------------------------------
audio/headset.h | 2 +
audio/manager.c | 4 +-
audio/telephony.c | 494 ++++++++++++++++++++++++++++++++++++++++++
audio/telephony.h | 25 +--
doc/audio-api.txt | 99 +++++++++
7 files changed, 620 insertions(+), 631 deletions(-)
create mode 100644 audio/telephony.c

diff --git a/Makefile.am b/Makefile.am
index 07b8626..31c1083 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -154,14 +154,8 @@ builtin_sources += audio/main.c \
audio/unix.h audio/unix.c \
audio/media.h audio/media.c \
audio/transport.h audio/transport.c \
- audio/telephony.h audio/a2dp-codecs.h
-builtin_nodist += audio/telephony.c
-
-noinst_LIBRARIES += audio/libtelephony.a
-
-audio_libtelephony_a_SOURCES = audio/telephony.h audio/telephony-dummy.c \
- audio/telephony-maemo5.c audio/telephony-ofono.c \
- audio/telephony-maemo6.c
+ audio/telephony.h audio/telephony.c \
+ audio/a2dp-codecs.h
endif

if SAPPLUGIN
@@ -476,9 +470,6 @@ MAINTAINERCLEANFILES = Makefile.in \
src/builtin.h: src/genbuiltin $(builtin_sources)
$(AM_V_GEN)$(srcdir)/src/genbuiltin $(builtin_modules) > $@

-audio/telephony.c: audio/@TELEPHONY_DRIVER@
- $(AM_V_GEN)$(LN_S) $(abs_top_srcdir)/$< $@
-
sap/sap.c: sap/@SAP_DRIVER@
$(AM_V_GEN)$(LN_S) $(abs_top_srcdir)/$< $@

diff --git a/audio/headset.c b/audio/headset.c
index 6aef6a8..74eb7a4 100644
--- a/audio/headset.c
+++ b/audio/headset.c
@@ -70,8 +70,6 @@
#define HEADSET_GAIN_MICROPHONE 'M'

static struct {
- gboolean telephony_ready; /* Telephony plugin initialized */
- uint32_t features; /* HFP AG features */
const struct indicator *indicators; /* Available HFP indicators */
int er_mode; /* Event reporting mode */
int er_ind; /* Event reporting for indicators */
@@ -81,8 +79,6 @@ static struct {
guint ring_timer; /* For incoming call indication */
const char *chld; /* Response to AT+CHLD=? */
} ag = {
- .telephony_ready = FALSE,
- .features = 0,
.er_mode = 3,
.er_ind = 0,
.rh = BTRH_NOT_SUPPORTED,
@@ -236,40 +232,6 @@ static void print_ag_features(uint32_t features)
g_free(str);
}

-static void print_hf_features(uint32_t features)
-{
- GString *gstr;
- char *str;
-
- if (features == 0) {
- DBG("HFP HF features: (none)");
- return;
- }
-
- gstr = g_string_new("HFP HF features: ");
-
- if (features & HF_FEATURE_EC_ANDOR_NR)
- g_string_append(gstr, "\"EC and/or NR function\" ");
- if (features & HF_FEATURE_CALL_WAITING_AND_3WAY)
- g_string_append(gstr, "\"Call waiting and 3-way calling\" ");
- if (features & HF_FEATURE_CLI_PRESENTATION)
- g_string_append(gstr, "\"CLI presentation capability\" ");
- if (features & HF_FEATURE_VOICE_RECOGNITION)
- g_string_append(gstr, "\"Voice recognition activation\" ");
- if (features & HF_FEATURE_REMOTE_VOLUME_CONTROL)
- g_string_append(gstr, "\"Remote volume control\" ");
- if (features & HF_FEATURE_ENHANCED_CALL_STATUS)
- g_string_append(gstr, "\"Enhanced call status\" ");
- if (features & HF_FEATURE_ENHANCED_CALL_CONTROL)
- g_string_append(gstr, "\"Enhanced call control\" ");
-
- str = g_string_free(gstr, FALSE);
-
- DBG("%s", str);
-
- g_free(str);
-}
-
static const char *state2str(headset_state_t state)
{
switch (state) {
@@ -333,97 +295,6 @@ static int __attribute__((format(printf, 2, 3)))
return ret;
}

-static int supported_features(struct audio_device *device, const char *buf)
-{
- struct headset *hs = device->headset;
- struct headset_slc *slc = hs->slc;
- int err;
-
- if (strlen(buf) < 9)
- return -EINVAL;
-
- slc->hf_features = strtoul(&buf[8], NULL, 10);
-
- print_hf_features(slc->hf_features);
-
- err = headset_send(hs, "\r\n+BRSF: %u\r\n", ag.features);
- if (err < 0)
- return err;
-
- return headset_send(hs, "\r\nOK\r\n");
-}
-
-static char *indicator_ranges(const struct indicator *indicators)
-{
- int i;
- GString *gstr;
-
- gstr = g_string_new("\r\n+CIND: ");
-
- for (i = 0; indicators[i].desc != NULL; i++) {
- if (i == 0)
- g_string_append_printf(gstr, "(\"%s\",(%s))",
- indicators[i].desc,
- indicators[i].range);
- else
- g_string_append_printf(gstr, ",(\"%s\",(%s))",
- indicators[i].desc,
- indicators[i].range);
- }
-
- g_string_append(gstr, "\r\n");
-
- return g_string_free(gstr, FALSE);
-}
-
-static char *indicator_values(const struct indicator *indicators)
-{
- int i;
- GString *gstr;
-
- gstr = g_string_new("\r\n+CIND: ");
-
- for (i = 0; indicators[i].desc != NULL; i++) {
- if (i == 0)
- g_string_append_printf(gstr, "%d", indicators[i].val);
- else
- g_string_append_printf(gstr, ",%d", indicators[i].val);
- }
-
- g_string_append(gstr, "\r\n");
-
- return g_string_free(gstr, FALSE);
-}
-
-static int report_indicators(struct audio_device *device, const char *buf)
-{
- struct headset *hs = device->headset;
- int err;
- char *str;
-
- if (strlen(buf) < 8)
- return -EINVAL;
-
- if (ag.indicators == NULL) {
- error("HFP AG indicators not initialized");
- return headset_send(hs, "\r\nERROR\r\n");
- }
-
- if (buf[7] == '=')
- str = indicator_ranges(ag.indicators);
- else
- str = indicator_values(ag.indicators);
-
- err = headset_send(hs, "%s", str);
-
- g_free(str);
-
- if (err < 0)
- return err;
-
- return headset_send(hs, "\r\nOK\r\n");
-}
-
static void pending_connect_complete(struct connect_cb *cb, struct audio_device *dev)
{
struct headset *hs = dev->headset;
@@ -656,7 +527,7 @@ static int hfp_cmp(struct headset *hs)
return -1;
}

-static void hfp_slc_complete(struct audio_device *dev)
+void headset_slc_complete(struct audio_device *dev)
{
struct headset *hs = dev->headset;
struct pending_connect *p = hs->pending;
@@ -721,73 +592,10 @@ int telephony_event_reporting_rsp(void *telephony_device, cme_error_t err)
return 0;

if (slc->hf_features & HF_FEATURE_CALL_WAITING_AND_3WAY &&
- ag.features & AG_FEATURE_THREE_WAY_CALLING)
- return 0;
-
- hfp_slc_complete(device);
-
- return 0;
-}
-
-static int event_reporting(struct audio_device *dev, const char *buf)
-{
- char **tokens; /* <mode>, <keyp>, <disp>, <ind>, <bfr> */
-
- if (strlen(buf) < 13)
- return -EINVAL;
-
- tokens = g_strsplit(&buf[8], ",", 5);
- if (g_strv_length(tokens) < 4) {
- g_strfreev(tokens);
- return -EINVAL;
- }
-
- ag.er_mode = atoi(tokens[0]);
- ag.er_ind = atoi(tokens[3]);
-
- g_strfreev(tokens);
- tokens = NULL;
-
- DBG("Event reporting (CMER): mode=%d, ind=%d",
- ag.er_mode, ag.er_ind);
-
- switch (ag.er_ind) {
- case 0:
- case 1:
- telephony_event_reporting_req(dev, ag.er_ind);
- break;
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int call_hold(struct audio_device *dev, const char *buf)
-{
- struct headset *hs = dev->headset;
- int err;
-
- if (strlen(buf) < 9)
- return -EINVAL;
-
- if (buf[8] != '?') {
- telephony_call_hold_req(dev, &buf[8]);
+ telephony_get_ag_features() & AG_FEATURE_THREE_WAY_CALLING)
return 0;
- }

- err = headset_send(hs, "\r\n+CHLD: (%s)\r\n", ag.chld);
- if (err < 0)
- return err;
-
- err = headset_send(hs, "\r\nOK\r\n");
- if (err < 0)
- return err;
-
- if (hs->state != HEADSET_STATE_CONNECTING)
- return 0;
-
- hfp_slc_complete(dev);
+ headset_slc_complete(device);

return 0;
}
@@ -797,47 +605,11 @@ int telephony_key_press_rsp(void *telephony_device, cme_error_t err)
return telephony_generic_rsp(telephony_device, err);
}

-static int key_press(struct audio_device *device, const char *buf)
-{
- if (strlen(buf) < 9)
- return -EINVAL;
-
- g_dbus_emit_signal(device->conn, device->path,
- AUDIO_HEADSET_INTERFACE, "AnswerRequested",
- DBUS_TYPE_INVALID);
-
- if (ag.ring_timer) {
- g_source_remove(ag.ring_timer);
- ag.ring_timer = 0;
- }
-
- telephony_key_press_req(device, &buf[8]);
-
- return 0;
-}
-
int telephony_answer_call_rsp(void *telephony_device, cme_error_t err)
{
return telephony_generic_rsp(telephony_device, err);
}

-static int answer_call(struct audio_device *device, const char *buf)
-{
- if (ag.ring_timer) {
- g_source_remove(ag.ring_timer);
- ag.ring_timer = 0;
- }
-
- if (ag.number) {
- g_free(ag.number);
- ag.number = NULL;
- }
-
- telephony_answer_call_req(device);
-
- return 0;
-}
-
int telephony_terminate_call_rsp(void *telephony_device,
cme_error_t err)
{
@@ -854,99 +626,21 @@ int telephony_terminate_call_rsp(void *telephony_device,
return headset_send(hs, "\r\nOK\r\n");
}

-static int terminate_call(struct audio_device *device, const char *buf)
-{
- if (ag.number) {
- g_free(ag.number);
- ag.number = NULL;
- }
-
- if (ag.ring_timer) {
- g_source_remove(ag.ring_timer);
- ag.ring_timer = 0;
- }
-
- telephony_terminate_call_req(device);
-
- return 0;
-}
-
-static int cli_notification(struct audio_device *device, const char *buf)
-{
- struct headset *hs = device->headset;
- struct headset_slc *slc = hs->slc;
-
- if (strlen(buf) < 9)
- return -EINVAL;
-
- slc->cli_active = buf[8] == '1' ? TRUE : FALSE;
-
- return headset_send(hs, "\r\nOK\r\n");
-}
-
int telephony_response_and_hold_rsp(void *telephony_device, cme_error_t err)
{
return telephony_generic_rsp(telephony_device, err);
}

-static int response_and_hold(struct audio_device *device, const char *buf)
-{
- struct headset *hs = device->headset;
-
- if (strlen(buf) < 8)
- return -EINVAL;
-
- if (ag.rh == BTRH_NOT_SUPPORTED)
- return telephony_generic_rsp(device, CME_ERROR_NOT_SUPPORTED);
-
- if (buf[7] == '=') {
- telephony_response_and_hold_req(device, atoi(&buf[8]) < 0);
- return 0;
- }
-
- if (ag.rh >= 0)
- headset_send(hs, "\r\n+BTRH: %d\r\n", ag.rh);
-
- return headset_send(hs, "\r\nOK\r\n");
-}
-
int telephony_last_dialed_number_rsp(void *telephony_device, cme_error_t err)
{
return telephony_generic_rsp(telephony_device, err);
}

-static int last_dialed_number(struct audio_device *device, const char *buf)
-{
- telephony_last_dialed_number_req(device);
-
- return 0;
-}
-
int telephony_dial_number_rsp(void *telephony_device, cme_error_t err)
{
return telephony_generic_rsp(telephony_device, err);
}

-static int dial_number(struct audio_device *device, const char *buf)
-{
- char number[BUF_SIZE];
- size_t buf_len;
-
- buf_len = strlen(buf);
-
- if (buf[buf_len - 1] != ';') {
- DBG("Rejecting non-voice call dial request");
- return -EINVAL;
- }
-
- memset(number, 0, sizeof(number));
- strncpy(number, &buf[3], buf_len - 4);
-
- telephony_dial_number_req(device, number);
-
- return 0;
-}
-
static int headset_set_gain(struct audio_device *device, uint16_t gain, char type)
{
struct headset *hs = device->headset;
@@ -994,111 +688,21 @@ static int headset_set_gain(struct audio_device *device, uint16_t gain, char typ
return 0;
}

-static int signal_gain_setting(struct audio_device *device, const char *buf)
-{
- struct headset *hs = device->headset;
- dbus_uint16_t gain;
- int err;
-
- if (strlen(buf) < 8) {
- error("Too short string for Gain setting");
- return -EINVAL;
- }
-
- gain = (dbus_uint16_t) strtol(&buf[7], NULL, 10);
-
- err = headset_set_gain(device, gain, buf[5]);
- if (err < 0 && err != -EALREADY)
- return err;
-
- return headset_send(hs, "\r\nOK\r\n");
-}
-
int telephony_transmit_dtmf_rsp(void *telephony_device, cme_error_t err)
{
return telephony_generic_rsp(telephony_device, err);
}

-static int dtmf_tone(struct audio_device *device, const char *buf)
-{
- char tone;
-
- if (strlen(buf) < 8) {
- error("Too short string for DTMF tone");
- return -EINVAL;
- }
-
- tone = buf[7];
- if (tone >= '#' && tone <= 'D')
- telephony_transmit_dtmf_req(device, tone);
- else
- return -EINVAL;
-
- return 0;
-}
-
int telephony_subscriber_number_rsp(void *telephony_device, cme_error_t err)
{
return telephony_generic_rsp(telephony_device, err);
}

-static int subscriber_number(struct audio_device *device, const char *buf)
-{
- telephony_subscriber_number_req(device);
-
- return 0;
-}
-
int telephony_list_current_calls_rsp(void *telephony_device, cme_error_t err)
{
return telephony_generic_rsp(telephony_device, err);
}

-static int list_current_calls(struct audio_device *device, const char *buf)
-{
- telephony_list_current_calls_req(device);
-
- return 0;
-}
-
-static int extended_errors(struct audio_device *device, const char *buf)
-{
- struct headset *hs = device->headset;
- struct headset_slc *slc = hs->slc;
-
- if (strlen(buf) < 9)
- return -EINVAL;
-
- if (buf[8] == '1') {
- slc->cme_enabled = TRUE;
- DBG("CME errors enabled for headset %p", hs);
- } else {
- slc->cme_enabled = FALSE;
- DBG("CME errors disabled for headset %p", hs);
- }
-
- return headset_send(hs, "\r\nOK\r\n");
-}
-
-static int call_waiting_notify(struct audio_device *device, const char *buf)
-{
- struct headset *hs = device->headset;
- struct headset_slc *slc = hs->slc;
-
- if (strlen(buf) < 9)
- return -EINVAL;
-
- if (buf[8] == '1') {
- slc->cwa_enabled = TRUE;
- DBG("Call waiting notification enabled for headset %p", hs);
- } else {
- slc->cwa_enabled = FALSE;
- DBG("Call waiting notification disabled for headset %p", hs);
- }
-
- return headset_send(hs, "\r\nOK\r\n");
-}
-
int telephony_operator_selection_rsp(void *telephony_device, cme_error_t err)
{
return telephony_generic_rsp(telephony_device, err);
@@ -1146,108 +750,6 @@ int telephony_operator_selection_ind(int mode, const char *oper)
return 0;
}

-static int operator_selection(struct audio_device *device, const char *buf)
-{
- struct headset *hs = device->headset;
-
- if (strlen(buf) < 8)
- return -EINVAL;
-
- switch (buf[7]) {
- case '?':
- telephony_operator_selection_req(device);
- break;
- case '=':
- return headset_send(hs, "\r\nOK\r\n");
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int nr_and_ec(struct audio_device *device, const char *buf)
-{
- struct headset *hs = device->headset;
- struct headset_slc *slc = hs->slc;
-
- if (strlen(buf) < 9)
- return -EINVAL;
-
- if (buf[8] == '0')
- slc->nrec_req = FALSE;
- else
- slc->nrec_req = TRUE;
-
- telephony_nr_and_ec_req(device, slc->nrec_req);
-
- return 0;
-}
-
-static int voice_dial(struct audio_device *device, const char *buf)
-{
- gboolean enable;
-
- if (strlen(buf) < 9)
- return -EINVAL;
-
- if (buf[8] == '0')
- enable = FALSE;
- else
- enable = TRUE;
-
- telephony_voice_dial_req(device, enable);
-
- return 0;
-}
-
-static int apple_command(struct audio_device *device, const char *buf)
-{
- DBG("Got Apple command: %s", buf);
-
- return telephony_generic_rsp(device, CME_ERROR_NONE);
-}
-
-static struct event event_callbacks[] = {
- { "ATA", answer_call },
- { "ATD", dial_number },
- { "AT+VG", signal_gain_setting },
- { "AT+BRSF", supported_features },
- { "AT+CIND", report_indicators },
- { "AT+CMER", event_reporting },
- { "AT+CHLD", call_hold },
- { "AT+CHUP", terminate_call },
- { "AT+CKPD", key_press },
- { "AT+CLIP", cli_notification },
- { "AT+BTRH", response_and_hold },
- { "AT+BLDN", last_dialed_number },
- { "AT+VTS", dtmf_tone },
- { "AT+CNUM", subscriber_number },
- { "AT+CLCC", list_current_calls },
- { "AT+CMEE", extended_errors },
- { "AT+CCWA", call_waiting_notify },
- { "AT+COPS", operator_selection },
- { "AT+NREC", nr_and_ec },
- { "AT+BVRA", voice_dial },
- { "AT+XAPL", apple_command },
- { "AT+IPHONEACCEV", apple_command },
- { 0 }
-};
-
-static int handle_event(struct audio_device *device, const char *buf)
-{
- struct event *ev;
-
- DBG("Received %s", buf);
-
- for (ev = event_callbacks; ev->cmd; ev++) {
- if (!strncmp(buf, ev->cmd, strlen(ev->cmd)))
- return ev->callback(device, buf);
- }
-
- return -EINVAL;
-}
-
static void close_sco(struct audio_device *device)
{
struct headset *hs = device->headset;
@@ -1266,94 +768,6 @@ static void close_sco(struct audio_device *device)
}
}

-static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond,
- struct audio_device *device)
-{
- struct headset *hs;
- struct headset_slc *slc;
- unsigned char buf[BUF_SIZE];
- ssize_t bytes_read;
- size_t free_space;
- int fd;
-
- if (cond & G_IO_NVAL)
- return FALSE;
-
- hs = device->headset;
- slc = hs->slc;
-
- if (cond & (G_IO_ERR | G_IO_HUP)) {
- DBG("ERR or HUP on RFCOMM socket");
- goto failed;
- }
-
- fd = g_io_channel_unix_get_fd(chan);
-
- bytes_read = read(fd, buf, sizeof(buf) - 1);
- if (bytes_read < 0)
- return TRUE;
-
- free_space = sizeof(slc->buf) - slc->data_start -
- slc->data_length - 1;
-
- if (free_space < (size_t) bytes_read) {
- /* Very likely that the HS is sending us garbage so
- * just ignore the data and disconnect */
- error("Too much data to fit incomming buffer");
- goto failed;
- }
-
- memcpy(&slc->buf[slc->data_start], buf, bytes_read);
- slc->data_length += bytes_read;
-
- /* Make sure the data is null terminated so we can use string
- * functions */
- slc->buf[slc->data_start + slc->data_length] = '\0';
-
- while (slc->data_length > 0) {
- char *cr;
- int err;
- off_t cmd_len;
-
- cr = strchr(&slc->buf[slc->data_start], '\r');
- if (!cr)
- break;
-
- cmd_len = 1 + (off_t) cr - (off_t) &slc->buf[slc->data_start];
- *cr = '\0';
-
- if (cmd_len > 1)
- err = handle_event(device, &slc->buf[slc->data_start]);
- else
- /* Silently skip empty commands */
- err = 0;
-
- if (err == -EINVAL) {
- error("Badly formated or unrecognized command: %s",
- &slc->buf[slc->data_start]);
- err = headset_send(hs, "\r\nERROR\r\n");
- if (err < 0)
- goto failed;
- } else if (err < 0)
- error("Error handling command %s: %s (%d)",
- &slc->buf[slc->data_start],
- strerror(-err), -err);
-
- slc->data_start += cmd_len;
- slc->data_length -= cmd_len;
-
- if (!slc->data_length)
- slc->data_start = 0;
- }
-
- return TRUE;
-
-failed:
- headset_set_state(device, HEADSET_STATE_DISCONNECTED);
-
- return FALSE;
-}
-
static gboolean sco_cb(GIOChannel *chan, GIOCondition cond,
struct audio_device *device)
{
@@ -1381,7 +795,7 @@ void headset_connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
}

/* For HFP telephony isn't ready just disconnect */
- if (hs->hfp_active && !ag.telephony_ready) {
+ if (hs->hfp_active && !telephony_get_ready_state()) {
error("Unable to accept HFP connection since the telephony "
"subsystem isn't initialized");
goto failed;
@@ -1397,8 +811,7 @@ void headset_connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
else
hs->auto_dc = FALSE;

- g_io_add_watch(chan, G_IO_IN | G_IO_ERR | G_IO_HUP| G_IO_NVAL,
- (GIOFunc) rfcomm_io_cb, dev);
+ hs->slc = telephony_device_connecting(chan, dev);

DBG("%s: Connected to %s", dev->path, hs_address);

@@ -1740,7 +1153,7 @@ static DBusMessage *hs_connect(DBusConnection *conn, DBusMessage *msg,
else if (hs->state > HEADSET_STATE_CONNECTING)
return btd_error_already_connected(msg);

- if (hs->hfp_handle && !ag.telephony_ready)
+ if (hs->hfp_handle && !telephony_get_ready_state())
return btd_error_not_ready(msg);

device->auto_connect = FALSE;
@@ -2245,7 +1658,7 @@ uint32_t headset_config_init(GKeyFile *config)

/* Use the default values if there is no config file */
if (config == NULL)
- return ag.features;
+ return telephony_get_ag_features();

str = g_key_file_get_string(config, "General", "SCORouting",
&err);
@@ -2275,7 +1688,7 @@ uint32_t headset_config_init(GKeyFile *config)
g_free(str);
}

- return ag.features;
+ return telephony_get_ag_features();
}

static gboolean hs_dc_timeout(struct audio_device *dev)
@@ -2518,6 +1931,10 @@ void headset_set_state(struct audio_device *dev, headset_state_t state)
case HEADSET_STATE_DISCONNECTED:
value = FALSE;
close_sco(dev);
+
+ if (dev->headset->slc)
+ telephony_device_disconnect(dev->headset->slc);
+
headset_close_rfcomm(dev);
emit_property_changed(dev->conn, dev->path,
AUDIO_HEADSET_INTERFACE, "State",
@@ -2546,7 +1963,8 @@ void headset_set_state(struct audio_device *dev, headset_state_t state)
AUDIO_HEADSET_INTERFACE, "State",
DBUS_TYPE_STRING, &state_str);
if (hs->state < state) {
- if (ag.features & AG_FEATURE_INBAND_RINGTONE)
+ if (telephony_get_ag_features() &
+ AG_FEATURE_INBAND_RINGTONE)
slc->inband_ring = TRUE;
else
slc->inband_ring = FALSE;
@@ -2880,15 +2298,13 @@ int telephony_ready_ind(uint32_t features,
const struct indicator *indicators, int rh,
const char *chld)
{
- ag.telephony_ready = TRUE;
- ag.features = features;
ag.indicators = indicators;
ag.rh = rh;
ag.chld = chld;

DBG("Telephony plugin initialized");

- print_ag_features(ag.features);
+ print_ag_features(telephony_get_ag_features());

return 0;
}
diff --git a/audio/headset.h b/audio/headset.h
index 99eeca8..d43952f 100644
--- a/audio/headset.h
+++ b/audio/headset.h
@@ -111,3 +111,5 @@ gboolean headset_unlock(struct audio_device *dev, headset_lock_t lock);
gboolean headset_suspend(struct audio_device *dev, void *data);
gboolean headset_play(struct audio_device *dev, void *data);
void headset_shutdown(struct audio_device *dev);
+
+void headset_slc_complete(struct audio_device *dev);
diff --git a/audio/manager.c b/audio/manager.c
index 8de5515..4624552 100644
--- a/audio/manager.c
+++ b/audio/manager.c
@@ -880,7 +880,7 @@ static void state_changed(struct btd_adapter *adapter, gboolean powered)
/* telephony driver already initialized*/
if (telephony == TRUE)
return;
- telephony_init();
+ telephony_init(adapter);
telephony = TRUE;
return;
}
@@ -896,7 +896,7 @@ static void state_changed(struct btd_adapter *adapter, gboolean powered)
return;
}

- telephony_exit();
+ telephony_exit(adapter);
telephony = FALSE;
}

diff --git a/audio/telephony.c b/audio/telephony.c
new file mode 100644
index 0000000..0f2e066
--- /dev/null
+++ b/audio/telephony.c
@@ -0,0 +1,494 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 Intel Corporation
+ * Copyright (C) 2004-2010 Marcel Holtmann <[email protected]>
+ * Copyright (C) 2011 Frederic Danis <[email protected]>
+ *
+ *
+ * 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 <stdlib.h>
+
+#include <dbus/dbus.h>
+#include <gdbus.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include "btio.h"
+#include "log.h"
+#include "device.h"
+#include "error.h"
+#include "glib-helper.h"
+#include "sdp-client.h"
+#include "headset.h"
+#include "telephony.h"
+#include "dbus-common.h"
+#include "../src/adapter.h"
+#include "../src/device.h"
+
+#define AUDIO_TELEPHONY_INTERFACE "org.bluez.Telephony"
+
+struct telsrv {
+ GSList *servers; /* server list */
+};
+
+struct tel_device {
+ struct tel_agent *agent;
+ struct audio_device *au_dev;
+ GIOChannel *rfcomm;
+ uint16_t version;
+ uint16_t features;
+};
+
+struct tel_agent {
+ char *name; /* agent DBus bus id */
+ char *path; /* agent object path */
+ const char *uuid; /* agent property UUID */
+ uint16_t version;
+ uint8_t features;
+ uint16_t r_class;
+ uint16_t r_profile;
+};
+
+static DBusConnection *connection = NULL;
+
+struct telsrv telsrv;
+
+static struct tel_agent *find_agent(const char *sender, const char *path,
+ const char *uuid)
+{
+ GSList *l;
+
+ for (l = telsrv.servers; l; l = l->next) {
+ struct tel_agent *agent = l->data;
+
+ if (sender && g_strcmp0(agent->name, sender) != 0)
+ continue;
+
+ if (path && g_strcmp0(agent->path, path) != 0)
+ continue;
+
+ if (uuid && g_strcmp0(agent->uuid, uuid) != 0)
+ continue;
+
+ return agent;
+ }
+
+ return NULL;
+}
+
+static int parse_properties(DBusMessageIter *props, const char **uuid,
+ uint16_t *version, uint8_t *features)
+{
+ gboolean has_uuid = FALSE;
+
+ 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);
+ has_uuid = TRUE;
+ } else if (strcasecmp(key, "Version") == 0) {
+ if (var != DBUS_TYPE_UINT16)
+ return -EINVAL;
+ dbus_message_iter_get_basic(&value, version);
+ } else if (strcasecmp(key, "Features") == 0) {
+ if (var != DBUS_TYPE_BYTE)
+ return -EINVAL;
+ dbus_message_iter_get_basic(&value, features);
+ }
+
+ dbus_message_iter_next(props);
+ }
+
+ return (has_uuid) ? 0 : -EINVAL;
+}
+
+static int dev_close(struct tel_device *dev)
+{
+ int sock;
+
+ if (dev->rfcomm) {
+ sock = g_io_channel_unix_get_fd(dev->rfcomm);
+ shutdown(sock, SHUT_RDWR);
+ }
+
+ return 0;
+}
+
+static gboolean agent_sendfd(struct tel_device *dev, int fd,
+ DBusPendingCallNotifyFunction notify)
+{
+ struct tel_agent *agent = dev->agent;
+ DBusMessage *msg;
+ DBusMessageIter iter, dict;
+ char *str;
+ DBusPendingCall *call;
+
+ msg = dbus_message_new_method_call(agent->name, agent->path,
+ "org.bluez.TelephonyAgent", "NewConnection");
+
+ dbus_message_iter_init_append(msg, &iter);
+
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_UNIX_FD, &fd);
+
+ 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);
+
+ str = g_strdup(agent->uuid);
+ dict_append_entry(&dict, "UUID", DBUS_TYPE_STRING, &str);
+ g_free(str);
+
+ dict_append_entry(&dict, "Version", DBUS_TYPE_UINT16, &dev->version);
+
+ if (dev->features != 0xFFFF)
+ dict_append_entry(&dict, "Features", DBUS_TYPE_BYTE,
+ &dev->features);
+
+ dbus_message_iter_close_container(&iter, &dict);
+
+ if (dbus_connection_send_with_reply(connection, msg, &call, -1) == FALSE)
+ return FALSE;
+
+ dbus_pending_call_set_notify(call, notify, dev, NULL);
+ dbus_pending_call_unref(call);
+ dbus_message_unref(msg);
+
+ return TRUE;
+}
+
+static gboolean agent_disconnect_cb(GIOChannel *chan, GIOCondition cond,
+ struct tel_device *dev)
+{
+ if (cond & G_IO_NVAL)
+ return FALSE;
+
+ headset_set_state(dev->au_dev, HEADSET_STATE_DISCONNECTED);
+
+ return FALSE;
+}
+
+static void newconnection_reply(DBusPendingCall *call, void *user_data)
+{
+ struct tel_device *dev = user_data;
+ DBusMessage *reply = dbus_pending_call_steal_reply(call);
+ DBusError derr;
+
+ if (!dev->rfcomm) {
+ DBG("RFCOMM disconnected from server before agent reply");
+ goto done;
+ }
+
+ dbus_error_init(&derr);
+ if (!dbus_set_error_from_message(&derr, reply)) {
+ DBG("Agent reply: file descriptor passed successfully");
+ g_io_add_watch(dev->rfcomm, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+ (GIOFunc) agent_disconnect_cb, dev);
+ headset_slc_complete(dev->au_dev);
+ goto done;
+ }
+
+ DBG("Agent reply: %s", derr.message);
+
+ dbus_error_free(&derr);
+ dev_close(dev);
+ headset_set_state(dev->au_dev, HEADSET_STATE_DISCONNECTED);
+
+done:
+ dbus_message_unref(reply);
+}
+
+static void get_record_cb(sdp_list_t *recs, int err, gpointer user_data)
+{
+ struct tel_device *dev = user_data;
+ sdp_data_t *sdpdata;
+ uuid_t uuid;
+ sdp_list_t *profiles;
+ sdp_profile_desc_t *desc;
+ int sk, ret;
+
+ if (err < 0) {
+ error("Unable to get service record: %s (%d)", strerror(-err),
+ -err);
+ goto failed;
+ }
+
+ if (!recs || !recs->data) {
+ error("No records found");
+ goto failed;
+ }
+
+ sdpdata = sdp_data_get(recs->data, SDP_ATTR_SUPPORTED_FEATURES);
+ if (sdpdata && sdpdata->dtd == SDP_UINT16)
+ dev->features = sdpdata->val.uint16;
+
+ sdp_uuid16_create(&uuid, dev->agent->r_profile);
+
+ sdp_get_profile_descs(recs->data, &profiles);
+ if (profiles == NULL)
+ goto failed;
+
+ desc = profiles->data;
+
+ if (sdp_uuid16_cmp(&desc->uuid, &uuid) == 0)
+ dev->version = desc->version;
+
+ sdp_list_free(profiles, free);
+
+ sk = g_io_channel_unix_get_fd(dev->rfcomm);
+
+ ret = agent_sendfd(dev, sk, newconnection_reply);
+
+ return;
+
+failed:
+ headset_set_state(dev->au_dev, HEADSET_STATE_DISCONNECTED);
+}
+
+void *telephony_device_connecting(GIOChannel *io, void *telephony_device)
+{
+ struct audio_device *device = telephony_device;
+ struct tel_device *dev;
+ const char *agent_uuid;
+ struct tel_agent *agent;
+ uuid_t uuid;
+ int err;
+
+ /*TODO: check for HS roles */
+ if (headset_get_hfp_active(device))
+ agent_uuid = HFP_AG_UUID;
+ else
+ agent_uuid = HSP_AG_UUID;
+
+ agent = find_agent(NULL, NULL, agent_uuid);
+ if (agent == NULL) {
+ error("No agent registered for %s", agent_uuid);
+ return NULL;
+ }
+
+ dev = g_new0(struct tel_device, 1);
+ dev->agent = agent;
+ dev->au_dev = telephony_device;
+ dev->rfcomm = io;
+ dev->features = 0xFFFF;
+
+ sdp_uuid16_create(&uuid, agent->r_class);
+
+ err = bt_search_service(&device->src, &device->dst, &uuid,
+ get_record_cb, dev, NULL);
+ if (err < 0) {
+ g_free(dev);
+ return NULL;
+ }
+
+ return dev;
+}
+
+void telephony_device_connected(void *telephony_device)
+{
+ DBG("telephony-dbus: device %p connected", telephony_device);
+}
+
+void telephony_device_disconnect(void *slc)
+{
+ struct tel_device *dev = slc;
+
+ dev_close(dev);
+}
+
+void telephony_device_disconnected(void *telephony_device)
+{
+ DBG("telephony-dbus: device %p disconnected", telephony_device);
+}
+
+gboolean telephony_get_ready_state(void)
+{
+ return find_agent(NULL, NULL, HFP_AG_UUID) ? TRUE : FALSE;
+}
+
+uint32_t telephony_get_ag_features(void)
+{
+ return 0;
+}
+
+static DBusMessage *register_agent(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ DBusMessageIter args, props;
+ const char *sender, *path, *uuid;
+ uint16_t version = 0;
+ uint8_t features = 0;
+ uint16_t r_class, r_profile;
+ struct tel_agent *agent;
+
+ 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 (find_agent(sender, path, NULL) != NULL)
+ return btd_error_already_exists(msg);
+
+ dbus_message_iter_recurse(&args, &props);
+ if (dbus_message_iter_get_arg_type(&props) != DBUS_TYPE_DICT_ENTRY)
+ return btd_error_invalid_args(msg);
+
+ if (parse_properties(&props, &uuid, &version, &features) < 0)
+ return btd_error_invalid_args(msg);
+
+ if (strcasecmp(uuid, HSP_HS_UUID) == 0) {
+ r_class = HEADSET_AGW_SVCLASS_ID;
+ r_profile = HEADSET_PROFILE_ID;
+ } else if (strcasecmp(uuid, HSP_AG_UUID) == 0) {
+ r_class = HEADSET_SVCLASS_ID;
+ r_profile = HEADSET_PROFILE_ID;
+ } else if (strcasecmp(uuid, HFP_HS_UUID) == 0) {
+ r_class = HANDSFREE_AGW_SVCLASS_ID;
+ r_profile = HANDSFREE_PROFILE_ID;
+ } else if (strcasecmp(uuid, HFP_AG_UUID) == 0) {
+ r_class = HANDSFREE_SVCLASS_ID;
+ r_profile = HANDSFREE_PROFILE_ID;
+ } else
+ return btd_error_invalid_args(msg);
+
+ if (find_agent(NULL, NULL, uuid) != NULL)
+ return btd_error_already_exists(msg);
+
+ DBG("Register agent : %s%s for %s version 0x%04X with features 0x%02X",
+ sender, path, uuid, version, features);
+
+ agent = g_new0(struct tel_agent, 1);
+ agent->name = g_strdup(sender);
+ agent->path = g_strdup(path);
+ agent->uuid = g_strdup(uuid);
+ agent->version = version;
+ agent->features = features;
+ agent->r_class = r_class;
+ agent->r_profile = r_profile;
+
+ telsrv.servers = g_slist_append(telsrv.servers, agent);
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *unregister_agent(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ const char *sender, *path;
+ struct tel_agent *agent;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID))
+ return NULL;
+
+ sender = dbus_message_get_sender(msg);
+
+ agent = find_agent(sender, path, NULL);
+ if (agent == NULL)
+ return btd_error_does_not_exist(msg);
+
+ telsrv.servers = g_slist_remove(telsrv.servers, agent);
+
+ DBG("Unregister agent : %s%s", sender, path);
+
+ g_free(agent);
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static GDBusMethodTable telsrv_methods[] = {
+ { "RegisterAgent", "oa{sv}", "", register_agent },
+ { "UnregisterAgent", "o", "", unregister_agent },
+ { NULL, NULL, NULL, NULL }
+};
+
+static void path_unregister(void *data)
+{
+ DBG("Unregistered interface %s", AUDIO_TELEPHONY_INTERFACE);
+}
+
+static int register_interface(void *adapter)
+{
+ const char *path;
+
+ if (DBUS_TYPE_UNIX_FD < 0)
+ return -1;
+
+ path = adapter_get_path(adapter);
+
+ if (!g_dbus_register_interface(connection, path,
+ AUDIO_TELEPHONY_INTERFACE,
+ telsrv_methods, NULL,
+ NULL, NULL, path_unregister)) {
+ error("D-Bus failed to register %s interface",
+ AUDIO_TELEPHONY_INTERFACE);
+ return -1;
+ }
+
+ DBG("Registered interface %s", AUDIO_TELEPHONY_INTERFACE);
+
+ return 0;
+}
+
+static void unregister_interface(void *adapter)
+{
+ g_dbus_unregister_interface(connection, adapter_get_path(adapter),
+ AUDIO_TELEPHONY_INTERFACE);
+}
+
+int telephony_init(void *adapter)
+{
+ DBG("");
+
+ connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+
+ return register_interface(adapter);
+}
+
+void telephony_exit(void *adapter)
+{
+ DBG("");
+
+ unregister_interface(adapter);
+
+ dbus_connection_unref(connection);
+ connection = NULL;
+}
diff --git a/audio/telephony.h b/audio/telephony.h
index 73b390c..7d1d337 100644
--- a/audio/telephony.h
+++ b/audio/telephony.h
@@ -144,26 +144,13 @@ struct indicator {
/* Notify telephony-*.c of connected/disconnected devices. Implemented by
* telephony-*.c
*/
+void *telephony_device_connecting(GIOChannel *io, void *telephony_device);
void telephony_device_connected(void *telephony_device);
+void telephony_device_disconnect(void *slc);
void telephony_device_disconnected(void *telephony_device);

-/* HF requests (sent by the handsfree device). These are implemented by
- * telephony-*.c
- */
-void telephony_event_reporting_req(void *telephony_device, int ind);
-void telephony_response_and_hold_req(void *telephony_device, int rh);
-void telephony_last_dialed_number_req(void *telephony_device);
-void telephony_terminate_call_req(void *telephony_device);
-void telephony_answer_call_req(void *telephony_device);
-void telephony_dial_number_req(void *telephony_device, const char *number);
-void telephony_transmit_dtmf_req(void *telephony_device, char tone);
-void telephony_subscriber_number_req(void *telephony_device);
-void telephony_list_current_calls_req(void *telephony_device);
-void telephony_operator_selection_req(void *telephony_device);
-void telephony_call_hold_req(void *telephony_device, const char *cmd);
-void telephony_nr_and_ec_req(void *telephony_device, gboolean enable);
-void telephony_voice_dial_req(void *telephony_device, gboolean enable);
-void telephony_key_press_req(void *telephony_device, const char *keys);
+gboolean telephony_get_ready_state(void);
+uint32_t telephony_get_ag_features(void);

/* AG responses to HF requests. These are implemented by headset.c */
int telephony_event_reporting_rsp(void *telephony_device, cme_error_t err);
@@ -240,5 +227,5 @@ static inline int telephony_get_indicator(const struct indicator *indicators,
return -ENOENT;
}

-int telephony_init(void);
-void telephony_exit(void);
+int telephony_init(void *adapter);
+void telephony_exit(void *adapter);
diff --git a/doc/audio-api.txt b/doc/audio-api.txt
index b85400b..d32b51d 100644
--- a/doc/audio-api.txt
+++ b/doc/audio-api.txt
@@ -456,3 +456,102 @@ properties boolean Connected [readonly]
uint16 MicrophoneGain [readonly]

The speaker gain when available.
+
+
+Telephony hierarchy [experiemental]
+===================
+
+Service org.bluez
+Interface org.bluez.Telephony
+Object path [variable prefix]/{hci0,hci1,...}
+
+Methods void RegisterAgent(object path, dict properties)
+
+ Register a TelephonyAgent to sender, the sender can
+ register as many agents as it likes.
+
+ Note: If the sender disconnects its agents are
+ automatically unregistered.
+
+ possible properties:
+
+ string UUID:
+
+ UUID of the profile which the agent is
+ for.
+
+ uint16 Version:
+
+ Version of the profile which the agent
+ implements.
+
+ byte Features:
+
+ Agent supported features as defined in
+ profile spec e.g. HFP.
+
+ Possible Errors: org.bluez.Error.InvalidArguments
+
+
+ void UnregisterAgent(object path)
+
+ Unregister sender agent.
+
+TelephonyAgent hierarchy
+========================
+
+Service unique name
+Interface org.bluez.TelephonyAgent
+Object path freely definable
+
+ethods void NewConnection(filedescriptor fd, dict properties)
+
+ This method gets called whenever a new connection
+ has been established. This method assumes that DBus
+ daemon with file descriptor passing capability is
+ being used.
+
+ The agent should only return successfully once the
+ establishment of the service level connection (SLC)
+ has been completed. In the case of Handsfree this
+ means that BRSF exchange has been performed and
+ necessary initialization has been done.
+
+ If Endpoint is set the agent is responsible to
+ create an object implementing org.bluez.MediaTransport
+ and notify the Endpoint using org.bluez.MediaEndpoint.
+
+ possible properties:
+
+ strict Device:
+
+ BlueZ remote device object.
+
+ string UUID:
+
+ Profile UUID of the connection.
+
+ uint16 Version:
+
+ Remote profile version.
+
+ byte Features:
+
+ Remote profile features.
+
+ string Endpoint:
+
+ Optional. Endpoint bus id.
+
+ string EndpointPath:
+
+ Optional. Endpoint object path.
+
+ Possible Errors: org.bluez.Error.InvalidArguments
+ org.bluez.Error.Failed
+
+ void Release()
+
+ This method gets called whenever the service daemon
+ unregisters the agent or whenever the Adapter where
+ the TelephonyAgent registers itself is removed.
--
1.7.1