Merge branch 'develop' into a2dp

This commit is contained in:
Milanka Ringwald 2017-01-30 14:53:14 +01:00
commit 6d5e5836ab
25 changed files with 1049 additions and 580 deletions

View File

@ -33,8 +33,8 @@ CSR, which has been acquired by Qualcomm, provides all relevant information on t
Chipset | Type | HCI Transport | BD_ADDR (1) | SCO over HCI (2) | LE DLE | Multiple LE Roles | BTstack folder | Comment
-------------------- |-----------| ---------------|--------------|------------------|--------|----------------------|----------------|---------
Broadcom UART | Dual mode | H4, H5 | rarely | No (didn't work) | No | Maybe (3) | bcm | Max UART baudrate 3 mbps
Broadcom USB Dongles | Dual mode | USB | Yes | No (didn't work) | No | No | bcm |
Broadcom UART | Dual mode | H4, H5 | rarely | Maybe | No | Maybe (3) | bcm | Max UART baudrate 3 mbps
Broadcom USB Dongles | Dual mode | USB | Yes | Yes | No | No | bcm |
CSR UART | Dual mode | H4, H5 | rarely | No (didn't work) | No | No | csr |
CSR USB Dongles | Dual mode | USB | Mostly | Yes | No | No | csr |
Dialog DA14581 | LE | H4, SPI | ? | n.a. | No | No | | Waiting for dev kit

View File

@ -622,17 +622,18 @@ static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t * even
}
break;
case HFP_SUBEVENT_AUDIO_CONNECTION_RELEASED:
printf("\n** Audio connection released **\n");
printf("Audio connection released\n");
sco_handle = 0;
sco_demo_close();
break;
case HFP_SUBEVENT_START_RINGINIG:
printf("\n** Start Ringing **\n");
printf("Start Ringing\n");
break;
case HFP_SUBEVENT_STOP_RINGINIG:
printf("\n** Stop Ringing **\n");
printf("Stop Ringing\n");
break;
case HFP_SUBEVENT_PLACE_CALL_WITH_NUMBER:
printf("\n** Outgoing call '%s' **\n", hfp_subevent_place_call_with_number_get_number(event));
printf("Outgoing call '%s'\n", hfp_subevent_place_call_with_number_get_number(event));
// validate number
if ( strcmp("1234567", hfp_subevent_place_call_with_number_get_number(event)) == 0
|| strcmp("7654321", hfp_subevent_place_call_with_number_get_number(event)) == 0
@ -646,11 +647,11 @@ static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t * even
break;
case HFP_SUBEVENT_ATTACH_NUMBER_TO_VOICE_TAG:
printf("\n** Attach number to voice tag. Sending '1234567\n");
printf("Attach number to voice tag. Sending '1234567\n");
hfp_ag_send_phone_number_for_voice_tag(acl_handle, "1234567");
break;
case HFP_SUBEVENT_TRANSMIT_DTMF_CODES:
printf("\n** Send DTMF Codes: '%s'\n", hfp_subevent_transmit_dtmf_codes_get_dtmf(event));
printf("Send DTMF Codes: '%s'\n", hfp_subevent_transmit_dtmf_codes_get_dtmf(event));
hfp_ag_send_dtmf_code_done(acl_handle);
break;
case HFP_SUBEVENT_CALL_ANSWERED:

View File

@ -39,7 +39,6 @@
* sco_demo_util.c - send/receive test data via SCO, used by hfp_*_demo and hsp_*_demo
*/
#include <stdio.h>
#include "sco_demo_util.h"
@ -53,86 +52,93 @@
#include "wav_util.h"
#endif
// configure test mode
#define SCO_DEMO_MODE_SINE 0
#define SCO_DEMO_MODE_ASCII 1
#define SCO_DEMO_MODE_COUNTER 2
#define SCO_DEMO_MODE_55 3
#define SCO_DEMO_MODE_00 4
// SCO demo configuration
#define SCO_DEMO_MODE SCO_DEMO_MODE_SINE
#define SCO_REPORT_PERIOD 100
#ifdef HAVE_POSIX_FILE_IO
#define SCO_WAV_FILENAME "sco_input.wav"
#define SCO_MSBC_OUT_FILENAME "sco_output.msbc"
#define SCO_MSBC_IN_FILENAME "sco_input.msbc"
#define SCO_WAV_DURATION_IN_SECONDS 15
#endif
#if defined(HAVE_PORTAUDIO) && (SCO_DEMO_MODE == SCO_DEMO_MODE_SINE)
#define USE_PORTAUDIO
#endif
#ifdef USE_PORTAUDIO
#ifdef HAVE_PORTAUDIO
#include <portaudio.h>
#include "btstack_ring_buffer.h"
#endif
// portaudio config
// test modes
#define SCO_DEMO_MODE_SINE 0
#define SCO_DEMO_MODE_ASCII 1
#define SCO_DEMO_MODE_COUNTER 2
#define SCO_DEMO_MODE_55 3
#define SCO_DEMO_MODE_00 4
#define SCO_DEMO_MODE_MICROPHONE 5
// SCO demo configuration
#define SCO_DEMO_MODE SCO_DEMO_MODE_SINE
// number of sco packets until 'report' on console
#define SCO_REPORT_PERIOD 100
// length and name of wav file on disc
#define SCO_WAV_DURATION_IN_SECONDS 15
#define SCO_WAV_FILENAME "sco_input.wav"
// name of sbc test files
#define SCO_MSBC_OUT_FILENAME "sco_output.msbc"
#define SCO_MSBC_IN_FILENAME "sco_input.msbc"
// pre-buffer for CVSD and mSBC - also defines latency
#define SCO_CVSD_PA_PREBUFFER_MS 50
#define SCO_MSBC_PA_PREBUFFER_MS 50
// constants
#define NUM_CHANNELS 1
#define CVSD_BYTES_PER_FRAME (2*NUM_CHANNELS)
#define CVSD_SAMPLE_RATE 8000
#define CVSD_FRAMES_PER_BUFFER 24
#define CVSD_PA_SAMPLE_TYPE paInt8
#define CVSD_BYTES_PER_FRAME (1*NUM_CHANNELS)
#define CVSD_PREBUFFER_MS 5
#define CVSD_PREBUFFER_BYTES (CVSD_PREBUFFER_MS * CVSD_SAMPLE_RATE/1000 * CVSD_BYTES_PER_FRAME)
#define MSBC_SAMPLE_RATE 16000
#define MSBC_FRAMES_PER_BUFFER 120
#define MSBC_PA_SAMPLE_TYPE paInt16
#define MSBC_BYTES_PER_FRAME (2*NUM_CHANNELS)
#define MSBC_PREBUFFER_MS 50
#define MSBC_PREBUFFER_BYTES (MSBC_PREBUFFER_MS * MSBC_SAMPLE_RATE/1000 * MSBC_BYTES_PER_FRAME)
// portaudio globals
static PaStream * stream;
static uint8_t pa_stream_started = 0;
#if defined(HAVE_PORTAUDIO) && (SCO_DEMO_MODE == SCO_DEMO_MODE_SINE || SCO_DEMO_MODE == SCO_DEMO_MODE_MICROPHONE)
#define USE_PORTAUDIO
#define CVSD_PA_PREBUFFER_BYTES (SCO_CVSD_PA_PREBUFFER_MS * CVSD_SAMPLE_RATE/1000 * CVSD_BYTES_PER_FRAME)
#define MSBC_PA_PREBUFFER_BYTES (SCO_MSBC_PA_PREBUFFER_MS * MSBC_SAMPLE_RATE/1000 * MSBC_BYTES_PER_FRAME)
#endif
#ifdef USE_PORTAUDIO
// bidirectional audio stream
static PaStream * pa_stream;
// output
static int pa_output_started = 0;
static int pa_output_paused = 0;
static uint8_t pa_output_ring_buffer_storage[2*MSBC_PA_PREBUFFER_BYTES];
static btstack_ring_buffer_t pa_output_ring_buffer;
// input
#if SCO_DEMO_MODE == SCO_DEMO_MODE_MICROPHONE
#define USE_PORTAUDIO_INPUT
static int pa_input_started = 0;
static int pa_input_paused = 0;
static uint8_t pa_input_ring_buffer_storage[2*8000]; // full second input buffer
static btstack_ring_buffer_t pa_input_ring_buffer;
static int pa_input_counter;
#endif
static uint8_t ring_buffer_storage[2*MSBC_PREBUFFER_BYTES];
static btstack_ring_buffer_t ring_buffer;
#endif
static int dump_data = 1;
static int count_sent = 0;
static int count_received = 0;
static uint8_t negotiated_codec = 0;
#if SCO_DEMO_MODE != SCO_DEMO_MODE_55
static int phase = 0;
#endif
static int negotiated_codec = -1;
btstack_sbc_decoder_state_t decoder_state;
btstack_cvsd_plc_state_t cvsd_plc_state;
FILE * msbc_file_in;
FILE * msbc_file_out;
int num_samples_to_write;
int num_audio_frames;
int phase;
#if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
// input signal: pre-computed sine wave, at 8000 kz
static const uint8_t sine_uint8[] = {
0, 15, 31, 46, 61, 74, 86, 97, 107, 114,
120, 124, 126, 126, 124, 120, 114, 107, 97, 86,
74, 61, 46, 31, 15, 0, 241, 225, 210, 195,
182, 170, 159, 149, 142, 136, 132, 130, 130, 132,
136, 142, 149, 159, 170, 182, 195, 210, 225, 241,
};
// input signal: pre-computed sine wave, 160 Hz at 16000 kHz
static const int16_t sine_int16[] = {
static const int16_t sine_int16_at_16000hz[] = {
0, 2057, 4107, 6140, 8149, 10126, 12062, 13952, 15786, 17557,
19260, 20886, 22431, 23886, 25247, 26509, 27666, 28714, 29648, 30466,
31163, 31738, 32187, 32509, 32702, 32767, 32702, 32509, 32187, 31738,
@ -145,41 +151,30 @@ static const int16_t sine_int16[] = {
-19260, -17557, -15786, -13952, -12062, -10126, -8149, -6140, -4107, -2057,
};
static void sco_demo_sine_wave_int8(int num_samples, int8_t * data){
int i;
for (i=0; i<num_samples; i++){
data[i] = (int8_t)sine_uint8[phase];
phase++;
if (phase >= sizeof(sine_uint8)) phase = 0;
}
}
static void sco_demo_sine_wave_int16(int num_samples, int16_t * data){
// ony use every second value from 16khz table
static void sco_demo_sine_wave_int16_at_8000_hz(int num_samples, int16_t * data){
int i;
for (i=0; i < num_samples; i++){
data[i] = sine_int16[phase++];
if (phase >= (sizeof(sine_int16) / sizeof(int16_t))){
data[i] = sine_int16_at_16000hz[phase++];
phase++;
if (phase >= (sizeof(sine_int16_at_16000hz) / sizeof(int16_t))){
phase = 0;
}
}
}
static int num_audio_frames = 0;
static void sco_demo_fill_audio_frame(void){
static void sco_demo_msbc_fill_sine_audio_frame(void){
if (!hfp_msbc_can_encode_audio_frame_now()) return;
int num_samples = hfp_msbc_num_audio_samples_per_frame();
int16_t sample_buffer[num_samples];
sco_demo_sine_wave_int16(num_samples, sample_buffer);
sco_demo_sine_wave_int16_at_8000_hz(num_samples, sample_buffer);
hfp_msbc_encode_audio_frame(sample_buffer);
num_audio_frames++;
}
#ifdef SCO_WAV_FILENAME
static btstack_sbc_decoder_state_t decoder_state;
static btstack_cvsd_plc_state_t cvsd_plc_state;
static int num_samples_to_write;
#endif
#ifdef USE_PORTAUDIO
static int patestCallback( const void *inputBuffer, void *outputBuffer,
static int portaudio_callback( const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
@ -189,45 +184,166 @@ static int patestCallback( const void *inputBuffer, void *outputBuffer,
(void) inputBuffer;
(void) userData;
uint32_t bytes_read = 0;
int bytes_per_buffer = framesPerBuffer;
if (negotiated_codec == HFP_CODEC_MSBC){
bytes_per_buffer *= MSBC_BYTES_PER_FRAME;
} else {
bytes_per_buffer *= CVSD_BYTES_PER_FRAME;
// output part
// config based on codec
int bytes_to_copy;
int prebuffer_bytes;
switch (negotiated_codec){
case HFP_CODEC_MSBC:
bytes_to_copy = framesPerBuffer * MSBC_BYTES_PER_FRAME;
prebuffer_bytes = MSBC_PA_PREBUFFER_BYTES;
break;
case HFP_CODEC_CVSD:
bytes_to_copy = framesPerBuffer * CVSD_BYTES_PER_FRAME;
prebuffer_bytes = CVSD_PA_PREBUFFER_BYTES;
break;
default:
bytes_to_copy = framesPerBuffer * 2; // assume 1 channel / 16 bit audio samples
prebuffer_bytes = 0xfffffff;
break;
}
if (btstack_ring_buffer_bytes_available(&ring_buffer) >= bytes_per_buffer){
btstack_ring_buffer_read(&ring_buffer, outputBuffer, bytes_per_buffer, &bytes_read);
} else {
printf("NOT ENOUGH DATA!\n");
memset(outputBuffer, 0, bytes_per_buffer);
// fill with silence while paused
if (pa_output_paused){
if (btstack_ring_buffer_bytes_available(&pa_output_ring_buffer) < prebuffer_bytes){
memset(outputBuffer, 0, bytes_to_copy);
return 0;
} else {
// resume playback
pa_output_paused = 0;
}
}
// printf("bytes avail after read: %d\n", btstack_ring_buffer_bytes_available(&ring_buffer));
// get data from ringbuffer
uint32_t bytes_read = 0;
btstack_ring_buffer_read(&pa_output_ring_buffer, outputBuffer, bytes_to_copy, &bytes_read);
bytes_to_copy -= bytes_read;
// fill with 0 if not enough
if (bytes_to_copy){
memset(outputBuffer + bytes_read, 0, bytes_to_copy);
pa_output_paused = 1;
}
// end of output part
// input part -- just store in ring buffer
#ifdef USE_PORTAUDIO_INPUT
btstack_ring_buffer_write(&pa_input_ring_buffer, (uint8_t *)inputBuffer, framesPerBuffer * 2);
pa_input_counter += framesPerBuffer * 2;
#endif
return 0;
}
// return 1 if ok
static int portaudio_initialize(int sample_rate){
PaError err;
/* -- initialize PortAudio -- */
printf("PortAudio: Initialize\n");
err = Pa_Initialize();
if( err != paNoError ) return 0;
/* -- setup input and output -- */
const PaDeviceInfo *deviceInfo;
PaStreamParameters * inputParameters = NULL;
PaStreamParameters outputParameters;
outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
outputParameters.channelCount = NUM_CHANNELS;
outputParameters.sampleFormat = paInt16;
outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultHighOutputLatency;
outputParameters.hostApiSpecificStreamInfo = NULL;
deviceInfo = Pa_GetDeviceInfo( outputParameters.device );
log_info("PortAudio: Output device: %s", deviceInfo->name);
#ifdef USE_PORTAUDIO_INPUT
PaStreamParameters theInputParameters;
theInputParameters.device = Pa_GetDefaultInputDevice(); /* default input device */
theInputParameters.channelCount = NUM_CHANNELS;
theInputParameters.sampleFormat = paInt16;
theInputParameters.suggestedLatency = Pa_GetDeviceInfo( theInputParameters.device )->defaultHighOutputLatency;
theInputParameters.hostApiSpecificStreamInfo = NULL;
inputParameters = &theInputParameters;
deviceInfo = Pa_GetDeviceInfo( inputParameters->device );
log_info("PortAudio: Input device: %s", deviceInfo->name);
#endif
/* -- setup output stream -- */
printf("PortAudio: Open stream\n");
err = Pa_OpenStream(
&pa_stream,
inputParameters,
&outputParameters,
sample_rate,
0,
paClipOff, /* we won't output out of range samples so don't bother clipping them */
portaudio_callback,
NULL );
if (err != paNoError){
printf("Error opening portaudio stream: \"%s\"\n", Pa_GetErrorText(err));
return 0;
}
memset(pa_output_ring_buffer_storage, 0, sizeof(pa_output_ring_buffer_storage));
btstack_ring_buffer_init(&pa_output_ring_buffer, pa_output_ring_buffer_storage, sizeof(pa_output_ring_buffer_storage));
#ifdef USE_PORTAUDIO_INPUT
memset(pa_input_ring_buffer_storage, 0, sizeof(pa_input_ring_buffer_storage));
btstack_ring_buffer_init(&pa_input_ring_buffer, pa_input_ring_buffer_storage, sizeof(pa_input_ring_buffer_storage));
printf("PortAudio: Input buffer size %u\n", btstack_ring_buffer_bytes_free(&pa_input_ring_buffer));
#endif
/* -- start stream -- */
err = Pa_StartStream(pa_stream);
if (err != paNoError){
printf("Error starting the stream: \"%s\"\n", Pa_GetErrorText(err));
return 0;
}
pa_output_started = 1;
pa_output_paused = 1;
#ifdef USE_PORTAUDIO_INPUT
pa_input_started = 1;
pa_input_paused = 1;
#endif
return 1;
}
static void portaudio_terminate(void){
if (!pa_stream) return;
PaError err;
printf("PortAudio: Stop Stream\n");
err = Pa_StopStream(pa_stream);
if (err != paNoError){
printf("Error stopping the stream: \"%s\"\n", Pa_GetErrorText(err));
return;
}
printf("PortAudio: Close Stream\n");
err = Pa_CloseStream(pa_stream);
if (err != paNoError){
printf("Error closing the stream: \"%s\"\n", Pa_GetErrorText(err));
return;
}
pa_stream = NULL;
printf("PortAudio: Terminate\n");
err = Pa_Terminate();
if (err != paNoError){
printf("Error terminating portaudio: \"%s\"\n", Pa_GetErrorText(err));
return;
}
}
#endif
static void handle_pcm_data(int16_t * data, int num_samples, int num_channels, int sample_rate, void * context){
UNUSED(context);
UNUSED(sample_rate);
// printf("handle_pcm_data num samples %u, sample rate %d\n", num_samples, num_channels);
#ifdef USE_PORTAUDIO
if (!pa_stream_started && btstack_ring_buffer_bytes_available(&ring_buffer) >= MSBC_PREBUFFER_BYTES){
/* -- start stream -- */
PaError err = Pa_StartStream(stream);
if (err != paNoError){
printf("Error starting the stream: \"%s\"\n", Pa_GetErrorText(err));
return;
}
pa_stream_started = 1;
}
btstack_ring_buffer_write(&ring_buffer, (uint8_t *)data, num_samples*num_channels*2);
// printf("bytes avail after write: %d\n", btstack_ring_buffer_bytes_available(&ring_buffer));
#ifdef HAVE_PORTAUDIO
btstack_ring_buffer_write(&pa_output_ring_buffer, (uint8_t *)data, num_samples*num_channels*2);
#else
UNUSED(num_channels);
#endif
#endif
if (!num_samples_to_write) return;
@ -242,54 +358,30 @@ static void handle_pcm_data(int16_t * data, int num_samples, int num_channels, i
}
static void sco_demo_init_mSBC(void){
int sample_rate = 16000;
wav_writer_open(SCO_WAV_FILENAME, 1, sample_rate);
printf("SCO Demo: Init mSBC\n");
wav_writer_open(SCO_WAV_FILENAME, 1, MSBC_SAMPLE_RATE);
btstack_sbc_decoder_init(&decoder_state, SBC_MODE_mSBC, &handle_pcm_data, NULL);
num_samples_to_write = sample_rate * SCO_WAV_DURATION_IN_SECONDS;
num_samples_to_write = MSBC_SAMPLE_RATE * SCO_WAV_DURATION_IN_SECONDS;
hfp_msbc_init();
sco_demo_fill_audio_frame();
#if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
sco_demo_msbc_fill_sine_audio_frame();
#endif
#ifdef SCO_MSBC_IN_FILENAME
msbc_file_in = fopen(SCO_MSBC_IN_FILENAME, "wb");
printf("SCO Demo: creating mSBC in file %s, %p\n", SCO_MSBC_IN_FILENAME, msbc_file_in);
#endif
#ifdef SCO_MSBC_OUT_FILENAME
msbc_file_out = fopen(SCO_MSBC_OUT_FILENAME, "wb");
printf("SCO Demo: creating mSBC out file %s, %p\n", SCO_MSBC_OUT_FILENAME, msbc_file_out);
#endif
#ifdef USE_PORTAUDIO
PaError err;
PaStreamParameters outputParameters;
/* -- initialize PortAudio -- */
err = Pa_Initialize();
if( err != paNoError ) return;
/* -- setup input and output -- */
outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
outputParameters.channelCount = NUM_CHANNELS;
outputParameters.sampleFormat = MSBC_PA_SAMPLE_TYPE;
outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultHighOutputLatency;
outputParameters.hostApiSpecificStreamInfo = NULL;
/* -- setup stream -- */
err = Pa_OpenStream(
&stream,
NULL, // &inputParameters,
&outputParameters,
MSBC_SAMPLE_RATE,
MSBC_FRAMES_PER_BUFFER,
paClipOff, /* we won't output out of range samples so don't bother clipping them */
patestCallback, /* no callback, use blocking API */
NULL ); /* no callback, so no callback userData */
if (err != paNoError){
printf("Error initializing portaudio: \"%s\"\n", Pa_GetErrorText(err));
return;
}
memset(ring_buffer_storage, 0, sizeof(ring_buffer_storage));
btstack_ring_buffer_init(&ring_buffer, ring_buffer_storage, sizeof(ring_buffer_storage));
pa_stream_started = 0;
portaudio_initialize(MSBC_SAMPLE_RATE);
#endif
}
@ -304,116 +396,65 @@ static void sco_demo_receive_mSBC(uint8_t * packet, uint16_t size){
}
static void sco_demo_init_CVSD(void){
int sample_rate = 8000;
wav_writer_open(SCO_WAV_FILENAME, 1, sample_rate);
printf("SCO Demo: Init CVSD\n");
wav_writer_open(SCO_WAV_FILENAME, 1, CVSD_SAMPLE_RATE);
btstack_cvsd_plc_init(&cvsd_plc_state);
num_samples_to_write = sample_rate * SCO_WAV_DURATION_IN_SECONDS;
num_samples_to_write = CVSD_SAMPLE_RATE * SCO_WAV_DURATION_IN_SECONDS;
#ifdef USE_PORTAUDIO
PaError err;
PaStreamParameters outputParameters;
/* -- initialize PortAudio -- */
err = Pa_Initialize();
if( err != paNoError ) return;
/* -- setup input and output -- */
outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
outputParameters.channelCount = NUM_CHANNELS;
outputParameters.sampleFormat = CVSD_PA_SAMPLE_TYPE;
outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultHighOutputLatency;
outputParameters.hostApiSpecificStreamInfo = NULL;
/* -- setup stream -- */
err = Pa_OpenStream(
&stream,
NULL, // &inputParameters,
&outputParameters,
CVSD_SAMPLE_RATE,
CVSD_FRAMES_PER_BUFFER,
paClipOff, /* we won't output out of range samples so don't bother clipping them */
patestCallback, /* no callback, use blocking API */
NULL ); /* no callback, so no callback userData */
if (err != paNoError){
printf("Error initializing portaudio: \"%s\"\n", Pa_GetErrorText(err));
return;
}
memset(ring_buffer_storage, 0, sizeof(ring_buffer_storage));
btstack_ring_buffer_init(&ring_buffer, ring_buffer_storage, sizeof(ring_buffer_storage));
pa_stream_started = 0;
portaudio_initialize(CVSD_SAMPLE_RATE);
#endif
}
static void sco_demo_receive_CVSD(uint8_t * packet, uint16_t size){
if (!num_samples_to_write) return;
int16_t audio_frame_out[255]; //
const int num_samples = size - 3;
if (size > sizeof(audio_frame_out)){
printf("sco_demo_receive_CVSD: SCO packet larger than local output buffer - dropping data.\n");
return;
}
const int audio_bytes_read = size - 3;
const int num_samples = audio_bytes_read / CVSD_BYTES_PER_FRAME;
const int samples_to_write = btstack_min(num_samples, num_samples_to_write);
int8_t audio_frame_out[24];
// memcpy(audio_frame_out, (int8_t*)(packet+3), 24);
#if 0
btstack_cvsd_plc_process_data(&cvsd_plc_state, (int8_t *)(packet+3), num_samples, audio_frame_out);
// int8_t * audio_frame_out = (int8_t*)&packet[3];
#else
memcpy(audio_frame_out, packet+3, audio_bytes_read);
#endif
wav_writer_write_int8(samples_to_write, audio_frame_out);
wav_writer_write_int16(samples_to_write, audio_frame_out);
num_samples_to_write -= samples_to_write;
if (num_samples_to_write == 0){
sco_demo_close();
}
#ifdef USE_PORTAUDIO
if (!pa_stream_started && btstack_ring_buffer_bytes_available(&ring_buffer) >= CVSD_PREBUFFER_BYTES){
/* -- start stream -- */
PaError err = Pa_StartStream(stream);
if (err != paNoError){
printf("Error starting the stream: \"%s\"\n", Pa_GetErrorText(err));
return;
}
pa_stream_started = 1;
}
btstack_ring_buffer_write(&ring_buffer, (uint8_t *)audio_frame_out, samples_to_write);
btstack_ring_buffer_write(&pa_output_ring_buffer, (uint8_t *)audio_frame_out, audio_bytes_read);
#endif
}
#endif
#endif
void sco_demo_close(void){
#if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
printf("SCO demo close\n");
#if (SCO_DEMO_MODE == SCO_DEMO_MODE_SINE) || (SCO_DEMO_MODE == SCO_DEMO_MODE_MICROPHONE)
#if defined(SCO_WAV_FILENAME) || defined(SCO_SBC_FILENAME)
wav_writer_close();
#endif
printf("SCO demo statistics: ");
if (negotiated_codec == HFP_CODEC_MSBC){
printf("Used mSBC with PLC, number of processed frames: \n - %d good frames, \n - %d zero frames, \n - %d bad frames.", decoder_state.good_frames_nr, decoder_state.zero_frames_nr, decoder_state.bad_frames_nr);
} else {
printf("Used CVSD with PLC, number of proccesed frames: \n - %d good frames, \n - %d bad frames.", cvsd_plc_state.good_frames_nr, cvsd_plc_state.bad_frames_nr);
}
#endif
#endif
#ifdef HAVE_PORTAUDIO
if (pa_stream_started){
PaError err = Pa_StopStream(stream);
if (err != paNoError){
printf("Error stopping the stream: \"%s\"\n", Pa_GetErrorText(err));
return;
}
pa_stream_started = 0;
err = Pa_CloseStream(stream);
if (err != paNoError){
printf("Error closing the stream: \"%s\"\n", Pa_GetErrorText(err));
return;
}
err = Pa_Terminate();
if (err != paNoError){
printf("Error terminating portaudio: \"%s\"\n", Pa_GetErrorText(err));
return;
}
}
portaudio_terminate();
#endif
#if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
#ifdef SCO_WAV_FILENAME
#if 0
printf("SCO Demo: closing wav file\n");
if (negotiated_codec == HFP_CODEC_MSBC){
@ -426,13 +467,16 @@ void sco_demo_close(void){
}
#endif
#endif
negotiated_codec = -1;
#endif
}
void sco_demo_set_codec(uint8_t codec){
if (negotiated_codec == codec) return;
negotiated_codec = codec;
#if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
#if (SCO_DEMO_MODE == SCO_DEMO_MODE_SINE) || (SCO_DEMO_MODE == SCO_DEMO_MODE_MICROPHONE)
#if defined(SCO_WAV_FILENAME) || defined(SCO_SBC_FILENAME)
if (negotiated_codec == HFP_CODEC_MSBC){
sco_demo_init_mSBC();
@ -444,8 +488,10 @@ void sco_demo_set_codec(uint8_t codec){
}
void sco_demo_init(void){
// status
#if SCO_DEMO_MODE == SCO_DEMO_MODE_MICROPHONE
printf("SCO Demo: Sending and receiving audio via portaudio.\n");
#endif
#if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
#ifdef HAVE_PORTAUDIO
printf("SCO Demo: Sending sine wave, audio output via portaudio.\n");
@ -460,7 +506,9 @@ void sco_demo_init(void){
printf("SCO Demo: Sending counter value, hexdump received data.\n");
#endif
#if SCO_DEMO_MODE != SCO_DEMO_MODE_SINE
#if (SCO_DEMO_MODE == SCO_DEMO_MODE_SINE) || (SCO_DEMO_MODE == SCO_DEMO_MODE_MICROPHONE)
hci_set_sco_voice_setting(0x60); // linear, unsigned, 16-bit, CVSD
#else
hci_set_sco_voice_setting(0x03); // linear, unsigned, 8-bit, transparent
#endif
@ -478,19 +526,16 @@ void sco_demo_send(hci_con_handle_t sco_handle){
if (!sco_handle) return;
const int sco_packet_length = 24 + 3; // hci_get_sco_packet_length();
const int sco_payload_length = sco_packet_length - 3;
int sco_packet_length = hci_get_sco_packet_length();
int sco_payload_length = sco_packet_length - 3;
hci_reserve_packet_buffer();
uint8_t * sco_packet = hci_get_outgoing_packet_buffer();
// set handle + flags
little_endian_store_16(sco_packet, 0, sco_handle);
// set len
sco_packet[2] = sco_payload_length;
const int audio_samples_per_packet = sco_payload_length; // for 8-bit data. for 16-bit data it's /2
#if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
if (negotiated_codec == HFP_CODEC_MSBC){
// overwrite
sco_payload_length = 24;
sco_packet_length = sco_payload_length + 3;
if (hfp_msbc_num_bytes_in_stream() < sco_payload_length){
log_error("mSBC stream is empty.");
@ -501,31 +546,111 @@ void sco_demo_send(hci_con_handle_t sco_handle){
fwrite(sco_packet + 3, sco_payload_length, 1, msbc_file_out);
}
sco_demo_fill_audio_frame();
sco_demo_msbc_fill_sine_audio_frame();
} else {
sco_demo_sine_wave_int8(audio_samples_per_packet, (int8_t *) (sco_packet+3));
const int audio_samples_per_packet = sco_payload_length / CVSD_BYTES_PER_FRAME;
sco_demo_sine_wave_int16_at_8000_hz(audio_samples_per_packet, (int16_t *) (sco_packet+3));
}
#endif
#if SCO_DEMO_MODE == SCO_DEMO_MODE_MICROPHONE
#ifdef HAVE_PORTAUDIO
if (negotiated_codec == HFP_CODEC_MSBC){
// MSBC
// overwrite
sco_payload_length = 24;
sco_packet_length = sco_payload_length + 3;
if (pa_input_paused){
if (btstack_ring_buffer_bytes_available(&pa_input_ring_buffer) >= MSBC_PA_PREBUFFER_BYTES){
// resume sending
pa_input_paused = 0;
}
}
if (!pa_input_paused){
int num_samples = hfp_msbc_num_audio_samples_per_frame();
if (hfp_msbc_can_encode_audio_frame_now() && btstack_ring_buffer_bytes_available(&pa_input_ring_buffer) >= (num_samples * MSBC_BYTES_PER_FRAME)){
int16_t sample_buffer[num_samples];
uint32_t bytes_read;
btstack_ring_buffer_read(&pa_input_ring_buffer, (uint8_t*) sample_buffer, num_samples * MSBC_BYTES_PER_FRAME, &bytes_read);
hfp_msbc_encode_audio_frame(sample_buffer);
num_audio_frames++;
}
}
if (hfp_msbc_num_bytes_in_stream() < sco_payload_length){
log_error("mSBC stream should not be empty.");
memset(sco_packet + 3, 0, sco_payload_length);
pa_input_paused = 1;
} else {
hfp_msbc_read_from_stream(sco_packet + 3, sco_payload_length);
if (msbc_file_out){
// log outgoing mSBC data for testing
fwrite(sco_packet + 3, sco_payload_length, 1, msbc_file_out);
}
}
} else {
// CVSD
log_info("send: bytes avail %u, free %u, counter %u", btstack_ring_buffer_bytes_available(&pa_input_ring_buffer), btstack_ring_buffer_bytes_free(&pa_input_ring_buffer), pa_input_counter);
// fill with silence while paused
int bytes_to_copy = sco_payload_length;
if (pa_input_paused){
if (btstack_ring_buffer_bytes_available(&pa_input_ring_buffer) >= CVSD_PA_PREBUFFER_BYTES){
// resume sending
pa_input_paused = 0;
}
}
// get data from ringbuffer
uint16_t pos = 0;
if (!pa_input_paused){
uint32_t bytes_read = 0;
btstack_ring_buffer_read(&pa_input_ring_buffer, sco_packet + 3, bytes_to_copy, &bytes_read);
bytes_to_copy -= bytes_read;
pos += bytes_read;
}
// fill with 0 if not enough
if (bytes_to_copy){
memset(sco_packet + 3 + pos, 0, bytes_to_copy);
pa_input_paused = 1;
}
}
#else
// just send '0's
if (negotiated_codec == HFP_CODEC_MSBC){
sco_payload_length = 24;
sco_packet_length = sco_payload_length + 3;
}
memset(sco_packet + 3, 0, sco_payload_length);
#endif
#endif
#if SCO_DEMO_MODE == SCO_DEMO_MODE_ASCII
memset(&sco_packet[3], phase++, audio_samples_per_packet);
memset(&sco_packet[3], phase++, sco_payload_length);
if (phase > 'z') phase = 'a';
#endif
#if SCO_DEMO_MODE == SCO_DEMO_MODE_COUNTER
int j;
for (j=0;j<audio_samples_per_packet;j++){
for (j=0;j<sco_payload_length;j++){
sco_packet[3+j] = phase++;
}
#endif
#if SCO_DEMO_MODE == SCO_DEMO_MODE_55
int j;
for (j=0;j<audio_samples_per_packet;j++){
for (j=0;j<sco_payload_length;j++){
// sco_packet[3+j] = j & 1 ? 0x35 : 0x53;
sco_packet[3+j] = 0x55;
}
#endif
#if SCO_DEMO_MODE == SCO_DEMO_MODE_00
int j;
for (j=0;j<audio_samples_per_packet;j++){
for (j=0;j<sco_payload_length;j++){
sco_packet[3+j] = 0x00;
}
// additional hack
@ -533,6 +658,14 @@ void sco_demo_send(hci_con_handle_t sco_handle){
(void) phase;
#endif
// test silence
// memset(sco_packet+3, 0, sco_payload_length);
// set handle + flags
little_endian_store_16(sco_packet, 0, sco_handle);
// set len
sco_packet[2] = sco_payload_length;
// finally send packet
hci_send_sco_packet_buffer(sco_packet_length);
// request another send event
@ -575,15 +708,18 @@ void sco_demo_receive(uint8_t * packet, uint16_t size){
packets = 0;
}
#if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
#ifdef SCO_WAV_FILENAME
if (negotiated_codec == HFP_CODEC_MSBC){
sco_demo_receive_mSBC(packet, size);
} else {
sco_demo_receive_CVSD(packet, size);
#if (SCO_DEMO_MODE == SCO_DEMO_MODE_SINE) || (SCO_DEMO_MODE == SCO_DEMO_MODE_MICROPHONE)
switch (negotiated_codec){
case HFP_CODEC_MSBC:
sco_demo_receive_mSBC(packet, size);
break;
case HFP_CODEC_CVSD:
sco_demo_receive_CVSD(packet, size);
break;
default:
break;
}
dump_data = 0;
#endif
#endif
if (packet[1] & 0x30){
@ -617,7 +753,7 @@ void sco_demo_receive(uint8_t * packet, uint16_t size){
}
printf("\n");
#endif
#if SCO_DEMO_MODE == SCO_DEMO_MODE_55 || SCO_DEMO_MODE_00
#if SCO_DEMO_MODE == SCO_DEMO_MODE_55 || SCO_DEMO_MODE == SCO_DEMO_MODE_00
int i;
int contains_error = 0;
for (i=3;i<size;i++){

View File

@ -85,17 +85,29 @@
// 4: Two 8 kHz voice channels with 16-bit encoding or one 16 kHz voice channel with 16-bit encoding
// 5: Three 8 kHz voice channels with 16-bit encoding or one 8 kHz voice channel with 16-bit encoding and one 16 kHz voice channel with 16-bit encoding
// --> support only a single SCO connection
#define ALT_SETTING (2)
// #define ALT_SETTING (1)
// alt setting for 1-3 connections and 8/16 bit
static const int alt_setting_8_bit[] = {1,2,3};
static const int alt_setting_16_bit[] = {2,4,5};
// for ALT_SETTING >= 1 and 8-bit channel, we need the following isochronous packets
// One complete SCO packet with 24 frames every 3 frames (== 3 ms)
#define NUM_ISO_PACKETS (3)
// results in 9 bytes per frame
#define ISO_PACKET_SIZE (9)
const uint16_t iso_packet_size_for_alt_setting[] = {
0,
9,
17,
25,
33,
49,
63,
};
// 49 bytes is the max usb packet size for alternate setting 5 (Three 8 kHz 16-bit channels or one 8 kHz 16-bit channel and one 16 kHz 16-bit channel)
// note: alt setting 6 has max packet size of 63 every 7.5 ms = 472.5 bytes / HCI packet, while max SCO packet has 255 byte payload
#define SCO_PACKET_SIZE (49)
#define SCO_PACKET_SIZE (49 * NUM_ISO_PACKETS)
// Outgoing SCO packet queue
// simplified ring buffer implementation
@ -162,6 +174,15 @@ static int sco_ring_write; // packet idx
static int sco_out_transfers_active;
static struct libusb_transfer *sco_out_transfers[SCO_OUT_BUFFER_COUNT];
static int sco_out_transfers_in_flight[SCO_OUT_BUFFER_COUNT];
// pause/resume
static uint16_t sco_voice_setting;
static int sco_num_connections;
static int sco_shutdown;
// dynamic SCO configuration
static uint16_t iso_packet_size;
#endif
// outgoing buffer for HCI Command packets
@ -240,22 +261,8 @@ LIBUSB_CALL static void async_callback(struct libusb_transfer *transfer){
int c;
// identify and free transfers as part of shutdown
if (libusb_state != LIB_USB_TRANSFERS_ALLOCATED) {
for (c=0;c<EVENT_IN_BUFFER_COUNT;c++){
if (transfer == event_in_transfer[c]){
libusb_free_transfer(transfer);
event_in_transfer[c] = 0;
return;
}
}
for (c=0;c<ACL_IN_BUFFER_COUNT;c++){
if (transfer == acl_in_transfer[c]){
libusb_free_transfer(transfer);
acl_in_transfer[c] = 0;
return;
}
}
#ifdef ENABLE_SCO_OVER_HCI
if (libusb_state != LIB_USB_TRANSFERS_ALLOCATED || sco_shutdown) {
for (c=0;c<SCO_IN_BUFFER_COUNT;c++){
if (transfer == sco_in_transfer[c]){
libusb_free_transfer(transfer);
@ -272,8 +279,24 @@ LIBUSB_CALL static void async_callback(struct libusb_transfer *transfer){
return;
}
}
}
#endif
if (libusb_state != LIB_USB_TRANSFERS_ALLOCATED) {
for (c=0;c<EVENT_IN_BUFFER_COUNT;c++){
if (transfer == event_in_transfer[c]){
libusb_free_transfer(transfer);
event_in_transfer[c] = 0;
return;
}
}
for (c=0;c<ACL_IN_BUFFER_COUNT;c++){
if (transfer == acl_in_transfer[c]){
libusb_free_transfer(transfer);
acl_in_transfer[c] = 0;
return;
}
}
return;
}
@ -327,9 +350,10 @@ static int usb_send_sco_packet(uint8_t *packet, int size){
memcpy(data, packet, size);
// setup transfer
// log_info("usb_send_sco_packet: size %u, max size %u, iso packet size %u", size, NUM_ISO_PACKETS * iso_packet_size, iso_packet_size);
struct libusb_transfer * sco_transfer = sco_out_transfers[tranfer_index];
libusb_fill_iso_transfer(sco_transfer, handle, sco_out_addr, data, size, NUM_ISO_PACKETS, async_callback, NULL, 0);
libusb_set_iso_packet_lengths(sco_transfer, ISO_PACKET_SIZE);
libusb_fill_iso_transfer(sco_transfer, handle, sco_out_addr, data, NUM_ISO_PACKETS * iso_packet_size, NUM_ISO_PACKETS, async_callback, NULL, 0);
libusb_set_iso_packet_lengths(sco_transfer, iso_packet_size);
r = libusb_submit_transfer(sco_transfer);
if (r < 0) {
log_error("Error submitting sco transfer, %d", r);
@ -724,13 +748,6 @@ static int prepare_device(libusb_device_handle * aHandle){
libusb_close(aHandle);
return r;
}
log_info("Switching to setting %u on interface 1..", ALT_SETTING);
r = libusb_set_interface_alt_setting(aHandle, 1, ALT_SETTING);
if (r < 0) {
fprintf(stderr, "Error setting alternative setting %u for interface 1: %s\n", ALT_SETTING, libusb_error_name(r));
libusb_close(aHandle);
return r;
}
#endif
return 0;
@ -760,14 +777,131 @@ static libusb_device_handle * try_open_device(libusb_device * device){
return dev_handle;
}
static int usb_open(void){
int r;
#ifdef ENABLE_SCO_OVER_HCI
static int usb_sco_start(void){
printf("usb_sco_start\n");
log_info("usb_sco_start");
sco_state_machine_init();
sco_ring_init();
int alt_setting;
if (sco_voice_setting & 0x0020){
// 16-bit PCM
alt_setting = alt_setting_16_bit[sco_num_connections-1];
} else {
// 8-bit PCM or mSBC
alt_setting = alt_setting_8_bit[sco_num_connections-1];
}
// derive iso packet size from alt setting
iso_packet_size = iso_packet_size_for_alt_setting[alt_setting];
log_info("Switching to setting %u on interface 1..", alt_setting);
int r = libusb_set_interface_alt_setting(handle, 1, alt_setting);
if (r < 0) {
log_error("Error setting alternative setting %u for interface 1: %s\n", alt_setting, libusb_error_name(r));
return r;
}
// incoming
int c;
for (c = 0 ; c < SCO_IN_BUFFER_COUNT ; c++) {
sco_in_transfer[c] = libusb_alloc_transfer(NUM_ISO_PACKETS); // isochronous transfers SCO in
if (!sco_in_transfer[c]) {
usb_close();
return LIBUSB_ERROR_NO_MEM;
}
// configure sco_in handlers
libusb_fill_iso_transfer(sco_in_transfer[c], handle, sco_in_addr,
hci_sco_in_buffer[c], NUM_ISO_PACKETS * iso_packet_size, NUM_ISO_PACKETS, async_callback, NULL, 0);
libusb_set_iso_packet_lengths(sco_in_transfer[c], iso_packet_size);
r = libusb_submit_transfer(sco_in_transfer[c]);
if (r) {
log_error("Error submitting isochronous in transfer %d", r);
usb_close();
return r;
}
}
// outgoing
for (c=0; c < SCO_OUT_BUFFER_COUNT ; c++){
sco_out_transfers[c] = libusb_alloc_transfer(NUM_ISO_PACKETS); // 1 isochronous transfers SCO out - up to 3 parts
sco_out_transfers_in_flight[c] = 0;
}
return 0;
}
static void usb_sco_stop(void){
printf("usb_sco_stop\n");
log_info("usb_sco_stop");
sco_shutdown = 1;
libusb_set_debug(NULL, LIBUSB_LOG_LEVEL_ERROR);
int c;
for (c = 0 ; c < SCO_IN_BUFFER_COUNT ; c++) {
libusb_cancel_transfer(sco_in_transfer[c]);
}
for (c = 0; c < SCO_OUT_BUFFER_COUNT ; c++){
if (sco_out_transfers_in_flight[c]) {
libusb_cancel_transfer(sco_out_transfers[c]);
} else {
libusb_free_transfer(sco_out_transfers[c]);
sco_out_transfers[c] = 0;
}
}
// wait until all transfers are completed
int completed = 0;
while (!completed){
struct timeval tv;
memset(&tv, 0, sizeof(struct timeval));
libusb_handle_events_timeout(NULL, &tv);
// check if all done
completed = 1;
// Cancel all synchronous transfer
for (c = 0 ; c < SCO_IN_BUFFER_COUNT ; c++) {
if (sco_in_transfer[c]){
completed = 0;
break;
}
}
if (!completed) continue;
for (c=0; c < SCO_OUT_BUFFER_COUNT ; c++){
if (sco_out_transfers[c]){
completed = 0;
break;
}
}
}
sco_shutdown = 0;
libusb_set_debug(NULL, LIBUSB_LOG_LEVEL_WARNING);
log_info("Switching to setting %u on interface 1..", 0);
int r = libusb_set_interface_alt_setting(handle, 1, 0);
if (r < 0) {
log_error("Error setting alternative setting %u for interface 1: %s", 0, libusb_error_name(r));
return;
}
printf("usb_sco_stop done\n");
}
#endif
static int usb_open(void){
int r;
handle_packet = NULL;
// default endpoint addresses
@ -900,36 +1034,6 @@ static int usb_open(void){
libusb_state = LIB_USB_TRANSFERS_ALLOCATED;
#ifdef ENABLE_SCO_OVER_HCI
// incoming
for (c = 0 ; c < SCO_IN_BUFFER_COUNT ; c++) {
sco_in_transfer[c] = libusb_alloc_transfer(NUM_ISO_PACKETS); // isochronous transfers SCO in
log_info("Alloc iso transfer");
if (!sco_in_transfer[c]) {
usb_close();
return LIBUSB_ERROR_NO_MEM;
}
// configure sco_in handlers
libusb_fill_iso_transfer(sco_in_transfer[c], handle, sco_in_addr,
hci_sco_in_buffer[c], SCO_PACKET_SIZE, NUM_ISO_PACKETS, async_callback, NULL, 0);
libusb_set_iso_packet_lengths(sco_in_transfer[c], ISO_PACKET_SIZE);
r = libusb_submit_transfer(sco_in_transfer[c]);
log_info("Submit iso transfer res = %d", r);
if (r) {
log_error("Error submitting isochronous in transfer %d", r);
usb_close();
return r;
}
}
// outgoing
for (c=0; c < SCO_OUT_BUFFER_COUNT ; c++){
sco_out_transfers[c] = libusb_alloc_transfer(NUM_ISO_PACKETS); // 1 isochronous transfers SCO out - up to 3 parts
sco_out_transfers_in_flight[c] = 0;
}
#endif
for (c = 0 ; c < EVENT_IN_BUFFER_COUNT ; c++) {
// configure event_in handlers
libusb_fill_interrupt_transfer(event_in_transfer[c], handle, event_in_addr,
@ -993,7 +1097,6 @@ static int usb_open(void){
return 0;
}
static int usb_close(void){
int c;
switch (libusb_state){
@ -1035,10 +1138,8 @@ static int usb_close(void){
}
for (c = 0; c < SCO_OUT_BUFFER_COUNT ; c++){
if (sco_out_transfers_in_flight[c]) {
log_info("libusb_cancel_transfer sco_out_transfers[%d]", c);
libusb_cancel_transfer(sco_out_transfers[c]);
} else {
log_info("libusb_free_transfer sco_out_transfers[%d]", c);
libusb_free_transfer(sco_out_transfers[c]);
sco_out_transfers[c] = 0;
}
@ -1197,6 +1298,23 @@ static int usb_send_packet(uint8_t packet_type, uint8_t * packet, int size){
}
}
#ifdef ENABLE_SCO_OVER_HCI
static void usb_set_sco_config(uint16_t voice_setting, int num_connections){
log_info("usb_set_sco_config: voice settings 0x%04x, num connections %u", voice_setting, num_connections);
if (num_connections != sco_num_connections){
sco_voice_setting = voice_setting;
if (sco_num_connections){
usb_sco_stop();
}
sco_num_connections = num_connections;
if (num_connections){
usb_sco_start();
}
}
}
#endif
static void usb_register_packet_handler(void (*handler)(uint8_t packet_type, uint8_t *packet, uint16_t size)){
log_info("registering packet handler");
packet_handler = handler;
@ -1219,6 +1337,9 @@ const hci_transport_t * hci_transport_usb_instance(void) {
hci_transport_usb->register_packet_handler = usb_register_packet_handler;
hci_transport_usb->can_send_packet_now = usb_can_send_packet_now;
hci_transport_usb->send_packet = usb_send_packet;
#ifdef ENABLE_SCO_OVER_HCI
hci_transport_usb->set_sco_config = usb_set_sco_config;
#endif
}
return hci_transport_usb;
}

View File

@ -167,16 +167,27 @@ static WinUsb_GetCurrentFrameNumber_t WinUsb_GetCurrentFrameNumber;
// --> support only a single SCO connection
#define ALT_SETTING (1)
// alt setting for 1-3 connections and 8/16 bit
const int alt_setting_8_bit[] = {1,2,3};
const int alt_setting_16_bit[] = {2,4,5};
// for ALT_SETTING >= 1 and 8-bit channel, we need the following isochronous packets
// One complete SCO packet with 24 frames every 3 frames (== 3 ms)
#define NUM_ISO_PACKETS (3)
// results in 9 bytes per frame
#define ISO_PACKET_SIZE (9)
const uint16_t iso_packet_size_for_alt_setting[] = {
0,
9,
17,
25,
33,
49,
63,
};
// 49 bytes is the max usb packet size for alternate setting 5 (Three 8 kHz 16-bit channels or one 8 kHz 16-bit channel and one 16 kHz 16-bit channel)
// note: alt setting 6 has max packet size of 63 every 7.5 ms = 472.5 bytes / HCI packet, while max SCO packet has 255 byte payload
#define SCO_PACKET_SIZE (NUM_ISO_PACKETS * ISO_PACKET_SIZE)
#define SCO_PACKET_SIZE (49 * NUM_ISO_PACKETS)
#define ISOC_BUFFERS 8
@ -278,6 +289,12 @@ static btstack_data_source_t usb_data_source_sco_out[SCO_RING_BUFFER_COUNT];
static uint8_t sco_ring_buffer[SCO_RING_BUFFER_SIZE];
static int sco_ring_write; // packet idx
// SCO Reconfiguration - pause/resume
static uint16_t sco_voice_setting;
static int sco_num_connections;
static int sco_shutdown;
static uint16_t iso_packet_size;
#endif
#if 0
@ -310,6 +327,30 @@ static void sco_ring_init(void){
static int sco_ring_have_space(void){
return sco_ring_transfers_active < SCO_RING_BUFFER_COUNT;
}
static void usb_sco_register_buffers(void){
BOOL result;
result = WinUsb_RegisterIsochBuffer(usb_interface_1_handle, sco_in_addr, hci_sco_in_buffer, sizeof(hci_sco_in_buffer), &hci_sco_in_buffer_handle);
if (!result) {
log_error("usb_sco_register_buffers: register in buffer failed, error %lu", GetLastError());
}
log_info("hci_sco_in_buffer_handle %p", hci_sco_in_buffer_handle);
result = WinUsb_RegisterIsochBuffer(usb_interface_1_handle, sco_out_addr, sco_ring_buffer, sizeof(sco_ring_buffer), &hci_sco_out_buffer_handle);
if (!result) {
log_error("usb_sco_unregister_buffers: register out buffer failed, error %lu", GetLastError());
}
log_info("hci_sco_out_buffer_handle %p", hci_sco_out_buffer_handle);
}
static void usb_sco_unregister_buffers(void){
if (hci_sco_in_buffer_handle){
WinUsb_UnregisterIsochBuffer(hci_sco_in_buffer_handle);
hci_sco_in_buffer_handle = NULL;
}
if (hci_sco_out_buffer_handle){
WinUsb_UnregisterIsochBuffer(hci_sco_out_buffer_handle);
hci_sco_out_buffer_handle = NULL;
}
}
#endif
static void usb_register_packet_handler(void (*handler)(uint8_t packet_type, uint8_t *packet, uint16_t size)){
@ -340,14 +381,7 @@ static void usb_free_resources(void){
}
#ifdef ENABLE_SCO_OVER_HCI
if (hci_sco_in_buffer_handle){
WinUsb_UnregisterIsochBuffer(hci_sco_in_buffer_handle);
hci_sco_in_buffer_handle = NULL;
}
if (hci_sco_out_buffer_handle){
WinUsb_UnregisterIsochBuffer(hci_sco_out_buffer_handle);
hci_sco_out_buffer_handle = NULL;
}
usb_sco_unregister_buffers();
#endif
}
@ -387,16 +421,21 @@ exit_on_error:
// frame number gets updated
static void usb_submit_sco_in_transfer_at_frame(int i, ULONG * frame_number){
if (sco_shutdown){
log_info("USB SCO Shutdown:: usb_submit_sco_in_transfer_at_frame called");
return;
}
LARGE_INTEGER timestamp;
ULONG current_frame_number;
WinUsb_GetCurrentFrameNumber(usb_interface_0_handle, &current_frame_number, &timestamp);
ULONG frame_before = *frame_number;
BOOL result = WinUsb_ReadIsochPipe(hci_sco_in_buffer_handle, i * SCO_PACKET_SIZE, SCO_PACKET_SIZE,
BOOL result = WinUsb_ReadIsochPipe(hci_sco_in_buffer_handle, i * SCO_PACKET_SIZE, iso_packet_size * NUM_ISO_PACKETS,
frame_number, NUM_ISO_PACKETS, &hci_sco_packet_descriptors[i * NUM_ISO_PACKETS], &usb_overlapped_sco_in[i]);
log_info("WinUsb_ReadIsochPipe #%02u: current %lu, planned %lu - buffer %lu", i, current_frame_number, frame_before, frame_before - current_frame_number);
// log_info("WinUsb_ReadIsochPipe #%02u: current %lu, planned %lu - buffer %lu", i, current_frame_number, frame_before, frame_before - current_frame_number);
if (!result) {
if (GetLastError() == ERROR_IO_PENDING) {
@ -415,13 +454,18 @@ exit_on_error:
static void usb_submit_sco_in_transfer_asap(int i, int continue_stream){
if (sco_shutdown){
log_info("USB SCO Shutdown:: usb_submit_sco_in_transfer_at_frame called");
return;
}
LARGE_INTEGER timestamp;
ULONG current_frame_number;
WinUsb_GetCurrentFrameNumber(usb_interface_0_handle, &current_frame_number, &timestamp);
// log_info("usb_submit_sco_in_transfer[%02u]: current frame %lu", i, current_frame_number);
BOOL result = WinUsb_ReadIsochPipeAsap(hci_sco_in_buffer_handle, i * SCO_PACKET_SIZE, SCO_PACKET_SIZE,
BOOL result = WinUsb_ReadIsochPipeAsap(hci_sco_in_buffer_handle, i * SCO_PACKET_SIZE, iso_packet_size * NUM_ISO_PACKETS,
continue_stream, NUM_ISO_PACKETS, &hci_sco_packet_descriptors[i * NUM_ISO_PACKETS], &usb_overlapped_sco_in[i]);
if (!result) {
@ -530,6 +574,11 @@ static void usb_process_sco_out(btstack_data_source_t *ds, btstack_data_source_
btstack_run_loop_disable_data_source_callbacks(ds, DATA_SOURCE_CALLBACK_WRITE);
if (sco_shutdown){
log_info("USB SCO Shutdown:: usb_process_sco_out called");
return;
}
// get current frame number
ULONG current_frame_number;
LARGE_INTEGER timestamp;
@ -541,11 +590,11 @@ static void usb_process_sco_out(btstack_data_source_t *ds, btstack_data_source_
if (ds == &usb_data_source_sco_out[transfer_index]) break;
}
log_info("usb_process_sco_out[%02u] -- current frame %lu", transfer_index, current_frame_number);
// log_info("usb_process_sco_out[%02u] -- current frame %lu", transfer_index, current_frame_number);
DWORD bytes_transferred;
BOOL ok = WinUsb_GetOverlappedResult(usb_interface_0_handle, &usb_overlapped_sco_out[transfer_index], &bytes_transferred, FALSE);
log_info("usb_process_sco_out_done: #%u result %u, bytes %u, state %u", transfer_index, ok, (int) bytes_transferred, sco_state);
// log_info("usb_process_sco_out_done: #%u result %u, bytes %u, state %u", transfer_index, ok, (int) bytes_transferred, sco_state);
if(!ok){
DWORD err = GetLastError();
if (err == ERROR_IO_INCOMPLETE){
@ -563,11 +612,11 @@ static void usb_process_sco_out(btstack_data_source_t *ds, btstack_data_source_
if (sco_ring_transfers_active){
// update expected and wait for completion
usb_sco_out_expected_transfer = (transfer_index+ 1) % SCO_RING_BUFFER_COUNT;
log_info("usb_process_sco_out_done[%02u]: wait for transfer %02u", transfer_index, usb_sco_out_expected_transfer);
// log_info("usb_process_sco_out_done[%02u]: wait for transfer %02u", transfer_index, usb_sco_out_expected_transfer);
btstack_run_loop_enable_data_source_callbacks(&usb_data_source_sco_out[usb_sco_out_expected_transfer], DATA_SOURCE_CALLBACK_WRITE);
}
log_info("usb_process_sco_out_done: transfers active %u", sco_ring_transfers_active);
// log_info("usb_process_sco_out_done: transfers active %u", sco_ring_transfers_active);
// mark free
if (sco_ring_have_space()) {
@ -580,6 +629,10 @@ static void usb_process_sco_in(btstack_data_source_t *ds, btstack_data_source_c
btstack_run_loop_disable_data_source_callbacks(ds, DATA_SOURCE_CALLBACK_READ);
if (sco_shutdown){
log_info("USB SCO Shutdown: usb_process_sco_out called");
return;
}
// find index
int i;
@ -705,19 +758,20 @@ static BOOL usb_scan_for_bluetooth_endpoints(void) {
sco_out_addr = 0;
sco_in_addr = 0;
// look for SCO pipes on Interface #1, Alt Setting ALT_SETTING
result = WinUsb_QueryInterfaceSettings(usb_interface_1_handle, ALT_SETTING, &usb_interface_descriptor);
// look for SCO pipes on Interface #1, Alt Setting 1
int alt_setting = 1;
result = WinUsb_QueryInterfaceSettings(usb_interface_1_handle, alt_setting, &usb_interface_descriptor);
if (!result) goto exit_on_error;
for (i=0;i<usb_interface_descriptor.bNumEndpoints;i++){
WINUSB_PIPE_INFORMATION_EX pipe;
result = WinUsb_QueryPipeEx(
usb_interface_1_handle,
ALT_SETTING,
alt_setting,
(UCHAR) i,
&pipe);
if (!result) goto exit_on_error;
log_info("Interface #1, Alt #%u, Pipe idx #%u: type %u, id 0x%02x, max packet size %u, interval %u, max bytes per interval %u",
ALT_SETTING, i, pipe.PipeType, pipe.PipeId, pipe.MaximumPacketSize, pipe.Interval, (int) pipe.MaximumBytesPerInterval);
alt_setting, i, pipe.PipeType, pipe.PipeId, pipe.MaximumPacketSize, pipe.Interval, (int) pipe.MaximumBytesPerInterval);
switch (pipe.PipeType){
case USB_ENDPOINT_TYPE_ISOCHRONOUS:
if (pipe.PipeId & 0x80) {
@ -766,6 +820,88 @@ exit_on_error:
return FALSE;
}
#ifdef ENABLE_SCO_OVER_HCI
static int usb_sco_start(void){
printf("usb_sco_start\n");
log_info("usb_sco_start");
sco_shutdown = 0;
sco_state_machine_init();
sco_ring_init();
// calc alt setting
int alt_setting;
if (sco_voice_setting & 0x0020){
// 16-bit PCM
alt_setting = alt_setting_16_bit[sco_num_connections-1];
} else {
// 8-bit PCM or mSBC
alt_setting = alt_setting_8_bit[sco_num_connections-1];
}
log_info("Switching to setting %u on interface 1..", alt_setting);
// WinUsb_SetCurrentAlternateSetting returns TRUE if the operation succeeds.
BOOL result = WinUsb_SetCurrentAlternateSetting(usb_interface_1_handle, alt_setting);
if (!result) goto exit_on_error;
// derive iso packet size from alt setting
iso_packet_size = iso_packet_size_for_alt_setting[alt_setting];
// register isochronous buffer after setting alternate setting
usb_sco_register_buffers();
#ifdef SCHEDULE_SCO_IN_TRANSFERS_MANUALLY
// get current frame number
ULONG current_frame_number;
LARGE_INTEGER timestamp;
WinUsb_GetCurrentFrameNumber(usb_interface_0_handle, &current_frame_number, &timestamp);
// plan for next tranfer
sco_next_transfer_at_frame = current_frame_number + ISOC_BUFFERS * NUM_ISO_PACKETS;
#endif
int i;
for (i=0;i<ISOC_BUFFERS;i++){
#ifdef SCHEDULE_SCO_IN_TRANSFERS_MANUALLY
usb_submit_sco_in_transfer_at_frame(i, &sco_next_transfer_at_frame);
#else
usb_submit_sco_in_transfer_asap(i, 0);
#endif
}
usb_sco_in_expected_transfer = 0;
// only await first transfer to return
btstack_run_loop_enable_data_source_callbacks(&usb_data_source_sco_in[usb_sco_in_expected_transfer], DATA_SOURCE_CALLBACK_READ);
return 1;
exit_on_error:
log_error("usb_sco_start: last error %lu", GetLastError());
usb_free_resources();
return 0;
}
static void usb_sco_stop(void){
printf("usb_sco_stop\n");
log_info("usb_sco_stop");
sco_shutdown = 1;
// abort SCO transfers
WinUsb_AbortPipe(usb_interface_0_handle, sco_in_addr);
WinUsb_AbortPipe(usb_interface_0_handle, sco_out_addr);
// unlock/free SCO buffers
usb_sco_unregister_buffers();
int alt_setting = 0;
log_info("Switching to setting %u on interface 1..", alt_setting);
// WinUsb_SetCurrentAlternateSetting returns TRUE if the operation succeeds.
WinUsb_SetCurrentAlternateSetting(usb_interface_1_handle, alt_setting);
}
#endif
// returns 0 if successful, -1 otherwise
static int usb_try_open_device(const char * device_path){
@ -810,11 +946,6 @@ static int usb_try_open_device(const char * device_path){
result = WinUsb_GetAssociatedInterface(usb_interface_0_handle, 0, &usb_interface_1_handle);
if (!result) goto exit_on_error;
log_info("Claiming interface 1: success");
log_info("Switching to setting %u on interface 1..", ALT_SETTING);
// WinUsb_SetCurrentAlternateSetting returns TRUE if the operation succeeds.
result = WinUsb_SetCurrentAlternateSetting(usb_interface_1_handle, ALT_SETTING);
if (!result) goto exit_on_error;
#endif
result = usb_scan_for_bluetooth_endpoints();
@ -830,14 +961,6 @@ static int usb_try_open_device(const char * device_path){
memset(hci_sco_packet_descriptors, 0, sizeof(hci_sco_packet_descriptors));
log_info("Size of packet descriptors for SCO IN%u", (int) sizeof(hci_sco_packet_descriptors));
result = WinUsb_RegisterIsochBuffer(usb_interface_1_handle, sco_in_addr, hci_sco_in_buffer, ISOC_BUFFERS * SCO_PACKET_SIZE, &hci_sco_in_buffer_handle);
if (!result) goto exit_on_error;
log_info("hci_sco_in_buffer_handle %p", hci_sco_in_buffer_handle);
result = WinUsb_RegisterIsochBuffer(usb_interface_1_handle, sco_out_addr, sco_ring_buffer, sizeof(sco_ring_buffer), &hci_sco_out_buffer_handle);
if (!result) goto exit_on_error;
log_info("hci_sco_out_buffer_handle %p", hci_sco_out_buffer_handle);
// setup async io && btstack handler
memset(&usb_overlapped_sco_in, 0, sizeof(usb_overlapped_sco_in));
for (i=0;i<ISOC_BUFFERS;i++){
@ -886,70 +1009,9 @@ static int usb_try_open_device(const char * device_path){
btstack_run_loop_set_data_source_handler(&usb_data_source_acl_out, &usb_process_acl_out);
btstack_run_loop_add_data_source(&usb_data_source_acl_out);
// submit all incoming transfers
usb_submit_event_in_transfer();
usb_submit_acl_in_transfer();
#ifdef ENABLE_SCO_OVER_HCI
#ifdef SCHEDULE_SCO_IN_TRANSFERS_MANUALLY
// get current frame number
ULONG current_frame_number;
LARGE_INTEGER timestamp;
WinUsb_GetCurrentFrameNumber(usb_interface_0_handle, &current_frame_number, &timestamp);
// plan for next tranfer
sco_next_transfer_at_frame = current_frame_number + ISOC_BUFFERS * NUM_ISO_PACKETS;
#endif
for (i=0;i<ISOC_BUFFERS;i++){
#ifdef SCHEDULE_SCO_IN_TRANSFERS_MANUALLY
usb_submit_sco_in_transfer_at_frame(i, &sco_next_transfer_at_frame);
#else
usb_submit_sco_in_transfer_asap(i, 0);
#endif
}
usb_sco_in_expected_transfer = 0;
// only await first transfer to return
btstack_run_loop_enable_data_source_callbacks(&usb_data_source_sco_in[usb_sco_in_expected_transfer], DATA_SOURCE_CALLBACK_READ);
#if 0
// AUTO_CLEAR_STALL, RAW_IO, IGNORE_SHORT_PACKETS is not callable for ISO EP
uint8_t value = 0;
ULONG value_len = 1;
result = WinUsb_GetPipePolicy(usb_interface_0_handle, event_in_addr, IGNORE_SHORT_PACKETS, &value_len, &value);
if (!result) goto exit_on_error;
log_info("IGNORE_SHORT_PACKETS = %u", value);
uint8_t value_on = 0;
result = WinUsb_SetPipePolicy(usb_interface_1_handle, sco_in_addr, IGNORE_SHORT_PACKETS, sizeof(value_on), &value_on);
if (!result) goto exit_on_error;
#endif
#if 0
while (1){
//
DWORD bytes_transferred;
BOOL ok = WinUsb_GetOverlappedResult(usb_interface_0_handle, &usb_overlapped_sco_in[usb_sco_in_expected_transfer], &bytes_transferred, TRUE);
// WinUsb_GetCurrentFrameNumber(usb_interface_0_handle, &current_frame_number, &timestamp);
// log_info("WinUsb_GetOverlappedResult[%02u]: ok = %u, current frame %lu", usb_sco_in_expected_transfer, ok, current_frame_number);
if (!ok){
log_error("WinUsb_GetOverlappedResult res %x", (int) GetLastError());
// return 0;
}
// update expected and wait for completion
usb_sco_in_expected_transfer = (usb_sco_in_expected_transfer + 1) % ISOC_BUFFERS;
usb_submit_sco_in_transfer_at_frame(usb_sco_in_expected_transfer, &sco_next_transfer_at_frame);
}
#endif
#endif
// submit all incoming transfers
usb_submit_event_in_transfer();
usb_submit_acl_in_transfer();
return 1;
exit_on_error:
@ -1131,8 +1193,7 @@ static int usb_close(void){
WinUsb_AbortPipe(usb_interface_0_handle, acl_in_addr);
WinUsb_AbortPipe(usb_interface_0_handle, acl_out_addr);
#ifdef ENABLE_SCO_OVER_HCI
WinUsb_AbortPipe(usb_interface_0_handle, sco_in_addr);
WinUsb_AbortPipe(usb_interface_0_handle, sco_out_addr);
usb_sco_stop();
#endif
usb_acl_out_active = 0;
@ -1232,7 +1293,7 @@ static int usb_send_sco_packet(uint8_t *packet, int size){
// setup transfer
int continue_stream = sco_ring_transfers_active > 0;
BOOL ok = WinUsb_WriteIsochPipeAsap(hci_sco_out_buffer_handle, transfer_index * SCO_PACKET_SIZE, size, continue_stream, &usb_overlapped_sco_out[transfer_index]);
log_info("usb_send_sco_packet: using slot #%02u, current frame %lu, continue stream %u, ok %u", transfer_index, current_frame_number, continue_stream, ok);
// log_info("usb_send_sco_packet: using slot #%02u, current frame %lu, continue stream %u, ok %u", transfer_index, current_frame_number, continue_stream, ok);
if (!ok) {
if (GetLastError() != ERROR_IO_PENDING) goto exit_on_error;
}
@ -1251,7 +1312,7 @@ static int usb_send_sco_packet(uint8_t *packet, int size){
uint8_t event[] = { HCI_EVENT_TRANSPORT_PACKET_SENT, 0};
packet_handler(HCI_EVENT_PACKET, &event[0], sizeof(event));
log_info("usb_send_sco_packet: transfers active %u", sco_ring_transfers_active);
// log_info("usb_send_sco_packet: transfers active %u", sco_ring_transfers_active);
// and if we have more space for SCO packets
if (sco_ring_have_space()) {
@ -1281,6 +1342,23 @@ static int usb_send_packet(uint8_t packet_type, uint8_t * packet, int size){
}
}
#ifdef ENABLE_SCO_OVER_HCI
static void usb_set_sco_config(uint16_t voice_setting, int num_connections){
log_info("usb_set_sco_config: voice settings 0x%04x, num connections %u", voice_setting, num_connections);
if (num_connections != sco_num_connections){
sco_voice_setting = voice_setting;
if (sco_num_connections){
usb_sco_stop();
}
sco_num_connections = num_connections;
if (num_connections){
usb_sco_start();
}
}
}
#endif
// get usb singleton
static const hci_transport_t hci_transport_usb = {
/* const char * name; */ "H2_WINUSB",
@ -1292,6 +1370,7 @@ static const hci_transport_t hci_transport_usb = {
/* int (*send_packet)(...); */ &usb_send_packet,
/* int (*set_baudrate)(uint32_t baudrate); */ NULL,
/* void (*reset_link)(void); */ NULL,
/* void (*set_sco_config)(uint16_t voice_setting, int num_connections); */ usb_set_sco_config,
};
const hci_transport_t * hci_transport_usb_instance(void) {

View File

@ -42,8 +42,10 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "btstack_ring_buffer.h"
#include "btstack_util.h"
#define ERROR_CODE_MEMORY_CAPACITY_EXCEEDED 0x07
@ -79,15 +81,27 @@ int btstack_ring_buffer_write(btstack_ring_buffer_t * ring_buffer, uint8_t * dat
if (btstack_ring_buffer_bytes_free(ring_buffer) < data_length){
return ERROR_CODE_MEMORY_CAPACITY_EXCEEDED;
}
int count = 0;
while (count < data_length){
if (ring_buffer->last_written_index < ring_buffer->size - 1){
ring_buffer->last_written_index++;
} else {
ring_buffer->last_written_index = 0;
}
ring_buffer->storage[ring_buffer->last_written_index] = data[count++];
// copy first chunk
unsigned int bytes_until_end = ring_buffer->size - ring_buffer->last_written_index;
unsigned int bytes_to_copy = btstack_min(bytes_until_end, data_length);
memcpy(&ring_buffer->storage[ring_buffer->last_written_index], data, bytes_to_copy);
data_length -= bytes_to_copy;
data += bytes_to_copy;
// update last written index
ring_buffer->last_written_index += bytes_to_copy;
if (ring_buffer->last_written_index == ring_buffer->size){
ring_buffer->last_written_index = 0;
}
// copy second chunk
if (data_length) {
memcpy(&ring_buffer->storage[0], data, data_length);
ring_buffer->last_written_index += data_length;
}
// mark buffer as full
if (ring_buffer->last_written_index == ring_buffer->last_read_index){
ring_buffer->full = 1;
}
@ -96,20 +110,30 @@ int btstack_ring_buffer_write(btstack_ring_buffer_t * ring_buffer, uint8_t * dat
// fetch data_length bytes from ring buffer
void btstack_ring_buffer_read(btstack_ring_buffer_t * ring_buffer, uint8_t * data, uint32_t data_length, uint32_t * number_of_bytes_read){
uint32_t count = 0;
while (count < data_length && btstack_ring_buffer_bytes_available(ring_buffer)){
if (ring_buffer->last_read_index < ring_buffer->last_written_index ) {
ring_buffer->last_read_index++;
} else {
if (ring_buffer->last_read_index < ring_buffer->size - 1){
ring_buffer->last_read_index++;
} else {
ring_buffer->last_read_index = 0;
}
}
ring_buffer->full = 0;
data[count++] = ring_buffer->storage[ring_buffer->last_read_index];
// limit data to get and report
data_length = btstack_min(data_length, btstack_ring_buffer_bytes_available(ring_buffer));
*number_of_bytes_read = data_length;
// copy first chunk
unsigned int bytes_until_end = ring_buffer->size - ring_buffer->last_read_index;
unsigned int bytes_to_copy = btstack_min(bytes_until_end, data_length);
memcpy(data, &ring_buffer->storage[ring_buffer->last_read_index], bytes_to_copy);
data_length -= bytes_to_copy;
data += bytes_to_copy;
// update last read index
ring_buffer->last_read_index += bytes_to_copy;
if (ring_buffer->last_read_index == ring_buffer->size){
ring_buffer->last_read_index = 0;
}
*number_of_bytes_read = count;
// copy second chunk
if (data_length) {
memcpy(data, &ring_buffer->storage[0], data_length);
ring_buffer->last_read_index += data_length;
}
// clear full flag
ring_buffer->full = 0;
}

View File

@ -49,11 +49,13 @@
#include "btstack_cvsd_plc.h"
#include "btstack_debug.h"
#define SAMPLE_FORMAT int16_t
static float rcos[CVSD_OLAL] = {
0.99148655,0.96623611,0.92510857,0.86950446,
0.80131732,0.72286918,0.63683150,0.54613418,
0.45386582,0.36316850,0.27713082,0.19868268,
0.13049554,0.07489143,0.03376389,0.00851345};
0.99148655f,0.96623611f,0.92510857f,0.86950446f,
0.80131732f,0.72286918f,0.63683150f,0.54613418f,
0.45386582f,0.36316850f,0.27713082f,0.19868268f,
0.13049554f,0.07489143f,0.03376389f,0.00851345f};
// taken from http://www.codeproject.com/Articles/69941/Best-Square-Root-Method-Algorithm-Function-Precisi
// Algorithm: Babylonian Method + some manipulations on IEEE 32 bit floating point representation
@ -79,7 +81,7 @@ static float absolute(float x){
return x;
}
static float CrossCorrelation(int8_t *x, int8_t *y){
static float CrossCorrelation(SAMPLE_FORMAT *x, SAMPLE_FORMAT *y){
float num = 0;
float den = 0;
float x2 = 0;
@ -94,7 +96,7 @@ static float CrossCorrelation(int8_t *x, int8_t *y){
return num/den;
}
static int PatternMatch(int8_t *y){
static int PatternMatch(SAMPLE_FORMAT *y){
float maxCn = -999999.0; // large negative number
int bestmatch = 0;
float Cn;
@ -109,7 +111,7 @@ static int PatternMatch(int8_t *y){
return bestmatch;
}
static float AmplitudeMatch(int8_t *y, int8_t bestmatch) {
static float AmplitudeMatch(SAMPLE_FORMAT *y, SAMPLE_FORMAT bestmatch) {
int i;
float sumx = 0;
float sumy = 0.000001f;
@ -126,19 +128,18 @@ static float AmplitudeMatch(int8_t *y, int8_t bestmatch) {
return sf;
}
static int8_t crop_to_int8(float val){
static SAMPLE_FORMAT crop_sample(float val){
float croped_val = val;
if (croped_val > 127.0) croped_val= 127.0;
if (croped_val < -128.0) croped_val=-128.0;
return (int8_t) croped_val;
if (croped_val > 32767.0) croped_val= 32767.0;
if (croped_val < -32768.0) croped_val=-32768.0;
return (SAMPLE_FORMAT) croped_val;
}
void btstack_cvsd_plc_init(btstack_cvsd_plc_state_t *plc_state){
memset(plc_state, 0, sizeof(btstack_cvsd_plc_state_t));
}
void btstack_cvsd_plc_bad_frame(btstack_cvsd_plc_state_t *plc_state, int8_t *out){
void btstack_cvsd_plc_bad_frame(btstack_cvsd_plc_state_t *plc_state, SAMPLE_FORMAT *out){
float val;
int i = 0;
float sf = 1;
@ -154,19 +155,19 @@ void btstack_cvsd_plc_bad_frame(btstack_cvsd_plc_state_t *plc_state, int8_t *out
sf = AmplitudeMatch(plc_state->hist, plc_state->bestlag);
for (i=0;i<CVSD_OLAL;i++){
val = sf*plc_state->hist[plc_state->bestlag+i];
plc_state->hist[CVSD_LHIST+i] = crop_to_int8(val);
plc_state->hist[CVSD_LHIST+i] = crop_sample(val);
}
for (;i<CVSD_FS;i++){
val = sf*plc_state->hist[plc_state->bestlag+i];
plc_state->hist[CVSD_LHIST+i] = crop_to_int8(val);
plc_state->hist[CVSD_LHIST+i] = crop_sample(val);
}
for (;i<CVSD_FS+CVSD_OLAL;i++){
float left = sf*plc_state->hist[plc_state->bestlag+i];
float right = plc_state->hist[plc_state->bestlag+i];
val = left*rcos[i-CVSD_FS] + right*rcos[CVSD_OLAL-1-i+CVSD_FS];
plc_state->hist[CVSD_LHIST+i] = crop_to_int8(val);
plc_state->hist[CVSD_LHIST+i] = crop_sample(val);
}
for (;i<CVSD_FS+CVSD_RT+CVSD_OLAL;i++){
@ -187,7 +188,7 @@ void btstack_cvsd_plc_bad_frame(btstack_cvsd_plc_state_t *plc_state, int8_t *out
}
}
void btstack_cvsd_plc_good_frame(btstack_cvsd_plc_state_t *plc_state, int8_t *in, int8_t *out){
void btstack_cvsd_plc_good_frame(btstack_cvsd_plc_state_t *plc_state, SAMPLE_FORMAT *in, SAMPLE_FORMAT *out){
float val;
int i = 0;
if (plc_state->nbf>0){
@ -199,7 +200,7 @@ void btstack_cvsd_plc_good_frame(btstack_cvsd_plc_state_t *plc_state, int8_t *in
float left = plc_state->hist[CVSD_LHIST+i];
float right = in[i];
val = left * rcos[i-CVSD_RT] + right *rcos[CVSD_OLAL+CVSD_RT-1-i];
out[i] = (int8_t)val;
out[i] = (SAMPLE_FORMAT)val;
}
}
@ -217,7 +218,7 @@ void btstack_cvsd_plc_good_frame(btstack_cvsd_plc_state_t *plc_state, int8_t *in
plc_state->nbf=0;
}
static int count_equal_bytes(int8_t * packet, uint16_t size){
static int count_equal_samples(SAMPLE_FORMAT * packet, uint16_t size){
int count = 0;
int temp_count = 1;
int i;
@ -237,13 +238,15 @@ static int count_equal_bytes(int8_t * packet, uint16_t size){
return count;
}
static int bad_frame(int8_t * frame, uint16_t size){
return count_equal_bytes(frame, size) > 20;
// @assumption frame len 24 samples
static int bad_frame(SAMPLE_FORMAT * frame, uint16_t size){
return count_equal_samples(frame, size) > 20;
}
void btstack_cvsd_plc_process_data(btstack_cvsd_plc_state_t * state, int8_t * in, uint16_t size, int8_t * out){
void btstack_cvsd_plc_process_data(btstack_cvsd_plc_state_t * state, SAMPLE_FORMAT * in, uint16_t size, SAMPLE_FORMAT * out){
if (size != 24){
log_error("btstack_cvsd_plc_process_data: audio frame size is incorrect. Expected %d, got %d", CVSD_FS, size);
return;
}
state->frame_count++;
if (bad_frame(in,size)){
@ -263,27 +266,6 @@ void btstack_cvsd_plc_process_data(btstack_cvsd_plc_state_t * state, int8_t * in
}
}
void btstack_cvsd_plc_mark_bad_frame(btstack_cvsd_plc_state_t * state, int8_t * in, uint16_t size, int8_t * out){
if (size != 24){
log_error("btstack_cvsd_plc_mark_bad_frame: audio frame size is incorrect. Expected %d, got %d", CVSD_FS, size);
}
state->frame_count++;
if (bad_frame(in,size)){
memcpy(out, in, size);
if (state->good_frames_nr > CVSD_LHIST/CVSD_FS){
memset(out, 50, size);
state->bad_frames_nr++;
}
} else {
memcpy(out, in, size);
state->good_frames_nr++;
if (state->good_frames_nr == 1){
log_info("First good frame at index %d\n", state->frame_count-1);
}
}
}
void btstack_cvsd_dump_statistics(btstack_cvsd_plc_state_t * state){
log_info("Good frames: %d\n", state->good_frames_nr);
log_info("Bad frames: %d\n", state->bad_frames_nr);

View File

@ -58,7 +58,7 @@ extern "C" {
/* PLC State Information */
typedef struct cvsd_plc_state {
int8_t hist[CVSD_LHIST+CVSD_FS+CVSD_RT+CVSD_OLAL];
int16_t hist[CVSD_LHIST+CVSD_FS+CVSD_RT+CVSD_OLAL];
int16_t bestlag;
int nbf;
@ -69,11 +69,9 @@ typedef struct cvsd_plc_state {
} btstack_cvsd_plc_state_t;
void btstack_cvsd_plc_init(btstack_cvsd_plc_state_t *plc_state);
void btstack_cvsd_plc_bad_frame(btstack_cvsd_plc_state_t *plc_state, int8_t *out);
void btstack_cvsd_plc_good_frame(btstack_cvsd_plc_state_t *plc_state, int8_t *in, int8_t *out);
uint8_t * btstack_cvsd_plc_zero_signal_frame(void);
void btstack_cvsd_plc_process_data(btstack_cvsd_plc_state_t * state, int8_t * in, uint16_t size, int8_t * out);
void btstack_cvsd_plc_mark_bad_frame(btstack_cvsd_plc_state_t * state, int8_t * in, uint16_t size, int8_t * out);
void btstack_cvsd_plc_bad_frame(btstack_cvsd_plc_state_t *plc_state, int16_t *out);
void btstack_cvsd_plc_good_frame(btstack_cvsd_plc_state_t *plc_state, int16_t *in, int16_t *out);
void btstack_cvsd_plc_process_data(btstack_cvsd_plc_state_t * state, int16_t * in, uint16_t size, int16_t * out);
void btstack_cvsd_dump_statistics(btstack_cvsd_plc_state_t * state);
#if defined __cplusplus

View File

@ -47,6 +47,8 @@
#include "btstack_sbc_plc.h"
#define SAMPLE_FORMAT int16_t
static uint8_t indices0[] = { 0xad, 0x00, 0x00, 0xc5, 0x00, 0x00, 0x00, 0x00, 0x77, 0x6d,
0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6d, 0xdd, 0xb6, 0xdb, 0x77, 0x6d,
0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6d, 0xdd, 0xb6, 0xdb, 0x77, 0x6d,
@ -84,7 +86,7 @@ static float absolute(float x){
return x;
}
static float CrossCorrelation(int16_t *x, int16_t *y){
static float CrossCorrelation(SAMPLE_FORMAT *x, SAMPLE_FORMAT *y){
float num = 0;
float den = 0;
float x2 = 0;
@ -99,13 +101,13 @@ static float CrossCorrelation(int16_t *x, int16_t *y){
return num/den;
}
static int PatternMatch(int16_t *y){
float maxCn = -999999.0; /* large negative number */
static int PatternMatch(SAMPLE_FORMAT *y){
float maxCn = -999999.0; // large negative number
int bestmatch = 0;
float Cn;
int n;
for (n=0;n<SBC_N;n++){
Cn = CrossCorrelation(&y[SBC_LHIST-SBC_M] /* x */, &y[n]);
Cn = CrossCorrelation(&y[SBC_LHIST-SBC_M], &y[n]);
if (Cn>maxCn){
bestmatch=n;
maxCn = Cn;
@ -114,8 +116,7 @@ static int PatternMatch(int16_t *y){
return bestmatch;
}
static float AmplitudeMatch(int16_t *y, int16_t bestmatch) {
static float AmplitudeMatch(SAMPLE_FORMAT *y, SAMPLE_FORMAT bestmatch) {
int i;
float sumx = 0;
float sumy = 0.000001f;
@ -126,17 +127,17 @@ static float AmplitudeMatch(int16_t *y, int16_t bestmatch) {
sumy += absolute(y[bestmatch+i]);
}
sf = sumx/sumy;
/* This is not in the paper, but limit the scaling factor to something reasonable to avoid creating artifacts */
// This is not in the paper, but limit the scaling factor to something reasonable to avoid creating artifacts
if (sf<0.75f) sf=0.75f;
if (sf>1.2f) sf=1.2f;
return sf;
}
static int16_t crop_to_int16(float val){
static SAMPLE_FORMAT crop_sample(float val){
float croped_val = val;
if (croped_val > 32767.0) croped_val= 32767.0;
if (croped_val < -32768.0) croped_val=-32768.0;
return (int16_t) croped_val;
return (SAMPLE_FORMAT) croped_val;
}
uint8_t * btstack_sbc_plc_zero_signal_frame(void){
@ -149,46 +150,44 @@ void btstack_sbc_plc_init(btstack_sbc_plc_state_t *plc_state){
memset(plc_state->hist,0,sizeof(plc_state->hist));
}
void btstack_sbc_plc_bad_frame(btstack_sbc_plc_state_t *plc_state, int16_t *ZIRbuf, int16_t *out){
void btstack_sbc_plc_bad_frame(btstack_sbc_plc_state_t *plc_state, SAMPLE_FORMAT *ZIRbuf, SAMPLE_FORMAT *out){
float val;
int i = 0;
float sf = 1;
plc_state->nbf++;
if (plc_state->nbf==1){
/* Perform pattern matching to find where to replicate */
// Perform pattern matching to find where to replicate
plc_state->bestlag = PatternMatch(plc_state->hist);
/* the replication begins after the template match */
// the replication begins after the template match
plc_state->bestlag += SBC_M;
/* Compute Scale Factor to Match Amplitude of Substitution Packet to that of Preceding Packet */
// Compute Scale Factor to Match Amplitude of Substitution Packet to that of Preceding Packet
sf = AmplitudeMatch(plc_state->hist, plc_state->bestlag);
for (i=0;i<SBC_OLAL;i++){
float left = ZIRbuf[i];
float right = sf*plc_state->hist[plc_state->bestlag+i];
val = left*rcos[i] + right*rcos[SBC_OLAL-1-i];
plc_state->hist[SBC_LHIST+i] = crop_to_int16(val);
plc_state->hist[SBC_LHIST+i] = crop_sample(val);
}
for (;i<SBC_FS;i++){
val = sf*plc_state->hist[plc_state->bestlag+i];
plc_state->hist[SBC_LHIST+i] = crop_to_int16(val);
plc_state->hist[SBC_LHIST+i] = crop_sample(val);
}
for (;i<SBC_FS+SBC_OLAL;i++){
float left = sf*plc_state->hist[plc_state->bestlag+i];
float right = plc_state->hist[plc_state->bestlag+i];
val = left*rcos[i-SBC_FS]+right*rcos[SBC_FS+SBC_OLAL-1-i];
plc_state->hist[SBC_LHIST+i] = crop_to_int16(val);
val = left*rcos[i-SBC_FS]+right*rcos[SBC_OLAL-1-i+SBC_FS];
plc_state->hist[SBC_LHIST+i] = crop_sample(val);
}
for (;i<SBC_FS+SBC_RT+SBC_OLAL;i++){
plc_state->hist[SBC_LHIST+i] = plc_state->hist[plc_state->bestlag+i];
}
} else {
for (i=0;i<SBC_FS+SBC_RT+SBC_OLAL;i++){
for (;i<SBC_FS+SBC_RT+SBC_OLAL;i++){
plc_state->hist[SBC_LHIST+i] = plc_state->hist[plc_state->bestlag+i];
}
}
@ -196,14 +195,13 @@ void btstack_sbc_plc_bad_frame(btstack_sbc_plc_state_t *plc_state, int16_t *ZIRb
out[i] = plc_state->hist[SBC_LHIST+i];
}
/* shift the history buffer */
// shift the history buffer
for (i=0;i<SBC_LHIST+SBC_RT+SBC_OLAL;i++){
plc_state->hist[i] = plc_state->hist[i+SBC_FS];
}
}
void btstack_sbc_plc_good_frame(btstack_sbc_plc_state_t *plc_state, int16_t *in, int16_t *out){
void btstack_sbc_plc_good_frame(btstack_sbc_plc_state_t *plc_state, SAMPLE_FORMAT *in, SAMPLE_FORMAT *out){
float val;
int i = 0;
if (plc_state->nbf>0){
@ -211,22 +209,22 @@ void btstack_sbc_plc_good_frame(btstack_sbc_plc_state_t *plc_state, int16_t *in,
out[i] = plc_state->hist[SBC_LHIST+i];
}
for (;i<SBC_RT+SBC_OLAL;i++){
for (i = SBC_RT;i<SBC_RT+SBC_OLAL;i++){
float left = plc_state->hist[SBC_LHIST+i];
float right = in[i];
val = left*rcos[i-SBC_RT] + right*rcos[SBC_OLAL-1-i+SBC_RT];
out[i] = (int16_t)val;
val = left*rcos[i-SBC_RT] + right*rcos[SBC_OLAL+SBC_RT-1-i];
out[i] = (SAMPLE_FORMAT)val;
}
}
for (;i<SBC_FS;i++){
out[i] = in[i];
}
/*Copy the output to the history buffer */
// Copy the output to the history buffer
for (i=0;i<SBC_FS;i++){
plc_state->hist[SBC_LHIST+i] = out[i];
}
/* shift the history buffer */
// shift the history buffer
for (i=0;i<SBC_LHIST;i++){
plc_state->hist[i] = plc_state->hist[i+SBC_FS];
}

View File

@ -539,7 +539,7 @@ void hfp_handle_hci_event(uint8_t packet_type, uint16_t channel, uint8_t *packet
hfp_connection_t * hfp_connection = NULL;
uint8_t status;
log_info("AG packet_handler type %u, event type %x, size %u", packet_type, hci_event_packet_get_type(packet), size);
log_debug("HFP packet_handler type %u, event type %x, size %u", packet_type, hci_event_packet_get_type(packet), size);
switch (hci_event_packet_get_type(packet)) {
case HCI_EVENT_CONNECTION_REQUEST:

View File

@ -650,7 +650,7 @@ static void hfp_ag_slc_established(hfp_connection_t * hfp_connection){
}
static int hfp_ag_run_for_context_service_level_connection(hfp_connection_t * hfp_connection){
log_info("hfp_ag_run_for_context_service_level_connection state %u, command %u", hfp_connection->state, hfp_connection->command);
// log_info("hfp_ag_run_for_context_service_level_connection state %u, command %u", hfp_connection->state, hfp_connection->command);
if (hfp_connection->state >= HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED) return 0;
int done = 0;
switch(hfp_connection->command){
@ -1641,7 +1641,7 @@ static void hfp_ag_send_call_status(hfp_connection_t * hfp_connection, int call_
static void hfp_run_for_context(hfp_connection_t *hfp_connection){
log_info("hfp_run_for_context %p", hfp_connection);
// log_info("hfp_run_for_context %p", hfp_connection);
if (!hfp_connection) return;

View File

@ -219,6 +219,20 @@ hci_connection_t * hci_connection_for_bd_addr_and_type(bd_addr_t addr, bd_addr_
#ifdef ENABLE_CLASSIC
#ifdef ENABLE_SCO_OVER_HCI
static int hci_number_sco_connections(void){
int connections = 0;
btstack_linked_list_iterator_t it;
btstack_linked_list_iterator_init(&it, &hci_stack->connections);
while (btstack_linked_list_iterator_has_next(&it)){
hci_connection_t * connection = (hci_connection_t *) btstack_linked_list_iterator_next(&it);
if (connection->address_type != BD_ADDR_TYPE_SCO) continue;
connections++;
}
return connections;
}
#endif
static void hci_connection_timeout_handler(btstack_timer_source_t *timer){
hci_connection_t * connection = (hci_connection_t *) btstack_run_loop_get_timer_context(timer);
#ifdef HAVE_EMBEDDED_TICK
@ -749,6 +763,10 @@ static void acl_handler(uint8_t *packet, int size){
static void hci_shutdown_connection(hci_connection_t *conn){
log_info("Connection closed: handle 0x%x, %s", conn->con_handle, bd_addr_to_str(conn->address));
#ifdef ENABLE_SCO_OVER_HCI
int addr_type = conn->address_type;
#endif
btstack_run_loop_remove_timer(&conn->timeout);
btstack_linked_list_remove(&hci_stack->connections, (btstack_linked_item_t *) conn);
@ -756,6 +774,13 @@ static void hci_shutdown_connection(hci_connection_t *conn){
// now it's gone
hci_emit_nr_connections_changed();
#ifdef ENABLE_SCO_OVER_HCI
// update SCO
if (addr_type == BD_ADDR_TYPE_SCO && hci_stack->hci_transport && hci_stack->hci_transport->set_sco_config){
hci_stack->hci_transport->set_sco_config(hci_stack->sco_voice_setting_active, hci_number_sco_connections());
}
#endif
}
#ifdef ENABLE_CLASSIC
@ -1157,6 +1182,13 @@ static void hci_initializing_run(void){
hci_stack->substate = HCI_INIT_W4_WRITE_DEFAULT_ERRONEOUS_DATA_REPORTING;
hci_send_cmd(&hci_write_default_erroneous_data_reporting, 1);
break;
// only sent if ENABLE_SCO_OVER_HCI and manufacturer is Broadcom
case HCI_INIT_BCM_WRITE_SCO_PCM_INT:
hci_stack->substate = HCI_INIT_W4_BCM_WRITE_SCO_PCM_INT;
log_info("BCM: Route SCO data via HCI transport");
hci_send_cmd(&hci_bcm_write_sco_pcm_int, 1, 0, 0, 0, 0);
break;
#endif
#ifdef ENABLE_BLE
// LE INIT
@ -1402,14 +1434,15 @@ static void hci_initializing_event_handler(uint8_t * packet, uint16_t size){
case HCI_INIT_W4_SET_EVENT_MASK:
// skip Classic init commands for LE only chipsets
if (!hci_classic_supported()){
#ifdef ENABLE_BLE
if (hci_le_supported()){
hci_stack->substate = HCI_INIT_LE_READ_BUFFER_SIZE; // skip all classic command
return;
} else {
log_error("Neither BR/EDR nor LE supported");
hci_init_done();
return;
}
#endif
log_error("Neither BR/EDR nor LE supported");
hci_init_done();
return;
}
if (!gap_ssp_supported()){
hci_stack->substate = HCI_INIT_WRITE_PAGE_TIMEOUT;
@ -1447,6 +1480,12 @@ static void hci_initializing_event_handler(uint8_t * packet, uint16_t size){
// explicit fall through to reduce repetitions
case HCI_INIT_W4_WRITE_DEFAULT_ERRONEOUS_DATA_REPORTING:
// skip bcm set sco pcm config on non-Broadcom chipsets
if (hci_stack->manufacturer == COMPANY_ID_BROADCOM_CORPORATION) break;
hci_stack->substate = HCI_INIT_W4_BCM_WRITE_SCO_PCM_INT;
// explicit fall through to reduce repetitions
case HCI_INIT_W4_BCM_WRITE_SCO_PCM_INT:
if (!hci_le_supported()){
// SKIP LE init for Classic only configuration
hci_init_done();
@ -1724,6 +1763,13 @@ static void event_handler(uint8_t *packet, int size){
}
conn->state = OPEN;
conn->con_handle = little_endian_read_16(packet, 3);
#ifdef ENABLE_SCO_OVER_HCI
// update SCO
if (conn->address_type == BD_ADDR_TYPE_SCO && hci_stack->hci_transport && hci_stack->hci_transport->set_sco_config){
hci_stack->hci_transport->set_sco_config(hci_stack->sco_voice_setting_active, hci_number_sco_connections());
}
#endif
break;
case HCI_EVENT_READ_REMOTE_SUPPORTED_FEATURES_COMPLETE:
@ -2175,8 +2221,8 @@ void hci_init(const hci_transport_t *transport, const void *config){
hci_stack->ssp_authentication_requirement = SSP_IO_AUTHREQ_MITM_PROTECTION_NOT_REQUIRED_GENERAL_BONDING;
hci_stack->ssp_auto_accept = 1;
// voice setting - signed 8 bit pcm data with CVSD over the air
hci_stack->sco_voice_setting = 0x40;
// voice setting - signed 16 bit pcm data with CVSD over the air
hci_stack->sco_voice_setting = 0x60;
hci_state_reset();
}
@ -3074,6 +3120,19 @@ int hci_send_cmd_packet(uint8_t *packet, int size){
connectionClearAuthenticationFlags(conn, SSP_PAIRING_ACTIVE);
}
}
#ifdef ENABLE_SCO_OVER_HCI
// setup_synchronous_connection? Voice setting at offset 22
if (IS_COMMAND(packet, hci_setup_synchronous_connection)){
// TODO: compare to current setting if sco connection already active
hci_stack->sco_voice_setting_active = little_endian_read_16(packet, 15);
}
// accept_synchronus_connection? Voice setting at offset 18
if (IS_COMMAND(packet, hci_accept_synchronous_connection)){
// TODO: compare to current setting if sco connection already active
hci_stack->sco_voice_setting_active = little_endian_read_16(packet, 19);
}
#endif
#endif
#ifdef ENABLE_BLE

View File

@ -550,6 +550,10 @@ typedef enum hci_init_state{
HCI_INIT_WRITE_DEFAULT_ERRONEOUS_DATA_REPORTING,
HCI_INIT_W4_WRITE_DEFAULT_ERRONEOUS_DATA_REPORTING,
// SCO over HCI Broadcom
HCI_INIT_BCM_WRITE_SCO_PCM_INT,
HCI_INIT_W4_BCM_WRITE_SCO_PCM_INT,
#ifdef ENABLE_BLE
HCI_INIT_LE_READ_BUFFER_SIZE,
HCI_INIT_W4_LE_READ_BUFFER_SIZE,
@ -694,6 +698,7 @@ typedef struct {
uint8_t new_scan_enable_value;
uint16_t sco_voice_setting;
uint16_t sco_voice_setting_active;
uint8_t loopback_mode;

View File

@ -1061,3 +1061,18 @@ OPCODE(OGF_LE_CONTROLLER, 0x26), "QQ"
};
#endif
// Broadcom / Cypress specific HCI commands
/**
* @brief Configure SCO Routing (BCM)
* @param sco_routing is 0 for PCM, 1 for Transport, 2 for Codec and 3 for I2S
* @param pcm_interface_rate is 0 for 128KBps, 1 for 256 KBps, 2 for 512KBps, 3 for 1024KBps, and 4 for 2048Kbps
* @param frame_type is 0 for short and 1 for long
* @param sync_mode is 0 for slave and 1 for master
* @param clock_mode is 0 for slabe and 1 for master
*/
const hci_cmd_t hci_bcm_write_sco_pcm_int = {
OPCODE(0x3f, 0x1c), "11111"
// return: status
};

View File

@ -187,6 +187,10 @@ extern const hci_cmd_t hci_le_set_scan_response_data;
extern const hci_cmd_t hci_le_start_encryption;
extern const hci_cmd_t hci_le_test_end;
extern const hci_cmd_t hci_le_transmitter_test;
// Broadcom / Cypress specific HCI commands
extern const hci_cmd_t hci_bcm_write_sco_pcm_int;
/**
* construct HCI Command based on template
*

View File

@ -104,6 +104,11 @@ typedef struct {
*/
void (*reset_link)(void);
/**
* extension for USB transport implementations: config SCO connections
*/
void (*set_sco_config)(uint16_t voice_setting, int num_connections);
} hci_transport_t;
typedef enum {

View File

@ -549,6 +549,7 @@ static const hci_transport_t hci_transport_h4 = {
/* int (*send_packet)(...); */ &hci_transport_h4_send_packet,
/* int (*set_baudrate)(uint32_t baudrate); */ &hci_transport_h4_set_baudrate,
/* void (*reset_link)(void); */ NULL,
/* void (*set_sco_config)(uint16_t voice_setting, int num_connections); */ NULL,
};
// configure and return h4 singleton

View File

@ -773,6 +773,7 @@ static const hci_transport_t hci_transport_h5 = {
/* int (*send_packet)(...); */ &hci_transport_h5_send_packet,
/* int (*set_baudrate)(uint32_t baudrate); */ &hci_transport_h5_set_baudrate,
/* void (*reset_link)(void); */ &hci_transport_h5_reset_link,
/* void (*set_sco_config)(uint16_t voice_setting, int num_connections); */ NULL,
};
// configure and return h5 singleton

View File

@ -11,10 +11,10 @@
#include "btstack_cvsd_plc.h"
#include "wav_util.h"
const int audio_samples_per_frame = 24;
static int8_t audio_frame_in[audio_samples_per_frame];
const int audio_samples_per_frame = 24;
static int16_t audio_frame_in[audio_samples_per_frame];
static uint8_t test_data[][audio_samples_per_frame] = {
static int16_t test_data[][audio_samples_per_frame] = {
{ 0x05, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
{ 0xff, 0xff, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x05 },
{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05 },
@ -23,16 +23,6 @@ static uint8_t test_data[][audio_samples_per_frame] = {
static btstack_cvsd_plc_state_t plc_state;
// input signal: pre-computed sine wave, at 8000 kz
static const uint8_t sine_uint8[] = {
0, 15, 31, 46, 61, 74, 86, 97, 107, 114,
120, 124, 126, 126, 124, 120, 114, 107, 97, 86,
74, 61, 46, 31, 15, 0, 241, 225, 210, 195,
182, 170, 159, 149, 142, 136, 132, 130, 130, 132,
136, 142, 149, 159, 170, 182, 195, 210, 225, 241,
};
// input signal: pre-computed sine wave, 160 Hz at 16000 kHz
static const int16_t sine_int16[] = {
0, 2057, 4107, 6140, 8149, 10126, 12062, 13952, 15786, 17557,
@ -47,27 +37,7 @@ static const int16_t sine_int16[] = {
-19260, -17557, -15786, -13952, -12062, -10126, -8149, -6140, -4107, -2057,
};
static int phase = 0;
void sco_demo_sine_wave_int8(int num_samples, int8_t * data){
int i;
for (i=0; i<num_samples; i++){
data[i] = (int8_t)sine_uint8[phase];
phase++;
if (phase >= sizeof(sine_uint8)) phase = 0;
}
}
void sco_demo_sine_wave_int16(int num_samples, int16_t * data){
int i;
for (i=0; i < num_samples; i++){
data[i] = sine_int16[phase++];
if (phase >= (sizeof(sine_int16) / sizeof(int16_t))){
phase = 0;
}
}
}
static int count_equal_bytes(uint8_t * packet, uint16_t size){
static int count_equal_samples(int16_t * packet, uint16_t size){
int count = 0;
int temp_count = 1;
int i;
@ -87,61 +57,120 @@ static int count_equal_bytes(uint8_t * packet, uint16_t size){
return count;
}
static void create_sine_wav(const char * out_filename){
// @assumption frame len 24 samples
static int bad_frame(int16_t * frame, uint16_t size){
return count_equal_samples(frame, size) > 20;
}
static void btstack_cvsd_plc_mark_bad_frame(btstack_cvsd_plc_state_t * state, int16_t * in, uint16_t size, int16_t * out){
if (size != 24){
printf("btstack_cvsd_plc_mark_bad_frame: audio frame size is incorrect. Expected %d, got %d\n", CVSD_FS, size);
return;
}
state->frame_count++;
if (bad_frame(in,size)){
memcpy(out, in, size * 2);
if (state->good_frames_nr > CVSD_LHIST/CVSD_FS){
memset(out, 0x33, size * 2);
state->bad_frames_nr++;
}
} else {
memcpy(out, in, size);
state->good_frames_nr++;
if (state->good_frames_nr == 1){
printf("First good frame at index %d\n", state->frame_count-1);
}
}
}
static int phase = 0;
static void create_sine_wave_int16_data(int num_samples, int16_t * data){
int i;
for (i=0; i < num_samples; i++){
data[i] = sine_int16[phase++];
phase++;
if (phase >= (sizeof(sine_int16) / sizeof(int16_t))){
phase = 0;
}
}
}
static int count_equal_bytes(int16_t * packet, uint16_t size){
int count = 0;
int temp_count = 1;
int i;
for (i = 0; i < size-1; i++){
if (packet[i] == packet[i+1]){
temp_count++;
continue;
}
if (count < temp_count){
count = temp_count;
}
temp_count = 1;
}
if (temp_count > count + 1){
count = temp_count;
}
return count;
}
void create_sine_wav(const char * out_filename){
btstack_cvsd_plc_init(&plc_state);
wav_writer_open(out_filename, 1, 8000);
int i;
for (i=0; i<2000; i++){
sco_demo_sine_wave_int8(audio_samples_per_frame, audio_frame_in);
wav_writer_write_int8(audio_samples_per_frame, audio_frame_in);
create_sine_wave_int16_data(audio_samples_per_frame, audio_frame_in);
wav_writer_write_int16(audio_samples_per_frame, audio_frame_in);
}
wav_writer_close();
}
static void introduce_bad_frames_to_wav_file(const char * in_filename, const char * out_filename, int corruption_step){
void introduce_bad_frames_to_wav_file(const char * in_filename, const char * out_filename, int corruption_step){
btstack_cvsd_plc_init(&plc_state);
wav_writer_open(out_filename, 1, 8000);
wav_reader_open(in_filename);
int fc = 0;
while (wav_reader_read_int8(audio_samples_per_frame, audio_frame_in)){
while (wav_reader_read_int16(audio_samples_per_frame, audio_frame_in)){
if (corruption_step > 0 && fc >= corruption_step && fc%corruption_step == 0){
memset(audio_frame_in, 50, audio_samples_per_frame);
memset(audio_frame_in, 50, audio_samples_per_frame * 2);
}
wav_writer_write_int8(audio_samples_per_frame, audio_frame_in);
wav_writer_write_int16(audio_samples_per_frame, audio_frame_in);
fc++;
}
wav_reader_close();
wav_writer_close();
}
static void process_wav_file_with_plc(const char * in_filename, const char * out_filename){
void process_wav_file_with_plc(const char * in_filename, const char * out_filename){
// printf("\nProcess %s -> %s\n", in_filename, out_filename);
btstack_cvsd_plc_init(&plc_state);
wav_writer_open(out_filename, 1, 8000);
wav_reader_open(in_filename);
while (wav_reader_read_int8(audio_samples_per_frame, audio_frame_in)){
int8_t audio_frame_out[audio_samples_per_frame];
while (wav_reader_read_int16(audio_samples_per_frame, audio_frame_in)){
int16_t audio_frame_out[audio_samples_per_frame];
btstack_cvsd_plc_process_data(&plc_state, audio_frame_in, audio_samples_per_frame, audio_frame_out);
wav_writer_write_int8(audio_samples_per_frame, audio_frame_out);
wav_writer_write_int16(audio_samples_per_frame, audio_frame_out);
}
wav_reader_close();
wav_writer_close();
btstack_cvsd_dump_statistics(&plc_state);
}
static void mark_bad_frames_wav_file(const char * in_filename, const char * out_filename){
void mark_bad_frames_wav_file(const char * in_filename, const char * out_filename){
// printf("\nMark bad frame %s -> %s\n", in_filename, out_filename);
btstack_cvsd_plc_init(&plc_state);
CHECK_EQUAL(wav_writer_open(out_filename, 1, 8000), 0);
CHECK_EQUAL(wav_reader_open(in_filename), 0);
while (wav_reader_read_int8(audio_samples_per_frame, audio_frame_in)){
int8_t audio_frame_out[audio_samples_per_frame];
while (wav_reader_read_int16(audio_samples_per_frame, audio_frame_in)){
int16_t audio_frame_out[audio_samples_per_frame];
btstack_cvsd_plc_mark_bad_frame(&plc_state, audio_frame_in, audio_samples_per_frame, audio_frame_out);
wav_writer_write_int8(audio_samples_per_frame, audio_frame_out);
wav_writer_write_int16(audio_samples_per_frame, audio_frame_out);
}
wav_reader_close();
wav_writer_close();
@ -161,8 +190,8 @@ TEST(CVSD_PLC, CountEqBytes){
TEST(CVSD_PLC, TestLiveWavFile){
int corruption_step = 10;
introduce_bad_frames_to_wav_file("data/sco_input.wav", "results/sco_input.wav", 0);
introduce_bad_frames_to_wav_file("data/sco_input.wav", "results/sco_input_with_bad_frames.wav", corruption_step);
introduce_bad_frames_to_wav_file("data/sco_input-16bit.wav", "results/sco_input.wav", 0);
introduce_bad_frames_to_wav_file("data/sco_input-16bit.wav", "results/sco_input_with_bad_frames.wav", corruption_step);
mark_bad_frames_wav_file("results/sco_input.wav", "results/sco_input_detected_frames.wav");
process_wav_file_with_plc("results/sco_input.wav", "results/sco_input_after_plc.wav");

Binary file not shown.

View File

@ -1,9 +1,14 @@
#include "CppUTest/TestHarness.h"
#include "CppUTest/CommandLineTestRunner.h"
#include "btstack_ring_buffer.h"
#include "btstack_util.h"
static uint8_t storage[10];
uint32_t btstack_min(uint32_t a, uint32_t b){
return a < b ? a : b;
}
TEST_GROUP(RingBuffer){
btstack_ring_buffer_t ring_buffer;
int storage_size;

View File

@ -48,7 +48,7 @@ def scrape_page(fout, url):
print('-' * 70)
# get all <tr> elements in <table id="gattTable">
rows = tree.xpath('//div[@class="table-time"]/table/tbody/tr')
rows = tree.xpath('//div[@class="copy-block ta-left"]/table/tbody/tr')
for row in rows:
children = row.getchildren()
data_type_value = children[0].text_content()

View File

@ -40,7 +40,7 @@ def scrape_page(fout, url):
page = requests.get(url)
tree = html.fromstring(page.content)
# get all <tr> elements in <table id="gattTable">
rows = tree.xpath('//table[@id="gattTable"]/tr')
rows = tree.xpath('//table[@id="gattTable"]/tbody/tr')
for row in rows:
children = row.getchildren()
summary = children[0].text_content()

View File

@ -38,7 +38,7 @@ def list_services():
page = requests.get(url)
tree = html.fromstring(page.content)
# get all <tr> elements in <table id="gattTable">
rows = tree.xpath('//table[@id="gattTable"]/tr')
rows = tree.xpath('//table[@id="gattTable"]/tbody/tr')
print("%-55s| %-30s| %s" % ('Specification Type', 'Specification Name', 'UUID'))
print('-'*55 + '+-' + '-' * 30 + '+-' + '-'*10)
maxlen_type = 0
@ -65,9 +65,14 @@ def parse_properties(element):
property_list_human = []
for property in properties:
property_name = property.tag
if property_name == "WriteWithoutResponse":
property_name = "Write_Without_Response"
if property_name == "InformationText":
continue
property_requirement = property.text
if (property_requirement != "Excluded"):
property_list_human.append(property_name)
if (property_requirement == "Excluded"):
continue
property_list_human.append(property_name)
return (property_list_human)
print("Characteristic %s - properties %s" % (name, property_list_human))
@ -163,7 +168,8 @@ def convert_service(fout, specification_type):
continue
if (type == 'org.bluetooth.descriptor.report_reference'):
print('-- Descriptor %-40s - WARNING: REPORT_REFERENCE not supported yet' % name)
print('-- Descriptor %-40s' % name)
fout.write('REPORT_REFERENCE, %s,\n' % properties)
continue
if (type == 'org.bluetooth.descriptor.number_of_digitals'):