Home | History | Annotate | Download | only in audio
      1 /*
      2  *
      3  *  BlueZ - Bluetooth protocol stack for Linux
      4  *
      5  *  Copyright (C) 2004-2010  Marcel Holtmann <marcel (at) holtmann.org>
      6  *
      7  *
      8  *  This library is free software; you can redistribute it and/or
      9  *  modify it under the terms of the GNU Lesser General Public
     10  *  License as published by the Free Software Foundation; either
     11  *  version 2.1 of the License, or (at your option) any later version.
     12  *
     13  *  This library is distributed in the hope that it will be useful,
     14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     16  *  Lesser General Public License for more details.
     17  *
     18  *  You should have received a copy of the GNU Lesser General Public
     19  *  License along with this library; if not, write to the Free Software
     20  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     21  *
     22  */
     23 
     24 #ifdef HAVE_CONFIG_H
     25 #include <config.h>
     26 #endif
     27 
     28 #include <sys/socket.h>
     29 #include <sys/un.h>
     30 
     31 #include <alsa/asoundlib.h>
     32 #include <alsa/control_external.h>
     33 
     34 #include <bluetooth/bluetooth.h>
     35 
     36 #include "ipc.h"
     37 
     38 #ifdef ENABLE_DEBUG
     39 #define DBG(fmt, arg...)  printf("DEBUG: %s: " fmt "\n" , __FUNCTION__ , ## arg)
     40 #else
     41 #define DBG(fmt, arg...)
     42 #endif
     43 
     44 #define BLUETOOTH_MINVOL 0
     45 #define BLUETOOTH_MAXVOL 15
     46 
     47 struct bluetooth_data {
     48 	snd_ctl_ext_t ext;
     49 	int sock;
     50 };
     51 
     52 enum {
     53 	BLUETOOTH_PLAYBACK,
     54 	BLUETOOTH_CAPTURE,
     55 };
     56 
     57 static const char *vol_devices[2] = {
     58 	[BLUETOOTH_PLAYBACK]	= "Playback volume",
     59 	[BLUETOOTH_CAPTURE]	= "Capture volume",
     60 };
     61 
     62 static void bluetooth_exit(struct bluetooth_data *data)
     63 {
     64 	if (data == NULL)
     65 		return;
     66 
     67 	if (data->sock >= 0)
     68 		bt_audio_service_close(data->sock);
     69 
     70 	free(data);
     71 }
     72 
     73 static void bluetooth_close(snd_ctl_ext_t *ext)
     74 {
     75 	struct bluetooth_data *data = ext->private_data;
     76 
     77 	DBG("ext %p", ext);
     78 
     79 	bluetooth_exit(data);
     80 }
     81 
     82 static int bluetooth_elem_count(snd_ctl_ext_t *ext)
     83 {
     84 	DBG("ext %p", ext);
     85 
     86 	return 2;
     87 }
     88 
     89 static int bluetooth_elem_list(snd_ctl_ext_t *ext,
     90 				unsigned int offset, snd_ctl_elem_id_t *id)
     91 {
     92 	DBG("ext %p offset %d id %p", ext, offset, id);
     93 
     94 	snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER);
     95 
     96 	if (offset > 1)
     97 		return -EINVAL;
     98 
     99 	snd_ctl_elem_id_set_name(id, vol_devices[offset]);
    100 
    101 	return 0;
    102 }
    103 
    104 static snd_ctl_ext_key_t bluetooth_find_elem(snd_ctl_ext_t *ext,
    105 						const snd_ctl_elem_id_t *id)
    106 {
    107 	const char *name = snd_ctl_elem_id_get_name(id);
    108 	int i;
    109 
    110 	DBG("ext %p id %p name '%s'", ext, id, name);
    111 
    112 	for (i = 0; i < 2; i++)
    113 		if (strcmp(name, vol_devices[i]) == 0)
    114 			return i;
    115 
    116 	return SND_CTL_EXT_KEY_NOT_FOUND;
    117 }
    118 
    119 static int bluetooth_get_attribute(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key,
    120 			int *type, unsigned int *acc, unsigned int *count)
    121 {
    122 	DBG("ext %p key %ld", ext, key);
    123 
    124 	*type = SND_CTL_ELEM_TYPE_INTEGER;
    125 	*acc = SND_CTL_EXT_ACCESS_READWRITE;
    126 	*count = 1;
    127 
    128 	return 0;
    129 }
    130 
    131 static int bluetooth_get_integer_info(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key,
    132 					long *imin, long *imax, long *istep)
    133 {
    134 	DBG("ext %p key %ld", ext, key);
    135 
    136 	*istep = 1;
    137 	*imin  = BLUETOOTH_MINVOL;
    138 	*imax  = BLUETOOTH_MAXVOL;
    139 
    140 	return 0;
    141 }
    142 
    143 static int bluetooth_send_ctl(struct bluetooth_data *data,
    144 			uint8_t mode, uint8_t key, struct bt_control_rsp *rsp)
    145 {
    146 	int ret;
    147 	struct bt_control_req *req = (void *) rsp;
    148 	bt_audio_error_t *err = (void *) rsp;
    149 	const char *type, *name;
    150 
    151 	memset(req, 0, BT_SUGGESTED_BUFFER_SIZE);
    152 	req->h.type = BT_REQUEST;
    153 	req->h.name = BT_CONTROL;
    154 	req->h.length = sizeof(*req);
    155 
    156 	req->mode = mode;
    157 	req->key = key;
    158 
    159 	ret = send(data->sock, req, BT_SUGGESTED_BUFFER_SIZE, MSG_NOSIGNAL);
    160 	if (ret <= 0) {
    161 		SYSERR("Unable to request new volume value to server");
    162 		return  -errno;
    163 	}
    164 
    165 	ret = recv(data->sock, rsp, BT_SUGGESTED_BUFFER_SIZE, 0);
    166 	if (ret <= 0) {
    167 		SNDERR("Unable to receive new volume value from server");
    168 		return  -errno;
    169 	}
    170 
    171 	if (rsp->h.type == BT_ERROR) {
    172 		SNDERR("BT_CONTROL failed : %s (%d)",
    173 					strerror(err->posix_errno),
    174 					err->posix_errno);
    175 		return -err->posix_errno;
    176 	}
    177 
    178 	type = bt_audio_strtype(rsp->h.type);
    179 	if (!type) {
    180 		SNDERR("Bogus message type %d "
    181 				"received from audio service",
    182 				rsp->h.type);
    183 		return -EINVAL;
    184 	}
    185 
    186 	name = bt_audio_strname(rsp->h.name);
    187 	if (!name) {
    188 		SNDERR("Bogus message name %d "
    189 				"received from audio service",
    190 				rsp->h.name);
    191 		return -EINVAL;
    192 	}
    193 
    194 	if (rsp->h.name != BT_CONTROL) {
    195 		SNDERR("Unexpected message %s received", type);
    196 		return -EINVAL;
    197 	}
    198 
    199 	return 0;
    200 }
    201 
    202 static int bluetooth_read_integer(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key,
    203 								long *value)
    204 {
    205 	struct bluetooth_data *data = ext->private_data;
    206 	int ret;
    207 	char buf[BT_SUGGESTED_BUFFER_SIZE];
    208 	struct bt_control_rsp *rsp = (void *) buf;
    209 
    210 	DBG("ext %p key %ld", ext, key);
    211 
    212 	memset(buf, 0, sizeof(buf));
    213 	*value = 0;
    214 
    215 	ret = bluetooth_send_ctl(data, key, 0, rsp);
    216 	if (ret < 0)
    217 		goto done;
    218 
    219 	*value = rsp->key;
    220 done:
    221 	return ret;
    222 }
    223 
    224 static int bluetooth_write_integer(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key,
    225 								long *value)
    226 {
    227 	struct bluetooth_data *data = ext->private_data;
    228 	char buf[BT_SUGGESTED_BUFFER_SIZE];
    229 	struct bt_control_rsp *rsp = (void *) buf;
    230 	long current;
    231 	int ret, keyvalue;
    232 
    233 	DBG("ext %p key %ld", ext, key);
    234 
    235 	ret = bluetooth_read_integer(ext, key, &current);
    236 	if (ret < 0)
    237 		return ret;
    238 
    239 	if (*value == current)
    240 		return 0;
    241 
    242 	while (*value != current) {
    243 		keyvalue = (*value > current) ? BT_CONTROL_KEY_VOL_UP :
    244 				BT_CONTROL_KEY_VOL_DOWN;
    245 
    246 		ret = bluetooth_send_ctl(data, key, keyvalue, rsp);
    247 		if (ret < 0)
    248 			break;
    249 
    250 		current = keyvalue;
    251 	}
    252 
    253 	return ret;
    254 }
    255 
    256 static int bluetooth_read_event(snd_ctl_ext_t *ext, snd_ctl_elem_id_t *id,
    257 						unsigned int *event_mask)
    258 {
    259 	struct bluetooth_data *data = ext->private_data;
    260 	char buf[BT_SUGGESTED_BUFFER_SIZE];
    261 	struct bt_control_ind *ind = (void *) buf;
    262 	int ret;
    263 	const char *type, *name;
    264 
    265 	DBG("ext %p id %p", ext, id);
    266 
    267 	memset(buf, 0, sizeof(buf));
    268 
    269 	ret = recv(data->sock, ind, BT_SUGGESTED_BUFFER_SIZE, MSG_DONTWAIT);
    270 	if (ret < 0) {
    271 		SNDERR("Failed while receiving data: %s (%d)",
    272 				strerror(errno), errno);
    273 		return -errno;
    274 	}
    275 
    276 	type = bt_audio_strtype(ind->h.type);
    277 	if (!type) {
    278 		SNDERR("Bogus message type %d "
    279 				"received from audio service",
    280 				ind->h.type);
    281 		return -EAGAIN;
    282 	}
    283 
    284 	name = bt_audio_strname(ind->h.name);
    285 	if (!name) {
    286 		SNDERR("Bogus message name %d "
    287 				"received from audio service",
    288 				ind->h.name);
    289 		return -EAGAIN;
    290 	}
    291 
    292 	if (ind->h.name != BT_CONTROL) {
    293 		SNDERR("Unexpected message %s received", name);
    294 		return -EAGAIN;
    295 	}
    296 
    297 	snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER);
    298 	snd_ctl_elem_id_set_name(id, ind->mode == BLUETOOTH_PLAYBACK ?
    299 				vol_devices[BLUETOOTH_PLAYBACK] :
    300 				vol_devices[BLUETOOTH_CAPTURE]);
    301 	*event_mask = SND_CTL_EVENT_MASK_VALUE;
    302 
    303 	return 1;
    304 }
    305 
    306 static snd_ctl_ext_callback_t bluetooth_callback = {
    307 	.close			= bluetooth_close,
    308 	.elem_count		= bluetooth_elem_count,
    309 	.elem_list		= bluetooth_elem_list,
    310 	.find_elem		= bluetooth_find_elem,
    311 	.get_attribute		= bluetooth_get_attribute,
    312 	.get_integer_info	= bluetooth_get_integer_info,
    313 	.read_integer		= bluetooth_read_integer,
    314 	.write_integer		= bluetooth_write_integer,
    315 	.read_event		= bluetooth_read_event,
    316 };
    317 
    318 static int bluetooth_init(struct bluetooth_data *data)
    319 {
    320 	int sk;
    321 
    322 	if (!data)
    323 		return -EINVAL;
    324 
    325 	memset(data, 0, sizeof(struct bluetooth_data));
    326 
    327 	data->sock = -1;
    328 
    329 	sk = bt_audio_service_open();
    330 	if (sk < 0)
    331 		return -errno;
    332 
    333 	data->sock = sk;
    334 
    335 	return 0;
    336 }
    337 
    338 SND_CTL_PLUGIN_DEFINE_FUNC(bluetooth);
    339 
    340 SND_CTL_PLUGIN_DEFINE_FUNC(bluetooth)
    341 {
    342 	struct bluetooth_data *data;
    343 	int err;
    344 
    345 	DBG("Bluetooth Control plugin");
    346 
    347 	data = malloc(sizeof(struct bluetooth_data));
    348 	if (!data) {
    349 		err = -ENOMEM;
    350 		goto error;
    351 	}
    352 
    353 	err = bluetooth_init(data);
    354 	if (err < 0)
    355 		goto error;
    356 
    357 	data->ext.version = SND_CTL_EXT_VERSION;
    358 	data->ext.card_idx = -1;
    359 
    360 	strncpy(data->ext.id, "bluetooth", sizeof(data->ext.id) - 1);
    361 	strncpy(data->ext.driver, "Bluetooth-Audio", sizeof(data->ext.driver) - 1);
    362 	strncpy(data->ext.name, "Bluetooth Audio", sizeof(data->ext.name) - 1);
    363 	strncpy(data->ext.longname, "Bluetooth Audio", sizeof(data->ext.longname) - 1);
    364 	strncpy(data->ext.mixername, "Bluetooth Audio", sizeof(data->ext.mixername) - 1);
    365 
    366 	data->ext.callback = &bluetooth_callback;
    367 	data->ext.poll_fd = data->sock;
    368 	data->ext.private_data = data;
    369 
    370 	err = snd_ctl_ext_create(&data->ext, name, mode);
    371 	if (err < 0)
    372 		goto error;
    373 
    374 	*handlep = data->ext.handle;
    375 
    376 	return 0;
    377 
    378 error:
    379 	bluetooth_exit(data);
    380 
    381 	return err;
    382 }
    383 
    384 SND_CTL_PLUGIN_SYMBOL(bluetooth);
    385