cwdaemon-0.9.5/0000755000175000017500000000000011740650276013154 5ustar acerionacerioncwdaemon-0.9.5/cwdaemon.c0000644000175000017500000005514311737345322015124 0ustar acerionacerion/* * cwdaemon - morse sounding daemon for the parallel or serial port * Copyright (C) 2002 -2005 Joop Stakenborg * and many authors, see the AUTHORS file. * * 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 2 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 Library General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ # if HAVE_STDIO_H # include #endif #if HAVE_SYS_TYPES_H # include #endif #if HAVE_SYS_STAT_H # include #endif #if STDC_HEADERS # include # include #else # if HAVE_STDLIB_H # include # endif #endif #if HAVE_STRING_H # if !STDC_HEADERS && HAVE_MEMORY_H # include # endif # include #endif #if HAVE_UNISTD_H # include #endif #if DHAVE_ARPA_INET # include #endif #if HAVE_FCNTL_H # include #endif #if HAVE_NETDB # include #endif #if HAVE_NETINET_IN_H # include #endif #if HAVE_SYS_SOCKET_H # include #endif #if HAVE_SYSLOG_H # include #endif #if HAVE_ERRNO_H # include #endif #if TIME_WITH_SYS_TIME # include # include #else # if HAVE_SYS_TIME_H # include # else # include # endif #endif #if HAVE_SIGNAL_H # include #endif #if HAVE_STDARG_H # include #endif #if HAVE_SYS_RESOURCE_H # include #endif #if (defined(__unix__) || defined(unix)) && !defined(USG) # include #endif #include #include #include "cwdaemon.h" /* network vars */ socklen_t sin_len, reply_socklen; int socket_descriptor; struct sockaddr_in k_sin, reply_sin; int port = 6789; /* default UDP port we listen on */ char reply_data[256]; /* morse defaults */ int morse_speed = 24; /* speed (wpm) */ int morse_tone = 800; /* tone (Hz) */ int morse_sound = 1; /* speaker on */ int morse_volume = 70; /* initial volume */ int wordmode = 0; /* start in character mode */ int ptt_delay = 0; /* default = 0 ms */ int console_sound = 1; /* speaker on */ int soundcard_sound = 0; /* soundcard off */ /* various variables */ int forking = 1; /* we fork by default */ int bandswitch; int priority = 0; int async_abort = 0; /* flags for different states */ int ptt_timer_running = 0; /* flag for PTT state */ int aborting = 0; int sendingmorse = 0; struct timeval now, end, left; /* PTT timers */ struct timespec sleeptime, time_remainder; /* delay timers */ #define MAXMORSE 4000 char morsetext[MAXMORSE]; char *keydev; cwdevice cwdevice_ttys = { init:ttys_init, free:ttys_free, reset:ttys_reset, cw:ttys_cw, ptt:ttys_ptt, ssbway:NULL, switchband:NULL, footswitch:NULL }; cwdevice cwdevice_null = { init:null_init, free:null_free, reset:null_reset, cw:null_cw, ptt:null_ptt, ssbway:NULL, switchband:NULL, footswitch:NULL }; #if defined (HAVE_LINUX_PPDEV_H) || defined (HAVE_DEV_PPBUS_PPI_H) cwdevice cwdevice_lp = { init:lp_init, free:lp_free, reset:lp_reset, cw:lp_cw, ptt:lp_ptt, ssbway:lp_ssbway, switchband:lp_switchband, footswitch:lp_footswitch }; #endif cwdevice *cwdev; static void playmorsestring (char *x); static int set_libcw_output (void); static void close_libcw (void); /* catch ^C when running in foreground */ static RETSIGTYPE catchint (int signal) { cwdev->free (cwdev); printf ("%s: Exiting\n", PACKAGE); exit (0); } /* print error message to the console or syslog if we are forked */ void errmsg (char *info, ...) { va_list ap; char s[1025]; va_start (ap, info); vsnprintf (s, 1024, info, ap); va_end (ap); if (forking) syslog (LOG_ERR, "%s\n", s); else printf ("%s: %s failed: %s\n", PACKAGE, s, strerror (errno)); } /* print only debug message to the console */ void debug (char *info, ...) { va_list ap; char s[1025]; if (!forking) { va_start (ap, info); vsnprintf (s, 1024, info, ap); va_end (ap); printf ("%s: %s \n", PACKAGE, s); } } /* delay in microseconds */ static void udelay (unsigned long us) { sleeptime.tv_sec = 0; sleeptime.tv_nsec = us * 1000; if (nanosleep (&sleeptime, &time_remainder) == -1) { if (errno == EINTR) nanosleep (&time_remainder, NULL); else errmsg ("Nanosleep"); } } /* some simple timing utilities, see * http://www.erlenstar.demon.co.uk/unix/faq_8.html */ static void timer_add (struct timeval *tv, long secs, long usecs) { tv->tv_sec += secs; if ((tv->tv_usec += usecs) >= 1000000) { tv->tv_sec += tv->tv_usec / 1000000; tv->tv_usec %= 1000000; } } /* Set *RES = *A - *B, returning the sign of the result */ static int timer_sub (struct timeval *res, const struct timeval *a, const struct timeval *b) { long sec = a->tv_sec - b->tv_sec; long usec = a->tv_usec - b->tv_usec; if (usec < 0) usec += 1000000, --sec; res->tv_sec = sec; res->tv_usec = usec; return (sec < 0) ? (-1) : ((sec == 0 && usec == 0) ? 0 : 1); } /* band switch function, pin 2, 7, 8, 9 */ static void set_switch (unsigned int bandswitch) { unsigned int lp_switchbyte = 0; lp_switchbyte = (bandswitch & 0x01) | ((bandswitch & 0x0e) * 16); if (cwdev->switchband) { cwdev->switchband (cwdev, lp_switchbyte); debug ("Set bandswitch to %x", bandswitch); } else debug ("Bandswitch output not implemented"); } /* tune a number of seconds */ static void tune (int seconds) { int us; if (seconds > 0) { cw_flush_tone_queue (); if (ptt_delay) { cwdev->ptt (cwdev, ON); debug ("PTT (TUNE) on"); /* TOD */ udelay (ptt_delay); } us = seconds * 1000000; debug ("CW (TUNE) on"); cw_queue_tone (us, morse_tone); if (ptt_delay) { udelay (us + ptt_delay); cwdev->ptt (cwdev, OFF); debug ("PTT (TUNE) off"); } } } /* (re)set initial parameters of libcw */ static void reset_libcw (void) { /* just in case if an old generator exists */ close_libcw (); set_libcw_output (); cw_set_frequency (morse_tone); cw_set_send_speed (morse_speed); cw_set_volume (morse_volume); cw_set_gap (0); } static void close_libcw (void) { cw_generator_stop (); cw_generator_delete (); } /* set up output of libcw */ static int set_libcw_output (void) { int rv = 0; if (soundcard_sound && !console_sound) { rv = cw_generator_new (CW_AUDIO_ALSA, NULL); if (rv != CW_FAILURE) { rv = cw_generator_start(); } } else if (!soundcard_sound && console_sound) { rv = cw_generator_new (CW_AUDIO_CONSOLE, NULL); if (rv != CW_FAILURE) { rv = cw_generator_start(); } } else { /* libcw can't do both soundcard and console, and it has to have one and only one sound system specified */ errmsg ("Sound output specified incorrectly"); rv = CW_FAILURE; } return rv == CW_FAILURE ? -1 : 0; } /* properly parse a 'long' integer */ static int get_long(const char *buf, long *lvp) { char *ep; long lv; errno = 0; lv = strtol(buf, &ep, 10); if (buf[0] == '\0' || (*ep != '\0' && *ep != '\n')) return (-1); if (errno == ERANGE && (lv == LONG_MAX || lv == LONG_MIN)) return (-1); *lvp = lv; return (0); } /* watch the socket and if there is an escape character check what it is, otherwise play morse. Return 0 with escape characters and empty messages.*/ static int recv_code (void) { char message[257], *ndev; ssize_t recv_rc; int valid_sdevice = 0, fd; long lv; recv_rc = recvfrom (socket_descriptor, message, sizeof (message) - 1, 0, (struct sockaddr *) &k_sin, &sin_len); if (recv_rc == -1 && errno != EAGAIN) { errmsg ("Recvfrom"); exit (1); } if (recv_rc > 0) { message[recv_rc] = '\0'; if (message[0] != 27) { /* no ESCAPE */ debug ("Message = %s", message); if ((strlen (message) + strlen (morsetext)) <= MAXMORSE - 1) { strcat (morsetext, message); playmorsestring (morsetext); } return 1; } else { /* check ESCAPE characters */ switch ((int) message[1]) { case '0': /* reset all values */ morsetext[0] = '\0'; morse_speed = 24; morse_tone = 800; morse_volume = 70; console_sound = 1; soundcard_sound = 0; reset_libcw (); wordmode = 0; async_abort = 0; cwdev->reset (cwdev); debug ("Reset all values"); break; case '2': /*speed */ if (get_long(message + 2, &lv)) break; if (lv > 4 && lv < 61) { morse_speed = lv; cw_set_send_speed (morse_speed); debug ("Speed: %d wpm", morse_speed); } break; case '3': /* tone */ if (get_long(message + 2, &lv)) break; if (lv > 0 && lv < 4001) { morse_tone = lv; cw_set_frequency (morse_tone); cw_set_volume (70); morse_sound = 1; debug ("Tone: %s Hz, volume 70%", message + 2); } else if (lv == 0) /* sidetone off */ { morse_tone = 0; cw_set_volume (0); morse_sound = 0; debug ("Volume off"); } break; case '4': /* message abort */ debug ("Message abort"); strcpy (morsetext, ""); cw_flush_tone_queue (); cw_wait_for_tone_queue (); aborting = 1; break; case '5': /* exit */ cwdev->free (cwdev); errno = 0; errmsg ("Sender has told me to end the connection"); exit (0); break; case '6': /* set uninterruptable */ message[0] = '\0'; morsetext[0] = '\0'; wordmode = 1; debug ("Wordmode set"); break; case '7': /* set weighting */ if (get_long(message + 2, &lv)) break; if ((lv > -51) && (lv < 51)) /* only allowed range */ { cw_set_weighting (lv * 0.6 + 50); debug ("Weight: %ld", lv); } break; case '8': /* device type */ ndev = message + 2; debug ("Device: %s", ndev); if ((fd = dev_is_tty(ndev)) != -1) { cwdev->free(cwdev); cwdev = &cwdevice_ttys; keydev = cwdev->desc = ndev; cwdev->init(cwdev, fd); } #if defined(HAVE_LINUX_PPDEV_H) || defined(HAVE_DEV_PPBUS_PPI_H) else if ((fd = dev_is_parport(ndev)) != -1) { cwdev->free(cwdev); cwdev = &cwdevice_lp; keydev = cwdev->desc = ndev; cwdev->init(cwdev, fd); } #endif else if ((fd = dev_is_null(ndev)) != -1) { cwdev->free(cwdev); cwdev = &cwdevice_null; keydev = cwdev->desc = ndev; cwdev->init(cwdev, fd); } else debug ("Unknown device"); break; case '9': /* base port number */ debug ("Obsolete"); break; case 'a': /* PTT keying on or off */ if (get_long(message + 2, &lv)) break; if (lv) { cwdev->ptt (cwdev, ON); debug ("PTT on"); } else { cwdev->ptt (cwdev, OFF); debug ("PTT off"); } break; case 'b': /* SSB way */ if (get_long(message + 2, &lv)) break; if (lv) { if (cwdev->ssbway) { cwdev->ssbway (cwdev, SOUNDCARD); debug ("SSB way set to SOUNDCARD", PACKAGE); } else debug ("SSB way to SOUNDCARD unimplemented"); } else { if (cwdev->ssbway) { cwdev->ssbway (cwdev, MICROPHONE); debug ("SSB way set to MIC"); } else debug ("SSB way to MICROPHONE unimplemented"); } break; case 'c': /* Tune for a number of seconds */ if (get_long(message + 2, &lv)) break; if (lv <= 10) tune(lv); break; case 'd': /* set ptt delay (TOD, Turn On Delay) */ if (get_long(message + 2, &lv)) break; if (lv >= 0 && lv < 51) ptt_delay = lv * 1000; else ptt_delay = 50000; debug ("PTT delay(TOD): %d ms", ptt_delay / 1000); if (ptt_delay == 0) { cwdev->ptt (cwdev, OFF); debug ("PTT off"); } break; case 'e': /* set bandswitch output on parport bits 2(lsb),7,8,9(msb) */ if (get_long(message + 2, &lv)) break; if (lv <= 15 && lv >= 0) { bandswitch = lv; set_switch(bandswitch); } break; case 'f': /* switch console/soundcard */ if (!strncmp (message + 2, "n", 1)) { console_sound = 0; soundcard_sound = 0; valid_sdevice = 1; } else if (!strncmp (message + 2, "c", 1)) { console_sound = 1; soundcard_sound = 0; valid_sdevice = 1; } else if (!strncmp (message + 2, "s", 1)) { console_sound = 0; soundcard_sound = 1; valid_sdevice = 1; } else if (!strncmp (message + 2, "b", 1)) { console_sound = 1; soundcard_sound = 1; valid_sdevice = 1; } if (valid_sdevice == 1) { debug ("Sound device: %s", message + 2); set_libcw_output (); } break; case 'g': /* volume */ if (get_long(message + 2, &lv)) break; if (lv <= 100 && lv >= 0) { morse_volume = lv; cw_set_volume (morse_volume); } break; case 'h': /* send echo to main program when CW playing is done */ memcpy (&reply_sin, &k_sin, sizeof(reply_sin)); /* remember sender */ reply_socklen = sin_len; strncpy (reply_data + 1, message, sizeof(reply_data) - 2); reply_data[sizeof(reply_data) - 1] = '\0'; reply_data[0]='h'; if (strlen (message) + 1 <= MAXMORSE - 1) strcat (morsetext, "^"); break; } return 0; } } return 0; } /* check every character for speed increase or decrease, and play others */ static void playmorsestring (char *x) { int i = 0, validchar = 0; char c; sendingmorse = 1; while (*x) { c = *x; if ((c == '+') || (c == '-')) { /* speed in- & decrease */ if ((c == '+') && (morse_speed <= 58)) morse_speed += 2; if ((c == '-') && (morse_speed >= 10)) morse_speed -= 2; cw_set_send_speed (morse_speed); } else if (c == '~') cw_set_gap (2); /* 2 dots time additional for the next char */ else if (c == '^') { debug ("Echo '%s'", reply_data); if (strlen (reply_data) == 0) return; sendto (socket_descriptor, reply_data, strlen (reply_data), 0, (struct sockaddr *)&reply_sin, reply_socklen); strcpy (reply_data,""); break; } else { validchar = 1; if (ptt_delay) { if (ptt_timer_running == 1) { gettimeofday (&end, NULL); timer_add (&end, 0, ptt_delay); } else { cwdev->ptt (cwdev, ON); debug ("PTT on"); /* TOD */ udelay (ptt_delay); } } if (c == '*') c = '+'; debug ("Morse = %c", c); cw_send_character (c); if (cw_get_gap () == 2) cw_set_gap (0); cw_wait_for_tone_queue(); } x++; i++; if (i >= strlen (morsetext)) { i = 0; break; } if (wordmode == 0) recv_code (); } morsetext[0] = '\0'; /* start ptt off timer */ if (ptt_delay && validchar) { gettimeofday (&end, NULL); timer_add (&end, 0, ptt_delay); ptt_timer_running = 1; } sendingmorse = 0; } static void keyingevent (void *arg, int keystate) { if (keystate == 1) cwdev->cw (cwdev, ON); else cwdev->cw (cwdev, OFF); } /* parse the command line and check for options, do some error checking */ static void parsecommandline (int argc, char *argv[]) { long lv; int p; while ((p = getopt (argc, argv, "d:hnp:P:s:t:v:Vw:x:")) != -1) { switch (p) { case ':': case '?': case 'h': printf ("Usage: %s [option]...\n", PACKAGE); printf (" -d "); printf ("Use a different device\n "); #if defined (HAVE_LINUX_PPDEV_H) printf ("(e.g. ttyS0,1,2, parport0,1, etc. default = parport0)\n"); #elif defined (HAVE_DEV_PPBUS_PPI_H) printf ("(e.g. ttyd0,1,2, ppi0,1, etc. default = ppi0)\n"); #else #ifdef BSD printf ("(e.g. ttyd0,1,2, etc. default = ttyd0)\n"); #else printf ("(e.g. ttyS0,1,2, etc. default = ttyS0)\n"); #endif #endif printf (" -h "); printf ("Display this help and exit\n"); printf (" -n "); printf ("Do not fork and print debug information to stdout\n"); printf (" -p "); printf ("Use a different UDP port number (> 1023, default = 6789)\n"); #if defined(HAVE_SETPRIORITY) && defined(PRIO_PROCESS) printf (" -P "); printf ("Set cwdaemon priority (-20 ... 20, default = 0)\n"); #endif printf (" -s "); printf ("Set morse speed (4 ... 60 wpm, default = 24)\n"); printf (" -t