• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *
3  *  BlueZ - Bluetooth protocol stack for Linux
4  *
5  *  Copyright (C) 2004-2009  Marcel Holtmann <marcel@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 
bluetooth_exit(struct bluetooth_data * data)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 
bluetooth_close(snd_ctl_ext_t * ext)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 
bluetooth_elem_count(snd_ctl_ext_t * ext)82 static int bluetooth_elem_count(snd_ctl_ext_t *ext)
83 {
84 	DBG("ext %p", ext);
85 
86 	return 2;
87 }
88 
bluetooth_elem_list(snd_ctl_ext_t * ext,unsigned int offset,snd_ctl_elem_id_t * id)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 
bluetooth_find_elem(snd_ctl_ext_t * ext,const snd_ctl_elem_id_t * id)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 
bluetooth_get_attribute(snd_ctl_ext_t * ext,snd_ctl_ext_key_t key,int * type,unsigned int * acc,unsigned int * count)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 
bluetooth_get_integer_info(snd_ctl_ext_t * ext,snd_ctl_ext_key_t key,long * imin,long * imax,long * istep)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 
bluetooth_send_ctl(struct bluetooth_data * data,uint8_t mode,uint8_t key,struct bt_control_rsp * rsp)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 
bluetooth_read_integer(snd_ctl_ext_t * ext,snd_ctl_ext_key_t key,long * value)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 
bluetooth_write_integer(snd_ctl_ext_t * ext,snd_ctl_ext_key_t key,long * value)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 
bluetooth_read_event(snd_ctl_ext_t * ext,snd_ctl_elem_id_t * id,unsigned int * event_mask)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 
bluetooth_init(struct bluetooth_data * data)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 
SND_CTL_PLUGIN_DEFINE_FUNC(bluetooth)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