summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohan Hedberg <johan.hedberg@nokia.com>2006-11-24 15:41:08 +0000
committerJohan Hedberg <johan.hedberg@nokia.com>2006-11-24 15:41:08 +0000
commit6834de18b98fea0e7d370a296318115a659047af (patch)
treec534bbafc79bc64b486b98454b0baf48807629c9
parente8dc0cc060fa03ac83cb8fc04679c79607adc023 (diff)
More implementation. E.g. signals and Play and Stop methods
-rw-r--r--audio/headset.c343
1 files changed, 266 insertions, 77 deletions
diff --git a/audio/headset.c b/audio/headset.c
index 350f5973..4491fc2e 100644
--- a/audio/headset.c
+++ b/audio/headset.c
@@ -65,7 +65,7 @@ struct pending_connect {
GIOChannel *io;
};
-struct hs_connection {
+struct headset {
char address[18];
GIOChannel *rfcomm;
@@ -78,7 +78,7 @@ struct hs_connection {
int data_length;
};
-static gboolean connect_in_progress = FALSE;
+static struct pending_connect *connect_in_progress = NULL;
static uint8_t config_channel = 0;
@@ -92,7 +92,7 @@ static DBusConnection *connection = NULL;
static GMainLoop *main_loop = NULL;
-static struct hs_connection *connected_hs = NULL;
+static struct headset *hs = NULL;
static GIOChannel *server_sk = NULL;
@@ -101,6 +101,8 @@ static DBusHandlerResult hs_connect(DBusConnection *conn, DBusMessage *msg,
static DBusHandlerResult hs_disconnect(DBusConnection *conn, DBusMessage *msg);
static DBusHandlerResult hs_ring(DBusConnection *conn, DBusMessage *msg);
static DBusHandlerResult hs_cancel_ringing(DBusConnection *conn, DBusMessage *msg);
+static DBusHandlerResult hs_play(DBusConnection *conn, DBusMessage *msg);
+static DBusHandlerResult hs_stop(DBusConnection *conn, DBusMessage *msg);
static int set_nonblocking(int fd, int *err)
{
@@ -140,7 +142,7 @@ static void pending_connect_free(struct pending_connect *c, gboolean unref_io)
dbus_connection_unref(c->conn);
free(c);
- connect_in_progress = FALSE;
+ connect_in_progress = NULL;
}
static DBusHandlerResult error_reply(DBusConnection *conn, DBusMessage *msg,
@@ -198,14 +200,14 @@ static DBusHandlerResult err_failed(DBusConnection *conn, DBusMessage *msg)
static void send_gain_setting(const char *buf)
{
+ /* Not yet implemented */
}
-static void send_button_press(void)
+static void send_simple_signal(const char *name)
{
DBusMessage *signal;
- signal = dbus_message_new_signal(HEADSET_PATH, "org.bluez.Headset",
- "AnswerRequested");
+ signal = dbus_message_new_signal(HEADSET_PATH, "org.bluez.Headset", name);
if (!signal) {
error("Unable to allocate new AnswerRequested signal");
return;
@@ -228,14 +230,14 @@ static void parse_headset_event(const char *buf, char *rsp, int rsp_len)
buf += 2;
if (!strncmp(buf, "+CKPD", 5))
- send_button_press();
+ send_simple_signal("AnswerRequested");
else if (!strncmp(buf, "+VG", 3))
send_gain_setting(buf);
snprintf(rsp, rsp_len, "\r\nOK\r\n");
}
-static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, struct hs_connection *hs)
+static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, gpointer user_data)
{
int sk, ret;
unsigned char buf[BUF_SIZE];
@@ -252,75 +254,81 @@ static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, struct hs_conn
ret = read(sk, buf, sizeof(buf) - 1);
if (ret > 0) {
- int free_space = sizeof(connected_hs->buf) -
- connected_hs->data_start -
- connected_hs->data_length - 1;
+ int free_space;
char *cr;
+ free_space = sizeof(hs->buf) - hs->data_start
+ - hs->data_length - 1;
+
if (free_space < ret) {
+ /* Very likely that the HS is sending us garbage so
+ * just ignore the data and disconnect */
error("Too much data to fit incomming buffer");
goto failed;
}
- memcpy(&connected_hs->buf[connected_hs->data_start], buf, ret);
- connected_hs->data_length += ret;
+ memcpy(&hs->buf[hs->data_start], buf, ret);
+ hs->data_length += ret;
/* Make sure the data is null terminated so we can use string
* functions */
- connected_hs->buf[connected_hs->data_length] = '\0';
+ hs->buf[hs->data_length] = '\0';
- cr = strchr(&connected_hs->buf[connected_hs->data_start], '\r');
+ cr = strchr(&hs->buf[hs->data_start], '\r');
if (cr) {
char rsp[BUF_SIZE];
- int len, written = 0;
- off_t cmd_len = 1 + (off_t) cr -
- (off_t) &connected_hs->buf[connected_hs->data_start];
-
+ int len, written;
+ off_t cmd_len;
+
+ cmd_len = 1 + (off_t) cr - (off_t) &hs->buf[hs->data_start];
*cr = '\0';
memset(rsp, 0, sizeof(rsp));
- parse_headset_event(&connected_hs->buf[connected_hs->data_start],
+ parse_headset_event(&hs->buf[hs->data_start],
rsp, sizeof(rsp));
len = strlen(rsp);
+ written = 0;
while (written < len) {
int ret;
ret = write(sk, &rsp[written], len - written);
if (ret < 0) {
- error("write: %s (%d)", errno, strerror(errno));
+ error("write: %s (%d)",
+ strerror(errno), errno);
break;
}
written += ret;
}
- connected_hs->data_start += cmd_len;
- connected_hs->data_length -= cmd_len;
+ hs->data_start += cmd_len;
+ hs->data_length -= cmd_len;
- if (!connected_hs->data_length)
- connected_hs->data_start = 0;
+ if (!hs->data_length)
+ hs->data_start = 0;
}
}
- if (connected_hs->ring_timer) {
- g_timeout_remove(connected_hs->ring_timer);
- connected_hs->ring_timer = 0;
+ if (hs->ring_timer) {
+ g_timeout_remove(hs->ring_timer);
+ hs->ring_timer = 0;
}
return TRUE;
failed:
info("Disconnected from %s", hs->address);
+ send_simple_signal("Disconnected");
if (hs->sco)
g_io_channel_close(hs->sco);
g_io_channel_close(chan);
free(hs);
- connected_hs = NULL;
+ hs = NULL;
return FALSE;
}
@@ -351,40 +359,110 @@ static gboolean server_io_cb(GIOChannel *chan, GIOCondition cond, void *data)
return TRUE;
}
- if (connected_hs || connect_in_progress) {
+ if (hs || connect_in_progress) {
debug("Refusing new connection since one already exists");
close(cli_sk);
return TRUE;
}
- connected_hs = malloc(sizeof(struct hs_connection));
- if (!connected_hs) {
+ hs = malloc(sizeof(struct headset));
+ if (!hs) {
error("Allocating new hs connection struct failed!");
close(cli_sk);
return TRUE;
}
- memset(connected_hs, 0, sizeof(struct hs_connection));
+ memset(hs, 0, sizeof(struct headset));
- connected_hs->rfcomm = g_io_channel_unix_new(cli_sk);
- if (!connected_hs->rfcomm) {
+ hs->rfcomm = g_io_channel_unix_new(cli_sk);
+ if (!hs->rfcomm) {
error("Allocating new GIOChannel failed!");
close(cli_sk);
- free(connected_hs);
- connected_hs = NULL;
+ free(hs);
+ hs = NULL;
return TRUE;
}
- ba2str(&addr.rc_bdaddr, connected_hs->address);
+ ba2str(&addr.rc_bdaddr, hs->address);
+
+ debug("Accepted connection from %s", hs->address);
+
+ send_simple_signal("Connected");
+
+ g_io_add_watch(hs->rfcomm, G_IO_IN, (GIOFunc) rfcomm_io_cb,
+ hs);
+
+ return TRUE;
+}
+
+static gboolean sco_io_cb(GIOChannel *chan, GIOCondition cond, gpointer user_data)
+{
+ if (cond & G_IO_NVAL) {
+ g_io_channel_unref(chan);
+ return FALSE;
+ }
- debug("Accepted connection from %s", connected_hs->address);
+ if (cond & (G_IO_HUP | G_IO_ERR)) {
+ error("Audio connection got disconnected");
+ g_io_channel_close(chan);
+ hs->sco = NULL;
+ send_simple_signal("Stopped");
+ return FALSE;
+ }
- g_io_add_watch(connected_hs->rfcomm, G_IO_IN, (GIOFunc) rfcomm_io_cb,
- connected_hs);
+ debug("sco_io_cb: Unhandled IO condition");
return TRUE;
}
+static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond,
+ struct pending_connect *c)
+{
+ int ret, sk, err;
+ DBusMessage *reply;
+ socklen_t len;
+
+ if (cond & G_IO_NVAL) {
+ g_io_channel_unref(chan);
+ return FALSE;
+ }
+
+ sk = g_io_channel_unix_get_fd(chan);
+
+ len = sizeof(ret);
+ if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &ret, &len) < 0) {
+ err = errno;
+ error("getsockopt(SO_ERROR): %s (%d)", strerror(err), err);
+ goto failed;
+ }
+
+ if (ret != 0) {
+ err = ret;
+ error("connect(): %s (%d)", strerror(ret), ret);
+ goto failed;
+ }
+
+ hs->sco = chan;
+ g_io_add_watch(chan, 0, sco_io_cb, NULL);
+
+ reply = dbus_message_new_method_return(c->msg);
+ if (reply) {
+ dbus_connection_send(c->conn, reply, NULL);
+ dbus_message_unref(reply);
+ }
+
+ pending_connect_free(c, FALSE);
+
+ send_simple_signal("Playing");
+
+ return FALSE;
+
+failed:
+ err_connect_failed(c->conn, c->msg, err);
+ pending_connect_free(c, TRUE);
+
+ return FALSE;
+}
static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, struct pending_connect *c)
{
@@ -411,21 +489,23 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, struct pe
goto failed;
}
- connected_hs = malloc(sizeof(struct hs_connection));
- if (!connected_hs) {
+ hs = malloc(sizeof(struct headset));
+ if (!hs) {
err = ENOMEM;
error("Allocating new hs connection struct failed!");
goto failed;
}
- memset(connected_hs, 0, sizeof(struct hs_connection));
+ memset(hs, 0, sizeof(struct headset));
- ba2str(&c->bda, connected_hs->address);
- connected_hs->rfcomm = chan;
+ ba2str(&c->bda, hs->address);
+ hs->rfcomm = chan;
- debug("Connected to %s", connected_hs->address);
+ send_simple_signal("Connected");
- g_io_add_watch(chan, G_IO_IN, (GIOFunc) rfcomm_io_cb, connected_hs);
+ debug("Connected to %s", hs->address);
+
+ g_io_add_watch(chan, G_IO_IN, (GIOFunc) rfcomm_io_cb, hs);
if (c->msg) {
DBusMessage *reply;
@@ -773,13 +853,13 @@ static DBusHandlerResult stop_message(DBusConnection *conn,
dbus_message_unref(reply);
- if (connected_hs) {
- if (connected_hs->sco)
- g_io_channel_close(connected_hs->sco);
- if (connected_hs->rfcomm)
- g_io_channel_close(connected_hs->rfcomm);
- free(connected_hs);
- connected_hs = NULL;
+ if (hs) {
+ if (hs->sco)
+ g_io_channel_close(hs->sco);
+ if (hs->rfcomm)
+ g_io_channel_close(hs->rfcomm);
+ free(hs);
+ hs = NULL;
}
if (!config_channel && record_id) {
@@ -852,7 +932,11 @@ static DBusHandlerResult hs_message(DBusConnection *conn,
if (strcmp(member, "CancelRinging") == 0)
return hs_cancel_ringing(conn, msg);
- /* Handle Headset interface methods here */
+ if (strcmp(member, "Play") == 0)
+ return hs_play(conn, msg);
+
+ if (strcmp(member, "Stop") == 0)
+ return hs_stop(conn, msg);
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
@@ -1096,22 +1180,24 @@ static DBusHandlerResult hs_disconnect(DBusConnection *conn, DBusMessage *msg)
return DBUS_HANDLER_RESULT_HANDLED;
}
- if (!connected_hs || strcasecmp(address, connected_hs->address) != 0)
+ if (!hs || strcasecmp(address, hs->address) != 0)
return err_not_connected(conn, msg);
reply = dbus_message_new_method_return(msg);
if (!reply)
return DBUS_HANDLER_RESULT_NEED_MEMORY;
- if (connected_hs->sco)
- g_io_channel_close(connected_hs->sco);
- if (connected_hs->rfcomm)
- g_io_channel_close(connected_hs->rfcomm);
+ if (hs->sco)
+ g_io_channel_close(hs->sco);
+ if (hs->rfcomm)
+ g_io_channel_close(hs->rfcomm);
- info("Disconnected from %s", connected_hs->address);
+ info("Disconnected from %s", hs->address);
+
+ send_simple_signal("Disconnected");
- free(connected_hs);
- connected_hs = NULL;
+ free(hs);
+ hs = NULL;
dbus_connection_send(conn, reply, NULL);
@@ -1143,7 +1229,7 @@ static DBusHandlerResult hs_connect(DBusConnection *conn, DBusMessage *msg,
}
}
- if (connected_hs)
+ if (hs)
return err_already_connected(conn, msg);
c = malloc(sizeof(struct pending_connect));
@@ -1152,7 +1238,7 @@ static DBusHandlerResult hs_connect(DBusConnection *conn, DBusMessage *msg,
return DBUS_HANDLER_RESULT_NEED_MEMORY;
}
- connect_in_progress = TRUE;
+ connect_in_progress = c;
memset(c, 0, sizeof(struct pending_connect));
@@ -1192,7 +1278,7 @@ static int send_ring(GIOChannel *io)
const char *ring_str = "\r\nRING\r\n";
int sk, written, len;
- sk = g_io_channel_unix_get_fd(connected_hs->rfcomm);
+ sk = g_io_channel_unix_get_fd(hs->rfcomm);
len = strlen(ring_str);
written = 0;
@@ -1213,7 +1299,7 @@ static int send_ring(GIOChannel *io)
static gboolean ring_timer(gpointer user_data)
{
- if (send_ring(connected_hs->rfcomm) < 0)
+ if (send_ring(hs->rfcomm) < 0)
error("Sending RING failed");
return TRUE;
@@ -1223,24 +1309,24 @@ static DBusHandlerResult hs_ring(DBusConnection *conn, DBusMessage *msg)
{
DBusMessage *reply;
- if (!connected_hs)
+ if (!hs)
return err_not_connected(conn, msg);
reply = dbus_message_new_method_return(msg);
if (!reply)
return DBUS_HANDLER_RESULT_NEED_MEMORY;
- if (connected_hs->ring_timer) {
+ if (hs->ring_timer) {
debug("Got Ring method call while ringing already in progress");
goto done;
}
- if (send_ring(connected_hs->rfcomm) < 0) {
+ if (send_ring(hs->rfcomm) < 0) {
dbus_message_unref(reply);
return err_failed(conn, msg);
}
- connected_hs->ring_timer = g_timeout_add(RING_INTERVAL, ring_timer, NULL);
+ hs->ring_timer = g_timeout_add(RING_INTERVAL, ring_timer, NULL);
done:
dbus_connection_send(conn, reply, NULL);
@@ -1253,20 +1339,20 @@ static DBusHandlerResult hs_cancel_ringing(DBusConnection *conn, DBusMessage *ms
{
DBusMessage *reply;
- if (!connected_hs)
+ if (!hs)
return err_not_connected(conn, msg);
reply = dbus_message_new_method_return(msg);
if (!reply)
return DBUS_HANDLER_RESULT_NEED_MEMORY;
- if (!connected_hs->ring_timer) {
+ if (!hs->ring_timer) {
debug("Got CancelRinging method call but ringing is not in progress");
goto done;
}
- g_timeout_remove(connected_hs->ring_timer);
- connected_hs->ring_timer = 0;
+ g_timeout_remove(hs->ring_timer);
+ hs->ring_timer = 0;
done:
dbus_connection_send(conn, reply, NULL);
@@ -1275,6 +1361,109 @@ done:
return DBUS_HANDLER_RESULT_HANDLED;
}
+static DBusHandlerResult hs_play(DBusConnection *conn, DBusMessage *msg)
+{
+ struct sockaddr_sco addr;
+ struct pending_connect *c;
+ int sk, err;
+
+ if (!hs)
+ return err_not_connected(conn, msg);
+
+ if (hs->sco)
+ return err_already_connected(conn, msg);
+
+ c = malloc(sizeof(struct pending_connect));
+ if (!c)
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+ memset(c, 0, sizeof(struct pending_connect));
+
+ c->conn = dbus_connection_ref(conn);
+ c->msg = dbus_message_ref(msg);
+
+ sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
+ if (sk < 0) {
+ err = errno;
+ error("socket(BTPROTO_SCO): %s (%d)", strerror(err), err);
+ err_connect_failed(conn, msg, err);
+ goto failed;
+ }
+
+ c->io = g_io_channel_unix_new(sk);
+ if (!c->io) {
+ close(sk);
+ pending_connect_free(c, TRUE);
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+ }
+
+ g_io_channel_set_close_on_unref(c->io, TRUE);
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sco_family = AF_BLUETOOTH;
+ bacpy(&addr.sco_bdaddr, BDADDR_ANY);
+ if (bind(sk, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ err = errno;
+ error("socket(BTPROTO_SCO): %s (%d)", strerror(err), err);
+ err_connect_failed(conn, msg, err);
+ goto failed;
+ }
+
+ if (set_nonblocking(sk, &err) < 0) {
+ err_connect_failed(conn, msg, err);
+ goto failed;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sco_family = AF_BLUETOOTH;
+ str2ba(hs->address, &addr.sco_bdaddr);
+
+ if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ if (!(errno == EAGAIN || errno == EINPROGRESS)) {
+ err = errno;
+ error("connect: %s (%d)", strerror(errno), errno);
+ goto failed;
+ }
+
+ debug("Connect in progress");
+
+ g_io_add_watch(c->io, G_IO_OUT, (GIOFunc) sco_connect_cb, c);
+ } else {
+ debug("Connect succeeded with first try");
+ sco_connect_cb(c->io, G_IO_OUT, c);
+ }
+
+ return 0;
+
+failed:
+ if (c)
+ pending_connect_free(c, TRUE);
+ close(sk);
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusHandlerResult hs_stop(DBusConnection *conn, DBusMessage *msg)
+{
+ DBusMessage *reply;
+
+ if (!hs || !hs->sco)
+ return err_not_connected(conn, msg);
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+ g_io_channel_close(hs->sco);
+ hs->sco = NULL;
+
+ send_simple_signal("Stopped");
+
+ dbus_connection_send(conn, reply, NULL);
+ dbus_message_unref(reply);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
int main(int argc, char *argv[])
{
struct sigaction sa;