~ubuntu-branches/ubuntu/wily/bluez/wily

« back to all changes in this revision

Viewing changes to audio/ctl_bluetooth.c

  • Committer: Bazaar Package Importer
  • Author(s): Mario Limonciello
  • Date: 2008-10-07 12:10:29 UTC
  • Revision ID: james.westby@ubuntu.com-20081007121029-4gup4fmmh2vfo5nh
Tags: upstream-4.12
ImportĀ upstreamĀ versionĀ 4.12

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 *
 
3
 *  BlueZ - Bluetooth protocol stack for Linux
 
4
 *
 
5
 *  Copyright (C) 2004-2008  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
 
 
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 *ctl_rsp)
 
145
{
 
146
        int ret;
 
147
        struct bt_control_req *ctl_req = (void *) ctl_rsp;
 
148
        const char *type;
 
149
 
 
150
        memset(ctl_req, 0, BT_AUDIO_IPC_PACKET_SIZE);
 
151
        ctl_req->h.msg_type = BT_CONTROL_REQ;
 
152
        ctl_req->mode = mode;
 
153
        ctl_req->key = key;
 
154
 
 
155
        ret = send(data->sock, ctl_req, BT_AUDIO_IPC_PACKET_SIZE, MSG_NOSIGNAL);
 
156
        if (ret <= 0) {
 
157
                SYSERR("Unable to request new volume value to server");
 
158
                return  -errno;
 
159
        }
 
160
 
 
161
        ret = recv(data->sock, ctl_rsp, BT_AUDIO_IPC_PACKET_SIZE, 0);
 
162
        if (ret <= 0) {
 
163
                SNDERR("Unable to receive new volume value from server");
 
164
                return  -errno;
 
165
        }
 
166
 
 
167
        type = bt_audio_strmsg(ctl_rsp->rsp_h.msg_h.msg_type);
 
168
        if (!type) {
 
169
                SNDERR("Bogus message type %d "
 
170
                                "received from audio service",
 
171
                                ctl_rsp->rsp_h.msg_h.msg_type);
 
172
                return -EINVAL;
 
173
        }
 
174
 
 
175
        if (ctl_rsp->rsp_h.msg_h.msg_type != BT_CONTROL_RSP) {
 
176
                SNDERR("Unexpected message %s received", type);
 
177
                return -EINVAL;
 
178
        }
 
179
 
 
180
        if (ctl_rsp->rsp_h.posix_errno != 0) {
 
181
                SNDERR("BT_CONTROL failed : %s (%d)",
 
182
                                        strerror(ctl_rsp->rsp_h.posix_errno),
 
183
                                        ctl_rsp->rsp_h.posix_errno);
 
184
                return -ctl_rsp->rsp_h.posix_errno;
 
185
        }
 
186
 
 
187
        return 0;
 
188
}
 
189
 
 
190
static int bluetooth_read_integer(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key,
 
191
                                                                long *value)
 
192
{
 
193
        struct bluetooth_data *data = ext->private_data;
 
194
        int ret;
 
195
        char buf[BT_AUDIO_IPC_PACKET_SIZE];
 
196
        struct bt_control_rsp *rsp = (void *) buf;
 
197
 
 
198
        DBG("ext %p key %ld", ext, key);
 
199
 
 
200
        memset(buf, 0, sizeof(buf));
 
201
        *value = 0;
 
202
 
 
203
        ret = bluetooth_send_ctl(data, key, 0, rsp);
 
204
        if (ret < 0)
 
205
                goto done;
 
206
 
 
207
        *value = rsp->key;
 
208
done:
 
209
        return ret;
 
210
}
 
211
 
 
212
static int bluetooth_write_integer(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key,
 
213
                                                                long *value)
 
214
{
 
215
        struct bluetooth_data *data = ext->private_data;
 
216
        char buf[BT_AUDIO_IPC_PACKET_SIZE];
 
217
        struct bt_control_rsp *rsp = (void *) buf;
 
218
        long current;
 
219
        int ret, keyvalue;
 
220
 
 
221
        DBG("ext %p key %ld", ext, key);
 
222
 
 
223
        ret = bluetooth_read_integer(ext, key, &current);
 
224
        if (ret < 0)
 
225
                return ret;
 
226
 
 
227
        if (*value == current)
 
228
                return 0;
 
229
 
 
230
        while (*value != current) {
 
231
                keyvalue = (*value > current) ? BT_CONTROL_KEY_VOL_UP :
 
232
                                BT_CONTROL_KEY_VOL_DOWN;
 
233
 
 
234
                ret = bluetooth_send_ctl(data, key, keyvalue, rsp);
 
235
                if (ret < 0)
 
236
                        break;
 
237
 
 
238
                current = keyvalue;
 
239
        }
 
240
 
 
241
        return ret;
 
242
}
 
243
 
 
244
static int bluetooth_read_event(snd_ctl_ext_t *ext, snd_ctl_elem_id_t *id,
 
245
                                                unsigned int *event_mask)
 
246
{
 
247
        struct bluetooth_data *data = ext->private_data;
 
248
        char buf[BT_AUDIO_IPC_PACKET_SIZE];
 
249
        struct bt_control_ind *ind = (void *) buf;
 
250
        int ret;
 
251
        const char *type;
 
252
 
 
253
        DBG("ext %p id %p", ext, id);
 
254
 
 
255
        memset(buf, 0, sizeof(buf));
 
256
 
 
257
        ret = recv(data->sock, ind, BT_AUDIO_IPC_PACKET_SIZE, MSG_DONTWAIT);
 
258
        type = bt_audio_strmsg(ind->h.msg_type);
 
259
        if (!type) {
 
260
                SNDERR("Bogus message type %d "
 
261
                                "received from audio service",
 
262
                                ind->h.msg_type);
 
263
                return -EAGAIN;
 
264
        }
 
265
 
 
266
        if (ind->h.msg_type != BT_CONTROL_IND) {
 
267
                SNDERR("Unexpected message %s received", type);
 
268
                return -EAGAIN;
 
269
        }
 
270
 
 
271
        snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER);
 
272
        snd_ctl_elem_id_set_name(id, ind->mode == BLUETOOTH_PLAYBACK ?
 
273
                                vol_devices[BLUETOOTH_PLAYBACK] :
 
274
                                vol_devices[BLUETOOTH_CAPTURE]);
 
275
        *event_mask = SND_CTL_EVENT_MASK_VALUE;
 
276
 
 
277
        return 1;
 
278
}
 
279
 
 
280
static snd_ctl_ext_callback_t bluetooth_callback = {
 
281
        .close                  = bluetooth_close,
 
282
        .elem_count             = bluetooth_elem_count,
 
283
        .elem_list              = bluetooth_elem_list,
 
284
        .find_elem              = bluetooth_find_elem,
 
285
        .get_attribute          = bluetooth_get_attribute,
 
286
        .get_integer_info       = bluetooth_get_integer_info,
 
287
        .read_integer           = bluetooth_read_integer,
 
288
        .write_integer          = bluetooth_write_integer,
 
289
        .read_event             = bluetooth_read_event,
 
290
};
 
291
 
 
292
static int bluetooth_init(struct bluetooth_data *data)
 
293
{
 
294
        int sk;
 
295
 
 
296
        if (!data)
 
297
                return -EINVAL;
 
298
 
 
299
        memset(data, 0, sizeof(struct bluetooth_data));
 
300
 
 
301
        data->sock = -1;
 
302
 
 
303
        sk = bt_audio_service_open();
 
304
        if (sk < 0)
 
305
                return -errno;
 
306
 
 
307
        data->sock = sk;
 
308
 
 
309
        return 0;
 
310
}
 
311
 
 
312
SND_CTL_PLUGIN_DEFINE_FUNC(bluetooth)
 
313
{
 
314
        struct bluetooth_data *data;
 
315
        int err;
 
316
 
 
317
        DBG("Bluetooth Control plugin");
 
318
 
 
319
        data = malloc(sizeof(struct bluetooth_data));
 
320
        if (!data) {
 
321
                err = -ENOMEM;
 
322
                goto error;
 
323
        }
 
324
 
 
325
        err = bluetooth_init(data);
 
326
        if (err < 0)
 
327
                goto error;
 
328
 
 
329
        data->ext.version = SND_CTL_EXT_VERSION;
 
330
        data->ext.card_idx = -1;
 
331
 
 
332
        strncpy(data->ext.id, "bluetooth", sizeof(data->ext.id) - 1);
 
333
        strncpy(data->ext.driver, "Bluetooth-Audio", sizeof(data->ext.driver) - 1);
 
334
        strncpy(data->ext.name, "Bluetooth Audio", sizeof(data->ext.name) - 1);
 
335
        strncpy(data->ext.longname, "Bluetooth Audio", sizeof(data->ext.longname) - 1);
 
336
        strncpy(data->ext.mixername, "Bluetooth Audio", sizeof(data->ext.mixername) - 1);
 
337
 
 
338
        data->ext.callback = &bluetooth_callback;
 
339
        data->ext.poll_fd = data->sock;
 
340
        data->ext.private_data = data;
 
341
 
 
342
        err = snd_ctl_ext_create(&data->ext, name, mode);
 
343
        if (err < 0)
 
344
                goto error;
 
345
 
 
346
        *handlep = data->ext.handle;
 
347
 
 
348
        return 0;
 
349
 
 
350
error:
 
351
        bluetooth_exit(data);
 
352
 
 
353
        return err;
 
354
}
 
355
 
 
356
SND_CTL_PLUGIN_SYMBOL(bluetooth);