xpp: fix manpage of astribank_hexload
[dahdi/tools.git] / fxotune.c
1 /* 
2  * fxotune.c -- A utility for tuning the various settings on the fxo
3  *              modules for the TDM400 cards.
4  *
5  * by Matthew Fredrickson <creslin@digium.com>
6  * 
7  * (C) 2004-2008 Digium, Inc.
8  */
9
10 /*
11  * See http://www.asterisk.org for more information about
12  * the Asterisk project. Please do not directly contact
13  * any of the maintainers of this project for assistance;
14  * the project provides a web site, mailing lists and IRC
15  * channels for your use.
16  *
17  * This program is free software, distributed under the terms of
18  * the GNU General Public License Version 2 as published by the
19  * Free Software Foundation. See the LICENSE file included with
20  * this program for more details.
21  */
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <errno.h>
26 #include <string.h>
27 #include <sys/ioctl.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <unistd.h>
31 #include <fcntl.h>
32 #include <math.h>
33 #include <sys/time.h>
34
35 #include <dahdi/user.h>
36 #include <dahdi/wctdm_user.h>
37
38 #include "dahdi_tools_version.h"
39 #include "fxotune.h"
40
41 #define TEST_DURATION 2000
42 #define BUFFER_LENGTH (2 * TEST_DURATION)
43 #define SKIP_SAMPLES 800
44 #define SINE_SAMPLES 8000
45
46 static float sintable[SINE_SAMPLES];
47
48 static const float amplitude = 16384.0;
49
50 static char *dahdipath = "/dev/dahdi";
51 static char *configfile = "/etc/fxotune.conf";
52
53 static int audio_dump_fd = -1;
54
55 static int printbest = 0;
56
57 #define MAX_RESULTS     (5)
58 struct result_catalog {
59         int     idx;
60         float   echo;
61         float   freqres;
62         struct wctdm_echo_coefs settings;
63 };
64
65 struct {
66         struct result_catalog results[MAX_RESULTS];
67         int numactive;
68 }       topresults;
69
70 static char *usage =
71 "Usage: fxotune [-v[vv] (-s | -i <options> | -d <options>)\n"
72 "\n"
73 "       -s : set previously calibrated echo settings\n"
74 "       -i : calibrate echo settings\n"
75 "               options : [<dialstring>] [-t <calibtype>]\n"
76 "               [-b <startdev>][-e <stopdev>]\n"
77 "               [-n <dialstring>][-l <delaytosilence>][-m <silencegoodfor>]\n"
78 "       -d : dump input and output waveforms to ./fxotune_dump.vals\n"
79 "               options : [-b <device>][-w <waveform>]\n"
80 "                  [-n <dialstring>][-l <delaytosilence>][-m <silencegoodfor>]\n"
81 "       -v : more output (-vv, -vvv also)\n"
82 "       -p : print the 5 best candidates for acim and coefficients settings\n"
83 "       -x : Perform sin/cos functions using table lookup\n"
84 "       -o <path> : Write the received raw 16-bit signed linear audio that is\n"
85 "                   used in processing to the file specified by <path>\n"
86 "       -c <config_file>\n"
87 "\n"
88 "               <calibtype>      - type of calibration\n"
89 "                                  (default 2, old method 1)\n"
90 "               <startdev>\n"
91 "               <stopdev>        - defines a range of devices to test\n"
92 "                                  (default: 1-252)\n"
93 "               <dialstring>     - string to dial to clear the line\n"
94 "                                  (default 5)\n"
95 "               <delaytosilence> - seconds to wait for line to clear (default 0)\n"
96 "               <silencegoodfor> - seconds before line will no longer be clear\n"
97 "                                  (default 18)\n"
98 "               <device>         - the device to perform waveform dump on\n"
99 "                                  (default 1)\n"
100 "               <waveform>       - -1 for multitone waveform, or frequency of\n"
101 "                                  single tone (default -1)\n"
102 "               <config_file>    - Alternative file to set from / calibrate to.\n"
103 "                                  (Default: /etc/fxotune.conf)\n"
104 ;
105
106
107 #define OUT_OF_BOUNDS(x) ((x) < 0 || (x) > 255)
108
109 struct silence_info{
110         char *dialstr;
111         /** fd of device we are working with */
112         int device; 
113         /** seconds we should wait after dialing the dialstring before we know for sure we'll have silence */
114         int initial_delay;
115         /** seconds after which a reset should occur */
116         int reset_after;
117         /** time of last reset */
118         struct timeval last_reset; 
119 };
120
121 static short outbuf[TEST_DURATION];
122 static int debug = 0;
123
124 static FILE *debugoutfile = NULL;
125
126 static int use_table = 0;
127
128 static int fxotune_read(int fd, void *buffer, int len)
129 {
130         int res;
131
132         res = read(fd, buffer, len);
133
134         if ((res > 0) && (audio_dump_fd != -1)) {
135                 res = write(audio_dump_fd, buffer, len);
136         }
137
138         return res;
139 }
140
141 /**
142  * Makes sure that the line is clear.
143  * Right now, we do this by relying on the user to specify how long after dialing the
144  * dialstring we can rely on the line being silent (before the telco complains about
145  * the user not hitting the next digit).
146  * 
147  * A more robust way to do this would be to actually measure the sound levels on the line,
148  * but that's a lot more complicated, and this should work.
149  * 
150  * @return 0 if succesful (no errors), 1 if unsuccesful
151  */
152 static int ensure_silence(struct silence_info *info)
153 {
154         struct timeval tv;
155         long int elapsedms;
156         int x = DAHDI_ONHOOK;
157         struct dahdi_dialoperation dop;
158
159         gettimeofday(&tv, NULL);
160         
161         if (info->last_reset.tv_sec == 0) {
162                 /* this is the first request, we will force it to run */
163                 elapsedms = -1;
164         } else {
165                 /* this is not the first request, we will compute elapsed time */
166                 elapsedms = ((tv.tv_sec - info->last_reset.tv_sec) * 1000L + (tv.tv_usec - info->last_reset.tv_usec) / 1000L);
167         }
168         if (debug > 4) {
169                 fprintf(stdout, "Reset line request received - elapsed ms = %li / reset after = %ld\n", elapsedms, info->reset_after * 1000L);
170         }
171
172         if (elapsedms > 0 && elapsedms < info->reset_after * 1000L)
173                 return 0;
174         
175         if (debug > 1){
176                 fprintf(stdout, "Resetting line\n");
177         }
178         
179         /* do a line reset */
180         /* prepare line for silence */
181         /* Do line hookstate reset */
182
183         if (ioctl(info->device, DAHDI_HOOK, &x)) {
184                 fprintf(stderr, "Unable to hang up fd %d\n", info->device);
185                 return -1;
186         }
187
188         sleep(2);
189         x = DAHDI_OFFHOOK;
190         if (ioctl(info->device, DAHDI_HOOK, &x)) {
191                 fprintf(stderr, "Cannot bring fd %d off hook\n", info->device);
192                 return -1;
193         }
194         sleep(2); /* Added to ensure that dial can actually takes place */
195
196         memset(&dop, 0, sizeof(dop));
197         dop.op = DAHDI_DIAL_OP_REPLACE;
198         dop.dialstr[0] = 'T';
199         dahdi_copy_string(dop.dialstr + 1, info->dialstr, sizeof(dop.dialstr));
200
201
202         if (ioctl(info->device, DAHDI_DIAL, &dop)) {
203                 fprintf(stderr, "Unable to dial!\n");
204                 return -1;
205         }
206         sleep(1); 
207         sleep(info->initial_delay);  
208         
209         
210         gettimeofday(&info->last_reset, NULL);
211         
212         
213         return 0;
214 }
215
216 /**
217  * Generates a tone of specified frequency.
218  * 
219  * @param hz the frequency of the tone to be generated
220  * @param idx the current sample
221  *              to begenerated.  For a normal waveform you need to increment
222  *              this every time you execute the function.
223  *
224  * @return 16bit slinear sample for the specified index
225  */
226 static short inline gentone(int hz, int idx)
227 {
228         return amplitude * sin((idx * 2.0 * M_PI * hz)/8000);
229 }
230
231 /* Using DTMF tones for now since they provide good mid band testing 
232  * while not being harmonics of each other */
233 static int freqs[] = {697, 770, 941, 1209, 1336, 1633};
234 static int freqcount = 6;
235
236 /**
237  * Generates a waveform of several frequencies.
238  * 
239  * @param idx the current sample
240  *              to begenerated.  For a normal waveform you need to increment
241  *              this every time you execute the function.
242  *
243  * @return 16bit slinear sample for the specified index
244  */
245 static short inline genwaveform(int idx)
246 {
247         int i = 0;
248         float response = (float)0;
249         for (i = 0; i < freqcount; i++){
250                 response += sin((idx * 2.0 * M_PI * freqs[i])/8000);
251         }
252         
253
254         return amplitude * response / freqcount;
255 }
256
257
258 /**
259  *  Calculates the RMS of the waveform buffer of samples in 16bit slinear format.
260  *  prebuf the buffer of either shorts or floats
261  *  bufsize the number of elements in the prebuf buffer (not the number of bytes!)
262  *  short_format 1 if prebuf points to an array of shorts, 0 if it points to an array of floats
263  *  
264  *  Formula for RMS (http://en.wikipedia.org/wiki/Root_mean_square): 
265  *  
266  *  Xrms = sqrt(1/N Sum(x1^2, x2^2, ..., xn^2))
267  *  
268  *  Note:  this isn't really a power calculation - but it gives a good measure of the level of the response
269  *  
270  *  @param prebuf the buffer containing the values to compute
271  *  @param bufsize the size of the buffer
272  *  @param short_format 1 if prebuf contains short values, 0 if it contains float values
273  */
274 static float power_of(void *prebuf, int bufsize, int short_format)
275 {
276         float sum_of_squares = 0;
277         int numsamples = 0;
278         float finalanswer = 0;
279         short *sbuf = (short*)prebuf;
280         float *fbuf = (float*)prebuf;
281         int i = 0;
282
283         if (short_format) {
284                 /* idiot proof checks */
285                 if (bufsize <= 0)
286                         return -1;
287
288                 numsamples = bufsize; /* Got rid of divide by 2 - the bufsize parameter should give the number of samples (that's what it does for the float computation, and it should do it here as well) */
289
290                 for (i = 0; i < numsamples; i++) {
291                         sum_of_squares += ((float)sbuf[i] * (float)sbuf[i]);
292                 }
293         } else {
294                 /* Version for float inputs */
295                 for (i = 0; i < bufsize; i++) {
296                         sum_of_squares += (fbuf[i] * fbuf[i]);
297                 }
298         }
299
300         finalanswer = sum_of_squares/(float)bufsize; /* need to divide by the number of elements in the sample for RMS calc */
301
302         if (finalanswer < 0) {
303                 fprintf(stderr, "Error: Final answer negative number %f\n", finalanswer);
304                 return -3;
305         }
306
307         return sqrtf(finalanswer);
308 }
309
310 /* 
311  * In an effort to eliminate as much as possible the effect of outside noise, we use principles
312  * from the Fourier Transform to attempt to calculate the return loss of our signal for each setting.
313  *
314  * To begin, we send our output signal out on the line.  We then receive back the reflected
315  * response.  In the Fourier Transform, each evenly distributed frequency within the window
316  * is correlated (multiplied against, then the resulting samples are added together) with
317  * the real (cos) and imaginary (sin) portions of that frequency base to detect that frequency.
318  * 
319  * Instead of doing a complete Fourier Transform, we solve the transform for only our signal
320  * by multiplying the received signal by the real and imaginary portions of our reference
321  * signal.  This then gives us the real and imaginary values that we can use to calculate
322  * the return loss of the sinusoids that we sent out on the line.  This is done by finding
323  * the magnitude (think polar form) of the vector resulting from the real and imaginary
324  * portions calculated above.
325  *
326  * This essentially filters out any other noise which maybe present on the line which is outside
327  * the frequencies used in our test multi-tone.
328  */
329
330 void init_sinetable(void)
331 {
332         int i;
333         if (debug) {
334                 fprintf(stdout, "Using sine tables with %d samples\n", SINE_SAMPLES);
335         }
336         for (i = 0; i < SINE_SAMPLES; i++) {
337                 sintable[i] = sin(((float)i * 2.0 * M_PI )/(float)(SINE_SAMPLES));
338         }
339 }
340
341 /* Sine and cosine table lookup to use periodicity of the calculations being done */
342 float sin_tbl(int arg, int num_per_period)
343 {
344         arg = arg % num_per_period;
345
346         arg = (arg * SINE_SAMPLES)/num_per_period;
347
348         return sintable[arg];
349 }
350
351 float cos_tbl(int arg, int num_per_period)
352 {
353         arg = arg  % num_per_period;
354
355         arg = (arg * SINE_SAMPLES)/num_per_period;
356
357         arg = (arg + SINE_SAMPLES/4) % SINE_SAMPLES;  /* Pi/2 adjustment */
358
359         return sintable[arg];
360 }
361
362
363 static float db_loss(float measured, float reference)
364 {
365         return 20 * (logf(measured/reference)/logf(10));
366 }
367
368 static void one_point_dft(const short *inbuf, int len, int frequency, float *real, float *imaginary)
369 {
370         float myreal = 0, myimag = 0;
371         int i;
372
373         for (i = 0; i < len; i++) {
374                 if (use_table) {
375                         myreal += (float) inbuf[i] * cos_tbl(i*frequency, 8000);
376                         myimag += (float) inbuf[i] * sin_tbl(i*frequency, 8000);
377                 } else {
378                         myreal += (float) inbuf[i] * cos((i * 2.0 * M_PI * frequency)/8000);
379                         myimag += (float) inbuf[i] * sin((i * 2.0 * M_PI * frequency)/8000);
380                 }
381         }
382
383         myimag *= -1;
384
385         *real = myreal / (float) len;
386         *imaginary = myimag / (float) len;
387 }
388
389
390 static float calc_magnitude(short *inbuf, int insamps)
391 {
392         float real, imaginary, magnitude;
393         float totalmagnitude = 0;
394         int i;
395
396         for (i = 0; i < freqcount; i++) {
397                 one_point_dft(inbuf, insamps, freqs[i], &real, &imaginary);
398                 magnitude = sqrtf((real * real) + (imaginary * imaginary));
399                 totalmagnitude += magnitude;
400         }
401
402         return totalmagnitude;
403 }
404
405
406 /**
407  *  dumps input and output buffer contents for the echo test - used to see exactly what's going on
408  */
409 static int maptone(int whichdahdi, int freq, char *dialstr, int delayuntilsilence)
410 {
411         int i = 0;
412         int res = 0, x = 0;
413         struct dahdi_bufferinfo bi;
414         short inbuf[TEST_DURATION]; /* changed from BUFFER_LENGTH - this buffer is for short values, so it should be allocated using the length of the test */
415         FILE *outfile = NULL;
416         int leadin = 50;
417         int trailout = 100;
418         struct silence_info sinfo;
419         float power_result;
420         float power_waveform;
421         float echo;
422
423         outfile = fopen("fxotune_dump.vals", "w");
424         if (!outfile) {
425                 fprintf(stdout, "Cannot create fxotune_dump.vals\n");
426                 return -1;
427         }
428
429         x = 1;
430         if (ioctl(whichdahdi, DAHDI_SETLINEAR, &x)) {
431                 fprintf(stderr, "Unable to set channel to signed linear mode.\n");
432                 return -1;
433         }
434
435         memset(&bi, 0, sizeof(bi));
436         if (ioctl(whichdahdi, DAHDI_GET_BUFINFO, &bi)) {
437                 fprintf(stderr, "Unable to get buffer information!\n");
438                 return -1;
439         }
440         bi.numbufs = 2;
441         bi.bufsize = TEST_DURATION; /* KD - changed from BUFFER_LENGTH; */
442         bi.txbufpolicy = DAHDI_POLICY_IMMEDIATE;
443         bi.rxbufpolicy = DAHDI_POLICY_IMMEDIATE;
444         if (ioctl(whichdahdi, DAHDI_SET_BUFINFO, &bi)) {
445                 fprintf(stderr, "Unable to set buffer information!\n");
446                 return -1;
447         }
448
449         /* Fill the output buffers */
450         for (i = 0; i < leadin; i++)
451                 outbuf[i] = 0;
452         for (; i < TEST_DURATION - trailout; i++){
453                 outbuf[i] = freq > 0 ? gentone(freq, i) : genwaveform(i); /* if frequency is negative, use a multi-part waveform instead of a single frequency */
454         }
455         for (; i < TEST_DURATION; i++)
456                 outbuf[i] = 0;
457
458         /* Make sure the line is clear */
459         memset(&sinfo, 0, sizeof(sinfo));
460         sinfo.device = whichdahdi;
461         sinfo.dialstr = dialstr;
462         sinfo.initial_delay = delayuntilsilence;
463         sinfo.reset_after = 4; /* doesn't matter - we are only running one test */
464         
465         if (ensure_silence(&sinfo)){
466                 fprintf(stderr, "Unable to get a clear outside line\n");
467                 return -1;
468         }
469
470         /* Flush buffers */
471         x = DAHDI_FLUSH_READ | DAHDI_FLUSH_WRITE | DAHDI_FLUSH_EVENT;
472         if (ioctl(whichdahdi, DAHDI_FLUSH, &x)) {
473                 fprintf(stderr, "Unable to flush I/O: %s\n", strerror(errno));
474                 return -1;
475         }
476
477         /* send data out on line */
478         res = write(whichdahdi, outbuf, BUFFER_LENGTH); /* we are sending a TEST_DURATION length array of shorts (which are 2 bytes each) */
479         if (res != BUFFER_LENGTH) { 
480                 fprintf(stderr, "Could not write all data to line\n");
481                 return -1;
482         }
483
484 retry:
485                 /* read return response */
486         res = fxotune_read(whichdahdi, inbuf, BUFFER_LENGTH);
487         if (res != BUFFER_LENGTH) {
488                 int dummy;
489
490                 ioctl(whichdahdi, DAHDI_GETEVENT, &dummy);
491                 goto retry;
492         }
493
494         /* write content of output buffer to debug file */
495         power_result = power_of(inbuf, TEST_DURATION, 1);
496         power_waveform = power_of(outbuf, TEST_DURATION, 1);
497         echo = power_result/power_waveform;
498         
499         fprintf(outfile, "Buffers, freq=%d, outpower=%0.0f, echo=%0.4f\n", freq, power_result, echo);
500         fprintf(outfile, "Sample, Input (received from the line), Output (sent to the line)\n");
501         for (i = 0; i < TEST_DURATION; i++){
502                 fprintf(outfile, "%d, %d, %d\n", 
503                         i,
504                         inbuf[i],
505                         outbuf[i]
506                 );
507         }
508
509         fclose(outfile);
510         
511         fprintf(stdout, "echo ratio = %0.4f (%0.1f / %0.1f)\n", echo, power_result, power_waveform);
512         
513         return 0;
514 }
515
516
517 /**
518  *  Initialize the data store for storing off best calculated results
519  */
520 static void init_topresults(void)
521 {
522         topresults.numactive = 0;
523 }
524
525
526 /**
527  *  If this is a best result candidate, store in the top results data store
528  *              This is dependent on being the lowest echo value
529  *
530  *  @param tbleoffset - The offset into the echo_trys table used
531  *  @param setting - Pointer to the settings used to achieve the fgiven value
532  *  @param echo - The calculated echo return value (in dB)
533  *  @param echo - The calculated magnitude of the response
534  */
535 static void set_topresults(int tbloffset, struct wctdm_echo_coefs *setting, float echo, float freqres)
536 {
537         int place;
538         int idx;
539
540         for ( place = 0; place < MAX_RESULTS && place < topresults.numactive; place++) {
541                 if (echo < topresults.results[place].echo) {
542                         break;
543                 }
544         }
545
546         if (place < MAX_RESULTS) {
547                 /*  move results to the bottom */
548                 for (idx = topresults.numactive-2; idx >= place; idx--) {
549                         topresults.results[idx+1] = topresults.results[idx];
550                 }
551                 topresults.results[place].idx = tbloffset;
552                 topresults.results[place].settings = *setting;
553                 topresults.results[place].echo = echo;
554                 topresults.results[place].freqres = freqres;
555                 if (MAX_RESULTS > topresults.numactive) {
556                         topresults.numactive++;
557                 }
558         }
559 }
560
561
562 /**
563  *  Prints the top results stored to stdout
564  *
565  *  @param header - Text that goes in the header of the response
566  */
567 static void print_topresults(char * header)
568 {
569         int item;
570
571         fprintf(stdout, "Top %d results for %s\n", topresults.numactive, header);
572         for (item = 0; item < topresults.numactive; item++) {
573                 fprintf(stdout, "Res #%d: index=%d, %3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d: magnitude = %0.0f, echo = %0.4f dB\n",
574                                 item+1, topresults.results[item].idx, topresults.results[item].settings.acim,
575                                 topresults.results[item].settings.coef1, topresults.results[item].settings.coef2,
576                                 topresults.results[item].settings.coef3, topresults.results[item].settings.coef4,
577                                 topresults.results[item].settings.coef5, topresults.results[item].settings.coef6,
578                                 topresults.results[item].settings.coef7, topresults.results[item].settings.coef8,
579                                 topresults.results[item].freqres, topresults.results[item].echo);
580                 
581         }
582 }
583
584
585 /**
586  * Perform calibration type 2 on the specified device
587  * 
588  * Determine optimum echo coefficients for the specified device
589  * 
590  * New tuning strategy.  If we have a number that we can dial that will result in silence from the
591  * switch, the tune will be *much* faster (we don't have to keep hanging up and dialing a digit, etc...)
592  * The downside is that the user needs to actually find a 'no tone' phone number at their CO's switch - but for
593  * really fixing echo problems, this is what it takes.
594  *
595  * Also, for the purposes of optimizing settings, if we pick a single frequency and test with that,
596  * we can try a whole bunch of impedence/echo coefficients.  This should give better results than trying
597  * a bunch of frequencies, and we can always do a a frequency sweep to pick between the best 3 or 4
598  * impedence/coefficients configurations.
599  *   
600  * Note:  It may be possible to take this even further and do some pertubation analysis on the echo coefficients
601  *               themselves (maybe use the 72 entry sweep to find some settings that are close to working well, then
602  *               deviate the coefficients a bit to see if we can improve things).  A better way to do this would be to
603  *               use the optimization strategy from silabs.  For reference, here is an application note that describes
604  *               the echo coefficients (and acim values):
605  *               
606  *               http://www.silabs.com/Support%20Documents/TechnicalDocs/an84.pdf
607  *
608  *               See Table 13 in this document for a breakdown of acim values by region.
609  *
610  *               http://www.silabs.com/Support%20Documents/TechnicalDocs/si3050-18-19.pdf
611  *               
612  */
613 static int acim_tune2(int whichdahdi, int freq, char *dialstr, int delayuntilsilence, int silencegoodfor, struct wctdm_echo_coefs *coefs_out)
614 {
615         int i = 0;
616         int res = 0, x = 0;
617         int lowesttry = -1;
618         float lowesttryresult = 999999999999.0;
619         float lowestecho = 999999999999.0;
620         struct dahdi_bufferinfo bi;
621         short inbuf[TEST_DURATION * 2];
622         struct silence_info sinfo;
623         int echo_trys_size = 72;
624         int trys = 0;
625         float waveform_power;
626         float freq_result;
627         float echo;
628
629         init_topresults();
630
631         if (debug && !debugoutfile) {
632                 if (!(debugoutfile = fopen("fxotune.vals", "w"))) {
633                         fprintf(stdout, "Cannot create fxotune.vals\n");
634                         return -1;
635                 }
636         }
637
638         /* Set echo settings */
639         if (ioctl(whichdahdi, WCTDM_SET_ECHOTUNE, &echo_trys[0])) {
640                 fprintf(stderr, "Unable to set impedance on fd %d\n", whichdahdi);
641                 return -1;
642         }
643
644         x = 1;
645         if (ioctl(whichdahdi, DAHDI_SETLINEAR, &x)) {
646                 fprintf(stderr, "Unable to set channel to signed linear mode.\n");
647                 return -1;
648         }
649
650         memset(&bi, 0, sizeof(bi));
651         if (ioctl(whichdahdi, DAHDI_GET_BUFINFO, &bi)) {
652                 fprintf(stderr, "Unable to get buffer information!\n");
653                 return -1;
654         }
655         bi.numbufs = 2;
656         bi.bufsize = BUFFER_LENGTH;
657         bi.txbufpolicy = DAHDI_POLICY_IMMEDIATE;
658         bi.rxbufpolicy = DAHDI_POLICY_IMMEDIATE;
659         if (ioctl(whichdahdi, DAHDI_SET_BUFINFO, &bi)) {
660                 fprintf(stderr, "Unable to set buffer information!\n");
661                 return -1;
662         }
663         x = DAHDI_OFFHOOK;
664         if (ioctl(whichdahdi, DAHDI_HOOK, &x)) {
665                 fprintf(stderr, "Cannot bring fd %d off hook", whichdahdi);
666                 return -1;
667         }
668
669
670         /* Set up silence settings */
671         memset(&sinfo, 0, sizeof(sinfo));
672         sinfo.device = whichdahdi;
673         sinfo.dialstr = dialstr;
674         sinfo.initial_delay = delayuntilsilence;
675         sinfo.reset_after = silencegoodfor;
676
677         /* Fill the output buffers */
678         for (i = 0; i < TEST_DURATION; i++)
679                 outbuf[i] = freq > 0 ? gentone(freq, i) : genwaveform(i); /* if freq is negative, use a multi-frequency waveform */
680         
681         /* compute power of input (so we can later compute echo levels relative to input) */
682         waveform_power = calc_magnitude(outbuf, TEST_DURATION);
683
684         /* sweep through the various coefficient settings and see how our responses look */
685
686         for (trys = 0; trys < echo_trys_size; trys++){
687                 
688                 /* ensure silence on the line */
689                 if (ensure_silence(&sinfo)){
690                         fprintf(stderr, "Unable to get a clear outside line\n");
691                         return -1;
692                 }
693                 
694                 if (ioctl(whichdahdi, WCTDM_SET_ECHOTUNE, &echo_trys[trys])) {
695                         fprintf(stderr, "Unable to set echo coefficients on fd %d\n", whichdahdi);
696                         return -1;
697                 }
698
699                 /* Flush buffers */
700                 x = DAHDI_FLUSH_READ | DAHDI_FLUSH_WRITE | DAHDI_FLUSH_EVENT;
701                 if (ioctl(whichdahdi, DAHDI_FLUSH, &x)) {
702                         fprintf(stderr, "Unable to flush I/O: %s\n", strerror(errno));
703                         return -1;
704                 }
705
706                 /* send data out on line */
707                 res = write(whichdahdi, outbuf, BUFFER_LENGTH);
708                 if (res != BUFFER_LENGTH) {
709                         fprintf(stderr, "Could not write all data to line\n");
710                         return -1;
711                 }
712
713 retry:
714                 /* read return response */
715                 res = fxotune_read(whichdahdi, inbuf, BUFFER_LENGTH * 2);
716                 if (res != BUFFER_LENGTH * 2) {
717                         int dummy;
718
719                         ioctl(whichdahdi, DAHDI_GETEVENT, &dummy);
720                         goto retry;
721                 }
722
723                 freq_result = calc_magnitude(inbuf, TEST_DURATION * 2);
724                 echo = db_loss(freq_result, waveform_power);
725                 
726 #if 0
727                 if (debug > 0)
728                         fprintf(stdout, "%3d,%d,%d,%d,%d,%d,%d,%d,%d: magnitude = %0.0f, echo = %0.4f dB\n", 
729                                         echo_trys[trys].acim, echo_trys[trys].coef1, echo_trys[trys].coef2,
730                                         echo_trys[trys].coef3, echo_trys[trys].coef4, echo_trys[trys].coef5,
731                                         echo_trys[trys].coef6, echo_trys[trys].coef7, echo_trys[trys].coef8,
732                                         freq_result, echo);
733 #endif
734
735                 if (freq_result < lowesttryresult){
736                         lowesttry = trys;
737                         lowesttryresult = freq_result;
738                         lowestecho = echo;
739                 }
740                 if (debug) {
741                         char result[256];
742                         snprintf(result, sizeof(result), "%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%f,%f", 
743                                                 echo_trys[trys].acim, 
744                                                 echo_trys[trys].coef1, 
745                                                 echo_trys[trys].coef2, 
746                                                 echo_trys[trys].coef3, 
747                                                 echo_trys[trys].coef4, 
748                                                 echo_trys[trys].coef5, 
749                                                 echo_trys[trys].coef6, 
750                                                 echo_trys[trys].coef7, 
751                                                 echo_trys[trys].coef8, 
752                                                 freq_result,
753                                                 echo
754                                         );
755                         
756                         fprintf(debugoutfile, "%s\n", result);
757                         fprintf(stdout, "%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d: magnitude = %0.0f, echo = %0.4f dB\n",
758                                         echo_trys[trys].acim, echo_trys[trys].coef1, echo_trys[trys].coef2,
759                                         echo_trys[trys].coef3, echo_trys[trys].coef4, echo_trys[trys].coef5,
760                                         echo_trys[trys].coef6, echo_trys[trys].coef7, echo_trys[trys].coef8,
761                                         freq_result, echo);
762                 }
763
764                 if (printbest) {
765                         set_topresults(trys, &echo_trys[trys], echo, freq_result);
766                 }
767         }
768
769         if (debug > 0)
770                 fprintf(stdout, "Config with lowest response = %d, magnitude = %0.0f, echo = %0.4f dB\n", lowesttry, lowesttryresult, lowestecho);
771
772         memcpy(coefs_out, &echo_trys[lowesttry], sizeof(struct wctdm_echo_coefs));
773         if (printbest) {
774                 print_topresults("Acim2_tune Test");
775         }
776
777         return 0;
778 }
779
780 /**
781  *  Perform calibration type 1 on the specified device.  Only tunes the line impedance.  Look for best response range 
782  */
783 static int acim_tune(int whichdahdi, char *dialstr, int delayuntilsilence, int silencegoodfor, struct wctdm_echo_coefs *coefs_out)
784 {
785         int i = 0, freq = 0, acim = 0;
786         int res = 0, x = 0;
787         struct dahdi_bufferinfo bi;
788         struct wctdm_echo_coefs coefs;
789         short inbuf[TEST_DURATION]; /* changed from BUFFER_LENGTH - this buffer is for short values, so it should be allocated using the length of the test */
790         int lowest = 0;
791         FILE *outfile = NULL;
792         float acim_results[16];
793         struct silence_info sinfo;
794
795         if (debug) {
796                 outfile = fopen("fxotune.vals", "w");
797                 if (!outfile) {
798                         fprintf(stdout, "Cannot create fxotune.vals\n");
799                         return -1;
800                 }
801         }
802
803         /* Set up silence settings */
804         memset(&sinfo, 0, sizeof(sinfo));
805         sinfo.device = whichdahdi;
806         sinfo.dialstr = dialstr;
807         sinfo.initial_delay = delayuntilsilence;
808         sinfo.reset_after = silencegoodfor;
809         
810         /* Set echo settings */
811         memset(&coefs, 0, sizeof(coefs));
812         if (ioctl(whichdahdi, WCTDM_SET_ECHOTUNE, &coefs)) {
813                 fprintf(stdout, "Skipping non-TDM / non-FXO\n");
814                 return -1;
815         }
816
817         x = 1;
818         if (ioctl(whichdahdi, DAHDI_SETLINEAR, &x)) {
819                 fprintf(stderr, "Unable to set channel to signed linear mode.\n");
820                 return -1;
821         }
822
823         memset(&bi, 0, sizeof(bi));
824         if (ioctl(whichdahdi, DAHDI_GET_BUFINFO, &bi)) {
825                 fprintf(stderr, "Unable to get buffer information!\n");
826                 return -1;
827         }
828         bi.numbufs = 2;
829         bi.bufsize = BUFFER_LENGTH;
830         bi.txbufpolicy = DAHDI_POLICY_IMMEDIATE;
831         bi.rxbufpolicy = DAHDI_POLICY_IMMEDIATE;
832         if (ioctl(whichdahdi, DAHDI_SET_BUFINFO, &bi)) {
833                 fprintf(stderr, "Unable to set buffer information!\n");
834                 return -1;
835         }
836
837         for (acim = 0; acim < 16; acim++) {
838                 float freq_results[15];
839
840                 coefs.acim = acim;
841                 if (ioctl(whichdahdi, WCTDM_SET_ECHOTUNE, &coefs)) {
842                         fprintf(stderr, "Unable to set impedance on fd %d\n", whichdahdi);
843                         return -1;
844                 }
845
846                 for (freq = 200; freq <=3000; freq+=200) {
847                         /* Fill the output buffers */
848                         for (i = 0; i < TEST_DURATION; i++)
849                                 outbuf[i] = gentone(freq, i);
850
851                         /* Make sure line is ready for next test iteration */
852                         if (ensure_silence(&sinfo)){
853                                 fprintf(stderr, "Unable to get a clear line\n");
854                                 return -1;
855                         }
856                         
857
858                         /* Flush buffers */
859                         x = DAHDI_FLUSH_READ | DAHDI_FLUSH_WRITE | DAHDI_FLUSH_EVENT;
860                         if (ioctl(whichdahdi, DAHDI_FLUSH, &x)) {
861                                 fprintf(stderr, "Unable to flush I/O: %s\n", strerror(errno));
862                                 return -1;
863                         }
864         
865                         /* send data out on line */
866                         res = write(whichdahdi, outbuf, BUFFER_LENGTH);
867                         if (res != BUFFER_LENGTH) {
868                                 fprintf(stderr, "Could not write all data to line\n");
869                                 return -1;
870                         }
871
872                         /* read return response */
873 retry:
874                         /* read return response */
875                         res = fxotune_read(whichdahdi, inbuf, BUFFER_LENGTH);
876                         if (res != BUFFER_LENGTH) {
877                                 int dummy;
878         
879                                 ioctl(whichdahdi, DAHDI_GETEVENT, &dummy);
880                                 goto retry;
881                         }
882
883                         /* calculate power of response */
884                         
885                         freq_results[(freq/200)-1] = power_of(inbuf+SKIP_SAMPLES, TEST_DURATION-SKIP_SAMPLES, 1); /* changed from inbuf+SKIP_BYTES, BUFFER_LENGTH-SKIP_BYTES, 1 */
886                         if (debug) fprintf(outfile, "%d,%d,%f\n", acim, freq, freq_results[(freq/200)-1]);
887                 }
888                 acim_results[acim] = power_of(freq_results, 15, 0);
889         }
890
891         if (debug) {
892                 for (i = 0; i < 16; i++)
893                         fprintf(outfile, "acim_results[%d] = %f\n", i, acim_results[i]);
894         }
895         /* Find out what the "best" impedance is for the line */
896         lowest = 0;
897         for (i = 0; i < 16; i++) {
898                 if (acim_results[i] < acim_results[lowest]) {
899                         lowest = i;
900                 }
901         }
902
903         coefs_out->acim = lowest;
904         coefs_out->coef1 = 0;
905         coefs_out->coef2 = 0;
906         coefs_out->coef3 = 0;
907         coefs_out->coef4 = 0;
908         coefs_out->coef5 = 0;
909         coefs_out->coef6 = 0;
910         coefs_out->coef7 = 0;
911         coefs_out->coef8 = 0;
912         
913         return 0;
914 }
915
916 /**
917  * Reads echo register settings from the configuration file and pushes them into
918  * the appropriate devices
919  * 
920  * @param configfilename the path of the file that the calibration results should be written to
921  * 
922  * @return 0 if successful, !0 otherwise
923  */     
924 static int do_set(char *configfilename)
925 {
926         FILE *fp = NULL;
927         int res = 0;
928         int fd = 0;
929                 
930         fp = fopen(configfile, "r");
931         
932     if (!fp) {
933             fprintf(stdout, "Cannot open %s!\n",configfile);
934             return -1;
935     }
936
937         
938         while (res != EOF) {
939                 struct wctdm_echo_coefs mycoefs;
940                 char completedahdipath[56] = "";
941                 int mydahdi,myacim,mycoef1,mycoef2,mycoef3,mycoef4,mycoef5,mycoef6,mycoef7,mycoef8;
942
943
944                 res = fscanf(fp, "%d=%d,%d,%d,%d,%d,%d,%d,%d,%d",&mydahdi,&myacim,&mycoef1,
945                                 &mycoef2,&mycoef3,&mycoef4,&mycoef5,&mycoef6,&mycoef7,
946                                 &mycoef8);
947
948                 if (res == EOF) {
949                         break;
950                 }
951
952                 /* Check to be sure conversion is done correctly */
953                 if (OUT_OF_BOUNDS(myacim) || OUT_OF_BOUNDS(mycoef1)||
954                         OUT_OF_BOUNDS(mycoef2)|| OUT_OF_BOUNDS(mycoef3)||
955                         OUT_OF_BOUNDS(mycoef4)|| OUT_OF_BOUNDS(mycoef5)||
956                         OUT_OF_BOUNDS(mycoef6)|| OUT_OF_BOUNDS(mycoef7)|| OUT_OF_BOUNDS(mycoef8)) {
957
958                         fprintf(stdout, "Bounds check error on inputs from %s:%d\n", configfile, mydahdi);
959                         return -1;
960                 }
961
962                 mycoefs.acim = myacim;
963                 mycoefs.coef1 = mycoef1;
964                 mycoefs.coef2 = mycoef2;
965                 mycoefs.coef3 = mycoef3;
966                 mycoefs.coef4 = mycoef4;
967                 mycoefs.coef5 = mycoef5;
968                 mycoefs.coef6 = mycoef6;
969                 mycoefs.coef7 = mycoef7;
970                 mycoefs.coef8 = mycoef8;
971         
972                 snprintf(completedahdipath, sizeof(completedahdipath), "%s/%d", dahdipath, mydahdi);
973                 fd = open(completedahdipath, O_RDWR);
974
975                 if (fd < 0) {
976                         fprintf(stdout, "open error on %s: %s\n", completedahdipath, strerror(errno));
977                         return -1;
978                 }
979
980                 if (ioctl(fd, WCTDM_SET_ECHOTUNE, &mycoefs)) {
981                         fprintf(stdout, "%s: %s\n", completedahdipath, strerror(errno));
982                         return -1;
983                 }
984
985                 close(fd);
986         }
987
988         fclose(fp);
989
990         if (debug)
991                 fprintf(stdout, "fxotune: successfully set echo coeffecients on FXO modules\n");
992         return 0;       
993 }
994
995 /**
996  * Output waveform information from a single test
997  * 
998  * Clears the line, then sends a single waveform (multi-tone, or single tone), and listens
999  * for the response on the line.  Output is written to fxotune_dump.vals
1000  * 
1001  * @param startdev the device to test
1002  * @param dialstr the string that should be dialed to clear the dialtone from the line
1003  * @param delayuntilsilence the number of seconds to wait after dialing dialstr before starting the test
1004  * @param silencegoodfor the number of seconds that the test can run before having to reset the line again
1005  *                      (this is basically the amount of time it takes before the 'if you'd like to make a call...' message
1006  *                      kicks in after you dial dialstr.  This test is so short that the value is pretty much ignored.
1007  * @param waveformtype the type of waveform to use - -1 = multi-tone waveform, otherwise the specified value
1008  *                      is used as the frequency of a single tone.  A value of 0 will output silence.
1009  */
1010 static int do_dump(int startdev, char* dialstr, int delayuntilsilence, int silencegoodfor, int waveformtype)
1011 {
1012         int res = 0;
1013         int fd;
1014         char dahdidev[80] = "";
1015         
1016         int dahdimodule = startdev;
1017         snprintf(dahdidev, sizeof(dahdidev), "%s/%d", dahdipath, dahdimodule);
1018
1019         fd = open(dahdidev, O_RDWR);
1020         if (fd < 0) {
1021                 fprintf(stdout, "%s absent: %s\n", dahdidev, strerror(errno));
1022                 return -1;
1023         }
1024
1025         fprintf(stdout, "Dumping module %s\n", dahdidev);
1026         res = maptone(fd, waveformtype, dialstr, delayuntilsilence); 
1027
1028         close(fd);
1029
1030         if (res) {
1031                 fprintf(stdout, "Failure!\n");
1032                 return res;
1033         } else {
1034                 fprintf(stdout, "Done!\n");
1035                 return 0;
1036         }
1037
1038 }       
1039
1040 /**
1041  * Performs calibration on all specified devices
1042  * 
1043  * @param startdev the first device to check
1044  * @param enddev the last device to check
1045  * @param calibtype the type of calibration to perform.  1=old style (loops through individual frequencies
1046  *                      doesn't optimize echo coefficients.  2=new style (uses multi-tone and optimizes echo coefficients
1047  *                      and acim setting)
1048  * @param configfilename the path of the file that the calibration results should be written to
1049  * @param dialstr the string that should be dialed to clear the dialtone from the line
1050  * @param delayuntilsilence the number of seconds to wait after dialing dialstr before starting the test
1051  * @param silencegoodfor the number of seconds that the test can run before having to reset the line again
1052  *                      (this is basically the amount of time it takes before the 'if you'd like to make a call...' message
1053  *                      kicks in after you dial dialstr
1054  * 
1055  * @return 0 if successful, -1 for serious error such as device not available , > 0 indicates the number of channels
1056  */     
1057 static int do_calibrate(int startdev, int enddev, int calibtype, char* configfilename, char* dialstr, int delayuntilsilence, int silencegoodfor)
1058 {
1059         int problems = 0;
1060         int res = 0;
1061         int configfd, fd;
1062         int devno = 0;
1063         char dahdidev[80] = "";
1064         struct wctdm_echo_coefs coefs;
1065         
1066         configfd = open(configfile, O_CREAT|O_TRUNC|O_WRONLY, 0666);
1067
1068         if (configfd < 0) {
1069                 fprintf(stderr, "Cannot generate config file %s: open: %s\n", configfile, strerror(errno));
1070                 return -1;
1071         }
1072
1073         for (devno = startdev; devno <= enddev; devno++) {
1074                 snprintf(dahdidev, sizeof(dahdidev), "%s/%d", dahdipath, devno);
1075
1076                 fd = open(dahdidev, O_RDWR);
1077                 if (fd < 0) {
1078                         fprintf(stdout, "%s absent: %s\n", dahdidev, strerror(errno));
1079                         continue;
1080                 }
1081
1082                 fprintf(stdout, "Tuning module %s\n", dahdidev);
1083                 
1084                 if (1 == calibtype)
1085                         res = acim_tune(fd, dialstr, delayuntilsilence, silencegoodfor, &coefs);
1086                 else
1087                         res = acim_tune2(fd, -1, dialstr, delayuntilsilence, silencegoodfor, &coefs);
1088
1089                 close(fd);
1090                 
1091                 if (res) {
1092                         fprintf(stdout, "Failure!\n");
1093                         problems++;
1094                 } else {
1095                         fprintf(stdout, "Done!\n");
1096                 }
1097
1098                 if (res == 0) {
1099                         
1100                 /* Do output to file */
1101                         int len = 0;
1102                         static char output[255] = "";
1103
1104                         snprintf(output, sizeof(output), "%d=%d,%d,%d,%d,%d,%d,%d,%d,%d\n", 
1105                                 devno,
1106                                 coefs.acim, 
1107                                 coefs.coef1, 
1108                                 coefs.coef2, 
1109                                 coefs.coef3, 
1110                                 coefs.coef4, 
1111                                 coefs.coef5, 
1112                                 coefs.coef6, 
1113                                 coefs.coef7, 
1114                                 coefs.coef8
1115                         );
1116
1117                         if (debug)
1118                                 fprintf(stdout, "Found best echo coefficients: %s\n", output);
1119
1120                         len = strlen(output);
1121                         res = write(configfd, output, strlen(output));
1122                         if (res != len) {
1123                                 fprintf(stdout, "Unable to write line \"%s\" to file.\n", output);
1124                                 return -1;
1125                         }
1126                 }
1127         }
1128
1129         close(configfd);
1130         
1131         if (problems)
1132                 fprintf(stdout, "Unable to tune %d devices, even though those devices are present\n", problems);
1133         
1134         return problems;
1135 }       
1136         
1137 int main(int argc , char **argv)
1138 {
1139         int startdev = 1; /* -b */
1140         int stopdev = 252; /* -e */
1141         int calibtype = 2; /* -t */
1142         int waveformtype = -1; /* -w multi-tone by default.  If > 0, single tone of specified frequency */
1143         int delaytosilence = 0; /* -l */
1144         int silencegoodfor = 18; /* -m */
1145         char* dialstr = "5"; /* -n */
1146         int res = 0;
1147         int doset = 0; /* -s */
1148         int docalibrate = 0; /* -i <dialstr> */
1149         int dodump = 0; /* -d */
1150         int i = 0;
1151         int moreargs;
1152         
1153         for (i = 1; i < argc; i++){
1154                 if (!(argv[i][0] == '-' || argv[i][0] == '/') || (strlen(argv[i]) <= 1)){
1155                         fprintf(stdout, "Unknown option : %s\n", argv[i]);
1156                         /* Show usage */
1157                         fputs(usage, stdout);
1158                         return -1;
1159                 }
1160
1161                 moreargs = (i < argc - 1);
1162                 
1163                 switch(argv[i][1]){
1164                         case 's':
1165                                 doset=1;
1166                                 continue;
1167                         case 'i':
1168                                 docalibrate = 1;
1169                                 if (moreargs){ /* we need to check for a value after 'i' for backwards compatability with command line options of old fxotune */
1170                                         if (argv[i+1][0] != '-' && argv[i+1][0] != '/')
1171                                                 dialstr = argv[++i];
1172                                 }
1173                                 continue;
1174                         case 'c':
1175                                 configfile = moreargs ? argv[++i] : configfile;
1176                                 continue;
1177                         case 'd':
1178                                 dodump = 1;
1179                                 continue;
1180                         case 'b':
1181                                 startdev = moreargs ? atoi(argv[++i]) : startdev;
1182                                 break;
1183                         case 'e':
1184                                 stopdev = moreargs ? atoi(argv[++i]) : stopdev;
1185                                 break;
1186                         case 't':
1187                                 calibtype = moreargs ? atoi(argv[++i]) : calibtype;
1188                                 break;
1189                         case 'w':
1190                                 waveformtype = moreargs ? atoi(argv[++i]) : waveformtype;
1191                                 break;
1192                         case 'l':
1193                                 delaytosilence = moreargs ? atoi(argv[++i]) : delaytosilence;
1194                                 break;
1195                         case 'm':
1196                                 silencegoodfor = moreargs ? atoi(argv[++i]) : silencegoodfor;
1197                                 break;
1198                         case 'n':
1199                                 dialstr = moreargs ? argv[++i] : dialstr;
1200                                 break;
1201                         case 'p':
1202                                 printbest++;
1203                                 break;
1204                         case 'x':
1205                                 use_table = 1;
1206                                 break;
1207                         case 'v':
1208                                 debug = strlen(argv[i])-1;
1209                                 break;
1210                         case 'o':
1211                                 if (moreargs) {
1212                                         audio_dump_fd = open(argv[++i], O_WRONLY|O_CREAT|O_TRUNC, 0666);
1213                                         if (audio_dump_fd == -1) {
1214                                                 fprintf(stdout, "Unable to open file %s: %s\n", argv[i], strerror(errno));
1215                                                 return -1;
1216                                         }
1217                                         break;
1218                                 } else {
1219                                         fprintf(stdout, "No path supplied to -o option!\n");
1220                                         return -1;
1221                                 }
1222                         default:
1223                                 fprintf(stdout, "Unknown option : %s\n", argv[i]);
1224                                 /* Show usage */
1225                                 fputs(usage, stdout);
1226                                 return -1;
1227                                 
1228                 }
1229         }
1230         
1231         if (debug > 3){
1232                 fprintf(stdout, "Running with parameters:\n");
1233                 fprintf(stdout, "\tdoset=%d\n", doset); 
1234                 fprintf(stdout, "\tdocalibrate=%d\n", docalibrate);     
1235                 fprintf(stdout, "\tdodump=%d\n", dodump);       
1236                 fprintf(stdout, "\tprint best settings=%d\n", printbest);
1237                 fprintf(stdout, "\tstartdev=%d\n", startdev);
1238                 fprintf(stdout, "\tstopdev=%d\n", stopdev);     
1239                 fprintf(stdout, "\tcalibtype=%d\n", calibtype); 
1240                 fprintf(stdout, "\twaveformtype=%d\n", waveformtype);   
1241                 fprintf(stdout, "\tdelaytosilence=%d\n", delaytosilence);       
1242                 fprintf(stdout, "\tsilencegoodfor=%d\n", silencegoodfor);       
1243                 fprintf(stdout, "\tdialstr=%s\n", dialstr);     
1244                 fprintf(stdout, "\tdebug=%d\n", debug); 
1245         }
1246
1247         if(use_table) {
1248                 init_sinetable();
1249         }
1250         
1251         if (docalibrate){
1252                 res = do_calibrate(startdev, stopdev, calibtype, configfile, dialstr, delaytosilence, silencegoodfor);
1253                 if (!res)
1254                         return do_set(configfile);      
1255                 else
1256                         return -1;
1257         }
1258
1259         if (doset)
1260                 return do_set(configfile);
1261                                 
1262         if (dodump){
1263                 res = do_dump(startdev, dialstr, delaytosilence, silencegoodfor, waveformtype);
1264                 if (!res)
1265                         return 0;
1266                 else
1267                         return -1;
1268         }
1269
1270         fputs(usage, stdout);
1271         return -1;
1272 }