summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohan Hedberg <johan.hedberg@nokia.com>2007-08-31 11:51:43 +0000
committerJohan Hedberg <johan.hedberg@nokia.com>2007-08-31 11:51:43 +0000
commitec384afacde8614306abe32cf40c55b795783f0e (patch)
tree64becc3deee3b735acddd15157fa428ccfbf2b22
parentfc0d501d82773718d0f2d040f786136332c39813 (diff)
Implement proper locking for headsets
-rw-r--r--audio/headset.c18
-rw-r--r--audio/headset.h9
-rw-r--r--audio/pcm_bluetooth.c24
-rw-r--r--audio/unix.c37
4 files changed, 67 insertions, 21 deletions
diff --git a/audio/headset.c b/audio/headset.c
index 25a5ebc6..0f2732e3 100644
--- a/audio/headset.c
+++ b/audio/headset.c
@@ -102,7 +102,7 @@ struct headset {
int sp_gain;
int mic_gain;
- gboolean locked;
+ headset_lock_t lock;
};
static int rfcomm_connect(struct device *device, struct pending_connect *c);
@@ -1450,7 +1450,7 @@ unsigned int headset_request_stream(struct device *dev, headset_stream_cb_t cb,
if (hs->rfcomm && hs->sco) {
hs->pending = g_slist_append(hs->pending, c);
- g_idle_add((GSourceFunc) finalize_stream_setup, hs);
+ g_idle_add((GSourceFunc) finalize_stream_setup, dev);
return c->id;
}
@@ -1604,28 +1604,28 @@ gboolean headset_is_active(struct device *dev)
return FALSE;
}
-gboolean headset_lock(struct device *dev)
+gboolean headset_lock(struct device *dev, headset_lock_t lock)
{
struct headset *hs = dev->headset;
- if (hs->locked)
+ if (hs->lock & lock)
return FALSE;
- hs->locked = TRUE;
+ hs->lock |= lock;
return TRUE;
}
-gboolean headset_unlock(struct device *dev)
+gboolean headset_unlock(struct device *dev, headset_lock_t lock)
{
struct headset *hs = dev->headset;
- if (!hs->locked)
+ if (!(hs->lock & lock))
return FALSE;
- hs->locked = FALSE;
+ hs->lock &= ~lock;
- if (hs->state > HEADSET_STATE_DISCONNECTED)
+ if (!hs->lock && hs->state > HEADSET_STATE_DISCONNECTED)
headset_set_state(dev, HEADSET_STATE_DISCONNECTED);
return TRUE;
diff --git a/audio/headset.h b/audio/headset.h
index 6824e0ce..32bf701b 100644
--- a/audio/headset.h
+++ b/audio/headset.h
@@ -46,6 +46,11 @@ typedef enum {
SVC_HANDSFREE
} headset_type_t;
+typedef enum {
+ HEADSET_LOCK_READ = 1,
+ HEADSET_LOCK_WRITE = 1 << 1,
+} headset_lock_t;
+
typedef void (*headset_stream_cb_t) (struct device *dev, void *user_data);
struct headset *headset_init(struct device *dev, sdp_record_t *record,
@@ -74,7 +79,7 @@ int headset_get_sco_fd(struct device *dev);
gboolean headset_is_active(struct device *dev);
-gboolean headset_lock(struct device *dev);
-gboolean headset_unlock(struct device *dev);
+gboolean headset_lock(struct device *dev, headset_lock_t lock);
+gboolean headset_unlock(struct device *dev, headset_lock_t lock);
gboolean headset_suspend(struct device *dev, void *data);
gboolean headset_play(struct device *dev, void *data);
diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c
index b99f4ccd..5a3b2317 100644
--- a/audio/pcm_bluetooth.c
+++ b/audio/pcm_bluetooth.c
@@ -893,7 +893,8 @@ static int bluetooth_a2dp_init(struct bluetooth_data *data,
return 0;
}
-static int bluetooth_cfg_init(struct ipc_packet *pkt, snd_config_t *conf)
+static int bluetooth_cfg_init(struct ipc_packet *pkt, snd_pcm_stream_t stream,
+ snd_config_t *conf)
{
struct ipc_data_cfg *cfg = (void *) pkt->data;
struct ipc_codec_sbc *sbc = (void *) cfg->data;
@@ -901,6 +902,15 @@ static int bluetooth_cfg_init(struct ipc_packet *pkt, snd_config_t *conf)
const char *addr, *pref;
const char *mode, *allocation, *rate, *subbands, *blocks, *bitpool;
+ switch (stream) {
+ case SND_PCM_STREAM_PLAYBACK:
+ cfg->fd_opt = CFG_FD_OPT_WRITE;
+ break;
+ case SND_PCM_STREAM_CAPTURE:
+ cfg->fd_opt = CFG_FD_OPT_READ;
+ break;
+ }
+
snd_config_for_each(i, next, conf) {
snd_config_t *n = snd_config_iterator_entry(i);
const char *id;
@@ -1023,7 +1033,8 @@ static int bluetooth_cfg_init(struct ipc_packet *pkt, snd_config_t *conf)
return 0;
}
-static int bluetooth_cfg(struct bluetooth_data *data, snd_config_t *conf)
+static int bluetooth_cfg(struct bluetooth_data *data, snd_pcm_stream_t stream,
+ snd_config_t *conf)
{
int ret, total;
char buf[IPC_MTU];
@@ -1035,7 +1046,7 @@ static int bluetooth_cfg(struct bluetooth_data *data, snd_config_t *conf)
memset(buf, 0, sizeof(buf));
- ret = bluetooth_cfg_init(pkt, conf);
+ ret = bluetooth_cfg_init(pkt, stream, conf);
if (ret < 0)
return -ret;
@@ -1119,7 +1130,8 @@ done:
return 0;
}
-static int bluetooth_init(struct bluetooth_data *data, snd_config_t *conf)
+static int bluetooth_init(struct bluetooth_data *data, snd_pcm_stream_t stream,
+ snd_config_t *conf)
{
int sk, err;
struct sockaddr_un addr = {
@@ -1160,7 +1172,7 @@ static int bluetooth_init(struct bluetooth_data *data, snd_config_t *conf)
if (fcntl(data->pipefd[1], F_SETFL, O_NONBLOCK) < 0)
return -errno;
- return bluetooth_cfg(data, conf);
+ return bluetooth_cfg(data, stream, conf);
}
SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth)
@@ -1177,7 +1189,7 @@ SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth)
goto error;
}
- err = bluetooth_init(data, conf);
+ err = bluetooth_init(data, stream, conf);
if (err < 0)
goto error;
diff --git a/audio/unix.c b/audio/unix.c
index 3e8c7469..2cb0d804 100644
--- a/audio/unix.c
+++ b/audio/unix.c
@@ -64,6 +64,10 @@ struct a2dp_data {
struct a2dp_sep *sep;
};
+struct headset_data {
+ headset_lock_t lock;
+};
+
struct unix_client {
struct device *dev;
struct avdtp_service_capability *media_codec;
@@ -71,9 +75,10 @@ struct unix_client {
char *interface;
union {
struct a2dp_data a2dp;
- void *data;
+ struct headset_data hs;
} d;
int sock;
+ int fd_opt;
unsigned int req_id;
unsigned int cb_id;
gboolean (*cancel_stream) (struct device *dev, unsigned int id);
@@ -245,6 +250,7 @@ static void headset_setup_complete(struct device *dev, void *user_data)
{
struct unix_client *client = user_data;
struct ipc_data_cfg cfg;
+ struct headset_data *hs = &client->d.hs;
int fd;
client->req_id = 0;
@@ -255,11 +261,31 @@ static void headset_setup_complete(struct device *dev, void *user_data)
return;
}
- headset_lock(dev);
+ switch (client->fd_opt) {
+ case CFG_FD_OPT_READ:
+ hs->lock = HEADSET_LOCK_READ;
+ break;
+ case CFG_FD_OPT_WRITE:
+ hs->lock = HEADSET_LOCK_WRITE;
+ break;
+ case CFG_FD_OPT_READWRITE:
+ hs->lock = HEADSET_LOCK_READ | HEADSET_LOCK_WRITE;
+ break;
+ default:
+ hs->lock = 0;
+ break;
+ }
+
+ if (!headset_lock(dev, hs->lock)) {
+ error("Unable to lock headset");
+ unix_send_cfg(client->sock, NULL, -1);
+ client->dev = NULL;
+ return;
+ }
memset(&cfg, 0, sizeof(cfg));
- cfg.fd_opt = CFG_FD_OPT_READWRITE;
+ cfg.fd_opt = client->fd_opt;
cfg.codec = CFG_CODEC_SCO;
cfg.mode = CFG_MODE_MONO;
cfg.pkt_len = 48;
@@ -551,6 +577,8 @@ static void cfg_event(struct unix_client *client, struct ipc_packet *pkt, int le
str2ba(pkt->device, &bdaddr);
+ client->fd_opt = cfg->fd_opt;
+
if (client->interface) {
g_free(client->interface);
client->interface = NULL;
@@ -618,6 +646,7 @@ static gboolean client_cb(GIOChannel *chan, GIOCondition cond, gpointer data)
struct unix_client *client = data;
int len, len_check;
struct a2dp_data *a2dp = &client->d.a2dp;
+ struct headset_data *hs = &client->d.hs;
if (cond & G_IO_NVAL)
return FALSE;
@@ -627,7 +656,7 @@ static gboolean client_cb(GIOChannel *chan, GIOCondition cond, gpointer data)
switch (client->type) {
case TYPE_HEADSET:
if (client->dev)
- headset_unlock(client->dev);
+ headset_unlock(client->dev, hs->lock);
break;
case TYPE_SOURCE:
case TYPE_SINK: