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, ¤t);
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