diff options
| author | Johan Hedberg <johan.hedberg@nokia.com> | 2007-08-31 11:51:43 +0000 | 
|---|---|---|
| committer | Johan Hedberg <johan.hedberg@nokia.com> | 2007-08-31 11:51:43 +0000 | 
| commit | ec384afacde8614306abe32cf40c55b795783f0e (patch) | |
| tree | 64becc3deee3b735acddd15157fa428ccfbf2b22 | |
| parent | fc0d501d82773718d0f2d040f786136332c39813 (diff) | |
Implement proper locking for headsets
| -rw-r--r-- | audio/headset.c | 18 | ||||
| -rw-r--r-- | audio/headset.h | 9 | ||||
| -rw-r--r-- | audio/pcm_bluetooth.c | 24 | ||||
| -rw-r--r-- | 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: | 
