| /* |
| * |
| * BlueZ - Bluetooth protocol stack for Linux |
| * |
| * Copyright (C) 2004-2007 Marcel Holtmann <marcel@holtmann.org> |
| * |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| * |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include <config.h> |
| #endif |
| |
| #include <sys/socket.h> |
| #include <sys/un.h> |
| |
| #include <alsa/asoundlib.h> |
| #include <alsa/pcm_external.h> |
| |
| #include "ipc.h" |
| |
| #ifndef UNIX_PATH_MAX |
| #define UNIX_PATH_MAX 108 |
| #endif |
| |
| #define DBG(fmt, arg...) printf("DEBUG: %s: " fmt "\n" , __FUNCTION__ , ## arg) |
| |
| #define SOCKET_NAME "/org/bluez/audio" |
| |
| struct bluetooth_data { |
| snd_pcm_ioplug_t io; |
| snd_pcm_sframes_t hw_ptr; |
| int sock; |
| }; |
| |
| static int bluetooth_start(snd_pcm_ioplug_t *io) |
| { |
| DBG("io %p", io); |
| |
| return 0; |
| } |
| |
| static int bluetooth_stop(snd_pcm_ioplug_t *io) |
| { |
| DBG("io %p", io); |
| |
| return 0; |
| } |
| |
| static snd_pcm_sframes_t bluetooth_pointer(snd_pcm_ioplug_t *io) |
| { |
| struct bluetooth_data *data = io->private_data; |
| |
| //DBG("io %p", io); |
| |
| //DBG("hw_ptr=%lu", data->hw_ptr); |
| |
| return data->hw_ptr; |
| } |
| |
| static int bluetooth_close(snd_pcm_ioplug_t *io) |
| { |
| struct bluetooth_data *data = io->private_data; |
| |
| DBG("io %p", io); |
| |
| free(data); |
| |
| return 0; |
| } |
| |
| static snd_pcm_ioplug_callback_t bluetooth_playback_callback = { |
| .start = bluetooth_start, |
| .stop = bluetooth_stop, |
| .pointer = bluetooth_pointer, |
| .close = bluetooth_close, |
| #if 0 |
| .hw_params = bluetooth_hw_params, |
| .prepare = bluetooth_prepare, |
| .transfer = bluetooth_write, |
| #endif |
| }; |
| |
| static snd_pcm_ioplug_callback_t bluetooth_capture_callback = { |
| .start = bluetooth_start, |
| .stop = bluetooth_stop, |
| .pointer = bluetooth_pointer, |
| .close = bluetooth_close, |
| #if 0 |
| .hw_params = bluetooth_hw_params, |
| .prepare = bluetooth_prepare, |
| .transfer = bluetooth_read, |
| #endif |
| }; |
| |
| #define ARRAY_NELEMS(a) (sizeof((a)) / sizeof((a)[0])) |
| |
| static int bluetooth_hw_constraint(snd_pcm_ioplug_t *io) |
| { |
| snd_pcm_access_t access_list[] = { |
| SND_PCM_ACCESS_RW_INTERLEAVED, |
| /* Mmap access is really useless fo this driver, but we |
| * support it because some pieces of software out there |
| * insist on using it */ |
| SND_PCM_ACCESS_MMAP_INTERLEAVED |
| }; |
| unsigned int format_list[] = { |
| SND_PCM_FORMAT_S16_LE |
| }; |
| int err; |
| |
| err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_ACCESS, |
| ARRAY_NELEMS(access_list), access_list); |
| if (err < 0) |
| return err; |
| |
| err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_FORMAT, |
| ARRAY_NELEMS(format_list), format_list); |
| if (err < 0) |
| return err; |
| |
| err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_CHANNELS, 1, 1); |
| if (err < 0) |
| return err; |
| |
| err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_RATE, 8000, 8000); |
| if (err < 0) |
| return err; |
| |
| err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIOD_BYTES, 48, 48); |
| if (err < 0) |
| return err; |
| |
| err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIODS, 2, 200); |
| if (err < 0) |
| return err; |
| |
| return 0; |
| } |
| |
| SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth) |
| { |
| snd_config_iterator_t iter, next; |
| struct bluetooth_data *data; |
| struct sockaddr_un addr; |
| unsigned int id; |
| int sk, err; |
| |
| DBG("Bluetooth PCM plugin (%s)", |
| stream == SND_PCM_STREAM_PLAYBACK ? "Playback" : "Capture"); |
| |
| snd_config_for_each(iter, next, conf) { |
| snd_config_t *n = snd_config_iterator_entry(iter); |
| const char *id; |
| |
| if (snd_config_get_id(n, &id) < 0) |
| continue; |
| |
| if (strcmp(id, "comment") == 0 || strcmp(id, "type") == 0) |
| continue; |
| |
| if (strcmp(id, "bdaddr") == 0) { |
| const char *str; |
| if (snd_config_get_string(n, &str) < 0) { |
| SNDERR("Invalid type for %s", id); |
| return -EINVAL; |
| } |
| printf("bdaddr %s\n", str); |
| continue; |
| } |
| |
| SNDERR("Unknown field %s", id); |
| |
| return -EINVAL; |
| } |
| |
| id = abs(getpid() * rand()); |
| |
| sk = socket(PF_LOCAL, SOCK_DGRAM, 0); |
| if (sk < 0) { |
| SNDERR("Can't open socket"); |
| return -errno; |
| } |
| |
| memset(&addr, 0, sizeof(addr)); |
| addr.sun_family = AF_UNIX; |
| snprintf(addr.sun_path + 1, UNIX_PATH_MAX - 2, "%s/%d", SOCKET_NAME, id); |
| |
| if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { |
| SNDERR("Can't bind socket"); |
| close(sk); |
| return -errno; |
| } |
| |
| memset(&addr, 0, sizeof(addr)); |
| addr.sun_family = AF_UNIX; |
| snprintf(addr.sun_path + 1, UNIX_PATH_MAX - 2, "%s", SOCKET_NAME); |
| |
| if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { |
| SNDERR("Can't connect socket"); |
| close(sk); |
| return -errno; |
| } |
| |
| data = malloc(sizeof(*data)); |
| if (!data) { |
| close(sk); |
| return -ENOMEM; |
| } |
| |
| memset(data, 0, sizeof(*data)); |
| |
| data->sock = sk; |
| |
| data->io.version = SND_PCM_IOPLUG_VERSION; |
| data->io.name = "Bluetooth Audio"; |
| data->io.mmap_rw = 0; /* No direct mmap communication */ |
| |
| data->io.callback = stream == SND_PCM_STREAM_PLAYBACK ? |
| &bluetooth_playback_callback : &bluetooth_capture_callback; |
| data->io.poll_fd = sk; |
| data->io.poll_events = POLLIN; |
| data->io.private_data = data; |
| |
| err = snd_pcm_ioplug_create(&data->io, name, stream, mode); |
| if (err < 0) |
| goto error; |
| |
| err = bluetooth_hw_constraint(&data->io); |
| if (err < 0) { |
| snd_pcm_ioplug_delete(&data->io); |
| goto error; |
| } |
| |
| *pcmp = data->io.pcm; |
| |
| return 0; |
| |
| error: |
| close(sk); |
| |
| free(data); |
| |
| return err; |
| } |
| |
| SND_PCM_PLUGIN_SYMBOL(bluetooth); |