? Doxyfile ? Makefile.in ? aclocal.m4 ? autom4te.cache ? btsco.kdevelop ? btsco.kdevelop.pcs ? btsco.kdevses ? compile ? config.guess ? config.h.in ? config.sub ? configure ? depcomp ? install-sh ? missing ? alsa-plugins/Makefile.in ? avdtp/Makefile.in ? sbc/Makefile.in Index: alsa-plugins/BUILD =================================================================== RCS file: /cvsroot/bluetooth-alsa/btsco/alsa-plugins/BUILD,v retrieving revision 1.2 diff -u -r1.2 BUILD --- alsa-plugins/BUILD 12 Aug 2006 01:51:54 -0000 1.2 +++ alsa-plugins/BUILD 31 Aug 2006 15:58:56 -0000 @@ -26,4 +26,3 @@ Note that this device will not automatically appear in the list of alsa devices. The alsa folks did this by design but they added a new api for clients to enumerate plugin devices. - Index: alsa-plugins/Makefile.am =================================================================== RCS file: /cvsroot/bluetooth-alsa/btsco/alsa-plugins/Makefile.am,v retrieving revision 1.6 diff -u -r1.6 Makefile.am --- alsa-plugins/Makefile.am 6 Aug 2006 05:23:43 -0000 1.6 +++ alsa-plugins/Makefile.am 31 Aug 2006 15:58:56 -0000 @@ -4,8 +4,6 @@ if ALSAPLUGIN -#LIBS = - alsadir = $(libdir)/alsa-lib alsa_LTLIBRARIES = libasound_module_pcm_a2dp.la libasound_module_pcm_a2dpd.la libasound_module_ctl_a2dpd.la libasound_module_pcm_sco.la libasound_module_ctl_sco.la @@ -31,9 +29,10 @@ libasound_module_ctl_sco_la_LIBADD = @ALSA_LIBS@ bin_PROGRAMS = a2dpd -a2dpd_SOURCES = a2dpd.c a2dplib.c +a2dpd_SOURCES = a2dpd.c a2dplib.c alsalib.c +a2dpd_CFLAGS = $(AM_CFLAGS) #a2dp_timer.c a2dp_ipc.c -a2dpd_LDADD = a2dp_timer.o a2dp_ipc.o @BLUEZ_LIBS@ -lsbc -lpthread -lrt +a2dpd_LDADD = a2dp_timer.o a2dp_ipc.o @ALSA_LIBS@ @BLUEZ_LIBS@ -lsbc -lpthread -lrt AM_CFLAGS = @ALSA_CFLAGS@ @BLUEZ_CFLAGS@ -pthread AM_LDFLAGS = -module -avoid-version -export-dynamic Index: alsa-plugins/a2dp_ipc.c =================================================================== RCS file: /cvsroot/bluetooth-alsa/btsco/alsa-plugins/a2dp_ipc.c,v retrieving revision 1.3 diff -u -r1.3 a2dp_ipc.c --- alsa-plugins/a2dp_ipc.c 17 Aug 2006 14:06:27 -0000 1.3 +++ alsa-plugins/a2dp_ipc.c 31 Aug 2006 15:58:56 -0000 @@ -36,7 +36,11 @@ void close_socket(int sockfd) { - if(sockfd>0) close(sockfd); + if(sockfd>0) + { + shutdown(sockfd, SHUT_RDWR); + close(sockfd); + } } int make_udp_socket() @@ -98,7 +102,7 @@ { if(bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr))==0) { - if(listen(sockfd, 10)==0) + if(listen(sockfd, 0)==0) { // No error } Index: alsa-plugins/a2dp_timer.c =================================================================== RCS file: /cvsroot/bluetooth-alsa/btsco/alsa-plugins/a2dp_timer.c,v retrieving revision 1.2 diff -u -r1.2 a2dp_timer.c --- alsa-plugins/a2dp_timer.c 17 Aug 2006 14:06:27 -0000 1.2 +++ alsa-plugins/a2dp_timer.c 31 Aug 2006 15:58:56 -0000 @@ -26,104 +26,93 @@ #include #include -void sleeptodate(LPTIMERINFO lpTimerInfo, struct timeval* date, int predelay) +void sleeptodate(LPTIMERINFO lpTimerInfo, struct timeval *date, int predelay) { - struct timeval now; - struct timeval resolutionval={0, predelay + (int)(lpTimerInfo->timer_resolution.tv_nsec/1000)}; + struct timeval now; + struct timeval resolutionval = { 0, predelay + (int) (lpTimerInfo->timer_resolution.tv_nsec / 1000) }; - // See if we have time to wait - timeradd(&lpTimerInfo->timeofday, &resolutionval, &now); - if (timercmp(date, &now, >)) - { - // Synchronise with usleep 20 ms cycle - usleep(1); - // See if we must wait again - gettimeofday(&now, NULL); - timeradd(&now, &resolutionval, &now); - if (timercmp(date, &now, >)) - { - struct timeval interval = { 0, 0 }; - timersub(date, &now, &interval); - // sleep - usleep(interval.tv_usec); - } - } - else - { - // We're late, do not wait - } + // See if we have time to wait + timeradd(&lpTimerInfo->timeofday, &resolutionval, &now); + if (timercmp(date, &now, >)) { + // Synchronise with usleep 20 ms cycle + usleep(1); + // See if we must wait again + gettimeofday(&now, NULL); + timeradd(&now, &resolutionval, &now); + if (timercmp(date, &now, >)) { + struct timeval interval = { 0, 0 }; + timersub(date, &now, &interval); + // sleep + usleep(interval.tv_usec); + } + } else { + // We're late, do not wait + } } // This version uses values never reset void keepfreqtotal(LPTIMERINFO lpTimerInfo, int predelay) { - struct timeval playtime, theoricaldate; + struct timeval playtime, theoricaldate; - //FIXME It is not necessary to use unsigned long if we reset periodically the value of itotalcount (see MAXTOTALCOUNT) - // if MAXTOTALCOUNT < 700000, we will fit in signed 32bit and reset no more than every 36 mins. - // Resetting that value might cause a small sound break. - // Setting MAXTOTALCOUNT to lpTimerInfo->fps will reset every second (useful for testing purposes) - playtime.tv_sec=((unsigned long)((1.0*(lpTimerInfo->itotalcount)/lpTimerInfo->fps))); - playtime.tv_usec=((unsigned long)((1.0*1000.0*1000.0/lpTimerInfo->fps)*(lpTimerInfo->itotalcount)))%1000000; - timeradd(&lpTimerInfo->totalcounter, &playtime, &theoricaldate); - - // Si la date théorique est supérieure à la date actuelle - if(timercmp(&theoricaldate, &lpTimerInfo->timeofday, >)) - { - sleeptodate(lpTimerInfo, &theoricaldate, predelay); - } + //FIXME It is not necessary to use unsigned long if we reset periodically the value of itotalcount (see MAXTOTALCOUNT) + // if MAXTOTALCOUNT < 700000, we will fit in signed 32bit and reset no more than every 36 mins. + // Resetting that value might cause a small sound break. + // Setting MAXTOTALCOUNT to lpTimerInfo->fps will reset every second (useful for testing purposes) + playtime.tv_sec = ((unsigned long) ((1.0 * (lpTimerInfo->itotalcount) / lpTimerInfo->fps))); + playtime.tv_usec = ((unsigned long) ((1.0 * 1000.0 * 1000.0 / lpTimerInfo->fps) * (lpTimerInfo->itotalcount))) % 1000000; + timeradd(&lpTimerInfo->totalcounter, &playtime, &theoricaldate); + + // If calculated date is higher than current date + if (timercmp(&theoricaldate, &lpTimerInfo->timeofday, >)) { + sleeptodate(lpTimerInfo, &theoricaldate, predelay); + } } void a2dp_timer_notifyframe(LPTIMERINFO lpTimerInfo) { - struct timeval lastframe_interval = {0,0}; - struct timeval maxallowed_interval = {0,200*1000}; - gettimeofday(&lpTimerInfo->timeofday, NULL); - timersub(&lpTimerInfo->timeofday, &lpTimerInfo->lastframe, &lastframe_interval); - // Previous frames older than 1 second, reset counters - if(timercmp(&lastframe_interval, &maxallowed_interval, >)) - { - // We must reset the total counter because else, we will believe the date is late - gettimeofday(&lpTimerInfo->totalcounter, NULL); - lpTimerInfo->itotalcount=0; - } - gettimeofday(&lpTimerInfo->lastframe, NULL); + struct timeval lastframe_interval = { 0, 0 }; + struct timeval maxallowed_interval = { 0, 200 * 1000 }; + gettimeofday(&lpTimerInfo->timeofday, NULL); + timersub(&lpTimerInfo->timeofday, &lpTimerInfo->lastframe, &lastframe_interval); + // Previous frames older than 1 second, reset counters + if (timercmp(&lastframe_interval, &maxallowed_interval, >)) { + // We must reset the total counter because else, we will believe the date is late + gettimeofday(&lpTimerInfo->totalcounter, NULL); + lpTimerInfo->itotalcount = 0; + } + gettimeofday(&lpTimerInfo->lastframe, NULL); } void a2dp_timer_sleep(LPTIMERINFO lpTimerInfo, int predelay) { - gettimeofday(&lpTimerInfo->timeofday, NULL); + gettimeofday(&lpTimerInfo->timeofday, NULL); - // Initialize timers - if(lpTimerInfo->staticcounter.tv_sec==0) - gettimeofday(&lpTimerInfo->staticcounter, NULL); - if(lpTimerInfo->totalcounter.tv_sec==0 || lpTimerInfo->itotalcount>MAXTOTALCOUNT) - { - gettimeofday(&lpTimerInfo->totalcounter, NULL); - lpTimerInfo->itotalcount=0; - } - if(lpTimerInfo->timer_resolution.tv_nsec == 0) - clock_getres(CLOCK_REALTIME, &lpTimerInfo->timer_resolution); - - // Duration since last call - timersub(&lpTimerInfo->timeofday, &lpTimerInfo->staticcounter, &lpTimerInfo->duration); - - // Display data once per second - if(lpTimerInfo->duration.tv_sec>0) - { - // Reset all statistics - gettimeofday(&lpTimerInfo->staticcounter, NULL); - lpTimerInfo->display=lpTimerInfo->icount; - lpTimerInfo->icount=0; - } - else - { - lpTimerInfo->display=0; - } + // Initialize timers + if (lpTimerInfo->staticcounter.tv_sec == 0) + gettimeofday(&lpTimerInfo->staticcounter, NULL); + if (lpTimerInfo->totalcounter.tv_sec == 0 || lpTimerInfo->itotalcount > MAXTOTALCOUNT) { + gettimeofday(&lpTimerInfo->totalcounter, NULL); + lpTimerInfo->itotalcount = 0; + } + if (lpTimerInfo->timer_resolution.tv_nsec == 0) + clock_getres(CLOCK_REALTIME, &lpTimerInfo->timer_resolution); + + // Duration since last call + timersub(&lpTimerInfo->timeofday, &lpTimerInfo->staticcounter, &lpTimerInfo->duration); + + // Display data once per second + if (lpTimerInfo->duration.tv_sec > 0) { + // Reset all statistics + gettimeofday(&lpTimerInfo->staticcounter, NULL); + lpTimerInfo->display = lpTimerInfo->icount; + lpTimerInfo->icount = 0; + } else { + lpTimerInfo->display = 0; + } - keepfreqtotal(lpTimerInfo, predelay); + keepfreqtotal(lpTimerInfo, predelay); - lpTimerInfo->icount++; - lpTimerInfo->itotalcount++; + lpTimerInfo->icount++; + lpTimerInfo->itotalcount++; } - Index: alsa-plugins/a2dpair =================================================================== RCS file: alsa-plugins/a2dpair diff -N alsa-plugins/a2dpair --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ alsa-plugins/a2dpair 31 Aug 2006 15:58:57 -0000 @@ -0,0 +1,129 @@ +#!/bin/sh + +# +# Discovery +# +echo "Discovery in progress..." +I=0 +SCANFILE=/tmp/hci_scan +declare -a ARRAYADDR +declare -a ARRAYNAME + +# We need this file because else there is a problem with arrays in shell +# The while loop is run in a subshell +hcitool scan > $SCANFILE + +while read BTADDR BTDESC ; do + if expr match "$BTADDR" "..:..:..:..:..:.." > /dev/null ; then + # Truc + echo "$I) [$BTADDR] $BTDESC" + I=`expr $I + 1` + ARRAYADDR[$I]="$BTADDR" + ARRAYNAME[$I]="$BTDESC" + fi +done < $SCANFILE +rm -f $SCANFILE + +if [ $I -le 0 ] ; then + echo "Found $I devices" + exit -1 +fi + +# +# Device selection +# +SELECTION= +I=`expr $I - 1` +while [ -z $SELECTION ] || [ $SELECTION -gt $I ] ; do + echo "Choose device (0-$I)" + read SELECTION +done +# sh uses 1 based arrays +SELECTION=`expr $SELECTION + 1` +ADDRESS=${ARRAYADDR[$SELECTION]} +NAME=${ARRAYNAME[$SELECTION]} +# +# Pairing +# +echo "Pair device $NAME (y/N)?" +read CANPAIR + +if [ "$CANPAIR" = "y" ] ; then + + # Device passkey + SELECTION= + while [ -z $SELECTION ] ; do + echo "Enter passkey for $NAME" + read SELECTION + done + PASSKEY=$SELECTION + + # Prefetch password + sudo echo "Pairing in progress..." + + # passkey agent + if sudo passkey-agent --default $PASSKEY & PASSPID=$! ; then + + #echo "Registered passkey-agent pid=$PASSPID" + + # pairing + ANYTEXTISFAILURE=`sudo hcitool cc $ADDRESS 2>&1` + + if [ -z "$ANYTEXTISFAILURE" ] ; then + echo "Pairing successfull" + RESULT=0 + else + echo "$ANYTEXTISFAILURE" + echo "Pairing failed" + fi + + #echo "Killing pid=$PASSPID" + # Kill bg process + sudo kill $PASSPID + fi +fi + +# +# A2DP Setting +# +echo "Select device for a2dp (y/N)?" +read A2PARAM + +if [ "$A2PARAM" = "y" ] ; then + echo "Writing ~/.a2dprc" + if [ -f ~/.a2dprc ] ; then + mv -f ~/.a2dprc ~/.a2dprc~ + cat ~/.a2dprc~ | while read LINE ; do + # Address line + if expr "$LINE" : "address=.*" > /dev/null; then + echo "address=$ADDRESS" >> ~/.a2dprc + else + echo "$LINE" >> ~/.a2dprc + fi + done + else + echo "[A2DPD]" > ~/.a2dprc + echo "address=$ADDRESS" >> ~/.a2dprc + fi +fi + + +# +# A2DP Daemon +# +echo "Start a2dp daemon (y/N)?" +read A2DAEMON +if [ "$A2DAEMON" = "y" ] ; then + while killall a2dpd 2> /dev/null ; do + echo -n . + sleep 1 + done + a2dpd -d +v +fi + +# +# Ending +# +exit 0 + +############ Index: alsa-plugins/a2dpd.c =================================================================== RCS file: /cvsroot/bluetooth-alsa/btsco/alsa-plugins/a2dpd.c,v retrieving revision 1.7 diff -u -r1.7 a2dpd.c --- alsa-plugins/a2dpd.c 30 Aug 2006 14:43:21 -0000 1.7 +++ alsa-plugins/a2dpd.c 31 Aug 2006 15:58:57 -0000 @@ -37,6 +37,7 @@ #include #include "a2dplib.h" +#include "alsalib.h" #include "a2dpd_protocol.h" #include "a2dp_timer.h" #include "a2dp_ipc.h" @@ -51,16 +52,18 @@ #define UINPUT_DEVICE "/dev/input/uinput" #define A2DPD_CONFIG_FILE ".a2dpdrc" - -static char g_sOutputFilename [512]; -static char g_srcfilename [512]; -static char g_sCmdPlay [512]; -static char g_sCmdPause [512]; -static char g_sCmdPrev [512]; -static char g_sCmdNext [512]; -static char g_sCmdNew [512]; -static int g_nbdeviceconnected = 0; -static int uinput_fd = -1; +static char g_sOutputFilename[512]; +static char g_srcfilename[512]; +static char g_sCmdPlay[512]; +static char g_sCmdPause[512]; +static char g_sCmdPrev[512]; +static char g_sCmdNext[512]; +static char g_sCmdNew[512]; +static int g_nbdeviceconnected = 0; +static int uinput_fd = -1; +static int g_bavrcp = 0; +static int g_brereadconfig = 0; +static int g_breversestereo = 0; // This function is needed to destroy zombies processes // On Unix, any forked process which terminate before its parent create a zombie until parent call waitpid() @@ -69,56 +72,49 @@ // http://www.erlenstar.demon.co.uk/unix/faq_2.html void ignore_child_processes_return_values() { - struct sigaction sa; - sa.sa_handler = SIG_IGN; - #ifdef SA_NOCLDWAIT - sa.sa_flags = SA_NOCLDWAIT; - #else - sa.sa_flags = 0; - #endif - sigemptyset(&sa.sa_mask); - sigaction(SIGCHLD, &sa, NULL); + struct sigaction sa; + sa.sa_handler = SIG_IGN; +#ifdef SA_NOCLDWAIT + sa.sa_flags = SA_NOCLDWAIT; +#else + sa.sa_flags = 0; +#endif + sigemptyset(&sa.sa_mask); + sigaction(SIGCHLD, &sa, NULL); } -void make_daemon_process(int bFork, int bVerbose, char*output_file_name) +void make_daemon_process(int bFork, int bVerbose, char *output_file_name) { - // Fork to background process if needed - if (bFork == 1) - { - switch (fork()) - { - case -1: - exit(-1); - case 0: - break; - default: - exit(0); - } - - setsid(); - chdir("/"); - } - - // Redirect output to file (default /dev/null) in silent mode, verbose will print output to stdin/out/err - if(!bVerbose) - { - int fd; - if ((fd = open(output_file_name, O_CREAT|O_APPEND|O_RDWR, 0)) != -1) - { - fchmod(fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH); - (void) dup2(fd, STDIN_FILENO); - (void) dup2(fd, STDOUT_FILENO); - (void) dup2(fd, STDERR_FILENO); - if (fd > 2) - (void) close(fd); - } - else - { - printf("a2dpd: Couldn't redirect output to '%s' (errno=%d:%s)", output_file_name, errno, strerror(errno)); - } - } + // Fork to background process if needed + if (bFork == 1) { + switch (fork()) { + case -1: + exit(-1); + case 0: + break; + default: + exit(0); + } - printf("a2dpd [%s %s] starting ...", __DATE__, __TIME__); + setsid(); + chdir("/"); + } + // Redirect output to file (default /dev/null) in silent mode, verbose will print output to stdin/out/err + if (!bVerbose) { + int fd; + if ((fd = open(output_file_name, O_CREAT | O_APPEND | O_RDWR, 0)) != -1) { + fchmod(fd, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); + (void) dup2(fd, STDIN_FILENO); + (void) dup2(fd, STDOUT_FILENO); + (void) dup2(fd, STDERR_FILENO); + if (fd > 2) + (void) close(fd); + } else { + printf("a2dpd: Couldn't redirect output to '%s' (errno=%d:%s)", output_file_name, errno, strerror(errno)); + } + } + + printf("a2dpd [%s %s] starting ...", __DATE__, __TIME__); } static int lock_fd(int fd) @@ -141,54 +137,52 @@ // Prepare packet headers static void init_response(struct avctp_header *header) { - header->ipid = 0; - header->cr = AVCTP_RESPONSE_FRAME; - header->packet_type = PACKET_TYPE_SINGLE; + header->ipid = 0; + header->cr = AVCTP_RESPONSE_FRAME; + header->packet_type = PACKET_TYPE_SINGLE; } -static int init_uinput () +static int init_uinput() { int fd, i; struct uinput_user_dev dev = { .id = { - .bustype = BUS_BLUETOOTH, - .version = 0x0001, - } + .bustype = BUS_BLUETOOTH, + .version = 0x0001, + } }; - if((fd = open(UINPUT_DEVICE, O_WRONLY)) < 0) - { + if ((fd = open(UINPUT_DEVICE, O_WRONLY)) < 0) { perror("Cannot open " UINPUT_DEVICE); goto shutdown; } - if(write(fd, &dev, sizeof(dev)) < sizeof(dev)) - { + if (write(fd, &dev, sizeof(dev)) < sizeof(dev)) { perror("Cannot create a uinput device"); goto release; } - if(ioctl(fd, UI_SET_EVBIT, EV_KEY)) + if (ioctl(fd, UI_SET_EVBIT, EV_KEY)) goto release; - for(i = 0; i <= KEY_MAX; i++) - if(ioctl(fd, UI_SET_KEYBIT, i)) + for (i = 0; i <= KEY_MAX; i++) + if (ioctl(fd, UI_SET_KEYBIT, i)) goto release; - if(ioctl(fd, UI_DEV_CREATE)) + if (ioctl(fd, UI_DEV_CREATE)) goto release; uinput_fd = fd; return 0; - release: + release: ioctl(fd, UI_DEV_DESTROY); - shutdown: + shutdown: close(fd); return 1; } -static void kill_uinput () +static void kill_uinput() { if (uinput_fd == -1) return; @@ -197,99 +191,93 @@ close(uinput_fd); } -static void send_key (unsigned short code) +static void send_key(unsigned short code) { struct input_event ev = { .type = EV_KEY, .code = code, - .time = {0, } + .time = {0,} }; if (uinput_fd == -1) return; - if(code > KEY_MAX) + if (code > KEY_MAX) return; - - ev.value = 1; // press... + + ev.value = 1; // press... write(uinput_fd, &ev, sizeof(ev)); - ev.value = 0; // then release + ev.value = 0; // then release write(uinput_fd, &ev, sizeof(ev)); } // This function handle the bluetooth connection int a2dp_handle_avrcp_message(int sockfd) { - char lpFrame [A2DPMAXIMUMTRANSFERUNITSIZE]; - int iReceived = recv(sockfd, lpFrame, sizeof(lpFrame), 0); - if(iReceived>0) - { - struct avc_frame frame = *((struct avc_frame*)lpFrame); - - // Handle message - if(frame.ctype == CMD_PASSTHROUGH) - { - switch (frame.operand0) - { - case PLAY_OP: - printf("[play] %s\n", g_sCmdPlay); - if(g_sCmdPlay[0]) async_run_process(g_sCmdPlay); - send_key (KEY_PLAY); - break; - case PAUSE_OP: - printf("[pause] %s\n", g_sCmdPause); - if(g_sCmdPause[0]) async_run_process(g_sCmdPause); - send_key (KEY_PAUSE); - break; - case NEXT_OP: - printf("[next] %s\n", g_sCmdNext); - if(g_sCmdNext[0]) async_run_process(g_sCmdNext); - send_key (KEY_NEXTSONG); - break; - case PREV_OP: - printf("[previous] %s\n", g_sCmdPrev); - if(g_sCmdPrev[0]) async_run_process(g_sCmdPrev); - send_key (KEY_PREVIOUSSONG); - break; - default: - printf("received passthrough %d bytes:\n", iReceived); - //dump_packet(&frame, iReceived); - } - } - else - { - printf("received %d bytes:\n", iReceived); - //dump_packet(&frame, iReceived); - } - // Send response - if(iReceived > 0) - { - if(frame.ctype == CMD_ACCEPTED) - { - printf("(ack)\n"); - } - else - if(frame.ctype == CMD_PASSTHROUGH) - { - init_response(&frame.header); - frame.ctype = CMD_ACCEPTED; - write(sockfd, &frame, iReceived); - } - else - { - printf("only passthrough ctype command is implemented. doh!\n"); - // ierk!!! exit(0); - } - } - } - else - { - if(errno!=EAGAIN) - printf("socket %d: Receive failed %d (error %d:%s)\n", sockfd, iReceived, errno, strerror(errno)); - } + char lpFrame[A2DPMAXIMUMTRANSFERUNITSIZE]; + int iReceived = recv(sockfd, lpFrame, sizeof(lpFrame), 0); + if (iReceived > 0) { + struct avc_frame frame = *((struct avc_frame *) lpFrame); + + // Handle message + if (frame.ctype == CMD_PASSTHROUGH) { + switch (frame.operand0) { + case PLAY_OP: + printf("[play] %s\n", g_sCmdPlay); + if (g_sCmdPlay[0]) + async_run_process(g_sCmdPlay); + else + send_key(KEY_PLAY); + break; + case PAUSE_OP: + printf("[pause] %s\n", g_sCmdPause); + if (g_sCmdPause[0]) + async_run_process(g_sCmdPause); + else + send_key(KEY_PAUSE); + break; + case NEXT_OP: + printf("[next] %s\n", g_sCmdNext); + if (g_sCmdNext[0]) + async_run_process(g_sCmdNext); + else + send_key(KEY_NEXTSONG); + break; + case PREV_OP: + printf("[previous] %s\n", g_sCmdPrev); + if (g_sCmdPrev[0]) + async_run_process(g_sCmdPrev); + else + send_key(KEY_PREVIOUSSONG); + break; + default: + printf("received passthrough %d bytes:\n", iReceived); + //dump_packet(&frame, iReceived); + } + } else { + printf("received %d bytes:\n", iReceived); + //dump_packet(&frame, iReceived); + } + // Send response + if (iReceived > 0) { + if (frame.ctype == CMD_ACCEPTED) { + printf("(ack)\n"); + } else if (frame.ctype == CMD_PASSTHROUGH) { + init_response(&frame.header); + frame.ctype = CMD_ACCEPTED; + write(sockfd, &frame, iReceived); + } else { + printf("only passthrough ctype command is implemented. doh!\n"); + // ierk!!! exit(0); + } + } + } else { + if (errno != EAGAIN) + printf("socket %d: Receive failed %d (error %d:%s)\n", sockfd, iReceived, errno, strerror(errno)); + } - return iReceived; + return iReceived; } ////////////////////////////////////////// @@ -306,738 +294,709 @@ #define max(x,y) ((x)>(y)?(x):(y)) -char* pool_pop() +char *pool_pop() { - return malloc(POOLENTRYSIZE); + return malloc(POOLENTRYSIZE); } -void pool_push(char* pool) +void pool_push(char *pool) { - free(pool); + free(pool); } // Data used to mix audio -typedef struct -{ - int lives; - pthread_mutex_t mutex; - int ring_in; - int ring_out; - int ring_len[MAXCLIENTSRINGSIZE]; - char* ring[MAXCLIENTSRINGSIZE]; +typedef struct { + int lives; + pthread_mutex_t mutex; + int ring_in; + int ring_out; + int ring_len[MAXCLIENTSRINGSIZE]; + char *ring[MAXCLIENTSRINGSIZE]; } BTA2DPPERCLIENTDATA; // Data to keep per Bluetooth device -typedef struct -{ - char addr[20]; - pthread_t thread; - pthread_t receiverthread; - pthread_mutex_t mutex; - AUDIOMIXERDATA mixer; - int nb_clients; - BTA2DPPERCLIENTDATA clients[MAXCLIENTSPERDEVICE]; +typedef struct { + char addr[20]; + char plug[20]; + pthread_t thread; + pthread_t receiverthread; + pthread_mutex_t mutex; + AUDIOMIXERDATA mixer; + int nb_clients; + int bredirectalsa; + BTA2DPPERCLIENTDATA clients[MAXCLIENTSPERDEVICE]; } BTA2DPPERDEVICEDATA, *LPBTA2DPPERDEVICEDATA; // Data needed per Audio Streaming Client -typedef struct -{ - LPBTA2DPPERDEVICEDATA lpDevice; - int sockfd; - pthread_t thread; +typedef struct { + LPBTA2DPPERDEVICEDATA lpDevice; + int sockfd; + pthread_t thread; } A2DPDCLIENT, *LPA2DPDCLIENT; // Allocate a new device -LPBTA2DPPERDEVICEDATA bta2dpdevicenew(char* addr) +LPBTA2DPPERDEVICEDATA bta2dpdevicenew(char *addr) { - int i = 0; - LPBTA2DPPERDEVICEDATA lpDevice = malloc(sizeof(BTA2DPPERDEVICEDATA)); - if(lpDevice) - { - memset(lpDevice, 0, sizeof(BTA2DPPERDEVICEDATA)); - strncpy(lpDevice->addr, addr, sizeof(lpDevice->addr)); lpDevice->addr[sizeof(lpDevice->addr)-1] = 0; - lpDevice->mixer.volume_speaker_left = A2DPD_VOLUME_MAX; - lpDevice->mixer.volume_speaker_right = A2DPD_VOLUME_MAX; - lpDevice->mixer.volume_micro_left = A2DPD_VOLUME_MAX; - lpDevice->mixer.volume_micro_right = A2DPD_VOLUME_MAX; - pthread_mutex_init(&lpDevice->mutex, NULL); - for(i=0;iclients[i].mutex, NULL); - } - } - return lpDevice; + int i = 0; + LPBTA2DPPERDEVICEDATA lpDevice = malloc(sizeof(BTA2DPPERDEVICEDATA)); + if (lpDevice) { + memset(lpDevice, 0, sizeof(BTA2DPPERDEVICEDATA)); + strncpy(lpDevice->addr, addr, sizeof(lpDevice->addr)); + lpDevice->addr[sizeof(lpDevice->addr) - 1] = 0; + lpDevice->mixer.volume_speaker_left = A2DPD_VOLUME_MAX; + lpDevice->mixer.volume_speaker_right = A2DPD_VOLUME_MAX; + lpDevice->mixer.volume_micro_left = A2DPD_VOLUME_MAX; + lpDevice->mixer.volume_micro_right = A2DPD_VOLUME_MAX; + pthread_mutex_init(&lpDevice->mutex, NULL); + for (i = 0; i < MAXCLIENTSPERDEVICE; i++) { + pthread_mutex_init(&lpDevice->clients[i].mutex, NULL); + } + } + return lpDevice; } // Free a device void bta2dpdevicefree(LPBTA2DPPERDEVICEDATA lpDevice) { - int i = 0; - if(lpDevice) - { - for(i=0;iclients[i].mutex); - } - pthread_mutex_destroy(&lpDevice->mutex); - free(lpDevice); - } + int i = 0; + if (lpDevice) { + for (i = 0; i < MAXCLIENTSPERDEVICE; i++) { + pthread_mutex_destroy(&lpDevice->clients[i].mutex); + } + pthread_mutex_destroy(&lpDevice->mutex); + free(lpDevice); + } } // handle sigterm to terminate properly void sigint_handler(int sig) { - // User wants to force quit - if(bSigINTReceived==1) - { - printf("handling SIGINT again: exit forced\n"); - exit(0); - } - else - { - // Now we must quit properly - bSigINTReceived = 1; - printf("handling SIGINT\n"); - - // Dummy connection to unlock server (currently accepting) - close_socket(make_client_socket()); - } + // User wants to force quit + if (bSigINTReceived == 1) { + printf("handling SIGINT again: exit forced\n"); + exit(0); + } else { + // Now we must quit properly + bSigINTReceived = 1; + printf("handling SIGINT\n"); + + // Dummy connection to unlock server (currently accepting) + close_socket(make_client_socket()); + } } // This function handles a client -void* client_handler(void* param) +void *client_handler(void *param) { - int bError = 0; - int client_index = -1; - int32_t client_type = INVALID_CLIENT_TYPE; - int result; - LPA2DPDCLIENT lpClient = (LPA2DPDCLIENT)param; - AUDIOMIXERDATA AudioMixerData = INVALIDAUDIOMIXERDATA; - - // We should not terminate the process if clients are still running - iThreadsRunning ++; - - pthread_detach(lpClient->thread); - - setup_socket(lpClient->sockfd); - - // Receive type of client - result = recv_socket(lpClient->sockfd, &client_type, sizeof(client_type)); - - // This client wants to send us pcm control data - if(client_type==A2DPD_PLUGIN_CTL_WRITE) - { - printf("CTL WRITE thread %d.%d started\n", client_index, lpClient->sockfd); - - if(recv_socket(lpClient->sockfd, &AudioMixerData, sizeof(AudioMixerData))==sizeof(AudioMixerData)) - { - pthread_mutex_lock(&lpClient->lpDevice->mutex); - if(AudioMixerData.volume_speaker_left != -1) - lpClient->lpDevice->mixer.volume_speaker_left = AudioMixerData.volume_speaker_left; - if(AudioMixerData.volume_speaker_left != -1) - lpClient->lpDevice->mixer.volume_speaker_right= AudioMixerData.volume_speaker_right; - if(AudioMixerData.volume_micro_left != -1) - lpClient->lpDevice->mixer.volume_micro_left = AudioMixerData.volume_micro_left; - if(AudioMixerData.volume_micro_left != -1) - lpClient->lpDevice->mixer.volume_micro_right= AudioMixerData.volume_micro_right; - pthread_mutex_unlock(&lpClient->lpDevice->mutex); - // Notify other clients - int notifyfd = make_udp_socket(); - int i=send_socket(notifyfd, &AudioMixerData, sizeof(AudioMixerData)); - printf("Notify return %d\n", i); - close_socket(notifyfd); - } - } - - // This client wants to read our control status - if(client_type==A2DPD_PLUGIN_CTL_READ) - { - printf("CTL READ thread %d.%d started\n", client_index, lpClient->sockfd); - - pthread_mutex_lock(&lpClient->lpDevice->mutex); - AudioMixerData = lpClient->lpDevice->mixer; - pthread_mutex_unlock(&lpClient->lpDevice->mutex); - - send_socket(lpClient->sockfd, &AudioMixerData, sizeof(AudioMixerData)); - } - - // This client wants to send us pcm stream - if(client_type==A2DPD_PLUGIN_PCM_WRITE) - { - // Find an index in clients table for the mixer - pthread_mutex_lock(&lpClient->lpDevice->mutex); - for(client_index=0; client_indexlpDevice->clients[client_index].lives==0) - { - lpClient->lpDevice->clients[client_index].lives = 1; - lpClient->lpDevice->clients[client_index].ring_in = 0; - lpClient->lpDevice->clients[client_index].ring_out = 0; - break; - } - } - pthread_mutex_unlock(&lpClient->lpDevice->mutex); - - printf("PCM thread %d.%d started\n", client_index, lpClient->sockfd); - - if(client_index>=MAXCLIENTSPERDEVICE) - { - printf("Client thread %d cannot start (too many clients already connected)\n", client_index); - return 0; - } - - // Loop while we receive data - while(!bSigINTReceived && !bError) - { - // Receive data - int32_t pcm_buffer_size = 0; - result = recv_socket(lpClient->sockfd, &pcm_buffer_size, sizeof(pcm_buffer_size)); - if(result == sizeof(pcm_buffer_size) && pcm_buffer_size<=POOLENTRYSIZE) - { - char* pcm_buffer = pool_pop(); - result = recv_socket(lpClient->sockfd, pcm_buffer, pcm_buffer_size); - - if(result==pcm_buffer_size) - { - // Enqueue in bluetooth headset if we can else loose packet - pthread_mutex_lock(&lpClient->lpDevice->clients[client_index].mutex); - - // Append data to ring - int this_ring = lpClient->lpDevice->clients[client_index].ring_in; - int next_ring = ((this_ring+1)%MAXCLIENTSRINGSIZE); - - if(next_ring != lpClient->lpDevice->clients[client_index].ring_out) - { - lpClient->lpDevice->clients[client_index].ring[this_ring] = pcm_buffer; - lpClient->lpDevice->clients[client_index].ring_len[this_ring] = pcm_buffer_size; - lpClient->lpDevice->clients[client_index].ring_in = next_ring; - // We will not free that buffer, it's the bthandler thread which will do it - pcm_buffer = NULL; - } - - pthread_mutex_unlock(&lpClient->lpDevice->clients[client_index].mutex); - - // Reintegrate data in pool - if(pcm_buffer) pool_push(pcm_buffer); - } - else - { - printf("[2] Receiving failed on socket %d.%d error (%d/%d bytes)\n", client_index, lpClient->sockfd, result, pcm_buffer_size); - bError = 1; - } - } - else - { - if(result==sizeof(pcm_buffer_size)) - { - printf("[1] Receiving will not fit pool (poolentrysize=%d != pcm_buffer_size=%d)\n", POOLENTRYSIZE, pcm_buffer_size); - } - else - { - printf("[1] Receiving failed on socket %d.%d error (%d/%d bytes) errno=%d:%s\n", client_index, lpClient->sockfd, result, sizeof(pcm_buffer_size), errno, strerror(errno)); - } - bError = 1; - } - } - - pthread_mutex_lock(&lpClient->lpDevice->mutex); - if(client_index>=0) lpClient->lpDevice->clients[client_index].lives=0; - pthread_mutex_unlock(&lpClient->lpDevice->mutex); - } - - // Say goodbye - pthread_mutex_lock(&lpClient->lpDevice->mutex); - lpClient->lpDevice->nb_clients--; - pthread_mutex_unlock(&lpClient->lpDevice->mutex); - - // Close socket - printf("Client thread %d.%d ending: %s\n", client_index, lpClient->sockfd, (bError?(errno==EAGAIN?"timeout":"error"):"no error")); - close_socket(lpClient->sockfd); + int bError = 0; + int client_index = -1; + int32_t client_type = INVALID_CLIENT_TYPE; + int result; + LPA2DPDCLIENT lpClient = (LPA2DPDCLIENT) param; + AUDIOMIXERDATA AudioMixerData = INVALIDAUDIOMIXERDATA; + + // We should not terminate the process if clients are still running + iThreadsRunning++; + + pthread_detach(lpClient->thread); + + setup_socket(lpClient->sockfd); + + // Receive type of client + result = recv_socket(lpClient->sockfd, &client_type, sizeof(client_type)); + + // This client wants to send us pcm control data + if (client_type == A2DPD_PLUGIN_CTL_WRITE) { + printf("CTL WRITE thread %d.%d started\n", client_index, lpClient->sockfd); + + if (recv_socket(lpClient->sockfd, &AudioMixerData, sizeof(AudioMixerData)) == sizeof(AudioMixerData)) { + pthread_mutex_lock(&lpClient->lpDevice->mutex); + if (AudioMixerData.volume_speaker_left != -1) + lpClient->lpDevice->mixer.volume_speaker_left = AudioMixerData.volume_speaker_left; + if (AudioMixerData.volume_speaker_left != -1) + lpClient->lpDevice->mixer.volume_speaker_right = AudioMixerData.volume_speaker_right; + if (AudioMixerData.volume_micro_left != -1) + lpClient->lpDevice->mixer.volume_micro_left = AudioMixerData.volume_micro_left; + if (AudioMixerData.volume_micro_left != -1) + lpClient->lpDevice->mixer.volume_micro_right = AudioMixerData.volume_micro_right; + pthread_mutex_unlock(&lpClient->lpDevice->mutex); + // Notify other clients + int notifyfd = make_udp_socket(); + int i = send_socket(notifyfd, &AudioMixerData, + sizeof(AudioMixerData)); + printf("Notify return %d\n", i); + close_socket(notifyfd); + } + } + // This client wants to read our control status + if (client_type == A2DPD_PLUGIN_CTL_READ) { + printf("CTL READ thread %d.%d started\n", client_index, lpClient->sockfd); + + pthread_mutex_lock(&lpClient->lpDevice->mutex); + AudioMixerData = lpClient->lpDevice->mixer; + pthread_mutex_unlock(&lpClient->lpDevice->mutex); + + send_socket(lpClient->sockfd, &AudioMixerData, sizeof(AudioMixerData)); + } + // This client wants to send us pcm stream + if (client_type == A2DPD_PLUGIN_PCM_WRITE) { + // Find an index in clients table for the mixer + pthread_mutex_lock(&lpClient->lpDevice->mutex); + for (client_index = 0; client_index < MAXCLIENTSPERDEVICE; client_index++) { + if (lpClient->lpDevice->clients[client_index].lives == 0) { + lpClient->lpDevice->clients[client_index].lives = 1; + lpClient->lpDevice->clients[client_index].ring_in = 0; + lpClient->lpDevice->clients[client_index].ring_out = 0; + break; + } + } + pthread_mutex_unlock(&lpClient->lpDevice->mutex); + + printf("PCM thread %d.%d started\n", client_index, lpClient->sockfd); + + if (client_index >= MAXCLIENTSPERDEVICE) { + printf("Client thread %d cannot start (too many clients already connected)\n", client_index); + return 0; + } + // Loop while we receive data + while (!bSigINTReceived && !bError) { + // Receive data + int32_t pcm_buffer_size = 0; + result = recv_socket(lpClient->sockfd, &pcm_buffer_size, sizeof(pcm_buffer_size)); + if (result == sizeof(pcm_buffer_size) + && pcm_buffer_size <= POOLENTRYSIZE) { + char *pcm_buffer = pool_pop(); + result = recv_socket(lpClient->sockfd, pcm_buffer, pcm_buffer_size); + + if (result == pcm_buffer_size) { + // Enqueue in bluetooth headset if we can else loose packet + pthread_mutex_lock(&lpClient->lpDevice->clients[client_index].mutex); + + // Append data to ring + int this_ring = lpClient->lpDevice->clients[client_index].ring_in; + int next_ring = ((this_ring + 1) % MAXCLIENTSRINGSIZE); + + if (next_ring != lpClient->lpDevice->clients[client_index].ring_out) { + lpClient->lpDevice->clients[client_index].ring[this_ring] = pcm_buffer; + lpClient->lpDevice->clients[client_index].ring_len[this_ring] = pcm_buffer_size; + lpClient->lpDevice->clients[client_index].ring_in = next_ring; + // We will not free that buffer, it's the bthandler thread which will do it + pcm_buffer = NULL; + } + + pthread_mutex_unlock(&lpClient->lpDevice->clients[client_index].mutex); + + // Reintegrate data in pool + if (pcm_buffer) + pool_push(pcm_buffer); + } else { + printf("[2] Receiving failed on socket %d.%d error (%d/%d bytes)\n", client_index, lpClient->sockfd, result, pcm_buffer_size); + bError = 1; + } + } else { + if (result == sizeof(pcm_buffer_size)) { + printf("[1] Receiving will not fit pool (poolentrysize=%d != pcm_buffer_size=%d)\n", POOLENTRYSIZE, pcm_buffer_size); + } else { + printf("[1] Receiving failed on socket %d.%d error (%d/%d bytes) errno=%d:%s\n", client_index, lpClient->sockfd, result, sizeof(pcm_buffer_size), errno, + strerror(errno)); + } + bError = 1; + } + } + + pthread_mutex_lock(&lpClient->lpDevice->mutex); + if (client_index >= 0) + lpClient->lpDevice->clients[client_index].lives = 0; + pthread_mutex_unlock(&lpClient->lpDevice->mutex); + } + // Say goodbye + pthread_mutex_lock(&lpClient->lpDevice->mutex); + lpClient->lpDevice->nb_clients--; + pthread_mutex_unlock(&lpClient->lpDevice->mutex); + + // Close socket + printf("Client thread %d.%d ending: %s\n", client_index, lpClient->sockfd, (bError ? (errno == EAGAIN ? "timeout" : "error") : "no error")); + close_socket(lpClient->sockfd); + + // Free client data + free(lpClient); + + // Decrease thread count + iThreadsRunning--; - // Free client data - free(lpClient); + return 0; +} - // Decrease thread count - iThreadsRunning --; +///////////////////////////////// +int audio_mixer(void *pcm_buffer, char **pcm_buffers, int *pcm_buffers_size, int vol_left, int vol_right) +///////////////////////////////// +{ + int i, j; + int satured = 0; + + // Mix audio streams 16 bits stereo channels + // We require little endianness here + int pcm_buffer_filed_size = 0; + for (j = 0; j < POOLENTRYSIZE / 4; j++) { + int32_t *pBuffer = (int32_t *) pcm_buffer; + int32_t channel_1 = 0; + int32_t channel_2 = 0; + for (i = 0; i < MAXCLIENTSPERDEVICE; i++) { + int32_t *pBuffers = (int32_t *) (pcm_buffers[i]); + if (pBuffers != NULL && (j < pcm_buffers_size[i] / 4)) { + int16_t i1 = *(((int16_t *) (pBuffers + j)) + 0); + int16_t i2 = *(((int16_t *) (pBuffers + j)) + 1); + channel_1 += i1; + channel_2 += i2; + pcm_buffer_filed_size = max(pcm_buffer_filed_size, pcm_buffers_size[i]); + } + } + //printf("Value %08X|%08X %d|%d\n", channel_1, channel_2, channel_1, channel_2); + // Stay within 16 bits per channel range + if (channel_1 > +32767) { + channel_1 = +32767; + satured++; + } + if (channel_1 < -32768) { + channel_1 = -32768; + satured++; + } + if (channel_2 > +32767) { + channel_2 = +32767; + satured++; + } + if (channel_2 < -32768) { + channel_2 = -32768; + satured++; + } - return 0; + channel_1 *= vol_left; + channel_2 *= vol_right; + // yes this can be rewritten with << if we consider max volume of 2^x + // Isn't it already done by compiler? + channel_1 /= A2DPD_VOLUME_MAX; + channel_2 /= A2DPD_VOLUME_MAX; + if(g_breversestereo) { + pBuffer[j] = (((channel_1 & 0x0000FFFF) << 16) | (channel_2 & 0x0000FFFF)); + } else { + //FIXME We have a reverse stereo I don't know why + // The following line corrects the problem but I miss the cause so + pBuffer[j] = (((channel_2&0x0000FFFF)<<16)|(channel_1&0x0000FFFF)); + pBuffer[j] = ( (channel_1 & 0xFFFF0000) | (channel_2 & 0x0000FFFF) ); + } + } + return pcm_buffer_filed_size; } // This function handle the bluetooth connection -void* bt_handler(void* param) +void *bt_handler(void *param) { - int i,j; - // We should not terminate the process if clients are still running - iThreadsRunning ++; - - LPBTA2DPPERDEVICEDATA lpDevice = (LPBTA2DPPERDEVICEDATA)param; - pthread_detach(lpDevice->thread); - - // As long as daemon is running - while(!bSigINTReceived) - { - int bError = 0; - int destroy_count = 0; - int satured = 0; - int ibytespersecond = 0; - - // Connect to the A2DP device - LPA2DP lpA2dp = NULL; - char* pcm_buffer = pool_pop(); - enum { NOSOUND, SOUND }; - int state_previous = NOSOUND; - TIMERINFO TimerInfos; - int rate = read_config_int(g_srcfilename, "a2dpd", "rate", A2DPD_FRAME_RATE); - memset(&TimerInfos, 0, sizeof(TimerInfos)); - TimerInfos.fps = (float)((((float)rate)*((float)A2DPD_FRAME_BYTES)/((float)A2DPD_BLOCK_SIZE))/1.0); - printf("New connection to bluetooth [%d hz]\n", rate); - - // As long as we can send sound - while(!bSigINTReceived && !bError) - { - int pcm_buffer_filed_size = 0; - char* pcm_buffers[MAXCLIENTSPERDEVICE]; - int pcm_buffers_size[MAXCLIENTSPERDEVICE]; - int state_current = NOSOUND; - memset(pcm_buffers, 0, sizeof(pcm_buffers)); - memset(pcm_buffers_size, 0, sizeof(pcm_buffers_size)); - - // If there are BT data, send them - //FIXME Since we read nb_clients, we should lock mutex, but it may create timer issues - // pthread_mutex_lock(&lpDevice->mutex); - - if(lpDevice->nb_clients>0) - { - // Retrieve data for client where it is available - for(i=0; iclients[i].lives) - { - pthread_mutex_lock(&lpDevice->clients[i].mutex); - - if(lpDevice->clients[i].ring_in != lpDevice->clients[i].ring_out) - { - // Get ring buffer - pcm_buffers[i] = lpDevice->clients[i].ring[lpDevice->clients[i].ring_out]; - pcm_buffers_size[i] = lpDevice->clients[i].ring_len[lpDevice->clients[i].ring_out]; - // Tell client we got them - lpDevice->clients[i].ring[lpDevice->clients[i].ring_out] = NULL; - lpDevice->clients[i].ring_len[lpDevice->clients[i].ring_out] = 0; - - // Move to next ring - int next_ring = ((lpDevice->clients[i].ring_out+1)%MAXCLIENTSRINGSIZE); - - //printf("Reading pool %d[ %d] = %p\n", i, lpDevice->clients[i].ring_out, pcm_buffers[i]); - - lpDevice->clients[i].ring_out = next_ring; - - // Remember we got some sound - state_current = SOUND; - } - - pthread_mutex_unlock(&lpDevice->clients[i].mutex); - } - } - } - //FIXME - // pthread_mutex_unlock(&lpDevice->mutex); - - // Send mixed audio stream to clients - switch(state_current) - { - case SOUND: - { - ///////////////////////////////// - // Mix what we got - ///////////////////////////////// - - static int frames = 0; - frames ++; - - // Mix audio streams 16 bits stereo channels - // We require little endianness here - pcm_buffer_filed_size = 0; - for(j=0; j+32767) { channel_1 = +32767; satured++; } - if(channel_1<-32768) { channel_1 = -32768; satured++; } - if(channel_2>+32767) { channel_2 = +32767; satured++; } - if(channel_2<-32768) { channel_2 = -32768; satured++; } - - channel_1 *= lpDevice->mixer.volume_speaker_left; - channel_2 *= lpDevice->mixer.volume_speaker_right; - // yes this can be rewritten with << if we consider max volume of 2^x - // Isn't it already done by compiler - channel_1 /= A2DPD_VOLUME_MAX; - channel_2 /= A2DPD_VOLUME_MAX; - pBuffer[j] = (((channel_1&0x0000FFFF)<<16)|(channel_2&0x0000FFFF)); - //FIXME We have a reverse stereo I don't know why - // The following line corrects the problem but I miss the cause so - // Do not uncomment it - //pBuffer[j] = (((channel_2&0x0000FFFF)<<16)|(channel_1&0x0000FFFF)); - //pBuffer[j] = ( (channel_1 & 0xFFFF0000) | (channel_2 & 0x0000FFFF) ); - } - - // Free no longer used audio blocks - for(i=0; i0) - { - // Transfer takes place by A2DPD_BLOCK_SIZE bytes blocks - int blockstart = 0; - int blocksize = A2DPD_BLOCK_SIZE; - - // Allocate A2DP if we are not connected - if(!lpA2dp) - { - lpA2dp = a2dp_new(lpDevice->addr, rate); - g_nbdeviceconnected++; - destroy_count = 0; - } - - if(lpA2dp) - { - // Send data to BT headset - while(!bError && blockstart=0) - { - destroy_count = 0; - blockstart += blocksize; - ibytespersecond += transfer; - a2dp_timer_notifyframe(&TimerInfos); - } - else - { - printf("Error in a2dp_transfer_raw\n"); - bError = 1; - } - } - } - } - break; - } - case NOSOUND: - { - if(state_previous == SOUND) - { - //printf("Sound stream ran dry!!!\n"); - } - break; - } - } - - // Free the A2DP device if needed - // When destroy_count reaches 2000 we will destroy the A2DP link - // However, destroy_count is reset whenever data are sent - destroy_count++; - if(lpA2dp && destroy_count>2000) - { - printf("Destroying lpA2dp, destroy_count is %d\n", destroy_count); - g_nbdeviceconnected--; - a2dp_destroy(lpA2dp); - lpA2dp = NULL; - } - - // Wait must take place after sending a packet - // This way, you will allow the plugin to send it's data - // And you will collect the new data - // Time reference floating because of 44100/1000 error in integer calculation - a2dp_timer_sleep(&TimerInfos, A2DPTIMERPREDELAY); - - // Display infos each second - if(TimerInfos.display>0) - { - /* - char* lpszFormat = "A2DPD: [%d,%d|%d,%d] %s %s clients=%d freq=%d[%d b/s] sleep=%d satur=%d\n"; - if(satured==0) lpszFormat = "A2DPD: [%d,%d|%d,%d] %s %s clients=%d freq=%d[%d b/s]\n"; - printf(lpszFormat, - lpDevice->mixer.volume_speaker_left, - lpDevice->mixer.volume_speaker_right, - lpDevice->mixer.volume_micro_left, - lpDevice->mixer.volume_micro_right, - (state_current==SOUND)?"playing":"silent", - lpA2dp?"connected":"disconnected", lpDevice->nb_clients, TimerInfos.display, - ibytespersecond, - satured); - // Reset all variables used - ibytespersecond=0; - satured=0; - */ - } - - state_previous = state_current; - } - pool_push(pcm_buffer); - - // Sleep a little bit before retrying - if(!bSigINTReceived) - sleep(1); - - // Free A2DP - if(lpA2dp) - { - printf("Destroying lpA2dp, end of loop\n"); - g_nbdeviceconnected--; - a2dp_destroy(lpA2dp); - lpA2dp = NULL; - } - } + int i; + // We should not terminate the process if clients are still running + iThreadsRunning++; + + LPBTA2DPPERDEVICEDATA lpDevice = (LPBTA2DPPERDEVICEDATA) param; + pthread_detach(lpDevice->thread); + + // As long as daemon is running + while (!bSigINTReceived) { + int bError = 0; + int destroy_count = 0; + int ibytespersecond = 0; + + // Connect to the A2DP device + void *lpA2dp = NULL; + char *pcm_buffer = pool_pop(); + enum { NOSOUND, SOUND }; + int state_previous = NOSOUND; + TIMERINFO TimerInfos; + int rate = read_config_int(g_srcfilename, "a2dpd", "rate", + A2DPD_FRAME_RATE); + memset(&TimerInfos, 0, sizeof(TimerInfos)); + TimerInfos.fps = (float) ((((float) rate) * ((float) A2DPD_FRAME_BYTES) / ((float) A2DPD_BLOCK_SIZE)) / 1.0); + printf("New connection to bluetooth [%d hz]\n", rate); + + // As long as we can send sound + while (!bSigINTReceived && !bError) { + int pcm_buffer_filed_size = 0; + char *pcm_buffers[MAXCLIENTSPERDEVICE]; + int pcm_buffers_size[MAXCLIENTSPERDEVICE]; + int state_current = NOSOUND; + memset(pcm_buffers, 0, sizeof(pcm_buffers)); + memset(pcm_buffers_size, 0, sizeof(pcm_buffers_size)); + + // If there are BT data, send them + //FIXME Since we read nb_clients, we should lock mutex, but it may create timer issues + // degrading sound + // pthread_mutex_lock(&lpDevice->mutex); + + if (lpDevice->nb_clients > 0) { + // Retrieve data for client where it is available + for (i = 0; i < MAXCLIENTSPERDEVICE; i++) { + if (lpDevice->clients[i].lives) { + pthread_mutex_lock(&lpDevice->clients[i].mutex); + + if (lpDevice->clients[i].ring_in != lpDevice->clients[i].ring_out) { + // Get ring buffer + pcm_buffers[i] = lpDevice->clients[i].ring[lpDevice->clients[i].ring_out]; + pcm_buffers_size[i] = lpDevice->clients[i].ring_len[lpDevice->clients[i].ring_out]; + // Tell client we got them + lpDevice->clients[i].ring[lpDevice->clients[i].ring_out] = NULL; + lpDevice->clients[i].ring_len[lpDevice->clients[i].ring_out] = 0; + + // Move to next ring + int next_ring = ((lpDevice->clients[i].ring_out + 1) % MAXCLIENTSRINGSIZE); + + //printf("Reading pool %d[ %d] = %p\n", i, lpDevice->clients[i].ring_out, pcm_buffers[i]); + + lpDevice->clients[i].ring_out = next_ring; + + // Remember we got some sound + state_current = SOUND; + } + + pthread_mutex_unlock(&lpDevice->clients[i].mutex); + } + } + } + //FIXME + // pthread_mutex_unlock(&lpDevice->mutex); + + // Send mixed audio stream to clients + switch (state_current) { + case SOUND: + pcm_buffer_filed_size = audio_mixer(pcm_buffer, pcm_buffers, pcm_buffers_size, lpDevice->mixer.volume_speaker_left, lpDevice->mixer.volume_speaker_right); + + // Free no longer used audio blocks + for (i = 0; i < MAXCLIENTSPERDEVICE; i++) { + if (pcm_buffers[i]) { + // Reintegrate data where they come from + pool_push(pcm_buffers[i]); + } + } + + ///////////////////////////////// + // Transfer data to bluetooth + ///////////////////////////////// + + if (pcm_buffer && pcm_buffer_filed_size > 0) { + // Transfer takes place by A2DPD_BLOCK_SIZE bytes blocks + int blockstart = 0; + int blocksize = A2DPD_BLOCK_SIZE; + + // Allocate A2DP if we are not connected + if (!lpA2dp) { + // Select the good device + lpDevice->bredirectalsa = read_config_int(g_srcfilename, "a2dpd", "enableredirectalsa", 0); + read_config_string(g_srcfilename, "a2dpd", "address", lpDevice->addr, sizeof(lpDevice->addr), ""); + read_config_string(g_srcfilename, "a2dpd", "alsaoutput", lpDevice->plug, sizeof(lpDevice->plug), ""); + // Allocate it + if (lpDevice->bredirectalsa) + lpA2dp = alsa_new(lpDevice->plug, rate); + else + lpA2dp = a2dp_new(lpDevice->addr, rate); + g_nbdeviceconnected++; + destroy_count = 0; + } + + if (lpA2dp) { + // Send data to BT headset + while (!bError && blockstart < pcm_buffer_filed_size) { + int transfer; + + blocksize = (pcm_buffer_filed_size < A2DPD_BLOCK_SIZE) ? pcm_buffer_filed_size : A2DPD_BLOCK_SIZE; + + if (lpDevice->bredirectalsa) + transfer = alsa_transfer_raw(lpA2dp, pcm_buffer + blockstart, blocksize); + else + transfer = a2dp_transfer_raw(lpA2dp, pcm_buffer + blockstart, blocksize); + + if (transfer >= 0) { + destroy_count = 0; + blockstart += blocksize; + ibytespersecond += transfer; + a2dp_timer_notifyframe(&TimerInfos); + } else { + printf("Error in a2dp_transfer_raw\n"); + bError = 1; + } + } + } + } + break; + case NOSOUND: + if (state_previous == SOUND) { + //printf("Sound stream ran dry!!!\n"); + } + break; + } + + // Wait must take place after sending a packet + // This way, you will allow the plugin to send it's data + // And you will collect the new data + // Time reference floating because of 44100/1000 error in integer calculation + a2dp_timer_sleep(&TimerInfos, A2DPTIMERPREDELAY); + + // Read config file changes each second + if (TimerInfos.display > 0) { + if(g_brereadconfig) { + char addr[20]; + char plug[20]; + int bredirectalsa = read_config_int(g_srcfilename, "a2dpd", "enableredirectalsa", 0); + read_config_string(g_srcfilename, "a2dpd", "address", addr, sizeof(addr), ""); + read_config_string(g_srcfilename, "a2dpd", "alsaoutput", plug, sizeof(plug), ""); + if((strcmp(addr, lpDevice->addr) != 0) || (strcmp(plug, lpDevice->plug) != 0) || (bredirectalsa != lpDevice->bredirectalsa)) { + // Force destroy, device will be recreated upon audio incoming + destroy_count=10000; + } + } + /* + char* lpszFormat = "A2DPD: [%d,%d|%d,%d] %s %s clients=%d freq=%d[%d b/s] sleep=%d satur=%d\n"; + if(satured==0) lpszFormat = "A2DPD: [%d,%d|%d,%d] %s %s clients=%d freq=%d[%d b/s]\n"; + printf(lpszFormat, + lpDevice->mixer.volume_speaker_left, + lpDevice->mixer.volume_speaker_right, + lpDevice->mixer.volume_micro_left, + lpDevice->mixer.volume_micro_right, + (state_current==SOUND)?"playing":"silent", + lpA2dp?"connected":"disconnected", lpDevice->nb_clients, TimerInfos.display, + ibytespersecond, + satured); + // Reset all variables used + ibytespersecond=0; + satured=0; + */ + } + + // Free the A2DP device if needed + // When destroy_count reaches 2000 we will destroy the A2DP link + // However, destroy_count is reset whenever data are sent + destroy_count++; + if (lpA2dp && destroy_count > 2000) { + printf("Destroying lpA2dp, destroy_count is %d\n", destroy_count); + g_nbdeviceconnected--; + if (lpDevice->bredirectalsa) + alsa_destroy(lpA2dp); + else + a2dp_destroy(lpA2dp); + lpA2dp = NULL; + } + + state_previous = state_current; + } + pool_push(pcm_buffer); + + // Sleep a little bit before retrying + if (!bSigINTReceived) + sleep(1); + + // Free A2DP + if (lpA2dp) { + printf("Destroying lpA2dp, end of loop\n"); + g_nbdeviceconnected--; + if (lpDevice->bredirectalsa) + alsa_destroy(lpA2dp); + else + a2dp_destroy(lpA2dp); + lpA2dp = NULL; + } + } - iThreadsRunning --; + iThreadsRunning--; - return 0; + return 0; } // This function handle the bluetooth connection -void* avdtp_listener(void* param) +void *avdtp_listener(void *param) { - // We should not terminate the process if clients are still running - iThreadsRunning ++; + // We should not terminate the process if clients are still running + iThreadsRunning++; - LPBTA2DPPERDEVICEDATA lpDevice = (LPBTA2DPPERDEVICEDATA)param; - pthread_detach(lpDevice->thread); + LPBTA2DPPERDEVICEDATA lpDevice = (LPBTA2DPPERDEVICEDATA) param; + pthread_detach(lpDevice->thread); - // As long as daemon is running - printf("avdtp: Accepting incoming connection\n"); - while(!bSigINTReceived) - { - int sockfd = a2dp_make_listen_socket( 25); - if(sockfd>=0) - { - while(!bSigINTReceived) - { - // Wait for incoming connections - char szRemote[64]; - uint16_t iMTU = 0; - - int new_fd = a2dp_wait_connection(sockfd, szRemote, sizeof(szRemote), &iMTU); - - if(new_fd>0) - { - printf("avdtp: socket %d: Connection from %s, mtu=%d\n", new_fd, szRemote, iMTU); - - // Loop and manage what the client sends - setup_socket(new_fd); - int iReceived = 0; - int play = 0; - int count = 0; - do - { - iReceived = a2dp_handle_avdtp_message(NULL, new_fd, NULL, NULL, 0); - if(iReceived==0) - { - printf("avdtp: socket %d: Received frame, start %s\n", new_fd, g_sCmdNew); - play=1; - count=0; - break; - } - else if(iReceived<0) - { - if(errno!=EAGAIN) - printf("avdtp: socket %d: Received failed result=%d (errno=%d:%s)\n", new_fd, iReceived, errno, strerror(errno)); - } - count++; - } - // AVDTP do not need to have a device connected, since it can establish device connection - while(!bSigINTReceived && (iReceived>=0 || errno==EAGAIN) && count<10); - printf("avdtp: socket %d: timed out\n", new_fd); - close_socket(new_fd); - - if(play&&g_sCmdNew[0]) - { - async_run_process(g_sCmdNew); - } - } - else - { - if(errno!=EAGAIN) - { - printf("a2dp_wait_connection failed (AVDTP socket) : %d (errno=%d:%s)\n", new_fd, errno, strerror(errno)); - break; - } - } - } - - close_socket(sockfd); - } - - // Sleep a little bit if we must retry - sleep(bSigINTReceived?1:0); - } + // As long as daemon is running + printf("avdtp: Accepting incoming connection\n"); + while (!bSigINTReceived) { + int sockfd = a2dp_make_listen_socket(25); + if (sockfd >= 0) { + while (!bSigINTReceived) { + // Wait for incoming connections + char szRemote[64]; + uint16_t iMTU = 0; + + int new_fd = a2dp_wait_connection(sockfd, szRemote, sizeof(szRemote), &iMTU); + + if (new_fd > 0) { + printf("avdtp: socket %d: Connection from %s, mtu=%d\n", new_fd, szRemote, iMTU); + + // Loop and manage what the client sends + setup_socket(new_fd); + int iReceived = 0; + int play = 0; + int count = 0; + do { + iReceived = a2dp_handle_avdtp_message(NULL, new_fd, NULL, NULL, 0); + if (iReceived == 0) { + printf("avdtp: socket %d: Received frame, start %s\n", new_fd, g_sCmdNew); + play = 1; + count = 0; + break; + } else if (iReceived < 0) { + if (errno != EAGAIN) + printf("avdtp: socket %d: Received failed result=%d (errno=%d:%s)\n", new_fd, iReceived, errno, strerror(errno)); + } + count++; + } + // AVDTP do not need to have a device connected, since it can establish device connection + while (!bSigINTReceived && (iReceived >= 0 || errno == EAGAIN) + && count < 10); + printf("avdtp: socket %d: timed out\n", new_fd); + close_socket(new_fd); + + if (play && g_sCmdNew[0]) { + async_run_process(g_sCmdNew); + } + } else { + if (errno != EAGAIN) { + printf("a2dp_wait_connection failed (AVDTP socket) : %d (errno=%d:%s)\n", new_fd, errno, strerror(errno)); + break; + } + } + } - iThreadsRunning --; + close_socket(sockfd); + } + // Sleep a little bit if we must retry + sleep(bSigINTReceived ? 1 : 0); + } - return 0; + iThreadsRunning--; + + return 0; } // This function handle the bluetooth connection -void* avrcp_listener(void* param) +void *avrcp_listener(void *param) { - // We should not terminate the process if clients are still running - iThreadsRunning ++; + // We should not terminate the process if clients are still running + iThreadsRunning++; + + LPBTA2DPPERDEVICEDATA lpDevice = (LPBTA2DPPERDEVICEDATA) param; + pthread_detach(lpDevice->thread); - LPBTA2DPPERDEVICEDATA lpDevice = (LPBTA2DPPERDEVICEDATA)param; - pthread_detach(lpDevice->thread); + // As long as daemon is running + printf("avrcp: Accepting incoming connection\n"); + while (!bSigINTReceived) { + int sockfd = a2dp_make_listen_socket(23); + if (sockfd >= 0) { + while (!bSigINTReceived) { + // Wait for incoming connections + char szRemote[64]; + uint16_t iMTU = 0; + + int new_fd = a2dp_wait_connection(sockfd, szRemote, + sizeof(szRemote), + &iMTU); + + if (new_fd > 0) { + printf("avrcp: socket %d: Connection from %s, mtu=%d\n", new_fd, szRemote, iMTU); + // Loop and manage what the client sends + setup_socket(new_fd); + int iReceived = 0; + do { + printf("avrcp: socket %d: Reading from %s, mtu=%d\n", new_fd, szRemote, iMTU); + errno = 0; + iReceived = a2dp_handle_avrcp_message(new_fd); + } + // AVRCP need device connected + while (g_nbdeviceconnected && !bSigINTReceived && (iReceived > 0 || errno == EAGAIN)); + printf("avrcp: socket %d: timed out\n", new_fd); + close_socket(new_fd); + } else if (errno != EAGAIN) { + printf("a2dp_wait_connection failed (AVRCP socket) : %d (errno=%d:%s)\n", new_fd, errno, strerror(errno)); + break; + } + } - // As long as daemon is running - printf("avrcp: Accepting incoming connection\n"); - while(!bSigINTReceived) - { - int sockfd = a2dp_make_listen_socket( 23); - if(sockfd>=0) - { - while(!bSigINTReceived) - { - // Wait for incoming connections - char szRemote[64]; - uint16_t iMTU = 0; - - int new_fd = a2dp_wait_connection(sockfd, szRemote, sizeof(szRemote), &iMTU); - - if(new_fd>0) - { - printf("avrcp: socket %d: Connection from %s, mtu=%d\n", new_fd, szRemote, iMTU); - // Loop and manage what the client sends - setup_socket(new_fd); - int iReceived = 0; - do - { - iReceived = a2dp_handle_avrcp_message(new_fd); - } - // AVRCP need device connected - while(g_nbdeviceconnected && !bSigINTReceived && (iReceived>0 || errno==EAGAIN)); - printf("avrcp: socket %d: timed out\n", new_fd); - close_socket(new_fd); - } - else - { - if(errno!=EAGAIN) - { - printf("a2dp_wait_connection failed (AVRCP socket) : %d (errno=%d:%s)\n", new_fd, errno, strerror(errno)); - break; - } - } - } - - close_socket(sockfd); - } - - // Sleep a little bit if we must retry - sleep(bSigINTReceived?1:0); - } + close_socket(sockfd); + } + // Sleep a little bit if we must retry + sleep(bSigINTReceived ? 1 : 0); + } - iThreadsRunning --; + iThreadsRunning--; - return 0; + return 0; } // server processing loop -void main_loop(char* addr) +void main_loop(char *addr) { - while(!bSigINTReceived) - { - // Master socket - int sockfd = make_server_socket(); - - if(sockfd>0) - { - LPBTA2DPPERDEVICEDATA lpDevice = bta2dpdevicenew(addr); - // Set pthread stack size to decrease unused memory usage - pthread_attr_t tattr; - size_t size = PTHREAD_STACK_MIN; - int ret = pthread_attr_init(&tattr); - ret = pthread_attr_setstacksize(&tattr, size); - pthread_create(&lpDevice->thread, &tattr, bt_handler, lpDevice); - pthread_create(&lpDevice->thread, &tattr, avrcp_listener, lpDevice); - pthread_create(&lpDevice->thread, &tattr, avdtp_listener, lpDevice); - - while(!bSigINTReceived) - { - int new_fd = -1; - printf("Accepting incoming tcp stream connection\n"); - new_fd=accept_socket(sockfd); - printf("Accepted %d\n", new_fd); - - // Handle connection if it is not the final dummy client - if(!bSigINTReceived) - { - LPA2DPDCLIENT lpClient = malloc(sizeof(A2DPDCLIENT)); - lpClient->lpDevice = lpDevice; - lpClient->sockfd = new_fd; - - pthread_mutex_lock(&lpClient->lpDevice->mutex); - lpClient->lpDevice->nb_clients++; - pthread_mutex_unlock(&lpClient->lpDevice->mutex); - - pthread_create(&lpClient->thread, &tattr, client_handler, lpClient); - } - else - { - close_socket(new_fd); - } - usleep(10000); - } - - close_socket(sockfd); - - // Very minor race condition here - // No dramatic consequences - // But we Must wait all client termination - // We will pthread_join one day - int icount = 0; - while(iThreadsRunning>0 && icount<30) - { - printf("A2DPD still %d clients running\n", iThreadsRunning); - icount++; - sleep(1); - } - - // Free informations on the device - bta2dpdevicefree(lpDevice); - pthread_attr_destroy(&tattr); - } - else - { - printf("Error %d: cannot get the socket errno=%d (%s)\n", sockfd, errno, strerror(errno)); - } + while (!bSigINTReceived) { + // Master socket + int sockfd = make_server_socket(); + + if (sockfd > 0) { + LPBTA2DPPERDEVICEDATA lpDevice = bta2dpdevicenew(addr); + // Set pthread stack size to decrease unused memory usage + pthread_attr_t tattr; + pthread_t havrcp, havdtp; + size_t size = PTHREAD_STACK_MIN; + int ret = pthread_attr_init(&tattr); + ret = pthread_attr_setstacksize(&tattr, size); + pthread_create(&lpDevice->thread, &tattr, bt_handler, lpDevice); + if (g_bavrcp) + pthread_create(&havrcp, &tattr, avrcp_listener, lpDevice); + if (g_bavrcp) + pthread_create(&havdtp, &tattr, avdtp_listener, lpDevice); + + while (!bSigINTReceived) { + int new_fd = -1; + printf("main_thread:Accepting incoming tcp stream connection\n"); + new_fd = accept_socket(sockfd); + printf("main_thread: Accepted %d\n", new_fd); + + // Handle connection if it is not the final dummy client + if (!bSigINTReceived && new_fd > 0) { + LPA2DPDCLIENT lpClient = malloc(sizeof(A2DPDCLIENT)); + lpClient->lpDevice = lpDevice; + lpClient->sockfd = new_fd; + + pthread_mutex_lock(&lpClient->lpDevice->mutex); + lpClient->lpDevice->nb_clients++; + pthread_mutex_unlock(&lpClient->lpDevice->mutex); + + pthread_create(&lpClient->thread, &tattr, client_handler, lpClient); + } else if (new_fd > 0) { + close_socket(new_fd); + } + usleep(10000); + } + + close_socket(sockfd); + + // Very minor race condition here + // No dramatic consequences + // But we Must wait all client termination + // We will pthread_join one day + int icount = 0; + while (iThreadsRunning > 0 && icount < 30) { + printf("A2DPD still %d clients running\n", iThreadsRunning); + icount++; + sleep(1); + } + + // Free informations on the device + bta2dpdevicefree(lpDevice); + pthread_attr_destroy(&tattr); + } else { + printf("Error %d: cannot get the socket errno=%d (%s)\n", sockfd, errno, strerror(errno)); + } - sleep(1); - } + sleep(1); + } } @@ -1045,90 +1004,73 @@ // main function int main(int argc, char *argv[]) { - int i = 0; - int fd; - struct timespec timer_resolution = { 0, 0 }; - char address[256] = ""; - char* addr = &address[0]; - char* sonorix="00:0A:56:00:C0:C2"; - //char* iphono420= "C2:00:08:F4:30:07:64"; - //char* hpheadphone= "00:0D:44:2A:17:C7"; - struct sched_param schedparam = { sched_get_priority_max(SCHED_FIFO) }; - int res=0, bFork=0, bVerbose=1, bKill=0; + int i = 0; + struct timespec timer_resolution = { 0, 0 }; + char address[256] = ""; + char *addr = &address[0]; + char *sonorix = "00:0A:56:00:C0:C2"; + //char* iphono420= "C2:00:08:F4:30:07:64"; + //char* hpheadphone= "00:0D:44:2A:17:C7"; + struct sched_param schedparam = { sched_get_priority_max(SCHED_FIFO) }; + int res = 0, bFork = 0, bVerbose = 1, bKill = 0, fd = 0; FILE *fp; pid_t pid; - // Read config values from config file - get_config_filename(g_srcfilename, sizeof(g_srcfilename)); - read_config_string(g_srcfilename, "a2dpd", "address", address, sizeof(address), sonorix); - read_config_string(g_srcfilename, "a2dpd", "cmdplay", g_sCmdPlay, sizeof(g_sCmdPlay), ""); - read_config_string(g_srcfilename, "a2dpd", "cmdpause", g_sCmdPause, sizeof(g_sCmdPause), ""); - read_config_string(g_srcfilename, "a2dpd", "cmdprev", g_sCmdPrev, sizeof(g_sCmdPrev), ""); - read_config_string(g_srcfilename, "a2dpd", "cmdnext", g_sCmdNext, sizeof(g_sCmdNext), ""); - read_config_string(g_srcfilename, "a2dpd", "cmdnew", g_sCmdNew, sizeof(g_sCmdNew), ""); - read_config_string(g_srcfilename, "a2dpd", "logfile", g_sOutputFilename, sizeof(g_sOutputFilename), "/dev/null"); - - // Parse command line parameters - for(i=1; isbc.channels*44100*2/(size*a2dp->frame_bytes); // 344.53125=channels*freq*16 bits/sizeof(buf) #define A2DPD_BLOCK_SIZE (512*1) Index: alsa-plugins/a2dplib.c =================================================================== RCS file: /cvsroot/bluetooth-alsa/btsco/alsa-plugins/a2dplib.c,v retrieving revision 1.5 diff -u -r1.5 a2dplib.c --- alsa-plugins/a2dplib.c 30 Aug 2006 14:43:21 -0000 1.5 +++ alsa-plugins/a2dplib.c 31 Aug 2006 15:58:57 -0000 @@ -79,7 +79,7 @@ #define A2DP_SERVICE_NAME "A2DP Audio Source" #define A2DP_VERSION 0x0100 -static struct sigaction actions; +static struct sigaction actions; /* sdp_record_t* a2dp_advertise_sdp(sdp_session_t* sdpSessionP) { @@ -158,1128 +158,1026 @@ void a2dp_init(void) __attribute__ ((constructor)); void a2dp_exit(void) __attribute__ ((destructor)); */ -void memcpy_changeendian( void* dst, const void *src, int size) +void memcpy_changeendian(void *dst, const void *src, int size) { - int i; - const uint16_t* ptrsrc=src; - uint16_t* ptrdst=dst; - for(i = 0; i < size/2; i ++) - { - *ptrdst++ = htons(*ptrsrc++); - } + int i; + const uint16_t *ptrsrc = src; + uint16_t *ptrdst = dst; + for (i = 0; i < size / 2; i++) { + *ptrdst++ = htons(*ptrsrc++); + } } // Prepare packet headers -void init_request(struct avdtp_header * header, int request_id) +void init_request(struct avdtp_header *header, int request_id) { - static int transaction = 0; + static int transaction = 0; - header->packet_type = PACKET_TYPE_SINGLE; - header->message_type = MESSAGE_TYPE_COMMAND; - header->transaction_label = transaction; - header->signal_id = request_id; + header->packet_type = PACKET_TYPE_SINGLE; + header->message_type = MESSAGE_TYPE_COMMAND; + header->transaction_label = transaction; + header->signal_id = request_id; - // clear rfa bits - header->rfa0 = 0; + // clear rfa bits + header->rfa0 = 0; - transaction = (transaction + 1) & 0xf; + transaction = (transaction + 1) & 0xf; } // Analyse the SEIDs the sink has sent to us -int process_seid(int s, struct acp_seid_info * get_seid_resp, unsigned short *psm, sbc_t *sbc) +int process_seid(int s, struct acp_seid_info *get_seid_resp, unsigned short *psm, sbc_t * sbc) { - int v, size; - int seid = get_seid_resp->acp_seid; - struct getcap_req put_req; - struct getcap_resp cap_resp; - struct set_config s_config; - struct set_config_resp s_resp; - struct stream_cmd open_stream; - struct open_stream_rsp open_resp; - - memset(&put_req, 0, sizeof(put_req)); - init_request(&put_req.header, AVDTP_GET_CAPABILITIES); - put_req.acp_seid = seid; + int v, size; + int seid = get_seid_resp->acp_seid; + struct getcap_req put_req; + struct getcap_resp cap_resp; + struct set_config s_config; + struct set_config_resp s_resp; + struct stream_cmd open_stream; + struct open_stream_rsp open_resp; + + memset(&put_req, 0, sizeof(put_req)); + init_request(&put_req.header, AVDTP_GET_CAPABILITIES); + put_req.acp_seid = seid; + + if (write(s, &put_req, sizeof(put_req)) != sizeof(put_req)) { + DBG("Couldn't request capabilities for SEID = %d", seid); + return (-1); + } - if (write(s, &put_req, sizeof(put_req)) != sizeof(put_req)) - { - DBG("Couldn't request capabilities for SEID = %d", seid); - return (-1); - } + if (read(s, &cap_resp, sizeof(cap_resp)) < sizeof(cap_resp) || + cap_resp.header.message_type == MESSAGE_TYPE_REJECT || cap_resp.media_type != AUDIO_MEDIA_TYPE || cap_resp.media_codec_type != SBC_MEDIA_CODEC_TYPE) { + DBG("Didn't receive SBC codec parameters (first) for SEID = %d", seid); + return (-1); + } - if (read(s, &cap_resp, sizeof(cap_resp)) < sizeof(cap_resp) || - cap_resp.header.message_type == MESSAGE_TYPE_REJECT || - cap_resp.media_type != AUDIO_MEDIA_TYPE || - cap_resp.media_codec_type != SBC_MEDIA_CODEC_TYPE) { - DBG("Didn't receive SBC codec parameters (first) for SEID = %d", seid); - return (-1); - } + DBG("Got capabilities response:\nservcap_cap=%d, servcap_len=%d,\ncap_type=%d, length=%d, media_type=%d, codec=%d", + cap_resp.serv_cap, cap_resp.serv_cap_len, cap_resp.cap_type, cap_resp.length, cap_resp.media_type, cap_resp.media_codec_type); - DBG("Got capabilities response:\nservcap_cap=%d, servcap_len=%d,\ncap_type=%d, length=%d, media_type=%d, codec=%d", - cap_resp.serv_cap, - cap_resp.serv_cap_len, - cap_resp.cap_type, - cap_resp.length, - cap_resp.media_type, - cap_resp.media_codec_type - ); - - memset(&s_config, 0, sizeof(s_config)); - init_request(&s_config.header, AVDTP_SET_CONFIGURATION); - s_config.serv_cap = MEDIA_TRANSPORT_CATEGORY; - s_config.acp_seid = seid; - s_config.int_seid = 1; //FIXME how should I choose the int_seid?? - s_config.cap_type = MEDIA_CODEC; - s_config.length = 6; - s_config.media_type = AUDIO_MEDIA_TYPE; - s_config.media_codec_type = SBC_MEDIA_CODEC_TYPE; - - switch(sbc->channels) { - case 1: - v = 8; - break; - case 2: - default: - v = 2; - break; - } - s_config.codec_elements.sbc_elements.channel_mode = v; + memset(&s_config, 0, sizeof(s_config)); + init_request(&s_config.header, AVDTP_SET_CONFIGURATION); + s_config.serv_cap = MEDIA_TRANSPORT_CATEGORY; + s_config.acp_seid = seid; + s_config.int_seid = 1; //FIXME how should I choose the int_seid?? + s_config.cap_type = MEDIA_CODEC; + s_config.length = 6; + s_config.media_type = AUDIO_MEDIA_TYPE; + s_config.media_codec_type = SBC_MEDIA_CODEC_TYPE; + + switch (sbc->channels) { + case 1: + v = 8; + break; + case 2: + default: + v = 2; + break; + } + s_config.codec_elements.sbc_elements.channel_mode = v; - switch(sbc->rate) { - case 16000: - v = 8; - break; - case 32000: - v = 4; - break; - case 48000: - v = 1; - break; - case 44100: - default: - v = 2; - break; - } - s_config.codec_elements.sbc_elements.frequency = v; - s_config.codec_elements.sbc_elements.allocation_method = 1 << 1; + switch (sbc->rate) { + case 16000: + v = 8; + break; + case 32000: + v = 4; + break; + case 48000: + v = 1; + break; + case 44100: + default: + v = 2; + break; + } + s_config.codec_elements.sbc_elements.frequency = v; + s_config.codec_elements.sbc_elements.allocation_method = 1 << 1; - switch(sbc->subbands) { - case 4: - v = 2; - break; - case 8: - default: - v = 1; - break; - } - s_config.codec_elements.sbc_elements.subbands = v; + switch (sbc->subbands) { + case 4: + v = 2; + break; + case 8: + default: + v = 1; + break; + } + s_config.codec_elements.sbc_elements.subbands = v; - switch(sbc->blocks) { - case 4: - v = 8; - break; - case 8: - v = 4; - break; - case 12: - v = 2; - break; - case 16: - default: - v = 1; - break; - } - s_config.codec_elements.sbc_elements.block_length = v; - s_config.codec_elements.sbc_elements.min_bitpool = cap_resp.codec_elements.sbc_elements.min_bitpool; - s_config.codec_elements.sbc_elements.max_bitpool = cap_resp.codec_elements.sbc_elements.max_bitpool; + switch (sbc->blocks) { + case 4: + v = 8; + break; + case 8: + v = 4; + break; + case 12: + v = 2; + break; + case 16: + default: + v = 1; + break; + } + s_config.codec_elements.sbc_elements.block_length = v; + s_config.codec_elements.sbc_elements.min_bitpool = cap_resp.codec_elements.sbc_elements.min_bitpool; + s_config.codec_elements.sbc_elements.max_bitpool = cap_resp.codec_elements.sbc_elements.max_bitpool; - if (!(cap_resp.codec_elements.sbc_elements.channel_mode & s_config.codec_elements.sbc_elements.channel_mode)) { - DBG("headset does not support this channel mode"); - } + if (!(cap_resp.codec_elements.sbc_elements.channel_mode & s_config.codec_elements.sbc_elements.channel_mode)) { + DBG("headset does not support this channel mode"); + } - if (!(cap_resp.codec_elements.sbc_elements.frequency & s_config.codec_elements.sbc_elements.frequency)) { - DBG("headset does not support this frequency"); - } + if (!(cap_resp.codec_elements.sbc_elements.frequency & s_config.codec_elements.sbc_elements.frequency)) { + DBG("headset does not support this frequency"); + } - if (!(cap_resp.codec_elements.sbc_elements.allocation_method & s_config.codec_elements.sbc_elements.allocation_method)) { - DBG("headset does not support this allocation_method"); - } + if (!(cap_resp.codec_elements.sbc_elements.allocation_method & s_config.codec_elements.sbc_elements.allocation_method)) { + DBG("headset does not support this allocation_method"); + } - if (!(cap_resp.codec_elements.sbc_elements.subbands & s_config.codec_elements.sbc_elements.subbands)) { - DBG("headset does not support this subbands setting"); - } + if (!(cap_resp.codec_elements.sbc_elements.subbands & s_config.codec_elements.sbc_elements.subbands)) { + DBG("headset does not support this subbands setting"); + } - if (write(s, &s_config, sizeof(s_config)) != sizeof(s_config)) { - DBG("couldn't set config seid = %d", seid); - return (-1); - } + if (write(s, &s_config, sizeof(s_config)) != sizeof(s_config)) { + DBG("couldn't set config seid = %d", seid); + return (-1); + } - size = read(s, &s_resp, sizeof(s_resp)); - DBG("Got Set Configurations Response (%d bytes:msgtype=%d,pkttype=%d,lbl=%d,sig=%d,rfa=%d)", - size, - s_resp.header.message_type, - s_resp.header.packet_type, - s_resp.header.transaction_label, - s_resp.header.signal_id, - s_resp.header.rfa0); - - memset(&open_stream, 0, sizeof(open_stream)); - init_request(&open_stream.header, AVDTP_OPEN); - open_stream.acp_seid = seid; - - if (write(s, &open_stream, sizeof(open_stream)) != sizeof(open_stream)) { - DBG("Couldn't open stream SEID = %d", seid); - return (-1); - } + size = read(s, &s_resp, sizeof(s_resp)); + DBG("Got Set Configurations Response (%d bytes:msgtype=%d,pkttype=%d,lbl=%d,sig=%d,rfa=%d)", + size, s_resp.header.message_type, s_resp.header.packet_type, s_resp.header.transaction_label, s_resp.header.signal_id, s_resp.header.rfa0); + + memset(&open_stream, 0, sizeof(open_stream)); + init_request(&open_stream.header, AVDTP_OPEN); + open_stream.acp_seid = seid; + + if (write(s, &open_stream, sizeof(open_stream)) != sizeof(open_stream)) { + DBG("Couldn't open stream SEID = %d", seid); + return (-1); + } - if (read(s, &open_resp, sizeof(open_resp)) < sizeof(open_resp) - 1 || - open_resp.header.message_type == MESSAGE_TYPE_REJECT) { - DBG("Didn't receive open response confirm for SEID = %d", seid); - return (-1); - } + if (read(s, &open_resp, sizeof(open_resp)) < sizeof(open_resp) - 1 || open_resp.header.message_type == MESSAGE_TYPE_REJECT) { + DBG("Didn't receive open response confirm for SEID = %d", seid); + return (-1); + } - *psm = 25; - return 0; + *psm = 25; + return 0; } int test_sdp(dst) { int result = 0; - + return result; } // Connecting on PSM 25 -int do_connect(bdaddr_t *src, bdaddr_t *dst, unsigned short psm, uint16_t *mtu) +int do_connect(bdaddr_t * src, bdaddr_t * dst, unsigned short psm, uint16_t * mtu) { - struct sockaddr_l2 addr; - struct l2cap_options opts; - int sk; - unsigned int opt; - int tries; - - sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP); - if (sk < 0) { - DBG( "Can't create socket. %s(%d)", - strerror(errno), errno); - return -1; - } - - // Set connection timeout - struct timeval t = { 3, 0 }; - setsockopt( sk, SOL_SOCKET, SO_SNDTIMEO, &t, sizeof(t)); - setsockopt( sk, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof(t)); - - memset(&addr, 0, sizeof(addr)); - addr.l2_family = AF_BLUETOOTH; - bacpy(&addr.l2_bdaddr, src); - if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - DBG( "Can't bind socket. %s(%d)", - strerror(errno), errno); - return -1; - } - - /* Get default options */ - opt = sizeof(opts); - if (getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, &opt) < 0) { - DBG( "Can't get default L2CAP options. %s(%d)", - strerror(errno), errno); - return -1; - } + struct sockaddr_l2 addr; + struct l2cap_options opts; + int sk; + unsigned int opt; + int tries; + + sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP); + if (sk < 0) { + DBG("Can't create socket. %s(%d)", strerror(errno), errno); + return -1; + } + // Set connection timeout + struct timeval t = { 3, 0 }; + setsockopt(sk, SOL_SOCKET, SO_SNDTIMEO, &t, sizeof(t)); + setsockopt(sk, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof(t)); + + memset(&addr, 0, sizeof(addr)); + addr.l2_family = AF_BLUETOOTH; + bacpy(&addr.l2_bdaddr, src); + if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + DBG("Can't bind socket. %s(%d)", strerror(errno), errno); + return -1; + } - /* Set new options */ - if(mtu && *mtu) { - opts.omtu = *mtu; - //opts.imtu = *mtu; - } - if (setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, opt) < 0) { - DBG( "Can't set L2CAP options. %s(%d)", - strerror(errno), errno); - return -1; - } + /* Get default options */ + opt = sizeof(opts); + if (getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, &opt) < 0) { + DBG("Can't get default L2CAP options. %s(%d)", strerror(errno), errno); + return -1; + } - memset(&addr, 0, sizeof(addr)); - addr.l2_family = AF_BLUETOOTH; - bacpy(&addr.l2_bdaddr, dst); - addr.l2_psm = htobs(psm); - - tries = 0; - while (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - DBG("Can't connect to %s on psm %d. %s(%d)", - batostr(&addr.l2_bdaddr), psm, strerror(errno), errno); - if(++tries > NBSDPRETRIESMAX) { - close(sk); - return -1; - } - sleep(1); - } - opt = sizeof(opts); - if (getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, &opt) < 0) { - DBG( "Can't get L2CAP options. %s(%d)", - strerror(errno), errno); - close(sk); - return -1; - } + /* Set new options */ + if (mtu && *mtu) { + opts.omtu = *mtu; + //opts.imtu = *mtu; + } + if (setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, opt) < 0) { + DBG("Can't set L2CAP options. %s(%d)", strerror(errno), errno); + return -1; + } - //DBG( "Connected psm=%d sk=%d [imtu %d, omtu %d, flush_to %d]", psm, sk, opts.imtu, opts.omtu, opts.flush_to); + memset(&addr, 0, sizeof(addr)); + addr.l2_family = AF_BLUETOOTH; + bacpy(&addr.l2_bdaddr, dst); + addr.l2_psm = htobs(psm); + + tries = 0; + while (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + DBG("Can't connect to %s on psm %d. %s(%d)", batostr(&addr.l2_bdaddr), psm, strerror(errno), errno); + if (++tries > NBSDPRETRIESMAX) { + close(sk); + return -1; + } + sleep(1); + } + opt = sizeof(opts); + if (getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, &opt) < 0) { + DBG("Can't get L2CAP options. %s(%d)", strerror(errno), errno); + close(sk); + return -1; + } + //DBG( "Connected psm=%d sk=%d [imtu %d, omtu %d, flush_to %d]", psm, sk, opts.imtu, opts.omtu, opts.flush_to); - if (mtu) - *mtu = opts.omtu; + if (mtu) + *mtu = opts.omtu; - return sk; + return sk; } // Detect whether A2DP Sink is present at the destination or not -int detect_a2dp(bdaddr_t *src, bdaddr_t *dst, unsigned short *psm, unsigned long *flags) +int detect_a2dp(bdaddr_t * src, bdaddr_t * dst, unsigned short *psm, unsigned long *flags) { - sdp_session_t *sess; - sdp_list_t *attrid, *search, *seq, *next; - sdp_data_t *pdlist; - uuid_t group; - uint32_t range = 0x0000ffff; - int err; - int tries; + sdp_session_t *sess; + sdp_list_t *attrid, *search, *seq, *next; + sdp_data_t *pdlist; + uuid_t group; + uint32_t range = 0x0000ffff; + int err; + int tries; // Try to connect an L2CAP socket to the sdp psm with short timeout for user interaction int tmpsk = do_connect(src, dst, 1, NULL); - if(tmpsk>0) - { + if (tmpsk > 0) { close(tmpsk); - } - else - { - DBG( "Warning: failed to connect to SDP server"); + } else { + DBG("Warning: failed to connect to SDP server"); return -1; } - tries = 0; - while(!(sess = sdp_connect(src, dst, SDP_RETRY_IF_BUSY))) { - DBG("retrying sdp connect: %s", strerror(errno)); - if(++tries > NBSDPRETRIESMAX) { - break; - } - sleep(1); - } - if (!sess) { - DBG( "Warning: failed to connect to SDP server"); - if(psm) *psm = 25; - if(flags) *flags = 0; - return 0; - } - - /* 0x1108->all? 0x1101->rf sink 0x111e->handsfree 0x1108->headset */ - sdp_uuid16_create(&group, 0x110d); - search = sdp_list_append(0, &group); - attrid = sdp_list_append(0, &range); - err = sdp_service_search_attr_req(sess, search, - SDP_ATTR_REQ_RANGE, attrid, &seq); - sdp_list_free(search, 0); - sdp_list_free(attrid, 0); - - if (err) { - DBG( "Service Search failed: %s", strerror(errno)); - sdp_close(sess); - return -1; - } - - for (; seq; seq = next) { - sdp_record_t *rec = (sdp_record_t *) seq->data; + tries = 0; + while (!(sess = sdp_connect(src, dst, SDP_RETRY_IF_BUSY))) { + DBG("retrying sdp connect: %s", strerror(errno)); + if (++tries > NBSDPRETRIESMAX) { + break; + } + sleep(1); + } + if (!sess) { + DBG("Warning: failed to connect to SDP server"); + if (psm) + *psm = 25; + if (flags) + *flags = 0; + return 0; + } - DBG( "Found A2DP Sink"); - if (psm) - *psm = 25; - - next = seq->next; - free(seq); - sdp_record_free(rec); - } + /* 0x1108->all? 0x1101->rf sink 0x111e->handsfree 0x1108->headset */ + sdp_uuid16_create(&group, 0x110d); + search = sdp_list_append(0, &group); + attrid = sdp_list_append(0, &range); + err = sdp_service_search_attr_req(sess, search, SDP_ATTR_REQ_RANGE, attrid, &seq); + sdp_list_free(search, 0); + sdp_list_free(attrid, 0); + + if (err) { + DBG("Service Search failed: %s", strerror(errno)); + sdp_close(sess); + return -1; + } - sdp_uuid16_create(&group, PNP_INFO_SVCLASS_ID); - search = sdp_list_append(0, &group); - attrid = sdp_list_append(0, &range); - err = sdp_service_search_attr_req(sess, search, - SDP_ATTR_REQ_RANGE, attrid, &seq); - sdp_list_free(search, 0); - sdp_list_free(attrid, 0); - - if (err) - goto done; - - if (flags) - *flags = 0; - - for (; seq; seq = next) { - sdp_record_t *rec = (sdp_record_t *) seq->data; - uint16_t vendor, product, version; - - pdlist = sdp_data_get(rec, 0x0201); - vendor = pdlist ? pdlist->val.uint16 : 0x0000; - - pdlist = sdp_data_get(rec, 0x0202); - product = pdlist ? pdlist->val.uint16 : 0x0000; - - pdlist = sdp_data_get(rec, 0x0203); - version = pdlist ? pdlist->val.uint16 : 0x0000; - - DBG( "Product ID %04x:%04x:%04x", vendor, product, version); - - if (vendor == 0x1310 && product == 0x0100 && version == 0x0104) { - DBG( "Enabling GCT media payload workaround"); - if (flags) - *flags |= NONSPECAUDIO; - } + for (; seq; seq = next) { + sdp_record_t *rec = (sdp_record_t *) seq->data; - next = seq->next; - free(seq); - sdp_record_free(rec); - } + DBG("Found A2DP Sink"); + if (psm) + *psm = 25; + + next = seq->next; + free(seq); + sdp_record_free(rec); + } -done: - sdp_close(sess); - return 0; -} - -int connect_stream(bdaddr_t *src, bdaddr_t *dst, int *cmdfd_return, sbc_t *sbc, int* seid_return, int* omtu) -{ - int cmdfd=-1; - struct sepd_req discover_req; - struct sepd_resp discover_resp; - struct stream_cmd start_stream; - struct start_stream_rsp start_resp; - int seid, nb_seid; - int size; - int i; - unsigned short psm_cmd,psm_stream=25; - unsigned long flags = 0; - int streamfd = -1; - uint16_t mtu = 0; - int tries, res; - - if (detect_a2dp(src, dst, &psm_cmd, &flags) < 0) { - DBG( "could not find A2DP services on device %s", batostr(dst)); - return -1; - } - else { - DBG( "Found A2DP Sink at the destination (psm_cmd=%d)", psm_cmd); - } + sdp_uuid16_create(&group, PNP_INFO_SVCLASS_ID); + search = sdp_list_append(0, &group); + attrid = sdp_list_append(0, &range); + err = sdp_service_search_attr_req(sess, search, SDP_ATTR_REQ_RANGE, attrid, &seq); + sdp_list_free(search, 0); + sdp_list_free(attrid, 0); + + if (err) + goto done; + + if (flags) + *flags = 0; + + for (; seq; seq = next) { + sdp_record_t *rec = (sdp_record_t *) seq->data; + uint16_t vendor, product, version; + + pdlist = sdp_data_get(rec, 0x0201); + vendor = pdlist ? pdlist->val.uint16 : 0x0000; + + pdlist = sdp_data_get(rec, 0x0202); + product = pdlist ? pdlist->val.uint16 : 0x0000; + + pdlist = sdp_data_get(rec, 0x0203); + version = pdlist ? pdlist->val.uint16 : 0x0000; + + DBG("Product ID %04x:%04x:%04x", vendor, product, version); + + if (vendor == 0x1310 && product == 0x0100 && version == 0x0104) { + DBG("Enabling GCT media payload workaround"); + if (flags) + *flags |= NONSPECAUDIO; + } + + next = seq->next; + free(seq); + sdp_record_free(rec); + } - psm_cmd=25; - cmdfd = do_connect(src, dst, psm_cmd, &mtu); - if (cmdfd < 0) { - DBG( "cannot open psm_cmd = %d", psm_cmd); - return -1; - } + done: + sdp_close(sess); + return 0; +} + +int connect_stream(bdaddr_t * src, bdaddr_t * dst, int *cmdfd_return, sbc_t * sbc, int *seid_return, int *omtu) +{ + int cmdfd = -1; + struct sepd_req discover_req; + struct sepd_resp discover_resp; + struct stream_cmd start_stream; + struct start_stream_rsp start_resp; + int seid, nb_seid; + int size; + int i; + unsigned short psm_cmd, psm_stream = 25; + unsigned long flags = 0; + int streamfd = -1; + uint16_t mtu = 0; + int tries, res; - // avdt_discover_req - memset(&discover_req, 0, sizeof(discover_req)); - init_request(&discover_req.header, AVDTP_DISCOVER); - - res=write(cmdfd, &discover_req, sizeof(discover_req)); - if (res != sizeof(discover_req)) { - DBG("couldn't send avdtp_discover (res=%d)", res); - close(cmdfd); - return -1; - } - else { - DBG("Sent the Stream End Point Discovery Command"); - } - tries = 0; - memset(&discover_resp, 0, sizeof(discover_resp)); + if (detect_a2dp(src, dst, &psm_cmd, &flags) < 0) { + DBG("could not find A2DP services on device %s", batostr(dst)); + return -1; + } else { + DBG("Found A2DP Sink at the destination (psm_cmd=%d)", psm_cmd); + } - // SONORIX sends us a discover signal we should answer but we will discard - while(tries<10) - { - size=a2dp_handle_avdtp_message(NULL, cmdfd, &discover_req.header, &discover_resp.header, sizeof(discover_resp)); - if(size>0) - { - // Answer to what we send - break; - } - else - { - // Not answer - usleep(100); - tries++; - } - } + psm_cmd = 25; + cmdfd = do_connect(src, dst, psm_cmd, &mtu); + if (cmdfd < 0) { + DBG("cannot open psm_cmd = %d", psm_cmd); + return -1; + } + // avdt_discover_req + memset(&discover_req, 0, sizeof(discover_req)); + init_request(&discover_req.header, AVDTP_DISCOVER); + + res = write(cmdfd, &discover_req, sizeof(discover_req)); + if (res != sizeof(discover_req)) { + DBG("couldn't send avdtp_discover (res=%d)", res); + close(cmdfd); + return -1; + } else { + DBG("Sent the Stream End Point Discovery Command"); + } + tries = 0; + memset(&discover_resp, 0, sizeof(discover_resp)); - if (size > sizeof(discover_resp.header)) - { - DBG("Got a Stream End Point Discovery (%d bytes) Response (msgtype=%d,pkttype=%d,lbl=%d,sig=%d,rfa=%d)", - size, - discover_resp.header.message_type, - discover_resp.header.packet_type, - discover_resp.header.transaction_label, - discover_resp.header.signal_id, - discover_resp.header.rfa0); - for(i=0; i 0) { + // Answer to what we send + break; + } else { + // Not answer + usleep(100); + tries++; + } + } - seid = -1; - nb_seid = (size-sizeof(discover_resp.header))/sizeof(struct acp_seid_info); + if (size > sizeof(discover_resp.header)) { + DBG("Got a Stream End Point Discovery (%d bytes) Response (msgtype=%d,pkttype=%d,lbl=%d,sig=%d,rfa=%d)", + size, discover_resp.header.message_type, discover_resp.header.packet_type, discover_resp.header.transaction_label, discover_resp.header.signal_id, discover_resp.header.rfa0); + for (i = 0; i < size; i++) + printf("%02X", (int) (*(((char *) &discover_resp) + i))); + printf("\n"); + } else { + DBG("couldn't get avdtp_discover (size=%d, min=%d, max=%d)", size, sizeof(discover_resp.header), sizeof(discover_resp)); + close(cmdfd); + return -1; + } - DBG("received %d capabilities", nb_seid); + seid = -1; + nb_seid = (size - sizeof(discover_resp.header)) / sizeof(struct acp_seid_info); - for(i=0; i < nb_seid; i++) - { - if (process_seid(cmdfd, &discover_resp.infos[i], &psm_stream, sbc) == 0) - { - seid = discover_resp.infos[i].acp_seid; - break; - } - } + DBG("received %d capabilities", nb_seid); - if(seid == -1) { - //We have not found the seid that we want - DBG("couldn't locate the correct seid"); - return -1; - } + for (i = 0; i < nb_seid; i++) { + if (process_seid(cmdfd, &discover_resp.infos[i], &psm_stream, sbc) == 0) { + seid = discover_resp.infos[i].acp_seid; + break; + } + } - // open the stream - streamfd = do_connect(src, dst, psm_stream, &mtu); - if (streamfd < 0) { - DBG("cannot open psm_stream = %d", psm_stream); - return -1; - } + if (seid == -1) { + //We have not found the seid that we want + DBG("couldn't locate the correct seid"); + return -1; + } + // open the stream + streamfd = do_connect(src, dst, psm_stream, &mtu); + if (streamfd < 0) { + DBG("cannot open psm_stream = %d", psm_stream); + return -1; + } + // start the stream - // start the stream + memset(&start_stream, 0, sizeof(start_stream)); + init_request(&start_stream.header, AVDTP_START); + start_stream.acp_seid = seid; + + if (write(cmdfd, &start_stream, sizeof(start_stream)) != sizeof(start_stream)) { + DBG("couldn't send start_stream"); + close(streamfd); + close(cmdfd); + return -1; + } - memset(&start_stream, 0, sizeof(start_stream)); - init_request(&start_stream.header, AVDTP_START); - start_stream.acp_seid = seid; - - if (write(cmdfd, &start_stream, sizeof(start_stream)) != sizeof(start_stream)) { - DBG("couldn't send start_stream"); - close(streamfd); - close(cmdfd); - return -1; - } + DBG("Sent stream start(seid=%d)", seid); - DBG("Sent stream start(seid=%d)", seid); + if (read(cmdfd, &start_resp, sizeof(start_resp)) < sizeof(start_resp) - 2 || start_resp.header.message_type == MESSAGE_TYPE_REJECT) { + DBG("didn't receive start_resp confirm for seid = %d", seid); + close(streamfd); + close(cmdfd); + return (-1); + } - if (read(cmdfd, &start_resp, sizeof(start_resp)) < sizeof(start_resp) - 2 ||start_resp.header.message_type == MESSAGE_TYPE_REJECT) { - DBG("didn't receive start_resp confirm for seid = %d", seid); - close(streamfd); - close(cmdfd); - return (-1); - } + DBG("Got start stream confirm"); - DBG("Got start stream confirm"); + *omtu = A2DPMAXIMUMTRANSFERUNITSIZE; //mtu; + *seid_return = seid; + *cmdfd_return = cmdfd; + return streamfd; +} + +typedef struct snd_pcm_a2dp { + bdaddr_t src; + bdaddr_t dst; + int sk; + int control_sk; + sbc_t sbc; + unsigned char buf[1024]; // contain sbc encoded data, incrementally filled + unsigned int len; // number of valid bytes in buf + unsigned int frame_bytes; // fixed when initializing + + char bufe[BUFS]; // temporary encoding buffer + int lenbufe; //=0; + + time_t timestamp; //=0; + uint16_t seq_num; //=1; + int frame_count; //=0; // Number of sbc frames in one AVDTP packet + + int mtu; //=A2DPMAXIMUMTRANSFERUNITSIZE + int seid; + + // Used to control stream from headset + int stop_writing; // = 0; + int pause_writing; // = 0; + pthread_t hListenThread; - *omtu = A2DPMAXIMUMTRANSFERUNITSIZE; //mtu; - *seid_return = seid; - *cmdfd_return = cmdfd; - return streamfd; -} - -typedef struct snd_pcm_a2dp -{ - bdaddr_t src; - bdaddr_t dst; - int sk; - int control_sk; - sbc_t sbc; - unsigned char buf[1024]; // contain sbc encoded data, incrementally filled - unsigned int len; // number of valid bytes in buf - unsigned int frame_bytes; // fixed when initializing - - char bufe[BUFS]; // temporary encoding buffer - int lenbufe;//=0; - - time_t timestamp;//=0; - uint16_t seq_num;//=1; - int frame_count;//=0; // Number of sbc frames in one AVDTP packet - - int mtu;//=A2DPMAXIMUMTRANSFERUNITSIZE - int seid; - - // Used to control stream from headset - int stop_writing;// = 0; - int pause_writing;// = 0; - pthread_t hListenThread; - } snd_pcm_a2dp_t; // We have pcm data to send through bluetooth -int a2dp_transfer_raw(LPA2DP a2dp, const char* pcm_buffer, int pcm_buffer_size) +int a2dp_transfer_raw(LPA2DP a2dp, const char *pcm_buffer, int pcm_buffer_size) { - // No error - int result = 0; - struct media_packet_header packet_header; - struct media_payload_header payload_header; - int codesize,datatoread; - int written; - - // Check parameter - if(a2dp==0 || pcm_buffer==0 || pcm_buffer_size==0) return EINVAL; - - // How much data can be encoded by sbc at a time? - // 16 bits * 2 channels * 16 blocks * 8 subbands = 4096bits = 512 o - codesize=a2dp->sbc.subbands*a2dp->sbc.blocks*a2dp->sbc.channels*2; - // 44 bitpool? - //codesize=a2dp->sbc.bitpool*a2dp->sbc.subbands*a2dp->sbc.blocks/8; - datatoread=min(codesize,pcm_buffer_size); - - // Enqueue data in bufe - if(a2dp->lenbufe+datatoread < BUFS) - { - // Append data to bufe, for sbc encoding - memcpy_changeendian(a2dp->bufe+a2dp->lenbufe, pcm_buffer, datatoread); - a2dp->lenbufe+=datatoread; - } - else - { - datatoread=0; - } - - - // If bufe is full, encode - if(a2dp->lenbufe>=codesize) - { - // Enough data to encode (sbc wants 1k blocks) - int encoded; - encoded = sbc_encode(&(a2dp->sbc), a2dp->bufe, codesize); //encode - - if (encoded <= 0) - return encoded; - - memmove(a2dp->bufe, a2dp->bufe + encoded, a2dp->lenbufe - encoded); // Shift the bufe - a2dp->lenbufe -= encoded; - - // Send data through bluetooth - if(a2dp->len + a2dp->sbc.len >= a2dp->mtu) - { - // time to prepare and send the packet - memset(&payload_header, 0, sizeof(payload_header)); - memset(&packet_header, 0, sizeof(packet_header)); - payload_header.frame_count=a2dp->frame_count; - packet_header.v = 2; - packet_header.pt = 1; - packet_header.cc = 0; - packet_header.sequence_number = htons(a2dp->seq_num); - packet_header.timestamp = htonl(a2dp->timestamp); - packet_header.ssrc = htonl(1); - - a2dp->timestamp += (a2dp->sbc.blocks+1)*4 * (a2dp->sbc.subbands+1)*4; - memcpy(a2dp->buf, &packet_header, sizeof(packet_header)); - memcpy(a2dp->buf + sizeof(packet_header), &payload_header, sizeof(payload_header)); - if(a2dp->sk>0) - { - // Check if data are to be read - // Not seen a device showing this yet - fd_set readfds; - struct timeval zero_timeout = {0,0}; - FD_ZERO(&readfds); - FD_SET(a2dp->sk, &readfds); - int iselect=select(1, &readfds, NULL, NULL, &zero_timeout); - if(iselect>0) - { - if(FD_ISSET(a2dp->sk, &readfds)) - { - a2dp_handle_avdtp_message(a2dp, a2dp->sk, NULL, NULL, 0); - } - } - // Pause? - // The value 0 have never been tested - // However, we may safely simulate a failed write - if(!a2dp->pause_writing) - { - // Send our data - if((written = write(a2dp->sk,a2dp->buf,a2dp->len)) != a2dp->len) - { - // Error while sending data - DBG("Wrote %d not %d bytes; (errno=%d:%s)", written, a2dp->len, errno, - strerror(errno)); - result = written; - } - result = written; - } - else - { - // Make the upper layer believe we sent data - result = a2dp->len; - } - } + // No error + int result = 0; + struct media_packet_header packet_header; + struct media_payload_header payload_header; + int codesize, datatoread; + int written; + + // Check parameter + if (a2dp == 0 || pcm_buffer == 0 || pcm_buffer_size == 0) + return EINVAL; + + // How much data can be encoded by sbc at a time? + // 16 bits * 2 channels * 16 blocks * 8 subbands = 4096bits = 512 o + codesize = a2dp->sbc.subbands * a2dp->sbc.blocks * a2dp->sbc.channels * 2; + // 44 bitpool? + //codesize=a2dp->sbc.bitpool*a2dp->sbc.subbands*a2dp->sbc.blocks/8; + datatoread = min(codesize, pcm_buffer_size); + + // Enqueue data in bufe + if (a2dp->lenbufe + datatoread < BUFS) { + // Append data to bufe, for sbc encoding + memcpy_changeendian(a2dp->bufe + a2dp->lenbufe, pcm_buffer, datatoread); + a2dp->lenbufe += datatoread; + } else { + datatoread = 0; + } - // Reset buffer of data to send - a2dp->len = sizeof(struct media_packet_header)+sizeof(struct media_payload_header); - a2dp->frame_count=0; - a2dp->seq_num++; - } - // Append sbc encoded data to buf, until buf reaches A2DPMAXIMUMTRANSFERUNITSIZE to send - a2dp->frame_count++; - memcpy(a2dp->buf + a2dp->len, a2dp->sbc.data, a2dp->sbc.len); - a2dp->len+=a2dp->sbc.len; - } + // If bufe is full, encode + if (a2dp->lenbufe >= codesize) { + // Enough data to encode (sbc wants 1k blocks) + int encoded; + encoded = sbc_encode(&(a2dp->sbc), a2dp->bufe, codesize); //encode + + if (encoded <= 0) + return encoded; + + memmove(a2dp->bufe, a2dp->bufe + encoded, a2dp->lenbufe - encoded); // Shift the bufe + a2dp->lenbufe -= encoded; + + // Send data through bluetooth + if (a2dp->len + a2dp->sbc.len >= a2dp->mtu) { + // time to prepare and send the packet + memset(&payload_header, 0, sizeof(payload_header)); + memset(&packet_header, 0, sizeof(packet_header)); + payload_header.frame_count = a2dp->frame_count; + packet_header.v = 2; + packet_header.pt = 1; + packet_header.cc = 0; + packet_header.sequence_number = htons(a2dp->seq_num); + packet_header.timestamp = htonl(a2dp->timestamp); + packet_header.ssrc = htonl(1); + + a2dp->timestamp += (a2dp->sbc.blocks + 1) * 4 * (a2dp->sbc.subbands + 1) * 4; + memcpy(a2dp->buf, &packet_header, sizeof(packet_header)); + memcpy(a2dp->buf + sizeof(packet_header), &payload_header, sizeof(payload_header)); + if (a2dp->sk > 0) { + // Check if data are to be read + // Not seen a device showing this yet + fd_set readfds; + struct timeval zero_timeout = { 0, 0 }; + FD_ZERO(&readfds); + FD_SET(a2dp->sk, &readfds); + int iselect = select(1, &readfds, NULL, NULL, &zero_timeout); + if (iselect > 0) { + if (FD_ISSET(a2dp->sk, &readfds)) { + a2dp_handle_avdtp_message(a2dp, a2dp->sk, NULL, NULL, 0); + } + } + // Pause? + // The value 0 have never been tested + // However, we may safely simulate a failed write + if (!a2dp->pause_writing) { + // Send our data + if ((written = write(a2dp->sk, a2dp->buf, a2dp->len)) != a2dp->len) { + // Error while sending data + DBG("Wrote %d not %d bytes; (errno=%d:%s)", written, a2dp->len, errno, strerror(errno)); + result = written; + } + result = written; + } else { + // Make the upper layer believe we sent data + result = a2dp->len; + } + } + // Reset buffer of data to send + a2dp->len = sizeof(struct media_packet_header) + sizeof(struct media_payload_header); + a2dp->frame_count = 0; + a2dp->seq_num++; + } + // Append sbc encoded data to buf, until buf reaches A2DPMAXIMUMTRANSFERUNITSIZE to send + a2dp->frame_count++; + memcpy(a2dp->buf + a2dp->len, a2dp->sbc.data, a2dp->sbc.len); + a2dp->len += a2dp->sbc.len; + } - return result; + return result; } -static void init_response(struct avdtp_header * header, int response_type) +static void init_response(struct avdtp_header *header, int response_type) { - // leave signal_id and transaction label since we are reusing the request - header->packet_type = PACKET_TYPE_SINGLE; - header->message_type = response_type; + // leave signal_id and transaction label since we are reusing the request + header->packet_type = PACKET_TYPE_SINGLE; + header->message_type = response_type; - // clear rfa bits - header->rfa0 = 0; + // clear rfa bits + header->rfa0 = 0; } // monitor the control connection for pause/play signals from headset // note this signaling is in the avdtp core; avrcp signaling is different -static void* listen_thread(void* param) +static void *listen_thread(void *param) { - snd_pcm_a2dp_t* a2dp = (snd_pcm_a2dp_t*)param; + snd_pcm_a2dp_t *a2dp = (snd_pcm_a2dp_t *) param; - if(a2dp->control_sk<0) - { - DBG("Listen thread not started [control_sk=%d]", a2dp->control_sk); + if (a2dp->control_sk < 0) { + DBG("Listen thread not started [control_sk=%d]", a2dp->control_sk); return NULL; - } - - DBG("Listen thread running [control_sk=%d]", a2dp->control_sk); + } - // Set a timeout to close thread - struct timeval t = { 1, 0 }; - setsockopt( a2dp->control_sk, SOL_SOCKET, SO_SNDTIMEO, &t, sizeof(t)); - setsockopt( a2dp->control_sk, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof(t)); + DBG("Listen thread running [control_sk=%d]", a2dp->control_sk); - // Loop until end of writing - while(!a2dp->stop_writing) - { - char szBuffer[A2DPMAXIMUMTRANSFERUNITSIZE]; - struct stream_cmd* cmd = (struct stream_cmd*)szBuffer; - if(a2dp_handle_avdtp_message(a2dp, a2dp->control_sk, NULL, NULL, 0)<0) - { - // Error - usleep(100*1000); - } - - int size = read(a2dp->control_sk, szBuffer, sizeof(szBuffer)); - if(size>0) - { - if(cmd->header.signal_id == AVDTP_SUSPEND) - { - DBG("Received signal AVDTP_SUSPEND(%d) from set", cmd->header.signal_id); - a2dp->pause_writing = 1; - } - else if(cmd->header.signal_id == AVDTP_START) - { - DBG("Received signal AVDTP_START(%d) from set", cmd->header.signal_id); - a2dp->pause_writing = 0; - } - else - { - DBG("Unexpected headset directive %d", cmd->header.signal_id); - } - // ack the command regardless - //FIXME take a shortcut and reuse the command struct (knock one byte off length) - init_response(&cmd->header, MESSAGE_TYPE_ACCEPT); - if (write(a2dp->control_sk, &cmd, sizeof(cmd)-1) != sizeof(cmd)-1) - { - DBG("Couldn't ack %d", cmd->header.signal_id); - } - } - else - { - if(errno!=EAGAIN) - DBG("Error while receiving %d (errno=%d:%s)", size, errno, strerror(errno)); - if(errno!=EINTR) - break; - } - } + // Set a timeout to close thread + struct timeval t = { 1, 0 }; + setsockopt(a2dp->control_sk, SOL_SOCKET, SO_SNDTIMEO, &t, sizeof(t)); + setsockopt(a2dp->control_sk, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof(t)); + + // Loop until end of writing + while (!a2dp->stop_writing) { + char szBuffer[A2DPMAXIMUMTRANSFERUNITSIZE]; + struct stream_cmd *cmd = (struct stream_cmd *) szBuffer; + if (a2dp_handle_avdtp_message(a2dp, a2dp->control_sk, NULL, NULL, 0) < 0) { + // Error + usleep(100 * 1000); + } + + int size = read(a2dp->control_sk, szBuffer, sizeof(szBuffer)); + if (size > 0) { + if (cmd->header.signal_id == AVDTP_SUSPEND) { + DBG("Received signal AVDTP_SUSPEND(%d) from set", cmd->header.signal_id); + a2dp->pause_writing = 1; + } else if (cmd->header.signal_id == AVDTP_START) { + DBG("Received signal AVDTP_START(%d) from set", cmd->header.signal_id); + a2dp->pause_writing = 0; + } else { + DBG("Unexpected headset directive %d", cmd->header.signal_id); + } + // ack the command regardless + //FIXME take a shortcut and reuse the command struct (knock one byte off length) + init_response(&cmd->header, MESSAGE_TYPE_ACCEPT); + if (write(a2dp->control_sk, &cmd, sizeof(cmd) - 1) != sizeof(cmd) - 1) { + DBG("Couldn't ack %d", cmd->header.signal_id); + } + } else { + if (errno != EAGAIN) + DBG("Error while receiving %d (errno=%d:%s)", size, errno, strerror(errno)); + if (errno != EINTR) + break; + } + } - return NULL; + return NULL; } -int a2dp_connect(snd_pcm_a2dp_t *a2dp) +int a2dp_connect(snd_pcm_a2dp_t * a2dp) { - //struct sockaddr_rc addr; - //socklen_t len; - int sk = -1; - int control_sk = -1; - errno=0; - /* - if(a2dp->use_rfcomm) { - sk = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); - if (sk < 0) - return -errno; - - memset(&addr, 0, sizeof(addr)); - addr.rc_family = AF_BLUETOOTH; - bacpy(&addr.rc_bdaddr, &a2dp->src); - - if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - close(sk); - return -errno; - } - - memset(&addr, 0, sizeof(addr)); - addr.rc_family = AF_BLUETOOTH; - bacpy(&addr.rc_bdaddr, &a2dp->dst); - addr.rc_channel = 1; - - if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - close(sk); - return -errno; - } - - memset(&addr, 0, sizeof(addr)); - len = sizeof(addr); - - if (getsockname(sk, (struct sockaddr *) &addr, &len) < 0) { - close(sk); - return -errno; - } - - bacpy(&a2dp->src, &addr.rc_bdaddr); + //struct sockaddr_rc addr; + //socklen_t len; + int sk = -1; + int control_sk = -1; + errno = 0; + /* + if(a2dp->use_rfcomm) { + sk = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); + if (sk < 0) + return -errno; + + memset(&addr, 0, sizeof(addr)); + addr.rc_family = AF_BLUETOOTH; + bacpy(&addr.rc_bdaddr, &a2dp->src); + + if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + close(sk); + return -errno; + } + + memset(&addr, 0, sizeof(addr)); + addr.rc_family = AF_BLUETOOTH; + bacpy(&addr.rc_bdaddr, &a2dp->dst); + addr.rc_channel = 1; + + if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + close(sk); + return -errno; + } + + memset(&addr, 0, sizeof(addr)); + len = sizeof(addr); + + if (getsockname(sk, (struct sockaddr *) &addr, &len) < 0) { + close(sk); + return -errno; + } + + bacpy(&a2dp->src, &addr.rc_bdaddr); + + //fcntl(sk, F_SETFL, fcntl(sk, F_GETFL) | O_NONBLOCK); + } else { */ + sk = connect_stream(&a2dp->src, &a2dp->dst, &control_sk, &a2dp->sbc, &a2dp->seid, &a2dp->mtu); + //} + + a2dp->sk = sk; + a2dp->control_sk = control_sk; + + // Start listen thread + a2dp->pause_writing = 0; + a2dp->stop_writing = 0; - //fcntl(sk, F_SETFL, fcntl(sk, F_GETFL) | O_NONBLOCK); - } else {*/ - sk = connect_stream(&a2dp->src, &a2dp->dst, &control_sk, &a2dp->sbc, &a2dp->seid, &a2dp->mtu); - //} - - a2dp->sk = sk; - a2dp->control_sk = control_sk; - - // Start listen thread - a2dp->pause_writing = 0; - a2dp->stop_writing = 0; - - if(sk>0) - { + if (sk > 0) { // Set pthread stack size to decrease unused memory usage pthread_attr_t tattr; size_t size = PTHREAD_STACK_MIN; int ret = pthread_attr_init(&tattr); ret = pthread_attr_setstacksize(&tattr, size); - pthread_create(&a2dp->hListenThread, &tattr, listen_thread, (void*)a2dp); + pthread_create(&a2dp->hListenThread, &tattr, listen_thread, (void *) a2dp); pthread_attr_destroy(&tattr); } - return sk; + return sk; } snd_pcm_a2dp_t *a2dp_alloc(void) { - snd_pcm_a2dp_t *a2dp; - a2dp = malloc(sizeof(*a2dp)); - if (!a2dp) - return NULL; - - memset(a2dp, 0, sizeof(*a2dp)); - a2dp->seq_num = 1; - a2dp->mtu = A2DPMAXIMUMTRANSFERUNITSIZE; - a2dp->len = sizeof(struct media_packet_header)+sizeof(struct media_payload_header); - - sbc_init(&a2dp->sbc, 0L); - a2dp->sbc.rate = A2DPD_FRAME_RATE; - a2dp->sbc.subbands = 8; // safe default - a2dp->sbc.blocks = 16; // safe default - a2dp->sbc.bitpool = 32; // recommended value 53, safe default is 32 - return a2dp; -} - -void a2dp_free(snd_pcm_a2dp_t *a2dp) -{ - DBG(""); - if (a2dp->sk > 0) - close(a2dp->sk); - if (a2dp->control_sk > 0) - close(a2dp->control_sk); + snd_pcm_a2dp_t *a2dp; + a2dp = malloc(sizeof(*a2dp)); + if (!a2dp) + return NULL; - sbc_finish(&a2dp->sbc); + memset(a2dp, 0, sizeof(*a2dp)); + a2dp->seq_num = 1; + a2dp->mtu = A2DPMAXIMUMTRANSFERUNITSIZE; + a2dp->len = sizeof(struct media_packet_header) + sizeof(struct media_payload_header); + + sbc_init(&a2dp->sbc, 0L); + a2dp->sbc.rate = A2DPD_FRAME_RATE; + a2dp->sbc.subbands = 8; // safe default + a2dp->sbc.blocks = 16; // safe default + a2dp->sbc.bitpool = 32; // recommended value 53, safe default is 32 + return a2dp; +} + +void a2dp_free(snd_pcm_a2dp_t * a2dp) +{ + DBG(""); + if (a2dp->sk > 0) + close(a2dp->sk); + if (a2dp->control_sk > 0) + close(a2dp->control_sk); - free(a2dp); + sbc_finish(&a2dp->sbc); + + free(a2dp); } static void sighand(int signo) { - return; + return; } void a2dp_init(void) { - // set up thread signal handler - memset(&actions, 0, sizeof(actions)); - sigemptyset(&actions.sa_mask); - actions.sa_flags = 0; - actions.sa_handler = sighand; - sigaction(SIGALRM,&actions,NULL); - - // Start sdp advertising - /* - g_sdpSessionP = sdp_connect(BDADDR_ANY, BDADDR_LOCAL, SDP_RETRY_IF_BUSY); - g_recordP = a2dp_advertise_sdp(g_sdpSessionP); - */ + // set up thread signal handler + memset(&actions, 0, sizeof(actions)); + sigemptyset(&actions.sa_mask); + actions.sa_flags = 0; + actions.sa_handler = sighand; + sigaction(SIGALRM, &actions, NULL); + + // Start sdp advertising + /* + g_sdpSessionP = sdp_connect(BDADDR_ANY, BDADDR_LOCAL, SDP_RETRY_IF_BUSY); + g_recordP = a2dp_advertise_sdp(g_sdpSessionP); + */ } void a2dp_exit(void) { } -LPA2DP a2dp_new(char* addr, int framerate) +LPA2DP a2dp_new(char *addr, int framerate) { - snd_pcm_a2dp_t *a2dp = NULL; - bdaddr_t src, dst; - int err; //, pos = -1, use_rfcomm = 0; - - DBG("%s, %d", addr, framerate); - - bacpy(&src, BDADDR_ANY); - bacpy(&dst, BDADDR_ANY); - str2ba(addr, &dst); - - a2dp = a2dp_alloc(); - if (!a2dp) { - DBG("Can't allocate"); - return NULL; - } - if(a2dp) a2dp->sbc.rate=framerate; + snd_pcm_a2dp_t *a2dp = NULL; + bdaddr_t src, dst; + int err; //, pos = -1, use_rfcomm = 0; - bacpy(&a2dp->src, &src); - bacpy(&a2dp->dst, &dst); - //a2dp->use_rfcomm = use_rfcomm; - - err = a2dp_connect(a2dp); - if (err < 0) { - DBG("Can't connect"); - goto error; - } + DBG("%s, %d", addr, framerate); - return a2dp; + bacpy(&src, BDADDR_ANY); + bacpy(&dst, BDADDR_ANY); + str2ba(addr, &dst); -error: - a2dp_free(a2dp); - return NULL; -} + a2dp = a2dp_alloc(); + if (!a2dp) { + DBG("Can't allocate"); + return NULL; + } + if (a2dp) + a2dp->sbc.rate = framerate; -void a2dp_destroy(LPA2DP a2dp) -{ - struct stream_cmd close_stream; - struct close_stream_rsp close_resp; + bacpy(&a2dp->src, &src); + bacpy(&a2dp->dst, &dst); + //a2dp->use_rfcomm = use_rfcomm; + + err = a2dp_connect(a2dp); + if (err < 0) { + DBG("Can't connect"); + goto error; + } - DBG("Begin"); - a2dp->stop_writing = 1; - pthread_kill(a2dp->hListenThread, SIGALRM); - pthread_join(a2dp->hListenThread, NULL); - - memset(&close_stream, 0, sizeof(close_stream)); - memset(&close_resp, 0, sizeof(close_resp)); - - // the stream-close used to make the iTech headset lock up and require it to be powercycled - // should be tested again now that we drain the queue properly - - init_request(&close_stream.header, AVDTP_CLOSE); - close_stream.acp_seid = a2dp->seid; - // Use control_sk if it is needed - if((a2dp->control_sk>0 && (write(a2dp->control_sk, &close_stream, sizeof(close_stream)) != sizeof(close_stream))) - // Else use sk - || (write(a2dp->sk, &close_stream, sizeof(close_stream)) != sizeof(close_stream)) - ) - { - DBG("Couldn't send close_stream (errno=%d:%s)", errno, strerror(errno)); - } + return a2dp; - a2dp_free(a2dp); - DBG("a2dp_destroy(%p) OK", a2dp); + error: + a2dp_free(a2dp); + return NULL; } -int a2dp_make_listen_socket(unsigned short psm) +void a2dp_destroy(LPA2DP a2dp) { - char* lpszError = NULL; - int sockfd = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP); - - if (sockfd >= 0) - { - struct sockaddr_l2 addr; - memset(&addr, 0, sizeof(addr)); - addr.l2_family = AF_BLUETOOTH; - bacpy(&addr.l2_bdaddr, BDADDR_ANY); - addr.l2_psm=htobs(psm); - if (bind(sockfd, (struct sockaddr *) &addr, sizeof(addr)) >= 0) - { - /* - struct l2cap_options opts; - unsigned int iOptSize = sizeof(opts); - // Get default options - if (getsockopt(sockfd, SOL_L2CAP, L2CAP_OPTIONS, &opts, &opt) >= 0) - { - if (setsockopt(sockfd, SOL_L2CAP, L2CAP_OPTIONS, &opts, opt) >= 0) - { - */ - if(listen(sockfd,5)>=0) - { - } - else - { - lpszError = "Can't listen."; - } - /* - } - else - { - lpszError = "Can't get default L2CAP options."; - } - } - else - { - lpszError = "Can't set L2CAP options."; - } - */ - } - else - { - lpszError = "Can't bind socket (already used?)."; - } - } - else - { - lpszError = "Can't create socket."; - } + struct stream_cmd close_stream; + struct close_stream_rsp close_resp; - if(lpszError) - { - DBG("%s %s(%d)", lpszError, strerror(errno), errno); - close(sockfd); - sockfd=-1; - } - - return sockfd; + DBG("Begin"); + a2dp->stop_writing = 1; + pthread_kill(a2dp->hListenThread, SIGALRM); + pthread_join(a2dp->hListenThread, NULL); + + memset(&close_stream, 0, sizeof(close_stream)); + memset(&close_resp, 0, sizeof(close_resp)); + + // the stream-close used to make the iTech headset lock up and require it to be powercycled + // should be tested again now that we drain the queue properly + + init_request(&close_stream.header, AVDTP_CLOSE); + close_stream.acp_seid = a2dp->seid; + // Use control_sk if it is needed + if ((a2dp->control_sk > 0 && (write(a2dp->control_sk, &close_stream, sizeof(close_stream)) != sizeof(close_stream))) + // Else use sk + || (write(a2dp->sk, &close_stream, sizeof(close_stream)) != sizeof(close_stream)) + ) { + DBG("Couldn't send close_stream (errno=%d:%s)", errno, strerror(errno)); + } + + a2dp_free(a2dp); + DBG("a2dp_destroy(%p) OK", a2dp); } -int a2dp_wait_connection( int sockfd, char* szRemote, int iRemoteSize, uint16_t *mtu) +int a2dp_make_listen_socket(unsigned short psm) { - // Wait client connection - struct sockaddr_l2 addr; - socklen_t addrlen = sizeof(addr); - - // Timeouts each second to read variables - struct timeval t = { 1, 0 }; - setsockopt( sockfd, SOL_SOCKET, SO_SNDTIMEO, &t, sizeof(t)); - setsockopt( sockfd, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof(t)); + char *lpszError = NULL; + int sockfd = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP); - int new_fd = accept(sockfd, (struct sockaddr *) &addr, &addrlen); + if (sockfd >= 0) { + struct sockaddr_l2 addr; + memset(&addr, 0, sizeof(addr)); + addr.l2_family = AF_BLUETOOTH; + bacpy(&addr.l2_bdaddr, BDADDR_ANY); + addr.l2_psm = htobs(psm); + if (bind(sockfd, (struct sockaddr *) &addr, sizeof(addr)) >= 0) { + /* + struct l2cap_options opts; + unsigned int iOptSize = sizeof(opts); + // Get default options + if (getsockopt(sockfd, SOL_L2CAP, L2CAP_OPTIONS, &opts, &opt) >= 0) + { + if (setsockopt(sockfd, SOL_L2CAP, L2CAP_OPTIONS, &opts, opt) >= 0) + { + */ + if (listen(sockfd, 5) >= 0) { + } else { + lpszError = "Can't listen."; + } + /* + } + else + { + lpszError = "Can't get default L2CAP options."; + } + } + else + { + lpszError = "Can't set L2CAP options."; + } + */ + } else { + lpszError = "Can't bind socket (already used?)."; + } + } else { + lpszError = "Can't create socket."; + } - if (szRemote) *szRemote='\0'; + if (lpszError) { + DBG("%s %s(%d)", lpszError, strerror(errno), errno); + close(sockfd); + sockfd = -1; + } - if(new_fd >= 0) - { - // Get default options - struct l2cap_options opts; - unsigned int iOptSize = sizeof(opts); - if (getsockopt(sockfd, SOL_L2CAP, L2CAP_OPTIONS, &opts, &iOptSize) >= 0) - { - if (mtu && opts.imtu) - *mtu = opts.imtu; - if (mtu && opts.omtu) - *mtu = opts.omtu; - if (!(*mtu)) - *mtu=A2DPMAXIMUMTRANSFERUNITSIZE; - } - //DBG("Connected [imtu %d, omtu %d, flush_to %d]", opts.imtu, opts.omtu, opts.flush_to); + return sockfd; +} - if (szRemote) - { - strncpy(szRemote, batostr(&addr.l2_bdaddr), iRemoteSize); - szRemote[iRemoteSize-1] = '\0'; - } - } - return new_fd; +int a2dp_wait_connection(int sockfd, char *szRemote, int iRemoteSize, uint16_t * mtu) +{ + // Wait client connection + struct sockaddr_l2 addr; + socklen_t addrlen = sizeof(addr); + + // Timeouts each second to read variables + struct timeval t = { 1, 0 }; + setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &t, sizeof(t)); + setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof(t)); + + int new_fd = accept(sockfd, (struct sockaddr *) &addr, &addrlen); + + if (szRemote) + *szRemote = '\0'; + + if (new_fd >= 0) { + // Get default options + struct l2cap_options opts; + unsigned int iOptSize = sizeof(opts); + if (getsockopt(sockfd, SOL_L2CAP, L2CAP_OPTIONS, &opts, &iOptSize) >= 0) { + if (mtu && opts.imtu) + *mtu = opts.imtu; + if (mtu && opts.omtu) + *mtu = opts.omtu; + if (!(*mtu)) + *mtu = A2DPMAXIMUMTRANSFERUNITSIZE; + } + //DBG("Connected [imtu %d, omtu %d, flush_to %d]", opts.imtu, opts.omtu, opts.flush_to); + + if (szRemote) { + strncpy(szRemote, batostr(&addr.l2_bdaddr), iRemoteSize); + szRemote[iRemoteSize - 1] = '\0'; + } + } + return new_fd; } // This function handle the bluetooth connection -int a2dp_handle_avdtp_message(LPA2DP a2dp, int sockfd, struct avdtp_header* sent_packet, struct avdtp_header* answer, int answer_size) +int a2dp_handle_avdtp_message(LPA2DP a2dp, int sockfd, struct avdtp_header *sent_packet, struct avdtp_header *answer, int answer_size) { - int result = 0; - int wrresult = 0; - - char lpFrame [A2DPMAXIMUMTRANSFERUNITSIZE]; - int iReceived = recv(sockfd, lpFrame, sizeof(lpFrame), 0); - struct avdtp_header* pkt_hdr = (struct avdtp_header*)lpFrame; - if(iReceived>0) - { - // Manage the packet - if(sent_packet==NULL) - { - int i; - printf("socket %d: Received %d bytes\n", sockfd, iReceived); - for(i = 0; imessage_type == MESSAGE_TYPE_ACCEPT) &&(pkt_hdr->signal_id == sent_packet->signal_id) - ) - { - // Got expected answer - memcpy(answer, lpFrame, answer_size>iReceived?answer_size:iReceived); - result=iReceived; - } - else - { - // Got bad answer - result=0; - } - - // Reply to the packet by rejecting it - if(pkt_hdr->message_type == MESSAGE_TYPE_COMMAND) - { - int accepted = 0; - if(a2dp && pkt_hdr->signal_id == AVDTP_SUSPEND) - { - DBG("Received signal AVDTP_SUSPEND(%d) from set", pkt_hdr->signal_id); - a2dp->pause_writing = 1; - accepted=1; - } - else if(a2dp && pkt_hdr->signal_id == AVDTP_START) - { - DBG("Received signal AVDTP_START(%d) from set", pkt_hdr->signal_id); - a2dp->pause_writing = 0; - accepted=1; - } - else - { - DBG("Unexpected headset directive %d", pkt_hdr->signal_id); - } - - DBG("Answering command packet (msgtype=%s,signal=%d)", accepted?"MESSAGE_TYPE_ACCEPT":"MESSAGE_TYPE_REJECT", pkt_hdr->signal_id); - // Reject a command received - pkt_hdr->message_type = accepted?MESSAGE_TYPE_ACCEPT:MESSAGE_TYPE_REJECT; + int result = 0; + int wrresult = 0; - wrresult = write(sockfd, pkt_hdr, sizeof(*pkt_hdr)); + char lpFrame[A2DPMAXIMUMTRANSFERUNITSIZE]; + int iReceived = recv(sockfd, lpFrame, sizeof(lpFrame), 0); + struct avdtp_header *pkt_hdr = (struct avdtp_header *) lpFrame; + if (iReceived > 0) { + // Manage the packet + if (sent_packet == NULL) { + int i; + printf("socket %d: Received %d bytes\n", sockfd, iReceived); + for (i = 0; i < iReceived; i++) { + char c = lpFrame[i]; + if (i % 16 == 0) + printf("%05d: ", i); + printf("%02x ", c); + if (i % 16 == 15) + printf("\n"); + } + printf("\n"); + result = 0; + } else if ((pkt_hdr->message_type == MESSAGE_TYPE_ACCEPT) && (pkt_hdr->signal_id == sent_packet->signal_id) + ) { + // Got expected answer + memcpy(answer, lpFrame, answer_size > iReceived ? answer_size : iReceived); + result = iReceived; + } else { + // Got bad answer + result = 0; + } + + // Reply to the packet by rejecting it + if (pkt_hdr->message_type == MESSAGE_TYPE_COMMAND) { + int accepted = 0; + if (a2dp && pkt_hdr->signal_id == AVDTP_SUSPEND) { + DBG("Received signal AVDTP_SUSPEND(%d) from set", pkt_hdr->signal_id); + a2dp->pause_writing = 1; + accepted = 1; + } else if (a2dp && pkt_hdr->signal_id == AVDTP_START) { + DBG("Received signal AVDTP_START(%d) from set", pkt_hdr->signal_id); + a2dp->pause_writing = 0; + accepted = 1; + } else { + DBG("Unexpected headset directive %d", pkt_hdr->signal_id); + } - if(wrresult != sizeof(*pkt_hdr)) - { - DBG("FAILED Answering command packet (msgtype=%s,signal=%d) wrresult=%d/%d (errno=%d:%s)", accepted?"MESSAGE_TYPE_ACCEPT":"MESSAGE_TYPE_REJECT", pkt_hdr->signal_id, wrresult, sizeof(*pkt_hdr), errno, strerror(errno)); - } - } - else - { - DBG("Read non command packet (msgtype=%d,signal=%d)", pkt_hdr->message_type, pkt_hdr->signal_id); - } - } - else - { - result=iReceived; - if(errno!=EAGAIN) - printf("socket %d: Receive failed %d (errno=%d:%s)\n", sockfd, iReceived, errno, strerror(errno)); - } + DBG("Answering command packet (msgtype=%s,signal=%d)", accepted ? "MESSAGE_TYPE_ACCEPT" : "MESSAGE_TYPE_REJECT", pkt_hdr->signal_id); + // Reject a command received + pkt_hdr->message_type = accepted ? MESSAGE_TYPE_ACCEPT : MESSAGE_TYPE_REJECT; + + wrresult = write(sockfd, pkt_hdr, sizeof(*pkt_hdr)); + + if (wrresult != sizeof(*pkt_hdr)) { + DBG("FAILED Answering command packet (msgtype=%s,signal=%d) wrresult=%d/%d (errno=%d:%s)", accepted ? "MESSAGE_TYPE_ACCEPT" : "MESSAGE_TYPE_REJECT", pkt_hdr->signal_id, + wrresult, sizeof(*pkt_hdr), errno, strerror(errno)); + } + } else { + DBG("Read non command packet (msgtype=%d,signal=%d)", pkt_hdr->message_type, pkt_hdr->signal_id); + } + } else { + result = iReceived; + if (errno != EAGAIN) + printf("socket %d: Receive failed %d (errno=%d:%s)\n", sockfd, iReceived, errno, strerror(errno)); + } - return result; + return result; } - Index: alsa-plugins/alsalib.c =================================================================== RCS file: alsa-plugins/alsalib.c diff -N alsa-plugins/alsalib.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ alsa-plugins/alsalib.c 31 Aug 2006 15:58:57 -0000 @@ -0,0 +1,203 @@ +/* +* +* BlueZ - Bluetooth protocol stack for Linux +* +* Copyright (C) 2004-2005 Marcel Holtmann +* +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +* +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include "alsalib.h" +#include "a2dpd_protocol.h" + +#define NBSDPRETRIESMAX 0 +#define NONSPECAUDIO 1 +#define BUFS 2048 + +#define DBG(fmt, arg...) { if(errno!=0) printf("DEBUG: %s: (errno=%d:%s)" fmt "\n" , __FUNCTION__ , errno, strerror(errno), ## arg); else printf("DEBUG: %s: " fmt "\n" , __FUNCTION__ , ## arg); errno=0; } + +//#define DBG(fmt, arg...) printf("DEBUG: %s: " fmt "\n" , __FUNCTION__ , ## arg) +//#define DBG(D...) + +static struct sigaction actions; + +typedef struct snd_pcm_alsa { + snd_pcm_t *playback_handle; +} snd_pcm_alsa_t; + +/* +* Underrun and suspend recovery +*/ + +static int xrun_recovery(snd_pcm_t * handle, int err) +{ + if (err == -EPIPE) { /* under-run */ + err = snd_pcm_prepare(handle); + if (err < 0) + printf("Can't recovery from underrun, prepare failed: %s\n", snd_strerror(err)); + return 0; + } else if (err == -ESTRPIPE) { + while ((err = snd_pcm_resume(handle)) == -EAGAIN) + sleep(1); /* wait until the suspend flag is released */ + if (err < 0) { + err = snd_pcm_prepare(handle); + if (err < 0) + printf("Can't recovery from suspend, prepare failed: %s\n", snd_strerror(err)); + } + return 0; + } + return err; +} + +int alsa_transfer_raw(LPALSA alsa, const char *pcm_buffer, int pcm_buffer_size) +{ + int result = 0; + + result = snd_pcm_writei(alsa->playback_handle, pcm_buffer, pcm_buffer_size / A2DPD_FRAME_BYTES); + switch (result) { + case -EBADFD: + DBG("EBADFD(%d)", result); + break; + case -EPIPE: + // To manage underrun, we will try to ignore + xrun_recovery(alsa->playback_handle, result); + //result = 0; + DBG("EPIPE(%d)", result); + break; + case -ESTRPIPE: + xrun_recovery(alsa->playback_handle, result); + //result=0; + DBG("ESTRPIPE(%d)", result); + break; + } + + return result; +} + +snd_pcm_alsa_t *alsa_alloc(void) +{ + snd_pcm_alsa_t *alsa; + alsa = malloc(sizeof(*alsa)); + if (!alsa) + return NULL; + + memset(alsa, 0, sizeof(*alsa)); + return alsa; +} + +void alsa_free(snd_pcm_alsa_t * alsa) +{ + free(alsa); +} + +static void sighand(int signo) +{ + return; +} + +void alsa_init(void) +{ + // set up thread signal handler + memset(&actions, 0, sizeof(actions)); + sigemptyset(&actions.sa_mask); + actions.sa_flags = 0; + actions.sa_handler = sighand; + sigaction(SIGALRM, &actions, NULL); +} + +void alsa_exit(void) +{ +} + +LPALSA alsa_new(char *device, int framerate) +{ + DBG(""); + snd_pcm_alsa_t *alsa = NULL; + snd_pcm_hw_params_t *hw_params = NULL; + int bcontinue = 1; + char *devname = (device && device[0]) ? device : "plughw:0,0"; + + alsa = alsa_alloc(); + if (!alsa) { + DBG("Can't allocate"); + return NULL; + } + // Setup alsa + bcontinue = bcontinue && (snd_pcm_open(&alsa->playback_handle, devname, SND_PCM_STREAM_PLAYBACK, 0) >= 0); + DBG("snd_pcm_open()==%d", bcontinue); + bcontinue = bcontinue && (snd_pcm_hw_params_malloc(&hw_params) >= 0); + DBG("snd_pcm_hw_params_malloc()==%d", bcontinue); + bcontinue = bcontinue && (snd_pcm_hw_params_any(alsa->playback_handle, hw_params) >= 0); + DBG("snd_pcm_hw_params_any()==%d", bcontinue); + bcontinue = bcontinue && (snd_pcm_hw_params_set_access(alsa->playback_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED) >= 0); + DBG("snd_pcm_hw_params_set_access()==%d", bcontinue); + bcontinue = bcontinue && (snd_pcm_hw_params_set_format(alsa->playback_handle, hw_params, SND_PCM_FORMAT_S16_LE) >= 0); + DBG("snd_pcm_hw_params_set_format()==%d", bcontinue); + bcontinue = bcontinue && (snd_pcm_hw_params_set_rate(alsa->playback_handle, hw_params, framerate, 0) >= 0); + DBG("snd_pcm_hw_params_set_rate()==%d", bcontinue); + bcontinue = bcontinue && (snd_pcm_hw_params_set_channels(alsa->playback_handle, hw_params, 2) >= 0); + DBG("snd_pcm_hw_params_set_channels()==%d", bcontinue); + bcontinue = bcontinue && (snd_pcm_hw_params(alsa->playback_handle, hw_params) >= 0); + DBG("snd_pcm_hw_params()==%d", bcontinue); + bcontinue = bcontinue && (snd_pcm_prepare(alsa->playback_handle) >= 0); + DBG("snd_pcm_prepare()==%d", bcontinue); + + // Free if allocated + if (hw_params) + snd_pcm_hw_params_free(hw_params); + + if (alsa->playback_handle != NULL) { + } + + if (!bcontinue) { + alsa_destroy(alsa); + alsa = NULL; + } + + return alsa; +} + +void alsa_destroy(LPALSA alsa) +{ + DBG(""); + if (alsa->playback_handle != NULL) { + snd_pcm_close(alsa->playback_handle); + } + alsa_free(alsa); + DBG("OK"); +} Index: alsa-plugins/alsalib.h =================================================================== RCS file: alsa-plugins/alsalib.h diff -N alsa-plugins/alsalib.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ alsa-plugins/alsalib.h 31 Aug 2006 15:58:57 -0000 @@ -0,0 +1,48 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-2005 Marcel Holtmann + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef __ALSA_LIB_H__ +#define __ALSA_LIB_H__ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#define A2DPMAXIMUMTRANSFERUNITSIZE 610 + +typedef struct snd_pcm_alsa* LPALSA; + +// Global library initialisation +extern void alsa_init( void); +extern void alsa_exit( void); + +// Connect to alsa +extern LPALSA alsa_new( char* device, int framerate); +extern void alsa_destroy( LPALSA a2dp); + +// transfers data +extern int alsa_transfer_raw( LPALSA a2dp, const char* pcm_buffer, int pcm_buffer_size); + +#endif Index: alsa-plugins/pcm_a2dpd.c =================================================================== RCS file: /cvsroot/bluetooth-alsa/btsco/alsa-plugins/pcm_a2dpd.c,v retrieving revision 1.4 diff -u -r1.4 pcm_a2dpd.c --- alsa-plugins/pcm_a2dpd.c 17 Aug 2006 14:06:27 -0000 1.4 +++ alsa-plugins/pcm_a2dpd.c 31 Aug 2006 15:58:57 -0000 @@ -51,381 +51,358 @@ #define DBG(fmt, arg...) printf("DEBUG: %s: (errno=%d:%s)" fmt "\n" , __FUNCTION__ , errno, strerror(errno), ## arg) //#define DBG(D...) -static char g_srcfilename [512]; +static char g_srcfilename[512]; // Signal handler, there is a strange SIGPIPE when the daemon is not running // We catch it to not quit void sighand(int signo) { - printf("A2DPD CTL in signal handler %d\n", signo); - return; + printf("A2DPD CTL in signal handler %d\n", signo); + return; } -typedef struct snd_pcm_a2dp -{ - snd_pcm_ioplug_t io; - int sk; - int rate; - int channels; - snd_pcm_sframes_t num; - unsigned int frame_bytes; - TIMERINFO TimerInfos; +typedef struct snd_pcm_a2dp { + snd_pcm_ioplug_t io; + int sk; + int rate; + int channels; + snd_pcm_sframes_t num; + unsigned int frame_bytes; + TIMERINFO TimerInfos; } snd_pcm_a2dp_t; -static int a2dp_disconnect(snd_pcm_a2dp_t *a2dp) +static int a2dp_disconnect(snd_pcm_a2dp_t * a2dp) { - close_socket(a2dp->sk); - a2dp->sk = -1; - return 0; -} - -static int a2dp_connect(snd_pcm_a2dp_t *a2dp) -{ - if(a2dp->sk <= 0) - { - int sockfd = make_client_socket(); - a2dp->sk = -1; - if(sockfd>0) - { - int32_t client_type = A2DPD_PLUGIN_PCM_WRITE; - if(send_socket(sockfd, &client_type, sizeof(client_type))==sizeof(client_type)) - { - a2dp->sk = sockfd; - syslog(LOG_INFO, "Connected a2dp %p, sk %d", a2dp, a2dp->sk); - } - else - { - close_socket(sockfd); - syslog(LOG_WARNING, "Connected a2dp %p, sk %d, Authorisation failed", a2dp, a2dp->sk); - } - } - else - { - syslog(LOG_ERR, "Socket failed a2dp %p, sk %d", a2dp, a2dp->sk); - } - } - return 0; + close_socket(a2dp->sk); + a2dp->sk = -1; + return 0; +} + +static int a2dp_connect(snd_pcm_a2dp_t * a2dp) +{ + if (a2dp->sk <= 0) { + int sockfd = make_client_socket(); + a2dp->sk = -1; + if (sockfd > 0) { + int32_t client_type = A2DPD_PLUGIN_PCM_WRITE; + if (send_socket(sockfd, &client_type, sizeof(client_type)) == sizeof(client_type)) { + a2dp->sk = sockfd; + syslog(LOG_INFO, "Connected a2dp %p, sk %d", a2dp, a2dp->sk); + } else { + close_socket(sockfd); + syslog(LOG_WARNING, "Connected a2dp %p, sk %d, Authorisation failed", a2dp, a2dp->sk); + } + } else { + syslog(LOG_ERR, "Socket failed a2dp %p, sk %d", a2dp, a2dp->sk); + } + } + return 0; } static inline snd_pcm_a2dp_t *a2dp_alloc(void) { - snd_pcm_a2dp_t *a2dp; - DBG("Init"); - a2dp = malloc(sizeof(*a2dp)); - if (!a2dp) - return NULL; - memset(a2dp, 0, sizeof(*a2dp)); - a2dp->sk = -1; + snd_pcm_a2dp_t *a2dp; + DBG("Init"); + a2dp = malloc(sizeof(*a2dp)); + if (!a2dp) + return NULL; + memset(a2dp, 0, sizeof(*a2dp)); + a2dp->sk = -1; - { - get_config_filename(g_srcfilename, sizeof(g_srcfilename)); - int rate = read_config_int(g_srcfilename, "a2dpd", "rate", A2DPD_FRAME_RATE); - a2dp->TimerInfos.fps = (float)((((float)rate)*((float)A2DPD_FRAME_BYTES)/((float)A2DPD_BLOCK_SIZE))/1.0); - } - DBG("OK"); - return a2dp; + { + get_config_filename(g_srcfilename, sizeof(g_srcfilename)); + int rate = read_config_int(g_srcfilename, "a2dpd", "rate", A2DPD_FRAME_RATE); + a2dp->TimerInfos.fps = (float) ((((float) rate) * ((float) A2DPD_FRAME_BYTES) / ((float) A2DPD_BLOCK_SIZE)) / 1.0); + } + DBG("OK"); + return a2dp; } -static inline void a2dp_free(snd_pcm_a2dp_t *a2dp) +static inline void a2dp_free(snd_pcm_a2dp_t * a2dp) { - DBG("Finishing"); - a2dp_disconnect(a2dp); - free(a2dp); - DBG("OK"); + DBG("Finishing"); + a2dp_disconnect(a2dp); + free(a2dp); + DBG("OK"); } -static int a2dp_start(snd_pcm_ioplug_t *io) +static int a2dp_start(snd_pcm_ioplug_t * io) { - //snd_pcm_a2dp_t *a2dp = io->private_data; - //FIXME - return 0; + //snd_pcm_a2dp_t *a2dp = io->private_data; + //FIXME + return 0; } -static int a2dp_stop(snd_pcm_ioplug_t *io) +static int a2dp_stop(snd_pcm_ioplug_t * io) { - //snd_pcm_a2dp_t *a2dp = io->private_data; - return 0; + //snd_pcm_a2dp_t *a2dp = io->private_data; + return 0; } -static snd_pcm_sframes_t a2dp_pointer(snd_pcm_ioplug_t *io) +static snd_pcm_sframes_t a2dp_pointer(snd_pcm_ioplug_t * io) { - snd_pcm_a2dp_t *a2dp = io->private_data; - return a2dp->num; + snd_pcm_a2dp_t *a2dp = io->private_data; + return a2dp->num; } // This is the main transfer func which does the transfer and sleep job -static snd_pcm_sframes_t a2dp_transfer2(snd_pcm_ioplug_t *io, - char* buf, - int32_t datatoread) -{ - snd_pcm_a2dp_t* a2dp = io->private_data; - int transfer = 0; - - // Connect if needed and send - a2dp_connect(a2dp); - if(transfer>=0) transfer=send_socket(a2dp->sk, &datatoread, sizeof(datatoread)); - if(transfer>=0) transfer=send_socket(a2dp->sk, buf, datatoread); - - // Disconnect if error detected - if(transfer<0) a2dp_disconnect(a2dp); - - // The data are sent to the daemon that act as a proxy thus we double transfer delay to compensate latency - a2dp_timer_notifyframe(&a2dp->TimerInfos); - a2dp_timer_sleep(&a2dp->TimerInfos, 4*A2DPTIMERPREDELAY); - - // Stats - if(a2dp->TimerInfos.display>0) - { - if(errno != 0 || transfer <= 0) - { - syslog( LOG_INFO, "send_socket(%d bytes)=%d (errno=%d:%s)", datatoread, transfer, errno, strerror(errno)); - } - } +static snd_pcm_sframes_t a2dp_transfer2(snd_pcm_ioplug_t * io, char *buf, int32_t datatoread) +{ + snd_pcm_a2dp_t *a2dp = io->private_data; + int transfer = 0; - // update pointer, tell alsa we're done - a2dp->num += datatoread / a2dp->frame_bytes; + // Connect if needed and send + a2dp_connect(a2dp); + if (transfer >= 0) + transfer = send_socket(a2dp->sk, &datatoread, sizeof(datatoread)); + if (transfer >= 0) + transfer = send_socket(a2dp->sk, buf, datatoread); + + // Disconnect if error detected + if (transfer < 0) + a2dp_disconnect(a2dp); + + // The data are sent to the daemon that act as a proxy thus we double transfer delay to compensate latency + a2dp_timer_notifyframe(&a2dp->TimerInfos); + a2dp_timer_sleep(&a2dp->TimerInfos, 4 * A2DPTIMERPREDELAY); + + // Stats + if (a2dp->TimerInfos.display > 0) { + if (errno != 0 || transfer <= 0) { + syslog(LOG_INFO, "send_socket(%d bytes)=%d (errno=%d:%s)", datatoread, transfer, errno, strerror(errno)); + } + } + // update pointer, tell alsa we're done + a2dp->num += datatoread / a2dp->frame_bytes; - return datatoread / a2dp->frame_bytes; + return datatoread / a2dp->frame_bytes; } // also works but sleeps between transfers // This is the main transfer func which does the transfer and sleep job -static snd_pcm_sframes_t a2dp_transfer_all(snd_pcm_ioplug_t *io, - const snd_pcm_channel_area_t *areas, - snd_pcm_uframes_t offset, snd_pcm_uframes_t size) +static snd_pcm_sframes_t a2dp_transfer_all(snd_pcm_ioplug_t * io, const snd_pcm_channel_area_t * areas, snd_pcm_uframes_t offset, snd_pcm_uframes_t size) { - snd_pcm_a2dp_t* a2dp = io->private_data; - int i = 0; - snd_pcm_sframes_t totaltransfered = 0; - while(i++<1 && totaltransfered < size) - { - char* buf = (char *) areas->addr + (areas->first + areas->step * offset) / 8; - int datatoread=min(A2DPD_BLOCK_SIZE,size*a2dp->frame_bytes); - snd_pcm_sframes_t transfered = a2dp_transfer2(io, buf, datatoread); - if(transfered>0) - { - offset += transfered; - totaltransfered += transfered; - } - else - { - break; - } - } - return totaltransfered; + snd_pcm_a2dp_t *a2dp = io->private_data; + int i = 0; + snd_pcm_sframes_t totaltransfered = 0; + while (i++ < 1 && totaltransfered < size) { + char *buf = (char *) areas->addr + (areas->first + areas->step * offset) / 8; + int datatoread = min(A2DPD_BLOCK_SIZE, size * a2dp->frame_bytes); + snd_pcm_sframes_t transfered = a2dp_transfer2(io, buf, datatoread); + if (transfered > 0) { + offset += transfered; + totaltransfered += transfered; + } else { + break; + } + } + return totaltransfered; } -static int a2dp_close(snd_pcm_ioplug_t *io) +static int a2dp_close(snd_pcm_ioplug_t * io) { - snd_pcm_a2dp_t *a2dp = io->private_data; - a2dp_disconnect(a2dp); - a2dp_free(a2dp); - return 0; + snd_pcm_a2dp_t *a2dp = io->private_data; + a2dp_disconnect(a2dp); + a2dp_free(a2dp); + return 0; } -static int a2dp_params(snd_pcm_ioplug_t *io, snd_pcm_hw_params_t *params) +static int a2dp_params(snd_pcm_ioplug_t * io, snd_pcm_hw_params_t * params) { - snd_pcm_a2dp_t *a2dp = io->private_data; - unsigned int period_bytes; + snd_pcm_a2dp_t *a2dp = io->private_data; + unsigned int period_bytes; - DBG("a2dp %p", a2dp); + DBG("a2dp %p", a2dp); - a2dp->frame_bytes = (snd_pcm_format_physical_width(io->format) * io->channels) / 8; + a2dp->frame_bytes = (snd_pcm_format_physical_width(io->format) * io->channels) / 8; - period_bytes = io->period_size * a2dp->frame_bytes; + period_bytes = io->period_size * a2dp->frame_bytes; - DBG("format %s rate %d channels %d", snd_pcm_format_name(io->format), - io->rate, io->channels); + DBG("format %s rate %d channels %d", snd_pcm_format_name(io->format), io->rate, io->channels); - DBG("frame_bytes %d period_bytes %d period_size %ld buffer_size %ld", - a2dp->frame_bytes, period_bytes, io->period_size, io->buffer_size); + DBG("frame_bytes %d period_bytes %d period_size %ld buffer_size %ld", a2dp->frame_bytes, period_bytes, io->period_size, io->buffer_size); - return 0; + return 0; } -static int a2dp_prepare(snd_pcm_ioplug_t *io) +static int a2dp_prepare(snd_pcm_ioplug_t * io) { - snd_pcm_a2dp_t *a2dp = io->private_data; - DBG("a2dp %p", a2dp); - a2dp->num = 0; - a2dp->rate = io->rate; - a2dp->channels = io->channels; - return 0; + snd_pcm_a2dp_t *a2dp = io->private_data; + DBG("a2dp %p", a2dp); + a2dp->num = 0; + a2dp->rate = io->rate; + a2dp->channels = io->channels; + return 0; } -static int a2dp_drain(snd_pcm_ioplug_t *io) +static int a2dp_drain(snd_pcm_ioplug_t * io) { - snd_pcm_a2dp_t *a2dp = io->private_data; - DBG("a2dp %p", a2dp); - return 0; + snd_pcm_a2dp_t *a2dp = io->private_data; + DBG("a2dp %p", a2dp); + return 0; } -static int a2dp_descriptors_count(snd_pcm_ioplug_t *io) +static int a2dp_descriptors_count(snd_pcm_ioplug_t * io) { - return 1; + return 1; } -static int a2dp_descriptors(snd_pcm_ioplug_t *io, struct pollfd *pfds, unsigned int space) +static int a2dp_descriptors(snd_pcm_ioplug_t * io, struct pollfd *pfds, unsigned int space) { - if (space < 1) - { - DBG("Can't fill in descriptors"); - SNDERR("Can't fill in descriptors"); - return 0; - } - - // Alsa does make sure writing now will not block - // So give him an always writable socket! - pfds[0].fd = fileno(stdout); - pfds[0].events = POLLOUT; - return 1; + if (space < 1) { + DBG("Can't fill in descriptors"); + SNDERR("Can't fill in descriptors"); + return 0; + } + // Alsa does make sure writing now will not block + // So give him an always writable socket! + pfds[0].fd = fileno(stdout); + pfds[0].events = POLLOUT; + return 1; } -static int a2dp_poll(snd_pcm_ioplug_t *io, struct pollfd *pfds, - unsigned int nfds, unsigned short *revents) +static int a2dp_poll(snd_pcm_ioplug_t * io, struct pollfd *pfds, unsigned int nfds, unsigned short *revents) { - snd_pcm_a2dp_t *a2dp = io->private_data; - *revents = pfds[0].revents; + snd_pcm_a2dp_t *a2dp = io->private_data; + *revents = pfds[0].revents; - if (a2dp->sk<=0) - return 0; + if (a2dp->sk <= 0) + return 0; - if (pfds[0].revents & POLLHUP) { - a2dp_disconnect(a2dp); - snd_pcm_ioplug_reinit_status(&a2dp->io); - } + if (pfds[0].revents & POLLHUP) { + a2dp_disconnect(a2dp); + snd_pcm_ioplug_reinit_status(&a2dp->io); + } - return 0; + return 0; } static snd_pcm_ioplug_callback_t a2dp_callback = { - .close = a2dp_close, - .start = a2dp_start, - .stop = a2dp_stop, - .prepare = a2dp_prepare, - .transfer = a2dp_transfer_all, - .pointer = a2dp_pointer, - .hw_params = a2dp_params, - .drain = a2dp_drain, - .poll_descriptors_count = a2dp_descriptors_count, - .poll_descriptors = a2dp_descriptors, - .poll_revents = a2dp_poll, + .close = a2dp_close, + .start = a2dp_start, + .stop = a2dp_stop, + .prepare = a2dp_prepare, + .transfer = a2dp_transfer_all, + .pointer = a2dp_pointer, + .hw_params = a2dp_params, + .drain = a2dp_drain, + .poll_descriptors_count = a2dp_descriptors_count, + .poll_descriptors = a2dp_descriptors, + .poll_revents = a2dp_poll, }; // Force alsa to give use the 44100 hz sound // Or say alsa we will accept only 44100hz? -static int a2dp_constraint(snd_pcm_a2dp_t *a2dp) +static int a2dp_constraint(snd_pcm_a2dp_t * a2dp) { - snd_pcm_ioplug_t *io = &a2dp->io; - snd_pcm_access_t access_list[] = { - SND_PCM_ACCESS_RW_INTERLEAVED, - SND_PCM_ACCESS_MMAP_INTERLEAVED, - }; - unsigned int format[2], channel[2], rate[2]; - int err; + snd_pcm_ioplug_t *io = &a2dp->io; + snd_pcm_access_t access_list[] = { + SND_PCM_ACCESS_RW_INTERLEAVED, + SND_PCM_ACCESS_MMAP_INTERLEAVED, + }; + unsigned int format[2], channel[2], rate[2]; + int err; - syslog(LOG_INFO, "[build %s %s] a2dp %p", __DATE__, __TIME__, a2dp); + syslog(LOG_INFO, "[build %s %s] a2dp %p", __DATE__, __TIME__, a2dp); - err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_ACCESS, 2, access_list); - if (err < 0) - return err; + err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_ACCESS, 2, access_list); + if (err < 0) + return err; - format[0] = SND_PCM_FORMAT_S16_LE; + format[0] = SND_PCM_FORMAT_S16_LE; - err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_FORMAT, 1, format); - if (err < 0) - return err; + err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_FORMAT, 1, format); + if (err < 0) + return err; - channel[0] = 1; - channel[1] = 2; + channel[0] = 1; + channel[1] = 2; - err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_CHANNELS, 2, channel); - if (err < 0) - return err; + err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_CHANNELS, 2, channel); + if (err < 0) + return err; - get_config_filename(g_srcfilename, sizeof(g_srcfilename)); - rate[0] = read_config_int(g_srcfilename, "a2dpd", "rate", A2DPD_FRAME_RATE); - //rate[1] = 48000; + get_config_filename(g_srcfilename, sizeof(g_srcfilename)); + rate[0] = read_config_int(g_srcfilename, "a2dpd", "rate", A2DPD_FRAME_RATE); + //rate[1] = 48000; - err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_RATE, 1, rate); - if (err < 0) - return err; + err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_RATE, 1, rate); + if (err < 0) + return err; - err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIOD_BYTES, 8192, 8192); - if (err < 0) - return err; + err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIOD_BYTES, 8192, 8192); + if (err < 0) + return err; - err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIODS, 2, 2); - if (err < 0) - return err; + err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIODS, 2, 2); + if (err < 0) + return err; - return 0; + return 0; } SND_PCM_PLUGIN_DEFINE_FUNC(a2dpd) { - snd_pcm_a2dp_t *a2dp = NULL; - snd_config_iterator_t i, next; - int err = 0; - - DBG("name %s mode %d", name, mode); - - // set up thread signal handler - signal(SIGPIPE,sighand); - - 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 (!strcmp(id, "comment") || !strcmp(id, "type")) - continue; - - // Ignore old options - if (strstr("ipaddr bdaddr port src dst use_rfcomm", id)) - continue; - - SNDERR("Unknown field %s", id); - return -EINVAL; - } - - a2dp = a2dp_alloc(); - if (!a2dp) - { - SNDERR("Can't allocate plugin data"); - return -ENOMEM; - } - - // Connect - a2dp_connect(a2dp); - - // Notify plugin - a2dp->io.version = SND_PCM_IOPLUG_VERSION; - a2dp->io.name = "Bluetooth Advanced Audio Distribution"; - a2dp->io.mmap_rw = 0; - a2dp->io.callback = &a2dp_callback; - a2dp->io.private_data = a2dp; - - err = snd_pcm_ioplug_create(&a2dp->io, name, stream, mode); - if (err < 0) - goto error; - - err = a2dp_constraint(a2dp); - if (err < 0) { - snd_pcm_ioplug_delete(&a2dp->io); - goto error; - } - - *pcmp = a2dp->io.pcm; - return 0; - -error: - a2dp_disconnect(a2dp); - a2dp_free(a2dp); + snd_pcm_a2dp_t *a2dp = NULL; + snd_config_iterator_t i, next; + int err = 0; + + DBG("name %s mode %d", name, mode); + + // set up thread signal handler + signal(SIGPIPE, sighand); + + 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 (!strcmp(id, "comment") || !strcmp(id, "type")) + continue; + + // Ignore old options + if (strstr("ipaddr bdaddr port src dst use_rfcomm", id)) + continue; + + SNDERR("Unknown field %s", id); + return -EINVAL; + } + + a2dp = a2dp_alloc(); + if (!a2dp) { + SNDERR("Can't allocate plugin data"); + return -ENOMEM; + } + // Connect + a2dp_connect(a2dp); + + // Notify plugin + a2dp->io.version = SND_PCM_IOPLUG_VERSION; + a2dp->io.name = "Bluetooth Advanced Audio Distribution"; + a2dp->io.mmap_rw = 0; + a2dp->io.callback = &a2dp_callback; + a2dp->io.private_data = a2dp; + + err = snd_pcm_ioplug_create(&a2dp->io, name, stream, mode); + if (err < 0) + goto error; + + err = a2dp_constraint(a2dp); + if (err < 0) { + snd_pcm_ioplug_delete(&a2dp->io); + goto error; + } + + *pcmp = a2dp->io.pcm; + return 0; + + error: + a2dp_disconnect(a2dp); + a2dp_free(a2dp); - return err; + return err; } SND_PCM_PLUGIN_SYMBOL(a2dpd); Index: alsa-plugins/sample.a2dprc =================================================================== RCS file: /cvsroot/bluetooth-alsa/btsco/alsa-plugins/sample.a2dprc,v retrieving revision 1.2 diff -u -r1.2 sample.a2dprc --- alsa-plugins/sample.a2dprc 17 Aug 2006 14:06:27 -0000 1.2 +++ alsa-plugins/sample.a2dprc 31 Aug 2006 15:58:57 -0000 @@ -1,10 +1,38 @@ [a2dpd] +# +# Rate +# use 32000 if your headset seems to not support 44100 (HP works well at 44100, Sonorix at 32000) +# Alsa output may not work depending on your graphics card +# Very few players supports it (xmms does, but not amarok/gxine engine) +# rate=44100 #rate=32000 -address=00:0D:44:2A:17:C7 +enablereversestereo=0 + +# +# AVRCP Commands to run +# cmdplay=xmms --play cmdpause=xmms --pause cmdprev=xmms --rew cmdnext=xmms --fwd cmdnew=xmms --play +# Put to 0 to ignore AVRCP (if your computer freezes when commands are received) +enableavrcp=1 + +# +# Audio routing +# +# If set to 1 (at a2dp startup only) a2dp will reread configuration file +# for audio routing changes each second +enablerereadconfig=1 + +# 0 => Bluetooth A2DP Sink +# 1 => Alsa +enableredirectalsa=0 + +# Your bluetooth headset address +address=00:08:F4:30:07:64 +# Address of your alsa output (default : plughw:0,0) you have to know what to do +alsaoutput=