From 1dd49b43aa1dd5736e9301015dc7d4a1e515cdcc Mon Sep 17 00:00:00 2001 From: pico-coder <99157244+pico-coder@users.noreply.github.com> Date: Sun, 6 Feb 2022 21:03:01 -0700 Subject: [PATCH] Add files via upload --- pico_pgen/CMakeLists.txt | 23 + pico_pgen/pico_sdk_pgen.c | 51 ++ pico_sdk_sr_gen1/CMakeLists.txt | 26 + pico_sdk_sr_gen1/pico_sdk_sr_gen_1.bak.c | 171 +++++ pico_sdk_sr_gen1/sr_device.h | 371 +++++++++++ sigrok-generic/api.c | 604 +++++++++++++++++ sigrok-generic/protocol.c | 797 +++++++++++++++++++++++ sigrok-generic/protocol.h | 169 +++++ 8 files changed, 2212 insertions(+) create mode 100644 pico_pgen/CMakeLists.txt create mode 100644 pico_pgen/pico_sdk_pgen.c create mode 100644 pico_sdk_sr_gen1/CMakeLists.txt create mode 100644 pico_sdk_sr_gen1/pico_sdk_sr_gen_1.bak.c create mode 100644 pico_sdk_sr_gen1/sr_device.h create mode 100644 sigrok-generic/api.c create mode 100644 sigrok-generic/protocol.c create mode 100644 sigrok-generic/protocol.h diff --git a/pico_pgen/CMakeLists.txt b/pico_pgen/CMakeLists.txt new file mode 100644 index 0000000..66c6251 --- /dev/null +++ b/pico_pgen/CMakeLists.txt @@ -0,0 +1,23 @@ +cmake_minimum_required(VERSION 3.13) + +include(pico_sdk_import.cmake) + +project(test_project C CXX ASM) +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +pico_sdk_init() + +add_executable(pico_sdk_pgen + pico_sdk_pgen.c +) + +pico_enable_stdio_usb(pico_sdk_pgen 0) +pico_enable_stdio_uart(pico_sdk_pgen 1) + +pico_add_extra_outputs(pico_sdk_pgen) + +target_link_libraries( + pico_sdk_pgen + pico_stdlib + +) diff --git a/pico_pgen/pico_sdk_pgen.c b/pico_pgen/pico_sdk_pgen.c new file mode 100644 index 0000000..2d79bd7 --- /dev/null +++ b/pico_pgen/pico_sdk_pgen.c @@ -0,0 +1,51 @@ +//simple generator +//Mask of bits 22:2 to use as outputs - +#define GPIO_D_MASK 0x7FFFFC + +#include +#include "pico/stdlib.h" +#include "hardware/gpio.h" +#include "stdarg.h" +#include + + +int main(){ + + long int i,j; + +// set_sys_clock_khz(250000,true); +// uart_set_format(uart0,8,1,1); +// uart_init(uart0,115200); + stdio_init_all(); + gpio_set_function(0, GPIO_FUNC_UART); + gpio_set_function(1, GPIO_FUNC_UART); + sleep_us(100000); + printf("\n\n\nhello from pgen \r\n\n\n"); + //Use 22:2 as digital + gpio_init_mask(GPIO_D_MASK); //set as GPIO_FUNC_SIO and clear output enable + gpio_set_dir_masked(GPIO_D_MASK,GPIO_D_MASK); //TODO - I think an out can still be an ind? + uint32_t ctime,tstart; + uint32_t cval; + tstart=time_us_32(); + while(true){ + ctime=time_us_32(); + // shift up/down to test performance with rle etc + //cval=(ctime-tstart)<<4; //not a true 62.5ns/8Mhz wave, but will increment as such + //cval=(ctime-tstart)<<2; //not a true 250ns/2Mhz wave, but will increment as such + //cval=(ctime-tstart); //1us, 500kz + //cval=(ctime-tstart)>>1; //2us, 256khz + //cval=(ctime-tstart)>>3; //8us, 64khz + cval=(ctime-tstart)>>5; //32us, 16khz + //cval=(ctime-tstart)>>6; //64us, 8khz + //cval=(ctime-tstart)>>7; //128us, 4khz + //cval=(ctime-tstart)>>9; //512us, 1khz + //cval=(ctime-tstart)>>11; //2ms, 250hz + //cval=0x123456789; + //Note: both the mask and the value must be shifted up by the starting number of bits + gpio_put_masked(GPIO_D_MASK,cval<<2); + //printf("cval 0x%X",cval); + }//while true + + + +}//main diff --git a/pico_sdk_sr_gen1/CMakeLists.txt b/pico_sdk_sr_gen1/CMakeLists.txt new file mode 100644 index 0000000..154f245 --- /dev/null +++ b/pico_sdk_sr_gen1/CMakeLists.txt @@ -0,0 +1,26 @@ +cmake_minimum_required(VERSION 3.13) + +include(pico_sdk_import.cmake) + +project(test_project C CXX ASM) +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +pico_sdk_init() + +add_executable(pico_sdk_sr_gen_1 + pico_sdk_sr_gen_1.c +) + +pico_enable_stdio_usb(pico_sdk_sr_gen_1 1) +pico_enable_stdio_uart(pico_sdk_sr_gen_1 0) + +pico_add_extra_outputs(pico_sdk_sr_gen_1) + +target_link_libraries( + pico_sdk_sr_gen_1 + pico_stdlib + hardware_adc + hardware_dma + hardware_pio + pico_multicore +) diff --git a/pico_sdk_sr_gen1/pico_sdk_sr_gen_1.bak.c b/pico_sdk_sr_gen1/pico_sdk_sr_gen_1.bak.c new file mode 100644 index 0000000..62daa63 --- /dev/null +++ b/pico_sdk_sr_gen1/pico_sdk_sr_gen_1.bak.c @@ -0,0 +1,171 @@ +//1st version to communicate over sigrok generic +#include +#include "pico/stdlib.h" +#include "hardware/gpio.h" +#include "pico/binary_info.h" +#include "stdarg.h" +#include +const uint LED_PIN = 25; +#define MAX_FMT_SIZE 256 +#define MAX_SAMPLE_RATE 125000000 +#define MAX_SAMPLES 100000 + +int Dprintf(const char *fmt, ...) +{ + va_list argptr; + int len = 1; + char _dstr[256]; + + + memset(&_dstr, 0x00, sizeof(_dstr) ); + va_start(argptr, fmt); + len = vsprintf(_dstr, fmt, argptr); + va_end(argptr); + + if ( (len > 0) && (len < 256 ) ) + { + + uart_puts(uart0,_dstr); + uart_puts(uart0,"\r\n"); + } + return len; +} +int main(){ + char cmdstr[20]; + int cmdstrptr=0; + char charin,tmpchar; + int intin; + long int intin2,i,j,idx; + int cnt=0; + int armed=0; + int num_a=3,num_d=2; + long int sample_rate=125000000; //max sample rate is initial value + unsigned long int scnt,num_samples=5; + char tmpstr[100],a_bytes=3; + long int tmpint,tmpint2; + int res; + uint64_t starttime,endtime; +// sleep_ms(1000); //might help stability??? + stdio_usb_init(); +// sleep_ms(1000); + uart_set_format(uart0,8,1,1); + intin=uart_init(uart0,115200); + + gpio_set_function(0, GPIO_FUNC_UART); + gpio_set_function(1, GPIO_FUNC_UART); +//doesn't seem to help stabiliyt sleep_ms(3000); + Dprintf("hello from loggerport \r\n"); + intin=0; + while(1){ + intin=getchar_timeout_us(0);//000); + if(intin>=0){ //PICO_ERROR_TIMEOUT=-1 + charin=(char)intin; +//TODO - SCPI may only send the \n, not \r... + if((charin=='\r')||(charin=='\n')){ + cmdstr[cmdstrptr]=0; + Dprintf("Cmd %s",cmdstr); + if(!strcmp(cmdstr,"*IDN?")){ +//AxxxyDzzz - num analog, analog size, num digital +//24 characters plus newline +// printf("SR_GENERIC,A0023D002,0,0\n"); + printf("SR_GENERIC,A%03d%dD%03d,0,0\n",num_a,a_bytes,num_d); + Dprintf("sent IDN response"); + } + else if(!strncmp(cmdstr,"SRT",3)){ + if(cmdstr[3]=='?'){ + printf("%d\n",sample_rate); + Dprintf("smp rate rsp %d",sample_rate); + }else{ + tmpint=atoi(&cmdstr[3]); + if((tmpint>0)&&(tmpint<=MAX_SAMPLE_RATE)){ + sample_rate=tmpint; + Dprintf("set smp rate to %d",sample_rate); + }else{ + Dprintf("unsupported smp rate %s",cmdstr); + } + } + } + else if(!strncmp(cmdstr,"SMP",3)){ + if(cmdstr[3]=='?'){ + printf("%d\n",num_samples); + Dprintf("num smp rate rsp %d",num_samples); + }else{ + tmpint=atoi(&cmdstr[3]); + if((tmpint>0)&&(tmpint<=MAX_SAMPLES)){ + num_samples=tmpint; + Dprintf("set num smp to %d",num_samples); + }else{ + Dprintf("unsupported num samples %s",cmdstr); + } + } + } + else if(!strncmp(cmdstr,"ARM",3)){ + armed=1; + scnt=0; + Dprintf("armed"); + starttime=time_us_64();//get_absolute_time(); + } + else if(!strncmp(cmdstr,"STP",3)){ + //TODO -send end of capture if armed... + armed=0; + scnt=0; + Dprintf("stopped"); + } + cmdstrptr=0; + + }else{ +// sprintf(tmpstr,"newchar %c %d \r\n",charin,intin); +// uart_puts(uart0,tmpstr); + if(cmdstrptr>=19){ + cmdstrptr=0; + Dprintf("Command overflow"); + } + cmdstr[cmdstrptr++]=charin; + + } + }//intin>=0 +//I modified the pico sdk to add a puts_raw_len function so that I could send byte patterns of fixed length +//that were not c strings. But, going to use ascii compliant values to avoid serial configuration issues +//and thus can end a set of bytes with a null to make it a string +// puts_raw_len("0000000000111111111122222222223333333333",40); +// scnt+=4; + + if(armed&&(scnt>(j*8))&0xFF; +// Dprintf("tmpint2 %d",tmpint2); + //convert to ascii mode - lowest nibble first + tmpstr[idx++]=((char)(tmpint2&0xF))+'0'; + tmpstr[idx++]=((char)((tmpint2>>4)&0xF))+'0'; + // Dprintf("chars %c %c",tmpstr[idx-2],tmpstr[idx-1]); + } + } //for num_a + for(i=0;i>i)&0xf; + // Dprintf("D%d val %d",i,tmpint); + tmpstr[idx++]=(tmpint&0xF)+'0'; + // Dprintf("chars %c ",tmpstr[idx-1]); + } + tmpstr[idx]=0; +// puts_raw(tmpstr); + printf("%s",tmpstr); + Dprintf("Slice string %s",tmpstr); + scnt++; + if(scnt==num_samples){ + armed=0; + endtime=time_us_64(); + Dprintf("Total time %llu %llu %llu",(endtime-starttime),endtime,starttime); + } + } //if armed + }//while(1) + + +}//main diff --git a/pico_sdk_sr_gen1/sr_device.h b/pico_sdk_sr_gen1/sr_device.h new file mode 100644 index 0000000..28fb0b9 --- /dev/null +++ b/pico_sdk_sr_gen1/sr_device.h @@ -0,0 +1,371 @@ +//number of analog channels +//#define NUM_A_CHAN 3 +//TODO - testing a D4 only build to see if it's faster because I don't +//need to replicate unused channel data +#define NUM_A_CHAN 0 +//number of digital channels +//GP0 and 1 are reserved for uart +//GP23-25 are not on the board and 26-28 are ADC. +//Thus a max of 21 digital channels +//#define NUM_D_CHAN 21 + +#define NUM_D_CHAN 4 +//Mask of bits 22:2 to use as inputs - +#define GPIO_D_MASK 0x7FFFFC +//number of bytes per analog sample, must be 1,2,3,or 4 +//TODO - make it just be always 1 +#define A_BYTES 1 +//enable RLE encoding +#define RLE_EN 1 +//TODO -finalize a value +#define RLE_THRESH 2000 +//number of chars between makers +#define STRIDE 128 +//TODO - implement various limits on sample rate +#define MAX_SAMPLE_RATE 120000000 +//warning - with a 256b limit in the Dprintf, large strings won't dprintf well +#define MRK_STR_SIZE 240 //TODO - larger for testing puts_raw_len +//TODO - long term prints don't belong in this code +int Dprintf(const char *fmt, ...) +{ + va_list argptr; + int len = 1; + char _dstr[256]; + + + memset(&_dstr, 0x00, sizeof(_dstr) ); + va_start(argptr, fmt); + len = vsprintf(_dstr, fmt, argptr); + va_end(argptr); + + if ( (len > 0) && (len < 240 ) ) + { + + uart_puts(uart0,_dstr); + uart_tx_wait_blocking(uart0); +//don't always want \n +// uart_puts(uart0,"\r\n"); +// uart_tx_wait_blocking(uart0); + } + else{ + uart_puts(uart0,"UART OVRFLW"); + uart_tx_wait_blocking(uart0); + + } + return len; +} +typedef struct sr_device { + uint32_t sample_rate; + uint32_t num_samples; + uint32_t a_mask,d_mask; + uint32_t byte_cnt; //total serial bytes sent + uint32_t samples_per_half; //number of samples for one of the 4 dma target arrays + uint8_t a_chan_cnt; //count of enabled analog channels + uint8_t d_chan_cnt; //count of enabled digital channels + //Pins sampled by the PIO - 4,8,16 or 32 + uint8_t pin_count; + uint8_t d_nps; //digital nibbles per slice from a PIO/DMA perspective. + uint32_t scnt; //number of samples sent + char cmdstrptr; + char cmdstr[20];//used for parsing input + uint32_t d_size,a_size; //size of each of the two data buffers for each of a& d + uint32_t dbuf0_start,dbuf1_start,abuf0_start,abuf1_start; + //TODO optimize this - this is the size of string we send , for now deriving from sample size + uint32_t send_size; + char rspstr[20]; + //The current slice being created + char slicestr[64]; + char slicestridx; +#ifdef RLE_EN + char prevslicestr[64]; + uint32_t rle_cnt; +#endif + //String with markers inserted used to send the final transmit data + uint32_t mrkbyte_cnt; + char mrkstr[MRK_STR_SIZE]; + uint16_t mrkstridx; +//mark key control variables voltatile since multiple cores access them + volatile bool started; + volatile bool sending; + volatile bool cont; + volatile bool aborted; + } sr_device_t; + +//Add one 4 bit equivalent character +//TODO - I don't think the inlines help. +void inline add_slice_char(sr_device_t *d,char bytein){ + d->slicestr[d->slicestridx++]=bytein; +}; + + +//returns 0 if no string to send +//otherwise returns the length of the string that is in d->mrkstridx which needs to be printed +uint32_t slice_done(sr_device_t *d){ + bool almost_done; +// int almost_done; + almost_done =((d->cont==true)||(((d->num_samples)-(d->scnt))>2)) ? false: true; +// almost_done =((((d->num_samples)-(d->scnt))>2)) ? false: true; +// almost_done =((((d->num_samples)-(d->scnt))>2)) ? 0: 1; + if(d->scnt>99900){ + // Dprintf("sd cnt %u ad %d cont %d ns %d\n\r",d->scnt,almost_done,d->cont,d->num_samples); + } + //Dprintf("sd %d\n\r",d->slicestridx); + //terminate the current slice string + d->slicestr[d->slicestridx]=0; + bool match; +#ifdef RLE_EN + match=((d->scnt>0)&&(RLE_EN)&&(strncmp(d->prevslicestr,d->slicestr,d->slicestridx)==0)) ? true : false; + strncpy(d->prevslicestr,d->slicestr,d->slicestridx); +//Only increment rle if we matched, as if we don't match we just flush the previous ones. + if(match) d->rle_cnt++; + //If not a match we must drain all RLEs before sending the new sample. + //The third condition ensures we don't have any rle counts that haven't been pushed to the marker + //string, so send all slices as we get close to the end. + if(((match==false)&&(d->rle_cnt>0)) + ||(d->rle_cnt>=RLE_THRESH) + ||((d->rle_cnt>0)&&(almost_done==true))){ + uint8_t rlenum; + char rlechar; +// Dprintf("push rle %d match %d slicestridx %d ad %d scnt %u\n\r",d->rle_cnt,match,d->slicestridx,almost_done,d->scnt); + while(d->rle_cnt>256){ + rlenum=(d->rle_cnt>=4096) ? 16 : (d->rle_cnt>>8); + //subtract the 1 because 256 is starting value, not 0 + rlechar=rlenum+96-1; + d->rle_cnt-=rlenum<<8; + insert_mark(d,rlechar); + //Dprintf("rle256 %d char %d %c cnt %d",rlenum,rlechar,rlechar,rle_cnt); + } + while(d->rle_cnt>16){ + rlenum=(d->rle_cnt>=256) ? 16: (d->rle_cnt>>4); + rlechar=rlenum+80-1; + d->rle_cnt-=rlenum<<4; + insert_mark(d,rlechar); + //Dprintf("rle16 %d char %d %c cnt %d",rlenum,rlechar,rlechar,rle_cnt); + } + while(d->rle_cnt>0){ + rlenum=(d->rle_cnt>=16) ? 16: (d->rle_cnt); + rlechar=rlenum+64-1; + d->rle_cnt-=rlenum; + insert_mark(d,rlechar); + //Dprintf("rle1 %d char %d %c cnt %d",rlenum,rlechar,rlechar,rle_cnt); + } + }//(match==false) || rle_cnt +#else + match=false; +#endif + if(match==false){ +// Dprintf("nomatch pushes slice idx %d",slicestridx); + for(int cnt=0;cntslicestridx;cnt++){ + insert_mark(d,d->slicestr[cnt]); + } + + } + //Empiricaly found that 16 bytes provides the highest bandwidth + //likely that's because we balance new sample generation with the ability to drain a packet. + //TODO - validate that is still the case... + uint32_t ret; + if(almost_done==true){ + // Dprintf("ad %u\n\r",d->scnt); + } +// Dprintf("curr #%s# \n\r",d->mrkstr); +// Dprintf("curr idx %d\n\r",d->mrkstridx); + +//TODO - 16 was before I added the sleep.. trying larger payloads with smaller sleep + if((d->mrkstridx>d->send_size) //SPOT + ||(d->mrkstridx>=(MRK_STR_SIZE-2)) + ||(almost_done==true)){ + d->mrkstr[d->mrkstridx]=0; +// Dprintf("tx %s %d\n\r",d->mrkstr,d->mrkstridx); + ret=d->mrkstridx; +//TODO - hack - for the len function rely on user to clear +// d->mrkstridx=0; + }else{ + ret=0; + } + d->scnt++; + d->slicestridx=0; + return ret; + +}; +//reset as part of init, or on a completed send +void reset(sr_device_t *d){ + d->sending=0; + d->cont=0; + d->aborted=false; + d->started=false; + d->mrkbyte_cnt=0; + d->mrkstridx=0; + d->slicestridx=0; + d->scnt=0; +#ifdef RLE_EN + d->rle_cnt=0; +#endif +}; +//initial post reset state +void init(sr_device_t *d){ + reset(d); + d->a_mask=0; + d->d_mask=0; + d->sample_rate=1000; + d->num_samples=10; + d->a_chan_cnt=0; + d->d_nps=0; + d->cmdstrptr=0; +} +void tx_init(sr_device_t *d){ + reset(d); + d->a_chan_cnt=0; + for(int i=0;ia_mask)>>i)&1){d->a_chan_cnt++;} + } + //Nibbles per slice controls how PIO digital data is stored + //Only support 0,1,2,4 or 8, which use 0,4,8,16 or 32 bits of PIO fifo data + //per sample clock. + d->d_nps=(d->d_mask& 0xF) ? 1 : 0; + d->d_nps=(d->d_mask& 0xF0) ? (d->d_nps)+1 : d->d_nps; + d->d_nps=(d->d_mask& 0xFF00) ? (d->d_nps)+2 : d->d_nps; + d->d_nps=(d->d_mask&0xFFFF0000) ? (d->d_nps)+4 : d->d_nps; + + if((d->d_nps==0) &&(d-> a_chan_cnt==0)){ + Dprintf("Error no channels enabled\n\r"); + printf("!!!!!"); //stop transfer to host + return; + } + //Digital channels must enable from D0 and go up + int invld=0; //have we seen a digital channel that is not valid + d->d_chan_cnt=0; + for(int i=0;id_mask)>>i)&1){ + Dprintf("i %d inv %d mask %X\n\r",i,invld,d->d_mask); + if(invld){ + Dprintf("Error:Dig chan D%d (pin D%d) not continuous from 0 (pin D2)\n\r",i,i+2); + printf("!!!!!"); //stop transfer to host + return; + + }else{ + d->d_chan_cnt++; + } + }else{ + invld=1; + } + } + + + d->sending=true; + +} +//Since the sending of data on the link is device +//specific, return a non zero value to tell +//the user code to issue a write of d->respstr +int process_char(sr_device_t *d,char charin){ + int tmpint,tmpint2,ret; + //set default rspstr for all commands that have a dataless ack + d->rspstr[0]='*'; + d->rspstr[1]=0; + //the reset character works by itself + if(charin=='*'){ + reset(d); + Dprintf("RST* sending %d\n\r",d->sending); + return 0; + }else if((charin=='\r')||(charin=='\n')){ + d->cmdstr[d->cmdstrptr]=0; + switch(d->cmdstr[0]){ + case 'i': + //SREGEN,AxxyDzz,00 - num analog, analog size, num digital,version + sprintf(d->rspstr,"SRGEN,A%02d1D%02d,00\n",NUM_A_CHAN,NUM_D_CHAN); + Dprintf("ID rsp %s\n\r",d->rspstr); + ret=1; + break; + case 'R': + tmpint=atol(&(d->cmdstr[1])); + if((tmpint>0)&&(tmpint<=MAX_SAMPLE_RATE)){ + d->sample_rate=tmpint; + Dprintf("SMPRATE= %u\n\r",d->sample_rate); + ret=1; + } + else { + Dprintf("unsupported smp rate %s\n\r",d->cmdstr); + ret=0; + } + break; + //sample limit + case 'L': + tmpint=atol(&(d->cmdstr[1])); + if(tmpint>0){ + d->num_samples=tmpint; + Dprintf("NUMSMP=%u",d->num_samples); + ret=1; + }else{ + Dprintf("unsupported num samples %s\n\r",d->cmdstr); + ret=0; + } + break; + case 'a': + tmpint=atoi(&(d->cmdstr[1])); //extract channel number + if(tmpint>=0){ + sprintf(d->rspstr,"0.0257,0.0\n"); //3.3/(2^7) and 0V offset + Dprintf("ASCL%d\n\r",tmpint); + ret=1; + }else{ + Dprintf("bad ascale %s\n\r",d->cmdstr); + ret=1; //this will return a '*' causing the host to fail + } + break; + case 'F': //fixed set of samples + Dprintf("starting fixed dump\n\r"); + tx_init(d); + d->cont=0; + ret=0; + break; + case 'C': //continous mode + tx_init(d); + d->cont=1; + Dprintf("starting continous stream\n\r"); + ret=0; + break; + //format is Axyy where x is 0 for disabled, 1 for enabled and yy is channel # + case 'A': ///enable analog channel always a set + tmpint=d->cmdstr[1]-'0'; //extract enable value + tmpint2=atoi(&(d->cmdstr[2])); //extract channel number + if((tmpint>=0)&&(tmpint<=1)&&(tmpint2>=0)&&(tmpint2<=31)){ + d->a_mask=d->a_mask & ~(1<a_mask=d->a_mask | (tmpint<a_mask); + ret=1; + }else{ + ret=0; + } + break; + //format is Dxyy where x is 0 for disabled, 1 for enabled and yy is channel # + case 'D': ///enable digital channel always a set + tmpint=d->cmdstr[1]-'0'; //extract enable value + tmpint2=atoi(&(d->cmdstr[2])); //extract channel number + if((tmpint>=0)&&(tmpint<=1)&&(tmpint2>=0)&&(tmpint2<=31)){ + d->d_mask=d->d_mask & ~(1<d_mask=d->d_mask | (tmpint<d_mask); + ret=1; + }else{ + ret=0; + } + break; + default: + Dprintf("bad command %s\n\r",d->cmdstr); + ret=0; + }//case +// Dprintf("CmdDone %s\n\r",d->cmdstr); + d->cmdstrptr=0; + }else{ //no CR/LF + if(d->cmdstrptr>=19){ + d->cmdstr[18]=0; + Dprintf("Command overflow %s\n\r",d->cmdstr); + d->cmdstrptr=0; + } + d->cmdstr[d->cmdstrptr++]=charin; + ret=0; + }//else +//default return 0 means to not send any kind of response + return ret; +}//process_char + + diff --git a/sigrok-generic/api.c b/sigrok-generic/api.c new file mode 100644 index 0000000..640e3b2 --- /dev/null +++ b/sigrok-generic/api.c @@ -0,0 +1,604 @@ +/* + * This file is part of the libsigrok project. + * + * Copyright (C) 2018 mhooijboer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +//debug print levels are err/warn/info/dbg/spew +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "libsigrok-internal.h" +#include "protocol.h" + + +#define SERIALCOMM "115200/8n1" + +static const uint32_t scanopts[] = { + SR_CONF_CONN, //Required OS name for the port, i.e. /dev/ttyACM0 + SR_CONF_SERIALCOMM, //Optional config of the port, i.e. 115200/8n1 +}; + +//PulseView reads a sample rate config list as a min, max and step. +//If step is 1 then it creates a 1,2,5,10 set of selects, as well as the max. +//If step is not 1, then it gives a place to enter any value, which gives the greatest flexibility +static const uint64_t samplerates[] = { + SR_HZ(10), + SR_MHZ(120), + SR_HZ(2), +}; + +static const uint32_t drvopts[] = { + SR_CONF_OSCILLOSCOPE, + SR_CONF_LOGIC_ANALYZER, +}; +//SW trigger requires this +static const int32_t trigger_matches[] = { + SR_TRIGGER_ZERO, + SR_TRIGGER_ONE, + SR_TRIGGER_RISING, + SR_TRIGGER_FALLING, + SR_TRIGGER_EDGE, +}; + + +static const uint32_t devopts[] = { +//CLI prefers LIMIT_SAMPLES to be a list of high,low + SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, + SR_CONF_TRIGGER_MATCH | SR_CONF_LIST, + SR_CONF_CAPTURE_RATIO | SR_CONF_GET | SR_CONF_SET, +//pulseview needs a list return to allow sample rate setting + SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, +}; + +static struct sr_dev_driver sr_generic_driver_info; + + +static GSList *scan(struct sr_dev_driver *di, GSList *options) +{ + struct sr_config *src; + struct sr_dev_inst *sdi; + struct sr_serial_dev_inst *serial; + struct dev_context *devc; + struct sr_channel *ch; + GSList *l; + int num_read; + unsigned int i; + const char *conn, *serialcomm; + char buf[32]; + int len; + uint8_t num_a,num_d,a_size; + gchar *channel_name; + + conn = serialcomm = NULL; + for (l = options; l; l = l->next) { + src = l->data; + switch (src->key) { + case SR_CONF_CONN: + conn = g_variant_get_string(src->data, NULL); + break; + case SR_CONF_SERIALCOMM: + serialcomm = g_variant_get_string(src->data, NULL); + break; + } + } + if (!conn) + return NULL; + + if (!serialcomm) + serialcomm = SERIALCOMM; + + serial = sr_serial_dev_inst_new(conn, serialcomm); + sr_info("Opening %s.", conn); + if (serial_open(serial, SERIAL_RDWR) != SR_OK){ + sr_err("1st serial open fail"); + return NULL; + } + sr_info("Reseting device with *s at %s.", conn); + send_serial_char(serial,'*'); + g_usleep(10000); + //drain any inflight data + do{ + sr_warn("Drain reads"); + len=serial_read_blocking(serial, buf,32,100); + sr_warn("Drain reads done"); + if(len) sr_dbg("Dropping in flight serial data"); + }while(len>0); + + + //Send identify + num_read=send_serial_w_resp(serial,"i\n",buf,20); + if(num_read<16){ + sr_err("1st identify failed"); + serial_close(serial); + g_usleep(100000); + if (serial_open(serial, SERIAL_RDWR) != SR_OK){ + sr_err("2st serial open fail"); + return NULL; + } + g_usleep(100000); + sr_err("Send second *"); + send_serial_char(serial,'*'); + g_usleep(100000); + num_read=send_serial_w_resp(serial,"i\n",buf,20); + if(num_read<10){ + sr_err("Second attempt failed"); + return NULL; + } + } + //Expected ID response is SRGEN,AxxyDzz,VV + //where xx are number of analog channels, y is bytes per analog sample + //and zz is number of digital channels, and VV is two digit version# which must be 00 + if((num_read<16) + ||(strncmp(buf,"SRGEN,A",7)) + ||(buf[10]!='D') + ||(buf[14]!='0') + ||(buf[15]!='0')){ + sr_err("ERROR:Bad response string %s %d",buf,num_read); + return NULL; + } + a_size=buf[9]-'0'; + buf[9]='\0'; //Null to end the str for atois + buf[13]='\0'; //Null to end the str for atois + num_a=atoi(&buf[7]); + num_d=atoi(&buf[11]); + + + sdi = g_malloc0(sizeof(struct sr_dev_inst)); + sdi->status = SR_ST_INACTIVE; + sdi->vendor = g_strdup("Sigrok"); + sdi->model = g_strdup("Generic"); + sdi->version = g_strdup("00"); + sdi->conn = serial; +//broken/fixme +// sdi->driver = &sr_generic_driver_info; + sdi->inst_type = SR_INST_SERIAL; + sdi->serial_num = g_strdup("N/A"); + if(((num_a==0)&&(num_d==0)) + ||(num_a>MAX_ANALOG_CHANNELS) + ||(num_d>MAX_DIGITAL_CHANNELS) + ||(a_size<1) + ||(a_size>4)){ + sr_err("ERROR: invalid channel config a %d d %d asz %d",num_a,num_d,a_size); + return NULL; + } + devc = g_malloc0(sizeof(struct dev_context)); + //TODO- deprecate a_size + devc->a_size=a_size; + //multiple bytes per analog sample not supported + if((num_a>0)&&(devc->a_size!=1)){ + sr_err("Only Analog Size of 1 supported\n\r"); + return NULL; + } + devc->num_a_channels=num_a; + devc->num_d_channels=num_d; + devc->a_chan_mask=((1<d_chan_mask=((1<dig_sample_bytes=((devc->num_d_channels/8)+1); + devc->dig_sample_bytes=(devc->num_d_channels/8); + if((devc->num_d_channels)&0x7) devc->dig_sample_bytes++; + //These are the slice sizes of the data on the wire + //1 7 bit field per byte + devc->bytes_per_slice=(devc->num_a_channels*devc->a_size); + if(devc->num_d_channels>0){ + // logic sent in groups of 7 + devc->bytes_per_slice+=(devc->num_d_channels+6)/7; + } + sr_dbg("num channels a %d d %d bps %d dsb %d",num_a,num_d,devc->bytes_per_slice,devc->dig_sample_bytes); +//Each analog channel is it's own grup +//Digital are just channels +//Grouping of channels is rather arbitrary as parameters like sample rate and number of samples +//apply to all changes. Analog channels do have a scale and offset, but that it does +//without involvement of the session. + devc->analog_groups = g_malloc0(sizeof(struct sr_channel_group *) * + devc->num_a_channels); + for (i = 0; i < devc->num_a_channels; i++) { + channel_name = g_strdup_printf("A%d", i ); + //sdi, index, type, enabled,name + ch = sr_channel_new(sdi, i, SR_CHANNEL_ANALOG, TRUE, channel_name); + devc->analog_groups[i] = g_malloc0(sizeof(struct sr_channel_group)); + devc->analog_groups[i]->name = channel_name; + devc->analog_groups[i]->channels = g_slist_append(NULL, ch); + sdi->channel_groups = g_slist_append(sdi->channel_groups, + devc->analog_groups[i]); + } + + if (devc->num_d_channels>0) { + for (i = 0; i < devc->num_d_channels; i++){ + channel_name = g_strdup_printf("D%d", i); + sr_channel_new(sdi, i, SR_CHANNEL_LOGIC, TRUE, + channel_name); + g_free(channel_name); + } + + } + //In large sample usages we get the call to receive with large transfers. + //Since the CDC serial implemenation can silenty lose data as it gets close to full, allocate + //storage for a half buffer which in a worst case scenario has 2x ratio of transmitted bytes to storage bytes + devc->serial_buffer_size=256000; + devc->buffer=NULL; + sr_dbg("Setting serial buffer size: %i.", devc->serial_buffer_size); + devc->cbuf_wrptr=0; + //While slices are sent as a group of one sample across all channels, sigrok wants analog + //channel data sent as separate packets. + //Logical trace values are packed together. + //A serial byte can never represent more than one sample, but allocate 2x the space anyway. + devc->sample_buf_size=devc->serial_buffer_size*2; + for(i=0;inum_a_channels;i++){ + devc->a_data_bufs[i]=NULL; + devc->a_pretrig_bufs[i]=NULL; + } + devc->d_data_buf=NULL; + devc->sample_rate=1000; + devc->capture_ratio=10; + devc->rxstate=RX_IDLE; + sdi->priv = devc; + //Set an initial value as various code relies on an inital value. + devc->limit_samples=1000; + +//TODO - check return code,though I might just eliminate this function and add it's code here + sr_generic_get_dev_cfg(sdi); + + sr_err("sr_err level logging enabled"); + sr_warn("sr_warn level logging enabled"); + sr_info("sr_info level logging enabled"); + sr_dbg("sr_dbg level logging enabled"); + sr_spew("sr_spew level logging enabled"); +//TODO - ols closes the serial port here - should I? + return std_scan_complete(di, g_slist_append(NULL, sdi)); + +} + + + +//Note that on the initial driver load we pull all values into local storage. +//Thus gets can return local data, but sets have to issue commands to device. +static int config_set(uint32_t key, GVariant *data, + const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) +{ + struct dev_context *devc; + int ret; + (void)cg; + if (!sdi) + return SR_ERR_ARG; + devc=sdi->priv; + ret = SR_OK; + sr_dbg("Got config_set key %d \n",key); + switch (key) { + case SR_CONF_SAMPLERATE: + devc->sample_rate = g_variant_get_uint64(data); + sr_dbg("config_set sr %llu\n",devc->sample_rate); + break; + case SR_CONF_LIMIT_SAMPLES: + devc->limit_samples = g_variant_get_uint64(data); + sr_dbg("config_set slimit %lld\n",devc->limit_samples); + break; + case SR_CONF_CAPTURE_RATIO: + devc->capture_ratio = g_variant_get_uint64(data); + break; + + default: + sr_err("ERROR:config_set undefine %d\n",key); + ret = SR_ERR_NA; + } + + return ret; +} + +static int config_get(uint32_t key, GVariant **data, + const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) +{ + struct dev_context *devc; + sr_dbg("at config_get key %d",key); + (void)cg; + if (!sdi) + return SR_ERR_ARG; + + devc = sdi->priv; + switch (key) { + case SR_CONF_SAMPLERATE: + *data = g_variant_new_uint64(devc->sample_rate); + sr_spew("sample rate get of %lld",devc->sample_rate); + break; + case SR_CONF_CAPTURE_RATIO: + if (!sdi) + return SR_ERR; + devc = sdi->priv; + *data = g_variant_new_uint64(devc->capture_ratio); + break; + case SR_CONF_LIMIT_SAMPLES: + sr_spew("config_get limit_samples of %llu",devc->limit_samples); + *data = g_variant_new_uint64(devc->limit_samples); + break; + default: + sr_spew("unsupported cfg_get key %d",key); + return SR_ERR_NA; + } + return SR_OK; +} + +static int config_list(uint32_t key, GVariant **data, + const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) +{ + (void)cg; + //scan or device options are the only ones that can be called without a defined instance + if((key==SR_CONF_SCAN_OPTIONS)||(key==SR_CONF_DEVICE_OPTIONS)){ + return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts); + } + if (!sdi){ + sr_err("ERROR:\n\r\n\r\n\r Call to config list with null sdi\n\r\n\r"); + return SR_ERR_ARG; + } + sr_dbg("start config_list with key %X\n",key); + switch(key){ +//Pulseview in pulseview/pv/toolbars/mainbar.cpp requires list support for frequencies as a triple +//of min,max,step. If step is 1, then it proves a 1,2,5,10 select, but if not 1 it allows a spin box + case SR_CONF_SAMPLERATE: + sr_dbg("Return sample rate list"); + *data = std_gvar_samplerates_steps(ARRAY_AND_SIZE(samplerates)); + break; +//This must be set to get SW trigger support + case SR_CONF_TRIGGER_MATCH: + *data = std_gvar_array_i32(ARRAY_AND_SIZE(trigger_matches)); + break; + case SR_CONF_LIMIT_SAMPLES: + //Really this limit is up to the memory capacity of the host, + //and users that pick huge values deserve what they get. + //But setting this limit to prevent really crazy things. + *data = std_gvar_tuple_u64(1LL,1000000000LL); + sr_dbg("sr_config_list limit samples "); + break; + default: + sr_dbg("reached default statement of config_list"); + + return SR_ERR_NA; + } + + return SR_OK; +} + +static int dev_acquisition_start(const struct sr_dev_inst *sdi) +{ + struct sr_serial_dev_inst *serial; + struct dev_context *devc; + struct sr_channel *ch; + struct sr_trigger *trigger; + char tmpstr[20]; + GSList *l; + int a_enabled=0,d_enabled=0,len; + //TODO - downgrade all the sr_err that are not errors once this works + serial = sdi->conn; + int i; + devc = sdi->priv; + sr_dbg("Enter acq start"); + sr_dbg("dsbstart %d",devc->dig_sample_bytes); + devc->buffer = g_malloc(devc->serial_buffer_size); + if(!(devc->buffer)){sr_err("ERROR:serial buffer malloc fail");return SR_ERR_MALLOC;} + +//Get device in idle state + if(serial_drain(serial)!=SR_OK){sr_err("Initial Drain Failed\n\r");return SR_ERR;} + send_serial_char(serial,'*'); + if(serial_drain(serial)!=SR_OK){sr_err("Second Drain Failed\n\r");return SR_ERR;} + + sprintf(tmpstr,"L%lld\n", devc->limit_samples); + if(send_serial_w_ack(serial, tmpstr)!=SR_OK) { + sr_err("Sample limit to device failed"); + return SR_ERR; + } + sprintf(&tmpstr[0],"R%llu\n", devc->sample_rate); + if(send_serial_w_ack(serial, tmpstr)!=SR_OK) { + sr_err("Sample rate to device failed"); + return SR_ERR; + } + + for (l = sdi->channels; l; l = l->next) { + ch = l->data; + sr_dbg("c %d enabled %d name %s\n",ch->index,ch->enabled,ch->name); + + if(ch->name[0]=='A'){ + devc->a_chan_mask&=~(1<index); + if(ch->enabled) { + devc->a_chan_mask|=(ch->enabled<index); + a_enabled++; + } + sr_dbg("A%d en %d mask 0x%X",ch->index,ch->enabled,devc->a_chan_mask); + + } + if(ch->name[0]=='D'){ + devc->d_chan_mask&=~(1<index); + if(ch->enabled) { + devc->d_chan_mask|=(ch->enabled<index); + d_enabled++; + } + sr_dbg("D%d en %d mask 0x%X",ch->index,ch->enabled,devc->d_chan_mask); + } + sprintf(tmpstr,"%c%d%d\n",ch->name[0],ch->enabled,ch->index); +//TODO - do something with the error + if (send_serial_w_ack(serial,tmpstr) != SR_OK){ + sr_err("ERROR:Channel enable fail"); + } else{ + } + }//for all channels + //recalculate bytes_per_slice. + devc->bytes_per_slice=(a_enabled*devc->a_size); + + for(i=0;inum_d_channels;i+=7){ + if(((devc->d_chan_mask)>>i)&(0x7F)){(devc->bytes_per_slice)++;} + } + + sr_dbg("bps %d\n",devc->bytes_per_slice); + devc->sent_samples=0; + devc->byte_cnt=0; + devc->bytes_avail=0; + devc->wrptr=0; + devc->cbuf_wrptr=0; + len=serial_read_blocking(serial, devc->buffer, devc->serial_buffer_size,serial_timeout(serial, 4)); + if(len>0){ + sr_info("Pre-ARM drain had %d characters:",len); + devc->buffer[len]=0; + sr_info("%s",devc->buffer); + } + + for(i=0;inum_a_channels;i++){ + devc->a_data_bufs[i]=g_malloc(devc->sample_buf_size*sizeof(float)); + if(!(devc->a_data_bufs[i])){sr_err("ERROR:analog buffer malloc fail");return SR_ERR_MALLOC;} + } + if(devc->num_d_channels>0){ + devc->d_data_buf=g_malloc(devc->sample_buf_size*devc->dig_sample_bytes); + if(!(devc->d_data_buf)){sr_err("ERROR:logic buffer malloc fail");return SR_ERR_MALLOC;} + } + +//TODO-understand these params + if ((trigger = sr_session_trigger_get(sdi->session))) { + devc->pretrig_entries = (devc->capture_ratio * devc->limit_samples) / 100; + devc->stl = soft_trigger_logic_new(sdi, trigger, devc->pretrig_entries); + if (!devc->stl) + return SR_ERR_MALLOC; + devc->trigger_fired=FALSE; + if(devc->pretrig_entries>0){ + sr_dbg("Allocating pretrig buffers size %d",devc->pretrig_entries); + for(i=0;inum_a_channels;i++){ + if((devc->a_chan_mask>>i)&1){ +//TODO-might look for the pointers being not null? + devc->a_pretrig_bufs[i] = g_malloc0(sizeof(float)*devc->pretrig_entries); + if(!devc->a_pretrig_bufs[i]){ + sr_err("ERROR:Analog pretrigger buffer malloc failure, disabling"); + devc->trigger_fired=TRUE; + } + }//if chan_mask + }//for num_a_channels + }//if pre_trigger + + + sr_info("Entering sw triggered mode"); + //post the receive before starting the device to ensure we are ready to receive data ASAP +//TODO-Jan/1 I had this at 7000 for scpi version but most serial uses are in the 50-200 range +//note that I call it twice (just below) + serial_source_add(sdi->session, serial, G_IO_IN, 200,sr_generic_receive, (void *) sdi); + sprintf(tmpstr,"C\n"); + if(send_serial_str(serial, tmpstr) != SR_OK) + return SR_ERR; + + } else{ + devc->trigger_fired=TRUE; + devc->pretrig_entries=0; + sr_info("Entering fixed sample mode"); + serial_source_add(sdi->session, serial, G_IO_IN, 200,sr_generic_receive, (void *) sdi); + sprintf(tmpstr,"F\n"); + if(send_serial_str(serial,tmpstr) != SR_OK) + return SR_ERR; + } + std_session_send_df_header(sdi); + + sr_dbg("dsbstartend %d",devc->dig_sample_bytes); + + if(devc->trigger_fired) std_session_send_df_trigger(sdi); + //Keep this at the end as we don't want to be RX_ACTIVE unless everything is ok + devc->rxstate=RX_ACTIVE; + + return SR_OK; +} +//TODO-it's not clear how this is a clean stop if we are in the middle of an acquisition +//as the generic_receive may still be running and it's not clear how the device gets reinited +//part of the answer may be that we get a special event sent to the generic_receiver??? +//though protocol.c references the enabled channels without checking for it becoming null.. +//also need to review to make sure any mallocs in acquisition start are freed + +//TODO - answer to above seems that we can hit this stop if the user hits stop in the pulseview app +//(perhaps a runaway sample), but we also need to always call this on the completion of any transfer +//because we free memory of the serail buffer and other things + +static int dev_acquisition_stop(struct sr_dev_inst *sdi) +{ + struct dev_context *devc; + struct sr_serial_dev_inst *serial; + sr_dbg("****at dev_acquisition_stop"); + int len; + devc = sdi->priv; + serial = sdi->conn; + + std_session_send_df_end(sdi); + //If we reached this while still active it is likely because the stop button was pushed in pulseview. + //That normally should be some kind of error condition, so we don't try to check the bytenct + if(devc->rxstate==RX_ACTIVE){ + sr_err("Reached dev_acquisition_stop in RX_ACTIVE"); + //In case we get calls to generic_receive force it to exit + devc->rxstate=RX_IDLE; + } + + //TODO- a reset '*' may be a bit too much, and the + likely is responded too quicker + if(devc->rxstate!=RX_IDLE){ + sr_err("Sending plus to stop device stream\n\r"); + send_serial_char(serial,'+'); + } + //drain data from device so that it doesn't confuse subsequent commands + do{ + len=serial_read_blocking(serial, devc->buffer, devc->serial_buffer_size,100); + if(len) sr_err("Dropping %d device bytes\n\r",len); + }while(len>0); + + + + if(devc->buffer){g_free(devc->buffer);devc->buffer=NULL;} + + for(int i=0;inum_a_channels;i++){ + if(devc->a_data_bufs[i]){ + g_free(devc->a_data_bufs[i]); + devc->a_data_bufs[i]=NULL; + } + } + + if(devc->d_data_buf){g_free(devc->d_data_buf);devc->d_data_buf=NULL;} + for(int i=0;inum_a_channels;i++){ + if(devc->a_pretrig_bufs[i]) g_free(devc->a_pretrig_bufs[i]); + devc->a_pretrig_bufs[i]=NULL; + } + + serial= sdi->conn; + serial_source_remove(sdi->session, serial); + + return SR_OK; +} + +static struct sr_dev_driver sr_generic_driver_info = { + .name = "sigrok-generic", + .longname = "Sigrok Generic", + .api_version = 1, + .init = std_init, + .cleanup = std_cleanup, + .scan = scan, + .dev_list = std_dev_list, + .dev_clear = std_dev_clear, + .config_get = config_get, + .config_set = config_set, + .config_list = config_list, + .dev_open = std_serial_dev_open, + .dev_close = std_serial_dev_close, + .dev_acquisition_start = dev_acquisition_start, + .dev_acquisition_stop = dev_acquisition_stop, + .context = NULL, +}; +SR_REGISTER_DEV_DRIVER(sr_generic_driver_info); diff --git a/sigrok-generic/protocol.c b/sigrok-generic/protocol.c new file mode 100644 index 0000000..a85aed2 --- /dev/null +++ b/sigrok-generic/protocol.c @@ -0,0 +1,797 @@ +/* + * This file is part of the libsigrok project. + * + * Copyright (C) 2016 mhooijboer + * Copyright (C) 2012 Martin Ling + * Copyright (C) 2013 Bert Vermeulen + * Copyright (C) 2013 Mathias Grimmberger + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "libsigrok-internal.h" +#include "protocol.h" + + +SR_PRIV int send_serial_str(struct sr_serial_dev_inst *serial, char *str){ + int len=strlen(str); + if((len>15)||(len<1)){ //limit length to catch errant strings + sr_err("ERROR:Serial string len %d invalid ",len); + return SR_ERR; + } + if(serial_write_blocking(serial,str,len,serial_timeout(serial, 1)) != len){ + sr_err("ERROR:Serial str write failed"); + return SR_ERR; + } + +//TODO serial drains take a while and may not add much +// if (serial_drain(serial) != SR_OK){ +// sr_err("ERROR:Serial str drain failed"); +// return SR_ERR; +// } + + return SR_OK; +} +SR_PRIV int send_serial_char(struct sr_serial_dev_inst *serial, char ch){ + char buf[1]; + buf[0]=ch; + if(serial_write_blocking(serial,buf,1,serial_timeout(serial, 1)) != 1){ + sr_err("ERROR:Serial char write failed"); + return SR_ERR; + } +//TODO serial drains take a while and may not add much +/* + if (serial_drain(serial) != SR_OK){ + sr_err("ERROR:Serial char drain failed"); + return SR_ERR; + } +*/ + return SR_OK; +} +//Issue a command that expects a string return, return length of string +int send_serial_w_resp(struct sr_serial_dev_inst *serial, char *str,char *resp,size_t cnt){ + int num_read,i; + send_serial_str(serial,str); + //Using the serial_read_blocking function when reading a response of unknown length requires + //a long worst case timeout to always be taken. So, instead loop waiting for a first byte, and + //then a final small delay for the rest. + for(i=0;i<1000;i++){ + num_read = serial_read_blocking(serial, resp, cnt, 1); + if(num_read>0) break; + } + sr_spew("rwprsp1 i %d nr %d",i,num_read); + //A typical 20char response at 115kbaud is ~1ms after the first char so 10ms is plenty + num_read+= serial_read_blocking(serial, &(resp[num_read]), cnt-num_read, 10); + sr_spew("rwrsp2 nr %d",num_read); + + if ((num_read < 1)||(num_read>30)) { + sr_err("ERROR:Serial_w_resp try 1 failed (%d).", num_read); + return -1; + }else{ + return num_read; + } +//TODO cleanup +/* + num_read = serial_read_blocking(serial, resp, cnt, 100); + if ((num_read < 1)||(num_read>30)) { + sr_err("ERROR:Serial_w_resp try 2 failed (%d).", num_read); + return -1; + } + return num_read; +*/ +} +//Issue a command that expects a single char ack +SR_PRIV int send_serial_w_ack(struct sr_serial_dev_inst *serial, char *str){ + char buf[2]; + int num_read; + while(num_read =serial_read_blocking(serial, buf, 2, 10)){ + sr_dbg("swack drops 2 previous bytes %d %d",buf[0],buf[1]); + } + send_serial_str(serial,str); + //1000ms timeout + num_read =serial_read_blocking(serial, buf, 1, 1000); + if ((num_read == 1)&&(buf[0]=='*')) { + return SR_OK; + }else{ + sr_err("ERROR:Serial_w_ack %s failed (%d).", str,num_read); + if(num_read){ + sr_err("ack resp char %c d %d\n\r",buf[0],buf[0]); + } + return SR_ERR; + } +} + +//Process incoming data stream assuming it is optimized packing of 4 channels (or less) +//Each byte is 4 channels of data and a 3 bit rle value, or a larger rle value, or a control signal. +//This also checks for aborts and ends. +//If an end is seen we stop processing but do not check the byte_cnt +//The output is a set of samples fed to process group to perform sw triggering and sending of data to the session +//as well as maintenance of the serial rx byte cnt. +//Since we can get huge rle values we chop them up for processing into smaller groups +void process_D4(struct sr_dev_inst *sdi,struct dev_context *d){ + int32_t j; + uint8_t cbyte; + uint8_t cval; + uint32_t rlecnt=0,rlerem,rlecopied; + uint32_t sampcnt=0; //number of samples received with no rles + //In 4 channel mode we can always consume all bytes because there are no cases where the processing of one + //byte requires another. + //COvering the sr_ prints in case they cause perf overhead issues even when not used +#ifdef D4_DBG + sr_dbg("pd4 %d %d %d",d->ser_rdptr,d->wrptr,d->bytes_avail); +#endif + //TODO the use of ser_rdptr is likely redundant as I thik it should always zero on entry + //and thus the wile loop might instead be for loop setting rdptr to 0 and going to bytes avail? +#ifdef D4_DBG + if(d->ser_rdptr){ + sr_err("serial read pointer %d not zero",d->ser_rdptr); + } +#endif + while((d->ser_rdptr)<(d->bytes_avail)){ + cbyte=d->buffer[(d->ser_rdptr)]; +#ifdef D4_DBG + sr_spew("cbyte 0x%X rdptr %d",cbyte,d->ser_rdptr); //rle only +#endif + if((cbyte>=48)&&(cbyte<=79)){ + rlecnt+=(cbyte-47)*8; + d->byte_cnt++; +#ifdef D4_DBG + sr_spew("Rle only byte 0x%X rlenct %d",cbyte,rlecnt); +#endif + }else if(cbyte>=0x80){ //sample with possible rle + rlecnt+=(cbyte&0x70)>>4; + + //We queue up large numbers of samples if they don't have rle, and then flush them + //once we get an rle + if(sampcnt&&rlecnt){ +#ifdef D4_DBG + sr_spew("Pushing %d samples ahead of rlecnt %d",sampcnt,rlecnt); +#endif + process_group(sdi,d,sampcnt); + sampcnt=0; + } + if(rlecnt){ +#ifdef D4_DBG + sr_spew("Copy %d rles from sample byte",rlecnt); +#endif + rlerem=rlecnt; + while(rlerem){ + rlecopied=rle_memset(d,rlerem); + process_group(sdi,d,rlecopied); + rlerem-=rlecopied; + } + rlecnt=0; + } + //In this mode we know that only ever set the LSByte and pad in up to dig_sample_bytes + cval=cbyte&0xF; + d->d_data_buf[d->cbuf_wrptr++]=cval; + for(j=1;jdig_sample_bytes;j++){ + d->d_data_buf[d->cbuf_wrptr++]=0; + } + sampcnt++; + d->byte_cnt++; +#ifdef D4_DBG + sr_spew("Dchan4 rdptr %d wrptr %d bytein 0x%X rle %d cval 0x%X\n", + (d->ser_rdptr)-1,d->cbuf_wrptr,cbyte,rlecnt,cval); +#endif + d->d_last[0]=cval; + } + //Any other character ends parsing - it could be a frame error or a start of the final byte cnt + else { + if(cbyte=='$'){ + sr_info("D4 Data stream stops with cbyte %d char %c rdidx %d cnt %llu",cbyte,cbyte,d->ser_rdptr,d->byte_cnt); + d->rxstate=RX_STOPPED; + }else{ + sr_err("D4 Data stream aborts with cbyte %d char %c rdidx %d cnt %llu",cbyte,cbyte,d->ser_rdptr,d->byte_cnt); + d->rxstate=RX_ABORT; + } + break; //break from while loop + } + (d->ser_rdptr)++; + }//while rdptr < wrptr + //TODO - i no longer think this is an error. If we get a sample and then a bunch of rles after + //it we can just do the sample and then the rles after it in the end. + //we really only need to force a sample if it has rles before it and after it + // if(sampcnt&&rlecnt){ + // sr_err("ERROR: Reached end of process_d4 with both samples %d and RLE %d",sampcnt,rlecnt); + // } + if(sampcnt){ +#ifdef D4_DBG + sr_dbg("Process_d4 residual samples %d",sampcnt); +#endif + process_group(sdi,d,sampcnt); + sampcnt=0; + } + if(rlecnt){ +#ifdef D4_DBG + sr_info("Process_d4 residual rle %d",rlecnt); +#endif + rlerem=rlecnt; + while(rlerem){ + rlecopied=rle_memset(d,rlerem); + process_group(sdi,d,rlecopied); + rlerem-=rlecopied; + } + rlecnt=0; + } + +}//Process_D4 + +//Process incoming data stream and forward to trigger processing with process_group +//The final value of ser_rdptr indicates how many bytes were processed. +//todo - review memory allocations to make sure target buffer can't overflow... + +void process_slice(struct sr_dev_inst *sdi,struct dev_context *devc){ + int32_t i; + uint32_t tmp32; + uint8_t cbyte; + uint32_t slices_avail=0; + uint32_t cword; + uint32_t slice_bytes; //number of bytes that have legal slice values + + //Only process legal data values for this mode which are >=0x80 + for(slice_bytes=1;(slice_bytesbytes_avail)&&(devc->buffer[slice_bytes-1]>=0x80);slice_bytes++); + + + if(slice_bytes!=devc->bytes_avail){ + cbyte=devc->buffer[slice_bytes-1]; +// slice_bytes-=2; //Don't process the ending character + slice_bytes--; + if(cbyte=='$'){ + sr_info("Data stream stops with cbyte %d char %c rdidx %d sbytes %d cnt %llu",cbyte,cbyte,devc->ser_rdptr,slice_bytes,devc->byte_cnt); + devc->rxstate=RX_STOPPED; + }else{ + sr_err("Data stream aborts with cbyte %d char %c rdidx %d sbytes %d cnt %llu",cbyte,cbyte,devc->ser_rdptr,slice_bytes,devc->byte_cnt); + devc->rxstate=RX_ABORT; + } + + } +//TODO - make sure this works (but if we don't see bytecnt errors it means it does :)) +//if the wrptr is non-zero due to a residual we don't want to double count it + devc->byte_cnt+=slice_bytes-(devc->wrptr); + sr_dbg("at process slice avail %d rdptr %d sb %d byte_cnt %d",devc->bytes_avail,devc->ser_rdptr,slice_bytes,devc->byte_cnt); + //must have a full slice + while((devc->ser_rdptr+devc->bytes_per_slice)<=slice_bytes){ + //The use of devc->cbuf_wrptr is different between analog and digital. + //For analog it targets a float sized offset for that channel's buffer and thus + //thus the analog channel size in bytes is sizeof(float)*max wrptr value + //For digital it targets a bit, so the 3 lsbs are bit offsets within a byte + //and thus a digital channel size is (max_wrptr value)/8 + sr_spew("Start slice"); + slices_avail++; + cword=0; + //build up a word 7 bits at a time, using only enabled channels + for(i=0;inum_d_channels;i+=7){ + if(((devc->d_chan_mask)>>i)&0x7F){ + cword|=((devc->buffer[devc->ser_rdptr])&0x7F)<ser_rdptr,devc->buffer[devc->ser_rdptr],cword); + (devc->ser_rdptr)++; + } + } + //and then distribute up to the word 8 bits at a time to all possible channels + for(i=0;inum_d_channels;i+=8){ + uint32_t idx=((devc->cbuf_wrptr)*devc->dig_sample_bytes)+(i>>3); + devc->d_data_buf[idx]=cword&0xFF; + sr_spew("Dchan out i %d wrptr %d idx %d char 0x%X cword 0x%X",i,devc->cbuf_wrptr,idx,devc->d_data_buf[idx],cword); + cword>>=8; + } + //Each analog value is a 7 bit value + for(i=0;inum_a_channels;i++){ + if((devc->a_chan_mask>>i)&1){ + //a_size is depracted + tmp32=devc->buffer[devc->ser_rdptr]-0x80; + devc->a_data_bufs[i][devc->cbuf_wrptr]=((float)tmp32 * devc->a_scale[i])+devc->a_offset[i]; + devc->a_last[i]=devc->a_data_bufs[i][devc->cbuf_wrptr]; + sr_spew("AChan %d value %f ",i,devc->a_data_bufs[i][devc->cbuf_wrptr]); + devc->ser_rdptr++; + }//if channel enabled + }//for num_a_channels + devc->cbuf_wrptr++; + }//While another slice available + if(slices_avail){ + process_group(sdi,devc,slices_avail); + } + +} + +int send_analog(struct sr_dev_inst *sdi,struct dev_context *devc,uint32_t num_samples, uint32_t offset){ + struct sr_datafeed_packet packet; + struct sr_datafeed_analog analog; + struct sr_analog_encoding encoding; + struct sr_analog_meaning meaning; + struct sr_analog_spec spec; + struct sr_channel *ch; + uint32_t i; + float *fptr; + + sr_analog_init(&analog, &encoding, &meaning, &spec, ANALOG_DIGITS); + for(i=0;inum_a_channels;i++){ + if((devc->a_chan_mask>>i)&1){ + ch=devc->analog_groups[i]->channels->data; + analog.meaning->channels = g_slist_append(NULL, ch); + analog.num_samples = num_samples; + analog.data = (devc->a_data_bufs[i]) + offset; + fptr=analog.data; + sr_spew("send analog num %d offset %d first %f 2 %f",num_samples,offset,*(devc->a_data_bufs[i]),*fptr); + analog.meaning->mq = SR_MQ_VOLTAGE; + analog.meaning->unit = SR_UNIT_VOLT; + analog.meaning->mqflags = 0; + packet.type = SR_DF_ANALOG; + packet.payload = &analog; + sr_session_send(sdi, &packet); + g_slist_free(analog.meaning->channels); + } //if enabled + }//for channels + return 0; + +} +/*Send the ring buffer of pre-trigger analog samples. + Send the entire buffer (as long as it filled once), but need send two payloads split at the + the writeptr */ +int send_analog_ring(struct sr_dev_inst *sdi,struct dev_context *devc,uint32_t num_samples){ + struct sr_datafeed_packet packet; + struct sr_datafeed_analog analog; + struct sr_analog_encoding encoding; + struct sr_analog_meaning meaning; + struct sr_analog_spec spec; + struct sr_channel *ch; + int i; + uint32_t num_pre,start_pre; + uint32_t num_post,start_post; + num_pre=(num_samples>=devc->pretrig_wr_ptr) ? devc->pretrig_wr_ptr : num_samples; + start_pre=devc->pretrig_wr_ptr-num_pre; + num_post=num_samples-num_pre; + start_post=devc->pretrig_entries-num_post; + sr_dbg("send_analog ring wrptr %u ns %d npre %u spre %u npost %u spost %u",devc->pretrig_wr_ptr,num_samples,num_pre,start_pre,num_post,start_post); + float *fptr; + sr_analog_init(&analog, &encoding, &meaning, &spec, ANALOG_DIGITS); + for(i=0;inum_a_channels;i++){ + if((devc->a_chan_mask>>i)&1){ + ch=devc->analog_groups[i]->channels->data; + analog.meaning->channels = g_slist_append(NULL, ch); + analog.meaning->mq = SR_MQ_VOLTAGE; + analog.meaning->unit = SR_UNIT_VOLT; + analog.meaning->mqflags = 0; + packet.type = SR_DF_ANALOG; + packet.payload = &analog; + //First send what is after the write pointer because it is oldest + if(num_post){ + analog.num_samples = num_post; + analog.data = (devc->a_pretrig_bufs[i]) + start_post; + sr_dbg("ring buf %d starts at %p",i,(void *) devc->a_pretrig_bufs[i]); + sr_dbg("analog data %d starts at %p",i,(void *) analog.data); + sr_dbg("Sending A%d ring buffer oldest ",i); + for(uint32_t j=0;ja_pretrig_bufs[i])+start_pre; + sr_dbg("Sending A%d ring buffer newest ",i); + for(uint32_t j=0;jchannels); + sr_dbg("Sending A%d ring buffer done ",i); + } //if enabled + }//for channels + return 0; + +} + +//Given a chunk of slices forward to trigger check or session as appropriate and update state +//these could be real slices or those generated by rles +int process_group(struct sr_dev_inst *sdi,struct dev_context *devc,uint32_t num_slices){ + int trigger_offset; + int pre_trigger_samples; + //These are samples sent to session and are less than num_slices if we reach limit_samples + size_t num_samples; + struct sr_datafeed_logic logic; + struct sr_datafeed_packet packet; + int i; + size_t cbuf_wrptr_cpy; + cbuf_wrptr_cpy=devc->cbuf_wrptr; + //regardless of whether we forward samples on or not, always reset the + //pointer into the device data buffers + devc->cbuf_wrptr=0; + if(devc->trigger_fired){ //send directly to session + if (devc->limit_samples && + num_slices > devc->limit_samples - devc->sent_samples){ + num_samples = devc->limit_samples - devc->sent_samples; + }else{ + num_samples=num_slices; + } + if(num_samples>0) { + sr_dbg("Process_group sending %d post trig samples dsb %d",num_samples,devc->dig_sample_bytes); + if(devc->num_d_channels){ + packet.type = SR_DF_LOGIC; + packet.payload = &logic; + //Size the number of bytes required to fit all of the channels + logic.unitsize = devc->dig_sample_bytes; + //The total length of the array sent + logic.length=num_samples*logic.unitsize; + logic.data = devc->d_data_buf; + sr_session_send(sdi, &packet); + } + send_analog(sdi,devc,num_samples,0); + }//num_sample>0 + devc->sent_samples+=num_samples; + return 0; + } //trigger_fired + else{ + size_t num_ring_samples; + size_t sptr; + size_t eptr; + size_t numtail; + size_t numwrap; + size_t srcptr; + sr_dbg("Process_group check %d pre trig samples",num_slices); + //The trigger_offset is -1 if no trigger is found, but if a trigger is found + //then trigger_offset is the offset into the data buffer sent to it. + //The pre_trigger_samples is the total number of samples before the trigger, but limited to + //the size of the ring buffer set by the capture_ratio. So the pre_trigger_samples can include both the new samples + //and the ring buffer, but trigger_offset is only in relation to the new samples + trigger_offset = soft_trigger_logic_check(devc->stl, + devc->d_data_buf, num_slices * devc->dig_sample_bytes, &pre_trigger_samples); + //A trigger offset >=0 indicate a trigger was seen. The stl will isue the trigger to the session + //and will forward all pre trigger logic samples, but we must send any post trigger logic + //and all pre and post trigger analog signals + sr_dbg("trggr_off %d",trigger_offset); + sr_dbg("pre_samp %d",pre_trigger_samples); + if (trigger_offset > -1) { + devc->trigger_fired = TRUE; + devc->sent_samples += pre_trigger_samples; + packet.type = SR_DF_LOGIC; + packet.payload = &logic; + num_samples = num_slices - trigger_offset; +//Since we are in continuous mode for SW triggers it is possible get more samples than limit_samples, so +//once the trigger fires make sure we don't get beyond limit samples. At this point sent_samples should +//be equal to pre_trigger_samples (just added above) because without being triggered we'd never increment +//sent_samples. +//This number is the number of post trigger logic samples to send to the session, the number of floats +//is larger. + if (devc->limit_samples && + num_samples > devc->limit_samples - devc->sent_samples) + num_samples = devc->limit_samples - devc->sent_samples; + //The soft trigger logic issues the trigger and sends packet for all logic data that was pretrigger + //so only send what is left + if(num_samples>0){ + sr_dbg("Sending post trigger logical remainder of %d",num_samples); + logic.length = num_samples * devc->dig_sample_bytes; + logic.unitsize = devc->dig_sample_bytes; + logic.data = devc->d_data_buf + (trigger_offset * devc->dig_sample_bytes); + devc->sent_samples += num_samples; + sr_session_send(sdi, &packet); + } + size_t new_start,new_end,new_samples,ring_samples; + //Figure out the analog data to send. + //We might need to send: + //-some or all of incoming data + //-all of incoming data and some of ring buffer + //-all of incoming data and all of ring buffer (and still might be short) + //We don't need to compare to limit_samples because pretrig_entries can never be more than limit_samples + //trigger offset indicatese where in the new samples the trigger was, but we need to go back pretrig_entries before it + new_start=(trigger_offset>(int)devc->pretrig_entries) ? trigger_offset-devc->pretrig_entries : 0; + //Note that we might not have gotten all the pre triggerstore data we were looking for. In such a case the sw trigger + //logic seems to fill up to the limit_samples and thus the ratio is off, but we get the full number of samples + //The number of entries in the ring buffer is pre_trigger_samples-trigger_offset so subtract that from limit samples + //as a threshold + new_end=MIN(num_slices-1,devc->limit_samples-(pre_trigger_samples-trigger_offset)-1); + //This includes pre and post trigger storage. + new_samples=new_end-new_start+1; + //pre_trigger_samples can never be greater than trigger_offset by more than the ring buffer depth (pretrig entries) + ring_samples=(pre_trigger_samples>trigger_offset) ? pre_trigger_samples-trigger_offset : 0; + sr_dbg("SW trigger float info newstart %zu new_end %zu new_samp %zu ring_samp %zu",new_start,new_end,new_samples,ring_samples); + if(ring_samples>0){ + send_analog_ring(sdi,devc,ring_samples); + } + if(new_samples){ + send_analog(sdi,devc,new_samples,new_start); + } + + }//if trigger_offset + else { //We didn't trigger but need to copy to ring buffer + if((devc->a_chan_mask)&&(devc->pretrig_entries)){ + //The incoming data buffer could be much larger than the ring buffer, so never copy more than + //the size of the ring buffer + num_ring_samples=num_slices > devc->pretrig_entries ? devc->pretrig_entries : num_slices; + sptr=devc->pretrig_wr_ptr; //starting pointer to copy to + //endptr can't go past the end + eptr=(sptr+num_ring_samples)>=devc->pretrig_entries ? devc->pretrig_entries-1 : sptr+num_ring_samples-1; + numtail=(eptr-sptr)+1; //number of samples to copy to the tail of ring buffer without wrapping + numwrap=(num_ring_samples>numtail) ? num_ring_samples-numtail:0; + //cbuf_wrptr points to where the next write should go, not theactual write data + srcptr=cbuf_wrptr_cpy-num_ring_samples; + sr_spew("RNG num %zu sptr %zu eptr %zu ",num_ring_samples,sptr,eptr); + sr_spew("RNG srcptr %zu nt %zu nw %zu",srcptr,numtail,numwrap); + for(i=0;inum_a_channels;i++){ + if((devc->a_chan_mask>>i)&1){ + //copy tail + for(uint32_t j=0;ja_pretrig_bufs[i][sptr+j]=devc->a_data_bufs[i][srcptr+j]; + sr_spew("RNGCpyT C%d src %zu dest %zu",i,srcptr+j,sptr+j); + }//for j + } //if chan_mask + }//for channels + //Copy wrap + srcptr+=numtail; + for(i=0;inum_a_channels;i++){ + if((devc->a_chan_mask>>i)&1){ + for(uint32_t j=0;ja_pretrig_bufs[i][j]=devc->a_data_bufs[i][srcptr+j]; + sr_spew("RNGCpyW C%d src %zu dest %zu",i,srcptr+j,j); + }//for j + }//if chan_mask + }//for channels + devc->pretrig_wr_ptr=(numwrap) ? numwrap : (eptr+1)%devc->pretrig_entries; + sr_dbg("RNG pwrptr new %u",devc->pretrig_wr_ptr); + }//if any analog channel enabled and pretrig_entries + }//else (trigger not detected) + }//trigger not set on function entry + return 0; +}//process_group + + +//Duplicate previous sample values +//Only duplicates up the the maximum of the devc data buffer storage and returns how many were sent +//This assumes all previous data in the device buffers have been forwarded +int rle_memset(struct dev_context *devc,uint32_t num_slices){ + uint32_t slices_to_send; + uint32_t i,j,k; + //this function assumes that all previous slices have been sent to the session + /* todo commenting for performance + if(devc->cbuf_wrptr){ + sr_err("ERROR:Sample buffer not previously cleared"); + return SR_ERR; + } + */ + //todo - can this be removed - with a 2x ratio of sample_buf to serial and with only d4 doing rle is this check needed? + slices_to_send=(num_slices>devc->sample_buf_size) ? devc->sample_buf_size : num_slices; + // sr_spew("rle_memset duplicating %d of %d dig_samp %d\n",slices_to_send,num_slices,devc->dig_sample_bytes); + //Force a known value on disabled channels, they should never be seen + /*TODO - rle is no longer supported for any ases where analog is sent so this can likely be removed + for(i=0;inum_a_channels;i++){ + for(j=0;ja_data_bufs[i][j]=((devc->a_chan_mask>>i)&1) ? devc->a_last[i] : -0.314; + } + } + */ + //Even if a channel is disabled, PV expects the same location for the enabled channels as if the channel were enabled. + //So don't make any adjustments + //no longer need to check for num_d_channels as rle is only on D4 + // if(devc->num_d_channels){ + for(k=0;kdig_sample_bytes;k++){ + sr_spew("rle_memset replicated group %d byte value 0x%X",k,devc->d_last[k]); + for(j=0;jdig_sample_bytes)+k; + devc->d_data_buf[didx]=devc->d_last[k]; + } + } + //} + + //Updating the sample write pointer probably isn't needed as well likely send the buffer to the session + //without adding more samples at which point this will be cleared. + devc->cbuf_wrptr=slices_to_send; + return slices_to_send; + } + + + +//This callback function is mapped from api.c with serial_source_add and is created after a capture +//has been setup and is responsible for querying the device trigger status, downloading data +//and forwarding packets +SR_PRIV int sr_generic_receive(int fd, int revents, void *cb_data) +{ + struct sr_dev_inst *sdi; + struct dev_context *devc; + struct sr_serial_dev_inst *serial; + uint32_t i; + int len; + uint32_t bytes_rem; + uint32_t residual_bytes; + (void)fd; + + if (!(sdi = cb_data)) + return TRUE; + + if (!(devc = sdi->priv)) + return TRUE; + sr_dbg("sr_gen_rcv"); + if(devc->rxstate!=RX_ACTIVE){ + //This condition is normal operation and expected to happen + //but printed as informatio - todo - might get rid of it.. + sr_dbg("Reached non active state in receive %d",devc->rxstate); + //don't return - we may be waiting for a final bytecnt + //return TRUE; + } + if(devc->rxstate==RX_IDLE){ + //This is the normal end condition where we do one more receive + sr_dbg("Reached idle state in receive %d",devc->rxstate); + //return because we are done ? TODO-fixme - is that right? + return FALSE; + } + + serial = sdi->conn; + //return true if it is some kind of event we don't handle + if (!(revents == G_IO_IN || revents == 0)) + return TRUE; + //Fill the buffer, the end may have partial slices + bytes_rem=devc->serial_buffer_size - devc->wrptr; + //Read one byte less so that we can null it and print as a string + len=serial_read_blocking(serial, &(devc->buffer[devc->wrptr]), bytes_rem-1,serial_timeout(serial, 0)); + sr_dbg("Entry wrptr %u bytes_rem %u len %d",devc->wrptr,bytes_rem,len); + + if(len>0){ + devc->buffer[devc->wrptr+len]=0; + //Add the "#" so that spaces are clearly seen + sr_info("rx string %s#",devc->buffer); + //This is not guaranteed to be a dataloss condition, but definitely indicates we are + //processing data right at the incoming rate. + if(len>=(int)bytes_rem-8){ + sr_err("ERROR: Serial buffer near or at max depth, data from device may have been lost"); + } + devc->bytes_avail=(devc->wrptr+len); + //len int, bytes_avail uint32, sent_samples uint64 wrptr uint32 + sr_dbg("rx len %d bytes_avail %ul sent_samples %ul wrptr %u",len,devc->bytes_avail,devc->sent_samples,devc->wrptr); + //sr_err("rx len %d ",len); + }else if (len==0){ + return TRUE; + }else { + sr_err("ERROR:Negative serial read code %d",len); + sdi->driver->dev_acquisition_stop(sdi); + return FALSE;//todo/fixme -do I want false or TRUE; + }//len>0 + //This can be used as a bit bucket to drop all samples to see how host processing time effects + //the devices ability to send data + // return TRUE; + //Start reading incoming serial data at start of buffer. + devc->ser_rdptr=0; + if(devc->rxstate==RX_ACTIVE){ + if((devc->a_chan_mask==0)&&((devc->d_chan_mask&0xFFFFFFF0)==0)){ + process_D4(sdi,devc); + }else{ + process_slice(sdi,devc); + } + } + //process_slice/process_D4 increment ser_rdptr as bytes of the serial buffer are used + //But they may not use all of it, and thus the residual unused bytes are shifted to the start of the buffer + //for the next call. + residual_bytes=devc->bytes_avail - devc->ser_rdptr; + sr_dbg("Residuals resid %d avail %d rdptr %d wrptr %d\n",residual_bytes,devc->bytes_avail,devc->ser_rdptr,devc->wrptr); + if(residual_bytes){ + for(i=0;ibuffer[i]=devc->buffer[i+devc->ser_rdptr]; + } + devc->ser_rdptr=0; + devc->wrptr=residual_bytes; + sr_dbg("Residual shift rdptr %u wrptr %u",devc->ser_rdptr,devc->wrptr); + }else{ + //If there are no residuals shifted then zero the wrptr since all data is used + devc->wrptr=0; + } + //ABORT ends immediately + if(devc->rxstate==RX_ABORT){ + sr_err("Ending receive on abort"); + sdi->driver->dev_acquisition_stop(sdi); + return FALSE;//todo -broken/fixme -does this work? + } + //if stopped look for final '+' + if(devc->rxstate==RX_STOPPED){ + sr_dbg("Stopped, checking byte_cnt"); + if(devc->buffer[0]!='$'){ + //If this happens it means that we got a set of data that was not processed as + //whole groups of slice bytes, so either we lost data or are not parsing it correctly. + //TODO - treat this as a framing error? + sr_err("ERROR: Stop marker should be byte zero"); + devc->rxstate=RX_ABORT; + sdi->driver->dev_acquisition_stop(sdi); + return FALSE; + } + for(i=1;iwrptr;i++){ + if(devc->buffer[i]=='+'){ + sr_dbg("Found plus"); + devc->buffer[i]=0; + uint64_t rxbytecnt; + rxbytecnt=atol(&(devc->buffer[1])); + sr_dbg("sent cnt %llu received cnt %llu",rxbytecnt,devc->byte_cnt); + if(rxbytecnt!=devc->byte_cnt){ + sr_err("ERROR: received %llu and counted %llu bytecnts don't match, data may be lost",rxbytecnt,devc->byte_cnt); + } + //We must always call acquisition_stop on all completed runs, and it sends the df_end + sdi->driver->dev_acquisition_stop(sdi); + std_session_send_df_end(sdi); + devc->rxstate=RX_IDLE; + return TRUE; + } + } + //It's possible we need one more serial transfer to get the byte_cnt, + sr_dbg("Didn't find plus"); + }//RX_STOPPED + //If at the sample limit, send a "+" in case we are in continuous mode and need + //to stop the device. Not that even in non continous mode there might be cases where get an extra + //sample or two... + + if((devc->sent_samples>=devc->limit_samples)&&(devc->rxstate==RX_ACTIVE)){ + sr_dbg("Ending: sent %u of limit %llu samples byte_cnt %llu", + devc->sent_samples,devc->limit_samples,devc->byte_cnt); + send_serial_char(serial,'+'); + + } + sr_spew("Receive function done: sent %u ",devc->sent_samples); + sr_spew("Receive function done: wrptr %u",devc->wrptr); + sr_spew("Receive function done: len %d",len); + sr_spew("Receive function done: sent %u limit %llu wrptr %u len %d",devc->sent_samples,devc->limit_samples,devc->wrptr,len); + return TRUE; +}//sr_generic_receive + +//Read device specific information from the device +SR_PRIV int sr_generic_get_dev_cfg(const struct sr_dev_inst *sdi) +{ + struct dev_context *devc; + struct sr_serial_dev_inst *serial; + char *cmd, response[20]; + gchar **tokens; + unsigned int i; + int ret,num_tokens; + + devc = sdi->priv; + sr_dbg("At get_dev_cfg"); + serial = sdi->conn; + for(i=0;inum_a_channels;i++){ + cmd = g_strdup_printf("a%d\n",i); + ret = send_serial_w_resp(serial,cmd,response,20); + if(ret<=0){ + sr_err("ERROR:No response from device for analog channel query"); + return SR_ERR; + } + tokens=NULL; + tokens = g_strsplit(response, ",", 0); + num_tokens = g_strv_length(tokens); + if (num_tokens == 2) { + devc->a_scale[i]=atof(tokens[0]); + devc->a_offset[i]=atof(tokens[1]); + sr_dbg("A%d scale %f offset %f\n",i,devc->a_scale[i],devc->a_offset[i]); + }else{ + sr_err("ERROR:Ascale read c%d got unparseable response %s",i,response); + //force a good fixed value + devc->a_scale[i]=1/256; + devc->a_offset[i]=0; + } + g_strfreev(tokens); + g_free(cmd); + } + + + return SR_OK; + +} + diff --git a/sigrok-generic/protocol.h b/sigrok-generic/protocol.h new file mode 100644 index 0000000..708b92c --- /dev/null +++ b/sigrok-generic/protocol.h @@ -0,0 +1,169 @@ +/* + * This file is part of the libsigrok project. + * + * Copyright (C) 2018 mhooijboer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef LIBSIGROK_HARDWARE_SR_GENERIC_PROTOCOL_H +#define LIBSIGROK_HARDWARE_SR_GENERIC_PROTOCOL_H + +#include +#include +#include +#include "libsigrok-internal.h" + +//This is used by sr_dbg/log etc +#define LOG_PREFIX "srgn" + //number of bytes between markers +#define MRK_STRIDE 128 + +//Limit to 32 since many channel enable/disable masks and other elements may be only 32 bits wide. +#define MAX_ANALOG_CHANNELS 32 +#define MAX_DIGITAL_CHANNELS 32 +//digits input to sr_analog_init +#define ANALOG_DIGITS 4 +//todo - remove old rle state and other defines +#define CHAR_0 48 +#define CHAR_15 63 + +//1..16, +//32.. +//RLEs that increment by 1 +#define CHAR_RLE_BY1_1 64 +#define CHAR_RLE_BY1_16 79 +//RLEs that increment by 16 +#define CHAR_RLE_BY16_16 80 +#define CHAR_RLE_BY16_256 95 +//RLEs that increment by 256 +#define CHAR_RLE_BY256_256 96 +#define CHAR_RLE_BY256_4096 111 +SR_PRIV int send_serial_str(struct sr_serial_dev_inst *serial, char *str); +SR_PRIV int send_serial_char(struct sr_serial_dev_inst *serial, char ch); +int send_serial_w_resp(struct sr_serial_dev_inst *serial, char *str,char *resp,size_t cnt); +SR_PRIV int send_serial_w_ack(struct sr_serial_dev_inst *serial, char *str); + +//Data Formats +typedef enum dfmt { + DIG1, //1 Digital channel -D0 only, packing 4 samples into one char + DIG2, //2 Digital channels -D0 and D1 only, packing 2x2channel samples into one char + SLICE //Slice mode - any number of digital and analog channels +} dfmt_t; +typedef enum rxstate { + RX_IDLE=0,//not receiving + RX_ACTIVE=1, //receiving data + RX_STOPPED=2, //received stop marker, waiting for byte cnt + RX_ABORT=3, //received aborted marker or other error +}rxstate_t; +struct dev_context { +/*Configuration Parameters */ + //It is up to the user to understand sample rates and serial download speed etc and + // do the right thing. i.e. don't select a sample rate it doesn't support, or expect + //continuous streaming bandwidth greater than serial link speed etc... + //The number of samples the user expects to see. + uint64_t limit_samples; + uint64_t sample_rate; + //Number of samples that have been received and processed + uint32_t num_samples; + //Initial Number of analog and digital channels. + //This is set by initial device config. Channels can be disabled/enabled, + //but can not be added/removed once driver is loaded. + uint16_t num_a_channels; + uint16_t num_d_channels; + //Masks of enabled channels + uint32_t a_chan_mask; + uint32_t d_chan_mask; + // Channel groups -each analog channel is it's own group + struct sr_channel_group **analog_groups; + struct sr_channel_group *digital_group; + //Data size in bytes for each analog channel in bytes - must be 1,2,3 or 4 + uint8_t a_size; + //Offset and scale for each analog channel to covert 1,2,3,4bytes to float + float a_offset[MAX_ANALOG_CHANNELS]; + float a_scale[MAX_ANALOG_CHANNELS]; + // % ratio of pre-trigger to post trigger samples + uint64_t capture_ratio; + // total number of bytes of data sent for one sample across all channels + uint16_t bytes_per_slice; + //The number of bytes needed to store all channels for one sample in the device data buff + uint32_t dig_sample_bytes; +/* Tracking/status once started */ + //number of bytes in the current serial input stream + uint32_t bytes_avail; + //Samples sent to the session */ + uint32_t sent_samples; + //count total received bytes to detect lost info*/ + uint64_t byte_cnt; + //For SW based triggering we put the device into continuous transmit and stop when + // we detect a sample and capture all the samples we need. trigger_fired is thus set when + // the sw trigger logic detects a trigger. + //For non triggered modes we send a start and a number of samples and the device + //transmits that much. trigger_fired is set immediately at the start. + gboolean trigger_fired; + //Has the device, via an "!" indicated it has stopped sending data, or has a marker + //error been detected + // gboolean device_stopped; + rxstate_t rxstate; +/* Serial Related */ + // Serial data buffer + unsigned char *buffer; + //Size of incoming serial buffer + uint32_t serial_buffer_size; + //Current byte in serial read stream that is being processed + uint32_t ser_rdptr; + //write pointer into the serial input buffer + uint32_t wrptr; + +/* Buffering Related */ + /* parsed serial read data is split into each channels dedicated buffer for analog*/ + float *a_data_bufs[MAX_ANALOG_CHANNELS]; + /*digital samples are stored packed together since cli/pulseview want it that way*/ + uint8_t *d_data_buf; + /*write point for the the per channel data buffers*/ + uint32_t cbuf_wrptr; + /*size of packet data buffers for each channel*/ + uint32_t sample_buf_size; +/* RLE related*/ + /*Previous sample values to duplicate for rle */ + float a_last[MAX_ANALOG_CHANNELS]; + uint8_t d_last[MAX_DIGITAL_CHANNELS/8]; + +/* SW Trigger Related */ + struct soft_trigger_logic *stl; + //Maximum number of entries to store pre-trigger + uint32_t pretrig_entries; + /* Analog pre-trigger storage for software based triggering + because sw based only has internal storage for logic*/ + float *a_pretrig_bufs[MAX_ANALOG_CHANNELS]; + uint32_t pretrig_wr_ptr; + +}; + +SR_PRIV int sr_generic_receive(int fd, int revents, void *cb_data); +SR_PRIV int sr_generic_get_dev_cfg(const struct sr_dev_inst *sdi); + +void process_D4(struct sr_dev_inst *sdi,struct dev_context *d); +void process_slice(struct sr_dev_inst *sdi,struct dev_context *devc); + +int send_analog(struct sr_dev_inst *sdi,struct dev_context *devc,uint32_t num_samples, uint32_t offset); +int send_analog_ring(struct sr_dev_inst *sdi,struct dev_context *devc,uint32_t num_samples); + +int process_group(struct sr_dev_inst *sdi,struct dev_context *devc,uint32_t num_slices); +int rle_memset(struct dev_context *devc,uint32_t num_slices); +SR_PRIV int check_marker(struct dev_context *d,int *len); + + + +#endif