mirror of
https://git.kernel.org/pub/scm/bluetooth/bluez.git
synced 2024-11-17 17:24:32 +08:00
Make headset also connect rfcomm when it demands, remove asserts and some code cleanups.
This commit is contained in:
parent
bd0bc21437
commit
c205cc8890
613
audio/headset.c
613
audio/headset.c
@ -35,7 +35,6 @@
|
||||
#include <getopt.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <bluetooth/bluetooth.h>
|
||||
#include <bluetooth/hci.h>
|
||||
@ -96,6 +95,7 @@ struct headset {
|
||||
|
||||
static DBusHandlerResult hs_disconnect(DBusConnection *conn, DBusMessage *msg,
|
||||
void *data);
|
||||
static int rfcomm_connect(struct device *device, struct pending_connect *c);
|
||||
|
||||
static void pending_connect_free(struct pending_connect *c)
|
||||
{
|
||||
@ -177,7 +177,6 @@ static void close_sco(struct device *device)
|
||||
g_io_channel_close(hs->sco);
|
||||
g_io_channel_unref(hs->sco);
|
||||
hs->sco = NULL;
|
||||
assert(hs->rfcomm);
|
||||
hs->state = HEADSET_STATE_CONNECTED;
|
||||
dbus_connection_emit_signal(device->conn, device->path,
|
||||
AUDIO_HEADSET_INTERFACE, "Stopped",
|
||||
@ -311,8 +310,6 @@ static GIOError headset_send(struct headset *hs, const char *str)
|
||||
GIOError err;
|
||||
gsize total_written, written, count;
|
||||
|
||||
assert(hs != NULL);
|
||||
|
||||
if (hs->state < HEADSET_STATE_CONNECTED || !hs->rfcomm) {
|
||||
error("headset_send: the headset is not connected");
|
||||
return G_IO_ERROR_UNKNOWN;
|
||||
@ -346,11 +343,6 @@ static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond,
|
||||
|
||||
hs = device->headset;
|
||||
|
||||
assert(hs != NULL);
|
||||
assert(hs->pending_connect != NULL);
|
||||
assert(hs->sco == NULL);
|
||||
assert(hs->state == HEADSET_STATE_PLAY_IN_PROGRESS);
|
||||
|
||||
sk = g_io_channel_unix_get_fd(chan);
|
||||
|
||||
len = sizeof(ret);
|
||||
@ -411,13 +403,16 @@ static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond,
|
||||
return FALSE;
|
||||
|
||||
failed:
|
||||
err_connect_failed(device->conn, hs->pending_connect->msg, strerror(err));
|
||||
if (hs->pending_connect->msg)
|
||||
err_connect_failed(device->conn, hs->pending_connect->msg,
|
||||
strerror(err));
|
||||
if (hs->pending_connect->io)
|
||||
g_io_channel_close(hs->pending_connect->io);
|
||||
if (hs->pending_connect->pkt)
|
||||
unix_send_cfg(hs->pending_connect->sock,
|
||||
hs->pending_connect->pkt);
|
||||
pending_connect_free(hs->pending_connect);
|
||||
hs->pending_connect = NULL;
|
||||
|
||||
assert(hs->rfcomm);
|
||||
hs->state = HEADSET_STATE_CONNECTED;
|
||||
|
||||
return FALSE;
|
||||
@ -492,7 +487,7 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond,
|
||||
{
|
||||
struct headset *hs;
|
||||
char hs_address[18];
|
||||
int sk, ret, err;
|
||||
int sk, ret, err = 0;
|
||||
socklen_t len;
|
||||
|
||||
if (cond & G_IO_NVAL)
|
||||
@ -500,11 +495,6 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond,
|
||||
|
||||
hs = device->headset;
|
||||
|
||||
assert(hs != NULL);
|
||||
assert(hs->pending_connect != NULL);
|
||||
assert(hs->rfcomm == NULL);
|
||||
assert(hs->state == HEADSET_STATE_CONNECT_IN_PROGRESS);
|
||||
|
||||
sk = g_io_channel_unix_get_fd(chan);
|
||||
|
||||
len = sizeof(ret);
|
||||
@ -529,6 +519,7 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond,
|
||||
AUDIO_HEADSET_INTERFACE,
|
||||
"Connected", DBUS_TYPE_INVALID);
|
||||
|
||||
device_store(device, FALSE);
|
||||
debug("Connected to %s", hs_address);
|
||||
|
||||
g_io_add_watch(chan, G_IO_IN | G_IO_ERR | G_IO_HUP| G_IO_NVAL,
|
||||
@ -541,6 +532,11 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond,
|
||||
if (reply)
|
||||
send_message_and_unref(device->conn, reply);
|
||||
}
|
||||
else if (hs->pending_connect->pkt) {
|
||||
if (sco_connect(device, hs->pending_connect) < 0)
|
||||
goto failed;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
pending_connect_free(hs->pending_connect);
|
||||
hs->pending_connect = NULL;
|
||||
@ -548,98 +544,24 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond,
|
||||
return FALSE;
|
||||
|
||||
failed:
|
||||
err_connect_failed(device->conn, hs->pending_connect->msg, strerror(err));
|
||||
if (hs->pending_connect->msg)
|
||||
err_connect_failed(device->conn, hs->pending_connect->msg,
|
||||
strerror(err));
|
||||
if (hs->pending_connect->pkt)
|
||||
unix_send_cfg(hs->pending_connect->sock,
|
||||
hs->pending_connect->pkt);
|
||||
if (hs->pending_connect->io)
|
||||
g_io_channel_close(hs->pending_connect->io);
|
||||
if (hs->rfcomm)
|
||||
hs->state = HEADSET_STATE_CONNECTED;
|
||||
else
|
||||
hs->state = HEADSET_STATE_DISCONNECTED;
|
||||
pending_connect_free(hs->pending_connect);
|
||||
hs->pending_connect = NULL;
|
||||
|
||||
hs->state = HEADSET_STATE_DISCONNECTED;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static int rfcomm_connect(struct device *device, int *err)
|
||||
{
|
||||
struct headset *hs = device->headset;
|
||||
struct sockaddr_rc addr;
|
||||
char address[18];
|
||||
int sk;
|
||||
|
||||
assert(hs != NULL);
|
||||
assert(hs->pending_connect != NULL);
|
||||
assert(hs->state == HEADSET_STATE_CONNECT_IN_PROGRESS);
|
||||
|
||||
ba2str(&device->dst, address);
|
||||
|
||||
debug("Connecting to %s channel %d", address, hs->rfcomm_ch);
|
||||
|
||||
sk = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
|
||||
if (sk < 0) {
|
||||
if (err)
|
||||
*err = errno;
|
||||
error("socket: %s (%d)", strerror(errno), errno);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.rc_family = AF_BLUETOOTH;
|
||||
bacpy(&addr.rc_bdaddr, BDADDR_ANY);
|
||||
addr.rc_channel = 0;
|
||||
|
||||
if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
|
||||
if (err)
|
||||
*err = errno;
|
||||
error("bind: %s (%d)", strerror(errno), errno);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (set_nonblocking(sk) < 0) {
|
||||
if (err)
|
||||
*err = errno;
|
||||
goto failed;
|
||||
}
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.rc_family = AF_BLUETOOTH;
|
||||
bacpy(&addr.rc_bdaddr, &device->dst);
|
||||
addr.rc_channel = hs->rfcomm_ch;
|
||||
|
||||
hs->pending_connect->io = g_io_channel_unix_new(sk);
|
||||
if (!hs->pending_connect->io) {
|
||||
if (err)
|
||||
*err = ENOMEM;
|
||||
error("channel_unix_new failed in rfcomm connect");
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
|
||||
if (!(errno == EAGAIN || errno == EINPROGRESS)) {
|
||||
if (err)
|
||||
*err = errno;
|
||||
error("connect() failed: %s (%d)", strerror(errno),
|
||||
errno);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
debug("Connect in progress");
|
||||
|
||||
g_io_add_watch(hs->pending_connect->io, G_IO_OUT | G_IO_NVAL,
|
||||
(GIOFunc) rfcomm_connect_cb, device);
|
||||
} else {
|
||||
debug("Connect succeeded with first try");
|
||||
rfcomm_connect_cb(hs->pending_connect->io, G_IO_OUT, device);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
failed:
|
||||
if (!hs->pending_connect->io && sk >= 0)
|
||||
close(sk);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void get_record_reply(DBusPendingCall *call, void *data)
|
||||
{
|
||||
DBusMessage *reply;
|
||||
@ -653,10 +575,6 @@ static void get_record_reply(DBusPendingCall *call, void *data)
|
||||
struct headset *hs = device->headset;
|
||||
struct pending_connect *c;
|
||||
|
||||
assert(hs != NULL);
|
||||
assert(hs->pending_connect);
|
||||
assert(!hs->rfcomm);
|
||||
|
||||
c = hs->pending_connect;
|
||||
|
||||
reply = dbus_pending_call_steal_reply(call);
|
||||
@ -726,10 +644,11 @@ static void get_record_reply(DBusPendingCall *call, void *data)
|
||||
|
||||
hs->rfcomm_ch = ch;
|
||||
|
||||
if (rfcomm_connect(device, &err) < 0) {
|
||||
if ((err = rfcomm_connect(device, NULL)) < 0) {
|
||||
error("Unable to connect");
|
||||
if (c->msg)
|
||||
err_connect_failed(device->conn, c->msg, strerror(err));
|
||||
err_connect_failed(device->conn, c->msg,
|
||||
strerror(-err));
|
||||
goto failed;
|
||||
}
|
||||
|
||||
@ -757,6 +676,228 @@ failed:
|
||||
device_finish_sdp_transaction(device);
|
||||
}
|
||||
|
||||
static void get_handles_reply(DBusPendingCall *call, void *data)
|
||||
{
|
||||
DBusMessage *msg = NULL, *reply;
|
||||
DBusPendingCall *pending;
|
||||
DBusError derr;
|
||||
struct device *device = data;
|
||||
struct headset *hs = device->headset;
|
||||
struct pending_connect *c;
|
||||
char address[18], *addr_ptr = address;
|
||||
dbus_uint32_t *array = NULL;
|
||||
dbus_uint32_t handle;
|
||||
int array_len;
|
||||
|
||||
c = hs->pending_connect;
|
||||
|
||||
reply = dbus_pending_call_steal_reply(call);
|
||||
|
||||
dbus_error_init(&derr);
|
||||
if (dbus_set_error_from_message(&derr, reply)) {
|
||||
error("GetRemoteServiceHandles failed: %s", derr.message);
|
||||
if (c->msg) {
|
||||
if (dbus_error_has_name(&derr,
|
||||
"org.bluez.Error.ConnectionAttemptFailed"))
|
||||
err_connect_failed(device->conn, c->msg,
|
||||
strerror(EHOSTDOWN));
|
||||
else
|
||||
err_not_supported(device->conn, c->msg);
|
||||
}
|
||||
dbus_error_free(&derr);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (!dbus_message_get_args(reply, NULL,
|
||||
DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32,
|
||||
&array, &array_len,
|
||||
DBUS_TYPE_INVALID)) {
|
||||
error("Unable to get args from reply");
|
||||
if (c->msg)
|
||||
err_not_supported(device->conn, c->msg);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (!array) {
|
||||
error("get_handles_reply: Unable to get handle array from reply");
|
||||
if (c->msg)
|
||||
err_not_supported(device->conn, c->msg);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (array_len < 1) {
|
||||
debug("No record handles found");
|
||||
if (c->msg)
|
||||
err_not_supported(device->conn, c->msg);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (array_len > 1)
|
||||
debug("Multiple records found. Using the first one.");
|
||||
|
||||
msg = dbus_message_new_method_call("org.bluez", device->adapter_path,
|
||||
"org.bluez.Adapter",
|
||||
"GetRemoteServiceRecord");
|
||||
if (!msg) {
|
||||
error("Unable to allocate new method call");
|
||||
if (c->msg)
|
||||
err_connect_failed(device->conn, c->msg, strerror(ENOMEM));
|
||||
goto failed;
|
||||
}
|
||||
|
||||
ba2str(&device->dst, address);
|
||||
|
||||
handle = array[0];
|
||||
|
||||
dbus_message_append_args(msg, DBUS_TYPE_STRING, &addr_ptr,
|
||||
DBUS_TYPE_UINT32, &handle,
|
||||
DBUS_TYPE_INVALID);
|
||||
|
||||
if (!dbus_connection_send_with_reply(device->conn, msg, &pending, -1)) {
|
||||
error("Sending GetRemoteServiceRecord failed");
|
||||
if (c->msg)
|
||||
err_connect_failed(device->conn, c->msg, strerror(EIO));
|
||||
goto failed;
|
||||
}
|
||||
|
||||
dbus_pending_call_set_notify(pending, get_record_reply, device, NULL);
|
||||
dbus_pending_call_unref(pending);
|
||||
dbus_message_unref(msg);
|
||||
dbus_message_unref(reply);
|
||||
|
||||
return;
|
||||
|
||||
failed:
|
||||
if (msg)
|
||||
dbus_message_unref(msg);
|
||||
dbus_message_unref(reply);
|
||||
hs_disconnect(NULL, NULL, device);
|
||||
}
|
||||
|
||||
static int get_handles(struct device *device)
|
||||
{
|
||||
DBusPendingCall *pending;
|
||||
struct headset *hs = device->headset;
|
||||
const char *hs_svc;
|
||||
const char *addr_ptr;
|
||||
char hs_address[18];
|
||||
DBusMessage *msg;
|
||||
|
||||
msg = dbus_message_new_method_call("org.bluez", device->adapter_path,
|
||||
"org.bluez.Adapter",
|
||||
"GetRemoteServiceHandles");
|
||||
if (!msg) {
|
||||
error("Could not create a new dbus message");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (hs->type == SVC_HEADSET)
|
||||
hs_svc = "hsp";
|
||||
else
|
||||
hs_svc = "hfp";
|
||||
|
||||
ba2str(&device->dst, hs_address);
|
||||
addr_ptr = hs_address;
|
||||
dbus_message_append_args(msg, DBUS_TYPE_STRING, &addr_ptr,
|
||||
DBUS_TYPE_STRING, &hs_svc,
|
||||
DBUS_TYPE_INVALID);
|
||||
|
||||
hs->state = HEADSET_STATE_CONNECT_IN_PROGRESS;
|
||||
if (!dbus_connection_send_with_reply(device->conn, msg, &pending, -1)) {
|
||||
error("Sending GetRemoteServiceHandles failed");
|
||||
dbus_message_unref(msg);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
dbus_pending_call_set_notify(pending, get_handles_reply, device, NULL);
|
||||
dbus_pending_call_unref(pending);
|
||||
dbus_message_unref(msg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rfcomm_connect(struct device *device, struct pending_connect *c)
|
||||
{
|
||||
struct headset *hs = device->headset;
|
||||
struct sockaddr_rc addr;
|
||||
char address[18];
|
||||
int sk, err;
|
||||
|
||||
if (c != NULL) {
|
||||
hs->pending_connect = c;
|
||||
hs->type = hs->hfp_handle ? SVC_HANDSFREE : SVC_HEADSET;
|
||||
|
||||
if (hs->state != HEADSET_STATE_DISCONNECTED)
|
||||
return -EBUSY;
|
||||
|
||||
if (hs->rfcomm_ch < 0)
|
||||
return get_handles(device);
|
||||
}
|
||||
|
||||
ba2str(&device->dst, address);
|
||||
|
||||
debug("Connecting to %s channel %d", address, hs->rfcomm_ch);
|
||||
|
||||
sk = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
|
||||
if (sk < 0) {
|
||||
err = errno;
|
||||
error("socket: %s (%d)", strerror(err), err);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.rc_family = AF_BLUETOOTH;
|
||||
bacpy(&addr.rc_bdaddr, BDADDR_ANY);
|
||||
addr.rc_channel = 0;
|
||||
|
||||
if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
|
||||
err = errno;
|
||||
error("bind: %s (%d)", strerror(errno), errno);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (set_nonblocking(sk) < 0) {
|
||||
err = errno;
|
||||
goto failed;
|
||||
}
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.rc_family = AF_BLUETOOTH;
|
||||
bacpy(&addr.rc_bdaddr, &device->dst);
|
||||
addr.rc_channel = hs->rfcomm_ch;
|
||||
|
||||
hs->pending_connect->io = g_io_channel_unix_new(sk);
|
||||
if (!hs->pending_connect->io) {
|
||||
err = ENOMEM;
|
||||
error("channel_unix_new failed in rfcomm connect");
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
|
||||
if (!(errno == EAGAIN || errno == EINPROGRESS)) {
|
||||
err = errno;
|
||||
error("connect() failed: %s (%d)", strerror(err), err);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
debug("Connect in progress");
|
||||
|
||||
g_io_add_watch(hs->pending_connect->io, G_IO_OUT | G_IO_NVAL,
|
||||
(GIOFunc) rfcomm_connect_cb, device);
|
||||
} else {
|
||||
debug("Connect succeeded with first try");
|
||||
rfcomm_connect_cb(hs->pending_connect->io, G_IO_OUT, device);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
failed:
|
||||
if (!hs->pending_connect->io && sk >= 0)
|
||||
close(sk);
|
||||
|
||||
return -err;
|
||||
}
|
||||
|
||||
static DBusHandlerResult hs_stop(DBusConnection *conn, DBusMessage *msg,
|
||||
void *data)
|
||||
{
|
||||
@ -800,8 +941,6 @@ static DBusHandlerResult hs_is_playing(DBusConnection *conn, DBusMessage *msg,
|
||||
DBusMessage *reply;
|
||||
dbus_bool_t playing;
|
||||
|
||||
assert(hs);
|
||||
|
||||
reply = dbus_message_new_method_return(msg);
|
||||
if (!reply)
|
||||
return DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||
@ -815,7 +954,7 @@ static DBusHandlerResult hs_is_playing(DBusConnection *conn, DBusMessage *msg,
|
||||
DBUS_TYPE_INVALID);
|
||||
|
||||
send_message_and_unref(conn, reply);
|
||||
|
||||
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
|
||||
@ -827,8 +966,6 @@ static DBusHandlerResult hs_disconnect(DBusConnection *conn, DBusMessage *msg,
|
||||
DBusMessage *reply = NULL;
|
||||
char hs_address[18];
|
||||
|
||||
assert(hs);
|
||||
|
||||
if (msg) {
|
||||
reply = dbus_message_new_method_return(msg);
|
||||
if (!reply)
|
||||
@ -876,7 +1013,7 @@ static DBusHandlerResult hs_disconnect(DBusConnection *conn, DBusMessage *msg,
|
||||
|
||||
if (reply)
|
||||
send_message_and_unref(conn, reply);
|
||||
|
||||
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
|
||||
@ -898,194 +1035,43 @@ static DBusHandlerResult hs_is_connected(DBusConnection *conn,
|
||||
DBUS_TYPE_INVALID);
|
||||
|
||||
send_message_and_unref(conn, reply);
|
||||
|
||||
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
|
||||
static void get_handles_reply(DBusPendingCall *call, void *data)
|
||||
{
|
||||
DBusMessage *msg = NULL, *reply;
|
||||
DBusPendingCall *pending;
|
||||
DBusError derr;
|
||||
struct device *device = data;
|
||||
struct headset *hs = device->headset;
|
||||
struct pending_connect *c;
|
||||
char address[18], *addr_ptr = address;
|
||||
dbus_uint32_t *array = NULL;
|
||||
dbus_uint32_t handle;
|
||||
int array_len;
|
||||
|
||||
assert(hs != NULL);
|
||||
assert(hs->pending_connect);
|
||||
|
||||
c = hs->pending_connect;
|
||||
|
||||
reply = dbus_pending_call_steal_reply(call);
|
||||
|
||||
dbus_error_init(&derr);
|
||||
if (dbus_set_error_from_message(&derr, reply)) {
|
||||
error("GetRemoteServiceHandles failed: %s", derr.message);
|
||||
if (c->msg) {
|
||||
if (dbus_error_has_name(&derr,
|
||||
"org.bluez.Error.ConnectionAttemptFailed"))
|
||||
err_connect_failed(device->conn, c->msg,
|
||||
strerror(EHOSTDOWN));
|
||||
else
|
||||
err_not_supported(device->conn, c->msg);
|
||||
}
|
||||
dbus_error_free(&derr);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (!dbus_message_get_args(reply, NULL,
|
||||
DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32,
|
||||
&array, &array_len,
|
||||
DBUS_TYPE_INVALID)) {
|
||||
error("Unable to get args from reply");
|
||||
if (c->msg)
|
||||
err_not_supported(device->conn, c->msg);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (!array) {
|
||||
error("get_handles_reply: Unable to get handle array from reply");
|
||||
if (c->msg)
|
||||
err_not_supported(device->conn, c->msg);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (array_len < 1) {
|
||||
debug("No record handles found");
|
||||
if (c->msg)
|
||||
err_not_supported(device->conn, c->msg);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (array_len > 1)
|
||||
debug("Multiple records found. Using the first one.");
|
||||
|
||||
msg = dbus_message_new_method_call("org.bluez", device->adapter_path,
|
||||
"org.bluez.Adapter",
|
||||
"GetRemoteServiceRecord");
|
||||
if (!msg) {
|
||||
error("Unable to allocate new method call");
|
||||
if (c->msg)
|
||||
err_connect_failed(device->conn, c->msg, strerror(ENOMEM));
|
||||
goto failed;
|
||||
}
|
||||
|
||||
ba2str(&device->dst, address);
|
||||
|
||||
handle = array[0];
|
||||
|
||||
dbus_message_append_args(msg, DBUS_TYPE_STRING, &addr_ptr,
|
||||
DBUS_TYPE_UINT32, &handle,
|
||||
DBUS_TYPE_INVALID);
|
||||
|
||||
if (!dbus_connection_send_with_reply(device->conn, msg, &pending, -1)) {
|
||||
error("Sending GetRemoteServiceRecord failed");
|
||||
if (c->msg)
|
||||
err_connect_failed(device->conn, c->msg, strerror(EIO));
|
||||
goto failed;
|
||||
}
|
||||
|
||||
dbus_pending_call_set_notify(pending, get_record_reply, device, NULL);
|
||||
dbus_pending_call_unref(pending);
|
||||
dbus_message_unref(msg);
|
||||
dbus_message_unref(reply);
|
||||
|
||||
return;
|
||||
|
||||
failed:
|
||||
if (msg)
|
||||
dbus_message_unref(msg);
|
||||
dbus_message_unref(reply);
|
||||
hs_disconnect(NULL, NULL, device);
|
||||
}
|
||||
|
||||
static DBusHandlerResult hs_connect(DBusConnection *conn, DBusMessage *msg,
|
||||
void *data)
|
||||
{
|
||||
DBusPendingCall *pending;
|
||||
struct device *device = data;
|
||||
struct headset *hs = device->headset;
|
||||
const char *hs_svc;
|
||||
const char *addr_ptr;
|
||||
char hs_address[18];
|
||||
struct pending_connect *c;
|
||||
int err;
|
||||
|
||||
assert(hs != NULL);
|
||||
|
||||
if (hs->state > HEADSET_STATE_DISCONNECTED || hs->pending_connect)
|
||||
return err_already_connected(conn, msg);
|
||||
|
||||
hs->pending_connect = g_try_new0(struct pending_connect, 1);
|
||||
if (!hs->pending_connect) {
|
||||
c = g_try_new0(struct pending_connect, 1);
|
||||
if (!c) {
|
||||
error("Out of memory when allocating struct pending_connect");
|
||||
return DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||
}
|
||||
|
||||
hs->state = HEADSET_STATE_CONNECT_IN_PROGRESS;
|
||||
c->msg = dbus_message_ref(msg);
|
||||
|
||||
hs->pending_connect->msg = msg ? dbus_message_ref(msg) : NULL;
|
||||
if ((err = rfcomm_connect(device, c)) < 0)
|
||||
goto error;
|
||||
|
||||
hs->type = hs->hfp_handle ? SVC_HANDSFREE : SVC_HEADSET;
|
||||
|
||||
if (hs->rfcomm_ch > 0) {
|
||||
if (rfcomm_connect(device, &err) < 0) {
|
||||
error("Unable to connect");
|
||||
pending_connect_free(hs->pending_connect);
|
||||
hs->pending_connect = NULL;
|
||||
hs->state = HEADSET_STATE_DISCONNECTED;
|
||||
return err_connect_failed(conn, msg, strerror(err));
|
||||
} else
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
|
||||
msg = dbus_message_new_method_call("org.bluez", device->adapter_path,
|
||||
"org.bluez.Adapter",
|
||||
"GetRemoteServiceHandles");
|
||||
if (!msg) {
|
||||
error("Could not create a new dbus message");
|
||||
pending_connect_free(hs->pending_connect);
|
||||
hs->pending_connect = NULL;
|
||||
hs->state = HEADSET_STATE_DISCONNECTED;
|
||||
return DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||
}
|
||||
|
||||
if (hs->type == SVC_HEADSET)
|
||||
hs_svc = "hsp";
|
||||
else
|
||||
hs_svc = "hfp";
|
||||
|
||||
ba2str(&device->dst, hs_address);
|
||||
addr_ptr = hs_address;
|
||||
dbus_message_append_args(msg, DBUS_TYPE_STRING, &addr_ptr,
|
||||
DBUS_TYPE_STRING, &hs_svc,
|
||||
DBUS_TYPE_INVALID);
|
||||
|
||||
if (!dbus_connection_send_with_reply(conn, msg, &pending, -1)) {
|
||||
error("Sending GetRemoteServiceHandles failed");
|
||||
pending_connect_free(hs->pending_connect);
|
||||
hs->pending_connect = NULL;
|
||||
hs->state = HEADSET_STATE_DISCONNECTED;
|
||||
dbus_message_unref(msg);
|
||||
return err_connect_failed(conn, msg, strerror(EIO));
|
||||
}
|
||||
|
||||
dbus_pending_call_set_notify(pending, get_handles_reply, device, NULL);
|
||||
dbus_pending_call_unref(pending);
|
||||
dbus_message_unref(msg);
|
||||
|
||||
return DBUS_HANDLER_RESULT_HANDLED;;
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
error:
|
||||
if (c)
|
||||
pending_connect_free(c);
|
||||
return err_connect_failed(conn, msg, strerror(-err));
|
||||
}
|
||||
|
||||
static gboolean ring_timer_cb(gpointer data)
|
||||
{
|
||||
struct device *device = data;
|
||||
|
||||
assert(device != NULL);
|
||||
|
||||
if (headset_send(device->headset, "\r\nRING\r\n") != G_IO_ERROR_NONE)
|
||||
error("Sending RING failed");
|
||||
|
||||
@ -1099,8 +1085,6 @@ static DBusHandlerResult hs_ring(DBusConnection *conn, DBusMessage *msg,
|
||||
struct headset *hs = device->headset;
|
||||
DBusMessage *reply = NULL;
|
||||
|
||||
assert(hs != NULL);
|
||||
|
||||
if (hs->state < HEADSET_STATE_CONNECTED)
|
||||
return err_not_connected(conn, msg);
|
||||
|
||||
@ -1167,6 +1151,7 @@ static DBusHandlerResult hs_play(DBusConnection *conn, DBusMessage *msg,
|
||||
struct device *device = data;
|
||||
struct headset *hs = device->headset;
|
||||
struct pending_connect *c;
|
||||
int err;
|
||||
|
||||
if (hs->state < HEADSET_STATE_CONNECTED)
|
||||
return err_not_connected(conn, msg);
|
||||
@ -1183,13 +1168,11 @@ static DBusHandlerResult hs_play(DBusConnection *conn, DBusMessage *msg,
|
||||
|
||||
c->msg = msg ? dbus_message_ref(msg) : NULL;
|
||||
|
||||
if (sco_connect(device, c) < 0)
|
||||
goto failed;
|
||||
|
||||
return 0;
|
||||
failed:
|
||||
if (c)
|
||||
err = sco_connect(device, c);
|
||||
if (err < 0) {
|
||||
pending_connect_free(c);
|
||||
return err_failed(conn, msg, strerror(-err));
|
||||
}
|
||||
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
@ -1203,8 +1186,6 @@ static DBusHandlerResult hs_get_speaker_gain(DBusConnection *conn,
|
||||
DBusMessage *reply;
|
||||
dbus_uint16_t gain;
|
||||
|
||||
assert(hs);
|
||||
|
||||
if (hs->state < HEADSET_STATE_CONNECTED || hs->sp_gain < 0)
|
||||
return err_not_available(conn, msg);
|
||||
|
||||
@ -1218,7 +1199,7 @@ static DBusHandlerResult hs_get_speaker_gain(DBusConnection *conn,
|
||||
DBUS_TYPE_INVALID);
|
||||
|
||||
send_message_and_unref(conn, reply);
|
||||
|
||||
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
|
||||
@ -1231,8 +1212,6 @@ static DBusHandlerResult hs_get_mic_gain(DBusConnection *conn,
|
||||
DBusMessage *reply;
|
||||
dbus_uint16_t gain;
|
||||
|
||||
assert(hs);
|
||||
|
||||
if (hs->state < HEADSET_STATE_CONNECTED || hs->mic_gain < 0)
|
||||
return err_not_available(conn, msg);
|
||||
|
||||
@ -1246,7 +1225,7 @@ static DBusHandlerResult hs_get_mic_gain(DBusConnection *conn,
|
||||
DBUS_TYPE_INVALID);
|
||||
|
||||
send_message_and_unref(conn, reply);
|
||||
|
||||
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
|
||||
@ -1261,8 +1240,6 @@ static DBusHandlerResult hs_set_gain(DBusConnection *conn,
|
||||
dbus_uint16_t gain;
|
||||
char str[13];
|
||||
|
||||
assert(hs);
|
||||
|
||||
if (hs->state < HEADSET_STATE_CONNECTED)
|
||||
return err_not_connected(conn, msg);
|
||||
|
||||
@ -1313,7 +1290,7 @@ done:
|
||||
}
|
||||
|
||||
send_message_and_unref(conn, reply);
|
||||
|
||||
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
|
||||
@ -1419,12 +1396,12 @@ void headset_update(void *device, sdp_record_t *record, uint16_t svc)
|
||||
}
|
||||
|
||||
struct headset *headset_init(void *device, sdp_record_t *record,
|
||||
uint16_t svc)
|
||||
uint16_t svc, int channel)
|
||||
{
|
||||
struct headset *headset;
|
||||
|
||||
headset = g_new0(struct headset, 1);
|
||||
headset->rfcomm_ch = -1;
|
||||
headset->rfcomm_ch = channel;
|
||||
headset->sp_gain = -1;
|
||||
headset->mic_gain = -1;
|
||||
|
||||
@ -1446,19 +1423,17 @@ struct headset *headset_init(void *device, sdp_record_t *record,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
headset_set_channel(headset, record);
|
||||
register_iface:
|
||||
if (!dbus_connection_register_interface(((struct device *) device)->conn,
|
||||
((struct device *) device)->path,
|
||||
AUDIO_HEADSET_INTERFACE,
|
||||
headset_methods,
|
||||
headset_signals, NULL)) {
|
||||
((struct device *) device)->path,
|
||||
AUDIO_HEADSET_INTERFACE,
|
||||
headset_methods,
|
||||
headset_signals, NULL)) {
|
||||
g_free(headset);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (record)
|
||||
headset_set_channel(headset, record);
|
||||
|
||||
return headset;
|
||||
}
|
||||
|
||||
@ -1466,8 +1441,15 @@ void headset_free(void *device)
|
||||
{
|
||||
struct headset *headset = ((struct device *) device)->headset;
|
||||
|
||||
if (headset->state != HEADSET_STATE_DISCONNECTED)
|
||||
hs_disconnect(NULL, NULL, device);
|
||||
if (headset->sco) {
|
||||
g_io_channel_close(headset->sco);
|
||||
g_io_channel_unref(headset->sco);
|
||||
}
|
||||
|
||||
if (headset->rfcomm) {
|
||||
g_io_channel_close(headset->rfcomm);
|
||||
g_io_channel_unref(headset->rfcomm);
|
||||
}
|
||||
|
||||
g_free(headset);
|
||||
headset = NULL;
|
||||
@ -1480,7 +1462,17 @@ int headset_get_config(void *device, int sock, struct ipc_packet *pkt)
|
||||
int err = EINVAL;
|
||||
struct pending_connect *c;
|
||||
|
||||
if (headset->sco == NULL) {
|
||||
if (headset->rfcomm == NULL) {
|
||||
c = g_try_new0(struct pending_connect, 1);
|
||||
if (c == NULL)
|
||||
goto error;
|
||||
c->sock = sock;
|
||||
c->pkt = pkt;
|
||||
if ((err = rfcomm_connect(device, c)) < 0)
|
||||
goto error;
|
||||
return 0;
|
||||
}
|
||||
else if (headset->sco == NULL) {
|
||||
c = g_try_new0(struct pending_connect, 1);
|
||||
if (c == NULL)
|
||||
goto error;
|
||||
@ -1502,6 +1494,8 @@ int headset_get_config(void *device, int sock, struct ipc_packet *pkt)
|
||||
|
||||
return 0;
|
||||
error:
|
||||
if (c)
|
||||
pending_connect_free(c);
|
||||
cfg->fd = -1;
|
||||
return -err;
|
||||
}
|
||||
@ -1579,3 +1573,10 @@ headset_state_t headset_get_state(void *device)
|
||||
|
||||
return headset->state;
|
||||
}
|
||||
|
||||
int headset_get_channel(void *device)
|
||||
{
|
||||
struct headset *headset = ((struct device *) device)->headset;
|
||||
|
||||
return headset->rfcomm_ch;
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ typedef enum {
|
||||
struct headset;
|
||||
|
||||
struct headset *headset_init(void *device, sdp_record_t *record,
|
||||
uint16_t svc);
|
||||
uint16_t svc, int channel);
|
||||
|
||||
void headset_free(void *device);
|
||||
|
||||
@ -71,3 +71,5 @@ int headset_close_rfcomm(void *device);
|
||||
|
||||
headset_state_t headset_get_state(void *device);
|
||||
void headset_set_state(void *device, headset_state_t state);
|
||||
|
||||
int headset_get_channel(void *device);
|
||||
|
Loading…
Reference in New Issue
Block a user