* Re: [PATCH v4 00/16] mSBC tests
From: Frédéric Dalleau @ 2012-11-14 10:00 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1351589975-22640-1-git-send-email-frederic.dalleau@linux.intel.com>
Hi folks, Siarhei,
On 10/30/2012 10:39 AM, Frédéric Dalleau wrote:
> Hi,
>
> v4 integrate latest comments from Siarhei:
> - remove state->pending field from sbc_encoder_state
> - add armv6 and iwmmxt primitives
> - use simd primitive instead of neon
> - reorder patches so that the SBC_MSBC flag is exposed to users only when
> implementation is complete.
Any feedback ?
Thanks,
Frédéric
^ permalink raw reply
* Re: [RFC] Bluetooth: Add BT_DEFER_SETUP option to sco socket
From: Andrei Emeltchenko @ 2012-11-14 9:17 UTC (permalink / raw)
To: Frédéric Dalleau; +Cc: linux-bluetooth
In-Reply-To: <1352830825-13987-2-git-send-email-frederic.dalleau@linux.intel.com>
Hi Frédéric,
On Tue, Nov 13, 2012 at 07:20:25PM +0100, Frédéric Dalleau wrote:
> In order to authenticate and later configure an incoming SCO connection, the
> BT_DEFER_SETUP option is added.
> When an connection is requested, the listening socket is unblocked but the
> effective connection setup happens only on first recv.
> Any send between accept and recv fails with -ENOTCONN.
> ---
Apart from other comments..
> +static inline int hci_proto_defered(struct hci_dev *hdev, bdaddr_t *bdaddr,
I think we do not use "inline" anymore.
> + __u8 type)
> +{
> + switch (type) {
> + case SCO_LINK:
> + case ESCO_LINK:
> + return sco_defer(hdev, bdaddr);
> +
> + default:
> + return 0;
> + }
> +}
> +
> static inline void hci_proto_connect_cfm(struct hci_conn *conn, __u8 status)
> {
> switch (conn->type) {
> diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
> index 9f5c5f2..167eca0 100644
> --- a/net/bluetooth/hci_event.c
> +++ b/net/bluetooth/hci_event.c
> @@ -2047,15 +2047,54 @@ unlock:
> hci_conn_check_pending(hdev);
> }
>
> +void hci_conn_accept(struct hci_conn *conn, int mask)
> +{
> + struct hci_dev *hdev = conn->hdev;
> +
> + BT_DBG("conn %p", conn);
> +
> + if (!lmp_esco_capable(hdev)) {
> + struct hci_cp_accept_conn_req cp;
> +
> + conn->state = BT_CONNECT;
> + bacpy(&cp.bdaddr, &conn->dst);
> +
> + if (lmp_rswitch_capable(hdev) && (mask & HCI_LM_MASTER))
> + cp.role = 0x00; /* Become master */
> + else
> + cp.role = 0x01; /* Remain slave */
> +
> + hci_send_cmd(hdev, HCI_OP_ACCEPT_CONN_REQ, sizeof(cp),
> + &cp);
> + } else /* lmp_esco_capable(hdev)) */ {
> + struct hci_cp_accept_sync_conn_req cp;
> +
> + conn->state = BT_CONNECT;
> + bacpy(&cp.bdaddr, &conn->dst);
> + cp.pkt_type = cpu_to_le16(conn->pkt_type);
> +
> + cp.tx_bandwidth = __constant_cpu_to_le32(0x00001f40);
> + cp.rx_bandwidth = __constant_cpu_to_le32(0x00001f40);
> + cp.max_latency = __constant_cpu_to_le16(0xffff);
maybe some comments about those numbers
> + cp.content_format = cpu_to_le16(hdev->voice_setting);
> + cp.retrans_effort = 0xff;
> +
> + hci_send_cmd(hdev, HCI_OP_ACCEPT_SYNC_CONN_REQ,
> + sizeof(cp), &cp);
> + }
> +}
> +
...
> static int sco_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int optlen)
> {
> struct sock *sk = sock->sk;
> int err = 0;
> + u32 opt;
>
> BT_DBG("sk %p", sk);
>
> lock_sock(sk);
>
> switch (optname) {
> +
> + case BT_DEFER_SETUP:
> + if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) {
> + err = -EINVAL;
> + break;
> + }
> +
> + if (get_user(opt, (u32 __user *) optval)) {
> + err = -EFAULT;
> + break;
> + }
> +
> + if (opt)
> + set_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags);
> + else
> + clear_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags);
> + break;
> +
> default:
> err = -ENOPROTOOPT;
> break;
> @@ -753,6 +794,19 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname, char
> lock_sock(sk);
>
> switch (optname) {
> +
> + case BT_DEFER_SETUP:
> + if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) {
> + err = -EINVAL;
> + break;
> + }
> +
> + if (put_user(test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags),
> + (u32 __user *) optval))
> + err = -EFAULT;
> +
> + break;
> +
I think set/get sockopt might be implemented in a separate patch.
Maybe you can also break even into more logical chunks so this would be
easier to understand.
Best regards
Andrei Emeltchenko
^ permalink raw reply
* Re: [RFC] Bluetooth: Add BT_DEFER_SETUP option to sco socket
From: Michael Knudsen @ 2012-11-14 8:26 UTC (permalink / raw)
To: Frédéric Dalleau; +Cc: linux-bluetooth
In-Reply-To: <1352830825-13987-2-git-send-email-frederic.dalleau@linux.intel.com>
On 2012-11-13 19:20, Frédéric Dalleau wrote:
> In order to authenticate and later configure an incoming SCO connection, the
> BT_DEFER_SETUP option is added.
> When an connection is requested, the listening socket is unblocked but the
> effective connection setup happens only on first recv.
> Any send between accept and recv fails with -ENOTCONN.
I have a few comments, please see below.
> diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
> index ef5b85d..2ee0ecb 100644
> --- a/include/net/bluetooth/hci_core.h
> +++ b/include/net/bluetooth/hci_core.h
> @@ -377,6 +377,7 @@ extern int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb,
> u16 flags);
>
> extern int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr);
> +extern int sco_defer(struct hci_dev *hdev, bdaddr_t *bdaddr);
I would prefer to make sco_defer() static and call it from sco_connect_ind()
instead of as a separate step. More on this below.
> +static inline int hci_proto_defered(struct hci_dev *hdev, bdaddr_t *bdaddr,
> + __u8 type)
> +{
> + switch (type) {
> + case SCO_LINK:
> + case ESCO_LINK:
> + return sco_defer(hdev, bdaddr);
> +
> + default:
> + return 0;
> + }
> +}
My idea is to define (I'm not sure if this is exactly the same as what you
suggested yourself) a bit, LM_DEFER, that e.g. sco_connect_ind() may return
and propagate this bit to hci_conn_request_evt(). This way all policing of
accepting SCO connections is handled from a single entry point into sco.c.
> static inline void hci_proto_connect_cfm(struct hci_conn *conn, __u8 status)
> {
> switch (conn->type) {
> static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
> {
> struct hci_ev_conn_request *ev = (void *) skb->data;
> int mask = hdev->link_mode;
> + int defered;
>
> BT_DBG("%s bdaddr %pMR type 0x%x", hdev->name, &ev->bdaddr,
> ev->link_type);
>
> mask |= hci_proto_connect_ind(hdev, &ev->bdaddr, ev->link_type);
> + defered = hci_proto_defered(hdev, &ev->bdaddr, ev->link_type);
Given that these two take exactly the same parameters, I would much
rather see hci_proto_defered() folded into hci_proto_connect_ind()
and set (LM_ACCEPT | LM_DEFERRED) for deferred cases. This would
also make sure we only do one iteration over the SCO socket list and
that the same SCO sockets are taken into account. With your approach
there is a risk that one of the loops is changed and not the other.
-m.
^ permalink raw reply
* Re: [PATCH v2 00/20] CSCP plugin
From: Lucas De Marchi @ 2012-11-14 0:35 UTC (permalink / raw)
To: Andrzej Kaczmarek; +Cc: linux-bluetooth
In-Reply-To: <1352105705-13988-1-git-send-email-andrzej.kaczmarek@tieto.com>
On Mon, Nov 5, 2012 at 6:54 AM, Andrzej Kaczmarek
<andrzej.kaczmarek@tieto.com> wrote:
> Hi,
>
> Here are v2 patches for CSC profile implementation (cyclingspeed plugin).
>
> Changes since v1:
> - signal is emited when Location property is changed
> - supported locations CP procedure is done only once (see notes below)
> - test script can now calculate instantenous speed and cadence
> - few minor fixes found during testing and review
>
> It's now also tested with PTS - can pass all CSCP testcases but there are
> few things to remember if someone want to retest:
> - testcases in 4.5.3 are broken, look into erratas for updated ets file
> - TP/SPL/CO/BV-01-I shall be run after device is removed because Request
> Supported Sensor Location procedure is done only once and then result
> is cached - this is because running it every time device is connected
> will make other testcases using SC Control Point fail due to unexpected
> opcode written (and CSCP spec requires this value to be cached anyway)
> - as for now watcher shall be registered before running testcases using
> SC Control Point to make them work - this is because PTS expects that
> measurement notifications are enabled before control point indications,
> otherwise it will wait for writing control point CCC even though it
> was written before. Errata for this is already accepted and TS will be
> changed so both CCC can be written in any order.
> - TP/SPS/CO/BV-02-I seems to be broken as it expected different value to
> be written than what is displayed to tester - issue in PTS for this is
> in progress
>
>
> Comments are welcome.
I reviewed mainly the gdbus calls, particularly the DBus.Properties
ones. It looks good for me.
Lucas De Marchi
^ permalink raw reply
* Re: [PATCH v2 11/20] cyclingspeed: Add stub to use SC Control Point
From: Lucas De Marchi @ 2012-11-14 0:31 UTC (permalink / raw)
To: Andrzej Kaczmarek; +Cc: linux-bluetooth
In-Reply-To: <1352105705-13988-12-git-send-email-andrzej.kaczmarek@tieto.com>
On Mon, Nov 5, 2012 at 6:54 AM, Andrzej Kaczmarek
<andrzej.kaczmarek@tieto.com> wrote:
> This patch implements common functions to use SC Control Point.
> Individual procedures will be implemented in subsequent patches.
> ---
> profiles/cyclingspeed/cyclingspeed.c | 145 +++++++++++++++++++++++++++++++++--
> 1 file changed, 140 insertions(+), 5 deletions(-)
>
> diff --git a/profiles/cyclingspeed/cyclingspeed.c b/profiles/cyclingspeed/cyclingspeed.c
> index 5a65507..ffef6ae 100644
> --- a/profiles/cyclingspeed/cyclingspeed.c
> +++ b/profiles/cyclingspeed/cyclingspeed.c
> @@ -41,6 +41,11 @@
> #include "log.h"
> #include "cyclingspeed.h"
>
> +/* min length for ATT indication or notification: opcode (1b) + handle (2b) */
> +#define ATT_HDR_LEN 3
> +
> +#define ATT_TIMEOUT 30
> +
> #define CYCLINGSPEED_INTERFACE "org.bluez.CyclingSpeed"
> #define CYCLINGSPEED_MANAGER_INTERFACE "org.bluez.CyclingSpeedManager"
> #define CYCLINGSPEED_WATCHER_INTERFACE "org.bluez.CyclingSpeedWatcher"
> @@ -52,6 +57,25 @@
> #define WHEEL_REV_PRESENT 0x01
> #define CRANK_REV_PRESENT 0x02
>
> +#define SET_CUMULATIVE_VALUE 0x01
> +#define START_SENSOR_CALIBRATION 0x02
> +#define UPDATE_SENSOR_LOC 0x03
> +#define REQUEST_SUPPORTED_SENSOR_LOC 0x04
> +#define RESPONSE_CODE 0x10
> +
> +#define RSP_SUCCESS 0x01
> +#define RSP_NOT_SUPPORTED 0x02
> +#define RSP_INVALID_PARAM 0x03
> +#define RSP_FAILED 0x04
> +
> +struct csc;
> +
> +struct controlpoint_req {
> + struct csc *csc;
> + uint8_t opcode;
> + guint timeout;
> +};
> +
> struct csc_adapter {
> struct btd_adapter *adapter;
> GSList *devices; /* list of registered devices */
> @@ -66,6 +90,8 @@ struct csc {
> guint attioid;
> /* attio id for measurement characteristics value notifications */
> guint attio_measurement_id;
> + /* attio id for SC Control Point characteristics value indications */
> + guint attio_controlpoint_id;
>
> struct att_range *svc_range;
>
> @@ -75,6 +101,8 @@ struct csc {
> uint16_t feature;
> gboolean has_location;
> uint8_t location;
> +
> + struct controlpoint_req *pending_req;
> };
>
> struct watcher {
> @@ -216,6 +244,7 @@ static void destroy_csc(gpointer user_data)
>
> if (csc->attrib != NULL) {
> g_attrib_unregister(csc->attrib, csc->attio_measurement_id);
> + g_attrib_unregister(csc->attrib, csc->attio_controlpoint_id);
> g_attrib_unref(csc->attrib);
> }
>
> @@ -235,6 +264,35 @@ static void char_write_cb(guint8 status, const guint8 *pdu, guint16 len,
> g_free(msg);
> }
>
> +static gboolean controlpoint_timeout(gpointer user_data)
> +{
> + struct controlpoint_req *req = user_data;
> +
> + req->csc->pending_req = NULL;
> + g_free(req);
> +
> + return FALSE;
> +}
> +
> +__attribute__((unused)) /* TODO: remove once controlpoint ops are implemented */
This is a weird way to split the patches... but I don't care that much
for a bootstrap
Lucas De Marchi
^ permalink raw reply
* Re: [PATCHv1 11/12] Bluetooth: AMP: Use l2cap_physical_cfm in phylink complete evt
From: Marcel Holtmann @ 2012-11-13 22:49 UTC (permalink / raw)
To: Mat Martineau; +Cc: Andrei Emeltchenko, linux-bluetooth
In-Reply-To: <alpine.DEB.2.02.1211130911200.27104@mathewm-linux>
Hi Mat,
> > ...
> >>>>> +void amp_physical_cfm(struct hci_conn *bredr_hcon, struct hci_conn *hs_hcon)
> >>>>> +{
> >>>>> + struct hci_dev *bredr_hdev = hci_dev_hold(bredr_hcon->hdev);
> >>>>> + struct amp_mgr *mgr = hs_hcon->amp_mgr;
> >>>>> + struct l2cap_chan *bredr_chan;
> >>>>> +
> >>>>> + BT_DBG("bredr_hcon %p hs_hcon %p mgr %p", bredr_hcon, hs_hcon, mgr);
> >>>>> +
> >>>>> + if (!bredr_hdev || !mgr || !mgr->bredr_chan)
> >>>>> + return;
> >>>>> +
> >>>>> + bredr_chan = mgr->bredr_chan;
> >>>>> +
> >>>>> + set_bit(FLAG_EFS_ENABLE, &bredr_chan->flags);
> >>>>> + bredr_chan->ctrl_id = hs_hcon->remote_id;
> >>>>> + bredr_chan->hs_hcon = hs_hcon;
> >>>>> + bredr_chan->conn->mtu = hs_hcon->hdev->block_mtu;
> >>>>> + bredr_chan->fcs = L2CAP_FCS_NONE;
> >>
> >> Changing FCS requires L2CAP reconfiguration for the channel, and
> >> chan->fcs shouldn't be modified until reconfiguration happens.
> >> While it doesn't make much sense to do so, the remote device may
> >> want to keep FCS enabled. The move may also fail and you don't want
> >> to forget the original FCS setting in that case.
> >
> > So we agree that FCS shall not be used for High Speed channels. I was
> > thinking more about the case where we start sending data right over HS
> > channel. The configuration should be just started.
>
> No matter what the BlueZ implementation prefers, it must be able to
> handle a remote device that requests FCS during L2CAP config. If one
> side requests FCS, then it must be used.
>
> Do we want BlueZ to always ignore the L2CAP FCS socket option on AMP
> controllers? (Marcel?) This checksum is always redundant with 802.11
> AMP controllers.
if it is there, we should check it. Even on 802.11. Mainly just in case
someone tries to sneak packets in. But if by any means possible we
should try to disable FCS on AMP controllers.
Regards
Marcel
^ permalink raw reply
* Re: [RFC] Bluetooth: Add BT_DEFER_SETUP option to sco socket
From: Luiz Augusto von Dentz @ 2012-11-13 18:45 UTC (permalink / raw)
To: Frédéric Dalleau; +Cc: linux-bluetooth
In-Reply-To: <1352830825-13987-2-git-send-email-frederic.dalleau@linux.intel.com>
Hi Frédéric,
On Tue, Nov 13, 2012 at 8:20 PM, Frédéric Dalleau
<frederic.dalleau@linux.intel.com> wrote:
> In order to authenticate and later configure an incoming SCO connection, the
> BT_DEFER_SETUP option is added.
> When an connection is requested, the listening socket is unblocked but the
> effective connection setup happens only on first recv.
> Any send between accept and recv fails with -ENOTCONN.
I think you should detail when exactly we do defer, we need to defer
before the audio settings negotiation takes place because latter once
we add support for passing the settings this happens during this stage
before accepting.
> ---
> include/net/bluetooth/hci_core.h | 15 +++++++
> net/bluetooth/hci_event.c | 47 +++++++++++++++++++-
> net/bluetooth/sco.c | 88 ++++++++++++++++++++++++++++++++++++--
> 3 files changed, 145 insertions(+), 5 deletions(-)
>
> diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
> index ef5b85d..2ee0ecb 100644
> --- a/include/net/bluetooth/hci_core.h
> +++ b/include/net/bluetooth/hci_core.h
> @@ -377,6 +377,7 @@ extern int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb,
> u16 flags);
>
> extern int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr);
> +extern int sco_defer(struct hci_dev *hdev, bdaddr_t *bdaddr);
> extern void sco_connect_cfm(struct hci_conn *hcon, __u8 status);
> extern void sco_disconn_cfm(struct hci_conn *hcon, __u8 reason);
> extern int sco_recv_scodata(struct hci_conn *hcon, struct sk_buff *skb);
> @@ -577,6 +578,7 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst);
> int hci_conn_del(struct hci_conn *conn);
> void hci_conn_hash_flush(struct hci_dev *hdev);
> void hci_conn_check_pending(struct hci_dev *hdev);
> +void hci_conn_accept(struct hci_conn *conn, int mask);
>
> struct hci_chan *hci_chan_create(struct hci_conn *conn);
> void hci_chan_del(struct hci_chan *chan);
> @@ -796,6 +798,19 @@ static inline int hci_proto_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr,
> }
> }
>
> +static inline int hci_proto_defered(struct hci_dev *hdev, bdaddr_t *bdaddr,
> + __u8 type)
> +{
> + switch (type) {
> + case SCO_LINK:
> + case ESCO_LINK:
> + return sco_defer(hdev, bdaddr);
> +
> + default:
> + return 0;
> + }
> +}
> +
> static inline void hci_proto_connect_cfm(struct hci_conn *conn, __u8 status)
> {
> switch (conn->type) {
> diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
> index 9f5c5f2..167eca0 100644
> --- a/net/bluetooth/hci_event.c
> +++ b/net/bluetooth/hci_event.c
> @@ -2047,15 +2047,54 @@ unlock:
> hci_conn_check_pending(hdev);
> }
>
> +void hci_conn_accept(struct hci_conn *conn, int mask)
> +{
> + struct hci_dev *hdev = conn->hdev;
> +
> + BT_DBG("conn %p", conn);
> +
> + if (!lmp_esco_capable(hdev)) {
> + struct hci_cp_accept_conn_req cp;
> +
> + conn->state = BT_CONNECT;
> + bacpy(&cp.bdaddr, &conn->dst);
> +
> + if (lmp_rswitch_capable(hdev) && (mask & HCI_LM_MASTER))
> + cp.role = 0x00; /* Become master */
> + else
> + cp.role = 0x01; /* Remain slave */
> +
> + hci_send_cmd(hdev, HCI_OP_ACCEPT_CONN_REQ, sizeof(cp),
> + &cp);
> + } else /* lmp_esco_capable(hdev)) */ {
> + struct hci_cp_accept_sync_conn_req cp;
> +
> + conn->state = BT_CONNECT;
> + bacpy(&cp.bdaddr, &conn->dst);
> + cp.pkt_type = cpu_to_le16(conn->pkt_type);
> +
> + cp.tx_bandwidth = __constant_cpu_to_le32(0x00001f40);
> + cp.rx_bandwidth = __constant_cpu_to_le32(0x00001f40);
> + cp.max_latency = __constant_cpu_to_le16(0xffff);
> + cp.content_format = cpu_to_le16(hdev->voice_setting);
> + cp.retrans_effort = 0xff;
> +
> + hci_send_cmd(hdev, HCI_OP_ACCEPT_SYNC_CONN_REQ,
> + sizeof(cp), &cp);
> + }
> +}
> +
> static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
> {
> struct hci_ev_conn_request *ev = (void *) skb->data;
> int mask = hdev->link_mode;
> + int defered;
>
> BT_DBG("%s bdaddr %pMR type 0x%x", hdev->name, &ev->bdaddr,
> ev->link_type);
>
> mask |= hci_proto_connect_ind(hdev, &ev->bdaddr, ev->link_type);
> + defered = hci_proto_defered(hdev, &ev->bdaddr, ev->link_type);
>
> if ((mask & HCI_LM_ACCEPT) &&
> !hci_blacklist_lookup(hdev, &ev->bdaddr)) {
> @@ -2085,7 +2124,8 @@ static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
>
> hci_dev_unlock(hdev);
>
> - if (ev->link_type == ACL_LINK || !lmp_esco_capable(hdev)) {
> + if (ev->link_type == ACL_LINK ||
> + (!defered && !lmp_esco_capable(hdev))) {
> struct hci_cp_accept_conn_req cp;
>
> bacpy(&cp.bdaddr, &ev->bdaddr);
> @@ -2097,7 +2137,7 @@ static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
>
> hci_send_cmd(hdev, HCI_OP_ACCEPT_CONN_REQ, sizeof(cp),
> &cp);
> - } else {
> + } else if (!defered) {
> struct hci_cp_accept_sync_conn_req cp;
>
> bacpy(&cp.bdaddr, &ev->bdaddr);
> @@ -2111,6 +2151,9 @@ static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
>
> hci_send_cmd(hdev, HCI_OP_ACCEPT_SYNC_CONN_REQ,
> sizeof(cp), &cp);
> + } else {
> + hci_proto_connect_cfm(conn, 0);
> + hci_conn_put(conn);
> }
> } else {
> /* Connection rejected */
> diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c
> index 450cdcd..416801a 100644
> --- a/net/bluetooth/sco.c
> +++ b/net/bluetooth/sco.c
> @@ -662,16 +662,57 @@ static int sco_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
> return err;
> }
>
> +static int sco_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
> + struct msghdr *msg, size_t len, int flags)
> +{
> + struct sock *sk = sock->sk;
> + struct sco_pinfo *pi = sco_pi(sk);
> +
> + lock_sock(sk);
> +
> + if (sk->sk_state == BT_CONNECT &&
> + test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) {
> + hci_conn_accept(pi->conn->hcon, 0);
> + sk->sk_state = BT_CONNECT2;
> +
> + release_sock(sk);
> + return 0;
> + }
> +
> + release_sock(sk);
> +
> + return bt_sock_recvmsg(iocb, sock, msg, len, flags);
> +}
> +
> static int sco_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int optlen)
> {
> struct sock *sk = sock->sk;
> int err = 0;
> + u32 opt;
>
> BT_DBG("sk %p", sk);
>
> lock_sock(sk);
>
> switch (optname) {
> +
> + case BT_DEFER_SETUP:
> + if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) {
> + err = -EINVAL;
> + break;
> + }
> +
> + if (get_user(opt, (u32 __user *) optval)) {
> + err = -EFAULT;
> + break;
> + }
> +
> + if (opt)
> + set_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags);
> + else
> + clear_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags);
> + break;
> +
> default:
> err = -ENOPROTOOPT;
> break;
> @@ -753,6 +794,19 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname, char
> lock_sock(sk);
>
> switch (optname) {
> +
> + case BT_DEFER_SETUP:
> + if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) {
> + err = -EINVAL;
> + break;
> + }
> +
> + if (put_user(test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags),
> + (u32 __user *) optval))
> + err = -EFAULT;
> +
> + break;
> +
> default:
> err = -ENOPROTOOPT;
> break;
> @@ -874,7 +928,10 @@ static void sco_conn_ready(struct sco_conn *conn)
> hci_conn_hold(conn->hcon);
> __sco_chan_add(conn, sk, parent);
>
> - sk->sk_state = BT_CONNECTED;
> + if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(parent)->flags))
> + sk->sk_state = BT_CONNECT;
> + else
> + sk->sk_state = BT_CONNECTED;
>
> /* Wake up parent */
> parent->sk_data_ready(parent, 1);
> @@ -912,6 +969,32 @@ int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr)
> return lm;
> }
>
> +int sco_defer(struct hci_dev *hdev, bdaddr_t *bdaddr)
> +{
> + struct sock *sk;
> + struct hlist_node *node;
> + int defer = 0;
> +
> + BT_DBG("hdev %s, bdaddr %pMR", hdev->name, bdaddr);
> +
> + /* Find listening sockets */
> + read_lock(&sco_sk_list.lock);
> + sk_for_each(sk, node, &sco_sk_list.head) {
> + if (sk->sk_state != BT_LISTEN)
> + continue;
> +
> + if (!bacmp(&bt_sk(sk)->src, &hdev->bdaddr) ||
> + !bacmp(&bt_sk(sk)->src, BDADDR_ANY)) {
> + if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags))
> + defer = 1;
> + break;
> + }
> + }
> + read_unlock(&sco_sk_list.lock);
> +
> + return defer;
> +}
> +
> void sco_connect_cfm(struct hci_conn *hcon, __u8 status)
> {
> BT_DBG("hcon %p bdaddr %pMR status %d", hcon, &hcon->dst, status);
> @@ -992,7 +1075,7 @@ static const struct proto_ops sco_sock_ops = {
> .accept = sco_sock_accept,
> .getname = sco_sock_getname,
> .sendmsg = sco_sock_sendmsg,
> - .recvmsg = bt_sock_recvmsg,
> + .recvmsg = sco_sock_recvmsg,
> .poll = bt_sock_poll,
> .ioctl = bt_sock_ioctl,
> .mmap = sock_no_mmap,
> @@ -1036,7 +1119,6 @@ int __init sco_init(void)
> BT_ERR("Failed to create SCO debug file");
> }
>
> - BT_INFO("SCO socket layer initialized");
There doesn't seems to be any need to remove the line above, could you
please fix that.
--
Luiz Augusto von Dentz
^ permalink raw reply
* [RFC] Bluetooth: Add BT_DEFER_SETUP option to sco socket
From: Frédéric Dalleau @ 2012-11-13 18:20 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Frédéric Dalleau
In-Reply-To: <1352830825-13987-1-git-send-email-frederic.dalleau@linux.intel.com>
In order to authenticate and later configure an incoming SCO connection, the
BT_DEFER_SETUP option is added.
When an connection is requested, the listening socket is unblocked but the
effective connection setup happens only on first recv.
Any send between accept and recv fails with -ENOTCONN.
---
include/net/bluetooth/hci_core.h | 15 +++++++
net/bluetooth/hci_event.c | 47 +++++++++++++++++++-
net/bluetooth/sco.c | 88 ++++++++++++++++++++++++++++++++++++--
3 files changed, 145 insertions(+), 5 deletions(-)
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index ef5b85d..2ee0ecb 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -377,6 +377,7 @@ extern int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb,
u16 flags);
extern int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr);
+extern int sco_defer(struct hci_dev *hdev, bdaddr_t *bdaddr);
extern void sco_connect_cfm(struct hci_conn *hcon, __u8 status);
extern void sco_disconn_cfm(struct hci_conn *hcon, __u8 reason);
extern int sco_recv_scodata(struct hci_conn *hcon, struct sk_buff *skb);
@@ -577,6 +578,7 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst);
int hci_conn_del(struct hci_conn *conn);
void hci_conn_hash_flush(struct hci_dev *hdev);
void hci_conn_check_pending(struct hci_dev *hdev);
+void hci_conn_accept(struct hci_conn *conn, int mask);
struct hci_chan *hci_chan_create(struct hci_conn *conn);
void hci_chan_del(struct hci_chan *chan);
@@ -796,6 +798,19 @@ static inline int hci_proto_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr,
}
}
+static inline int hci_proto_defered(struct hci_dev *hdev, bdaddr_t *bdaddr,
+ __u8 type)
+{
+ switch (type) {
+ case SCO_LINK:
+ case ESCO_LINK:
+ return sco_defer(hdev, bdaddr);
+
+ default:
+ return 0;
+ }
+}
+
static inline void hci_proto_connect_cfm(struct hci_conn *conn, __u8 status)
{
switch (conn->type) {
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 9f5c5f2..167eca0 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -2047,15 +2047,54 @@ unlock:
hci_conn_check_pending(hdev);
}
+void hci_conn_accept(struct hci_conn *conn, int mask)
+{
+ struct hci_dev *hdev = conn->hdev;
+
+ BT_DBG("conn %p", conn);
+
+ if (!lmp_esco_capable(hdev)) {
+ struct hci_cp_accept_conn_req cp;
+
+ conn->state = BT_CONNECT;
+ bacpy(&cp.bdaddr, &conn->dst);
+
+ if (lmp_rswitch_capable(hdev) && (mask & HCI_LM_MASTER))
+ cp.role = 0x00; /* Become master */
+ else
+ cp.role = 0x01; /* Remain slave */
+
+ hci_send_cmd(hdev, HCI_OP_ACCEPT_CONN_REQ, sizeof(cp),
+ &cp);
+ } else /* lmp_esco_capable(hdev)) */ {
+ struct hci_cp_accept_sync_conn_req cp;
+
+ conn->state = BT_CONNECT;
+ bacpy(&cp.bdaddr, &conn->dst);
+ cp.pkt_type = cpu_to_le16(conn->pkt_type);
+
+ cp.tx_bandwidth = __constant_cpu_to_le32(0x00001f40);
+ cp.rx_bandwidth = __constant_cpu_to_le32(0x00001f40);
+ cp.max_latency = __constant_cpu_to_le16(0xffff);
+ cp.content_format = cpu_to_le16(hdev->voice_setting);
+ cp.retrans_effort = 0xff;
+
+ hci_send_cmd(hdev, HCI_OP_ACCEPT_SYNC_CONN_REQ,
+ sizeof(cp), &cp);
+ }
+}
+
static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_ev_conn_request *ev = (void *) skb->data;
int mask = hdev->link_mode;
+ int defered;
BT_DBG("%s bdaddr %pMR type 0x%x", hdev->name, &ev->bdaddr,
ev->link_type);
mask |= hci_proto_connect_ind(hdev, &ev->bdaddr, ev->link_type);
+ defered = hci_proto_defered(hdev, &ev->bdaddr, ev->link_type);
if ((mask & HCI_LM_ACCEPT) &&
!hci_blacklist_lookup(hdev, &ev->bdaddr)) {
@@ -2085,7 +2124,8 @@ static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
hci_dev_unlock(hdev);
- if (ev->link_type == ACL_LINK || !lmp_esco_capable(hdev)) {
+ if (ev->link_type == ACL_LINK ||
+ (!defered && !lmp_esco_capable(hdev))) {
struct hci_cp_accept_conn_req cp;
bacpy(&cp.bdaddr, &ev->bdaddr);
@@ -2097,7 +2137,7 @@ static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
hci_send_cmd(hdev, HCI_OP_ACCEPT_CONN_REQ, sizeof(cp),
&cp);
- } else {
+ } else if (!defered) {
struct hci_cp_accept_sync_conn_req cp;
bacpy(&cp.bdaddr, &ev->bdaddr);
@@ -2111,6 +2151,9 @@ static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
hci_send_cmd(hdev, HCI_OP_ACCEPT_SYNC_CONN_REQ,
sizeof(cp), &cp);
+ } else {
+ hci_proto_connect_cfm(conn, 0);
+ hci_conn_put(conn);
}
} else {
/* Connection rejected */
diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c
index 450cdcd..416801a 100644
--- a/net/bluetooth/sco.c
+++ b/net/bluetooth/sco.c
@@ -662,16 +662,57 @@ static int sco_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
return err;
}
+static int sco_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
+ struct msghdr *msg, size_t len, int flags)
+{
+ struct sock *sk = sock->sk;
+ struct sco_pinfo *pi = sco_pi(sk);
+
+ lock_sock(sk);
+
+ if (sk->sk_state == BT_CONNECT &&
+ test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) {
+ hci_conn_accept(pi->conn->hcon, 0);
+ sk->sk_state = BT_CONNECT2;
+
+ release_sock(sk);
+ return 0;
+ }
+
+ release_sock(sk);
+
+ return bt_sock_recvmsg(iocb, sock, msg, len, flags);
+}
+
static int sco_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int optlen)
{
struct sock *sk = sock->sk;
int err = 0;
+ u32 opt;
BT_DBG("sk %p", sk);
lock_sock(sk);
switch (optname) {
+
+ case BT_DEFER_SETUP:
+ if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) {
+ err = -EINVAL;
+ break;
+ }
+
+ if (get_user(opt, (u32 __user *) optval)) {
+ err = -EFAULT;
+ break;
+ }
+
+ if (opt)
+ set_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags);
+ else
+ clear_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags);
+ break;
+
default:
err = -ENOPROTOOPT;
break;
@@ -753,6 +794,19 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname, char
lock_sock(sk);
switch (optname) {
+
+ case BT_DEFER_SETUP:
+ if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) {
+ err = -EINVAL;
+ break;
+ }
+
+ if (put_user(test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags),
+ (u32 __user *) optval))
+ err = -EFAULT;
+
+ break;
+
default:
err = -ENOPROTOOPT;
break;
@@ -874,7 +928,10 @@ static void sco_conn_ready(struct sco_conn *conn)
hci_conn_hold(conn->hcon);
__sco_chan_add(conn, sk, parent);
- sk->sk_state = BT_CONNECTED;
+ if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(parent)->flags))
+ sk->sk_state = BT_CONNECT;
+ else
+ sk->sk_state = BT_CONNECTED;
/* Wake up parent */
parent->sk_data_ready(parent, 1);
@@ -912,6 +969,32 @@ int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr)
return lm;
}
+int sco_defer(struct hci_dev *hdev, bdaddr_t *bdaddr)
+{
+ struct sock *sk;
+ struct hlist_node *node;
+ int defer = 0;
+
+ BT_DBG("hdev %s, bdaddr %pMR", hdev->name, bdaddr);
+
+ /* Find listening sockets */
+ read_lock(&sco_sk_list.lock);
+ sk_for_each(sk, node, &sco_sk_list.head) {
+ if (sk->sk_state != BT_LISTEN)
+ continue;
+
+ if (!bacmp(&bt_sk(sk)->src, &hdev->bdaddr) ||
+ !bacmp(&bt_sk(sk)->src, BDADDR_ANY)) {
+ if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags))
+ defer = 1;
+ break;
+ }
+ }
+ read_unlock(&sco_sk_list.lock);
+
+ return defer;
+}
+
void sco_connect_cfm(struct hci_conn *hcon, __u8 status)
{
BT_DBG("hcon %p bdaddr %pMR status %d", hcon, &hcon->dst, status);
@@ -992,7 +1075,7 @@ static const struct proto_ops sco_sock_ops = {
.accept = sco_sock_accept,
.getname = sco_sock_getname,
.sendmsg = sco_sock_sendmsg,
- .recvmsg = bt_sock_recvmsg,
+ .recvmsg = sco_sock_recvmsg,
.poll = bt_sock_poll,
.ioctl = bt_sock_ioctl,
.mmap = sock_no_mmap,
@@ -1036,7 +1119,6 @@ int __init sco_init(void)
BT_ERR("Failed to create SCO debug file");
}
- BT_INFO("SCO socket layer initialized");
return 0;
--
1.7.9.5
^ permalink raw reply related
* [RFC] sco: BT_DEFER_SETUP for SCO sockets
From: Frédéric Dalleau @ 2012-11-13 18:20 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Frédéric Dalleau
Hi,
This is a first draft about what can be done to implement DEFER_SETUP on SCO
sockets. I couldn't test it today I just pulled bluetooth-next and build
failed, so I plan to go further tomorrow.
hci layer get some changes since previous behavior was to accept all SCO
connections.
Some questions are open :
Should we be able to defer non eSCO capable devices? This would remove a few
lines of code.
hci_proto_defered is ugly: What about a flag similar to LM_ACCEPT called
LM_DEFER that would indicate that accepting is defered.
Let me know what you think.
Best regards,
Frédéric
Frédéric Dalleau (1):
Bluetooth: Add BT_DEFER_SETUP option to sco socket
include/net/bluetooth/hci_core.h | 15 +++++++
net/bluetooth/hci_event.c | 47 +++++++++++++++++++-
net/bluetooth/sco.c | 88 ++++++++++++++++++++++++++++++++++++--
3 files changed, 145 insertions(+), 5 deletions(-)
--
1.7.9.5
^ permalink raw reply
* Re: [PATCH BlueZ 1/2] TODO: Add entry for runtime selection of drivers
From: Johan Hedberg @ 2012-11-13 17:42 UTC (permalink / raw)
To: Anderson Lizardo; +Cc: linux-bluetooth
In-Reply-To: <1352821786-20230-1-git-send-email-anderson.lizardo@openbossa.org>
Hi Lizardo,
On Tue, Nov 13, 2012, Anderson Lizardo wrote:
> This will allow to drop usage of symlinks to C files, which require
> messing with CPPFLAGS so headers can be found by the preprocessor.
>
> It also allows for easier testing, because it is not necessary to
> rebuild BlueZ to test different drivers.
> ---
> TODO | 9 +++++++++
> 1 file changed, 9 insertions(+)
Both patches have been applied. Thanks.
Johan
^ permalink raw reply
* Fw: [Bug 50541] New: Kernel OOPS in hidp when resuming from suspend
From: Stephen Hemminger @ 2012-11-13 17:33 UTC (permalink / raw)
To: Marcel Holtmann; +Cc: linux-bluetooth
Begin forwarded message:
Date: Tue, 13 Nov 2012 17:04:18 +0000 (UTC)
From: bugzilla-daemon@bugzilla.kernel.org
To: shemminger@linux-foundation.org
Subject: [Bug 50541] New: Kernel OOPS in hidp when resuming from suspend
https://bugzilla.kernel.org/show_bug.cgi?id=50541
Summary: Kernel OOPS in hidp when resuming from suspend
Product: Networking
Version: 2.5
Kernel Version: 3.5
Platform: All
OS/Version: Linux
Tree: Mainline
Status: NEW
Severity: normal
Priority: P1
Component: Other
AssignedTo: shemminger@linux-foundation.org
ReportedBy: karllinuxtest.relton@ntlworld.com
Regression: Yes
Somewhere between 3.2 and 3.5 an issue with bluetooth keyboards on resuming
from suspend crept in.
On a resume I now get the following:
Oct 23 15:42:34 laptop2 kernel: [10357.752131] BUG: unable to handle kernel
NULL pointer dereference at (null)
Oct 23 15:42:34 laptop2 kernel: [10357.752273] IP: [<c1037a58>]
__ticket_spin_lock+0x8/0x30
Oct 23 15:42:34 laptop2 kernel: [10357.752379] *pdpt = 0000000000000000 *pde =
f000ec62f000ec62
Oct 23 15:42:34 laptop2 kernel: [10357.752485] Oops: 0002 [#1] SMP
Oct 23 15:42:34 laptop2 kernel: [10357.752551] Modules linked in: hid_generic
hid_apple hidp hid bnep rfcomm parport_pc ppdev autofs4 nfsd nfs binfmt_misc
lockd fscache auth_rpcgss nfs_acl sunrpc nls_iso8859_1 dm_crypt arc4
snd_hda_codec_idt snd_hda_intel snd_hda_codec joydev coretemp pcmcia iwlwifi
snd_hwdep snd_pcm yenta_socket snd_seq_midi btusb pcmcia_rsrc snd_rawmidi
pcmcia_core mac80211 kvm bluetooth snd_seq_midi_event snd_seq dell_laptop
dell_wmi cfg80211 snd_timer snd_seq_device microcode psmouse gpio_ich dcdbas
serio_raw sparse_keymap lpc_ich mac_hid snd soundcore snd_page_alloc
firewire_sbp2 lp parport firewire_ohci firewire_core crc_itu_t tg3 i915 wmi
drm_kms_helper drm i2c_algo_bit video
Oct 23 15:42:34 laptop2 kernel: [10357.753834]
Oct 23 15:42:34 laptop2 kernel: [10357.753868] Pid: 10165, comm:
khidpd_05ac0256 Tainted: G W 3.5.0-17-generic #28-Ubuntu Dell Inc.
Latitude D630 /0KU184
Oct 23 15:42:34 laptop2 kernel: [10357.754104] EIP: 0060:[<c1037a58>] EFLAGS:
00010096 CPU: 1
Oct 23 15:42:34 laptop2 kernel: [10357.754205] EIP is at
__ticket_spin_lock+0x8/0x30
Oct 23 15:42:34 laptop2 kernel: [10357.754286] EAX: 00000000 EBX: 00000296 ECX:
00000000 EDX: 00000100
Oct 23 15:42:34 laptop2 kernel: [10357.754393] ESI: dfaedfa4 EDI: 00000000 EBP:
dfaedefc ESP: dfaedefc
Oct 23 15:42:34 laptop2 kernel: [10357.754501] DS: 007b ES: 007b FS: 00d8 GS:
00e0 SS: 0068
Oct 23 15:42:34 laptop2 kernel: [10357.754594] CR0: 8005003b CR2: 00000000 CR3:
01974000 CR4: 000007f0
Oct 23 15:42:34 laptop2 kernel: [10357.754702] DR0: 00000000 DR1: 00000000 DR2:
00000000 DR3: 00000000
Oct 23 15:42:34 laptop2 kernel: [10357.754808] DR6: ffff0ff0 DR7: 00000400
Oct 23 15:42:34 laptop2 kernel: [10357.754875] Process khidpd_05ac0256 (pid:
10165, ti=dfaec000 task=df2b0cb0 task.ti=dfaec000)
Oct 23 15:42:34 laptop2 kernel: [10357.755021] Stack:
Oct 23 15:42:34 laptop2 kernel: [10357.755058] dfaedf04 c1037af8 dfaedf18
c15c924d dfaedf98 dfaedfa4 00000000 dfaedf2c
Oct 23 15:42:34 laptop2 kernel: [10357.755221] c10654b3 00000000 cfb0b800
e56d0c00 dfaedfb8 f902ea9b 00000000 00000001
Oct 23 15:42:34 laptop2 kernel: [10357.755383] df2b0cb0 cfb0b8c0 e56d1a00
e56d0c00 cfb0b890 cfb0b880 df2b0cb0 e56d1a48
Oct 23 15:42:34 laptop2 kernel: [10357.755544] Call Trace:
Oct 23 15:42:34 laptop2 kernel: [10357.755595] [<c1037af8>]
default_spin_lock_flags+0x8/0x10
Oct 23 15:42:34 laptop2 kernel: [10357.755698] [<c15c924d>]
_raw_spin_lock_irqsave+0x2d/0x40
Oct 23 15:42:34 laptop2 kernel: [10357.755800] [<c10654b3>]
finish_wait+0x33/0x70
Oct 23 15:42:34 laptop2 kernel: [10357.755885] [<f902ea9b>]
hidp_session+0x48b/0x730 [hidp]
Oct 23 15:42:34 laptop2 kernel: [10357.755985] [<c1075e90>] ?
try_to_wake_up+0x230/0x230
Oct 23 15:42:34 laptop2 kernel: [10357.756079] [<c1075e90>] ?
try_to_wake_up+0x230/0x230
Oct 23 15:42:34 laptop2 kernel: [10357.756090] [<c10654f0>] ?
finish_wait+0x70/0x70
Oct 23 15:42:34 laptop2 kernel: [10357.756090] [<f902e610>] ?
hidp_input_report.isra.4+0x2a0/0x2a0 [hidp]
Oct 23 15:42:34 laptop2 kernel: [10357.756090] [<c1064dd2>] kthread+0x72/0x80
Oct 23 15:42:34 laptop2 kernel: [10357.756090] [<c1064d60>] ?
kthread_freezable_should_stop+0x60/0x60
Oct 23 15:42:34 laptop2 kernel: [10357.756090] [<c15d04fe>]
kernel_thread_helper+0x6/0x10
Oct 23 15:42:34 laptop2 kernel: [10357.756090] Code: b9 fa 78 03 c1 e9 59 ff ff
ff 90 b8 03 79 03 c1 b9 00 79 03 c1 e9 49 ff ff ff 66 90 66 90 66 90 66 90 90
55 ba 00 01 00 00 89 e5 <f0> 66 0f c1 10 0f b6 ce 38 d1 74 0d 8d 74 26 00 f3 90
0f b6 10
Oct 23 15:42:34 laptop2 kernel: [10357.756090] EIP: [<c1037a58>]
__ticket_spin_lock+0x8/0x30 SS:ESP 0068:dfaedefc
Oct 23 15:42:34 laptop2 kernel: [10357.756090] CR2: 0000000000000000
Oct 23 15:42:34 laptop2 kernel: [10357.756090] ---[ end trace 9a445e0f7d0acd41
]---
The issue seems to be in hidp, which makes sense.
Problem still exists in 3.7.
--
Configure bugmail: https://bugzilla.kernel.org/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
You are the assignee for the bug.
^ permalink raw reply
* Re: [PATCHv1 11/12] Bluetooth: AMP: Use l2cap_physical_cfm in phylink complete evt
From: Mat Martineau @ 2012-11-13 17:29 UTC (permalink / raw)
To: Andrei Emeltchenko; +Cc: linux-bluetooth, marcel
In-Reply-To: <20121112092641.GA10008@aemeltch-MOBL1>
Hi Andrei -
On Mon, 12 Nov 2012, Andrei Emeltchenko wrote:
> Hi Mat,
>
> On Fri, Nov 02, 2012 at 08:39:09AM -0700, Mat Martineau wrote:
> ...
>>>>> +void amp_physical_cfm(struct hci_conn *bredr_hcon, struct hci_conn *hs_hcon)
>>>>> +{
>>>>> + struct hci_dev *bredr_hdev = hci_dev_hold(bredr_hcon->hdev);
>>>>> + struct amp_mgr *mgr = hs_hcon->amp_mgr;
>>>>> + struct l2cap_chan *bredr_chan;
>>>>> +
>>>>> + BT_DBG("bredr_hcon %p hs_hcon %p mgr %p", bredr_hcon, hs_hcon, mgr);
>>>>> +
>>>>> + if (!bredr_hdev || !mgr || !mgr->bredr_chan)
>>>>> + return;
>>>>> +
>>>>> + bredr_chan = mgr->bredr_chan;
>>>>> +
>>>>> + set_bit(FLAG_EFS_ENABLE, &bredr_chan->flags);
>>>>> + bredr_chan->ctrl_id = hs_hcon->remote_id;
>>>>> + bredr_chan->hs_hcon = hs_hcon;
>>>>> + bredr_chan->conn->mtu = hs_hcon->hdev->block_mtu;
>>>>> + bredr_chan->fcs = L2CAP_FCS_NONE;
>>
>> Changing FCS requires L2CAP reconfiguration for the channel, and
>> chan->fcs shouldn't be modified until reconfiguration happens.
>> While it doesn't make much sense to do so, the remote device may
>> want to keep FCS enabled. The move may also fail and you don't want
>> to forget the original FCS setting in that case.
>
> So we agree that FCS shall not be used for High Speed channels. I was
> thinking more about the case where we start sending data right over HS
> channel. The configuration should be just started.
No matter what the BlueZ implementation prefers, it must be able to
handle a remote device that requests FCS during L2CAP config. If one
side requests FCS, then it must be used.
Do we want BlueZ to always ignore the L2CAP FCS socket option on AMP
controllers? (Marcel?) This checksum is always redundant with 802.11
AMP controllers.
> What would be the better place to init FCS? l2cap_physical_cfm after
> checking channel moving status?
I think it should be as late as possible before L2CAP configuration
begins in order to be sure the channel is really on AMP. For the
"create channel" case, set_default_fcs could see if an AMP link is
being used and turn the FCS off.
>>>> Sorry I missed this earlier: bredr_chan needs to be locked before
>>>> changing it. I suggest passing the information to
>>>> l2cap_physical_cfm and letting that function update the structure
>>>> members while it holds the lock.
>>>
>>> what about locking here and changing l2cap_physical_cfm to unlocked
>>> __l2cap_physical_cfm ?
>>
>> My preference is to not manipulate l2cap_chan too much outside of
>> l2cap_core, and to keep the channel move or channel create state
>> machines inside l2cap_core. l2cap_physical_cfm checks the channel
>> state before modifying it. The move or new connection may have been
>> canceled or be in the wrong state, in which case the structure
>> should not be modified even though the physical link was completed.
>
> so maybe we shall move some code to l2cap_physical_cfm ?
Yes, I think l2cap_physical_cfm is a better place for changes to
l2cap_chan.
Regards,
--
Mat Martineau
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
^ permalink raw reply
* Re: [PATCH BlueZ 1/2] audio: Fix headset NULL pointer dereference during AT+BLDN response
From: Syam Sidhardhan @ 2012-11-13 16:27 UTC (permalink / raw)
To: Syam Sidhardhan, Syam Sidhardhan, linux-bluetooth
In-Reply-To: <20121113154019.GA29811@x220>
Hi Johan,
On Tue, Nov 13, 2012 at 9:10 PM, Johan Hedberg <johan.hedberg@gmail.com> wrote:
> Hi Syam,
>
> On Fri, Nov 09, 2012, Syam Sidhardhan wrote:
>> On Tue, Oct 23, 2012 at 7:27 PM, Syam Sidhardhan <s.syam@samsung.com> wrote:
>> > While waiting for the AT+BLDN asynchronous response, if RFCOMM got
>> > disconnected, then respose will cause NULL pointer dereference.
>> >
>> > During headset disconnection, the headset state changes from
>> > HEADSET_STATE_CONNECTED to HEADSET_STATE_DISCONNECTED along with
>> > freeing the dev->headset. During the response, in telephony_generic_rsp
>> > its dereferencing.
>> >
>> > Log:
>> > bluetoothd[5573]: audio/headset.c:handle_event() Received AT+BLDN
>> > bluetoothd[5573]: audio/telephony.c:telephony_last_dialed_number_req()
>> > telephony-tizen: last dialed number request
>> > bluetoothd[5573]: audio/telephony.c:dbus_method_call_send() +
>> > bluetoothd[5573]: audio/telephony.c:dbus_method_call_send() -
>> > bluetoothd[5573]: Endpoint replied with an error: org.freedesktop.DBus\
>> > .Error.NoReply
>> > bluetoothd[5573]: audio/telephony.c:telephony_device_disconnected()
>> > telephony-tizen: device 0x40439b60 disconnected
>> > bluetoothd[5573]: audio/headset.c:headset_set_state() State changed
>> > /org/bluez/5573/hci0/dev_BC_47_60_F5_88_89:
>> > HEADSET_STATE_CONNECTED -> HEADSET_STATE_DISCONNECTED
>> > bluetoothd[5573]: audio/media.c:headset_state_changed()
>> > bluetoothd[5573]: audio/media.c:headset_state_changed() Clear endpoint
>> > 0x40430620
>> > bluetoothd[5573]: audio/telephony.c:telephony_dial_number_reply()
>> > redial_reply
>> > bluetoothd[5573]: audio/telephony.c:telephony_dial_number_reply()
>> > dial_reply reply: No Call log
>> > ---
>> > audio/headset.c | 3 +++
>> > 1 files changed, 3 insertions(+), 0 deletions(-)
>> >
>> > diff --git a/audio/headset.c b/audio/headset.c
>> > index bd83a65..30d24cf 100644
>> > --- a/audio/headset.c
>> > +++ b/audio/headset.c
>> > @@ -689,6 +689,9 @@ static int telephony_generic_rsp(struct audio_device *device, cme_error_t err)
>> > struct headset *hs = device->headset;
>> > struct headset_slc *slc = hs->slc;
>> >
>> > + if (!slc)
>> > + return -EIO;
>> > +
>> > if ((err != CME_ERROR_NONE) && slc->cme_enabled)
>> > return headset_send(hs, "\r\n+CME ERROR: %d\r\n", err);
>> >
>>
>> ping.
>
> This file doesn't exist in bluez.git anymore.
>
Yes, I noticed Luiz patch which removes the headset.c and other
stuff. Thank you.
Regards,
Syam.
^ permalink raw reply
* net, bluetooth: object debug warning in bt_host_release()
From: Sasha Levin @ 2012-11-13 16:18 UTC (permalink / raw)
To: marcel, gustavo, Johan Hedberg, David S. Miller
Cc: linux-bluetooth, netdev, linux-kernel@vger.kernel.org, Dave Jones
Hi all,
While fuzzing with trinity on a KVM tools (lkvm) guest running latest -next kernel I've
stumbled on the following:
[ 1434.201149] ------------[ cut here ]------------
[ 1434.204998] WARNING: at lib/debugobjects.c:261 debug_print_object+0x8e/0xb0()
[ 1434.208324] ODEBUG: free active (active state 0) object type: work_struct hint: hci_power_on+0x0/0x90
[ 1434.210386] Pid: 8564, comm: trinity-child25 Tainted: G W 3.7.0-rc5-next-20121112-sasha-00018-g2f4ce0e #127
[ 1434.210760] Call Trace:
[ 1434.210760] [<ffffffff819f3d6e>] ? debug_print_object+0x8e/0xb0
[ 1434.210760] [<ffffffff8110b887>] warn_slowpath_common+0x87/0xb0
[ 1434.210760] [<ffffffff8110b911>] warn_slowpath_fmt+0x41/0x50
[ 1434.210760] [<ffffffff819f3d6e>] debug_print_object+0x8e/0xb0
[ 1434.210760] [<ffffffff8376b750>] ? hci_dev_open+0x310/0x310
[ 1434.210760] [<ffffffff83bf94e5>] ? _raw_spin_unlock_irqrestore+0x55/0xa0
[ 1434.210760] [<ffffffff819f3ee5>] __debug_check_no_obj_freed+0xa5/0x230
[ 1434.210760] [<ffffffff83785db0>] ? bt_host_release+0x10/0x20
[ 1434.210760] [<ffffffff819f4d15>] debug_check_no_obj_freed+0x15/0x20
[ 1434.210760] [<ffffffff8125eee7>] kfree+0x227/0x330
[ 1434.210760] [<ffffffff83785db0>] bt_host_release+0x10/0x20
[ 1434.210760] [<ffffffff81e539e5>] device_release+0x65/0xc0
[ 1434.210760] [<ffffffff819d3975>] kobject_cleanup+0x145/0x190
[ 1434.210760] [<ffffffff819d39cd>] kobject_release+0xd/0x10
[ 1434.210760] [<ffffffff819d33cc>] kobject_put+0x4c/0x60
[ 1434.210760] [<ffffffff81e548b2>] put_device+0x12/0x20
[ 1434.210760] [<ffffffff8376a334>] hci_free_dev+0x24/0x30
[ 1434.210760] [<ffffffff82fd8fe1>] vhci_release+0x31/0x60
[ 1434.210760] [<ffffffff8127be12>] __fput+0x122/0x250
[ 1434.210760] [<ffffffff811cab0d>] ? rcu_user_exit+0x9d/0xd0
[ 1434.210760] [<ffffffff8127bf49>] ____fput+0x9/0x10
[ 1434.210760] [<ffffffff81133402>] task_work_run+0xb2/0xf0
[ 1434.210760] [<ffffffff8106cfa7>] do_notify_resume+0x77/0xa0
[ 1434.210760] [<ffffffff83bfb0ea>] int_signal+0x12/0x17
[ 1434.210760] ---[ end trace a6d57fefbc8a8cc7 ]---
Not that the guest doesn't emulate anything that looks like a bluetooth device or
has bluetooth capabilities.
Thanks,
Sasha
^ permalink raw reply
* [PATCH BlueZ 2/2] TODO: Trivial typo fixes
From: Anderson Lizardo @ 2012-11-13 15:49 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Anderson Lizardo
In-Reply-To: <1352821786-20230-1-git-send-email-anderson.lizardo@openbossa.org>
---
TODO | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/TODO b/TODO
index 4f58423..ad3232b 100644
--- a/TODO
+++ b/TODO
@@ -24,7 +24,7 @@ General
Priority: high
Complexity: C4
-- Rename glib-helper file to a more convenient name. The ideia is try to keep
+- Rename glib-helper file to a more convenient name. The idea is try to keep
only sdp helpers functions. bt_* prefix shall be also changed.
Priority: Low
@@ -48,7 +48,7 @@ General
BlueZ 5
=======
-Priority/Complexity ommited as all items are required before 5.0 is
+Priority/Complexity omitted as all items are required before 5.0 is
released.
- Remove telephony driver framework and replace with a org.bluez.Profile
@@ -163,7 +163,7 @@ Low Energy
Complexity: C1
- Static random address setup and storage. Once this address is written
- in the a given remote, the address can not be changed anymore.
+ in a given remote, the address can not be changed anymore.
Priority: Low
Complexity: C1
@@ -178,7 +178,7 @@ Low Energy
- Device Name Characteristic is a GAP characteristic for Low Energy. This
characteristic shall be integrated/used in the discovery procedure. The
- ideia is to report the value of this characteristic using DeviceFound signals.
+ idea is to report the value of this characteristic using DeviceFound signals.
Discussion with the community is needed before to start this task. Other GAP
characteristics for LE needs to follow a similar approach. It is not clear
if all GAP characteristics can be exposed using properties instead of a primary
@@ -268,7 +268,7 @@ ATT/GATT
Priority: Low
Complecity: C1
-- Add sdp discovery support to gattool with BR (--sdp, default is 0x1f)
+- Add sdp discovery support to gatttool with BR (--sdp, default is 0x1f)
Priority: Low
Complexity: C1
--
1.7.9.5
^ permalink raw reply related
* [PATCH BlueZ 1/2] TODO: Add entry for runtime selection of drivers
From: Anderson Lizardo @ 2012-11-13 15:49 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Anderson Lizardo
This will allow to drop usage of symlinks to C files, which require
messing with CPPFLAGS so headers can be found by the preprocessor.
It also allows for easier testing, because it is not necessary to
rebuild BlueZ to test different drivers.
---
TODO | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/TODO b/TODO
index ca14955..4f58423 100644
--- a/TODO
+++ b/TODO
@@ -36,6 +36,15 @@ General
Priority: Low
Complexity: C1
+- Remove usage of symlinks for drivers, such as profiles/input/suspend.c and
+ profiles/sap/sap.c. Instead, select drivers at runtime by using config
+ options or probing for running D-Bus services (using e.g.
+ g_dbus_add_service_watch()). Idea first mentioned on
+ http://thread.gmane.org/gmane.linux.bluez.kernel/30175/focus=30190.
+
+ Priority: Low
+ Complexity: C2
+
BlueZ 5
=======
--
1.7.9.5
^ permalink raw reply related
* Re: [PATCH BlueZ 1/2] audio: Fix headset NULL pointer dereference during AT+BLDN response
From: Johan Hedberg @ 2012-11-13 15:40 UTC (permalink / raw)
To: Syam Sidhardhan; +Cc: Syam Sidhardhan, linux-bluetooth
In-Reply-To: <CAFBvHifFX-8vcQciLktVhUgjN7Z-QaQmST2Jbi=gKtz96iHLvg@mail.gmail.com>
Hi Syam,
On Fri, Nov 09, 2012, Syam Sidhardhan wrote:
> On Tue, Oct 23, 2012 at 7:27 PM, Syam Sidhardhan <s.syam@samsung.com> wrote:
> > While waiting for the AT+BLDN asynchronous response, if RFCOMM got
> > disconnected, then respose will cause NULL pointer dereference.
> >
> > During headset disconnection, the headset state changes from
> > HEADSET_STATE_CONNECTED to HEADSET_STATE_DISCONNECTED along with
> > freeing the dev->headset. During the response, in telephony_generic_rsp
> > its dereferencing.
> >
> > Log:
> > bluetoothd[5573]: audio/headset.c:handle_event() Received AT+BLDN
> > bluetoothd[5573]: audio/telephony.c:telephony_last_dialed_number_req()
> > telephony-tizen: last dialed number request
> > bluetoothd[5573]: audio/telephony.c:dbus_method_call_send() +
> > bluetoothd[5573]: audio/telephony.c:dbus_method_call_send() -
> > bluetoothd[5573]: Endpoint replied with an error: org.freedesktop.DBus\
> > .Error.NoReply
> > bluetoothd[5573]: audio/telephony.c:telephony_device_disconnected()
> > telephony-tizen: device 0x40439b60 disconnected
> > bluetoothd[5573]: audio/headset.c:headset_set_state() State changed
> > /org/bluez/5573/hci0/dev_BC_47_60_F5_88_89:
> > HEADSET_STATE_CONNECTED -> HEADSET_STATE_DISCONNECTED
> > bluetoothd[5573]: audio/media.c:headset_state_changed()
> > bluetoothd[5573]: audio/media.c:headset_state_changed() Clear endpoint
> > 0x40430620
> > bluetoothd[5573]: audio/telephony.c:telephony_dial_number_reply()
> > redial_reply
> > bluetoothd[5573]: audio/telephony.c:telephony_dial_number_reply()
> > dial_reply reply: No Call log
> > ---
> > audio/headset.c | 3 +++
> > 1 files changed, 3 insertions(+), 0 deletions(-)
> >
> > diff --git a/audio/headset.c b/audio/headset.c
> > index bd83a65..30d24cf 100644
> > --- a/audio/headset.c
> > +++ b/audio/headset.c
> > @@ -689,6 +689,9 @@ static int telephony_generic_rsp(struct audio_device *device, cme_error_t err)
> > struct headset *hs = device->headset;
> > struct headset_slc *slc = hs->slc;
> >
> > + if (!slc)
> > + return -EIO;
> > +
> > if ((err != CME_ERROR_NONE) && slc->cme_enabled)
> > return headset_send(hs, "\r\n+CME ERROR: %d\r\n", err);
> >
>
> ping.
This file doesn't exist in bluez.git anymore.
Johan
^ permalink raw reply
* Re: [PATCH BlueZ 00/13] Remove HFP implementation
From: Luiz Augusto von Dentz @ 2012-11-13 15:39 UTC (permalink / raw)
To: Luiz Augusto von Dentz, linux-bluetooth
In-Reply-To: <20121113141639.GA28578@x220>
Hi Johan,
On Tue, Nov 13, 2012 at 4:16 PM, Johan Hedberg <johan.hedberg@gmail.com> wrote:
> Hi Luiz,
>
> On Tue, Nov 13, 2012, Luiz Augusto von Dentz wrote:
>> These changes remove HFP implemention from audio plugin and move audio
>> under profiles directory.
>>
>> HFP can be emulated by test-profile -u hfp, all the telephony backends
>> are gone as well since this is now suppose to be a external profile
>> probably done inside the telephony stack e.g. oFono in use in the
>> platform.
>>
>> Luiz Augusto von Dentz (13):
>> audio: Remove HFP and HSP headset role support
>> audio: Remove HFP option from audio.conf
>> audio: Remove code using headset
>> build: Remove telephony-maemo5.c
>> build: Remove telephony-maemo6.c
>> build: Remove telephony-ofono.c
>> build: Remove telephony-dummy.c
>> build: Remove telephony.h
>> build: Remove headset.c and headset.h
>> audio: Remove code HFP and HS gateway role
>> build: Remove gateway.c and gateway.h
>> audio: Move to profiles directory
>> audio: Add support for passing endpoints as custom property
>>
>> Makefile.am | 48 +-
>> audio/gateway.c | 1048 ------------
>> audio/gateway.h | 75 -
>> audio/headset.c | 2825 -------------------------------
>> audio/headset.h | 112 --
>> audio/manager.c | 1387 ---------------
>> audio/telephony-dummy.c | 444 -----
>> audio/telephony-maemo5.c | 2102 -----------------------
>> audio/telephony-maemo6.c | 2196 ------------------------
>> audio/telephony-ofono.c | 1636 ------------------
>> audio/telephony.h | 244 ---
>> {audio => profiles/audio}/a2dp-codecs.h | 0
>> {audio => profiles/audio}/a2dp.c | 0
>> {audio => profiles/audio}/a2dp.h | 0
>> {audio => profiles/audio}/audio.conf | 4 -
>> {audio => profiles/audio}/avctp.c | 0
>> {audio => profiles/audio}/avctp.h | 0
>> {audio => profiles/audio}/avdtp.c | 0
>> {audio => profiles/audio}/avdtp.h | 0
>> {audio => profiles/audio}/avrcp.c | 0
>> {audio => profiles/audio}/avrcp.h | 0
>> {audio => profiles/audio}/control.c | 0
>> {audio => profiles/audio}/control.h | 0
>> {audio => profiles/audio}/device.c | 198 +--
>> {audio => profiles/audio}/device.h | 2 -
>> {audio => profiles/audio}/main.c | 98 +-
>> profiles/audio/manager.c | 552 ++++++
>> {audio => profiles/audio}/manager.h | 7 +-
>> {audio => profiles/audio}/media.c | 245 ++-
>> {audio => profiles/audio}/media.h | 0
>> {audio => profiles/audio}/player.c | 0
>> {audio => profiles/audio}/player.h | 0
>> {audio => profiles/audio}/rtp.h | 0
>> {audio => profiles/audio}/sink.c | 0
>> {audio => profiles/audio}/sink.h | 0
>> {audio => profiles/audio}/source.c | 0
>> {audio => profiles/audio}/source.h | 0
>> {audio => profiles/audio}/transport.c | 380 -----
>> {audio => profiles/audio}/transport.h | 0
>> 39 files changed, 673 insertions(+), 12930 deletions(-)
>> delete mode 100644 audio/gateway.c
>> delete mode 100644 audio/gateway.h
>> delete mode 100644 audio/headset.c
>> delete mode 100644 audio/headset.h
>> delete mode 100644 audio/manager.c
>> 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
>> delete mode 100644 audio/telephony.h
>> rename {audio => profiles/audio}/a2dp-codecs.h (100%)
>> rename {audio => profiles/audio}/a2dp.c (100%)
>> rename {audio => profiles/audio}/a2dp.h (100%)
>> rename {audio => profiles/audio}/audio.conf (93%)
>> rename {audio => profiles/audio}/avctp.c (100%)
>> rename {audio => profiles/audio}/avctp.h (100%)
>> rename {audio => profiles/audio}/avdtp.c (100%)
>> rename {audio => profiles/audio}/avdtp.h (100%)
>> rename {audio => profiles/audio}/avrcp.c (100%)
>> rename {audio => profiles/audio}/avrcp.h (100%)
>> rename {audio => profiles/audio}/control.c (100%)
>> rename {audio => profiles/audio}/control.h (100%)
>> rename {audio => profiles/audio}/device.c (69%)
>> rename {audio => profiles/audio}/device.h (96%)
>> rename {audio => profiles/audio}/main.c (53%)
>> create mode 100644 profiles/audio/manager.c
>> rename {audio => profiles/audio}/manager.h (89%)
>> rename {audio => profiles/audio}/media.c (91%)
>> rename {audio => profiles/audio}/media.h (100%)
>> rename {audio => profiles/audio}/player.c (100%)
>> rename {audio => profiles/audio}/player.h (100%)
>> rename {audio => profiles/audio}/rtp.h (100%)
>> rename {audio => profiles/audio}/sink.c (100%)
>> rename {audio => profiles/audio}/sink.h (100%)
>> rename {audio => profiles/audio}/source.c (100%)
>> rename {audio => profiles/audio}/source.h (100%)
>> rename {audio => profiles/audio}/transport.c (72%)
>> rename {audio => profiles/audio}/transport.h (100%)
>
> Looks good to me. Feel free to apply.
Pushed upstream.
--
Luiz Augusto von Dentz
^ permalink raw reply
* Re: [PATCH BlueZ 00/13] Remove HFP implementation
From: Johan Hedberg @ 2012-11-13 14:16 UTC (permalink / raw)
To: Luiz Augusto von Dentz; +Cc: linux-bluetooth
In-Reply-To: <1352812880-7306-1-git-send-email-luiz.dentz@gmail.com>
Hi Luiz,
On Tue, Nov 13, 2012, Luiz Augusto von Dentz wrote:
> These changes remove HFP implemention from audio plugin and move audio
> under profiles directory.
>
> HFP can be emulated by test-profile -u hfp, all the telephony backends
> are gone as well since this is now suppose to be a external profile
> probably done inside the telephony stack e.g. oFono in use in the
> platform.
>
> Luiz Augusto von Dentz (13):
> audio: Remove HFP and HSP headset role support
> audio: Remove HFP option from audio.conf
> audio: Remove code using headset
> build: Remove telephony-maemo5.c
> build: Remove telephony-maemo6.c
> build: Remove telephony-ofono.c
> build: Remove telephony-dummy.c
> build: Remove telephony.h
> build: Remove headset.c and headset.h
> audio: Remove code HFP and HS gateway role
> build: Remove gateway.c and gateway.h
> audio: Move to profiles directory
> audio: Add support for passing endpoints as custom property
>
> Makefile.am | 48 +-
> audio/gateway.c | 1048 ------------
> audio/gateway.h | 75 -
> audio/headset.c | 2825 -------------------------------
> audio/headset.h | 112 --
> audio/manager.c | 1387 ---------------
> audio/telephony-dummy.c | 444 -----
> audio/telephony-maemo5.c | 2102 -----------------------
> audio/telephony-maemo6.c | 2196 ------------------------
> audio/telephony-ofono.c | 1636 ------------------
> audio/telephony.h | 244 ---
> {audio => profiles/audio}/a2dp-codecs.h | 0
> {audio => profiles/audio}/a2dp.c | 0
> {audio => profiles/audio}/a2dp.h | 0
> {audio => profiles/audio}/audio.conf | 4 -
> {audio => profiles/audio}/avctp.c | 0
> {audio => profiles/audio}/avctp.h | 0
> {audio => profiles/audio}/avdtp.c | 0
> {audio => profiles/audio}/avdtp.h | 0
> {audio => profiles/audio}/avrcp.c | 0
> {audio => profiles/audio}/avrcp.h | 0
> {audio => profiles/audio}/control.c | 0
> {audio => profiles/audio}/control.h | 0
> {audio => profiles/audio}/device.c | 198 +--
> {audio => profiles/audio}/device.h | 2 -
> {audio => profiles/audio}/main.c | 98 +-
> profiles/audio/manager.c | 552 ++++++
> {audio => profiles/audio}/manager.h | 7 +-
> {audio => profiles/audio}/media.c | 245 ++-
> {audio => profiles/audio}/media.h | 0
> {audio => profiles/audio}/player.c | 0
> {audio => profiles/audio}/player.h | 0
> {audio => profiles/audio}/rtp.h | 0
> {audio => profiles/audio}/sink.c | 0
> {audio => profiles/audio}/sink.h | 0
> {audio => profiles/audio}/source.c | 0
> {audio => profiles/audio}/source.h | 0
> {audio => profiles/audio}/transport.c | 380 -----
> {audio => profiles/audio}/transport.h | 0
> 39 files changed, 673 insertions(+), 12930 deletions(-)
> delete mode 100644 audio/gateway.c
> delete mode 100644 audio/gateway.h
> delete mode 100644 audio/headset.c
> delete mode 100644 audio/headset.h
> delete mode 100644 audio/manager.c
> 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
> delete mode 100644 audio/telephony.h
> rename {audio => profiles/audio}/a2dp-codecs.h (100%)
> rename {audio => profiles/audio}/a2dp.c (100%)
> rename {audio => profiles/audio}/a2dp.h (100%)
> rename {audio => profiles/audio}/audio.conf (93%)
> rename {audio => profiles/audio}/avctp.c (100%)
> rename {audio => profiles/audio}/avctp.h (100%)
> rename {audio => profiles/audio}/avdtp.c (100%)
> rename {audio => profiles/audio}/avdtp.h (100%)
> rename {audio => profiles/audio}/avrcp.c (100%)
> rename {audio => profiles/audio}/avrcp.h (100%)
> rename {audio => profiles/audio}/control.c (100%)
> rename {audio => profiles/audio}/control.h (100%)
> rename {audio => profiles/audio}/device.c (69%)
> rename {audio => profiles/audio}/device.h (96%)
> rename {audio => profiles/audio}/main.c (53%)
> create mode 100644 profiles/audio/manager.c
> rename {audio => profiles/audio}/manager.h (89%)
> rename {audio => profiles/audio}/media.c (91%)
> rename {audio => profiles/audio}/media.h (100%)
> rename {audio => profiles/audio}/player.c (100%)
> rename {audio => profiles/audio}/player.h (100%)
> rename {audio => profiles/audio}/rtp.h (100%)
> rename {audio => profiles/audio}/sink.c (100%)
> rename {audio => profiles/audio}/sink.h (100%)
> rename {audio => profiles/audio}/source.c (100%)
> rename {audio => profiles/audio}/source.h (100%)
> rename {audio => profiles/audio}/transport.c (72%)
> rename {audio => profiles/audio}/transport.h (100%)
Looks good to me. Feel free to apply.
Johan
^ permalink raw reply
* Re: [PATCH BlueZ 1/3] avctp: Fix dead assignment in control_response
From: Luiz Augusto von Dentz @ 2012-11-13 13:33 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1352812880-7306-3-git-send-email-luiz.dentz@gmail.com>
Hi,
On Tue, Nov 13, 2012 at 3:21 PM, Luiz Augusto von Dentz
<luiz.dentz@gmail.com> wrote:
> From: Szymon Janc <szymon.janc@tieto.com>
>
> Value stored to req is never read before writing it again.
> ---
> audio/avctp.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/audio/avctp.c b/audio/avctp.c
> index 6ba25e4..29756f6 100644
> --- a/audio/avctp.c
> +++ b/audio/avctp.c
> @@ -577,7 +577,7 @@ static void control_response(struct avctp_channel *control,
> size_t operand_count)
> {
> struct avctp_pending_req *p = control->p;
> - struct avctp_control_req *req = p->data;
> + struct avctp_control_req *req;
> GSList *l;
>
> if (p && p->transaction == avctp->transaction) {
> --
> 1.7.11.7
Please ignore this set, it was in my outgoing folder as I just applied
them but they are not part of HFP patchset.
--
Luiz Augusto von Dentz
^ permalink raw reply
* [PATCH BlueZ 13/13] audio: Add support for passing endpoints as custom property
From: Luiz Augusto von Dentz @ 2012-11-13 13:21 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1352812880-7306-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This adds the possibility to pass the registered endpoints to external
components implementing the same profile.
---
profiles/audio/media.c | 140 ++++++++++++++++++++++++++++++++++++++++---------
1 file changed, 114 insertions(+), 26 deletions(-)
diff --git a/profiles/audio/media.c b/profiles/audio/media.c
index 65037fe..6d45373 100644
--- a/profiles/audio/media.c
+++ b/profiles/audio/media.c
@@ -35,7 +35,9 @@
#include <gdbus.h>
#include "../src/adapter.h"
+#include "../src/device.h"
#include "../src/dbus-common.h"
+#include "../src/profile.h"
#include "glib-helper.h"
#include "log.h"
@@ -574,6 +576,111 @@ static gboolean endpoint_init_a2dp_sink(struct media_endpoint *endpoint,
return TRUE;
}
+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;
+}
+
+static bool endpoint_properties_exists(const char *uuid,
+ struct btd_device *dev,
+ void *user_data)
+{
+ struct media_adapter *adapter = user_data;
+ struct btd_adapter *btd_adapter = device_get_adapter(dev);
+ const bdaddr_t *src = adapter_get_address(btd_adapter);
+
+ if (bacmp(&adapter->src, src) != 0)
+ return false;
+
+ if (media_adapter_find_endpoint(adapter, NULL, NULL, uuid) == NULL)
+ return false;
+
+ return true;
+}
+
+static void append_endpoint(struct media_endpoint *endpoint,
+ DBusMessageIter *dict)
+{
+ DBusMessageIter entry, var, props;
+
+ dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
+ NULL, &entry);
+
+ dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING,
+ &endpoint->sender);
+
+ dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "a{sv}",
+ &var);
+
+ dbus_message_iter_open_container(&var, 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,
+ &props);
+
+ dict_append_entry(&props, "Path", DBUS_TYPE_OBJECT_PATH,
+ &endpoint->path);
+ dict_append_entry(&props, "Codec", DBUS_TYPE_BYTE, &endpoint->codec);
+ dict_append_array(&props, "Capabilities", DBUS_TYPE_BYTE,
+ &endpoint->capabilities, endpoint->size);
+
+ dbus_message_iter_close_container(&var, &props);
+ dbus_message_iter_close_container(&entry, &var);
+ dbus_message_iter_close_container(dict, &entry);
+}
+
+static bool endpoint_properties_get(const char *uuid,
+ struct btd_device *dev,
+ DBusMessageIter *iter,
+ void *user_data)
+{
+ struct media_adapter *adapter = user_data;
+ DBusMessageIter dict;
+ GSList *l;
+
+ 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);
+
+ for (l = adapter->endpoints; l; l = l->next) {
+ struct media_endpoint *endpoint = l->data;
+
+ if (g_strcmp0(endpoint->uuid, uuid) != 0)
+ continue;
+
+ append_endpoint(endpoint, &dict);
+ }
+
+ dbus_message_iter_close_container(iter, &dict);
+
+ return true;
+}
+
static struct media_endpoint *media_endpoint_create(struct media_adapter *adapter,
const char *sender,
const char *path,
@@ -629,6 +736,13 @@ static struct media_endpoint *media_endpoint_create(struct media_adapter *adapte
sender, media_endpoint_exit,
endpoint, NULL);
+ if (media_adapter_find_endpoint(adapter, NULL, NULL, uuid) == NULL) {
+ btd_profile_add_custom_prop(uuid, "a{sv}", "Endpoints",
+ endpoint_properties_exists,
+ endpoint_properties_get,
+ adapter);
+ }
+
adapter->endpoints = g_slist_append(adapter->endpoints, endpoint);
info("Endpoint registered: sender=%s path=%s", sender, path);
@@ -637,32 +751,6 @@ static struct media_endpoint *media_endpoint_create(struct media_adapter *adapte
return endpoint;
}
-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;
-}
-
static int parse_properties(DBusMessageIter *props, const char **uuid,
gboolean *delay_reporting, uint8_t *codec,
uint8_t **capabilities, int *size)
--
1.7.11.7
^ permalink raw reply related
* [PATCH BlueZ 12/13] audio: Move to profiles directory
From: Luiz Augusto von Dentz @ 2012-11-13 13:21 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1352812880-7306-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
---
Makefile.am | 32 ++++++++++++++++----------------
{audio => profiles/audio}/a2dp-codecs.h | 0
{audio => profiles/audio}/a2dp.c | 0
{audio => profiles/audio}/a2dp.h | 0
{audio => profiles/audio}/audio.conf | 0
{audio => profiles/audio}/avctp.c | 0
{audio => profiles/audio}/avctp.h | 0
{audio => profiles/audio}/avdtp.c | 0
{audio => profiles/audio}/avdtp.h | 0
{audio => profiles/audio}/avrcp.c | 0
{audio => profiles/audio}/avrcp.h | 0
{audio => profiles/audio}/control.c | 0
{audio => profiles/audio}/control.h | 0
{audio => profiles/audio}/device.c | 0
{audio => profiles/audio}/device.h | 0
{audio => profiles/audio}/main.c | 0
{audio => profiles/audio}/manager.c | 0
{audio => profiles/audio}/manager.h | 0
{audio => profiles/audio}/media.c | 0
{audio => profiles/audio}/media.h | 0
{audio => profiles/audio}/player.c | 0
{audio => profiles/audio}/player.h | 0
{audio => profiles/audio}/rtp.h | 0
{audio => profiles/audio}/sink.c | 0
{audio => profiles/audio}/sink.h | 0
{audio => profiles/audio}/source.c | 0
{audio => profiles/audio}/source.h | 0
{audio => profiles/audio}/transport.c | 0
{audio => profiles/audio}/transport.h | 0
29 files changed, 16 insertions(+), 16 deletions(-)
rename {audio => profiles/audio}/a2dp-codecs.h (100%)
rename {audio => profiles/audio}/a2dp.c (100%)
rename {audio => profiles/audio}/a2dp.h (100%)
rename {audio => profiles/audio}/audio.conf (100%)
rename {audio => profiles/audio}/avctp.c (100%)
rename {audio => profiles/audio}/avctp.h (100%)
rename {audio => profiles/audio}/avdtp.c (100%)
rename {audio => profiles/audio}/avdtp.h (100%)
rename {audio => profiles/audio}/avrcp.c (100%)
rename {audio => profiles/audio}/avrcp.h (100%)
rename {audio => profiles/audio}/control.c (100%)
rename {audio => profiles/audio}/control.h (100%)
rename {audio => profiles/audio}/device.c (100%)
rename {audio => profiles/audio}/device.h (100%)
rename {audio => profiles/audio}/main.c (100%)
rename {audio => profiles/audio}/manager.c (100%)
rename {audio => profiles/audio}/manager.h (100%)
rename {audio => profiles/audio}/media.c (100%)
rename {audio => profiles/audio}/media.h (100%)
rename {audio => profiles/audio}/player.c (100%)
rename {audio => profiles/audio}/player.h (100%)
rename {audio => profiles/audio}/rtp.h (100%)
rename {audio => profiles/audio}/sink.c (100%)
rename {audio => profiles/audio}/sink.h (100%)
rename {audio => profiles/audio}/source.c (100%)
rename {audio => profiles/audio}/source.h (100%)
rename {audio => profiles/audio}/transport.c (100%)
rename {audio => profiles/audio}/transport.h (100%)
diff --git a/Makefile.am b/Makefile.am
index 84ae237..10c811a 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -105,20 +105,20 @@ endif
if AUDIOPLUGIN
builtin_modules += audio
-builtin_sources += audio/main.c \
- audio/manager.h audio/manager.c \
- audio/control.h audio/control.c \
- audio/avctp.h audio/avctp.c \
- audio/avrcp.h audio/avrcp.c \
- audio/device.h audio/device.c \
- audio/source.h audio/source.c \
- audio/sink.h audio/sink.c \
- audio/a2dp.h audio/a2dp.c \
- audio/avdtp.h audio/avdtp.c \
- audio/media.h audio/media.c \
- audio/transport.h audio/transport.c \
- audio/player.h audio/player.c \
- audio/a2dp-codecs.h
+builtin_sources += profiles/audio/main.c \
+ profiles/audio/manager.h profiles/audio/manager.c \
+ profiles/audio/control.h profiles/audio/control.c \
+ profiles/audio/avctp.h profiles/audio/avctp.c \
+ profiles/audio/avrcp.h profiles/audio/avrcp.c \
+ profiles/audio/device.h profiles/audio/device.c \
+ profiles/audio/source.h profiles/audio/source.c \
+ profiles/audio/sink.h profiles/audio/sink.c \
+ profiles/audio/a2dp.h profiles/audio/a2dp.c \
+ profiles/audio/avdtp.h profiles/audio/avdtp.c \
+ profiles/audio/media.h profiles/audio/media.c \
+ profiles/audio/transport.h profiles/audio/transport.c \
+ profiles/audio/player.h profiles/audio/player.c \
+ profiles/audio/a2dp-codecs.h
endif
if SAPPLUGIN
@@ -305,7 +305,7 @@ endif
EXTRA_DIST += src/genbuiltin src/bluetooth.conf src/org.bluez.service \
src/main.conf profiles/network/network.conf \
profiles/input/input.conf profiles/proximity/proximity.conf \
- audio/audio.conf \
+ profiles/audio/audio.conf \
profiles/sap/sap-dummy.c profiles/sap/sap-u8500.c
include Makefile.tools
@@ -347,7 +347,7 @@ EXTRA_DIST += doc/manager-api.txt \
AM_CFLAGS += @DBUS_CFLAGS@ @GLIB_CFLAGS@
AM_CPPFLAGS = -I$(builddir)/lib -I$(builddir)/src -I$(srcdir)/src \
- -I$(srcdir)/audio -I$(srcdir)/gdbus \
+ -I$(srcdir)/profiles/audio -I$(srcdir)/gdbus \
-I$(srcdir)/attrib -I$(srcdir)/btio -I$(srcdir)/tools \
-I$(builddir)/tools -I$(srcdir)/monitor
diff --git a/audio/a2dp-codecs.h b/profiles/audio/a2dp-codecs.h
similarity index 100%
rename from audio/a2dp-codecs.h
rename to profiles/audio/a2dp-codecs.h
diff --git a/audio/a2dp.c b/profiles/audio/a2dp.c
similarity index 100%
rename from audio/a2dp.c
rename to profiles/audio/a2dp.c
diff --git a/audio/a2dp.h b/profiles/audio/a2dp.h
similarity index 100%
rename from audio/a2dp.h
rename to profiles/audio/a2dp.h
diff --git a/audio/audio.conf b/profiles/audio/audio.conf
similarity index 100%
rename from audio/audio.conf
rename to profiles/audio/audio.conf
diff --git a/audio/avctp.c b/profiles/audio/avctp.c
similarity index 100%
rename from audio/avctp.c
rename to profiles/audio/avctp.c
diff --git a/audio/avctp.h b/profiles/audio/avctp.h
similarity index 100%
rename from audio/avctp.h
rename to profiles/audio/avctp.h
diff --git a/audio/avdtp.c b/profiles/audio/avdtp.c
similarity index 100%
rename from audio/avdtp.c
rename to profiles/audio/avdtp.c
diff --git a/audio/avdtp.h b/profiles/audio/avdtp.h
similarity index 100%
rename from audio/avdtp.h
rename to profiles/audio/avdtp.h
diff --git a/audio/avrcp.c b/profiles/audio/avrcp.c
similarity index 100%
rename from audio/avrcp.c
rename to profiles/audio/avrcp.c
diff --git a/audio/avrcp.h b/profiles/audio/avrcp.h
similarity index 100%
rename from audio/avrcp.h
rename to profiles/audio/avrcp.h
diff --git a/audio/control.c b/profiles/audio/control.c
similarity index 100%
rename from audio/control.c
rename to profiles/audio/control.c
diff --git a/audio/control.h b/profiles/audio/control.h
similarity index 100%
rename from audio/control.h
rename to profiles/audio/control.h
diff --git a/audio/device.c b/profiles/audio/device.c
similarity index 100%
rename from audio/device.c
rename to profiles/audio/device.c
diff --git a/audio/device.h b/profiles/audio/device.h
similarity index 100%
rename from audio/device.h
rename to profiles/audio/device.h
diff --git a/audio/main.c b/profiles/audio/main.c
similarity index 100%
rename from audio/main.c
rename to profiles/audio/main.c
diff --git a/audio/manager.c b/profiles/audio/manager.c
similarity index 100%
rename from audio/manager.c
rename to profiles/audio/manager.c
diff --git a/audio/manager.h b/profiles/audio/manager.h
similarity index 100%
rename from audio/manager.h
rename to profiles/audio/manager.h
diff --git a/audio/media.c b/profiles/audio/media.c
similarity index 100%
rename from audio/media.c
rename to profiles/audio/media.c
diff --git a/audio/media.h b/profiles/audio/media.h
similarity index 100%
rename from audio/media.h
rename to profiles/audio/media.h
diff --git a/audio/player.c b/profiles/audio/player.c
similarity index 100%
rename from audio/player.c
rename to profiles/audio/player.c
diff --git a/audio/player.h b/profiles/audio/player.h
similarity index 100%
rename from audio/player.h
rename to profiles/audio/player.h
diff --git a/audio/rtp.h b/profiles/audio/rtp.h
similarity index 100%
rename from audio/rtp.h
rename to profiles/audio/rtp.h
diff --git a/audio/sink.c b/profiles/audio/sink.c
similarity index 100%
rename from audio/sink.c
rename to profiles/audio/sink.c
diff --git a/audio/sink.h b/profiles/audio/sink.h
similarity index 100%
rename from audio/sink.h
rename to profiles/audio/sink.h
diff --git a/audio/source.c b/profiles/audio/source.c
similarity index 100%
rename from audio/source.c
rename to profiles/audio/source.c
diff --git a/audio/source.h b/profiles/audio/source.h
similarity index 100%
rename from audio/source.h
rename to profiles/audio/source.h
diff --git a/audio/transport.c b/profiles/audio/transport.c
similarity index 100%
rename from audio/transport.c
rename to profiles/audio/transport.c
diff --git a/audio/transport.h b/profiles/audio/transport.h
similarity index 100%
rename from audio/transport.h
rename to profiles/audio/transport.h
--
1.7.11.7
^ permalink raw reply related
* [PATCH BlueZ 11/13] build: Remove gateway.c and gateway.h
From: Luiz Augusto von Dentz @ 2012-11-13 13:21 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1352812880-7306-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
---
Makefile.am | 1 -
audio/device.h | 1 -
audio/gateway.c | 1048 -------------------------------------------------------
audio/gateway.h | 75 ----
4 files changed, 1125 deletions(-)
delete mode 100644 audio/gateway.c
delete mode 100644 audio/gateway.h
diff --git a/Makefile.am b/Makefile.am
index 7f8ff26..84ae237 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -107,7 +107,6 @@ if AUDIOPLUGIN
builtin_modules += audio
builtin_sources += audio/main.c \
audio/manager.h audio/manager.c \
- audio/gateway.h audio/gateway.c \
audio/control.h audio/control.c \
audio/avctp.h audio/avctp.c \
audio/avrcp.h audio/avrcp.c \
diff --git a/audio/device.h b/audio/device.h
index 89916be..f0d178d 100644
--- a/audio/device.h
+++ b/audio/device.h
@@ -38,7 +38,6 @@ struct audio_device {
gboolean auto_connect;
- struct gateway *gateway;
struct sink *sink;
struct source *source;
struct control *control;
diff --git a/audio/gateway.c b/audio/gateway.c
deleted file mode 100644
index 1b1164c..0000000
--- a/audio/gateway.c
+++ /dev/null
@@ -1,1048 +0,0 @@
-/*
- *
- * BlueZ - Bluetooth protocol stack for Linux
- *
- * Copyright (C) 2006-2010 Nokia Corporation
- * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
- * Copyright (C) 2008-2009 Leonid Movshovich <event.riga@gmail.org>
- * Copyright (C) 2010 ProFUSION embedded systems
- *
- *
- * 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 <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <fcntl.h>
-#include <errno.h>
-
-#include <glib.h>
-#include <dbus/dbus.h>
-#include <gdbus.h>
-
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/sdp.h>
-#include <bluetooth/sdp_lib.h>
-
-#include "../src/adapter.h"
-#include "../src/device.h"
-
-#include "sdp-client.h"
-#include "device.h"
-#include "gateway.h"
-#include "log.h"
-#include "error.h"
-#include "btio.h"
-#include "dbus-common.h"
-
-struct hf_agent {
- char *name; /* Bus id */
- char *path; /* D-Bus path */
- guint watch; /* Disconnect watch */
- DBusPendingCall *call;
-};
-
-struct connect_cb {
- unsigned int id;
- gateway_stream_cb_t cb;
- void *cb_data;
-};
-
-struct gateway {
- gateway_state_t state;
- GIOChannel *rfcomm;
- GIOChannel *sco;
- GIOChannel *incoming;
- guint rfcomm_id;
- guint sco_id;
- GSList *callbacks;
- struct hf_agent *agent;
- DBusMessage *msg;
- int version;
- gateway_lock_t lock;
-};
-
-struct gateway_state_callback {
- gateway_state_cb cb;
- void *user_data;
- unsigned int id;
-};
-
-static GSList *gateway_callbacks = NULL;
-
-int gateway_close(struct audio_device *device);
-
-GQuark gateway_error_quark(void)
-{
- return g_quark_from_static_string("gateway-error-quark");
-}
-
-static const char *state2str(gateway_state_t state)
-{
- switch (state) {
- case GATEWAY_STATE_DISCONNECTED:
- return "disconnected";
- case GATEWAY_STATE_CONNECTING:
- return "connecting";
- case GATEWAY_STATE_CONNECTED:
- return "connected";
- case GATEWAY_STATE_PLAYING:
- return "playing";
- default:
- return "";
- }
-}
-
-static void agent_free(struct hf_agent *agent)
-{
- if (!agent)
- return;
-
- if (agent->call)
- dbus_pending_call_unref(agent->call);
-
- g_dbus_remove_watch(agent->watch);
-
- g_free(agent->name);
- g_free(agent->path);
- g_free(agent);
-}
-
-static void change_state(struct audio_device *dev, gateway_state_t new_state)
-{
- struct gateway *gw = dev->gateway;
- const char *val;
- GSList *l;
- gateway_state_t old_state;
-
- if (gw->state == new_state)
- return;
-
- val = state2str(new_state);
- old_state = gw->state;
- gw->state = new_state;
-
- emit_property_changed(device_get_path(dev->btd_dev),
- AUDIO_GATEWAY_INTERFACE, "State",
- DBUS_TYPE_STRING, &val);
-
- for (l = gateway_callbacks; l != NULL; l = l->next) {
- struct gateway_state_callback *cb = l->data;
- cb->cb(dev, old_state, new_state, cb->user_data);
- }
-}
-
-void gateway_set_state(struct audio_device *dev, gateway_state_t new_state)
-{
- switch (new_state) {
- case GATEWAY_STATE_DISCONNECTED:
- gateway_close(dev);
- break;
- case GATEWAY_STATE_CONNECTING:
- case GATEWAY_STATE_CONNECTED:
- case GATEWAY_STATE_PLAYING:
- break;
- }
-}
-
-static void agent_cancel(struct hf_agent *agent)
-{
- if (!agent->call)
- return;
-
- dbus_pending_call_cancel(agent->call);
- dbus_pending_call_unref(agent->call);
- agent->call = NULL;
-}
-
-static void agent_disconnect(struct audio_device *dev, struct hf_agent *agent)
-{
- DBusMessage *msg;
-
- msg = dbus_message_new_method_call(agent->name, agent->path,
- "org.bluez.HandsfreeAgent", "Release");
-
- g_dbus_send_message(btd_get_dbus_connection(), msg);
-
- agent_cancel(agent);
-}
-
-static gboolean agent_sendfd(struct hf_agent *agent, int fd,
- DBusPendingCallNotifyFunction notify, void *data)
-{
- struct audio_device *dev = data;
- struct gateway *gw = dev->gateway;
- DBusMessage *msg;
-
- if (agent->call)
- return FALSE;
-
- msg = dbus_message_new_method_call(agent->name, agent->path,
- "org.bluez.HandsfreeAgent", "NewConnection");
-
- dbus_message_append_args(msg, DBUS_TYPE_UNIX_FD, &fd,
- DBUS_TYPE_UINT16, &gw->version,
- DBUS_TYPE_INVALID);
-
- if (dbus_connection_send_with_reply(btd_get_dbus_connection(), msg,
- &agent->call, -1) == FALSE) {
- dbus_message_unref(msg);
- return FALSE;
- }
-
- dbus_pending_call_set_notify(agent->call, notify, dev, NULL);
- dbus_message_unref(msg);
-
- return TRUE;
-}
-
-static unsigned int connect_cb_new(struct gateway *gw,
- gateway_stream_cb_t func,
- void *user_data)
-{
- struct connect_cb *cb;
- static unsigned int free_cb_id = 1;
-
- if (!func)
- return 0;
-
- cb = g_new(struct connect_cb, 1);
-
- cb->cb = func;
- cb->cb_data = user_data;
- cb->id = free_cb_id++;
-
- gw->callbacks = g_slist_append(gw->callbacks, cb);
-
- return cb->id;
-}
-
-static void run_connect_cb(struct audio_device *dev, GError *err)
-{
- struct gateway *gw = dev->gateway;
- GSList *l;
-
- for (l = gw->callbacks; l != NULL; l = l->next) {
- struct connect_cb *cb = l->data;
- cb->cb(dev, err, cb->cb_data);
- }
-
- g_slist_free_full(gw->callbacks, g_free);
- gw->callbacks = NULL;
-}
-
-static gboolean sco_io_cb(GIOChannel *chan, GIOCondition cond,
- struct audio_device *dev)
-{
- struct gateway *gw = dev->gateway;
-
- if (cond & G_IO_NVAL)
- return FALSE;
-
- DBG("sco connection is released");
-
- g_source_remove(gw->sco_id);
- gw->sco_id = 0;
-
- g_io_channel_shutdown(gw->sco, TRUE, NULL);
- g_io_channel_unref(gw->sco);
- gw->sco = NULL;
- change_state(dev, GATEWAY_STATE_CONNECTED);
-
- return FALSE;
-}
-
-static void sco_connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
-{
- struct audio_device *dev = (struct audio_device *) user_data;
- struct gateway *gw = dev->gateway;
-
- DBG("at the begin of sco_connect_cb() in gateway.c");
-
- gw->sco = g_io_channel_ref(chan);
-
- gw->sco_id = g_io_add_watch(gw->sco, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
- (GIOFunc) sco_io_cb, dev);
-
- if (err) {
- error("sco_connect_cb(): %s", err->message);
- gateway_suspend_stream(dev);
- return;
- }
-
- change_state(dev, GATEWAY_STATE_PLAYING);
- run_connect_cb(dev, NULL);
-}
-
-static gboolean rfcomm_disconnect_cb(GIOChannel *chan, GIOCondition cond,
- struct audio_device *dev)
-{
- if (cond & G_IO_NVAL)
- return FALSE;
-
- gateway_close(dev);
-
- return FALSE;
-}
-
-static void newconnection_reply(DBusPendingCall *call, void *data)
-{
- struct audio_device *dev = data;
- struct gateway *gw = dev->gateway;
- struct hf_agent *agent = gw->agent;
- DBusMessage *reply = dbus_pending_call_steal_reply(call);
- DBusError derr;
-
- dbus_pending_call_unref(agent->call);
- agent->call = NULL;
-
- dbus_error_init(&derr);
- if (!dbus_set_error_from_message(&derr, reply)) {
- DBG("Agent reply: file descriptor passed successfully");
- gw->rfcomm_id = g_io_add_watch(gw->rfcomm,
- G_IO_ERR | G_IO_HUP | G_IO_NVAL,
- (GIOFunc) rfcomm_disconnect_cb,
- dev);
- change_state(dev, GATEWAY_STATE_CONNECTED);
- goto done;
- }
-
- DBG("Agent reply: %s", derr.message);
-
- dbus_error_free(&derr);
- gateway_close(dev);
-
-done:
- dbus_message_unref(reply);
-}
-
-static void rfcomm_connect_cb(GIOChannel *chan, GError *err,
- gpointer user_data)
-{
- DBusConnection *conn = btd_get_dbus_connection();
- struct audio_device *dev = user_data;
- struct gateway *gw = dev->gateway;
- DBusMessage *reply;
- int sk, ret;
-
- if (err) {
- error("connect(): %s", err->message);
- goto fail;
- }
-
- if (!gw->agent) {
- error("Handsfree Agent not registered");
- goto fail;
- }
-
- sk = g_io_channel_unix_get_fd(chan);
-
- if (gw->rfcomm == NULL)
- gw->rfcomm = g_io_channel_ref(chan);
-
- ret = agent_sendfd(gw->agent, sk, newconnection_reply, dev);
-
- if (!gw->msg)
- return;
-
- if (ret)
- reply = dbus_message_new_method_return(gw->msg);
- else
- reply = btd_error_failed(gw->msg, "Can't pass file descriptor");
-
- g_dbus_send_message(conn, reply);
-
- return;
-
-fail:
- if (gw->msg) {
- DBusMessage *reply;
- reply = btd_error_failed(gw->msg, "Connect failed");
- g_dbus_send_message(conn, reply);
- }
-
- gateway_close(dev);
-}
-
-static int get_remote_profile_version(sdp_record_t *rec)
-{
- uuid_t uuid;
- sdp_list_t *profiles;
- sdp_profile_desc_t *desc;
- int ver = 0;
-
- sdp_uuid16_create(&uuid, HANDSFREE_PROFILE_ID);
-
- sdp_get_profile_descs(rec, &profiles);
- if (profiles == NULL)
- goto done;
-
- desc = profiles->data;
-
- if (sdp_uuid16_cmp(&desc->uuid, &uuid) == 0)
- ver = desc->version;
-
- sdp_list_free(profiles, free);
-
-done:
- return ver;
-}
-
-static void get_incoming_record_cb(sdp_list_t *recs, int err,
- gpointer user_data)
-{
- struct audio_device *dev = user_data;
- struct gateway *gw = dev->gateway;
- GError *gerr = NULL;
-
- if (err < 0) {
- error("Unable to get service record: %s (%d)", strerror(-err),
- -err);
- goto fail;
- }
-
- if (!recs || !recs->data) {
- error("No records found");
- goto fail;
- }
-
- gw->version = get_remote_profile_version(recs->data);
- if (gw->version == 0)
- goto fail;
-
- rfcomm_connect_cb(gw->incoming, gerr, dev);
- return;
-
-fail:
- gateway_close(dev);
-}
-
-static void unregister_incoming(gpointer user_data)
-{
- struct audio_device *dev = user_data;
- struct gateway *gw = dev->gateway;
-
- if (gw->incoming) {
- g_io_channel_unref(gw->incoming);
- gw->incoming = NULL;
- }
-}
-
-static void rfcomm_incoming_cb(GIOChannel *chan, GError *err,
- gpointer user_data)
-{
- struct audio_device *dev = user_data;
- struct gateway *gw = dev->gateway;
- uuid_t uuid;
-
- gw->incoming = g_io_channel_ref(chan);
-
- sdp_uuid16_create(&uuid, HANDSFREE_AGW_SVCLASS_ID);
- if (bt_search_service(&dev->src, &dev->dst, &uuid,
- get_incoming_record_cb, dev,
- unregister_incoming) == 0)
- return;
-
- unregister_incoming(dev);
- gateway_close(dev);
-}
-
-static void get_record_cb(sdp_list_t *recs, int err, gpointer user_data)
-{
- struct audio_device *dev = user_data;
- struct gateway *gw = dev->gateway;
- int ch;
- sdp_list_t *protos, *classes;
- uuid_t uuid;
- GIOChannel *io;
- GError *gerr = NULL;
-
- if (err < 0) {
- error("Unable to get service record: %s (%d)", strerror(-err),
- -err);
- goto fail;
- }
-
- if (!recs || !recs->data) {
- error("No records found");
- err = -EIO;
- goto fail;
- }
-
- if (sdp_get_service_classes(recs->data, &classes) < 0) {
- error("Unable to get service classes from record");
- err = -EINVAL;
- goto fail;
- }
-
- if (sdp_get_access_protos(recs->data, &protos) < 0) {
- error("Unable to get access protocols from record");
- err = -ENODATA;
- goto fail;
- }
-
- gw->version = get_remote_profile_version(recs->data);
- if (gw->version == 0) {
- error("Unable to get profile version from record");
- err = -EINVAL;
- goto fail;
- }
-
- memcpy(&uuid, classes->data, sizeof(uuid));
- sdp_list_free(classes, free);
-
- if (!sdp_uuid128_to_uuid(&uuid) || uuid.type != SDP_UUID16 ||
- uuid.value.uuid16 != HANDSFREE_AGW_SVCLASS_ID) {
- sdp_list_free(protos, NULL);
- error("Invalid service record or not HFP");
- err = -EIO;
- goto fail;
- }
-
- ch = sdp_get_proto_port(protos, RFCOMM_UUID);
- sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, NULL);
- sdp_list_free(protos, NULL);
- if (ch <= 0) {
- error("Unable to extract RFCOMM channel from service record");
- err = -EIO;
- goto fail;
- }
-
- io = bt_io_connect(rfcomm_connect_cb, dev, NULL, &gerr,
- BT_IO_OPT_SOURCE_BDADDR, &dev->src,
- BT_IO_OPT_DEST_BDADDR, &dev->dst,
- BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
- BT_IO_OPT_CHANNEL, ch,
- BT_IO_OPT_INVALID);
- if (!io) {
- error("Unable to connect: %s", gerr->message);
- goto fail;
- }
-
- g_io_channel_unref(io);
- return;
-
-fail:
- if (gw->msg) {
- DBusMessage *reply = btd_error_failed(gw->msg,
- gerr ? gerr->message : strerror(-err));
- g_dbus_send_message(btd_get_dbus_connection(), reply);
- }
-
- gateway_close(dev);
-
- if (gerr)
- g_error_free(gerr);
-}
-
-static int get_records(struct audio_device *device)
-{
- uuid_t uuid;
- int err;
-
- sdp_uuid16_create(&uuid, HANDSFREE_AGW_SVCLASS_ID);
-
- err = bt_search_service(&device->src, &device->dst, &uuid,
- get_record_cb, device, NULL);
- if (err < 0)
- return err;
-
- change_state(device, GATEWAY_STATE_CONNECTING);
-
- return 0;
-}
-
-static DBusMessage *ag_connect(DBusConnection *conn, DBusMessage *msg,
- void *data)
-{
- struct audio_device *au_dev = (struct audio_device *) data;
- struct gateway *gw = au_dev->gateway;
- int err;
-
- if (gw->state == GATEWAY_STATE_CONNECTING)
- return btd_error_in_progress(msg);
- else if (gw->state > GATEWAY_STATE_CONNECTING)
- return btd_error_already_connected(msg);
-
- if (!gw->agent)
- return btd_error_agent_not_available(msg);
-
- err = get_records(au_dev);
- if (err < 0)
- return btd_error_failed(msg, strerror(-err));
-
- gw->msg = dbus_message_ref(msg);
-
- return NULL;
-}
-
-int gateway_close(struct audio_device *device)
-{
- GError *gerr = NULL;
- struct gateway *gw = device->gateway;
- int sock;
-
- if (gw->rfcomm_id != 0) {
- g_source_remove(gw->rfcomm_id);
- gw->rfcomm_id = 0;
- }
-
- if (gw->sco_id != 0) {
- g_source_remove(gw->sco_id);
- gw->sco_id = 0;
- }
-
- if (gw->rfcomm) {
- sock = g_io_channel_unix_get_fd(gw->rfcomm);
- shutdown(sock, SHUT_RDWR);
-
- g_io_channel_shutdown(gw->rfcomm, TRUE, NULL);
- g_io_channel_unref(gw->rfcomm);
- gw->rfcomm = NULL;
- }
-
- if (gw->sco) {
- g_io_channel_shutdown(gw->sco, TRUE, NULL);
- g_io_channel_unref(gw->sco);
- gw->sco = NULL;
- }
-
- if (gw->agent)
- agent_cancel(gw->agent);
-
- change_state(device, GATEWAY_STATE_DISCONNECTED);
- g_set_error(&gerr, GATEWAY_ERROR,
- GATEWAY_ERROR_DISCONNECTED, "Disconnected");
- run_connect_cb(device, gerr);
- g_error_free(gerr);
-
- return 0;
-}
-
-static DBusMessage *ag_disconnect(DBusConnection *conn, DBusMessage *msg,
- void *data)
-{
- struct audio_device *device = data;
- struct gateway *gw = device->gateway;
- DBusMessage *reply = NULL;
- char gw_addr[18];
-
- if (!gw->rfcomm)
- return btd_error_not_connected(msg);
-
- reply = dbus_message_new_method_return(msg);
- if (!reply)
- return NULL;
-
- gateway_close(device);
- ba2str(&device->dst, gw_addr);
- DBG("Disconnected from %s, %s", gw_addr,
- device_get_path(device->btd_dev));
-
- return reply;
-}
-
-static void agent_exited(DBusConnection *conn, void *data)
-{
- struct gateway *gateway = data;
- struct hf_agent *agent = gateway->agent;
-
- DBG("Agent %s exited", agent->name);
-
- agent_free(agent);
- gateway->agent = NULL;
-}
-
-static DBusMessage *ag_get_properties(DBusConnection *conn, DBusMessage *msg,
- void *data)
-{
- struct audio_device *device = data;
- struct gateway *gw = device->gateway;
- DBusMessage *reply;
- DBusMessageIter iter;
- DBusMessageIter dict;
- const char *value;
-
-
- 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);
-
- value = state2str(gw->state);
- dict_append_entry(&dict, "State",
- DBUS_TYPE_STRING, &value);
-
- dbus_message_iter_close_container(&iter, &dict);
-
- return reply;
-}
-
-static DBusMessage *register_agent(DBusConnection *conn,
- DBusMessage *msg, void *data)
-{
- struct audio_device *device = data;
- struct gateway *gw = device->gateway;
- struct hf_agent *agent;
- const char *path, *name;
-
- if (gw->agent)
- return btd_error_already_exists(msg);
-
- if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
- DBUS_TYPE_INVALID))
- return btd_error_invalid_args(msg);
-
- name = dbus_message_get_sender(msg);
- agent = g_new0(struct hf_agent, 1);
-
- agent->name = g_strdup(name);
- agent->path = g_strdup(path);
-
- agent->watch = g_dbus_add_disconnect_watch(conn, name,
- agent_exited, gw, NULL);
-
- gw->agent = agent;
-
- return dbus_message_new_method_return(msg);
-}
-
-static DBusMessage *unregister_agent(DBusConnection *conn,
- DBusMessage *msg, void *data)
-{
- struct audio_device *device = data;
- struct gateway *gw = device->gateway;
- const char *path;
-
- if (!gw->agent)
- goto done;
-
- if (strcmp(gw->agent->name, dbus_message_get_sender(msg)) != 0)
- return btd_error_not_authorized(msg);
-
- if (!dbus_message_get_args(msg, NULL,
- DBUS_TYPE_OBJECT_PATH, &path,
- DBUS_TYPE_INVALID))
- return btd_error_invalid_args(msg);
-
- if (strcmp(gw->agent->path, path) != 0)
- return btd_error_does_not_exist(msg);
-
- agent_free(gw->agent);
- gw->agent = NULL;
-
-done:
- return dbus_message_new_method_return(msg);
-}
-
-static const GDBusMethodTable gateway_methods[] = {
- { GDBUS_ASYNC_METHOD("Connect", NULL, NULL, ag_connect) },
- { GDBUS_ASYNC_METHOD("Disconnect", NULL, NULL, ag_disconnect) },
- { GDBUS_METHOD("GetProperties",
- NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
- ag_get_properties) },
- { GDBUS_METHOD("RegisterAgent",
- GDBUS_ARGS({ "agent", "o" }), NULL, register_agent) },
- { GDBUS_METHOD("UnregisterAgent",
- GDBUS_ARGS({ "agent", "o" }), NULL, unregister_agent) },
- { }
-};
-
-static const GDBusSignalTable gateway_signals[] = {
- { GDBUS_SIGNAL("PropertyChanged",
- GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
- { }
-};
-
-static void path_unregister(void *data)
-{
- struct audio_device *dev = data;
-
- DBG("Unregistered interface %s on path %s",
- AUDIO_GATEWAY_INTERFACE, device_get_path(dev->btd_dev));
-
- gateway_close(dev);
-
- if (dev->gateway->agent)
- agent_free(dev->gateway->agent);
-
- g_free(dev->gateway);
- dev->gateway = NULL;
-}
-
-void gateway_unregister(struct audio_device *dev)
-{
- if (dev->gateway->agent)
- agent_disconnect(dev, dev->gateway->agent);
-
- g_dbus_unregister_interface(btd_get_dbus_connection(),
- device_get_path(dev->btd_dev),
- AUDIO_GATEWAY_INTERFACE);
-}
-
-struct gateway *gateway_init(struct audio_device *dev)
-{
- if (!g_dbus_register_interface(btd_get_dbus_connection(),
- device_get_path(dev->btd_dev),
- AUDIO_GATEWAY_INTERFACE,
- gateway_methods, gateway_signals,
- NULL, dev, path_unregister))
- return NULL;
-
- return g_new0(struct gateway, 1);
-}
-
-gboolean gateway_is_connected(struct audio_device *dev)
-{
- struct gateway *gw = dev->gateway;
-
- if (gw->state == GATEWAY_STATE_CONNECTED)
- return TRUE;
-
- return FALSE;
-}
-
-gboolean gateway_is_active(struct audio_device *dev)
-{
- struct gateway *gw = dev->gateway;
-
- if (gw->state != GATEWAY_STATE_DISCONNECTED)
- return TRUE;
-
- return FALSE;
-}
-
-gateway_state_t gateway_get_state(struct audio_device *dev)
-{
- struct gateway *gw = dev->gateway;
-
- return gw->state;
-}
-
-int gateway_connect_rfcomm(struct audio_device *dev, GIOChannel *io)
-{
- if (!io)
- return -EINVAL;
-
- dev->gateway->rfcomm = g_io_channel_ref(io);
-
- change_state(dev, GATEWAY_STATE_CONNECTING);
-
- return 0;
-}
-
-int gateway_connect_sco(struct audio_device *dev, GIOChannel *io)
-{
- struct gateway *gw = dev->gateway;
-
- if (gw->sco)
- return -EISCONN;
-
- gw->sco = g_io_channel_ref(io);
-
- gw->sco_id = g_io_add_watch(gw->sco, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
- (GIOFunc) sco_io_cb, dev);
-
- change_state(dev, GATEWAY_STATE_PLAYING);
-
- return 0;
-}
-
-void gateway_start_service(struct audio_device *dev)
-{
- struct gateway *gw = dev->gateway;
- GError *err = NULL;
-
- if (gw->rfcomm == NULL)
- return;
-
- if (!bt_io_accept(gw->rfcomm, rfcomm_incoming_cb, dev, NULL, &err)) {
- error("bt_io_accept: %s", err->message);
- g_error_free(err);
- gateway_close(dev);
- }
-}
-
-static gboolean request_stream_cb(gpointer data)
-{
- run_connect_cb(data, NULL);
- return FALSE;
-}
-
-/* These are functions to be called from unix.c for audio system
- * ifaces (alsa, gstreamer, etc.) */
-unsigned int gateway_request_stream(struct audio_device *dev,
- gateway_stream_cb_t cb, void *user_data)
-{
- struct gateway *gw = dev->gateway;
- GError *err = NULL;
- GIOChannel *io;
-
- if (!gw->rfcomm) {
- if (get_records(dev) < 0)
- return 0;
- } else if (!gw->sco) {
- io = bt_io_connect(sco_connect_cb, dev, NULL, &err,
- BT_IO_OPT_SOURCE_BDADDR, &dev->src,
- BT_IO_OPT_DEST_BDADDR, &dev->dst,
- BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
- BT_IO_OPT_INVALID);
- if (!io) {
- error("%s", err->message);
- g_error_free(err);
- return 0;
- }
- } else
- g_idle_add(request_stream_cb, dev);
-
- return connect_cb_new(gw, cb, user_data);
-}
-
-gboolean gateway_cancel_stream(struct audio_device *dev, unsigned int id)
-{
- struct gateway *gw = dev->gateway;
- GSList *l;
- struct connect_cb *cb = NULL;
-
- for (l = gw->callbacks; l != NULL; l = l->next) {
- struct connect_cb *tmp = l->data;
-
- if (tmp->id == id) {
- cb = tmp;
- break;
- }
- }
-
- if (!cb)
- return FALSE;
-
- gw->callbacks = g_slist_remove(gw->callbacks, cb);
- g_free(cb);
-
- gateway_suspend_stream(dev);
-
- return TRUE;
-}
-
-int gateway_get_sco_fd(struct audio_device *dev)
-{
- struct gateway *gw = dev->gateway;
-
- if (!gw || !gw->sco)
- return -1;
-
- return g_io_channel_unix_get_fd(gw->sco);
-}
-
-void gateway_suspend_stream(struct audio_device *dev)
-{
- GError *gerr = NULL;
- struct gateway *gw = dev->gateway;
-
- if (!gw || !gw->sco)
- return;
-
- g_source_remove(gw->sco_id);
- gw->sco_id = 0;
-
- g_io_channel_shutdown(gw->sco, TRUE, NULL);
- g_io_channel_unref(gw->sco);
- gw->sco = NULL;
- g_set_error(&gerr, GATEWAY_ERROR, GATEWAY_ERROR_SUSPENDED, "Suspended");
- run_connect_cb(dev, gerr);
- g_error_free(gerr);
- change_state(dev, GATEWAY_STATE_CONNECTED);
-}
-
-unsigned int gateway_add_state_cb(gateway_state_cb cb, void *user_data)
-{
- struct gateway_state_callback *state_cb;
- static unsigned int id = 0;
-
- state_cb = g_new(struct gateway_state_callback, 1);
- state_cb->cb = cb;
- state_cb->user_data = user_data;
- state_cb->id = ++id;
-
- gateway_callbacks = g_slist_append(gateway_callbacks, state_cb);
-
- return state_cb->id;
-}
-
-gboolean gateway_remove_state_cb(unsigned int id)
-{
- GSList *l;
-
- for (l = gateway_callbacks; l != NULL; l = l->next) {
- struct gateway_state_callback *cb = l->data;
- if (cb && cb->id == id) {
- gateway_callbacks = g_slist_remove(gateway_callbacks,
- cb);
- g_free(cb);
- return TRUE;
- }
- }
-
- return FALSE;
-}
-
-gateway_lock_t gateway_get_lock(struct audio_device *dev)
-{
- struct gateway *gw = dev->gateway;
-
- return gw->lock;
-}
-
-gboolean gateway_lock(struct audio_device *dev, gateway_lock_t lock)
-{
- struct gateway *gw = dev->gateway;
-
- if (gw->lock & lock)
- return FALSE;
-
- gw->lock |= lock;
-
- return TRUE;
-}
-
-gboolean gateway_unlock(struct audio_device *dev, gateway_lock_t lock)
-{
- struct gateway *gw = dev->gateway;
-
- if (!(gw->lock & lock))
- return FALSE;
-
- gw->lock &= ~lock;
-
- if (gw->lock)
- return TRUE;
-
- if (gw->state == GATEWAY_STATE_PLAYING)
- gateway_suspend_stream(dev);
-
- return TRUE;
-}
diff --git a/audio/gateway.h b/audio/gateway.h
deleted file mode 100644
index d87d76a..0000000
--- a/audio/gateway.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- *
- * 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 AUDIO_GATEWAY_INTERFACE "org.bluez.HandsfreeGateway"
-
-#define DEFAULT_HFP_HS_CHANNEL 7
-
-typedef enum {
- GATEWAY_STATE_DISCONNECTED,
- GATEWAY_STATE_CONNECTING,
- GATEWAY_STATE_CONNECTED,
- GATEWAY_STATE_PLAYING,
-} gateway_state_t;
-
-typedef enum {
- GATEWAY_LOCK_READ = 1,
- GATEWAY_LOCK_WRITE = 1 << 1,
-} gateway_lock_t;
-
-typedef enum {
- GATEWAY_ERROR_DISCONNECTED,
- GATEWAY_ERROR_SUSPENDED,
-} gateway_error_t;
-
-#define GATEWAY_ERROR gateway_error_quark()
-
-GQuark gateway_error_quark(void);
-
-typedef void (*gateway_state_cb) (struct audio_device *dev,
- gateway_state_t old_state,
- gateway_state_t new_state,
- void *user_data);
-typedef void (*gateway_stream_cb_t) (struct audio_device *dev, GError *err,
- void *user_data);
-
-void gateway_set_state(struct audio_device *dev, gateway_state_t new_state);
-void gateway_unregister(struct audio_device *dev);
-struct gateway *gateway_init(struct audio_device *dev);
-gboolean gateway_is_active(struct audio_device *dev);
-gboolean gateway_is_connected(struct audio_device *dev);
-gateway_state_t gateway_get_state(struct audio_device *dev);
-int gateway_connect_rfcomm(struct audio_device *dev, GIOChannel *io);
-int gateway_connect_sco(struct audio_device *dev, GIOChannel *chan);
-void gateway_start_service(struct audio_device *device);
-unsigned int gateway_request_stream(struct audio_device *dev,
- gateway_stream_cb_t cb, void *user_data);
-gboolean gateway_cancel_stream(struct audio_device *dev, unsigned int id);
-int gateway_get_sco_fd(struct audio_device *dev);
-void gateway_suspend_stream(struct audio_device *dev);
-unsigned int gateway_add_state_cb(gateway_state_cb cb, void *user_data);
-gboolean gateway_remove_state_cb(unsigned int id);
-gateway_lock_t gateway_get_lock(struct audio_device *dev);
-gboolean gateway_lock(struct audio_device *dev, gateway_lock_t lock);
-gboolean gateway_unlock(struct audio_device *dev, gateway_lock_t lock);
--
1.7.11.7
^ permalink raw reply related
* [PATCH BlueZ 10/13] audio: Remove code HFP and HS gateway role
From: Luiz Augusto von Dentz @ 2012-11-13 13:21 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1352812880-7306-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This is to be replaced with external profile support.
---
audio/device.c | 7 --
audio/main.c | 72 +-------------
audio/manager.c | 288 +-----------------------------------------------------
audio/manager.h | 3 +-
audio/media.c | 71 +-------------
audio/transport.c | 165 -------------------------------
6 files changed, 8 insertions(+), 598 deletions(-)
diff --git a/audio/device.c b/audio/device.c
index aaa666d..df57d81 100644
--- a/audio/device.c
+++ b/audio/device.c
@@ -53,7 +53,6 @@
#include "control.h"
#include "avctp.h"
#include "avrcp.h"
-#include "gateway.h"
#include "sink.h"
#include "source.h"
@@ -500,9 +499,6 @@ gboolean audio_device_is_active(struct audio_device *dev,
else if (!strcmp(interface, AUDIO_CONTROL_INTERFACE) && dev->control &&
control_is_active(dev))
return TRUE;
- else if (!strcmp(interface, AUDIO_GATEWAY_INTERFACE) && dev->gateway &&
- gateway_is_active(dev))
- return TRUE;
return FALSE;
}
@@ -514,9 +510,6 @@ void audio_device_unregister(struct audio_device *device)
device->hs_preauth_id = 0;
}
- if (device->gateway)
- gateway_unregister(device);
-
if (device->sink)
sink_unregister(device);
diff --git a/audio/main.c b/audio/main.c
index a423b79..ce060fc 100644
--- a/audio/main.c
+++ b/audio/main.c
@@ -43,7 +43,6 @@
#include "log.h"
#include "device.h"
#include "manager.h"
-#include "gateway.h"
static GIOChannel *sco_server = NULL;
@@ -66,83 +65,18 @@ static GKeyFile *load_config_file(const char *file)
return keyfile;
}
-static void sco_server_cb(GIOChannel *chan, GError *err, gpointer data)
-{
- int sk;
- struct audio_device *device;
- char addr[18];
- bdaddr_t src, dst;
-
- if (err) {
- error("sco_server_cb: %s", err->message);
- return;
- }
-
- bt_io_get(chan, &err,
- BT_IO_OPT_SOURCE_BDADDR, &src,
- BT_IO_OPT_DEST_BDADDR, &dst,
- BT_IO_OPT_DEST, addr,
- BT_IO_OPT_INVALID);
- if (err) {
- error("bt_io_get: %s", err->message);
- goto drop;
- }
-
- device = manager_find_device(NULL, &src, &dst,
- AUDIO_GATEWAY_INTERFACE,
- FALSE);
- if (!device)
- goto drop;
-
- if (device->gateway) {
- if (!gateway_is_connected(device)) {
- DBG("Refusing SCO from non-connected AG");
- goto drop;
- }
-
- if (gateway_connect_sco(device, chan) < 0)
- goto drop;
- } else
- goto drop;
-
- sk = g_io_channel_unix_get_fd(chan);
- fcntl(sk, F_SETFL, 0);
-
- DBG("Accepted SCO connection from %s", addr);
-
- return;
-
-drop:
- g_io_channel_shutdown(chan, TRUE, NULL);
-}
-
static int audio_init(void)
{
GKeyFile *config;
- gboolean enable_sco;
config = load_config_file(CONFIGDIR "/audio.conf");
- if (audio_manager_init(config, &enable_sco) < 0)
- goto failed;
-
- if (!enable_sco)
- return 0;
-
- sco_server = bt_io_listen(sco_server_cb, NULL, NULL,
- NULL, NULL,
- BT_IO_OPT_INVALID);
- if (!sco_server) {
- error("Unable to start SCO server socket");
- goto failed;
+ if (audio_manager_init(config) < 0) {
+ audio_manager_exit();
+ return -EIO;
}
return 0;
-
-failed:
- audio_manager_exit();
-
- return -EIO;
}
static void audio_exit(void)
diff --git a/audio/manager.c b/audio/manager.c
index 0960d28..f59a2f3 100644
--- a/audio/manager.c
+++ b/audio/manager.c
@@ -61,7 +61,6 @@
#include "avdtp.h"
#include "media.h"
#include "a2dp.h"
-#include "gateway.h"
#include "sink.h"
#include "source.h"
#include "avrcp.h"
@@ -88,7 +87,6 @@ static GSList *adapters = NULL;
static GSList *devices = NULL;
static struct enabled_interfaces enabled = {
- .gateway = FALSE,
.sink = TRUE,
.source = FALSE,
.control = TRUE,
@@ -107,197 +105,6 @@ static struct audio_adapter *find_adapter(GSList *list,
return NULL;
}
-static sdp_record_t *hfp_hs_record(uint8_t ch)
-{
- sdp_list_t *svclass_id, *pfseq, *apseq, *root;
- uuid_t root_uuid, svclass_uuid, ga_svclass_uuid;
- uuid_t l2cap_uuid, rfcomm_uuid;
- sdp_profile_desc_t profile;
- sdp_record_t *record;
- sdp_list_t *aproto, *proto[2];
- sdp_data_t *channel;
-
- record = sdp_record_alloc();
- if (!record)
- return NULL;
-
- sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
- root = sdp_list_append(0, &root_uuid);
- sdp_set_browse_groups(record, root);
-
- sdp_uuid16_create(&svclass_uuid, HANDSFREE_SVCLASS_ID);
- svclass_id = sdp_list_append(0, &svclass_uuid);
- sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID);
- svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid);
- sdp_set_service_classes(record, svclass_id);
-
- sdp_uuid16_create(&profile.uuid, HANDSFREE_PROFILE_ID);
- profile.version = 0x0105;
- pfseq = sdp_list_append(0, &profile);
- sdp_set_profile_descs(record, pfseq);
-
- sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
- proto[0] = sdp_list_append(0, &l2cap_uuid);
- apseq = sdp_list_append(0, proto[0]);
-
- sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
- proto[1] = sdp_list_append(0, &rfcomm_uuid);
- channel = sdp_data_alloc(SDP_UINT8, &ch);
- proto[1] = sdp_list_append(proto[1], channel);
- apseq = sdp_list_append(apseq, proto[1]);
-
- aproto = sdp_list_append(0, apseq);
- sdp_set_access_protos(record, aproto);
-
- sdp_set_info_attr(record, "Hands-Free", 0, 0);
-
- sdp_data_free(channel);
- sdp_list_free(proto[0], 0);
- sdp_list_free(proto[1], 0);
- sdp_list_free(apseq, 0);
- sdp_list_free(pfseq, 0);
- sdp_list_free(aproto, 0);
- sdp_list_free(root, 0);
- sdp_list_free(svclass_id, 0);
-
- return record;
-}
-
-static void gateway_auth_cb(DBusError *derr, void *user_data)
-{
- struct audio_device *device = user_data;
-
- if (derr && dbus_error_is_set(derr)) {
- error("Access denied: %s", derr->message);
- gateway_set_state(device, GATEWAY_STATE_DISCONNECTED);
- } else {
- char ag_address[18];
-
- ba2str(&device->dst, ag_address);
- DBG("Accepted AG connection from %s for %s",
- ag_address, device_get_path(device->btd_dev));
-
- gateway_start_service(device);
- }
-}
-
-static void hf_io_cb(GIOChannel *chan, gpointer data)
-{
- bdaddr_t src, dst;
- GError *err = NULL;
- uint8_t ch;
- const char *server_uuid, *remote_uuid;
- struct audio_device *device;
- guint auth_id;
-
- bt_io_get(chan, &err,
- BT_IO_OPT_SOURCE_BDADDR, &src,
- BT_IO_OPT_DEST_BDADDR, &dst,
- BT_IO_OPT_CHANNEL, &ch,
- BT_IO_OPT_INVALID);
-
- if (err) {
- error("%s", err->message);
- g_error_free(err);
- return;
- }
-
- server_uuid = HFP_HS_UUID;
- remote_uuid = HFP_AG_UUID;
-
- device = manager_get_device(&src, &dst, TRUE);
- if (!device)
- goto drop;
-
- if (!device->gateway) {
- btd_device_add_uuid(device->btd_dev, remote_uuid);
- if (!device->gateway)
- goto drop;
- }
-
- if (gateway_is_active(device)) {
- DBG("Refusing new connection since one already exists");
- goto drop;
- }
-
- if (gateway_connect_rfcomm(device, chan) < 0) {
- error("Allocating new GIOChannel failed!");
- goto drop;
- }
-
- auth_id = btd_request_authorization(&device->src, &device->dst,
- server_uuid, gateway_auth_cb,
- device);
- if (auth_id == 0) {
- DBG("Authorization denied");
- gateway_set_state(device, GATEWAY_STATE_DISCONNECTED);
- }
-
- return;
-
-drop:
- g_io_channel_shutdown(chan, TRUE, NULL);
-}
-
-static int gateway_server_init(struct audio_adapter *adapter)
-{
- uint8_t chan = DEFAULT_HFP_HS_CHANNEL;
- sdp_record_t *record;
- gboolean master = TRUE;
- GError *err = NULL;
- GIOChannel *io;
- const bdaddr_t *src;
-
- if (config) {
- gboolean tmp;
-
- tmp = g_key_file_get_boolean(config, "General", "Master",
- &err);
- if (err) {
- DBG("audio.conf: %s", err->message);
- g_clear_error(&err);
- } else
- master = tmp;
- }
-
- src = adapter_get_address(adapter->btd_adapter);
-
- io = bt_io_listen(NULL, hf_io_cb, adapter, NULL, &err,
- BT_IO_OPT_SOURCE_BDADDR, src,
- BT_IO_OPT_CHANNEL, chan,
- BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
- BT_IO_OPT_MASTER, master,
- BT_IO_OPT_INVALID);
- if (!io) {
- error("%s", err->message);
- g_error_free(err);
- return -1;
- }
-
- adapter->hfp_hs_server = io;
- record = hfp_hs_record(chan);
- if (!record) {
- error("Unable to allocate new service record");
- goto failed;
- }
-
- if (add_record_to_server(src, record) < 0) {
- error("Unable to register HFP HS service record");
- sdp_record_free(record);
- goto failed;
- }
-
- adapter->hfp_hs_record_id = record->handle;
-
- return 0;
-
-failed:
- g_io_channel_shutdown(adapter->hfp_hs_server, TRUE, NULL);
- g_io_channel_unref(adapter->hfp_hs_server);
- adapter->hfp_hs_server = NULL;
- return -1;
-}
-
static struct audio_device *get_audio_dev(struct btd_device *device)
{
struct btd_adapter *adapter = device_get_adapter(device);
@@ -321,25 +128,6 @@ static void audio_remove(struct btd_profile *p, struct btd_device *device)
audio_device_unregister(dev);
}
-static int ag_probe(struct btd_profile *p, struct btd_device *device,
- GSList *uuids)
-{
- struct audio_device *audio_dev;
-
- audio_dev = get_audio_dev(device);
- if (!audio_dev) {
- DBG("unable to get a device object");
- return -1;
- }
-
- if (audio_dev->gateway)
- return -EALREADY;
-
- audio_dev->gateway = gateway_init(audio_dev);
-
- return 0;
-}
-
static int a2dp_probe(struct btd_profile *p, struct btd_device *device,
GSList *uuids)
{
@@ -431,49 +219,6 @@ static struct audio_adapter *audio_adapter_get(struct btd_adapter *adapter)
return adp;
}
-static int gateway_server_probe(struct btd_profile *p,
- struct btd_adapter *adapter)
-{
- struct audio_adapter *adp;
- int err;
-
- adp = audio_adapter_get(adapter);
- if (!adp)
- return -EINVAL;
-
- err = gateway_server_init(adp);
- if (err < 0)
- audio_adapter_unref(adp);
-
- return err;
-}
-
-static void gateway_server_remove(struct btd_profile *p,
- 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;
-
- if (adp->hfp_hs_record_id) {
- remove_record_from_server(adp->hfp_hs_record_id);
- adp->hfp_hs_record_id = 0;
- }
-
- if (adp->hfp_hs_server) {
- g_io_channel_shutdown(adp->hfp_hs_server, TRUE, NULL);
- g_io_channel_unref(adp->hfp_hs_server);
- adp->hfp_hs_server = NULL;
- }
-
- audio_adapter_unref(adp);
-}
-
static int a2dp_server_probe(struct btd_profile *p,
struct btd_adapter *adapter)
{
@@ -580,17 +325,6 @@ static void media_server_remove(struct btd_adapter *adapter)
audio_adapter_unref(adp);
}
-static struct btd_profile gateway_profile = {
- .name = "audio-gateway",
-
- .remote_uuids = BTD_UUIDS(HSP_AG_UUID, HFP_AG_UUID),
- .device_probe = ag_probe,
- .device_remove = audio_remove,
-
- .adapter_probe = gateway_server_probe,
- .adapter_remove = gateway_server_remove,
-};
-
static struct btd_profile a2dp_profile = {
.name = "audio-a2dp",
@@ -620,7 +354,7 @@ static struct btd_adapter_driver media_driver = {
.remove = media_server_remove,
};
-int audio_manager_init(GKeyFile *conf, gboolean *enable_sco)
+int audio_manager_init(GKeyFile *conf)
{
char **list;
int i;
@@ -635,9 +369,7 @@ int audio_manager_init(GKeyFile *conf, gboolean *enable_sco)
list = g_key_file_get_string_list(config, "General", "Enable",
NULL, NULL);
for (i = 0; list && list[i] != NULL; i++) {
- if (g_str_equal(list[i], "Gateway"))
- enabled.gateway = TRUE;
- else if (g_str_equal(list[i], "Sink"))
+ if (g_str_equal(list[i], "Sink"))
enabled.sink = TRUE;
else if (g_str_equal(list[i], "Source"))
enabled.source = TRUE;
@@ -649,9 +381,7 @@ int audio_manager_init(GKeyFile *conf, gboolean *enable_sco)
list = g_key_file_get_string_list(config, "General", "Disable",
NULL, NULL);
for (i = 0; list && list[i] != NULL; i++) {
- if (g_str_equal(list[i], "Gateway"))
- enabled.gateway = FALSE;
- else if (g_str_equal(list[i], "Sink"))
+ if (g_str_equal(list[i], "Sink"))
enabled.sink = FALSE;
else if (g_str_equal(list[i], "Source"))
enabled.source = FALSE;
@@ -677,9 +407,6 @@ int audio_manager_init(GKeyFile *conf, gboolean *enable_sco)
max_connected_headsets = i;
proceed:
- if (enabled.gateway)
- btd_profile_register(&gateway_profile);
-
if (enabled.source || enabled.sink)
btd_profile_register(&a2dp_profile);
@@ -688,8 +415,6 @@ proceed:
btd_register_adapter_driver(&media_driver);
- *enable_sco = enabled.gateway;
-
return 0;
}
@@ -700,9 +425,6 @@ void audio_manager_exit(void)
config = NULL;
}
- if (enabled.gateway)
- btd_profile_unregister(&gateway_profile);
-
if (enabled.source || enabled.sink)
btd_profile_unregister(&a2dp_profile);
@@ -734,10 +456,6 @@ GSList *manager_find_devices(const char *path,
if ((dst && bacmp(dst, BDADDR_ANY)) && bacmp(&dev->dst, dst))
continue;
- if (interface && !strcmp(AUDIO_GATEWAY_INTERFACE, interface)
- && !dev->gateway)
- continue;
-
if (interface && !strcmp(AUDIO_SINK_INTERFACE, interface)
&& !dev->sink)
continue;
diff --git a/audio/manager.h b/audio/manager.h
index d3b5692..5691063 100644
--- a/audio/manager.h
+++ b/audio/manager.h
@@ -23,14 +23,13 @@
*/
struct enabled_interfaces {
- gboolean gateway;
gboolean sink;
gboolean source;
gboolean control;
gboolean media_player;
};
-int audio_manager_init(GKeyFile *config, gboolean *enable_sco);
+int audio_manager_init(GKeyFile *config);
void audio_manager_exit(void);
struct audio_device *manager_find_device(const char *path,
diff --git a/audio/media.c b/audio/media.c
index ea0b44e..65037fe 100644
--- a/audio/media.c
+++ b/audio/media.c
@@ -46,7 +46,6 @@
#include "transport.h"
#include "a2dp.h"
#include "avrcp.h"
-#include "gateway.h"
#include "manager.h"
#define MEDIA_INTERFACE "org.bluez.Media"
@@ -140,9 +139,6 @@ static void media_endpoint_destroy(struct media_endpoint *endpoint)
{
DBG("sender=%s path=%s", endpoint->sender, endpoint->path);
- if (endpoint->ag_watch)
- gateway_remove_state_cb(endpoint->ag_watch);
-
media_endpoint_cancel_all(endpoint);
g_slist_free_full(endpoint->transports,
@@ -550,49 +546,6 @@ static void a2dp_destroy_endpoint(void *user_data)
release_endpoint(endpoint);
}
-static void gateway_setconf_cb(struct media_endpoint *endpoint, void *ret,
- int size, void *user_data)
-{
- struct audio_device *dev = user_data;
-
- if (ret != NULL)
- return;
-
- gateway_set_state(dev, GATEWAY_STATE_DISCONNECTED);
-}
-
-static void gateway_state_changed(struct audio_device *dev,
- gateway_state_t old_state,
- gateway_state_t new_state,
- void *user_data)
-{
- struct media_endpoint *endpoint = user_data;
- struct media_transport *transport;
-
- DBG("");
-
- if (bacmp(&endpoint->adapter->src, &dev->src) != 0)
- return;
-
- switch (new_state) {
- case GATEWAY_STATE_DISCONNECTED:
- transport = find_device_transport(endpoint, dev);
- if (transport != NULL) {
- DBG("Clear endpoint %p", endpoint);
- clear_configuration(endpoint, transport);
- }
- break;
- case GATEWAY_STATE_CONNECTING:
- set_configuration(endpoint, dev, NULL, 0,
- gateway_setconf_cb, dev, NULL);
- break;
- case GATEWAY_STATE_CONNECTED:
- break;
- case GATEWAY_STATE_PLAYING:
- break;
- }
-}
-
static gboolean endpoint_init_a2dp_source(struct media_endpoint *endpoint,
gboolean delay_reporting,
int *err)
@@ -621,28 +574,6 @@ static gboolean endpoint_init_a2dp_sink(struct media_endpoint *endpoint,
return TRUE;
}
-static gboolean endpoint_init_hs(struct media_endpoint *endpoint, int *err)
-{
- GSList *list;
- GSList *l;
-
- endpoint->ag_watch = gateway_add_state_cb(gateway_state_changed,
- endpoint);
- list = manager_find_devices(NULL, &endpoint->adapter->src, BDADDR_ANY,
- AUDIO_GATEWAY_INTERFACE, TRUE);
-
- for (l = list; l != NULL; l = l->next) {
- struct audio_device *dev = l->data;
-
- set_configuration(endpoint, dev, NULL, 0,
- gateway_setconf_cb, dev, NULL);
- }
-
- g_slist_free(list);
-
- return TRUE;
-}
-
static struct media_endpoint *media_endpoint_create(struct media_adapter *adapter,
const char *sender,
const char *path,
@@ -681,7 +612,7 @@ static struct media_endpoint *media_endpoint_create(struct media_adapter *adapte
succeeded = TRUE;
else if (strcasecmp(uuid, HFP_HS_UUID) == 0 ||
strcasecmp(uuid, HSP_HS_UUID) == 0)
- succeeded = endpoint_init_hs(endpoint, err);
+ succeeded = TRUE;
else {
succeeded = FALSE;
diff --git a/audio/transport.c b/audio/transport.c
index 74562ea..994c423 100644
--- a/audio/transport.c
+++ b/audio/transport.c
@@ -44,7 +44,6 @@
#include "media.h"
#include "transport.h"
#include "a2dp.h"
-#include "gateway.h"
#include "sink.h"
#include "source.h"
#include "avrcp.h"
@@ -109,7 +108,6 @@ struct media_transport {
transport_lock_t lock;
transport_state_t state;
guint hs_watch;
- guint ag_watch;
guint source_watch;
guint sink_watch;
guint (*resume) (struct media_transport *transport,
@@ -211,9 +209,6 @@ void media_transport_destroy(struct media_transport *transport)
{
char *path;
- if (transport->ag_watch)
- gateway_remove_state_cb(transport->ag_watch);
-
if (transport->sink_watch)
sink_remove_state_cb(transport->sink_watch);
@@ -461,127 +456,6 @@ static void cancel_a2dp(struct media_transport *transport, guint id)
a2dp_cancel(transport->device, id);
}
-static void gateway_resume_complete(struct audio_device *dev, GError *err,
- void *user_data)
-{
- struct media_owner *owner = user_data;
- struct media_request *req = owner->pending;
- struct media_transport *transport = owner->transport;
- int fd;
- uint16_t imtu, omtu;
- gboolean ret;
-
- req->id = 0;
-
- if (dev == NULL)
- goto fail;
-
- if (err) {
- error("Failed to resume gateway: error %s", err->message);
- goto fail;
- }
-
- fd = gateway_get_sco_fd(dev);
- if (fd < 0)
- goto fail;
-
- imtu = 48;
- omtu = 48;
-
- media_transport_set_fd(transport, fd, imtu, omtu);
-
- if ((owner->lock & TRANSPORT_LOCK_READ) == 0)
- imtu = 0;
-
- if ((owner->lock & TRANSPORT_LOCK_WRITE) == 0)
- omtu = 0;
-
- ret = g_dbus_send_reply(btd_get_dbus_connection(), req->msg,
- DBUS_TYPE_UNIX_FD, &fd,
- DBUS_TYPE_UINT16, &imtu,
- DBUS_TYPE_UINT16, &omtu,
- DBUS_TYPE_INVALID);
- if (ret == FALSE)
- goto fail;
-
- media_owner_remove(owner);
-
- transport_set_state(transport, TRANSPORT_STATE_ACTIVE);
-
- return;
-
-fail:
- media_transport_remove(transport, owner);
-}
-
-static guint resume_gateway(struct media_transport *transport,
- struct media_owner *owner)
-{
- struct audio_device *device = transport->device;
-
- if (state_in_use(transport->state))
- goto done;
-
- if (gateway_lock(device, GATEWAY_LOCK_READ |
- GATEWAY_LOCK_WRITE) == FALSE)
- return 0;
-
- if (transport->state == TRANSPORT_STATE_IDLE)
- transport_set_state(transport, TRANSPORT_STATE_REQUESTING);
-
-done:
- return gateway_request_stream(device, gateway_resume_complete,
- owner);
-}
-
-static gboolean gateway_suspend_complete(gpointer user_data)
-{
- struct media_owner *owner = user_data;
- struct media_transport *transport = owner->transport;
- struct audio_device *device = transport->device;
-
- /* Release always succeeds */
- if (owner->pending) {
- owner->pending->id = 0;
- media_request_reply(owner->pending, 0);
- media_owner_remove(owner);
- }
-
- gateway_unlock(device, GATEWAY_LOCK_READ | GATEWAY_LOCK_WRITE);
- transport_set_state(transport, TRANSPORT_STATE_IDLE);
- media_transport_remove(transport, owner);
- return FALSE;
-}
-
-static guint suspend_gateway(struct media_transport *transport,
- struct media_owner *owner)
-{
- struct audio_device *device = transport->device;
- static int id = 1;
-
- if (!owner) {
- gateway_state_t state = gateway_get_state(device);
-
- gateway_unlock(device, GATEWAY_LOCK_READ | GATEWAY_LOCK_WRITE);
-
- if (state == GATEWAY_STATE_PLAYING)
- transport_set_state(transport, TRANSPORT_STATE_PENDING);
- else
- transport_set_state(transport, TRANSPORT_STATE_IDLE);
-
- return 0;
- }
-
- gateway_suspend_stream(device);
- g_idle_add(gateway_suspend_complete, owner);
- return id++;
-}
-
-static void cancel_gateway(struct media_transport *transport, guint id)
-{
- gateway_cancel_stream(transport->device, id);
-}
-
static void media_owner_exit(DBusConnection *connection, void *user_data)
{
struct media_owner *owner = user_data;
@@ -806,13 +680,6 @@ static int set_property_a2dp(struct media_transport *transport,
return -EINVAL;
}
-static int set_property_gateway(struct media_transport *transport,
- const char *property,
- DBusMessageIter *value)
-{
- return -EINVAL;
-}
-
static DBusMessage *set_property(DBusConnection *conn, DBusMessage *msg,
void *data)
{
@@ -871,12 +738,6 @@ static void get_properties_a2dp(struct media_transport *transport,
&a2dp->volume);
}
-static void get_properties_gateway(struct media_transport *transport,
- DBusMessageIter *dict)
-{
- /* None */
-}
-
void transport_get_properties(struct media_transport *transport,
DBusMessageIter *iter)
{
@@ -1009,22 +870,6 @@ static void transport_update_playing(struct media_transport *transport,
transport_set_state(transport, TRANSPORT_STATE_PENDING);
}
-static void gateway_state_changed(struct audio_device *dev,
- gateway_state_t old_state,
- gateway_state_t new_state,
- void *user_data)
-{
- struct media_transport *transport = user_data;
-
- if (dev != transport->device)
- return;
-
- if (new_state == GATEWAY_STATE_PLAYING)
- transport_update_playing(transport, TRUE);
- else
- transport_update_playing(transport, FALSE);
-}
-
static void sink_state_changed(struct audio_device *dev,
sink_state_t old_state,
sink_state_t new_state,
@@ -1100,16 +945,6 @@ struct media_transport *media_transport_create(struct media_endpoint *endpoint,
transport->source_watch = source_add_state_cb(
source_state_changed,
transport);
- } else if (strcasecmp(uuid, HFP_HS_UUID) == 0 ||
- strcasecmp(uuid, HSP_HS_UUID) == 0) {
- transport->resume = resume_gateway;
- transport->suspend = suspend_gateway;
- transport->cancel = cancel_gateway;
- transport->get_properties = get_properties_gateway;
- transport->set_property = set_property_gateway;
- transport->ag_watch = gateway_add_state_cb(
- gateway_state_changed,
- transport);
} else
goto fail;
--
1.7.11.7
^ permalink raw reply related
* [PATCH BlueZ 09/13] build: Remove headset.c and headset.h
From: Luiz Augusto von Dentz @ 2012-11-13 13:21 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1352812880-7306-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
---
Makefile.am | 1 -
audio/device.h | 1 -
audio/headset.c | 2822 -------------------------------------------------------
audio/headset.h | 112 ---
4 files changed, 2936 deletions(-)
delete mode 100644 audio/headset.c
delete mode 100644 audio/headset.h
diff --git a/Makefile.am b/Makefile.am
index a8660af..7f8ff26 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -108,7 +108,6 @@ builtin_modules += audio
builtin_sources += audio/main.c \
audio/manager.h audio/manager.c \
audio/gateway.h audio/gateway.c \
- audio/headset.h audio/headset.c \
audio/control.h audio/control.c \
audio/avctp.h audio/avctp.c \
audio/avrcp.h audio/avrcp.c \
diff --git a/audio/device.h b/audio/device.h
index 1e2cac1..89916be 100644
--- a/audio/device.h
+++ b/audio/device.h
@@ -38,7 +38,6 @@ struct audio_device {
gboolean auto_connect;
- struct headset *headset;
struct gateway *gateway;
struct sink *sink;
struct source *source;
diff --git a/audio/headset.c b/audio/headset.c
deleted file mode 100644
index fb660f5..0000000
--- a/audio/headset.c
+++ /dev/null
@@ -1,2822 +0,0 @@
-/*
- *
- * 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
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <stdbool.h>
-#include <signal.h>
-#include <string.h>
-#include <getopt.h>
-#include <sys/ioctl.h>
-#include <sys/socket.h>
-#include <assert.h>
-
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/sdp.h>
-#include <bluetooth/sdp_lib.h>
-#include <bluetooth/uuid.h>
-
-#include <glib.h>
-#include <dbus/dbus.h>
-#include <gdbus.h>
-
-#include "log.h"
-#include "device.h"
-#include "manager.h"
-#include "error.h"
-#include "telephony.h"
-#include "headset.h"
-#include "sdp-client.h"
-#include "btio.h"
-#include "dbus-common.h"
-#include "../src/adapter.h"
-#include "../src/device.h"
-
-#define DC_TIMEOUT 3
-
-#define RING_INTERVAL 3
-
-#define BUF_SIZE 1024
-
-#define HEADSET_GAIN_SPEAKER 'S'
-#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 */
- int rh; /* Response and Hold state */
- char *number; /* Incoming phone number */
- int number_type; /* Incoming number type */
- 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,
- .number = NULL,
- .number_type = 0,
- .ring_timer = 0,
-};
-
-static gboolean sco_hci = TRUE;
-static gboolean fast_connectable = FALSE;
-
-static GSList *active_devices = NULL;
-
-static char *str_state[] = {
- "HEADSET_STATE_DISCONNECTED",
- "HEADSET_STATE_CONNECTING",
- "HEADSET_STATE_CONNECTED",
- "HEADSET_STATE_PLAY_IN_PROGRESS",
- "HEADSET_STATE_PLAYING",
-};
-
-struct headset_state_callback {
- headset_state_cb cb;
- void *user_data;
- unsigned int id;
-};
-
-struct headset_nrec_callback {
- unsigned int id;
- headset_nrec_cb cb;
- void *user_data;
-};
-
-struct connect_cb {
- unsigned int id;
- headset_stream_cb_t cb;
- void *cb_data;
-};
-
-struct pending_connect {
- DBusMessage *msg;
- DBusPendingCall *call;
- GIOChannel *io;
- int err;
- headset_state_t target_state;
- GSList *callbacks;
- uint16_t svclass;
-};
-
-struct headset_slc {
- char buf[BUF_SIZE];
- int data_start;
- int data_length;
-
- gboolean cli_active;
- gboolean cme_enabled;
- gboolean cwa_enabled;
- gboolean pending_ring;
- gboolean inband_ring;
- gboolean nrec;
- gboolean nrec_req;
-
- int sp_gain;
- int mic_gain;
-
- unsigned int hf_features;
-};
-
-struct headset {
- uint32_t hsp_handle;
- uint32_t hfp_handle;
-
- int rfcomm_ch;
-
- GIOChannel *rfcomm;
- GIOChannel *tmp_rfcomm;
- GIOChannel *sco;
- guint sco_id;
-
- gboolean auto_dc;
-
- guint dc_timer;
-
- gboolean hfp_active;
- gboolean search_hfp;
- gboolean rfcomm_initiator;
-
- headset_state_t state;
- struct pending_connect *pending;
-
- headset_lock_t lock;
- struct headset_slc *slc;
- GSList *nrec_cbs;
-};
-
-struct event {
- const char *cmd;
- int (*callback) (struct audio_device *device, const char *buf);
-};
-
-static GSList *headset_callbacks = NULL;
-
-static void error_connect_failed(DBusMessage *msg, int err)
-{
- DBusMessage *reply = btd_error_failed(msg,
- err < 0 ? strerror(-err) : "Connect failed");
- g_dbus_send_message(btd_get_dbus_connection(), reply);
-}
-
-static int rfcomm_connect(struct audio_device *device, headset_stream_cb_t cb,
- void *user_data, unsigned int *cb_id);
-static int get_records(struct audio_device *device, headset_stream_cb_t cb,
- void *user_data, unsigned int *cb_id);
-
-static void print_ag_features(uint32_t features)
-{
- GString *gstr;
- char *str;
-
- if (features == 0) {
- DBG("HFP AG features: (none)");
- return;
- }
-
- gstr = g_string_new("HFP AG features: ");
-
- if (features & AG_FEATURE_THREE_WAY_CALLING)
- g_string_append(gstr, "\"Three-way calling\" ");
- if (features & AG_FEATURE_EC_ANDOR_NR)
- g_string_append(gstr, "\"EC and/or NR function\" ");
- if (features & AG_FEATURE_VOICE_RECOGNITION)
- g_string_append(gstr, "\"Voice recognition function\" ");
- if (features & AG_FEATURE_INBAND_RINGTONE)
- g_string_append(gstr, "\"In-band ring tone capability\" ");
- if (features & AG_FEATURE_ATTACH_NUMBER_TO_VOICETAG)
- g_string_append(gstr, "\"Attach a number to a voice tag\" ");
- if (features & AG_FEATURE_REJECT_A_CALL)
- g_string_append(gstr, "\"Ability to reject a call\" ");
- if (features & AG_FEATURE_ENHANCED_CALL_STATUS)
- g_string_append(gstr, "\"Enhanced call status\" ");
- if (features & AG_FEATURE_ENHANCED_CALL_CONTROL)
- g_string_append(gstr, "\"Enhanced call control\" ");
- if (features & AG_FEATURE_EXTENDED_ERROR_RESULT_CODES)
- g_string_append(gstr, "\"Extended Error Result Codes\" ");
-
- str = g_string_free(gstr, FALSE);
-
- DBG("%s", str);
-
- 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) {
- case HEADSET_STATE_DISCONNECTED:
- return "disconnected";
- case HEADSET_STATE_CONNECTING:
- return "connecting";
- case HEADSET_STATE_CONNECTED:
- case HEADSET_STATE_PLAY_IN_PROGRESS:
- return "connected";
- case HEADSET_STATE_PLAYING:
- return "playing";
- }
-
- return NULL;
-}
-
-static int headset_send_valist(struct headset *hs, char *format, va_list ap)
-{
- char rsp[BUF_SIZE];
- ssize_t total_written, count;
- int fd;
-
- count = vsnprintf(rsp, sizeof(rsp), format, ap);
-
- if (count < 0)
- return -EINVAL;
-
- if (!hs->rfcomm) {
- error("headset_send: the headset is not connected");
- return -EIO;
- }
-
- total_written = 0;
- fd = g_io_channel_unix_get_fd(hs->rfcomm);
-
- while (total_written < count) {
- ssize_t written;
-
- written = write(fd, rsp + total_written,
- count - total_written);
- if (written < 0)
- return -errno;
-
- total_written += written;
- }
-
- return 0;
-}
-
-static int __attribute__((format(printf, 2, 3)))
- headset_send(struct headset *hs, char *format, ...)
-{
- va_list ap;
- int ret;
-
- va_start(ap, format);
- ret = headset_send_valist(hs, format, ap);
- va_end(ap);
-
- 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;
-
- if (hs->pending->err < 0)
- cb->cb(NULL, cb->cb_data);
- else
- cb->cb(dev, cb->cb_data);
-}
-
-static void pending_connect_finalize(struct audio_device *dev)
-{
- struct headset *hs = dev->headset;
- struct pending_connect *p = hs->pending;
-
- if (p == NULL)
- return;
-
- if (p->svclass)
- bt_cancel_discovery(&dev->src, &dev->dst);
-
- g_slist_foreach(p->callbacks, (GFunc) pending_connect_complete, dev);
-
- g_slist_free_full(p->callbacks, g_free);
-
- if (p->io) {
- g_io_channel_shutdown(p->io, TRUE, NULL);
- g_io_channel_unref(p->io);
- }
-
- if (p->msg)
- dbus_message_unref(p->msg);
-
- if (p->call) {
- dbus_pending_call_cancel(p->call);
- dbus_pending_call_unref(p->call);
- }
-
- g_free(p);
-
- hs->pending = NULL;
-}
-
-static void pending_connect_init(struct headset *hs, headset_state_t target_state)
-{
- if (hs->pending) {
- if (hs->pending->target_state < target_state)
- hs->pending->target_state = target_state;
- return;
- }
-
- hs->pending = g_new0(struct pending_connect, 1);
- hs->pending->target_state = target_state;
-}
-
-static unsigned int connect_cb_new(struct headset *hs,
- headset_state_t target_state,
- headset_stream_cb_t func,
- void *user_data)
-{
- struct connect_cb *cb;
- static unsigned int free_cb_id = 1;
-
- pending_connect_init(hs, target_state);
-
- if (!func)
- return 0;
-
- cb = g_new(struct connect_cb, 1);
-
- cb->cb = func;
- cb->cb_data = user_data;
- cb->id = free_cb_id++;
-
- hs->pending->callbacks = g_slist_append(hs->pending->callbacks,
- cb);
-
- return cb->id;
-}
-
-static void __attribute__((format(printf, 3, 4)))
- send_foreach_headset(GSList *devices,
- int (*cmp) (struct headset *hs),
- char *format, ...)
-{
- GSList *l;
- va_list ap;
-
- for (l = devices; l != NULL; l = l->next) {
- struct audio_device *device = l->data;
- struct headset *hs = device->headset;
- int ret;
-
- assert(hs != NULL);
-
- if (cmp && cmp(hs) != 0)
- continue;
-
- va_start(ap, format);
- ret = headset_send_valist(hs, format, ap);
- if (ret < 0)
- error("Failed to send to headset: %s (%d)",
- strerror(-ret), -ret);
- va_end(ap);
- }
-}
-
-static int cli_cmp(struct headset *hs)
-{
- struct headset_slc *slc = hs->slc;
-
- if (!hs->hfp_active)
- return -1;
-
- if (slc->cli_active)
- return 0;
- else
- return -1;
-}
-
-static gboolean ring_timer_cb(gpointer data)
-{
- send_foreach_headset(active_devices, NULL, "\r\nRING\r\n");
-
- if (ag.number)
- send_foreach_headset(active_devices, cli_cmp,
- "\r\n+CLIP: \"%s\",%d\r\n",
- ag.number, ag.number_type);
-
- return TRUE;
-}
-
-static void sco_connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
-{
- int sk;
- struct audio_device *dev = user_data;
- struct headset *hs = dev->headset;
- struct headset_slc *slc = hs->slc;
- struct pending_connect *p = hs->pending;
-
- if (err) {
- error("%s", err->message);
-
- if (p != NULL) {
- p->err = -errno;
- if (p->msg)
- error_connect_failed(p->msg, p->err);
- pending_connect_finalize(dev);
- }
-
- if (hs->rfcomm)
- headset_set_state(dev, HEADSET_STATE_CONNECTED);
- else
- headset_set_state(dev, HEADSET_STATE_DISCONNECTED);
-
- return;
- }
-
- DBG("SCO socket opened for headset %s", device_get_path(dev->btd_dev));
-
- sk = g_io_channel_unix_get_fd(chan);
-
- DBG("SCO fd=%d", sk);
-
- if (p) {
- p->io = NULL;
- if (p->msg) {
- DBusMessage *reply;
- reply = dbus_message_new_method_return(p->msg);
- g_dbus_send_message(btd_get_dbus_connection(), reply);
- }
-
- pending_connect_finalize(dev);
- }
-
- fcntl(sk, F_SETFL, 0);
-
- headset_set_state(dev, HEADSET_STATE_PLAYING);
-
- if (slc->pending_ring) {
- ring_timer_cb(NULL);
- ag.ring_timer = g_timeout_add_seconds(RING_INTERVAL,
- ring_timer_cb,
- NULL);
- slc->pending_ring = FALSE;
- }
-}
-
-static int sco_connect(struct audio_device *dev, headset_stream_cb_t cb,
- void *user_data, unsigned int *cb_id)
-{
- struct headset *hs = dev->headset;
- GError *err = NULL;
- GIOChannel *io;
-
- if (hs->state != HEADSET_STATE_CONNECTED)
- return -EINVAL;
-
- io = bt_io_connect(sco_connect_cb, dev, NULL, &err,
- BT_IO_OPT_SOURCE_BDADDR, &dev->src,
- BT_IO_OPT_DEST_BDADDR, &dev->dst,
- BT_IO_OPT_INVALID);
- if (!io) {
- error("%s", err->message);
- g_error_free(err);
- return -EIO;
- }
-
- hs->sco = io;
-
- headset_set_state(dev, HEADSET_STATE_PLAY_IN_PROGRESS);
-
- pending_connect_init(hs, HEADSET_STATE_PLAYING);
-
- if (cb) {
- unsigned int id = connect_cb_new(hs, HEADSET_STATE_PLAYING,
- cb, user_data);
- if (cb_id)
- *cb_id = id;
- }
-
- return 0;
-}
-
-static int hfp_cmp(struct headset *hs)
-{
- if (hs->hfp_active)
- return 0;
- else
- return -1;
-}
-
-static void hfp_slc_complete(struct audio_device *dev)
-{
- struct headset *hs = dev->headset;
- struct pending_connect *p = hs->pending;
-
- DBG("HFP Service Level Connection established");
-
- headset_set_state(dev, HEADSET_STATE_CONNECTED);
-
- if (p == NULL)
- return;
-
- if (p->target_state == HEADSET_STATE_CONNECTED) {
- if (p->msg) {
- DBusMessage *reply = dbus_message_new_method_return(p->msg);
- g_dbus_send_message(btd_get_dbus_connection(), reply);
- }
- pending_connect_finalize(dev);
- return;
- }
-
- p->err = sco_connect(dev, NULL, NULL, NULL);
- if (p->err < 0) {
- if (p->msg)
- error_connect_failed(p->msg, p->err);
- pending_connect_finalize(dev);
- }
-}
-
-static int telephony_generic_rsp(struct audio_device *device, cme_error_t err)
-{
- struct headset *hs = device->headset;
- struct headset_slc *slc = hs->slc;
-
- if ((err != CME_ERROR_NONE) && slc->cme_enabled)
- return headset_send(hs, "\r\n+CME ERROR: %d\r\n", err);
-
- switch (err) {
- case CME_ERROR_NONE:
- return headset_send(hs, "\r\nOK\r\n");
- case CME_ERROR_NO_NETWORK_SERVICE:
- return headset_send(hs, "\r\nNO CARRIER\r\n");
- default:
- return headset_send(hs, "\r\nERROR\r\n");
- }
-}
-
-int telephony_event_reporting_rsp(void *telephony_device, cme_error_t err)
-{
- struct audio_device *device = telephony_device;
- struct headset *hs = device->headset;
- struct headset_slc *slc = hs->slc;
- int ret;
-
- if (err != CME_ERROR_NONE)
- return telephony_generic_rsp(telephony_device, err);
-
- ret = headset_send(hs, "\r\nOK\r\n");
- if (ret < 0)
- return ret;
-
- if (hs->state != HEADSET_STATE_CONNECTING)
- 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]);
- 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);
-
- return 0;
-}
-
-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(btd_get_dbus_connection(),
- device_get_path(device->btd_dev),
- 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)
-{
- struct audio_device *device = telephony_device;
- struct headset *hs = device->headset;
-
- if (err != CME_ERROR_NONE)
- return telephony_generic_rsp(telephony_device, err);
-
- g_dbus_emit_signal(btd_get_dbus_connection(),
- device_get_path(device->btd_dev),
- AUDIO_HEADSET_INTERFACE, "CallTerminated",
- DBUS_TYPE_INVALID);
-
- 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;
- struct headset_slc *slc = hs->slc;
- const char *name, *property;
-
- if (gain > 15) {
- error("Invalid gain value: %u", gain);
- return -EINVAL;
- }
-
- switch (type) {
- case HEADSET_GAIN_SPEAKER:
- if (slc->sp_gain == gain) {
- DBG("Ignoring no-change in speaker gain");
- return -EALREADY;
- }
- name = "SpeakerGainChanged";
- property = "SpeakerGain";
- slc->sp_gain = gain;
- break;
- case HEADSET_GAIN_MICROPHONE:
- if (slc->mic_gain == gain) {
- DBG("Ignoring no-change in microphone gain");
- return -EALREADY;
- }
- name = "MicrophoneGainChanged";
- property = "MicrophoneGain";
- slc->mic_gain = gain;
- break;
- default:
- error("Unknown gain setting");
- return -EINVAL;
- }
-
- g_dbus_emit_signal(btd_get_dbus_connection(),
- device_get_path(device->btd_dev),
- AUDIO_HEADSET_INTERFACE, name,
- DBUS_TYPE_UINT16, &gain,
- DBUS_TYPE_INVALID);
-
- emit_property_changed(device_get_path(device->btd_dev),
- AUDIO_HEADSET_INTERFACE, property,
- DBUS_TYPE_UINT16, &gain);
-
- 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);
-}
-
-int telephony_call_hold_rsp(void *telephony_device, cme_error_t err)
-{
- return telephony_generic_rsp(telephony_device, err);
-}
-
-int telephony_nr_and_ec_rsp(void *telephony_device, cme_error_t err)
-{
- struct audio_device *device = telephony_device;
- struct headset *hs = device->headset;
- struct headset_slc *slc = hs->slc;
-
- if (err == CME_ERROR_NONE) {
- GSList *l;
-
- for (l = hs->nrec_cbs; l; l = l->next) {
- struct headset_nrec_callback *nrec_cb = l->data;
-
- nrec_cb->cb(device, slc->nrec_req, nrec_cb->user_data);
- }
-
- slc->nrec = hs->slc->nrec_req;
- }
-
- return telephony_generic_rsp(telephony_device, err);
-}
-
-int telephony_voice_dial_rsp(void *telephony_device, cme_error_t err)
-{
- return telephony_generic_rsp(telephony_device, err);
-}
-
-int telephony_operator_selection_ind(int mode, const char *oper)
-{
- if (!active_devices)
- return -ENODEV;
-
- send_foreach_headset(active_devices, hfp_cmp,
- "\r\n+COPS: %d,0,\"%s\"\r\n",
- mode, 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;
-
- if (hs->sco) {
- int sock = g_io_channel_unix_get_fd(hs->sco);
- shutdown(sock, SHUT_RDWR);
- g_io_channel_shutdown(hs->sco, TRUE, NULL);
- g_io_channel_unref(hs->sco);
- hs->sco = NULL;
- }
-
- if (hs->sco_id) {
- g_source_remove(hs->sco_id);
- hs->sco_id = 0;
- }
-}
-
-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 incoming 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 = telephony_generic_rsp(device,
- CME_ERROR_NOT_SUPPORTED);
- 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)
-{
- if (cond & G_IO_NVAL)
- return FALSE;
-
- error("Audio connection got disconnected");
-
- pending_connect_finalize(device);
- headset_set_state(device, HEADSET_STATE_CONNECTED);
-
- return FALSE;
-}
-
-void headset_connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
-{
- struct audio_device *dev = user_data;
- struct headset *hs = dev->headset;
- struct pending_connect *p = hs->pending;
- char hs_address[18];
-
- if (err) {
- error("%s", err->message);
- goto failed;
- }
-
- /* For HFP telephony isn't ready just disconnect */
- if (hs->hfp_active && !ag.telephony_ready) {
- error("Unable to accept HFP connection since the telephony "
- "subsystem isn't initialized");
- goto failed;
- }
-
- hs->rfcomm = hs->tmp_rfcomm;
- hs->tmp_rfcomm = NULL;
-
- ba2str(&dev->dst, hs_address);
-
- if (p)
- p->io = NULL;
- 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);
-
- DBG("%s: Connected to %s", device_get_path(dev->btd_dev), hs_address);
-
- hs->slc = g_new0(struct headset_slc, 1);
- hs->slc->sp_gain = 15;
- hs->slc->mic_gain = 15;
- hs->slc->nrec = TRUE;
-
- /* In HFP mode wait for Service Level Connection */
- if (hs->hfp_active)
- return;
-
- headset_set_state(dev, HEADSET_STATE_CONNECTED);
-
- if (p && p->target_state == HEADSET_STATE_PLAYING) {
- p->err = sco_connect(dev, NULL, NULL, NULL);
- if (p->err < 0)
- goto failed;
- return;
- }
-
- if (p && p->msg) {
- DBusMessage *reply = dbus_message_new_method_return(p->msg);
- g_dbus_send_message(btd_get_dbus_connection(), reply);
- }
-
- pending_connect_finalize(dev);
-
- return;
-
-failed:
- if (p && p->msg)
- error_connect_failed(p->msg, p->err);
- pending_connect_finalize(dev);
- if (hs->rfcomm)
- headset_set_state(dev, HEADSET_STATE_CONNECTED);
- else
- headset_set_state(dev, HEADSET_STATE_DISCONNECTED);
-}
-
-static int headset_set_channel(struct headset *headset,
- const sdp_record_t *record, uint16_t svc)
-{
- int ch;
- sdp_list_t *protos;
-
- if (sdp_get_access_protos(record, &protos) < 0) {
- error("Unable to get access protos from headset record");
- return -1;
- }
-
- ch = sdp_get_proto_port(protos, RFCOMM_UUID);
-
- sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, NULL);
- sdp_list_free(protos, NULL);
-
- if (ch <= 0) {
- error("Unable to get RFCOMM channel from Headset record");
- return -1;
- }
-
- headset->rfcomm_ch = ch;
-
- if (svc == HANDSFREE_SVCLASS_ID) {
- headset->hfp_handle = record->handle;
- headset->hsp_handle = 0;
- DBG("Discovered Handsfree service on channel %d", ch);
- } else {
- headset->hsp_handle = record->handle;
- headset->hfp_handle = 0;
- DBG("Discovered Headset service on channel %d", ch);
- }
-
- return 0;
-}
-
-static void get_record_cb(sdp_list_t *recs, int err, gpointer user_data)
-{
- struct audio_device *dev = user_data;
- struct headset *hs = dev->headset;
- struct pending_connect *p = hs->pending;
- sdp_record_t *record = NULL;
- sdp_list_t *r;
- uuid_t uuid;
-
- assert(hs->pending != NULL);
-
- if (err < 0) {
- error("Unable to get service record: %s (%d)",
- strerror(-err), -err);
- p->err = -err;
- if (p->msg)
- error_connect_failed(p->msg, p->err);
- goto failed;
- }
-
- if (!recs || !recs->data) {
- error("No records found");
- goto failed_not_supported;
- }
-
- sdp_uuid16_create(&uuid, p->svclass);
-
- for (r = recs; r != NULL; r = r->next) {
- sdp_list_t *classes;
- uuid_t class;
-
- record = r->data;
-
- if (sdp_get_service_classes(record, &classes) < 0) {
- error("Unable to get service classes from record");
- continue;
- }
-
- memcpy(&class, classes->data, sizeof(uuid));
-
- sdp_list_free(classes, free);
-
- if (sdp_uuid_cmp(&class, &uuid) == 0)
- break;
- }
-
- if (r == NULL) {
- error("No record found with UUID 0x%04x", p->svclass);
- goto failed_not_supported;
- }
-
- if (headset_set_channel(hs, record, p->svclass) < 0) {
- error("Unable to extract RFCOMM channel from service record");
- goto failed_not_supported;
- }
-
- /* Set svclass to 0 so we can easily check that SDP is no-longer
- * going on (to know if bt_cancel_discovery needs to be called) */
- p->svclass = 0;
-
- err = rfcomm_connect(dev, NULL, NULL, NULL);
- if (err < 0) {
- error("Unable to connect: %s (%d)", strerror(-err), -err);
- p->err = -err;
- if (p->msg != NULL)
- error_connect_failed(p->msg, p->err);
- goto failed;
- }
-
- return;
-
-failed_not_supported:
- if (p->svclass == HANDSFREE_SVCLASS_ID &&
- get_records(dev, NULL, NULL, NULL) == 0)
- return;
- if (p->msg) {
- DBusMessage *reply = btd_error_not_supported(p->msg);
- g_dbus_send_message(btd_get_dbus_connection(), reply);
- }
-failed:
- p->svclass = 0;
- pending_connect_finalize(dev);
- headset_set_state(dev, HEADSET_STATE_DISCONNECTED);
-}
-
-static int get_records(struct audio_device *device, headset_stream_cb_t cb,
- void *user_data, unsigned int *cb_id)
-{
- struct headset *hs = device->headset;
- uint16_t svclass;
- uuid_t uuid;
- int err;
-
- if (hs->pending && hs->pending->svclass == HANDSFREE_SVCLASS_ID)
- svclass = HEADSET_SVCLASS_ID;
- else
- svclass = hs->search_hfp ? HANDSFREE_SVCLASS_ID :
- HEADSET_SVCLASS_ID;
-
- sdp_uuid16_create(&uuid, svclass);
-
- err = bt_search_service(&device->src, &device->dst, &uuid,
- get_record_cb, device, NULL);
- if (err < 0)
- return err;
-
- if (hs->pending) {
- hs->pending->svclass = svclass;
- return 0;
- }
-
- headset_set_state(device, HEADSET_STATE_CONNECTING);
-
- pending_connect_init(hs, HEADSET_STATE_CONNECTED);
-
- hs->pending->svclass = svclass;
-
- if (cb) {
- unsigned int id;
- id = connect_cb_new(hs, HEADSET_STATE_CONNECTED,
- cb, user_data);
- if (cb_id)
- *cb_id = id;
- }
-
- return 0;
-}
-
-static int rfcomm_connect(struct audio_device *dev, headset_stream_cb_t cb,
- void *user_data, unsigned int *cb_id)
-{
- struct headset *hs = dev->headset;
- char address[18];
- GError *err = NULL;
-
- if (hs->rfcomm_ch < 0)
- return get_records(dev, cb, user_data, cb_id);
-
- ba2str(&dev->dst, address);
-
- DBG("%s: Connecting to %s channel %d", device_get_path(dev->btd_dev),
- address, hs->rfcomm_ch);
-
- hs->tmp_rfcomm = bt_io_connect(headset_connect_cb, dev,
- NULL, &err,
- BT_IO_OPT_SOURCE_BDADDR, &dev->src,
- BT_IO_OPT_DEST_BDADDR, &dev->dst,
- BT_IO_OPT_CHANNEL, hs->rfcomm_ch,
- BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
- BT_IO_OPT_INVALID);
-
- hs->rfcomm_ch = -1;
-
- if (!hs->tmp_rfcomm) {
- error("%s", err->message);
- g_error_free(err);
- return -EIO;
- }
-
- hs->hfp_active = hs->hfp_handle != 0 ? TRUE : FALSE;
- hs->rfcomm_initiator = FALSE;
-
- headset_set_state(dev, HEADSET_STATE_CONNECTING);
-
- pending_connect_init(hs, HEADSET_STATE_CONNECTED);
-
- if (cb) {
- unsigned int id = connect_cb_new(hs, HEADSET_STATE_CONNECTED,
- cb, user_data);
- if (cb_id)
- *cb_id = id;
- }
-
- return 0;
-}
-
-static DBusMessage *hs_stop(DBusConnection *conn, DBusMessage *msg,
- void *data)
-{
- struct audio_device *device = data;
- struct headset *hs = device->headset;
- DBusMessage *reply = NULL;
-
- if (hs->state < HEADSET_STATE_PLAY_IN_PROGRESS)
- return btd_error_not_connected(msg);
-
- reply = dbus_message_new_method_return(msg);
- if (!reply)
- return NULL;
-
- headset_set_state(device, HEADSET_STATE_CONNECTED);
-
- return reply;
-}
-
-static DBusMessage *hs_disconnect(DBusConnection *conn, DBusMessage *msg,
- void *data)
-{
- struct audio_device *device = data;
- struct headset *hs = device->headset;
- char hs_address[18];
-
- if (hs->state == HEADSET_STATE_DISCONNECTED)
- return btd_error_not_connected(msg);
-
- headset_shutdown(device);
- ba2str(&device->dst, hs_address);
- info("Disconnected from %s, %s", hs_address,
- device_get_path(device->btd_dev));
-
- return dbus_message_new_method_return(msg);
-
-}
-
-static DBusMessage *hs_is_connected(DBusConnection *conn,
- DBusMessage *msg,
- void *data)
-{
- struct audio_device *device = data;
- DBusMessage *reply;
- dbus_bool_t connected;
-
- reply = dbus_message_new_method_return(msg);
- if (!reply)
- return NULL;
-
- connected = (device->headset->state >= HEADSET_STATE_CONNECTED);
-
- dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &connected,
- DBUS_TYPE_INVALID);
-
- return reply;
-}
-
-static DBusMessage *hs_connect(DBusConnection *conn, DBusMessage *msg,
- void *data)
-{
- struct audio_device *device = data;
- struct headset *hs = device->headset;
- int err;
-
- if (hs->state == HEADSET_STATE_CONNECTING)
- return btd_error_in_progress(msg);
- else if (hs->state > HEADSET_STATE_CONNECTING)
- return btd_error_already_connected(msg);
-
- if (hs->hfp_handle && !ag.telephony_ready)
- return btd_error_not_ready(msg);
-
- device->auto_connect = FALSE;
-
- err = rfcomm_connect(device, NULL, NULL, NULL);
- if (err < 0)
- return btd_error_failed(msg, strerror(-err));
-
- hs->auto_dc = FALSE;
-
- hs->pending->msg = dbus_message_ref(msg);
-
- return NULL;
-}
-
-static DBusMessage *hs_ring(DBusConnection *conn, DBusMessage *msg,
- void *data)
-{
- struct audio_device *device = data;
- struct headset *hs = device->headset;
- DBusMessage *reply = NULL;
- int err;
-
- if (hs->state < HEADSET_STATE_CONNECTED)
- return btd_error_not_connected(msg);
-
- reply = dbus_message_new_method_return(msg);
- if (!reply)
- return NULL;
-
- if (ag.ring_timer) {
- DBG("IndicateCall received when already indicating");
- return reply;
- }
-
- err = headset_send(hs, "\r\nRING\r\n");
- if (err < 0) {
- dbus_message_unref(reply);
- return btd_error_failed(msg, strerror(-err));
- }
-
- ring_timer_cb(NULL);
- ag.ring_timer = g_timeout_add_seconds(RING_INTERVAL, ring_timer_cb,
- NULL);
-
- return reply;
-}
-
-static DBusMessage *hs_cancel_call(DBusConnection *conn,
- DBusMessage *msg,
- void *data)
-{
- struct audio_device *device = data;
- struct headset *hs = device->headset;
- DBusMessage *reply = NULL;
-
- if (hs->state < HEADSET_STATE_CONNECTED)
- return btd_error_not_connected(msg);
-
- reply = dbus_message_new_method_return(msg);
- if (!reply)
- return NULL;
-
- if (ag.ring_timer) {
- g_source_remove(ag.ring_timer);
- ag.ring_timer = 0;
- } else
- DBG("Got CancelCall method call but no call is active");
-
- return reply;
-}
-
-static DBusMessage *hs_set_gain(DBusMessage *msg,
- void *data, uint16_t gain,
- char type)
-{
- struct audio_device *device = data;
- struct headset *hs = device->headset;
- DBusMessage *reply;
- int err;
-
- if (hs->state < HEADSET_STATE_CONNECTED)
- return btd_error_not_connected(msg);
-
- err = headset_set_gain(device, gain, type);
- if (err < 0)
- return btd_error_invalid_args(msg);
-
- reply = dbus_message_new_method_return(msg);
- if (!reply)
- return NULL;
-
- if (hs->state == HEADSET_STATE_PLAYING) {
- err = headset_send(hs, "\r\n+VG%c=%u\r\n", type, gain);
- if (err < 0) {
- dbus_message_unref(reply);
- return btd_error_failed(msg, strerror(-err));
- }
- }
-
- return reply;
-}
-
-static DBusMessage *hs_get_properties(DBusConnection *conn,
- DBusMessage *msg, void *data)
-{
- struct audio_device *device = data;
- DBusMessage *reply;
- DBusMessageIter iter;
- DBusMessageIter dict;
- gboolean value;
- const char *state;
-
- 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);
-
-
- /* Playing */
- value = (device->headset->state == HEADSET_STATE_PLAYING);
- dict_append_entry(&dict, "Playing", DBUS_TYPE_BOOLEAN, &value);
-
- /* State */
- state = state2str(device->headset->state);
- if (state)
- dict_append_entry(&dict, "State", DBUS_TYPE_STRING, &state);
-
- /* Connected */
- value = (device->headset->state >= HEADSET_STATE_CONNECTED);
- dict_append_entry(&dict, "Connected", DBUS_TYPE_BOOLEAN, &value);
-
- if (!value)
- goto done;
-
- /* SpeakerGain */
- dict_append_entry(&dict, "SpeakerGain",
- DBUS_TYPE_UINT16,
- &device->headset->slc->sp_gain);
-
- /* MicrophoneGain */
- dict_append_entry(&dict, "MicrophoneGain",
- DBUS_TYPE_UINT16,
- &device->headset->slc->mic_gain);
-
-done:
- dbus_message_iter_close_container(&iter, &dict);
-
- return reply;
-}
-
-static DBusMessage *hs_set_property(DBusConnection *conn,
- DBusMessage *msg, void *data)
-{
- const char *property;
- DBusMessageIter iter;
- DBusMessageIter sub;
- uint16_t gain;
-
- if (!dbus_message_iter_init(msg, &iter))
- return btd_error_invalid_args(msg);
-
- if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
- return btd_error_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 btd_error_invalid_args(msg);
- dbus_message_iter_recurse(&iter, &sub);
-
- if (g_str_equal("SpeakerGain", property)) {
- if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_UINT16)
- return btd_error_invalid_args(msg);
-
- dbus_message_iter_get_basic(&sub, &gain);
- return hs_set_gain(msg, data, gain,
- HEADSET_GAIN_SPEAKER);
- } else if (g_str_equal("MicrophoneGain", property)) {
- if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_UINT16)
- return btd_error_invalid_args(msg);
-
- dbus_message_iter_get_basic(&sub, &gain);
- return hs_set_gain(msg, data, gain,
- HEADSET_GAIN_MICROPHONE);
- }
-
- return btd_error_invalid_args(msg);
-}
-
-static const GDBusMethodTable headset_methods[] = {
- { GDBUS_ASYNC_METHOD("Connect", NULL, NULL, hs_connect) },
- { GDBUS_METHOD("Disconnect", NULL, NULL, hs_disconnect) },
- { GDBUS_METHOD("IsConnected",
- NULL, GDBUS_ARGS({ "connected", "b" }),
- hs_is_connected) },
- { GDBUS_METHOD("IndicateCall", NULL, NULL, hs_ring) },
- { GDBUS_METHOD("CancelCall", NULL, NULL, hs_cancel_call) },
- { GDBUS_METHOD("Stop", NULL, NULL, hs_stop) },
- { GDBUS_METHOD("GetProperties",
- NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
- hs_get_properties) },
- { GDBUS_METHOD("SetProperty",
- GDBUS_ARGS({ "name", "s" }, { "value", "v" }), NULL,
- hs_set_property) },
- { }
-};
-
-static const GDBusSignalTable headset_signals[] = {
- { GDBUS_DEPRECATED_SIGNAL("Connected", NULL) },
- { GDBUS_DEPRECATED_SIGNAL("Disconnected", NULL) },
- { GDBUS_SIGNAL("AnswerRequested", NULL) },
- { GDBUS_DEPRECATED_SIGNAL("Stopped", NULL) },
- { GDBUS_DEPRECATED_SIGNAL("Playing", NULL) },
- { GDBUS_DEPRECATED_SIGNAL("SpeakerGainChanged",
- GDBUS_ARGS({ "gain", "q" })) },
- { GDBUS_DEPRECATED_SIGNAL("MicrophoneGainChanged",
- GDBUS_ARGS({ "gain", "q" })) },
- { GDBUS_SIGNAL("CallTerminated", NULL) },
- { GDBUS_SIGNAL("PropertyChanged",
- GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
- { }
-};
-
-void headset_update(struct audio_device *dev, struct headset *headset,
- GSList *uuids)
-{
- const sdp_record_t *record;
-
- if (g_slist_find_custom(uuids, HFP_HS_UUID, bt_uuid_strcmp) &&
- headset->hfp_handle == 0) {
- record = btd_device_get_record(dev->btd_dev, HFP_HS_UUID);
- if (record)
- headset->hfp_handle = record->handle;
- }
-
- if (g_slist_find_custom(uuids, HSP_HS_UUID, bt_uuid_strcmp) &&
- headset->hsp_handle == 0) {
- record = btd_device_get_record(dev->btd_dev, HSP_HS_UUID);
- if (record)
- headset->hsp_handle = record->handle;
- }
-}
-
-static int headset_close_rfcomm(struct audio_device *dev)
-{
- struct headset *hs = dev->headset;
- GIOChannel *rfcomm = hs->tmp_rfcomm ? hs->tmp_rfcomm : hs->rfcomm;
-
- if (rfcomm) {
- g_io_channel_shutdown(rfcomm, TRUE, NULL);
- g_io_channel_unref(rfcomm);
- hs->tmp_rfcomm = NULL;
- hs->rfcomm = NULL;
- }
-
- g_free(hs->slc);
- hs->slc = NULL;
-
- return 0;
-}
-
-static void headset_free(struct audio_device *dev)
-{
- struct headset *hs = dev->headset;
-
- if (hs->dc_timer) {
- g_source_remove(hs->dc_timer);
- hs->dc_timer = 0;
- }
-
- close_sco(dev);
-
- headset_close_rfcomm(dev);
-
- g_slist_free_full(hs->nrec_cbs, g_free);
-
- g_free(hs);
- dev->headset = NULL;
-}
-
-static void path_unregister(void *data)
-{
- struct audio_device *dev = data;
- struct headset *hs = dev->headset;
-
- if (hs->state > HEADSET_STATE_DISCONNECTED) {
- DBG("Headset unregistered while device was connected!");
- headset_shutdown(dev);
- }
-
- DBG("Unregistered interface %s on path %s",
- AUDIO_HEADSET_INTERFACE, device_get_path(dev->btd_dev));
-
- headset_free(dev);
-}
-
-void headset_unregister(struct audio_device *dev)
-{
- g_dbus_unregister_interface(btd_get_dbus_connection(),
- device_get_path(dev->btd_dev),
- AUDIO_HEADSET_INTERFACE);
-}
-
-struct headset *headset_init(struct audio_device *dev, GSList *uuids,
- gboolean hfp_enabled)
-{
- struct headset *hs;
-
- hs = g_new0(struct headset, 1);
- hs->rfcomm_ch = -1;
- hs->search_hfp = hfp_enabled;
-
- headset_update(dev, hs, uuids);
-
- if (!g_dbus_register_interface(btd_get_dbus_connection(),
- device_get_path(dev->btd_dev),
- AUDIO_HEADSET_INTERFACE,
- headset_methods, headset_signals, NULL,
- dev, path_unregister)) {
- g_free(hs);
- return NULL;
- }
-
- DBG("Registered interface %s on path %s", AUDIO_HEADSET_INTERFACE,
- device_get_path(dev->btd_dev));
-
- return hs;
-}
-
-uint32_t headset_config_init(GKeyFile *config)
-{
- GError *err = NULL;
- char *str;
-
- /* Use the default values if there is no config file */
- if (config == NULL)
- return ag.features;
-
- str = g_key_file_get_string(config, "General", "SCORouting",
- &err);
- if (err) {
- DBG("audio.conf: %s", err->message);
- g_clear_error(&err);
- } else {
- if (strcmp(str, "PCM") == 0)
- sco_hci = FALSE;
- else if (strcmp(str, "HCI") == 0)
- sco_hci = TRUE;
- else
- error("Invalid Headset Routing value: %s", str);
- g_free(str);
- }
-
- /* Init fast connectable option */
- str = g_key_file_get_string(config, "Headset", "FastConnectable",
- &err);
- if (err) {
- DBG("audio.conf: %s", err->message);
- g_clear_error(&err);
- } else {
- fast_connectable = strcmp(str, "true") == 0;
- if (fast_connectable)
- manager_set_fast_connectable(FALSE);
- g_free(str);
- }
-
- return ag.features;
-}
-
-static gboolean hs_dc_timeout(struct audio_device *dev)
-{
- headset_set_state(dev, HEADSET_STATE_DISCONNECTED);
- return FALSE;
-}
-
-gboolean headset_cancel_stream(struct audio_device *dev, unsigned int id)
-{
- struct headset *hs = dev->headset;
- struct pending_connect *p = hs->pending;
- GSList *l;
- struct connect_cb *cb = NULL;
-
- if (!p)
- return FALSE;
-
- for (l = p->callbacks; l != NULL; l = l->next) {
- struct connect_cb *tmp = l->data;
-
- if (tmp->id == id) {
- cb = tmp;
- break;
- }
- }
-
- if (!cb)
- return FALSE;
-
- p->callbacks = g_slist_remove(p->callbacks, cb);
- g_free(cb);
-
- if (p->callbacks || p->msg)
- return TRUE;
-
- if (hs->auto_dc) {
- if (hs->rfcomm)
- hs->dc_timer = g_timeout_add_seconds(DC_TIMEOUT,
- (GSourceFunc) hs_dc_timeout,
- dev);
- else
- headset_set_state(dev, HEADSET_STATE_DISCONNECTED);
- }
-
- return TRUE;
-}
-
-static gboolean dummy_connect_complete(struct audio_device *dev)
-{
- pending_connect_finalize(dev);
- return FALSE;
-}
-
-unsigned int headset_request_stream(struct audio_device *dev,
- headset_stream_cb_t cb,
- void *user_data)
-{
- struct headset *hs = dev->headset;
- unsigned int id;
-
- if (hs->state == HEADSET_STATE_PLAYING) {
- id = connect_cb_new(hs, HEADSET_STATE_PLAYING, cb, user_data);
- g_idle_add((GSourceFunc) dummy_connect_complete, dev);
- return id;
- }
-
- if (hs->dc_timer) {
- g_source_remove(hs->dc_timer);
- hs->dc_timer = 0;
- }
-
- if (hs->state == HEADSET_STATE_CONNECTING ||
- hs->state == HEADSET_STATE_PLAY_IN_PROGRESS)
- return connect_cb_new(hs, HEADSET_STATE_PLAYING, cb, user_data);
-
- if (hs->rfcomm == NULL) {
- if (rfcomm_connect(dev, cb, user_data, &id) < 0)
- return 0;
- hs->auto_dc = TRUE;
- } else if (sco_connect(dev, cb, user_data, &id) < 0)
- return 0;
-
- hs->pending->target_state = HEADSET_STATE_PLAYING;
-
- return id;
-}
-
-unsigned int headset_config_stream(struct audio_device *dev,
- gboolean auto_dc,
- headset_stream_cb_t cb,
- void *user_data)
-{
- struct headset *hs = dev->headset;
- unsigned int id = 0;
-
- if (hs->dc_timer) {
- g_source_remove(hs->dc_timer);
- hs->dc_timer = 0;
- }
-
- if (hs->state == HEADSET_STATE_CONNECTING)
- return connect_cb_new(hs, HEADSET_STATE_CONNECTED, cb,
- user_data);
-
- if (hs->rfcomm)
- goto done;
-
- if (rfcomm_connect(dev, cb, user_data, &id) < 0)
- return 0;
-
- hs->auto_dc = auto_dc;
- hs->pending->target_state = HEADSET_STATE_CONNECTED;
-
- return id;
-
-done:
- id = connect_cb_new(hs, HEADSET_STATE_CONNECTED, cb, user_data);
- g_idle_add((GSourceFunc) dummy_connect_complete, dev);
- return id;
-}
-
-unsigned int headset_suspend_stream(struct audio_device *dev,
- headset_stream_cb_t cb,
- void *user_data)
-{
- struct headset *hs = dev->headset;
- unsigned int id;
- int sock;
-
- if (hs->state == HEADSET_STATE_DISCONNECTED ||
- hs->state == HEADSET_STATE_CONNECTING)
- return 0;
-
- if (hs->dc_timer) {
- g_source_remove(hs->dc_timer);
- hs->dc_timer = 0;
- }
-
- if (hs->sco) {
- sock = g_io_channel_unix_get_fd(hs->sco);
-
- /* shutdown but leave the socket open and wait for hup */
- shutdown(sock, SHUT_RDWR);
- } else {
- headset_set_state(dev, HEADSET_STATE_CONNECTED);
-
- g_idle_add((GSourceFunc) dummy_connect_complete, dev);
- }
-
- id = connect_cb_new(hs, HEADSET_STATE_CONNECTED, cb, user_data);
-
- return id;
-}
-
-gboolean headset_get_hfp_active(struct audio_device *dev)
-{
- struct headset *hs = dev->headset;
-
- return hs->hfp_active;
-}
-
-void headset_set_hfp_active(struct audio_device *dev, gboolean active)
-{
- struct headset *hs = dev->headset;
-
- hs->hfp_active = active;
-}
-
-gboolean headset_get_rfcomm_initiator(struct audio_device *dev)
-{
- struct headset *hs = dev->headset;
-
- return hs->rfcomm_initiator;
-}
-
-void headset_set_rfcomm_initiator(struct audio_device *dev,
- gboolean initiator)
-{
- struct headset *hs = dev->headset;
-
- hs->rfcomm_initiator = initiator;
-}
-
-GIOChannel *headset_get_rfcomm(struct audio_device *dev)
-{
- struct headset *hs = dev->headset;
-
- return hs->tmp_rfcomm;
-}
-
-int headset_connect_rfcomm(struct audio_device *dev, GIOChannel *io)
-{
- struct headset *hs = dev->headset;
-
- if (hs->tmp_rfcomm)
- return -EALREADY;
-
- hs->tmp_rfcomm = g_io_channel_ref(io);
-
- return 0;
-}
-
-int headset_connect_sco(struct audio_device *dev, GIOChannel *io)
-{
- struct headset *hs = dev->headset;
- struct headset_slc *slc = hs->slc;
-
- if (hs->sco)
- return -EISCONN;
-
- hs->sco = g_io_channel_ref(io);
-
- if (slc->pending_ring) {
- ring_timer_cb(NULL);
- ag.ring_timer = g_timeout_add_seconds(RING_INTERVAL,
- ring_timer_cb,
- NULL);
- slc->pending_ring = FALSE;
- }
-
- return 0;
-}
-
-void headset_set_state(struct audio_device *dev, headset_state_t state)
-{
- struct headset *hs = dev->headset;
- struct headset_slc *slc = hs->slc;
- gboolean value;
- const char *state_str;
- headset_state_t old_state = hs->state;
- GSList *l;
-
- if (old_state == state)
- return;
-
- state_str = state2str(state);
-
- switch (state) {
- case HEADSET_STATE_DISCONNECTED:
- value = FALSE;
- close_sco(dev);
- headset_close_rfcomm(dev);
- emit_property_changed(device_get_path(dev->btd_dev),
- AUDIO_HEADSET_INTERFACE, "State",
- DBUS_TYPE_STRING, &state_str);
- g_dbus_emit_signal(btd_get_dbus_connection(),
- device_get_path(dev->btd_dev),
- AUDIO_HEADSET_INTERFACE,
- "Disconnected",
- DBUS_TYPE_INVALID);
- if (hs->state > HEADSET_STATE_CONNECTING) {
- emit_property_changed(device_get_path(dev->btd_dev),
- AUDIO_HEADSET_INTERFACE, "Connected",
- DBUS_TYPE_BOOLEAN, &value);
- telephony_device_disconnected(dev);
- }
- active_devices = g_slist_remove(active_devices, dev);
- break;
- case HEADSET_STATE_CONNECTING:
- emit_property_changed(device_get_path(dev->btd_dev),
- AUDIO_HEADSET_INTERFACE, "State",
- DBUS_TYPE_STRING, &state_str);
- break;
- case HEADSET_STATE_CONNECTED:
- close_sco(dev);
- if (hs->state != HEADSET_STATE_PLAY_IN_PROGRESS)
- emit_property_changed(device_get_path(dev->btd_dev),
- AUDIO_HEADSET_INTERFACE, "State",
- DBUS_TYPE_STRING, &state_str);
- if (hs->state < state) {
- if (ag.features & AG_FEATURE_INBAND_RINGTONE)
- slc->inband_ring = TRUE;
- else
- slc->inband_ring = FALSE;
- g_dbus_emit_signal(btd_get_dbus_connection(),
- device_get_path(dev->btd_dev),
- AUDIO_HEADSET_INTERFACE,
- "Connected",
- DBUS_TYPE_INVALID);
- value = TRUE;
- emit_property_changed(device_get_path(dev->btd_dev),
- AUDIO_HEADSET_INTERFACE,
- "Connected",
- DBUS_TYPE_BOOLEAN, &value);
- active_devices = g_slist_append(active_devices, dev);
- telephony_device_connected(dev);
- } else if (hs->state == HEADSET_STATE_PLAYING) {
- value = FALSE;
- g_dbus_emit_signal(btd_get_dbus_connection(),
- device_get_path(dev->btd_dev),
- AUDIO_HEADSET_INTERFACE,
- "Stopped",
- DBUS_TYPE_INVALID);
- emit_property_changed(device_get_path(dev->btd_dev),
- AUDIO_HEADSET_INTERFACE,
- "Playing",
- DBUS_TYPE_BOOLEAN, &value);
- }
- break;
- case HEADSET_STATE_PLAY_IN_PROGRESS:
- break;
- case HEADSET_STATE_PLAYING:
- value = TRUE;
- emit_property_changed(device_get_path(dev->btd_dev),
- AUDIO_HEADSET_INTERFACE, "State",
- DBUS_TYPE_STRING, &state_str);
-
- /* Do not watch HUP since we need to know when the link is
- really disconnected */
- hs->sco_id = g_io_add_watch(hs->sco,
- G_IO_ERR | G_IO_NVAL,
- (GIOFunc) sco_cb, dev);
-
- g_dbus_emit_signal(btd_get_dbus_connection(),
- device_get_path(dev->btd_dev),
- AUDIO_HEADSET_INTERFACE, "Playing",
- DBUS_TYPE_INVALID);
- emit_property_changed(device_get_path(dev->btd_dev),
- AUDIO_HEADSET_INTERFACE, "Playing",
- DBUS_TYPE_BOOLEAN, &value);
-
- if (slc->sp_gain >= 0)
- headset_send(hs, "\r\n+VGS=%u\r\n", slc->sp_gain);
- if (slc->mic_gain >= 0)
- headset_send(hs, "\r\n+VGM=%u\r\n", slc->mic_gain);
- break;
- }
-
- hs->state = state;
-
- DBG("State changed %s: %s -> %s", device_get_path(dev->btd_dev),
- str_state[old_state], str_state[state]);
-
- for (l = headset_callbacks; l != NULL; l = l->next) {
- struct headset_state_callback *cb = l->data;
- cb->cb(dev, old_state, state, cb->user_data);
- }
-}
-
-headset_state_t headset_get_state(struct audio_device *dev)
-{
- struct headset *hs = dev->headset;
-
- return hs->state;
-}
-
-int headset_get_channel(struct audio_device *dev)
-{
- struct headset *hs = dev->headset;
-
- return hs->rfcomm_ch;
-}
-
-gboolean headset_is_active(struct audio_device *dev)
-{
- struct headset *hs = dev->headset;
-
- if (hs->state != HEADSET_STATE_DISCONNECTED)
- return TRUE;
-
- return FALSE;
-}
-
-headset_lock_t headset_get_lock(struct audio_device *dev)
-{
- struct headset *hs = dev->headset;
-
- return hs->lock;
-}
-
-gboolean headset_lock(struct audio_device *dev, headset_lock_t lock)
-{
- struct headset *hs = dev->headset;
-
- if (hs->lock & lock)
- return FALSE;
-
- hs->lock |= lock;
-
- return TRUE;
-}
-
-gboolean headset_unlock(struct audio_device *dev, headset_lock_t lock)
-{
- struct headset *hs = dev->headset;
-
- if (!(hs->lock & lock))
- return FALSE;
-
- hs->lock &= ~lock;
-
- if (hs->lock)
- return TRUE;
-
- if (hs->state == HEADSET_STATE_PLAYING)
- headset_set_state(dev, HEADSET_STATE_CONNECTED);
-
- if (hs->auto_dc) {
- if (hs->state == HEADSET_STATE_CONNECTED)
- hs->dc_timer = g_timeout_add_seconds(DC_TIMEOUT,
- (GSourceFunc) hs_dc_timeout,
- dev);
- else
- headset_set_state(dev, HEADSET_STATE_DISCONNECTED);
- }
-
- return TRUE;
-}
-
-gboolean headset_suspend(struct audio_device *dev, void *data)
-{
- return TRUE;
-}
-
-gboolean headset_play(struct audio_device *dev, void *data)
-{
- return TRUE;
-}
-
-int headset_get_sco_fd(struct audio_device *dev)
-{
- struct headset *hs = dev->headset;
-
- if (!hs->sco)
- return -1;
-
- return g_io_channel_unix_get_fd(hs->sco);
-}
-
-gboolean headset_get_nrec(struct audio_device *dev)
-{
- struct headset *hs = dev->headset;
-
- if (!hs->slc)
- return TRUE;
-
- return hs->slc->nrec;
-}
-
-unsigned int headset_add_nrec_cb(struct audio_device *dev,
- headset_nrec_cb cb, void *user_data)
-{
- struct headset *hs = dev->headset;
- struct headset_nrec_callback *nrec_cb;
- static unsigned int id = 0;
-
- nrec_cb = g_new(struct headset_nrec_callback, 1);
- nrec_cb->cb = cb;
- nrec_cb->user_data = user_data;
- nrec_cb->id = ++id;
-
- hs->nrec_cbs = g_slist_prepend(hs->nrec_cbs, nrec_cb);
-
- return nrec_cb->id;
-}
-
-gboolean headset_remove_nrec_cb(struct audio_device *dev, unsigned int id)
-{
- struct headset *hs = dev->headset;
- GSList *l;
-
- for (l = hs->nrec_cbs; l != NULL; l = l->next) {
- struct headset_nrec_callback *cb = l->data;
- if (cb && cb->id == id) {
- hs->nrec_cbs = g_slist_remove(hs->nrec_cbs, cb);
- g_free(cb);
- return TRUE;
- }
- }
-
- return FALSE;
-}
-
-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;
-}
-
-void headset_shutdown(struct audio_device *dev)
-{
- struct pending_connect *p = dev->headset->pending;
-
- if (p && p->msg)
- error_connect_failed(p->msg, ECANCELED);
-
- pending_connect_finalize(dev);
- headset_set_state(dev, HEADSET_STATE_DISCONNECTED);
-}
-
-int telephony_event_ind(int index)
-{
- if (!active_devices)
- return -ENODEV;
-
- if (!ag.er_ind) {
- DBG("telephony_report_event called but events are disabled");
- return -EINVAL;
- }
-
- send_foreach_headset(active_devices, hfp_cmp,
- "\r\n+CIEV: %d,%d\r\n", index + 1,
- ag.indicators[index].val);
-
- return 0;
-}
-
-int telephony_response_and_hold_ind(int rh)
-{
- if (!active_devices)
- return -ENODEV;
-
- ag.rh = rh;
-
- /* If we aren't in any response and hold state don't send anything */
- if (ag.rh < 0)
- return 0;
-
- send_foreach_headset(active_devices, hfp_cmp, "\r\n+BTRH: %d\r\n",
- ag.rh);
-
- return 0;
-}
-
-int telephony_incoming_call_ind(const char *number, int type)
-{
- struct audio_device *dev;
- struct headset *hs;
- struct headset_slc *slc;
-
- if (fast_connectable)
- manager_set_fast_connectable(TRUE);
-
- if (!active_devices)
- return -ENODEV;
-
- /* Get the latest connected device */
- dev = active_devices->data;
- hs = dev->headset;
- slc = hs->slc;
-
- if (ag.ring_timer) {
- DBG("telephony_incoming_call_ind: already calling");
- return -EBUSY;
- }
-
- /* With HSP 1.2 the RING messages should *not* be sent if inband
- * ringtone is being used */
- if (!hs->hfp_active && slc->inband_ring)
- return 0;
-
- g_free(ag.number);
- ag.number = g_strdup(number);
- ag.number_type = type;
-
- if (slc->inband_ring && hs->hfp_active &&
- hs->state != HEADSET_STATE_PLAYING) {
- slc->pending_ring = TRUE;
- return 0;
- }
-
- ring_timer_cb(NULL);
- ag.ring_timer = g_timeout_add_seconds(RING_INTERVAL, ring_timer_cb,
- NULL);
-
- return 0;
-}
-
-int telephony_calling_stopped_ind(void)
-{
- struct audio_device *dev;
-
- if (fast_connectable)
- manager_set_fast_connectable(FALSE);
-
- if (ag.ring_timer) {
- g_source_remove(ag.ring_timer);
- ag.ring_timer = 0;
- }
-
- if (!active_devices)
- return 0;
-
- /* In case SCO isn't fully up yet */
- dev = active_devices->data;
-
- if (!dev->headset->slc->pending_ring && !ag.ring_timer)
- return -EINVAL;
-
- dev->headset->slc->pending_ring = FALSE;
-
- return 0;
-}
-
-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);
-
- return 0;
-}
-
-int telephony_deinit(void)
-{
- g_free(ag.number);
-
- memset(&ag, 0, sizeof(ag));
-
- ag.er_mode = 3;
- ag.rh = BTRH_NOT_SUPPORTED;
-
- DBG("Telephony deinitialized");
-
- return 0;
-}
-
-int telephony_list_current_call_ind(int idx, int dir, int status, int mode,
- int mprty, const char *number,
- int type)
-{
- if (!active_devices)
- return -ENODEV;
-
- if (number && strlen(number) > 0)
- send_foreach_headset(active_devices, hfp_cmp,
- "\r\n+CLCC: %d,%d,%d,%d,%d,\"%s\",%d\r\n",
- idx, dir, status, mode, mprty, number, type);
- else
- send_foreach_headset(active_devices, hfp_cmp,
- "\r\n+CLCC: %d,%d,%d,%d,%d\r\n",
- idx, dir, status, mode, mprty);
-
- return 0;
-}
-
-int telephony_subscriber_number_ind(const char *number, int type, int service)
-{
- if (!active_devices)
- return -ENODEV;
-
- send_foreach_headset(active_devices, hfp_cmp,
- "\r\n+CNUM: ,%s,%d,,%d\r\n",
- number, type, service);
-
- return 0;
-}
-
-static int cwa_cmp(struct headset *hs)
-{
- if (!hs->hfp_active)
- return -1;
-
- if (hs->slc->cwa_enabled)
- return 0;
- else
- return -1;
-}
-
-int telephony_call_waiting_ind(const char *number, int type)
-{
- if (!active_devices)
- return -ENODEV;
-
- send_foreach_headset(active_devices, cwa_cmp,
- "\r\n+CCWA: \"%s\",%d\r\n",
- number, type);
-
- return 0;
-}
-
-unsigned int headset_add_state_cb(headset_state_cb cb, void *user_data)
-{
- struct headset_state_callback *state_cb;
- static unsigned int id = 0;
-
- state_cb = g_new(struct headset_state_callback, 1);
- state_cb->cb = cb;
- state_cb->user_data = user_data;
- state_cb->id = ++id;
-
- headset_callbacks = g_slist_append(headset_callbacks, state_cb);
-
- return state_cb->id;
-}
-
-gboolean headset_remove_state_cb(unsigned int id)
-{
- GSList *l;
-
- for (l = headset_callbacks; l != NULL; l = l->next) {
- struct headset_state_callback *cb = l->data;
- if (cb && cb->id == id) {
- headset_callbacks = g_slist_remove(headset_callbacks, cb);
- g_free(cb);
- return TRUE;
- }
- }
-
- return FALSE;
-}
diff --git a/audio/headset.h b/audio/headset.h
deleted file mode 100644
index 736e4fe..0000000
--- a/audio/headset.h
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- *
- * 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 AUDIO_HEADSET_INTERFACE "org.bluez.Headset"
-
-#define DEFAULT_HS_AG_CHANNEL 12
-#define DEFAULT_HF_AG_CHANNEL 13
-
-typedef enum {
- HEADSET_STATE_DISCONNECTED,
- HEADSET_STATE_CONNECTING,
- HEADSET_STATE_CONNECTED,
- HEADSET_STATE_PLAY_IN_PROGRESS,
- HEADSET_STATE_PLAYING
-} headset_state_t;
-
-typedef enum {
- HEADSET_LOCK_READ = 1,
- HEADSET_LOCK_WRITE = 1 << 1,
-} headset_lock_t;
-
-typedef void (*headset_state_cb) (struct audio_device *dev,
- headset_state_t old_state,
- headset_state_t new_state,
- void *user_data);
-typedef void (*headset_nrec_cb) (struct audio_device *dev,
- gboolean nrec,
- void *user_data);
-
-unsigned int headset_add_state_cb(headset_state_cb cb, void *user_data);
-gboolean headset_remove_state_cb(unsigned int id);
-
-typedef void (*headset_stream_cb_t) (struct audio_device *dev, void *user_data);
-
-void headset_connect_cb(GIOChannel *chan, GError *err, gpointer user_data);
-
-GIOChannel *headset_get_rfcomm(struct audio_device *dev);
-
-struct headset *headset_init(struct audio_device *dev, GSList *uuids,
- gboolean hfp_enabled);
-
-void headset_unregister(struct audio_device *dev);
-
-uint32_t headset_config_init(GKeyFile *config);
-
-void headset_update(struct audio_device *dev, struct headset *headset,
- GSList *uuids);
-
-unsigned int headset_config_stream(struct audio_device *dev,
- gboolean auto_dc,
- headset_stream_cb_t cb,
- void *user_data);
-unsigned int headset_request_stream(struct audio_device *dev,
- headset_stream_cb_t cb,
- void *user_data);
-unsigned int headset_suspend_stream(struct audio_device *dev,
- headset_stream_cb_t cb,
- void *user_data);
-gboolean headset_cancel_stream(struct audio_device *dev, unsigned int id);
-
-gboolean headset_get_hfp_active(struct audio_device *dev);
-void headset_set_hfp_active(struct audio_device *dev, gboolean active);
-
-gboolean headset_get_rfcomm_initiator(struct audio_device *dev);
-void headset_set_rfcomm_initiator(struct audio_device *dev,
- gboolean initiator);
-
-int headset_connect_rfcomm(struct audio_device *dev, GIOChannel *chan);
-int headset_connect_sco(struct audio_device *dev, GIOChannel *io);
-
-headset_state_t headset_get_state(struct audio_device *dev);
-void headset_set_state(struct audio_device *dev, headset_state_t state);
-
-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);
-unsigned int headset_add_nrec_cb(struct audio_device *dev,
- headset_nrec_cb cb, void *user_data);
-gboolean headset_remove_nrec_cb(struct audio_device *dev, unsigned int id);
-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);
-
-headset_lock_t headset_get_lock(struct audio_device *dev);
-gboolean headset_lock(struct audio_device *dev, headset_lock_t lock);
-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);
--
1.7.11.7
^ 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