From ec384afacde8614306abe32cf40c55b795783f0e Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 31 Aug 2007 11:51:43 +0000 Subject: Implement proper locking for headsets --- audio/headset.c | 18 +++++++++--------- audio/headset.h | 9 +++++++-- audio/pcm_bluetooth.c | 24 ++++++++++++++++++------ audio/unix.c | 37 +++++++++++++++++++++++++++++++++---- 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: -- cgit