// Modified lsetad.c module by ON5GN 26 jan 2006 // Original module: lsetad.c from linrad-02.00c // Purpose: Make A/D and D/A setup easier // // Mod1: Get info from OSS or ALSA sound-driver about the defined audio // devices. // Display info on screen if space is available // Write info to soundboard_init.log // (New logic) // // Mod2: Modified function make_devname in order to get a match // between the line-number generated by Linrad and the number of // the audio device reported by the sound-driver. // Device /dev/dsp is associated with line number 63. // // Mod3: Modified error message handling in get_dadev and get_addev #include #include #include #include #include #include "globdef.h" #include "uidef.h" #include "sigdef.h" #include "fft1def.h" #include "fft2def.h" #include "conf.h" #include "lconf.h" #include "screendef.h" #include "sdrdef.h" #include "vernr.h" #include "thrdef.h" #include "hwaredef.h" #include "rusage.h" #include "ldef.h" #define SNDLOG fprintf(sndlog, #define ABOVE_MAX_SPEED 768000 #define MAX_COLUMN 80 #define SDR14_DEVICE_DRIVER "/dev/linrad_ft245" #if SND_DEV == 1 #define DEVNAME_BASE "/dev/dsp" #else #define DEVNAME_BASE "/dev/sound/dsp" #endif char dev_name[sizeof(DEVNAME_BASE)+2]; int devmodes[3]={O_RDONLY,O_WRONLY,O_RDWR}; char *devmode_txt[3]={"RDONLY","WRONLY","RDWR"}; int low_speeds[MAX_LOWSPEED]={1,5000,6000,8000,11025,16000, 22050,24000,48000,96000}; void lir_sdr14_write(void *s, int bytes) { write(sdr,s,bytes); } int lir_sdr14_read(void *s,int bytes) { return read(sdr, s, bytes); } void display_sdi(void) { FILE *sdifile; char *p; char work_line [80]; int sdi_screen_line_counter; char sdi_intro_msg [26]= "SOUND DRIVER INFORMATION:" ; char sdi_error_msg [46]= "No OSS or ALSA sound-driver information found"; char sndstat_filename [25]; char sndlog_sep [80]; if(screen_last_col < 100)return; memset (sndlog_sep,'-',sizeof(sndlog_sep)); sndlog_sep[0]='\n'; sndlog_sep[79]='\0'; fprintf(sndlog,sndlog_sep); fprintf(sndlog,"\n%s ", sdi_intro_msg ); settextcolor(YELLOW); lir_text(80, 0, sdi_intro_msg ); sdi_screen_line_counter=1; // Look for OSS-driver info strcpy (sndstat_filename,"/dev/sndstat"); sdifile=fopen ( sndstat_filename,"r"); // if OSS not active, try ALSA if (sdifile==NULL) { strcpy (sndstat_filename,"/proc/asound/oss/sndstat"); sdifile=fopen (sndstat_filename,"r"); } if (sdifile!=NULL) { fprintf(sndlog,"\n(output of 'cat %s' command): \n\n",sndstat_filename ); settextcolor(LIGHT_GREEN); while(fgets(work_line, sizeof(work_line)-1, sdifile) != NULL && sdi_screen_line_counter < screen_last_line) { // check if work_line is ready for output processing p=strchr(work_line, '\n'); if(p != NULL)p[0]=0; if (*work_line != '\0') { // display work_line only if it is not blank // and there are enough lines available on the screen // and there are at least 40 characters available on a line if (sdi_screen_line_counter < screen_last_line && (screen_last_col - 40)>= 80) { sdi_screen_line_counter++; lir_text (80, sdi_screen_line_counter, work_line); } // write to soundboard_init.log fprintf(sndlog,"%s\n",work_line); } } fclose(sdifile); } else { settextcolor(LIGHT_RED); lir_text( 80, sdi_screen_line_counter++, sdi_error_msg); fprintf(sndlog,"\n%s",sdi_error_msg ); } fprintf(sndlog,sndlog_sep); fflush(sndlog); } void clear_sdi(void) { int x, w; // Clear sdi area on screen x=80*text_width; w=screen_width-x-1; if(w>0)lir_fillbox(x,0,w,screen_height,0); } void thread_rx_adinput(void) { double dt1, read_start_time,total_reads; double total_time1, total_time2; short int *rxin_isho2; int *rxin_int, *rxin_int2; int i; int timing_loop_counter,timing_loop_counter_max,initial_skip_flag; int screen_loop_counter,screen_loop_counter_max; #if (RUSAGE_OLD == 1) int wlcnt,wlcnt_max; int local_reset; float t1,t3; double cpu_time1; double cpu_time2; total_time1=current_time(); cpu_time1=lir_get_cpu_time(); wlcnt_max=2*interrupt_rate; wlcnt=wlcnt_max; local_reset=workload_reset_flag; #endif rxinput_local_px=timf1p_pa; rxin_local_workload_reset=workload_reset_flag; timing_loop_counter_max=interrupt_rate; timing_loop_counter=2.5*timing_loop_counter_max; screen_loop_counter_max=0.1*interrupt_rate; if(screen_loop_counter_max==0)screen_loop_counter_max=1; screen_loop_counter=interrupt_rate; open_rx_sndin(); if(kill_all_flag) goto rx_adin_init_error; if(thread_command_flag[THREAD_SCREEN] != THRFLAG_NOT_ACTIVE) { while(thread_status_flag[THREAD_SCREEN]!=THRFLAG_ACTIVE && thread_status_flag[THREAD_SCREEN]!=THRFLAG_IDLE && thread_status_flag[THREAD_SCREEN]!=THRFLAG_SEM_WAIT) { if(thread_command_flag[THREAD_RX_ADINPUT] == THRFLAG_KILL)goto rxadin_error_exit; lir_sleep(10000); } } total_time1=current_time(); read_start_time=total_time1; total_reads=0; initial_skip_flag=1; thread_status_flag[THREAD_RX_ADINPUT]=THRFLAG_ACTIVE; while(thread_command_flag[THREAD_RX_ADINPUT] == THRFLAG_ACTIVE) { #if (RUSAGE_OLD==1) wlcnt--; if(wlcnt==0) { wlcnt=wlcnt_max; total_time2=current_time(); cpu_time2=lir_get_cpu_time(); t3=100/(total_time2-total_time1); t1=(cpu_time2-cpu_time1)*t3; if(t1<0)t1=0; workloads[THREAD_RX_ADINPUT]=t1; if(local_reset != workload_reset_flag) { local_reset=workload_reset_flag; total_time1=total_time2; cpu_time1=cpu_time2; } } #endif timing_loop_counter--; if(timing_loop_counter == 0) { timing_loop_counter=timing_loop_counter_max; total_time2=current_time(); if(initial_skip_flag != 0) { read_start_time=total_time2; total_reads=0; initial_skip_flag=0; } else { total_reads+=timing_loop_counter_max; dt1=total_time2-read_start_time; measured_ad_speed=total_reads*ad_read_fragments/dt1; } } // Here we post to the screen routine every 0.1 second. screen_loop_counter--; if(screen_loop_counter == 0) { screen_loop_counter=screen_loop_counter_max; lir_sem_post(SEM_SCREEN); } rxin_isho=(void*)(&timf1_char[timf1p_pa]); timf1p_pa=(timf1p_pa+ad_read_bytes)&timf1_bytemask; if(ui.rx_addev_no < 256) { read(rx_audio_in, rxin_isho, ad_read_bytes); } else { rxin_isho2=(void*)(&timf1_char[timf1p_pa]); read(rx_audio_in, rxin_isho, ad_read_bytes>>1); read(rx_audio_in2, rxin_isho2, ad_read_bytes>>1); if( (ui.rx_input_mode&DWORD_INPUT) == 0) { for(i=ad_read_fragments-1; i>=0; i--) { rxin_isho[4*i ]=rxin_isho[2*i ]; rxin_isho[4*i+1]=rxin_isho[2*i+1]; rxin_isho[4*i+2]=rxin_isho2[2*i ]; rxin_isho[4*i+3]=rxin_isho2[2*i+1]; } } else { rxin_int=(void*)rxin_isho; rxin_int2=(void*)rxin_isho2; for(i=ad_read_fragments-1; i>=0; i--) { rxin_int[4*i ]=rxin_int[2*i ]; rxin_int[4*i+1]=rxin_int[2*i+1]; rxin_int[4*i+2]=rxin_int2[2*i ]; rxin_int[4*i+3]=rxin_int2[2*i+1]; } } } finish_rx_read(rxin_isho); if(kill_all_flag) goto rxadin_error_exit; } rxadin_error_exit:; while( rx_audio_out == rx_audio_in)lir_sleep(10000); close_rx_sndin(); rx_adin_init_error:; thread_status_flag[THREAD_RX_ADINPUT]=THRFLAG_RETURNED; while(thread_command_flag[THREAD_RX_ADINPUT] != THRFLAG_NOT_ACTIVE) { lir_sleep(1000); } } int lir_rx_output_bytes(void) { if(ioctl(rx_audio_out,SNDCTL_DSP_GETOSPACE, &rx_da_info) == -1)err_restart_da(1109); return rx_da_info.bytes; } int make_da_wts(void) { int i; // Data written to device driver but not yet sent to the speaker. i=0; buftest:; if(ioctl(rx_audio_out,SNDCTL_DSP_GETOSPACE, &rx_da_info) == -1) { err_restart_da(1149); if(kill_all_flag) return 0; } // The number of bytes we can write is incorrectly zero sometimes // wait a little and see if we get a correct value to return. if(rx_da_info.bytes == 0) { lir_sleep(3000); i++; if(i<10)goto buftest; } i=rx_da_totbytes-rx_da_info.bytes; i/=(rx_daout_channels*rx_daout_bytes); return i; } void err_restart_da(int errcod) { int i; lir_sched_yield(); if(kill_all_flag || rx_audio_out == -1) return; close_rx_sndout(); open_rx_sndout(); if(kill_all_flag) return; for(i=0; i rx_da_maxbytes)rx_da_maxbytes=rx_da_info.bytes; if(rx_da_info.bytes < (rx_da_totbytes>>3)) { // Wait for a time corresponding to 0.1 times the duration of // the data currently in the device buffer. t1=rx_da_totbytes-rx_da_info.bytes; t1/=(rx_daout_channels*rx_daout_bytes); t1/=genparm[DA_OUTPUT_SPEED]; lir_sleep(t1*100000); i++; if(i<5)goto buftest; // If we had to wait for half the time corresponding to the // buffer content, something is very wrong. // Force a restart... err_restart_da(23855); if(kill_all_flag) return; } write(rx_audio_out,rx_da_wrbuf,rx_daout_block); } void sndlog_sync(void) { fflush(sndlog); sync(); } void make_devname(int n) { // This routine is greatly simplified July 2006 thanks to Diane Bruce, VA3DB. if(n>MAX_DEVNAMES) { lirerr(1255); return; } if (n+2 == MAX_DEVNAMES) { sprintf(dev_name, "%s", DEVNAME_BASE); } else { sprintf(dev_name, "%s%d", DEVNAME_BASE, n); } } void set_da_parms(void) { int i; char s[128]; ui.rx_min_da_channels=1; ui.rx_max_da_channels=2; if(ioctl(rx_audio_out, SNDCTL_DSP_CHANNELS, &ui.rx_max_da_channels) == -1) ui.rx_max_da_channels=-1; if(ioctl(rx_audio_out, SNDCTL_DSP_CHANNELS, &ui.rx_min_da_channels) == -1) ui.rx_min_da_channels=ui.rx_max_da_channels; if(ui.rx_min_da_channels == -1) { lirerr(1069); return; } if(ioctl(rx_audio_out, SNDCTL_DSP_GETFMTS, &i) == -1) { lirerr(1071); return; } ui.rx_max_da_bytes=1; ui.rx_min_da_bytes=1; if(i&AFMT_S16_LE)ui.rx_max_da_bytes=2; ui.rx_max_da_speed=ABOVE_MAX_SPEED; ui.rx_min_da_speed=1; // // To allow a separate #ifdef __FreeBSD__ // - Diane Bruce VA3DB, Sept 6, 2006 // #ifdef __linux__ if(ioctl(rx_audio_out, SNDCTL_DSP_SPEED, &ui.rx_max_da_speed) == -1) { lirerr(1072); return; } if(ioctl(rx_audio_out, SNDCTL_DSP_SPEED, &ui.rx_min_da_speed) == -1) { lirerr(1073); return; } #endif if(ui.rx_max_da_speed==ABOVE_MAX_SPEED || ui.rx_min_da_speed <= 1 || ui.rx_max_da_speed < ui.rx_min_da_speed) { lir_text(0,7,"Unable to verify output speed. Limits unknown"); genparm[DA_OUTPUT_SPEED]=ui.rx_ad_speed; ui.rx_max_da_speed=48000; ui.rx_min_da_speed=5000; } else { sprintf(s,"Output speed:%.3f to %.3f kHz",0.001*(float)(ui.rx_min_da_speed), 0.001*(float)(ui.rx_max_da_speed)); lir_text(0,7,s); } genparm[DA_OUTPUT_SPEED]=ui.rx_ad_speed; } void close_rx_sndin(void) { if ( rx_audio_in != -1) { if(rx_audio_out == rx_audio_in) { close_rx_sndout(); } if(close(rx_audio_in) == -1) { lirerr(1005); return; } if(ui.rx_addev_no > 255) { if(close(rx_audio_in2) == -1)lirerr(1173); } } rx_audio_in=-1; } int make_dafrag(void) { int i, frag; // We want the fragment size to match rx_output_blockrate, // the rate at which rx_daout_block is written to the output. frag=0; i=rx_daout_block>>1; while(i>0) { i>>=1; frag++; } if(frag < 4)frag=4; frag|=0x7fff0000; return frag; } void close_rx_sndout(void) { if(rx_audio_out == -1)return; if ( rx_audio_out != rx_audio_in) { if(ioctl(rx_audio_out,SNDCTL_DSP_RESET,0)==-1) { lirerr(1026); } if(close(rx_audio_out)==-1)lirerr(1006); } if( rx_da_wrbuf != NULL) { free(rx_da_wrbuf); rx_da_wrbuf=NULL; } rx_audio_out=-1; } void open_rx_sndin(void) { float t1; int i, j, frag; audio_buf_info ad_info; if( (rx_audio_in) != -1) { if( ui.rx_admode == O_RDWR)return; lirerr(100031); return; } make_devname(ui.rx_addev_no&255); rx_audio_in=open( dev_name ,ui.rx_admode , 0); if(rx_audio_in == -1) { lirerr(1007); return; } if(ui.rx_addev_no > 255) { i=ui.rx_addev_no/255-1; make_devname(i); rx_audio_in2=open( dev_name ,ui.rx_admode , 0); if(rx_audio_in2 == -1) { lirerr(1174); return; } } // We want the fragment size to match ad_read_bytes. frag=0; i=ad_read_bytes>>1; while(i>0) { i>>=1; frag++; } if(frag < 4)frag=4; if(ui.rx_addev_no > 255) { frag--; if(frag < 4)frag=4; frag=frag|0x7fff0000; if(ioctl(rx_audio_in2, SNDCTL_DSP_SETFRAGMENT, &frag) == -1) { lirerr(1175); return; } } frag=frag|0x7fff0000; if(ioctl(rx_audio_in, SNDCTL_DSP_SETFRAGMENT, &frag) == -1) { lirerr(1008); return; } i=AFMT_S16_LE; #ifdef AFMT_S32_LE if( (ui.rx_input_mode&DWORD_INPUT) != 0)i=AFMT_S32_LE; #else if( (ui.rx_input_mode&DWORD_INPUT) != 0) { lirerr(1246); return; } #endif j=i; if(ioctl(rx_audio_in, SNDCTL_DSP_SETFMT, &i) == -1) { lirerr(1013); return; } if(i!=j) { lirerr(1014); return; } if(ui.rx_addev_no > 255) { j=i; if(ioctl(rx_audio_in2, SNDCTL_DSP_SETFMT, &i) == -1) { lirerr(1176); return; } if(i!=j) { lirerr(1177); return; } } // Set number of ad_channels j=ui.rx_ad_channels; if(ui.rx_addev_no > 255)j=2; i=j; if(ioctl(rx_audio_in, SNDCTL_DSP_CHANNELS, &i) == -1) { lirerr(1009); return; } if(i != j) { lirerr(1010); return; } if(ui.rx_addev_no > 255) { i=j; if(ioctl(rx_audio_in2, SNDCTL_DSP_CHANNELS, &i) == -1) { lirerr(1178); return; } if(i != j) { lirerr(1179); return; } } i=ui.rx_ad_speed; if(ioctl(rx_audio_in, SNDCTL_DSP_SPEED, &i)==-1) { lirerr(1011); return; } t1=(float)(i)/ui.rx_ad_speed; if(t1 > 1.05 || t1 < 0.95) { lirerr(1012); return; } ui.rx_ad_speed=i; if(ui.rx_addev_no > 255) { if(ioctl(rx_audio_in2, SNDCTL_DSP_SPEED, &i)==-1) { lirerr(1180); return; } if(i != ui.rx_ad_speed) { lirerr(1181); return; } // Now that both devices are configured to be equal, synchronize them. i=0; if(ioctl(rx_audio_in,SNDCTL_DSP_SETTRIGGER,&i)==-1) { lirerr(1183); return; } i=0; if(ioctl(rx_audio_in2,SNDCTL_DSP_SETTRIGGER,&i)==-1) { lirerr(1184); return; } i=PCM_ENABLE_INPUT; if(ioctl(rx_audio_in,SNDCTL_DSP_SETTRIGGER,&i)==-1) { lirerr(1183); return; } i=PCM_ENABLE_INPUT; if(ioctl(rx_audio_in2,SNDCTL_DSP_SETTRIGGER,&i)==-1) { lirerr(1184); return; } } if(ioctl(rx_audio_in, SNDCTL_DSP_GETISPACE, &ad_info) == -1) { lirerr(1109); return; } overrun_limit=ad_info.fragstotal-1; if(ui.rx_addev_no > 255) { if(ioctl(rx_audio_in2, SNDCTL_DSP_GETISPACE, &ad_info) == -1) { lirerr(1109); return; } if(overrun_limit != ad_info.fragstotal-1) { lirerr(1109); } } // This has to be the last ioctl call before read or write!! ioctl(rx_audio_in,SNDCTL_DSP_GETBLKSIZE,&i); } void open_rx_sndout(void) { int i,j; int old_disksave_flag, old_mode, old_chan, old_speed; float t1; int frag; if( (rx_audio_out) != -1) { lirerr(1015); return; } if( (ui.rx_input_mode&NO_DUPLEX) != 0 && rx_audio_in != -1)return; if(ui.rx_dadev_no == -1) { if(disksave_flag >=2) { if(rx_audio_in != -1) { lirerr(9865); return; } make_devname(ui.rx_addev_no&255); rx_audio_out=open( dev_name ,O_WRONLY , 0); if(rx_audio_out == -1) { lirerr(1086); return; } goto set_da; } else { return; } } // ******************************************* if(ui.rx_addev_no == ui.rx_dadev_no && ui.rx_admode == O_RDWR ) { old_disksave_flag=disksave_flag; disksave_flag=0; old_mode=ui.rx_input_mode; ui.rx_input_mode=0; old_chan=ui.rx_ad_channels; ui.rx_ad_channels=rx_daout_channels; old_speed=ui.rx_ad_speed; ui.rx_ad_speed=genparm[DA_OUTPUT_SPEED]; if(rx_audio_in == -1) { open_rx_sndin(); if(kill_all_flag) return; } rx_audio_out=rx_audio_in; disksave_flag=old_disksave_flag; ui.rx_input_mode=old_mode; ui.rx_ad_channels=old_chan; ui.rx_ad_speed=old_speed; } else { make_devname(ui.rx_dadev_no); rx_audio_out=open( dev_name ,O_WRONLY , 0); if(rx_audio_out == -1) { if(disksave_flag==2) { lirerr(1191); } else { lirerr(1017); } return; } set_da:; #ifdef SNDCTL_DSP_COOKEDMODE j=0; ioctl(rx_audio_out, SNDCTL_DSP_COOKEDMODE, &j); #endif frag=make_dafrag(); if(ioctl(rx_audio_out,SNDCTL_DSP_SETFRAGMENT,&frag)==-1) { lirerr(1018); return; } i=rx_daout_channels; if(ioctl(rx_audio_out, SNDCTL_DSP_CHANNELS, &i) == -1) { lirerr(1019); return; } if(i != rx_daout_channels) { lirerr(1099); return; } if(rx_daout_bytes == 1) { i=AFMT_U8; } else { i=AFMT_S16_LE; } j=i; if(ioctl(rx_audio_out, SNDCTL_DSP_SETFMT, &i) == -1) { lirerr(1020); return; } if(i != j) { lirerr(1021); return; } i=genparm[DA_OUTPUT_SPEED]; if(i > 0) { if(ioctl(rx_audio_out, SNDCTL_DSP_SPEED, &i)==-1) { lirerr(1022); return; } t1=(float)(i)/genparm[DA_OUTPUT_SPEED]; if(t1 > 1.05 || t1 < 0.95) { lirerr(1023); return; } genparm[DA_OUTPUT_SPEED]=i; } } rx_da_wrbuf=malloc(rx_daout_block); if(rx_da_wrbuf == NULL) { lirerr(1025); return; } for(i=0; i= SPECIFIC_DEVICE_CODES)return 0; gettimeofday(&tim2,NULL); if(ioctl(rx_audio_in, SNDCTL_DSP_GETISPACE, &ad_info) == -1) { lirerr(1088); return 1; } i=0; while(ad_info.bytes > 1024) { gettimeofday(&tim1,NULL); t1=0.000001*(tim1.tv_usec-tim2.tv_usec)+(tim1.tv_sec-tim2.tv_sec); if(t1 > 1) { if(i==0) { SNDLOG"\nA/D stopped"); close_rx_sndin(); open_rx_sndin(); if(kill_all_flag) return 1; read(rx_audio_in, testbuff, 1024); i++; } if(t1>2) { SNDLOG"\nA/D restart failed"); return 1; } } read(rx_audio_in, testbuff, 1024); if(ioctl(rx_audio_in,SNDCTL_DSP_GETISPACE,&ad_info) == -1) { lirerr(1093); return 1; } } return 0; } int find_output_devices(int line, int *dev_flag, int *dev_wr_bits, int *dev_wr_channels, int *dev_max_wr_speed, int *dev_min_wr_speed) { char s[80]; int n, nn, device_no, j; lir_text(10,line+1,"Scanning output devices"); n=line; for(device_no=0; device_no MAX_COLUMN)maxcolumn=MAX_COLUMN; n=0; column=0; line=1; color=7; speed_warning=0; sndlog = fopen(logfile_name, "w"); if(sndlog == NULL) { lirerr(1016); return; } testbuff = malloc(2048); if(testbuff == NULL) { fclose(sndlog); lirerr(1044); return; } ui.rx_addev_no=-1; ui.rx_dadev_no=-1; clear_screen(); if((ui.network_flag&NET_RX_INPUT) != 0)goto network; timf1_char=malloc(0x8000); if(timf1_char == NULL) { lirerr(1231231); return; } init_sdr14(); free(timf1_char); if(kill_all_flag)return; if(ui.rx_addev_no == -1) { // look for other hardwares } if(ui.rx_addev_no != -1) { if(ui.rx_dadev_no == -1) { network:; line=6; n=find_output_devices(line, dev_flag, dev_wr_bits, dev_wr_channels, dev_max_wr_speed, dev_min_wr_speed); line=n+8; if(n==0) { lirerr(1139); goto setad_errexit; } goto dadev_select; } else { goto setad_x; } } // If none of the supported SDR hardwares is found, use the // sound cards for input and output. // ---------------------------------------------------------- // First step through all devices and check which ones // we can open in read, write and read-write mode. bottom_line=0; settextcolor(14); lir_text(10,0,"Checking device drivers"); settextcolor(7); for(device_no=0; device_no READ==WRITE // bit 1=1 => READ==RDWR // bit 2=1 => WRITE==RDWR if( (dev_flag[device_no]&3) == 3) { j|=1; if(dev_max_rd_channels[device_no]==dev_wr_channels[device_no] && dev_max_rd_speed[device_no]==dev_max_wr_speed[device_no] && dev_min_rd_speed[device_no]==dev_min_wr_speed[device_no] && dev_rd_bits[device_no]==dev_wr_bits[device_no]) k|=1; } if( (dev_flag[device_no]&5) == 5) { j|=2; if(dev_max_rd_channels[device_no]==dev_rdwr_channels[device_no] && dev_max_rd_speed[device_no]==dev_max_rdwr_speed[device_no] && dev_min_rd_speed[device_no]==dev_min_rdwr_speed[device_no] && dev_rd_bits[device_no]==dev_rdwr_bits[device_no])k|=2; } if( (dev_flag[device_no]&6) == 6) { j|=4; if(dev_wr_channels[device_no]==dev_rdwr_channels[device_no] && dev_max_wr_speed[device_no]==dev_max_rdwr_speed[device_no] && dev_min_wr_speed[device_no]==dev_min_rdwr_speed[device_no] && dev_wr_bits[device_no]==dev_rdwr_bits[device_no])k|=4; } SNDLOG"\nj=%d k=%d",j,k); if(j == k) { // There are no differences between devices. Put everything on one line. m=1; if( (dev_flag[device_no]&1) == 1) { sprintf(ss,"%02d:%12s %7d - %7d Hz %d Chan. %d bit", device_no,dev_name, dev_min_rd_speed[device_no], dev_max_rd_speed[device_no], dev_max_rd_channels[device_no], dev_rd_bits[device_no]); } else { if( (dev_flag[device_no]&2) == 2) { sprintf(ss,"%02d:%12s %7d - %7d Hz %d Chan. %d bit", device_no,dev_name, dev_min_wr_speed[device_no], dev_max_wr_speed[device_no], dev_wr_channels[device_no], dev_wr_bits[device_no]); } } for(j=0; j<3; j++) { k=1<0) { m--; line++; if(line >= maxline) { for(column=0; column line)line=bottom_line; if(n == 0) { if(line > maxline-12)line=maxline-12; lir_text(0,line,"No sound device found (for 16/32 bit input)."); lir_text(10,line+2,"If you are using OSS:"); lir_text(0,line+3,"Run soundconf command to make sure OSS is correctly"); lir_text(0,line+4,"initialised for your sound board(s)."); lir_text(0,line+5,"Then start sound with soundon command."); lir_text(10,line+7,"Without OSS:"); lir_text(0,line+8,"Find out how to configure your sound device drivers(s)"); lir_text(0,line+9,"Some distributions have the sndconfig command."); lir_text(0,line+10, "Sound board drivers are usually configured during Linux install"); lir_text(20,line+12,press_any_key); await_keyboard(); if(kill_all_flag) goto setad_errexit; SNDLOG"\nNo input device found"); goto setad_errexit; } if(n > 1) { get_addev:; settextcolor(14); lir_text(0,line,"Select device for input by first number on line"); display_sdi(); settextcolor(7); ui.rx_addev_no=lir_get_integer(48, line, 3, 0,MAX_DEVNAMES-1); if(kill_all_flag)goto setad_errexit; if( (5 & dev_flag[ui.rx_addev_no]) == 0) { settextcolor(15); lir_text(51,line,"???"); lir_text(35,line+1,"ERROR (press any key)"); await_keyboard(); if(kill_all_flag) goto setad_errexit; clear_lines(line,line+1); goto get_addev; } } clear_screen(); make_devname(ui.rx_addev_no); SNDLOG"\n\n %s selected by user for input.",dev_name); SNDLOG" dev_flag=%d",dev_flag[ui.rx_addev_no]); sndlog_sync(); // High end users might need two devices to read four A/D channels. // This is an option introduced April 2004 to allow two receive // channels with the Lynx Two card (model A). // Do not confuse other users by asking for a second input device // unless the selected device has 32 bits but only allows 2 channels. if( dev_rd_bits[ui.rx_addev_no] == 32 && dev_max_rd_channels[ui.rx_addev_no]==2 && (dev_flag[ui.rx_addev_no]&1) != 0) { SNDLOG"\n\nUser selected 32 bit device with max two channels."); j=0; line=1; for(i=0; i",dev_name); lir_text(10,10,s); await_processed_keyboard(); if(kill_all_flag) goto setad_errexit; if(lir_inkey == 'W')goto rdwr; if(lir_inkey != 'O')goto gt_rdo; SNDLOG"\nUser selected RDONLY"); } else { rdwr:; ui.rx_admode=O_RDWR; mode=2; SNDLOG"\nUser selected RDWR"); } } else { SNDLOG"\nOnly RDONLY available"); } sndlog_sync(); clear_screen(); sprintf(s,"For analog input: %s opened in %s mode",dev_name,devmode_txt[mode]); lir_text(0,0,s); rx_audio_in=open( dev_name, ui.rx_admode, 0); if(rx_audio_in == -1) { lirerr(1038); goto setad_errexit; } j=dev_max_rd_channels[ui.rx_addev_no]; m=j; k=j>>1; while(k!=0) { i=k; if(ioctl(rx_audio_in, SNDCTL_DSP_CHANNELS, &i) == -1) { lirerr(1039); goto setad_errexit; } if(i==k)m|=k; k>>=1; } lir_text(0,10,"Select radio interface:"); if(j == 2)j=3; k=0; if((m&1)==0)k=1; if(j==4 && (m&2)==0)k=4; for(i=k; ij)goto chsel; switch (lir_inkey) { case '1': ui.rx_rf_channels=1; ui.rx_input_mode=0; ui.rx_ad_channels=1; break; case '2': ui.rx_input_mode=IQ_DATA; ui.rx_rf_channels=1; ui.rx_ad_channels=2; break; case '3': ui.rx_input_mode=TWO_CHANNELS; ui.rx_rf_channels=2; ui.rx_ad_channels=2; break; case '4': ui.rx_input_mode=TWO_CHANNELS+IQ_DATA; ui.rx_rf_channels=2; ui.rx_ad_channels=4; break; default: goto chsel; } SNDLOG"\nui.rx_input_mode=%d ui.rx_rf_channels=%d ui.rx_ad_channels =%d", ui.rx_input_mode, ui.rx_rf_channels, ui.rx_ad_channels); sndlog_sync(); i=ui.rx_ad_channels; if(ioctl(rx_audio_in, SNDCTL_DSP_CHANNELS, &i) == -1) { lirerr(1039); goto setad_errexit; } if(i != ui.rx_ad_channels) { SNDLOG"\nDSP_CHANNELS goto setad_errexited %d (%d) exit:1027",i,ui.rx_ad_channels); sndlog_sync(); lirerr(1027); goto setad_errexit; } continue_dual:; clear_lines(10,11+j); #ifdef SOUND_OSS sprintf(s,"Use ossmix"); #else sprintf(s,"Use system mixer program"); #endif column=0; while(s[column] != 0)column++; sprintf(&s[column]," to select input and"); lir_text(0,2,s); lir_text(0,3,"to disable direct connection from input to output etc."); lir_text(0,5,"In case selection of sampling frequencies is not what you"); lir_text(0,6,"expect, check README files for your board - there may be ways"); lir_text(0,7,"to reconfigure the hardware. (Under OSS, run ossmix)"); max=dev_max_rd_speed[ui.rx_addev_no&255]; min=dev_min_rd_speed[ui.rx_addev_no&255]; if(max == ABOVE_MAX_SPEED || max < 1000) { max = ABOVE_MAX_SPEED; speed_warning = 1; } sprintf(s,"Select sampling speed(%d to %d)", min,max); lir_text(0,10,s); if(speed_warning != 0) { settextcolor(12); lir_text(0,12, "WARNING!!!! device does not respond correctly to speed setting command"); lir_text(0,13,"Incorrect (not supported) values may cause system to hang"); settextcolor(7); } column=0; while(s[column] != 0)column++; ui.rx_ad_speed=lir_get_integer(column+2, 10, 8, min,max); lir_refresh_screen(); if(kill_all_flag)goto setad_errexit; rdwr_max_speed=ui.rx_ad_speed; SNDLOG"\n%d Hz selected by user",ui.rx_ad_speed); clear_lines(5,10); if(ioctl(rx_audio_in, SNDCTL_DSP_SPEED, &ui.rx_ad_speed)==-1) { lirerr(1041); goto setad_errexit; } if( (float)ui.rx_ad_speed > (float)rdwr_max_speed*1.05 || (float)ui.rx_ad_speed < (float)rdwr_max_speed*0.95 ) { // The user selected something that was unacceptable to the device driver. // Rather than just accepting whatever value goto setad_errexited, // could be -22 (an unspecified error code) we step the desired speed // upwards, looking for a valid speed. // In case we fail, an attempt is made downwards as well. // **************************************************************** // This code is written by Ulf, SM0LCB SNDLOG"\nReqested speed didn't work"); // This code will search up/down from requested speed // until the driver will respond with valid code // Search use delta stepping of 3% from current speed // This to speed up the search for a valid speed // First search up in fq SNDLOG"\nSearch up : "); for( trySpeed=rdwr_max_speed, deltaSpeed=(float)(rdwr_max_speed)*0.03; trySpeed<192000; trySpeed+=deltaSpeed ) { ui.rx_ad_speed=trySpeed; if(ioctl(rx_audio_in, SNDCTL_DSP_SPEED, &ui.rx_ad_speed)==-1) { lirerr(1024); goto setad_errexit; } // test if valid speed // must be float converted because int will overflow if( (float)ui.rx_ad_speed < (float)trySpeed*1.05 && (float)ui.rx_ad_speed > (float)trySpeed*0.95 ) { SNDLOG"try speed %d Hz ", trySpeed); goto spok; } deltaSpeed=(float)(trySpeed)*0.03; } SNDLOG"no speed found"); // Then search down in fq SNDLOG"\nSearch down : "); for( trySpeed=rdwr_max_speed, deltaSpeed=(float)(rdwr_max_speed)*0.03; trySpeed>5000; trySpeed-=deltaSpeed ) { ui.rx_ad_speed=trySpeed; if(ioctl(rx_audio_in, SNDCTL_DSP_SPEED, &ui.rx_ad_speed)==-1) { lirerr(1135); goto setad_errexit; } // test if valid speed if( (float)ui.rx_ad_speed < (float)trySpeed*1.05 && (float)ui.rx_ad_speed > (float)trySpeed*0.95 ) { SNDLOG"try speed %d Hz ", trySpeed); goto spok; } deltaSpeed=(float)(trySpeed)*0.03; } SNDLOG"no speed found"); // If we didn't find any vaid speed SNDLOG"\nNo valid speed found !"); // Exit program lirerr(1134); goto setad_errexit; } spok:; SNDLOG"\n%d Hz reported by device",ui.rx_ad_speed); sndlog_sync(); sprintf(s,"Input speed: %.3fkHz",0.001*(float)(ui.rx_ad_speed)); lir_text(0,5,s); if( dev_rd_bits[ui.rx_addev_no&255] == 32) { lir_text(0,10,"Select A/D data format:"); lir_text(0,12,"1: 16 bit"); lir_text(0,13,"2: 32 bit"); selfmt:; await_processed_keyboard(); if(kill_all_flag) goto setad_errexit; if(lir_inkey == '2') { ui.rx_input_mode|=DWORD_INPUT; } else { if(lir_inkey != '1')goto selfmt; } } clear_lines(10,13); i=AFMT_S16_LE; j=16; #ifdef AFMT_S32_LE if( (ui.rx_input_mode&DWORD_INPUT) != 0) { i=AFMT_S32_LE; j=32; } #endif sprintf(s,"Input format: %d bit",j); SNDLOG"\nInput format: %d bit",j); lir_text(30,5,s); j=i; if(ioctl(rx_audio_in, SNDCTL_DSP_SETFMT, &i) == -1) { lirerr(1042); goto setad_errexit; } if(i!=j) { lirerr(1043); goto setad_errexit; } settextcolor(12); sprintf(s,"Trying to read %s",dev_name); lir_text(10,10,s); lir_text(0,12,"If you see this message for more than 2 seconds READ failed"); lir_text(0,13,"Then try another device"); if( ui.rx_admode == O_RDWR)lir_text(37,13,"or open as RDONLY"); settextcolor(7); gettimeofday(&tim2,NULL); read(rx_audio_in, testbuff, 2048); read(rx_audio_in, testbuff, 2048); if(ui.rx_addev_no > 255)read(rx_audio_in2, testbuff, 2048); if(ioctl(rx_audio_in, SNDCTL_DSP_GETISPACE, &ad_info) == -1) { lirerr(1045); goto setad_errexit; } t1=0; j=ui.rx_ad_speed*ui.rx_ad_channels; k=j; if( (ui.rx_input_mode&IQ_DATA) != 0)j*=2; while(t1<1.5 && j>0 && k>0) { gettimeofday(&tim1,NULL); t1=0.000001*(tim1.tv_usec-tim2.tv_usec)+(tim1.tv_sec-tim2.tv_sec); if(ioctl(rx_audio_in, SNDCTL_DSP_GETISPACE, &ad_info) == -1) { lirerr(1028); goto setad_errexit; } if(ad_info.bytes > 1024) { read(rx_audio_in, testbuff, 1024); j-=1024; } if(ui.rx_addev_no > 255) { if(ioctl(rx_audio_in2, SNDCTL_DSP_GETISPACE, &ad_info) == -1) { lirerr(1028); goto setad_errexit; } if(ad_info.bytes > 1024) { read(rx_audio_in2, testbuff, 1024); k-=1024; } else { k=j; } } } if(j > 0) { settextcolor(12); lir_text(10,14,"Read failed!!! (press any key)"); close_rx_sndin(); await_processed_keyboard(); if(kill_all_flag) goto setad_errexit; ui.rx_input_mode=-1; SNDLOG"\nRead test failed"); goto setad_errexit; } SNDLOG"\nRead test OK"); clear_lines(10,13); sprintf(s,"Now trying to open a second device while %s is open",dev_name); lir_text(0,6,s); lir_text(0,7,"This may cause your system to hang. Try ctrlC to exit (or reboot)"); lir_text(0,8,"Try another input device or install another sound system."); // Find devices that we may use for output. n=0; cn=0; line=10; for(i=0; i 2) { sprintf(&s[column], " RDWR useless (too many bits or channels) %d: %s",i,dev_name); dev_flag[i]=0; } else { sprintf(&s[column]," %d Hz %d Chan. 16 bit RDWR", ui.rx_ad_speed, ui.rx_ad_channels); n++; ui.rx_dadev_no=i; } } } else { // Remove duplex flags on all devices except the one we use for input dev_flag[i]&=0xffff-4; if( (dev_flag[i]&2) != 0 ) { rx_audio_out=open( dev_name, O_WRONLY|O_NONBLOCK, 0); if(read_test_ad(testbuff)!=0)goto clr_wrdev; if(rx_audio_out != -1) { #ifdef SNDCTL_DSP_COOKEDMODE j=0; ioctl(rx_audio_out, SNDCTL_DSP_COOKEDMODE, &j); #endif SNDLOG"\n%s opened as WRONLY",dev_name); sndlog_sync(); if(ioctl(rx_audio_in, SNDCTL_DSP_GETISPACE, &ad_info) == -1) { lirerr(1094); goto setad_errexit; } olbytes=ad_info.bytes; j=AFMT_U8; if(ioctl(rx_audio_out, SNDCTL_DSP_SETFMT, &j) == -1) { SNDLOG"\nCould not set 8-bit output"); dev_wr_bits[i]=0; } else { SNDLOG"\n8-bit output supported"); dev_wr_bits[i]=8; } sndlog_sync(); j=AFMT_S16_LE; if(ioctl(rx_audio_out, SNDCTL_DSP_SETFMT, &j) == -1 || j != AFMT_S16_LE) { SNDLOG"\nCould not set 16-bit output"); } else { dev_wr_bits[i]=16; } SNDLOG"\n16-bit output supported"); sndlog_sync(); if(dev_wr_bits[i]==0) goto skip_wr_dev; sndlog_sync(); j=2; if(ioctl(rx_audio_out, SNDCTL_DSP_CHANNELS, &j) == -1) { j=1; if(ioctl(rx_audio_out, SNDCTL_DSP_CHANNELS, &j) == -1) { SNDLOG"\nioctl(CHANNELS) failed"); goto skip_wr_dev; } } if( j != 1 && j != 2) { SNDLOG"\nNo of channels not 1 or 2"); goto skip_wr_dev; } SNDLOG"\nNo of output channels= %d",j); sndlog_sync(); dev_wr_channels[i]=j; j=ABOVE_MAX_SPEED; if(ioctl(rx_audio_out, SNDCTL_DSP_SPEED, &j) == -1 || j==ABOVE_MAX_SPEED) { SNDLOG"\nIncorrect speed response in %d out %d", ABOVE_MAX_SPEED, j); if(ioctl(rx_audio_out, SNDCTL_DSP_SPEED, &j)==-1) { SNDLOG"\nioctl(SPEED) failed"); goto skip_wr_dev; } } dev_max_wr_speed[i]=j; SNDLOG"\nMax output speed %d", dev_max_wr_speed[i]); j=1; if(ioctl(rx_audio_out, SNDCTL_DSP_SPEED, &j) == -1 || j==1) { SNDLOG"\nIncorrect speed response in %d out %d", 1, j); if(ioctl(rx_audio_out, SNDCTL_DSP_SPEED, &j)==-1) { SNDLOG"\nioctl(SPEED) failed"); goto skip_wr_dev; } } dev_min_wr_speed[i]=j; SNDLOG"\nMin output speed %d", dev_min_wr_speed[i]); sndlog_sync(); if(read_test_ad(testbuff)!=0)goto skip_wr_dev; if(rx_audio_out == -1) { SNDLOG"\nFailed to reopen for write test."); goto skip_wr_dev; } for(j=0; j<1024; j++)testbuff[j]=0; write(rx_audio_out, testbuff, 1024); if(ioctl(rx_audio_out,SNDCTL_DSP_GETOSPACE, &ad_info) == -1) { SNDLOG"\nWrite failed"); goto skip_wr_dev; } sound_start_time=current_time(); wrcnt=0; j=ad_info.bytes; while(wrcnt+ad_info.bytes < 3*1024+j ) { wrcnt+=1024; write(rx_audio_out, testbuff, 1024); if(ioctl(rx_audio_out,SNDCTL_DSP_GETOSPACE, &ad_info) == -1) { SNDLOG"\nSNDCTL_DSP_GETOSPACE failed"); goto skip_wr_dev; } if( current_time() - sound_start_time > 0.7) { SNDLOG"\nTimeout"); goto skip_wr_dev; } if(read_test_ad(testbuff)!=0)goto skip_wr_dev; } if(ioctl(rx_audio_out,SNDCTL_DSP_RESET, 0) == -1) { SNDLOG"\nSNDCTL_DSP_RESET"); goto skip_wr_dev; } if(read_test_ad(testbuff)!=0)goto skip_wr_dev; if(ioctl(rx_audio_in, SNDCTL_DSP_GETISPACE, &ad_info) == -1) { lirerr(1095); goto setad_errexit; } olbytes=ad_info.bytes; gettimeofday(&tim2,NULL); while(ad_info.bytes-olbytes < 512) { if(ioctl(rx_audio_in,SNDCTL_DSP_GETISPACE, &ad_info) == -1) { lirerr(1097); goto setad_errexit; } gettimeofday(&tim1,NULL); t1=0.000001*(tim1.tv_usec-tim2.tv_usec)+(tim1.tv_sec-tim2.tv_sec); if(t1 > 1) { SNDLOG"\nA/D input stopped after RESET D/A"); goto skip_wr_dev; } } if(close(rx_audio_out)==-1) { goto skip_wr_dev; } sprintf(&s[column]," %7d - %7d Hz %d Chan. %d bit WRONLY", dev_min_wr_speed[i], dev_max_wr_speed[i], dev_wr_channels[i], dev_wr_bits[i]); cn=1; n++; ui.rx_dadev_no=i; } else { goto clr_wrdev; skip_wr_dev:; sndlog_sync(); if(close(rx_audio_out)==-1) { lirerr(1067); goto setad_errexit; } clr_wrdev:; dev_flag[i]&=0xffff-2; } } } if(cn != 0) { lir_text(0,line,s); if(line < maxline+1)line++; } } if(cn == 0) { clear_lines(line,line); } else { line++; } if(n == 0) { if(line > maxline-5)line=maxline-5; lir_text(0,line,"No device found for 8/16 bit output while input is open."); lir_text(0,line+1,"You may use Linrad to watch spectra and record raw data."); lir_text(0,line+2,"Linrad will now look for devices that can be used to"); lir_text(0,line+3,"output sound while processing recorded data."); lir_text(0,line+4,press_any_key); SNDLOG"\nNo output device found while input is open."); close_rx_sndin(); await_processed_keyboard(); if(kill_all_flag) goto setad_errexit; clear_screen(); n=0; cn=0; line=0; ui.rx_dadev_no=-1; for(i=0; i 1024) { write(rx_audio_out, testbuff, 1024); if(ioctl(rx_audio_out,SNDCTL_DSP_GETOSPACE, &ad_info) == -1) goto skip_wr_dev_nodup; } if(close(rx_audio_out)==-1)goto skip_wr_dev_nodup; sprintf(&s[column]," %d Hz %d Chan. %d bit WRONLY", dev_max_wr_speed[i], dev_wr_channels[i], dev_wr_bits[i]); cn=1; n++; ui.rx_dadev_no=i; dev_flag[i] |= 2; } else { goto clr_wrdev_nodup; skip_wr_dev_nodup:; sndlog_sync(); if(close(rx_audio_out)==-1) { lirerr(1138); goto setad_errexit; } clr_wrdev_nodup:; dev_flag[i]&=0xffff-2; } if(cn != 0) { lir_text(0,line,s); if(line < maxline+1)line++; } } if(cn == 0) { clear_lines(line,line); } else { line++; } if(n == 0) { SNDLOG"\n"); fclose(sndlog); lirerr(1139); goto setad_errexit; } ui.rx_input_mode|=NO_DUPLEX; ad_info.bytes=0; } dadev_select:; clear_lines(6,8); if(n > 1) { get_dadev:; settextcolor(14); lir_text(0,line,"Select device for output by line number"); display_sdi(); settextcolor(7); ui.rx_dadev_no=lir_get_integer(41, line, 2, 0,99); // Mod3: Modified error message handling in get_dadev and get_addev if( (6 & dev_flag[ui.rx_dadev_no]) == 0) { settextcolor(15); lir_text(44,line,"???"); lir_text(35,line+1,"ERROR (press any key)"); await_keyboard(); if(kill_all_flag) goto setad_errexit; clear_lines(line,line+1); goto get_dadev; } clear_lines(6,line); line=8; } else { line=12; } if(ui.rx_dadev_no == -1) { lirerr(1289); goto setad_errexit; } make_devname(ui.rx_dadev_no); SNDLOG"\n%s selected for output.",dev_name); clear_sdi(); lir_text(0,line,"Use close and reopen for D/A when a new signal is"); lir_text(0,line+1,"selected or when the output device fails? (Y/N)"); get_da_stopstart:; await_processed_keyboard(); if(kill_all_flag) goto setad_errexit; if(lir_inkey == 'Y') { ui.rx_da_stopstart=1; } else { if(lir_inkey != 'N')goto get_da_stopstart; ui.rx_da_stopstart=0; } clear_lines(6,line+1); line=6; // If we selected the same device for input and output, // check if it was opened ad RDWR or if we shall open it // for output now if( ((ui.network_flag&NET_RX_INPUT) == 0) && ui.rx_dadev_no == ui.rx_addev_no && ui.rx_admode == O_RDWR && (ui.rx_input_mode&NO_DUPLEX) == 0 ) { rx_audio_out=rx_audio_in; ui.rx_max_da_channels=ui.rx_ad_channels; ui.rx_min_da_channels=ui.rx_ad_channels; ui.rx_max_da_bytes=2; ui.rx_min_da_bytes=2; ui.rx_max_da_speed=ui.rx_ad_speed; ui.rx_min_da_speed=ui.rx_ad_speed; } else { rx_audio_out=open( dev_name ,O_WRONLY , 0); sprintf(s,"Using %s for output",dev_name); lir_text(0,line,s); if(rx_audio_out == -1) { lirerr(1068); goto setad_errexit; } #ifdef SNDCTL_DSP_COOKEDMODE i=0; ioctl(rx_audio_out, SNDCTL_DSP_COOKEDMODE, &i); #endif rx_da_wrbuf = NULL; set_da_parms(); close_rx_sndout(); } close_rx_sndin(); sprintf(s,"Min output channels: %d",ui.rx_min_da_channels); lir_text(0,line+2,s); sprintf(s,"Max output channels: %d",ui.rx_max_da_channels); lir_text(0,line+3,s); rx_daout_channels=ui.rx_min_da_channels; rx_daout_bytes=ui.rx_max_da_bytes; sprintf(s,"Output no of bits=%d",8*ui.rx_max_da_bytes); lir_text(35,line+1,s); if(ui.rx_max_da_bytes != ui.rx_min_da_bytes) { sprintf(s,"or %d",8*ui.rx_min_da_bytes); lir_text(56,line+1,s); } // Reopen with the current parameters. // We will not goto setad_errexit here if something goes wrong. if( (ui.network_flag&NET_RX_INPUT) == 0 && (ui.rx_input_mode&NO_DUPLEX) == 0 && ui.rx_addev_no < SPECIFIC_DEVICE_CODES) { open_rx_sndin(); if(kill_all_flag) goto setad_errexit; genparm[DA_OUTPUT_SPEED]=0; open_rx_sndout(); if(kill_all_flag) goto setad_errexit; read(rx_audio_in, testbuff, 2048); if(ioctl(rx_audio_out,SNDCTL_DSP_GETOSPACE, &ad_info) == -1) { lirerr(1001); goto setad_errexit; } close_rx_sndin(); } close_rx_sndout(); lir_text(0,line+4,"Audio board(s) configured without errors."); lir_text(0,line+5,remind_parsave); setad_x:; lir_text(10,line+7,press_any_key); toupper_await_keyboard(); SNDLOG"\nNormal end"); setad_errexit:; SNDLOG"\n"); fclose(sndlog); free(testbuff); }