From: Marcel Holtmann <marcel@holtmann.org>
To: BlueZ Mailing List <bluez-devel@lists.sourceforge.net>
Subject: Re: [Bluez-devel] starting btsco-in-libalsa
Date: Thu, 09 Dec 2004 11:57:15 +0100 [thread overview]
Message-ID: <1102589835.9988.114.camel@pegasus> (raw)
In-Reply-To: <41B81ACB.1080807@xmission.com>
[-- Attachment #1: Type: text/plain, Size: 2059 bytes --]
Hi Brad,
> I put some junk in the btsco cvs:
>
> alsa-lib.patch
> bt/
>
> if you apply alsa-lib.patch and put bt inside alsa-lib/src/pcm then the
> idea is that you can build a userspace alsa driver.
>
> currently it doesn't even compile... it's just based on the jack plugin
> and not yet reworked for bluetooth.
nice start. Actually I tried this by myself some time ago, but haven't
got any further than a skeleton. The main problem I see at the moment is
that we have to develop this inside the ALSA lib source and that is bad.
The reason for this is "pcm_local.h" and its definitions that are not
installed system wide. I don't think that SND_PCM_TYPE_BLUETOOTH is
needed, but I can be wrong of course. The other thing is that this stuff
should belong to the pcm/ext/ directory. My skeleton is attached and I
used these extra lines in ~/.asoundrc
pcm.aiptek {
type a2dp
bdaddr "00:0B:xx:xx:xx:xx"
}
Assuming that /usr/lib/alsa-lib/libasound_module_pcm_a2dp.so exists this
works as it should. So we have PCM at the input side of this plugin and
we send SBC over AVDTP to our headphone.
> anyway, this is my next project. it depends on a couple of things in
> other parts of the project:
Actually I think this is the way we should go. Using a2play is great,
but this will open it up to any tool that can play music using the ALSA
library.
> it would be nice if 'make install' inside sbc/ would install the sbc
> includes and library (static libs would be ok)
This is the whole goal, but for now I think we should copy the source
and simply link it. My plan is to combine sbclib.c and sbc.c and include
sbc_internal.h into sbc.c then. Also sbc_tables.h is not really useful
as a separate include.
> the avdtp library is just a skeleton right now... should I just ignore
> it for now and hardcode what is currently in a2play?
It is more than a skeleton, but I haven't committed the other code and I
don't think that I will have much time to work on it. Ignore it for now,
because we can catch up with that later.
Regards
Marcel
[-- Attachment #2: pcm_a2dp.c --]
[-- Type: text/x-csrc, Size: 10261 bytes --]
/*
*
* Bluetooth Advanced Audio Distribution Profile (A2DP) plugin
*
* Copyright (C) 2004 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <sys/types.h>
#include <sys/socket.h>
#include <bluetooth/bluetooth.h>
#include "sbc.h"
#include "pcm_local.h"
#define DBG(fmt, arg...) printf("%s: " fmt "\n" , __FUNCTION__ , ## arg)
#ifndef PIC
/* entry for static linking */
const char *_snd_module_pcm_a2dp = "";
#endif
static int snd_pcm_a2dp_close(snd_pcm_t *pcm)
{
DBG("pcm %p", pcm);
return 0;
}
static int snd_pcm_a2dp_nonblock(snd_pcm_t *pcm, int nonblock)
{
DBG("pcm %p nonblock %d", pcm, nonblock);
return 0;
}
static int snd_pcm_a2dp_async(snd_pcm_t *pcm, int sig, pid_t pid)
{
DBG("pcm %p sig %d pid %d", pcm, sig, pid);
return -ENOSYS;
}
static int snd_pcm_a2dp_poll_revents(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents)
{
DBG("pcm %p pfds %p nfds %d revents %p", pcm, pfds, nfds, revents);
return 0;
}
static int snd_pcm_a2dp_info(snd_pcm_t *pcm, snd_pcm_info_t *info)
{
DBG("pcm %p info %p", pcm, info);
memset(info, 0, sizeof(*info));
info->stream = pcm->stream;
info->card = -1;
strncpy(info->id, pcm->name, sizeof(info->id));
strncpy(info->name, pcm->name, sizeof(info->name));
strncpy(info->subname, pcm->name, sizeof(info->subname));
info->subdevices_count = 1;
return 0;
}
static inline snd_mask_t *hw_param_mask(snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var)
{
return ¶ms->masks[var - SND_PCM_HW_PARAM_FIRST_MASK];
}
static inline snd_interval_t *hw_param_interval(snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var)
{
return ¶ms->intervals[var - SND_PCM_HW_PARAM_FIRST_INTERVAL];
}
static int snd_pcm_a2dp_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
{
static snd_mask_t access = { .bits = {
(1<<SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) |
(1<<SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED) |
(1<<SNDRV_PCM_ACCESS_RW_INTERLEAVED) |
(1<<SNDRV_PCM_ACCESS_RW_NONINTERLEAVED),
0, 0, 0 } };
static snd_pcm_format_mask_t format_mask = { { 1U<<SNDRV_PCM_FORMAT_S16_BE } };
snd_interval_t t;
int err;
DBG("pcm %p params %p", pcm, params);
err = snd_mask_refine(hw_param_mask(params, SND_PCM_HW_PARAM_ACCESS), &access);
if (err < 0)
return err;
err = snd_mask_refine(hw_param_mask(params, SND_PCM_HW_PARAM_FORMAT), &format_mask);
if (err < 0)
return err;
err = snd_interval_refine_set(hw_param_interval(params, SND_PCM_HW_PARAM_CHANNELS), 2);
if (err < 0)
return err;
err = snd_interval_refine_set(hw_param_interval(params, SND_PCM_HW_PARAM_RATE), 44100);
if (err < 0)
return err;
snd_interval_set_minmax(&t, 128, 1024*1024);
err = snd_interval_refine(hw_param_interval(params, SND_PCM_HW_PARAM_BUFFER_SIZE), &t);
if (err < 0)
return err;
snd_interval_set_minmax(&t, 64, 1024*1024);
err = snd_interval_refine(hw_param_interval(params, SND_PCM_HW_PARAM_PERIOD_SIZE), &t);
if (err < 0)
return err;
snd_interval_set_minmax(&t, 2, 64);
err = snd_interval_refine(hw_param_interval(params, SND_PCM_HW_PARAM_PERIODS), &t);
if (err < 0)
return err;
return 0;
}
static int snd_pcm_a2dp_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
{
DBG("pcm %p params %p", pcm, params);
return 0;
}
static int snd_pcm_a2dp_hw_free(snd_pcm_t *pcm)
{
DBG("pcm %p", pcm);
return 0;
}
static int snd_pcm_a2dp_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t *params)
{
DBG("pcm %p params %p", pcm, params);
return 0;
}
static int snd_pcm_a2dp_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t *info)
{
DBG("pcm %p info %p", pcm, info);
return snd_pcm_channel_info_shm(pcm, info, -1);
}
static void snd_pcm_a2dp_dump(snd_pcm_t *pcm, snd_output_t *out)
{
DBG("pcm %p out %p", pcm, out);
snd_output_printf(out, "Bluetooth A2DP PCM\n");
if (pcm->setup) {
snd_output_printf(out, "Its setup is:\n");
snd_pcm_dump_setup(pcm, out);
}
}
static int snd_pcm_a2dp_mmap(snd_pcm_t *pcm)
{
DBG("pcm %p", pcm);
return 0;
}
static int snd_pcm_a2dp_munmap(snd_pcm_t *pcm)
{
DBG("pcm %p", pcm);
return 0;
}
static snd_pcm_ops_t snd_pcm_a2dp_ops = {
.close = snd_pcm_a2dp_close,
.info = snd_pcm_a2dp_info,
.nonblock = snd_pcm_a2dp_nonblock,
.async = snd_pcm_a2dp_async,
.poll_revents = snd_pcm_a2dp_poll_revents,
.hw_refine = snd_pcm_a2dp_hw_refine,
.hw_params = snd_pcm_a2dp_hw_params,
.hw_free = snd_pcm_a2dp_hw_free,
.sw_params = snd_pcm_a2dp_sw_params,
.channel_info = snd_pcm_a2dp_channel_info,
.dump = snd_pcm_a2dp_dump,
.mmap = snd_pcm_a2dp_mmap,
.munmap = snd_pcm_a2dp_munmap,
};
static int snd_pcm_a2dp_status(snd_pcm_t *pcm, snd_pcm_status_t *status)
{
DBG("pcm %p status %p", pcm, status);
memset(status, 0, sizeof(*status));
status->avail = pcm->buffer_size;
return 0;
}
static int snd_pcm_a2dp_prepare(snd_pcm_t *pcm)
{
DBG("pcm %p", pcm);
return 0;
}
static int snd_pcm_a2dp_reset(snd_pcm_t *pcm)
{
DBG("pcm %p", pcm);
return 0;
}
static int snd_pcm_a2dp_start(snd_pcm_t *pcm)
{
DBG("pcm %p", pcm);
return 0;
}
static int snd_pcm_a2dp_drop(snd_pcm_t *pcm)
{
DBG("pcm %p", pcm);
return 0;
}
static int snd_pcm_a2dp_drain(snd_pcm_t *pcm)
{
DBG("pcm %p", pcm);
return 0;
}
static int snd_pcm_a2dp_pause(snd_pcm_t *pcm, int enable)
{
DBG("pcm %p enable %d", pcm, enable);
return 0;
}
static snd_pcm_state_t snd_pcm_a2dp_state(snd_pcm_t *pcm)
{
DBG("pcm %p", pcm);
return SND_PCM_STATE_RUNNING;
}
static int snd_pcm_a2dp_hwsync(snd_pcm_t *pcm)
{
DBG("pcm %p", pcm);
return 0;
}
static int snd_pcm_a2dp_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delay)
{
DBG("pcm %p delay %p", pcm, delay);
return 0;
}
static int snd_pcm_a2dp_resume(snd_pcm_t *pcm)
{
DBG("pcm %p", pcm);
return 0;
}
static int snd_pcm_a2dp_poll_ask(snd_pcm_t *pcm)
{
DBG("pcm %p", pcm);
return 0;
}
static snd_pcm_sframes_t snd_pcm_a2dp_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
{
DBG("pcm %p frames %d", pcm, frames);
return 0;
}
static snd_pcm_sframes_t snd_pcm_a2dp_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
{
DBG("pcm %p frames %d", pcm, frames);
return 0;
}
static snd_pcm_sframes_t snd_pcm_a2dp_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size)
{
DBG("pcm %p buffer %p size %d", pcm, buffer, size);
return 0;
}
static snd_pcm_sframes_t snd_pcm_a2dp_writen(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size)
{
DBG("pcm %p bufs %p size %d", pcm, bufs, size);
return 0;
}
static snd_pcm_sframes_t snd_pcm_a2dp_readi(snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size)
{
DBG("pcm %p buffer %p size %d", pcm, buffer, size);
return 0;
}
static snd_pcm_sframes_t snd_pcm_a2dp_readn(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size)
{
DBG("pcm %p bufs %p size %d", pcm, bufs, size);
return 0;
}
static snd_pcm_sframes_t snd_pcm_a2dp_avail_update(snd_pcm_t *pcm)
{
DBG("pcm %p", pcm);
return 0;
}
static snd_pcm_sframes_t snd_pcm_a2dp_mmap_commit(snd_pcm_t *pcm, snd_pcm_uframes_t offset, snd_pcm_uframes_t size)
{
DBG("pcm %p offset %d size %d", pcm, offset, size);
return 0;
}
static snd_pcm_fast_ops_t snd_pcm_a2dp_fast_ops = {
.status = snd_pcm_a2dp_status,
.prepare = snd_pcm_a2dp_prepare,
.reset = snd_pcm_a2dp_reset,
.start = snd_pcm_a2dp_start,
.drop = snd_pcm_a2dp_drop,
.drain = snd_pcm_a2dp_drain,
.pause = snd_pcm_a2dp_pause,
.state = snd_pcm_a2dp_state,
.hwsync = snd_pcm_a2dp_hwsync,
.delay = snd_pcm_a2dp_delay,
.resume = snd_pcm_a2dp_resume,
.poll_ask = snd_pcm_a2dp_poll_ask,
.rewind = snd_pcm_a2dp_rewind,
.forward = snd_pcm_a2dp_forward,
.writei = snd_pcm_a2dp_writei,
.writen = snd_pcm_a2dp_writen,
.readi = snd_pcm_a2dp_readi,
.readn = snd_pcm_a2dp_readn,
.avail_update = snd_pcm_a2dp_avail_update,
.mmap_commit = snd_pcm_a2dp_mmap_commit,
};
static int snd_pcm_a2dp_open(snd_pcm_t **pcmp, const char *name,
bdaddr_t *src, bdaddr_t *dst, snd_pcm_stream_t stream, int mode)
{
snd_pcm_t *pcm;
char addr[18];
int err;
DBG("name %s mode %d", name, mode);
ba2str(dst, addr);
printf("BD_ADDR %s\n", addr);
err = snd_pcm_new(&pcm, SND_PCM_TYPE_HW, name, stream, mode);
if (err < 0)
return err;
pcm->ops = &snd_pcm_a2dp_ops;
pcm->fast_ops = &snd_pcm_a2dp_fast_ops;
*pcmp = pcm;
return 0;
}
int _snd_pcm_a2dp_open(snd_pcm_t **pcmp, const char *name, snd_config_t *root,
snd_config_t *conf, snd_pcm_stream_t stream, int mode)
{
snd_config_iterator_t i, next;
bdaddr_t src, dst;
const char *addr;
int err;
DBG("name %s mode %d", name, mode);
bacpy(&src, BDADDR_ANY);
bacpy(&dst, BDADDR_ANY);
snd_config_for_each(i, next, conf) {
snd_config_t *n = snd_config_iterator_entry(i);
const char *id;
if (snd_config_get_id(n, &id) < 0)
continue;
if (snd_pcm_conf_generic_id(id))
continue;
if (strcmp(id, "bdaddr") == 0 || strcmp(id, "dst") == 0) {
if (snd_config_get_type(n) != SND_CONFIG_TYPE_STRING) {
SNDERR("Invalid type for %s", id);
return -EINVAL;
}
snd_config_get_string(n, &addr);
str2ba(addr, &dst);
continue;
}
if (strcmp(id, "src") == 0) {
if (snd_config_get_type(n) != SND_CONFIG_TYPE_STRING) {
SNDERR("Invalid type for %s", id);
return -EINVAL;
}
snd_config_get_string(n, &addr);
str2ba(addr, &src);
continue;
}
SNDERR("Unknown field %s", id);
return -EINVAL;
}
err = snd_pcm_a2dp_open(pcmp, name, &src, &dst, stream, mode);
return err;
}
SND_DLSYM_BUILD_VERSION(_snd_pcm_a2dp_open, SND_PCM_DLSYM_VERSION);
next prev parent reply other threads:[~2004-12-09 10:57 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2004-12-09 9:28 [Bluez-devel] starting btsco-in-libalsa Brad Midgley
2004-12-09 10:57 ` Marcel Holtmann [this message]
2004-12-09 11:48 ` [Bluez-devel] " suche.org
2004-12-09 12:07 ` Marcel Holtmann
2004-12-09 12:49 ` [Bluez-devel] Re2: " suche.org
2004-12-09 13:10 ` [Bluez-devel] " Marcel Holtmann
2004-12-09 17:31 ` Brad Midgley
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1102589835.9988.114.camel@pegasus \
--to=marcel@holtmann.org \
--cc=bluez-devel@lists.sourceforge.net \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.