-
Notifications
You must be signed in to change notification settings - Fork 45
/
cwdaemon.c
1679 lines (1370 loc) · 50.8 KB
/
cwdaemon.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* cwdaemon - morse sounding daemon for the parallel or serial port
* Copyright (C) 2002 - 2005 Joop Stakenborg <[email protected]>
* and many authors, see the AUTHORS file.
* Copyright (C) 2012 - 2015 Kamil Ignacak <[email protected]>
* Modififed 2020 by M5EVT.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
#define _POSIX_C_SOURCE 200809L /* nanosleep(), strdup() */
#define _GNU_SOURCE /* getopt_long() */
/* Define as the return type of signal handlers (`int' or `void'). */
#define RETSIGTYPE void
#include <stdlib.h>
#include <libcw.h>
#include <libcw_debug.h>
#include "cwdaemon.h"
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/param.h>
#include <gtk/gtk.h>
/**
\file cwdaemon.c
cwdaemon exchanges data with client through messages. Most of
messages are sent by client application to cwdaemon - those
are called in this file "requests". On several occasions cwdaemon
sends some data back to the client. Such messages are called
"replies".
message:
- request
- reply
Size of a message is not constant.
Maximal size of a message is CWDAEMON_MESSAGE_SIZE_MAX.
*/
#include "band.h"
#include "channel.h"
#include "discovered.h"
#include "mode.h"
#include "filter.h"
#include "receiver.h"
#include "transmitter.h"
#include "wideband.h"
#include "adc.h"
#include "dac.h"
#include "radio.h"
#include "main.h"
#include "protocol1.h"
#include "audio.h"
#include "signal.h"
#include "vfo.h"
#include "transmitter.h"
/* cwdaemon constants. */
#define CWDAEMON_MORSE_SPEED_DEFAULT 30 /* [wpm] */
#define CWDAEMON_MORSE_TONE_DEFAULT 650 /* [Hz] */
#define CWDAEMON_MORSE_VOLUME_DEFAULT 10 /* [%] */
/* TODO: why the limitation to 50 ms? Is it enough? */
#define CWDAEMON_PTT_DELAY_DEFAULT 0 /* [ms] */
#define CWDAEMON_PTT_DELAY_MIN 0 /* [ms] */
#define CWDAEMON_PTT_DELAY_MAX 50 /* [ms] */
/* Notice that the range accepted by cwdaemon is different than that
accepted by libcw. */
#define CWDAEMON_MORSE_WEIGHTING_DEFAULT 0
#define CWDAEMON_MORSE_WEIGHTING_MIN -50
#define CWDAEMON_MORSE_WEIGHTING_MAX 50
#define CWDAEMON_NETWORK_PORT_DEFAULT 6789
#define CWDAEMON_AUDIO_SYSTEM_DEFAULT CW_AUDIO_PA /* Console buzzer, from libcw.h. */
#define CWDAEMON_VERBOSITY_DEFAULT CWDAEMON_VERBOSITY_W /* Threshold of verbosity of debug strings. */
#define CWDAEMON_USECS_PER_MSEC 1000 /* Just to avoid magic numbers. */
#define CWDAEMON_USECS_PER_SEC 1000000 /* Just to avoid magic numbers. */
#define CWDAEMON_MESSAGE_SIZE_MAX 256 /* Maximal size of single message. */
#define CWDAEMON_REQUEST_QUEUE_SIZE_MAX 4000 /* Maximal size of common buffer/fifo where requests may be pushed to. */
#define CWDAEMON_TUNE_SECONDS_MAX 10 /* Maximal time of tuning. TODO: why the limitation to 10 s? Is it enough? */
GMutex cwdaemon_mutex;
bool keytx;
bool keysidetone;
/* Default values of parameters, may be modified only through
command line arguments passed to cwdaemon.
After setting these variables with values passed in command line,
these become the default state of cwdaemon. Values of default_*
will be used when resetting libcw and cwdaemon to initial state. */
static int default_morse_speed = CWDAEMON_MORSE_SPEED_DEFAULT;
static int default_morse_tone = CWDAEMON_MORSE_TONE_DEFAULT;
static int default_morse_volume = CWDAEMON_MORSE_VOLUME_DEFAULT;
static int default_ptt_delay = CWDAEMON_PTT_DELAY_DEFAULT;
static int default_audio_system = CWDAEMON_AUDIO_SYSTEM_DEFAULT;
static int default_weighting = CWDAEMON_MORSE_WEIGHTING_DEFAULT;
/* Actual values of parameters, used to control ongoing operation of
cwdaemon+libcw. These values can be modified through requests
received from socket in cwdaemon_receive(). */
static int current_morse_speed = CWDAEMON_MORSE_SPEED_DEFAULT;
static int current_morse_tone = CWDAEMON_MORSE_TONE_DEFAULT;
static int current_morse_volume = CWDAEMON_MORSE_VOLUME_DEFAULT;
static int current_ptt_delay = CWDAEMON_PTT_DELAY_DEFAULT;
static int current_audio_system = CWDAEMON_AUDIO_SYSTEM_DEFAULT;
static int current_weighting = CWDAEMON_MORSE_WEIGHTING_DEFAULT;
/* Level of libcw's tone queue that triggers 'callback for low level
in tone queue'. The callback function is
cwdaemon_tone_queue_low_callback(), it is registered with
cw_register_tone_queue_low_callback().
I REALLY don't think that you would want to set it to any value
other than '1'. */
static const int tq_low_watermark = 1;
/* Quick and dirty solution to following problem: when cwdaemon for
some reason fails to open audio output, and attempts to play
characters received from client, it crashes. It doesn't know that
it attempts to play to closed audio output.
This is a flag telling cwdaemon if audio output is available or not.
TODO: the variable is almost unused. Start using it.
TODO: decide on terminology: "audio system" or "sound system". */
static bool has_audio_output = false;
/* Network variables. */
/* cwdaemon usually receives requests from client, but on occasions
it needs to send a reply back. This is why in addition to
request_* we also have reply_* */
//static int socket_descriptor = 0;
/* Default UDP port we listen on. Can be changed only through command
line switch.
There is a code path suggesting that it was possible to change the
port using network request, but now this code path is marked as
"obsolete".
*/
//static int port = CWDAEMON_NETWORK_PORT_DEFAULT;
//static struct sockaddr_in request_addr;
//static socklen_t request_addrlen;
//static struct sockaddr_in reply_addr;
//static socklen_t reply_addrlen;
static char reply_buffer[CWDAEMON_MESSAGE_SIZE_MAX];
/* Debug variables. */
/* This table is addressed with values defined in "enum
cwdaemon_verbosity" (src/cwdaemon.h). */
static const char *cwdaemon_verbosity_labels[] = {
"NN", /* None - obviously this label will never be used. */
"EE", /* Error. */
"WW", /* Warning. */
"II", /* Information. */
"DD" }; /* Debug. */
/* Various variables. */
static int wordmode = 0; /* Start in character mode. */
static int inactivity_seconds = 9999; /* Inactive since nnn seconds. */
/* Incoming requests without Escape code are stored in this pseudo-FIFO
before they are played. */
static char request_queue[CWDAEMON_REQUEST_QUEUE_SIZE_MAX];
/* Flag for PTT state/behaviour. */
static unsigned char ptt_flag = 0;
/* Automatically turn PTT on and off.
Turn PTT on when starting to play Morse characters, and turn PTT off when
there are no more characters to play.
"Automatically" means that cwdaemon toggles PTT without any additional
actions taken by client. Client doesn't have to tell cwdaemon when to
turn PTT on/off - this is done by cwdaemon itself, automatically.
If ptt delay is non-zero, cwdaemon performs delay between turning PTT on
and starting to play Morse characters.
TODO: is there a delay before turning PTT off? */
#define PTT_ACTIVE_AUTO 0x01
/* PTT is turned on and off manually.
It is the client who decides when to turn the PTT on and off.
The client has to send 'a' escape code, followed by '1' or '0' to
'manually' turn PTT on or off.
'MANUAL' - the opposite of 'AUTO' where it is cwdaemon that decides
when to turn PTT on and off.
Perhaps "PTT_ON_REQUEST" would be a better name of the constant. */
#define PTT_ACTIVE_MANUAL 0x02
/* Don't turn PTT off until cwdaemon sends back an echo to client.
client may request echoing back to it a reply when cwdaemon finishes
playing given request. PTT shouldn't be turned off when sending the
reply (TODO: why it shouldn't?).
This flag is set whenever client sends request for sending back a
reply (i.e. either <ESC>h request, or caret request).
This flag is re-set whenever such reply is sent (to be more
precise: after playing a requested text, but just before sending to
the client the requested reply). */
#define PTT_ACTIVE_ECHO 0x04
void cwdaemon_set_ptt_on(cwdevice *device, const char *info);
void cwdaemon_set_ptt_off(cwdevice *device, const char *info);
void cwdaemon_switch_band(cwdevice *device, unsigned int band);
void cwdaemon_play_request(char *request);
void cwdaemon_tune(uint32_t seconds);
void cwdaemon_keyingevent(void *arg, int keystate);
void cwdaemon_prepare_reply(char *reply, const char *request, size_t n);
void cwdaemon_tone_queue_low_callback(void *arg);
bool cwdaemon_initialize_socket(void);
void cwdaemon_close_socket(void);
ssize_t cwdaemon_sendto(const char *reply);
int cwdaemon_recvfrom(char *request, int n);
int cwdaemon_receive(void);
void cwdaemon_handle_escaped_request(char *request);
void cwdaemon_reset_almost_all(void);
/* Functions managing libcw output. */
bool cwdaemon_open_libcw_output(int audio_system);
void cwdaemon_close_libcw_output(void);
void cwdaemon_reset_libcw_output(void);
/* Utility functions. */
bool cwdaemon_get_long(const char *buf, long *lvp);
void cwdaemon_udelay(unsigned long us);
//static bool cwdaemon_params_cwdevice(const char *optarg);
static bool cwdaemon_params_wpm(int *wpm, const char *optarg);
static bool cwdaemon_params_tune(uint32_t *seconds, const char *optarg);
static int cwdaemon_params_pttdelay(int *delay, const char *optarg);
static bool cwdaemon_params_volume(int *volume, const char *optarg);
static bool cwdaemon_params_weighting(int *weighting, const char *optarg);
static bool cwdaemon_params_tone(int *tone, const char *optarg);
static bool cwdaemon_params_system(int *system, const char *optarg);
static bool cwdaemon_params_ptt_on_off(const char *optarg);
/* Auto, manual, echo. */
static char cwdaemon_debug_ptt_flag[3 + 1];
static const char *cwdaemon_debug_ptt_flags(void);
/* Selected keying device:
serial port (cwdevice_ttys) || parallel port (cwdevice_lp) || null (cwdevice_null).
It should be configured with cwdaemon_cwdevice_set(). */
/* FIXME: if no device is specified in command line, and no physical
device is available, the global_cwdevice is NULL, which causes the
program to break. */
static cwdevice *global_cwdevice = NULL;
const char *cwdaemon_debug_ptt_flags(void)
{
if (ptt_flag & PTT_ACTIVE_AUTO) {
cwdaemon_debug_ptt_flag[0] = 'A';
} else {
cwdaemon_debug_ptt_flag[0] = 'a';
}
if (ptt_flag & PTT_ACTIVE_MANUAL) {
cwdaemon_debug_ptt_flag[1] = 'M';
} else {
cwdaemon_debug_ptt_flag[1] = 'm';
}
if (ptt_flag & PTT_ACTIVE_ECHO) {
cwdaemon_debug_ptt_flag[2] = 'E';
} else {
cwdaemon_debug_ptt_flag[2] = 'e';
}
cwdaemon_debug_ptt_flag[3] = '\0';
return cwdaemon_debug_ptt_flag;
}
/**
\brief Sleep for specified amount of microseconds
Function can detect an interrupt from a signal, and continue sleeping,
but only once.
\param us - microseconds to sleep
*/
void cwdaemon_udelay(unsigned long us)
{
struct timespec sleeptime, time_remainder;
sleeptime.tv_sec = 0;
sleeptime.tv_nsec = us * 1000;
if (nanosleep(&sleeptime, &time_remainder) == -1) {
if (errno == EINTR) {
nanosleep(&time_remainder, NULL);
} else {
printf("Nanosleep");
}
}
return;
}
/**
\brief Switch PTT on
\param device - current keying device
\param info - debug information displayed when performing the switching
*/
void cwdaemon_set_ptt_on(cwdevice *device, const char *info)
{
/* For backward compatibility it is assumed that ptt_delay=0
means "cwdaemon shouldn't turn PTT on, at all". */
if (current_ptt_delay && !(ptt_flag & PTT_ACTIVE_AUTO)) {
device->ptt(device, ON);
cwdaemon_udelay(current_ptt_delay * CWDAEMON_USECS_PER_MSEC);
ptt_flag |= PTT_ACTIVE_AUTO;
}
return;
}
/**
\brief Switch PTT off
\param device - current keying device
\param info - debug information displayed when performing the switching
*/
void cwdaemon_set_ptt_off(cwdevice *device, const char *info)
{
device->ptt(device, OFF);
ptt_flag = 0;
return;
}
/**
\brief Tune for a number of seconds
Play a continuous sound for a given number of seconds.
Parameter type is uint32_t, which gives us maximum of 4294967295
seconds, i.e. ~136 years. Should be enough.
TODO: change the argument type to size_t.
\param seconds - time of tuning
*/
void cwdaemon_tune(uint32_t seconds)
{
if (seconds > 0) {
cw_flush_tone_queue();
cwdaemon_set_ptt_on(global_cwdevice, "PTT (TUNE) on");
/* make it similar to normal CW, allowing interrupt */
for (uint32_t i = 0; i < seconds; i++) {
cw_queue_tone(CWDAEMON_USECS_PER_SEC, current_morse_tone);
}
cw_send_character('e'); /* append minimal tone to return to normal flow */
}
return;
}
/**
\brief Reset some initial parameters of cwdaemon and libcw
TODO: split this function into:
cwdaemon_reset_basic_params()
cwdaemon_reset_libcw_output()
and call these two functions separately instead of this one.
This function that combines these two doesn't make much sense.
*/
void cwdaemon_reset_almost_all(void)
{
current_morse_speed = default_morse_speed;
current_morse_tone = default_morse_tone;
current_morse_volume = default_morse_volume;
current_audio_system = default_audio_system;
current_ptt_delay = default_ptt_delay;
current_weighting = default_weighting;
cwdaemon_reset_libcw_output();
return;
}
/**
\brief Open audio sink using libcw
\param audio_system - audio system to be used by libcw
\return -1 on failure
\return 0 otherwise
*/
bool cwdaemon_open_libcw_output(int audio_system)
{
int rv = cw_generator_new(audio_system, NULL);
if (audio_system == CW_AUDIO_OSS && rv == CW_FAILURE) {
/* When reopening libcw output, previous audio system may
block audio device for a short period of time after the
output has been closed. In such a situation OSS may fail
to open audio device. Let's give it some time. */
for (int i = 0; i < 5; i++) {
printf("delaying switching to OSS, please wait few seconds.\n");
sleep(4);
rv = cw_generator_new(audio_system, NULL);
if (rv == CW_SUCCESS) {
break;
}
}
}
if (rv != CW_FAILURE) {
rv = cw_generator_start();
printf("starting generator with sound system \"%s\": %s", cw_get_audio_system_label(audio_system), rv ? "success" : "failure");
} else {
/* FIXME:
When cwdaemon failed to create a generator, and user
kills non-forked cwdaemon through Ctrl+C, there is
a memory protection error.
It seems that this error has been fixed with
changes in libcw, committed on 31.12.2012.
To be observed. */
printf( "failed to create generator with sound system \"%s\"", cw_get_audio_system_label(audio_system));
}
return rv == CW_FAILURE ? false : true;
}
/**
\brief Close libcw audio output
*/
void cwdaemon_close_libcw_output(void) {
cw_generator_stop();
cw_generator_delete();
return;
}
/**
\brief Reset parameters of libcw to default values
Function uses values of cwdaemon's global 'default_' variables, and some
other values to reset state of libcw.
*/
void cwdaemon_reset_libcw_output(void)
{
/* This function is called when cwdaemon receives '0' escape code.
README describes this code as "Reset to default values".
Therefore we use default_* below.
However, the function is called after "current_" values
have been reset to "default_" values. So maybe we could use
"current_" values and somehow encapsulate the calls to
cw_set_*() functions? The calls are also made elsewhere.
*/
/* Delete old generator (if it exists). */
cwdaemon_close_libcw_output();
printf("setting sound system \"%s\"", cw_get_audio_system_label(default_audio_system));
if (cwdaemon_open_libcw_output(default_audio_system)) {
has_audio_output = true;
} else {
has_audio_output = false;
return;
}
/* Remember that tone queue is bound to a generator. When
cwdaemon switches on request to other sound system, it will
have to re-register the callback. */
cw_register_tone_queue_low_callback(cwdaemon_tone_queue_low_callback, NULL, tq_low_watermark);
cw_set_frequency(default_morse_tone);
cw_set_send_speed(default_morse_speed);
cw_set_volume(default_morse_volume);
cw_set_gap(0);
cw_set_weighting(default_weighting * 0.6 + CWDAEMON_MORSE_WEIGHTING_MAX);
return;
}
/**
\brief Properly parse a 'long' integer
Parse a string with digits, convert it to a long integer
\param buf - input buffer with a string
\param lvp - pointer to output long int variable
\return false on failure
\return true on success
*/
bool cwdaemon_get_long(const char *buf, long *lvp)
{
errno = 0;
char *ep;
long lv = strtol(buf, &ep, 10);
if (buf[0] == '\0' || *ep != '\0') {
return false;
}
if (errno == ERANGE && (lv == LONG_MAX || lv == LONG_MIN)) {
return false;
}
*lvp = lv;
return true;
}
/**
\brief Prepare reply for the caller
Fill \p reply buffer with data from given \p request, prepare some
other variables for sending reply to the client.
Text of the reply is usually defined by caller, i.e. it is sent by client
to cwdaemon and marked by the client as text to be used in reply.
The reply should be sent back to client as soon as cwdaemon
finishes processing/playing received request.
There are two different procedures for recognizing what should be sent
back as reply and when:
\li received request ending with '^' character: the text of the request
should be played, but it also should be used as a reply.
This function does not specify when the reply should be sent back.
All it does is it prepares the text of reply.
'^' can be used for char-by-char communication: client software
message with single character followed by '^'. cwdaemon plays the
character, and informs client software about playing the sound. Then
client software can send request with next character followed by '^'.
\li received request starting with "<ESC>h" escape code: the text of
request should be sent back to the client after playing text of *next*
request. So there are two requests sent by client to cwdaemon:
first defines reply, and the second defines text to be played.
First should be echoed back (but not played), second should be played.
\param reply - buffer for reply (allocated by caller)
\param request - buffer with request
\param n - size of data in request
*/
void cwdaemon_prepare_reply(char *reply, const char *request, size_t n)
{
/* Since we need to prepare a reply, we need to mark our
intent to send echo. The echo (reply) will be sent to client
when libcw's tone queue becomes empty.
It is important to set this flag at the beginning of the function. */
ptt_flag |= PTT_ACTIVE_ECHO;
printf("PTT flag +PTT_ACTIVE_ECHO (0x%02x/%s)", ptt_flag, cwdaemon_debug_ptt_flags());
/* We are sending reply to the same host that sent a request. */
memcpy(&radio->reply_addr, &radio->request_addr, sizeof(radio->reply_addr));
radio->reply_addrlen = radio->request_addrlen;
strncpy(reply, request, n);
reply[n] = '\0'; /* FIXME: where is boundary checking? */
printf("text of request: \"%s\", text of reply: \"%s\"\n", request, reply);
printf("now waiting for end of transmission before echoing back to client\n");
return;
}
/**
\brief Wrapper around sendto()
Wrapper around sendto(), sending a \p reply to client.
Client is specified by reply_* network variables.
\param reply - reply to be sent
\return -1 on failure
\return number of characters sent on success
*/
ssize_t cwdaemon_sendto(const char *reply)
{
size_t len = strlen(reply);
/* TODO: Do we *really* need to end replies with CRLF? */
assert(reply[len - 2] == '\r' && reply[len - 1] == '\n');
ssize_t rv = sendto(radio->socket_descriptor, reply, len, 0,
(struct sockaddr *) &radio->reply_addr, radio->reply_addrlen);
if (rv == -1) {
printf("sendto: \"%s\"", strerror(errno));
return -1;
} else {
return rv;
}
}
/**
\brief Receive request sent through socket
Received request is returned through \p request.
Possible trailing '\r' and '\n' characters are stripped.
Request is ended with '\0'.
\param request - buffer for received request
\param n - size of the buffer
\return -2 if peer has performed an orderly shutdown
\return -1 if an error occurred during call to recvfrom
\return 0 if no request has been received
\return length of received request otherwise
*/
int cwdaemon_recvfrom(char *request, int n)
{
ssize_t recv_rc = recvfrom(radio->socket_descriptor,
request,
n,
0, /* flags */
(struct sockaddr *) &radio->request_addr,
/* TODO: request_addrlen may be modified. Check it. */
&radio->request_addrlen);
if (recv_rc == -1) { /* No requests available? */
if (errno == EAGAIN || errno == EWOULDBLOCK) { /* "a portable application should check for both possibilities" */
/* Yup, no requests available from non-blocking socket. Good luck next time. */
/* TODO: how much CPU time does it cost to loop waiting for a request?
Would it be reasonable to configure the socket as blocking?
How large is receive timeout? */
return 0;
} else {
/* Some other error. May be a serious error. */
g_print("Close thread\n");
return -1;
}
} else if (recv_rc == 0) {
/* "peer has performed an orderly shutdown" */
return -2;
} else {
; /* pass */
}
/* Remove CRLF if present. TCP buffer may end with '\n', so make
sure that every request is consistently ended with NUL only.
Do it early, do it now. */
int z;
while (recv_rc > 0
&& ( (z = request[recv_rc - 1]) == '\n' || z == '\r') ) {
request[--recv_rc] = '\0';
}
return recv_rc;
}
/**
\brief Receive message from socket, act upon it
Watch the socket and if there is an escape character check what it is,
otherwise play morse.
FIXME: duplicate return value (zero and zero).
\return 0 when an escape code has been received
\return 0 when no request or an empty request has been received
\return 1 when a text request has been played
*/
int cwdaemon_receive(void)
{
/* The request may be a printable string, so +1 for ending NUL
added somewhere below is necessary. */
char request_buffer[CWDAEMON_MESSAGE_SIZE_MAX + 1];
ssize_t recv_rc = cwdaemon_recvfrom(request_buffer, CWDAEMON_MESSAGE_SIZE_MAX);
if (recv_rc == -2) {
/* Sender has closed connection. */
return -1;
} else if (recv_rc == -1) {
/* TODO: should we really exit?
Shouldn't we recover from the error? */
return -1;
} else if (recv_rc == 0) {
//printf("...recv_from (no data)\n");
return 0;
} else {
; /* pass */
}
request_buffer[recv_rc] = '\0';
printf("-------------------\n");
/* TODO: replace the magic number 27 with constant. */
if (request_buffer[0] != 27) {
/* No ESCAPE. All received data should be treated
as text to be sent using Morse code.
Note that this does not exclude possibility of
caret request (e.g. "some text^"), which does
require sending a reply to client. Such request is
correctly handled by cwdaemon_play_request(). */
printf("request: \"%s\"\n", request_buffer);
if ((strlen(request_buffer) + strlen(request_queue)) <= CWDAEMON_REQUEST_QUEUE_SIZE_MAX - 1) {
strcat(request_queue, request_buffer);
cwdaemon_play_request(request_queue);
} else {
; /* TODO: how to handle this case? */
}
return 1;
} else {
/* Don't print literal escape value, use <ESC>
symbol. First reason is that the literal value
doesn't look good in console (some non-printable
glyph), second reason is that printing <ESC>c to
terminal makes funny things with the lines already
printed to the terminal (tested in xfce4-terminal
and xterm). */
printf("escaped request: \"<ESC>%s\"\n", request_buffer + 1);
cwdaemon_handle_escaped_request(request_buffer);
return 0;
}
}
/**
The function may call exit() if a request from client asks the
daemon to exit.
*/
void cwdaemon_handle_escaped_request(char *request)
{
/* Take action depending on Escape code. */
switch ((int) request[1]) {
case '0':
/* Reset all values. */
/*
printf("requested resetting of parameters");
request_queue[0] = '\0';
cwdaemon_reset_almost_all();
wordmode = 0;
async_abort = 0;
//global_cwdevice->reset(global_cwdevice);
ptt_flag = 0;
printf("PTT flag = 0 (0x%02x/%s)", ptt_flag, cwdaemon_debug_ptt_flags());
printf("resetting completed");
*/
break;
case '2':
/* Set speed of Morse code, in words per minute. */
if (cwdaemon_params_wpm(¤t_morse_speed, request + 2)) {
cw_set_send_speed(current_morse_speed);
}
break;
case '3':
break;
/* Set tone (frequency) of morse code, in Hz.
The code assumes that minimal valid frequency is zero. */
assert (CW_FREQUENCY_MIN == 0);
if (cwdaemon_params_tone(¤t_morse_tone, request + 2)) {
if (current_morse_tone > 0) {
cw_set_frequency(current_morse_tone);
printf("tone: %d Hz", current_morse_tone);
/* TODO: Should we really be adjusting
volume when the command is for
frequency? It would be more
"elegant" not to do so. */
cw_set_volume(current_morse_volume);
} else { /* current_morse_tone==0, sidetone off */
cw_set_volume(0);
printf("volume off");
}
}
break;
case '4':
/* Abort currently sent message. */
if (wordmode) {
printf("requested aborting of message - ignoring (word mode is active)");
} else {
printf("requested aborting of message - executing (character mode is active)");
if (ptt_flag & PTT_ACTIVE_ECHO) {
printf("echo \"break\"");
cwdaemon_sendto("break\r\n");
}
request_queue[0] = '\0';
cw_flush_tone_queue();
cw_wait_for_tone_queue();
if (ptt_flag) {
cwdaemon_set_ptt_off(global_cwdevice, "PTT off");
}
ptt_flag &= 0;
printf("PTT flag = 0 (0x%02x/%s)", ptt_flag, cwdaemon_debug_ptt_flags());
}
break;
case '5':
/* Exit cwdaemon. */
errno = 0;
printf("requested exit of daemon");
exit(EXIT_SUCCESS);
case '6':
/* Set uninterruptable (word mode). */
break;
request[0] = '\0';
request_queue[0] = '\0';
wordmode = 1;
printf("wordmode set");
break;
case '7':
break;
/* Set weighting of morse code dits and dashes.
Remember that cwdaemon uses values in range
-50/+50, but libcw accepts values in range
20/80. This is why you have the calculation
when calling cw_set_weighting(). */
if (cwdaemon_params_weighting(¤t_weighting, request + 2)) {
cw_set_weighting(current_weighting * 0.6 + CWDAEMON_MORSE_WEIGHTING_MAX);
}
break;
case '8': {
/* Set type of keying device. */
// UNUSED
break;
}
case '9':
/* Change network port number.
TODO: why this is obsolete? */
break;
case 'a':
/* PTT keying on or off */
cwdaemon_params_ptt_on_off(request + 2);
break;
case 'b':
/* SSB way. */
break;
case 'c':
{
/* FIXME: change this uint32_t to size_t. */
uint32_t seconds = 0;
/* Tune for a number of seconds. */
if (cwdaemon_params_tune(&seconds, request + 2)) {
cwdaemon_tune(seconds);
}
break;
}
case 'd':
{
break;
/* Set PTT delay (TOD, Turn On Delay).
The value is milliseconds. */
int rv = cwdaemon_params_pttdelay(¤t_ptt_delay, request + 2);
if (rv == 0) {
/* Value totally invalid. */
printf(
"invalid requested PTT delay [ms]: \"%s\" (should be integer between %d and %d inclusive)",
request + 2,
CWDAEMON_PTT_DELAY_MIN, CWDAEMON_PTT_DELAY_MAX);
} else if (rv == 1) {
/* Value totally valid. Information
debug string has been already
printed in
cwdaemon_params_pttdelay(). */
} else { /* rv == 2 */
/* Value invalid (out-of-range), but
acceptable when sent over network
request and then clipped to be
in-range. Value has been clipped in
cwdaemon_params_pttdelay(), but a
warning debug string must be
printed here. */
printf("requested PTT delay [ms] out of range: \"%s\", clipping to \"%d\" (should be between %d and %d inclusive)",
request + 2,
CWDAEMON_PTT_DELAY_MAX,
CWDAEMON_PTT_DELAY_MIN, CWDAEMON_PTT_DELAY_MAX);
}
if (rv && current_ptt_delay == 0) {
cwdaemon_set_ptt_off(global_cwdevice, "ensure PTT off");
}
}
break;
case 'e':
/* Set band switch output on parport bits 9 (MSB), 8, 7, 2 (LSB). */
break;
case 'f': {
break;
/* Change sound system used by libcw. */
/* FIXME: if "request+2" describes unavailable sound system,
cwdaemon fails to open the new sound system. Since
the old one is closed with cwdaemon_close_libcw_output(),
cwdaemon has no working sound system, and is unable to
play sound.
This can be fixed either by querying libcw if "request+2"
sound system is available, or by first trying to
open new sound system and then - on success -
closing the old one. In either case cwdaemon would
require some method to inform client about success
or failure to open new sound system. */
if (cwdaemon_params_system(¤t_audio_system, request + 2)) {
/* Handle valid request for changing sound system. */
cwdaemon_close_libcw_output();
if (cwdaemon_open_libcw_output(current_audio_system)) {
has_audio_output = true;
} else {
/* Fall back to NULL audio system. */
cwdaemon_close_libcw_output();
if (cwdaemon_open_libcw_output(CW_AUDIO_NULL)) {
printf("fall back to \"Null\" sound system");
current_audio_system = CW_AUDIO_PA;
has_audio_output = true;
} else {
printf(
"failed to fall back to \"Null\" sound system");
has_audio_output = false;
}
}
if (has_audio_output) {
/* Tone queue is bound to a
generator. Creating new generator
requires re-registering the
callback. */
cw_register_tone_queue_low_callback(cwdaemon_tone_queue_low_callback, NULL, tq_low_watermark);
/* This call recalibrates length of
dot and dash. */
cw_set_frequency(current_morse_tone);
cw_set_send_speed(current_morse_speed);
cw_set_volume(current_morse_volume);
/* Regardless if we are using
"default" or "current" parameters,
the gap is always zero. */
cw_set_gap(0);
cw_set_weighting(current_weighting * 0.6 + CWDAEMON_MORSE_WEIGHTING_MAX);
}
}
break;