| 1 | /* stream_test.c |
| 2 | * |
| 3 | * Test reading from FT2232H in synchronous FIFO mode. |
| 4 | * |
| 5 | * The FT2232H must supply data due to an appropriate circuit |
| 6 | * |
| 7 | * To check for skipped block with appended code, |
| 8 | * a structure as follows is assumed |
| 9 | * 1* uint32_t num (incremented in 0x4000 steps) |
| 10 | * 3* uint32_t dont_care |
| 11 | * |
| 12 | * After start, data will be read in streaming until the program is aborted |
| 13 | * Progess information wil be printed out |
| 14 | * If a filename is given on the command line, the data read will be |
| 15 | * written to that file |
| 16 | * |
| 17 | */ |
| 18 | #include <stdio.h> |
| 19 | #include <stdlib.h> |
| 20 | #include <stdint.h> |
| 21 | #include <string.h> |
| 22 | #include <unistd.h> |
| 23 | #include <getopt.h> |
| 24 | #include <signal.h> |
| 25 | #include <errno.h> |
| 26 | #include <ftdi.h> |
| 27 | void check_outfile(char *); |
| 28 | |
| 29 | static FILE *outputFile; |
| 30 | |
| 31 | static int check = 1; |
| 32 | static int exitRequested = 0; |
| 33 | /* |
| 34 | * sigintHandler -- |
| 35 | * |
| 36 | * SIGINT handler, so we can gracefully exit when the user hits ctrl-C. |
| 37 | */ |
| 38 | |
| 39 | static void |
| 40 | sigintHandler(int signum) |
| 41 | { |
| 42 | exitRequested = 1; |
| 43 | } |
| 44 | |
| 45 | static void |
| 46 | usage(const char *argv0) |
| 47 | { |
| 48 | fprintf(stderr, |
| 49 | "Usage: %s [options...] \n" |
| 50 | "Test streaming read from FT2232H\n" |
| 51 | "[-P string] only look for product with given string\n" |
| 52 | "[-n] don't check for special block structure\n" |
| 53 | "\n" |
| 54 | "If some filename is given, write data read to that file\n" |
| 55 | "Progess information is printed each second\n" |
| 56 | "Abort with ^C\n" |
| 57 | "\n" |
| 58 | "Options:\n" |
| 59 | "\n" |
| 60 | "Copyright (C) 2009 Micah Dowty <micah@navi.cx>\n" |
| 61 | "Adapted for use with libftdi (C) 2010 Uwe Bonnes <bon@elektron.ikp.physik.tu-darmstadt.de>\n", |
| 62 | argv0); |
| 63 | exit(1); |
| 64 | } |
| 65 | |
| 66 | static uint32_t start = 0; |
| 67 | static uint32_t offset = 0; |
| 68 | static uint64_t blocks = 0; |
| 69 | static uint32_t skips = 0; |
| 70 | static uint32_t n_err = 0; |
| 71 | static int |
| 72 | readCallback(uint8_t *buffer, int length, FTDIProgressInfo *progress, void *userdata) |
| 73 | { |
| 74 | if (length) |
| 75 | { |
| 76 | if (check) |
| 77 | { |
| 78 | int i,rem; |
| 79 | uint32_t num; |
| 80 | for (i= offset; i<length-16; i+=16) |
| 81 | { |
| 82 | num = *(uint32_t*) (buffer+i); |
| 83 | if (start && (num != start +0x4000)) |
| 84 | { |
| 85 | uint32_t delta = ((num-start)/0x4000)-1; |
| 86 | fprintf(stderr, "Skip %7d blocks from 0x%08x to 0x%08x at blocks %10llu\n", |
| 87 | delta, start -0x4000, num, (unsigned long long)blocks); |
| 88 | n_err++; |
| 89 | skips += delta; |
| 90 | } |
| 91 | blocks ++; |
| 92 | start = num; |
| 93 | } |
| 94 | rem = length -i; |
| 95 | if (rem >3) |
| 96 | { |
| 97 | num = *(uint32_t*) (buffer+i); |
| 98 | if (start && (num != start +0x4000)) |
| 99 | { |
| 100 | uint32_t delta = ((num-start)/0x4000)-1; |
| 101 | fprintf(stderr, "Skip %7d blocks from 0x%08x to 0x%08x at blocks %10llu\n", |
| 102 | delta, start -0x4000, num, (unsigned long long) blocks); |
| 103 | n_err++; |
| 104 | skips += delta; |
| 105 | } |
| 106 | start = num; |
| 107 | } |
| 108 | else if (rem) |
| 109 | start += 0x4000; |
| 110 | if (rem != 0) |
| 111 | { |
| 112 | blocks ++; |
| 113 | offset = 16-rem; |
| 114 | } |
| 115 | } |
| 116 | if (outputFile) |
| 117 | { |
| 118 | if (fwrite(buffer, length, 1, outputFile) != 1) |
| 119 | { |
| 120 | perror("Write error"); |
| 121 | return 1; |
| 122 | } |
| 123 | } |
| 124 | } |
| 125 | if (progress) |
| 126 | { |
| 127 | fprintf(stderr, "%10.02fs total time %9.3f MiB captured %7.1f kB/s curr rate %7.1f kB/s totalrate %d dropouts\n", |
| 128 | progress->totalTime, |
| 129 | progress->current.totalBytes / (1024.0 * 1024.0), |
| 130 | progress->currentRate / 1024.0, |
| 131 | progress->totalRate / 1024.0, |
| 132 | n_err); |
| 133 | } |
| 134 | return exitRequested ? 1 : 0; |
| 135 | } |
| 136 | |
| 137 | int main(int argc, char **argv) |
| 138 | { |
| 139 | struct ftdi_context *ftdi; |
| 140 | int err, c; |
| 141 | FILE *of = NULL; |
| 142 | char const *outfile = 0; |
| 143 | outputFile =0; |
| 144 | exitRequested = 0; |
| 145 | char *descstring = NULL; |
| 146 | int option_index; |
| 147 | static struct option long_options[] = {{NULL},}; |
| 148 | |
| 149 | while ((c = getopt_long(argc, argv, "P:n", long_options, &option_index)) !=- 1) |
| 150 | switch (c) |
| 151 | { |
| 152 | case -1: |
| 153 | break; |
| 154 | case 'P': |
| 155 | descstring = optarg; |
| 156 | break; |
| 157 | case 'n': |
| 158 | check = 0; |
| 159 | break; |
| 160 | default: |
| 161 | usage(argv[0]); |
| 162 | } |
| 163 | |
| 164 | if (optind == argc - 1) |
| 165 | { |
| 166 | // Exactly one extra argument- a dump file |
| 167 | outfile = argv[optind]; |
| 168 | } |
| 169 | else if (optind < argc) |
| 170 | { |
| 171 | // Too many extra args |
| 172 | usage(argv[0]); |
| 173 | } |
| 174 | |
| 175 | if ((ftdi = ftdi_new()) == 0) |
| 176 | { |
| 177 | fprintf(stderr, "ftdi_new failed\n"); |
| 178 | return EXIT_FAILURE; |
| 179 | } |
| 180 | |
| 181 | if (ftdi_set_interface(ftdi, INTERFACE_A) < 0) |
| 182 | { |
| 183 | fprintf(stderr, "ftdi_set_interface failed\n"); |
| 184 | ftdi_free(ftdi); |
| 185 | return EXIT_FAILURE; |
| 186 | } |
| 187 | |
| 188 | if (ftdi_usb_open_desc(ftdi, 0x0403, 0x6010, descstring, NULL) < 0) |
| 189 | { |
| 190 | fprintf(stderr,"Can't open ftdi device: %s\n",ftdi_get_error_string(ftdi)); |
| 191 | ftdi_free(ftdi); |
| 192 | return EXIT_FAILURE; |
| 193 | } |
| 194 | |
| 195 | /* A timeout value of 1 results in may skipped blocks */ |
| 196 | if(ftdi_set_latency_timer(ftdi, 2)) |
| 197 | { |
| 198 | fprintf(stderr,"Can't set latency, Error %s\n",ftdi_get_error_string(ftdi)); |
| 199 | ftdi_usb_close(ftdi); |
| 200 | ftdi_free(ftdi); |
| 201 | return EXIT_FAILURE; |
| 202 | } |
| 203 | |
| 204 | /* if(ftdi_usb_purge_rx_buffer(ftdi) < 0) |
| 205 | { |
| 206 | fprintf(stderr,"Can't rx purge\n",ftdi_get_error_string(ftdi)); |
| 207 | return EXIT_FAILURE; |
| 208 | }*/ |
| 209 | if (outfile) |
| 210 | if ((of = fopen(outfile,"w+")) == 0) |
| 211 | fprintf(stderr,"Can't open logfile %s, Error %s\n", outfile, strerror(errno)); |
| 212 | if (of) |
| 213 | if (setvbuf(of, NULL, _IOFBF , 1<<16) == 0) |
| 214 | outputFile = of; |
| 215 | signal(SIGINT, sigintHandler); |
| 216 | |
| 217 | err = ftdi_readstream(ftdi, readCallback, NULL, 8, 256); |
| 218 | if (err < 0 && !exitRequested) |
| 219 | exit(1); |
| 220 | |
| 221 | if (outputFile) { |
| 222 | fclose(outputFile); |
| 223 | outputFile = NULL; |
| 224 | } |
| 225 | fprintf(stderr, "Capture ended.\n"); |
| 226 | |
| 227 | if (ftdi_set_bitmode(ftdi, 0xff, BITMODE_RESET) < 0) |
| 228 | { |
| 229 | fprintf(stderr,"Can't set synchronous fifo mode, Error %s\n",ftdi_get_error_string(ftdi)); |
| 230 | ftdi_usb_close(ftdi); |
| 231 | ftdi_free(ftdi); |
| 232 | return EXIT_FAILURE; |
| 233 | } |
| 234 | ftdi_usb_close(ftdi); |
| 235 | ftdi_free(ftdi); |
| 236 | signal(SIGINT, SIG_DFL); |
| 237 | if (check && outfile) |
| 238 | { |
| 239 | if ((outputFile = fopen(outfile,"r")) == 0) |
| 240 | { |
| 241 | fprintf(stderr,"Can't open logfile %s, Error %s\n", outfile, strerror(errno)); |
| 242 | ftdi_usb_close(ftdi); |
| 243 | ftdi_free(ftdi); |
| 244 | return EXIT_FAILURE; |
| 245 | } |
| 246 | check_outfile(descstring); |
| 247 | fclose(outputFile); |
| 248 | } |
| 249 | else if (check) |
| 250 | fprintf(stderr,"%d errors of %llu blocks (%Le), %d (%Le) blocks skipped\n", |
| 251 | n_err, (unsigned long long) blocks, (long double)n_err/(long double) blocks, |
| 252 | skips, (long double)skips/(long double) blocks); |
| 253 | exit (0); |
| 254 | } |
| 255 | |
| 256 | void check_outfile(char *descstring) |
| 257 | { |
| 258 | if(strcmp(descstring,"FT2232HTEST") == 0) |
| 259 | { |
| 260 | char buf0[1024]; |
| 261 | char buf1[1024]; |
| 262 | char bufr[1024]; |
| 263 | char *pa, *pb, *pc; |
| 264 | unsigned int num_lines = 0, line_num = 1; |
| 265 | int err_count = 0; |
| 266 | unsigned int num_start, num_end; |
| 267 | |
| 268 | pa = buf0; |
| 269 | pb = buf1; |
| 270 | pc = buf0; |
| 271 | if(fgets(pa, 1023, outputFile) == NULL) |
| 272 | { |
| 273 | fprintf(stderr,"Empty output file\n"); |
| 274 | return; |
| 275 | } |
| 276 | while(fgets(pb, 1023, outputFile) != NULL) |
| 277 | { |
| 278 | num_lines++; |
| 279 | unsigned int num_save = num_start; |
| 280 | if( sscanf(pa,"%6u%94s%6u",&num_start, bufr,&num_end) !=3) |
| 281 | { |
| 282 | fprintf(stdout,"Format doesn't match at line %8d \"%s", |
| 283 | num_lines, pa); |
| 284 | err_count++; |
| 285 | line_num = num_save +2; |
| 286 | } |
| 287 | else |
| 288 | { |
| 289 | if ((num_start+1)%100000 != num_end) |
| 290 | { |
| 291 | if (err_count < 20) |
| 292 | fprintf(stdout,"Malformed line %d \"%s\"\n", |
| 293 | num_lines, pa); |
| 294 | err_count++; |
| 295 | } |
| 296 | else if(num_start != line_num) |
| 297 | { |
| 298 | if (err_count < 20) |
| 299 | fprintf(stdout,"Skipping from %d to %d\n", |
| 300 | line_num, num_start); |
| 301 | err_count++; |
| 302 | |
| 303 | } |
| 304 | line_num = num_end; |
| 305 | } |
| 306 | pa = pb; |
| 307 | pb = pc; |
| 308 | pc = pa; |
| 309 | } |
| 310 | if(err_count) |
| 311 | fprintf(stdout,"\n%d errors of %d data sets %f\n", err_count, num_lines, (double) err_count/(double)num_lines); |
| 312 | else |
| 313 | fprintf(stdout,"No errors for %d lines\n",num_lines); |
| 314 | } |
| 315 | else if(strcmp(descstring,"LLBBC10") == 0) |
| 316 | { |
| 317 | uint32_t block0[4]; |
| 318 | uint32_t block1[4]; |
| 319 | uint32_t *pa = block0; |
| 320 | uint32_t *pb = block1; |
| 321 | uint32_t *pc = block0; |
| 322 | uint32_t start= 0; |
| 323 | uint32_t nread = 0; |
| 324 | int n_shown = 0; |
| 325 | int n_errors = 0; |
| 326 | if (fread(pa, sizeof(uint32_t), 4,outputFile) < 4) |
| 327 | { |
| 328 | fprintf(stderr,"Empty result file\n"); |
| 329 | return; |
| 330 | } |
| 331 | while(fread(pb, sizeof(uint32_t), 4,outputFile) != 0) |
| 332 | { |
| 333 | blocks++; |
| 334 | nread = pa[0]; |
| 335 | if(start>0 && (nread != start)) |
| 336 | { |
| 337 | if(n_shown < 30) |
| 338 | { |
| 339 | fprintf(stderr, "Skip %7d blocks from 0x%08x to 0x%08x at blocks %10llu \n", |
| 340 | (nread-start)/0x4000, start -0x4000, nread, (unsigned long long) blocks); |
| 341 | n_shown ++; |
| 342 | } |
| 343 | n_errors++; |
| 344 | } |
| 345 | else if (n_shown >0) |
| 346 | n_shown--; |
| 347 | start = nread + 0x4000; |
| 348 | pa = pb; |
| 349 | pb = pc; |
| 350 | pc = pa; |
| 351 | } |
| 352 | if(n_errors) |
| 353 | fprintf(stderr, "%d blocks wrong from %llu blocks read\n", |
| 354 | n_errors, (unsigned long long) blocks); |
| 355 | else |
| 356 | fprintf(stderr, "%llu blocks all fine\n", (unsigned long long) blocks); |
| 357 | } |
| 358 | } |