/* osmo datenklo main file * * (C) 2019 by Andreas Eversberg * All Rights Reserved * * 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 . */ #include #include #include #include #include #include #include #include #include "../libsample/sample.h" #include "../libtimer/timer.h" #include "../liboptions/options.h" #include "../libdebug/debug.h" #include "../libfsk/fsk.h" #include "../libwave/wave.h" #include "../libdisplay/display.h" #include "am791x.h" #include "uart.h" #include "datenklo.h" #define MAX_DEVICES 2 #define OPT_ARRAY(num_name, name, value) \ { \ if (num_name == MAX_DEVICES) { \ fprintf(stderr, "Too many devices defined!\n"); \ exit(0); \ } \ name[num_name++] = value; \ } /* dummy functions */ int num_kanal = 1; /* only one channel used for debugging */ void *get_sender_by_empfangsfrequenz() { return "void"; } static datenklo_t datenklo[MAX_DEVICES]; static enum am791x_type am791x_type = AM791X_TYPE_7911; static int num_mc = 0; static uint8_t mc[MAX_DEVICES] = { 0 }; static int auto_rts = 0; static int num_tx_baudrate = 0, num_rx_baudrate = 0; static int tx_baudrate[MAX_DEVICES] = { 0 }, rx_baudrate[MAX_DEVICES] = { 0 }; static int num_ttydev = 0; static const char *ttydev[MAX_DEVICES] = { "/dev/ttyDATENKLO0" }; static const char *audiodev = "hw:0,0"; static int samplerate = 48000; static int latency = 50; static int stereo = 0; static int loopback = 0; static int fast_math = 0; const char *write_tx_wave = NULL; const char *write_rx_wave = NULL; const char *read_tx_wave = NULL; const char *read_rx_wave = NULL; void print_help(const char *arg0) { printf("Usage: %s [options] -M \n\n", arg0); /* - - */ printf(" -h --help\n"); printf(" This help\n"); printf(" --config [~/]\n"); printf(" Give a config file to use. If it starts with '~/', path is at home dir.\n"); printf(" Each line in config file is one option, '-' or '--' must not be given!\n"); printf(" -T --am791x-type 7910 | 7911\n"); printf(" Give modem chip type. (Default = 791%d)\n", am791x_type); printf(" -M --mc \n"); printf(" Give mode setting of AM7910/AM71911, use 'list' to list all modes.\n"); printf(" -A --auto-rts\n"); printf(" Automatically rais and drop modem's RTS line for half duplex operation.\n"); printf(" TX data will be queued while remote carrier is detected.\n"); printf(" -B --baudrate \n"); printf(" Given baud rate will override the baud rate given by ioctl.\n"); printf(" A baud rate of <= 150 Bps for TX and/or RX will attach TX and/or RX to\n"); printf(" the back channel instead of the main channel.\n"); printf(" -D --device\n"); printf(" Device name. The prefix '/dev/ is automatically added, if not given.\n"); printf(" (default = '%s')\n", ttydev[0]); printf(" -S --stereo\n"); printf(" Generate two devices. One device connects to the left and the other to\n"); printf(" the right channel of the audio device. The device number in the device\n"); printf(" name is automatically increased by one. You must also define the mode\n"); printf(" twice. (-M -M )\n"); printf(" -a --audio-device hw:,\n"); printf(" Sound card and device number (default = '%s')\n", audiodev); printf(" -s --samplerate \n"); printf(" Sample rate of sound device (default = '%d')\n", samplerate); printf(" -b --buffer \n"); printf(" How many milliseconds are processed in advance (default = '%d')\n", latency); printf(" -l --loopback \n"); printf(" Perform audio loopback to test modem.\n"); printf(" type 1: Audio from transmitter is fed into receiver (analog loopback)\n"); printf(" type 2: Audio is crossed between two modem instances. (use with -S)\n"); printf(" --fast-math\n"); printf(" Use fast math approximation for slow CPU / ARM based systems.\n"); printf(" --write-rx-wave \n"); printf(" Write received audio to given wave file.\n"); printf(" --write-tx-wave \n"); printf(" Write transmitted audio to given wave file.\n"); printf(" --read-rx-wave \n"); printf(" Replace received audio by given wave file.\n"); printf(" --read-tx-wave \n"); printf(" Replace transmitted audio by given wave file.\n"); } #define OPT_WRITE_RX_WAVE 1001 #define OPT_WRITE_TX_WAVE 1002 #define OPT_READ_RX_WAVE 1003 #define OPT_READ_TX_WAVE 1004 #define OPT_MNCC_NAME 1006 #define OPT_FAST_MATH 1007 static void add_options(void) { option_add('h', "help", 0); option_add('v', "debug", 1); option_add('T', "am791x_type", 1); option_add('M', "mc", 1); option_add('A', "auto-rts", 0); option_add('B', "baudrate", 2); option_add('D', "device", 1); option_add('S', "stereo", 0); option_add('a', "audio-device", 1); option_add('s', "samplerate", 1); option_add('b', "buffer", 1); option_add('l', "loopback", 1); option_add(OPT_WRITE_RX_WAVE, "write-rx-wave", 1); option_add(OPT_WRITE_TX_WAVE, "write-tx-wave", 1); option_add(OPT_READ_RX_WAVE, "read-rx-wave", 1); option_add(OPT_READ_TX_WAVE, "read-tx-wave", 1); option_add(OPT_FAST_MATH, "fast-math", 0); } static int handle_options(int short_option, int argi, char **argv) { int rc; switch (short_option) { case 'h': print_help(argv[0]); return 0; case 'v': if (!strcasecmp(argv[argi], "list")) { debug_list_cat(); return 0; } rc = parse_debug_opt(argv[argi]); if (rc < 0) { fprintf(stderr, "Failed to parse debug option, please use -h for help.\n"); return rc; } break; case 'T': am791x_type = atoi(argv[argi]); if (am791x_type < 0 || am791x_type > 1) { fprintf(stderr, "Given type parameter '%s' is invalid, use '-h' for help!\n", argv[argi]); return -EINVAL; } break; case 'M': if (!strcasecmp(argv[argi], "list")) { am791x_list_mc(am791x_type); return 0; } else if (argv[argi][0] >= '0' && argv[argi][0] <= '9') { OPT_ARRAY(num_mc, mc, atoi(argv[argi])) if (mc[num_mc - 1] > 31) goto mc_inval; } else { mc_inval: fprintf(stderr, "Given mode parameter '%s' is invalid, use '-h' for help!\n", argv[argi]); return -EINVAL; } break; case 'A': auto_rts = 1; break; case 'B': OPT_ARRAY(num_rx_baudrate, rx_baudrate, atoi(argv[argi])) OPT_ARRAY(num_tx_baudrate, tx_baudrate, atoi(argv[argi + 1])) break; case 'D': OPT_ARRAY(num_ttydev, ttydev, options_strdup(argv[argi])) break; case 'S': stereo = 1; break; case 'a': audiodev = options_strdup(argv[argi]); break; case 's': samplerate = atoi(argv[argi]); break; case 'b': latency = atoi(argv[argi]); break; case 'l': loopback = atoi(argv[argi]); break; case OPT_FAST_MATH: fast_math = 1; break; case OPT_WRITE_RX_WAVE: write_rx_wave = options_strdup(argv[argi]); break; case OPT_WRITE_TX_WAVE: write_tx_wave = options_strdup(argv[argi]); break; case OPT_READ_RX_WAVE: read_rx_wave = options_strdup(argv[argi]); break; case OPT_READ_TX_WAVE: read_tx_wave = options_strdup(argv[argi]); break; } return 1; } const char *inc_dev_name(const char *dev_name) { char *new_name, *number; int integer; /* clone */ new_name = malloc(256); strcpy(new_name, dev_name); /* find number and remove, if any */ number = new_name; while(*number < '0' || *number > '9') number++; if (!(*number)) integer = 2; else integer = atoi(number) + 1; /* change number */ sprintf(number, "%d", integer); return new_name; } int main(int argc, char *argv[]) { int rc, argi; int i; /* handle options / config file */ add_options(); rc = options_config_file(argc, argv, "~/.osmocom/analog/datenklo.conf", handle_options); if (rc < 0) return 0; argi = options_command_line(argc, argv, handle_options); if (argi <= 0) return argi; /* inits */ datenklo_init_global(); fm_init(fast_math); if (stereo) { num_kanal = 2; } if (num_mc == 0) { fprintf(stderr, "You need to set the mode of the modem chip. See '--help'.\n"); exit(0); } if (num_mc < num_kanal) { fprintf(stderr, "You need to specify as many mode settings as you have channels.\n"); exit(0); } /* create modem instance */ for (i = 0; i < num_kanal; i++) { /* remove /dev/ */ if (ttydev[i] && !strncmp(ttydev[i], "/dev/", 5)) ttydev[i] += 5; /* increment last name */ if (i && ttydev[i] == NULL) ttydev[i] = inc_dev_name(ttydev[i - 1]); rc = datenklo_init(&datenklo[i], ttydev[i], am791x_type, mc[i], auto_rts, tx_baudrate[i], rx_baudrate[i], samplerate, loopback); if (rc < 0) { fprintf(stderr, "Failed to create \"Datenklo\" instance. Quitting!\n"); goto fail; } if (i) datenklo[i - 1].slave = &datenklo[i]; printf("Datenklo on device '/dev/%s' ready. (using sound device '%s')\n", ttydev[i], audiodev); } rc = datenklo_open_audio(&datenklo[0], audiodev, latency, write_rx_wave, write_tx_wave, read_rx_wave, read_tx_wave); if (rc < 0) { fprintf(stderr, "Failed to initialize audio. Quitting!\n"); goto fail; } datenklo_main(&datenklo[0], loopback); fail: for (i = 0; i < num_kanal; i++) datenklo_exit(&datenklo[i]); options_free(); return 0; }