diff options
Diffstat (limited to 'cmd/ossphone/ossphone.c')
-rw-r--r-- | cmd/ossphone/ossphone.c | 412 |
1 files changed, 412 insertions, 0 deletions
diff --git a/cmd/ossphone/ossphone.c b/cmd/ossphone/ossphone.c new file mode 100644 index 0000000..380ab85 --- /dev/null +++ b/cmd/ossphone/ossphone.c @@ -0,0 +1,412 @@ +/* + * Purpose: Utility to make phone calls using Open Sound System modem support. + */ + +/* + * + * This file is part of Open Sound System. + * + * Copyright (C) 4Front Technologies 1996-2008. + * + * This this source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <stdint.h> +#include <unistd.h> +#include <signal.h> +#include <ctype.h> +#include <math.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/select.h> +#include <soundcard.h> + +char *cmdname=NULL; + +int modem_in_fd = -1; +int modem_out_fd = -1; +int dev_dsp_fd = -1; + +char *dspdev_name = "/dev/dsp"; /* Local audio device (headset) */ +int srate = 8000; +double digit_duration = 0.2; +double silence_duration = 0.1; + +/* + * http://en.wikipedia.org/wiki/DTMF#Keypad + */ + +const double dtmf_keypad_row[] = { 697.0, 770.0, 852.0, 941.0 }; +const double dtmf_keypad_col[] = { 1209.0, 1336.0, 1477.0, 1633.0 }; +const char *dtmf_keypad_keys = "123A456B789C*0#D"; + +static void +modem_write(int fd, const void *buf, size_t count) +{ + if (write(fd, buf, count) != count) + { + perror("Modem write"); + exit(1); + } +} + +static void +modem_read(int fd, void *buf, size_t count) +{ + if (read(fd, buf, count) != count) + { + perror("Modem read"); + exit(1); + } +} + +static int +dtmf_fill_digit(uint16_t *buf, size_t buf_len, int digit) +{ + const double A = 65536.0 / 8.0; + const double pi = 3.14159265358979323846; + + const char *keypad_digit; + double w1, w2; + double t, sample_duration; + size_t i, pos; + + keypad_digit = strchr(dtmf_keypad_keys, toupper(digit)); + + if (keypad_digit == NULL) + return -1; + + pos = (int)(keypad_digit - dtmf_keypad_keys); + + /* compute angular frequencies */ + w1 = 2.0*pi*dtmf_keypad_row[pos / 4]; + w2 = 2.0*pi*dtmf_keypad_col[pos % 4]; + + t = 0.0; + sample_duration = 1.0 / (double)srate; + + for (i = 0; i < buf_len; i++) + { + buf[i] = (uint16_t)(A + A*sin(w1*t) + A + A*sin(w2*t)); + t += sample_duration; + } + + return 0; +} + +static void +dtmf_fill_silence(uint16_t *buf, size_t buf_len) +{ + const uint16_t A = (uint16_t)(65536.0 / 4.0); + size_t i; + + for (i = 0; i < buf_len; i++) + { + buf[i] = A; + } +} + +static double +evaluate_dc_level(uint16_t *buf, size_t buf_len) +{ + double sum = 0.0; + size_t i; + + for (i = 0; i < buf_len; i++) + { + sum += ((double)buf[i]) / 65536.0; + } + + return (sum / (double)buf_len); +} + +static void +go_offhook() +{ + int offhook = 1; + + printf("Off-hook\n"); + ioctl (modem_out_fd, SNDCTL_DSP_MODEM_OFFHOOK, &offhook); +} + +static int wait_dialtone() +{ + double dc_level = 0.0; + const double min_dc_level = 0.2; + + uint16_t buf[4096]; + + int retries = 0; + const int max_retries = 10; + + printf("Waiting for dial tone...\n"); + while (dc_level < min_dc_level) + { + int dummy; + modem_read(modem_in_fd, buf, sizeof(buf)); + dummy=write(dev_dsp_fd, buf, sizeof(buf)); + + dc_level = evaluate_dc_level(buf, sizeof(buf)/sizeof(uint16_t)); + + if (retries++ > max_retries) + { + return -1; + } + } + + return 0; +} + +static void +dial_phone_number(const char *phone_number) +{ + size_t digit_len = (size_t)(digit_duration*srate); + size_t silence_len = (size_t)(silence_duration*srate); + size_t digit_size = digit_len * sizeof(uint16_t); + size_t silence_size = silence_len * sizeof(uint16_t); + + uint16_t *digit = (uint16_t *)malloc(digit_size); + uint16_t *silence = (uint16_t *)malloc(silence_size); + uint16_t *buf = (uint16_t *)malloc(silence_size); + + dtmf_fill_silence (silence, silence_len); + + printf("Dialing... "); + fflush(stdout); + + while (*phone_number != '\0') + { + if (dtmf_fill_digit (digit, digit_len, *phone_number) >= 0) + { + int dummy; + + printf("%c", *phone_number); + fflush(stdout); + + modem_write (modem_out_fd, digit, digit_size); + modem_read (modem_in_fd, digit, digit_size); + dummy=write (dev_dsp_fd, digit, digit_size); + + modem_write (modem_out_fd, silence, silence_size); + modem_read (modem_in_fd, buf, silence_size); + dummy=write (dev_dsp_fd, buf, silence_size); + } + phone_number++; + } + + free(buf); + free(silence); + free(digit); + + printf("\n"); +} + +static void +usage(void) +{ + fprintf (stderr, "Usage: %s [options] mdmin-dev mdmout-dev [phone-number]\n", cmdname); + fprintf (stderr, " Options: -d<devname> Change sound device (default: %s)\n", dspdev_name); + fprintf (stderr, " -s<rate> Change sampling rate (default: %d)\n", srate); + fprintf (stderr, " -t<duration> Change DTMF digit duration (default: %.1f s)\n", digit_duration); + fprintf (stderr, " -l<duration> Change DTMF silence duration (default: %.1f s)\n", silence_duration); + exit (-1); +} + +static void +exit_handler(void) +{ + if (modem_in_fd >= 0) + { + close (modem_in_fd); + modem_in_fd = -1; + } + if (modem_out_fd >= 0) + { + int offhook = 0; + printf("On-hook\n"); + ioctl (modem_out_fd, SNDCTL_DSP_MODEM_OFFHOOK, &offhook); + close (modem_out_fd); + modem_out_fd = -1; + } + if (dev_dsp_fd >= 0) + { + close (dev_dsp_fd); + dev_dsp_fd = -1; + } +} + +static void +sigint_handler(int sig) +{ + exit (0); +} + +int +main(int argc, char **argv) +{ + char *phone_number = ""; + + int channels = 1; + int format = AFMT_S16_LE; + + extern int optind; + extern char *optarg; + int c, tmp; + + cmdname=argv[0]; + + signal(SIGINT, sigint_handler); + + if (argc < 3) + { + usage (); + } + + while ((c = getopt (argc, argv, "d:s:t:l:")) != EOF) + { + switch (c) + { + case 'd': + dspdev_name = optarg; + break; + case 's': + srate = atoi (optarg); + break; + case 't': + digit_duration = atof (optarg); + break; + case 'l': + silence_duration = atof (optarg); + break; + default: + usage (); + } + } + + if ((argc - optind) < 2) + usage (); + + atexit(exit_handler); + + dev_dsp_fd = open (dspdev_name, O_RDWR); + if (dev_dsp_fd < 0) + { + perror (dspdev_name); + exit (-1); + } + + modem_in_fd = open (argv[optind], O_RDWR); + if (modem_in_fd < 0) + { + perror (argv[optind]); + exit (-1); + } + tmp=0; + ioctl(modem_in_fd, SNDCTL_DSP_COOKEDMODE, &tmp); // No error checking with this call + optind++; + + modem_out_fd = open(argv[optind], O_RDWR); + if (modem_out_fd < 0) + { + perror (argv[optind]); + exit (-1); + } + tmp=0; + ioctl(modem_out_fd, SNDCTL_DSP_COOKEDMODE, &tmp); // No error checking with this call + optind++; + + if (argc > optind) + phone_number = argv[optind]; + + assert ( ioctl (modem_in_fd, SNDCTL_DSP_CHANNELS, &channels) >= 0 ); + assert ( ioctl (modem_out_fd, SNDCTL_DSP_CHANNELS, &channels) >= 0 ); + assert ( ioctl (dev_dsp_fd, SNDCTL_DSP_CHANNELS, &channels) >= 0 ); + + assert ( ioctl (modem_in_fd, SNDCTL_DSP_SETFMT, &format) >= 0 ); + assert ( ioctl (modem_out_fd, SNDCTL_DSP_SETFMT, &format) >= 0 ); + assert ( ioctl (dev_dsp_fd, SNDCTL_DSP_SETFMT, &format) >= 0 ); + + assert ( ioctl (modem_in_fd, SNDCTL_DSP_SPEED, &srate) >= 0 ); + assert ( ioctl (modem_out_fd, SNDCTL_DSP_SPEED, &srate) >= 0 ); + assert ( ioctl (dev_dsp_fd, SNDCTL_DSP_SPEED, &srate) >= 0 ); + + { + int tmp; + tmp = 0; + assert ( ioctl (modem_in_fd, SNDCTL_DSP_SETTRIGGER, &tmp) >= 0 ); + tmp = PCM_ENABLE_INPUT; + assert ( ioctl (modem_in_fd, SNDCTL_DSP_SETTRIGGER, &tmp) >= 0 ); + tmp = 0; + assert ( ioctl (modem_out_fd, SNDCTL_DSP_SETTRIGGER, &tmp) >= 0 ); + tmp = PCM_ENABLE_OUTPUT; + assert ( ioctl (modem_out_fd, SNDCTL_DSP_SETTRIGGER, &tmp) >= 0 ); + tmp = 0; + assert ( ioctl (dev_dsp_fd, SNDCTL_DSP_SETTRIGGER, &tmp) >= 0 ); + tmp = PCM_ENABLE_INPUT | PCM_ENABLE_OUTPUT; + assert ( ioctl (dev_dsp_fd, SNDCTL_DSP_SETTRIGGER, &tmp) >= 0 ); + } + + go_offhook (); + + if (phone_number[0] != '\0') + { + if (wait_dialtone () < 0) + { + printf("No dial tone.\n"); + exit (-1); + } + dial_phone_number(phone_number); + } + + printf("Call in progress...\n"); + printf("Press Ctrl-C to quit.\n"); + + { + uint16_t buf[128]; + fd_set rfds; + int retval; + + int max_fd = modem_in_fd; + if (dev_dsp_fd > max_fd) + max_fd = dev_dsp_fd; + + while (1) + { + int dummy; + FD_ZERO(&rfds); + FD_SET(modem_in_fd, &rfds); + FD_SET(dev_dsp_fd, &rfds); + + retval = select(max_fd+1, &rfds, NULL, NULL, NULL); + if (retval == -1) + { + perror("select"); + } + else if (retval) + { + if (FD_ISSET(modem_in_fd, &rfds)) + { + modem_read(modem_in_fd, buf, sizeof(buf)); + dummy=write(dev_dsp_fd, buf, sizeof(buf)); + } + if (FD_ISSET(dev_dsp_fd, &rfds)) + { + dummy=read(dev_dsp_fd, buf, sizeof(buf)); + modem_write(modem_out_fd, buf, sizeof(buf)); + } + } + } + } + + // return 0; /* NOT REACHED */ +} + |