1 //
2 // This file is part of Dire Wolf, an amateur radio packet TNC.
3 //
4 // Copyright (C) 2011, 2013, 2014, 2015, 2016, 2017 John Langner, WB2OSZ
5 //
6 // This program is free software: you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation, either version 2 of the License, or
9 // (at your option) any later version.
10 //
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with this program. If not, see <http://www.gnu.org/licenses/>.
18
19
20 /*------------------------------------------------------------------
21 *
22 * Module: ptt.c
23 *
24 * Purpose: Activate the output control lines for push to talk (PTT) and other purposes.
25 *
26 * Description: Traditionally this is done with the RTS signal of the serial port.
27 *
28 * If we have two radio channels and only one serial port, DTR
29 * can be used for the second channel.
30 *
31 * If __WIN32__ is defined, we use the Windows interface.
32 * Otherwise we use the Linux interface.
33 *
34 * Version 0.9: Add ability to use GPIO pins on Linux.
35 *
36 * Version 1.1: Add parallel printer port for x86 Linux only.
37 *
38 * This is hardcoded to use the primary motherboard parallel
39 * printer port at I/O address 0x378. This might work with
40 * a PCI card configured to use the same address if the
41 * motherboard does not have a built in parallel port.
42 * It won't work with a USB-to-parallel-printer-port adapter.
43 *
44 * Version 1.2: More than two radio channels.
45 * Generalize for additional signals besides PTT.
46 *
47 * Version 1.3: HAMLIB support.
48 *
49 * Version 1.4: The spare "future" indicator is now used when connected to another station.
50 *
51 * Take advantage of the new 'gpio' group and new /sys/class/gpio protections in Raspbian Jessie.
52 *
53 * Handle more complicated gpio node names for CubieBoard, etc.
54 *
55 * Version 1.5: Ability to use GPIO pins of CM108/CM119 for PTT signal.
56 *
57 *
58 * References: http://www.robbayer.com/files/serial-win.pdf
59 *
60 * https://www.kernel.org/doc/Documentation/gpio.txt
61 *
62 *---------------------------------------------------------------*/
63
64 /*
65 A growing number of people have been asking about support for the DMK URI,
66 RB-USB RIM, etc.
67
68 These use a C-Media CM108/CM119 with an interesting addition, a GPIO
69 pin is used to drive PTT. Here is some related information.
70
71 DMK URI:
72
73 http://www.dmkeng.com/URI_Order_Page.htm
74 http://dmkeng.com/images/URI%20Schematic.pdf
75
76 RB-USB RIM:
77
78 http://www.repeater-builder.com/products/usb-rim-lite.html
79 http://www.repeater-builder.com/voip/pdf/cm119-datasheet.pdf
80
81 RA-35:
82
83 http://www.masterscommunications.com/products/radio-adapter/ra35.html
84
85 DINAH:
86
87 https://hamprojects.info/dinah/
88
89
90 Homebrew versions of the same idea:
91
92 http://images.ohnosec.org/usbfob.pdf
93 http://www.qsl.net/kb9mwr/projects/voip/usbfob-119.pdf
94 http://rtpdir.weebly.com/uploads/1/6/8/7/1687703/usbfob.pdf
95 http://www.repeater-builder.com/projects/fob/USB-Fob-Construction.pdf
96
97 Applications that have support for this:
98
99 http://docs.allstarlink.org/drupal/
100 http://soundmodem.sourcearchive.com/documentation/0.16-1/ptt_8c_source.html
101 https://github.com/N0NB/hamlib/blob/master/src/cm108.c#L190
102 http://permalink.gmane.org/gmane.linux.hams.hamlib.devel/3420
103
104 Information about the "hidraw" device:
105
106 http://unix.stackexchange.com/questions/85379/dev-hidraw-read-permissions
107 http://www.signal11.us/oss/udev/
108 http://www.signal11.us/oss/hidapi/
109 https://github.com/signal11/hidapi/blob/master/libusb/hid.c
110 http://stackoverflow.com/questions/899008/howto-write-to-the-gpio-pin-of-the-cm108-chip-in-linux
111 https://www.kernel.org/doc/Documentation/hid/hidraw.txt
112 https://github.com/torvalds/linux/blob/master/samples/hidraw/hid-example.c
113
114 Similar chips: SSS1621, SSS1623
115
116 https://irongarment.wordpress.com/2011/03/29/cm108-compatible-chips-with-gpio/
117
118 Here is an attempt to add direct CM108 support.
119 Seems to be hardcoded for only a single USB audio adapter.
120
121 https://github.com/donothingloop/direwolf_cm108
122
123 In version 1.3, we add HAMLIB support which should be able to do this in a roundabout way.
124 (Linux only at this point.)
125
126 This is documented in the User Guide, section called,
127 "Hamlib PTT Example 2: Use GPIO of USB audio adapter. (e.g. DMK URI)"
128
129 It's rather involved and the explantion doesn't cover the case of multiple
130 USB-Audio adapters. It would be nice to have a little script which lists all
131 of the USB-Audio adapters and the corresponding /dev/hidraw device.
132 ( We now have it. The included "cm108" application. )
133
134 In version 1.5 we have a flexible, easy to use implementation for Linux.
135 Windows would be a lot of extra work because USB devices are nothing like Linux.
136 We'd be starting from scratch to figure out how to do it.
137 */
138
139
140 #include "direwolf.h" // should be first. This includes windows.h.
141
142 #include <stdio.h>
143 #include <unistd.h>
144 #include <stdlib.h>
145 #include <assert.h>
146 #include <string.h>
147 #include <time.h>
148
149 #if __WIN32__
150 #else
151 #include <termios.h>
152 #include <sys/ioctl.h>
153 #include <fcntl.h>
154 #include <sys/types.h>
155 #include <sys/stat.h>
156 #include <unistd.h>
157 #include <errno.h>
158 #include <grp.h>
159 #include <dirent.h>
160
161 #ifdef USE_HAMLIB
162 #include <hamlib/rig.h>
163 #endif
164
165 #ifdef USE_CM108
166 #include "cm108.h"
167 #endif
168
169 /* So we can have more common code for fd. */
170 typedef int HANDLE;
171 #define INVALID_HANDLE_VALUE (-1)
172
173 #endif
174
175 #include "textcolor.h"
176 #include "audio.h"
177 #include "ptt.h"
178 #include "dlq.h"
179
180
181 #if __WIN32__
182
183 #define RTS_ON(fd) EscapeCommFunction(fd,SETRTS);
184 #define RTS_OFF(fd) EscapeCommFunction(fd,CLRRTS);
185 #define DTR_ON(fd) EscapeCommFunction(fd,SETDTR);
186 #define DTR_OFF(fd) EscapeCommFunction(fd,CLRDTR);
187
188 #else
189
190 #define RTS_ON(fd) { int stuff; ioctl (fd, TIOCMGET, &stuff); stuff |= TIOCM_RTS; ioctl (fd, TIOCMSET, &stuff); }
191 #define RTS_OFF(fd) { int stuff; ioctl (fd, TIOCMGET, &stuff); stuff &= ~TIOCM_RTS; ioctl (fd, TIOCMSET, &stuff); }
192 #define DTR_ON(fd) { int stuff; ioctl (fd, TIOCMGET, &stuff); stuff |= TIOCM_DTR; ioctl (fd, TIOCMSET, &stuff); }
193 #define DTR_OFF(fd) { int stuff; ioctl (fd, TIOCMGET, &stuff); stuff &= ~TIOCM_DTR; ioctl (fd, TIOCMSET, &stuff); }
194
195 #define LPT_IO_ADDR 0x378
196
197 #endif
198
199
200
201 static struct audio_s *save_audio_config_p; /* Save config information for later use. */
202
203 static int ptt_debug_level = 0;
204
ptt_set_debug(int debug)205 void ptt_set_debug(int debug)
206 {
207 ptt_debug_level = debug;
208 }
209
210
211 /*-------------------------------------------------------------------
212 *
213 * Name: get_access_to_gpio
214 *
215 * Purpose: Try to get access to the GPIO device.
216 *
217 * Inputs: path - Path to device node.
218 * /sys/class/gpio/export
219 * /sys/class/gpio/unexport
220 * /sys/class/gpio/gpio??/direction
221 * /sys/class/gpio/gpio??/value
222 *
223 * Description: First see if we have access thru the usual uid/gid/mode method.
224 * If that fails, we try a hack where we use "sudo chmod ..." to open up access.
225 * That requires that sudo be configured to work without a password.
226 * That's the case for 'pi' user in Raspbian but not not be for other boards / operating systems.
227 *
228 * Debug: Use the "-doo" command line option.
229 *
230 *------------------------------------------------------------------*/
231
232 #ifndef __WIN32__
233
234 #define MAX_GROUPS 50
235
236
get_access_to_gpio(const char * path)237 static void get_access_to_gpio (const char *path)
238 {
239
240 static int my_uid = -1;
241 static int my_gid = -1;
242 static gid_t my_groups[MAX_GROUPS];
243 static int num_groups = 0;
244 static int first_time = 1;
245
246 struct stat finfo;
247 int i;
248 char cmd[80];
249 int err;
250
251 /*
252 * Does path even exist?
253 */
254
255 if (stat(path, &finfo) < 0) {
256 text_color_set(DW_COLOR_ERROR);
257 dw_printf ("Can't get properties of %s.\n", path);
258 dw_printf ("This system is not configured with the GPIO user interface.\n");
259 dw_printf ("Use a different method for PTT control.\n");
260 exit (1);
261 }
262
263 if (first_time) {
264
265 // No need to fetch same information each time. Cache it.
266 my_uid = geteuid();
267 my_gid = getegid();
268 num_groups = getgroups (MAX_GROUPS, my_groups);
269
270 if (num_groups < 0) {
271 text_color_set(DW_COLOR_ERROR);
272 dw_printf ("getgroups() failed to get supplementary groups, errno=%d\n", errno);
273 num_groups = 0;
274 }
275 first_time = 0;
276 }
277
278 if (ptt_debug_level >= 2) {
279 text_color_set(DW_COLOR_DEBUG);
280 dw_printf ("%s: uid=%d, gid=%d, mode=o%o\n", path, finfo.st_uid, finfo.st_gid, finfo.st_mode);
281 dw_printf ("my uid=%d, gid=%d, supplementary groups=", my_uid, my_gid);
282 for (i = 0; i < num_groups; i++) {
283 dw_printf (" %d", my_groups[i]);
284 }
285 dw_printf ("\n");
286 }
287
288 /*
289 * Do we have permission to access it?
290 *
291 * On Debian 7 (Wheezy) we see this:
292 *
293 * $ ls -l /sys/class/gpio/export
294 * --w------- 1 root root 4096 Feb 27 12:31 /sys/class/gpio/export
295 *
296 *
297 * Only root can write to it.
298 * Our work-around is change the protection so that everyone can write.
299 * This requires that the current user can use sudo without a password.
300 * This has been the case for the predefined "pi" user but can be a problem
301 * when people add new user names.
302 * Other operating systems could have different default configurations.
303 *
304 * A better solution is available in Debian 8 (Jessie). The group is now "gpio"
305 * so anyone in that group can now write to it.
306 *
307 * $ ls -l /sys/class/gpio/export
308 * -rwxrwx--- 1 root gpio 4096 Mar 4 21:12 /sys/class/gpio/export
309 *
310 *
311 * First see if we can access it by the usual file protection rules.
312 * If not, we will try the "sudo chmod go+rw ..." hack.
313 *
314 */
315
316
317 /*
318 * Do I have access?
319 * We could just try to open for write but this gives us more debugging information.
320 */
321
322 if ((my_uid == finfo.st_uid) && (finfo.st_mode & S_IWUSR)) { // user write 00200
323 if (ptt_debug_level >= 2) {
324 text_color_set(DW_COLOR_DEBUG);
325 dw_printf ("My uid matches and we have user write permission.\n");
326 }
327 return;
328 }
329
330 if ((my_gid == finfo.st_gid) && (finfo.st_mode & S_IWGRP)) { // group write 00020
331 if (ptt_debug_level >= 2) {
332 text_color_set(DW_COLOR_DEBUG);
333 dw_printf ("My primary gid matches and we have group write permission.\n");
334 }
335 return;
336 }
337
338 for (i = 0; i < num_groups; i++) {
339 if ((my_groups[i] == finfo.st_gid) && (finfo.st_mode & S_IWGRP)) { // group write 00020
340 if (ptt_debug_level >= 2) {
341 text_color_set(DW_COLOR_DEBUG);
342 dw_printf ("My supplemental group %d matches and we have group write permission.\n", my_groups[i]);
343 }
344 return;
345 }
346 }
347
348 if (finfo.st_mode & S_IWOTH) { // other write 00002
349 if (ptt_debug_level >= 2) {
350 text_color_set(DW_COLOR_DEBUG);
351 dw_printf ("We have other write permission.\n");
352 }
353 return;
354 }
355
356 /*
357 * We don't have permission.
358 * Try a hack which requires that the user be set up to use sudo without a password.
359 */
360
361 if (ptt_debug_level >= 2) {
362 text_color_set(DW_COLOR_ERROR); // debug message but different color so it stands out.
363 dw_printf ("Trying 'sudo chmod go+rw %s' hack.\n", path);
364 }
365
366 snprintf (cmd, sizeof(cmd), "sudo chmod go+rw %s", path);
367 err = system (cmd);
368 (void)err; // suppress warning about not using result.
369
370 /*
371 * I don't trust status coming back from system() so we will check the mode again.
372 */
373
374 if (stat(path, &finfo) < 0) {
375 /* Unexpected because we could do it before. */
376 text_color_set(DW_COLOR_ERROR);
377 dw_printf ("This system is not configured with the GPIO user interface.\n");
378 dw_printf ("Use a different method for PTT control.\n");
379 exit (1);
380 }
381
382 /* Did we succeed in changing the protection? */
383
384 if ( (finfo.st_mode & 0266) != 0266) {
385 text_color_set(DW_COLOR_ERROR);
386 dw_printf ("You don't have the necessary permission to access GPIO.\n");
387 dw_printf ("There are three different solutions: \n");
388 dw_printf (" 1. Run as root. (not recommended)\n");
389 dw_printf (" 2. If operating system has 'gpio' group, add your user id to it.\n");
390 dw_printf (" 3. Configure your user id for sudo without a password.\n");
391 dw_printf ("\n");
392 dw_printf ("Read the documentation and try -doo command line option for debugging details.\n");
393 exit (1);
394 }
395
396 }
397
398 #endif
399
400
401
402 /*-------------------------------------------------------------------
403 *
404 * Name: export_gpio
405 *
406 * Purpose: Tell the GPIO subsystem to export a GPIO line for
407 * us to use, and set the initial state of the GPIO.
408 *
409 * Inputs: ch - Radio Channel.
410 * ot - Output type.
411 * invert: - Is the GPIO active low?
412 * direction: - 0 for input, 1 for output
413 *
414 * Outputs: out_gpio_name - in the audio configuration structure.
415 * in_gpio_name
416 *
417 *------------------------------------------------------------------*/
418
419 #ifndef __WIN32__
420
421
export_gpio(int ch,int ot,int invert,int direction)422 void export_gpio(int ch, int ot, int invert, int direction)
423 {
424 HANDLE fd;
425 const char gpio_export_path[] = "/sys/class/gpio/export";
426 char gpio_direction_path[80];
427 char gpio_value_path[80];
428 char stemp[16];
429 int gpio_num;
430 char *gpio_name;
431
432 // Raspberry Pi was easy. GPIO 24 has the name gpio24.
433 // Others, such as the Cubieboard, take a little more effort.
434 // The name might be gpio24_ph11 meaning connector H, pin 11.
435 // When we "export" GPIO number, we will store the corresponding
436 // device name for future use when we want to access it.
437
438 if (direction) {
439 gpio_num = save_audio_config_p->achan[ch].octrl[ot].out_gpio_num;
440 gpio_name = save_audio_config_p->achan[ch].octrl[ot].out_gpio_name;
441 }
442 else {
443 gpio_num = save_audio_config_p->achan[ch].ictrl[ot].in_gpio_num;
444 gpio_name = save_audio_config_p->achan[ch].ictrl[ot].in_gpio_name;
445 }
446
447
448 get_access_to_gpio (gpio_export_path);
449
450 fd = open(gpio_export_path, O_WRONLY);
451 if (fd < 0) {
452 // Not expected. Above should have obtained permission or exited.
453 text_color_set(DW_COLOR_ERROR);
454 dw_printf ("Permissions do not allow access to GPIO.\n");
455 exit (1);
456 }
457
458 snprintf (stemp, sizeof(stemp), "%d", gpio_num);
459 if (write (fd, stemp, strlen(stemp)) != strlen(stemp)) {
460 int e = errno;
461 /* Ignore EBUSY error which seems to mean */
462 /* the device node already exists. */
463 if (e != EBUSY) {
464 text_color_set(DW_COLOR_ERROR);
465 dw_printf ("Error writing \"%s\" to %s, errno=%d\n", stemp, gpio_export_path, e);
466 dw_printf ("%s\n", strerror(e));
467 exit (1);
468 }
469 }
470 /* Wait for udev to adjust permissions after enabling GPIO. */
471 /* https://github.com/wb2osz/direwolf/issues/176 */
472 SLEEP_MS(250);
473 close (fd);
474
475 /*
476 * Added in release 1.4.
477 *
478 * On the RPi, the device path for GPIO number XX is simply /sys/class/gpio/gpioXX.
479 *
480 * There was a report that it is different for the CubieBoard. For instance
481 * GPIO 61 has gpio61_pi13 in the path. This indicates connector "i" pin 13.
482 * https://github.com/cubieplayer/Cubian/wiki/GPIO-Introduction
483 *
484 * For another similar single board computer, we find the same thing:
485 * https://www.olimex.com/wiki/A20-OLinuXino-LIME#GPIO_under_Linux
486 *
487 * How should we deal with this? Some possibilities:
488 *
489 * (1) The user might explicitly mention the name in direwolf.conf.
490 * (2) We might be able to find the names in some system device config file.
491 * (3) Get a directory listing of /sys/class/gpio then search for a
492 * matching name. Suppose we wanted GPIO 61. First look for an exact
493 * match to "gpio61". If that is not found, look for something
494 * matching the pattern "gpio61_*".
495 *
496 * We are finally implementing the third choice.
497 */
498
499 struct dirent **file_list;
500 int num_files;
501 int i;
502 int ok = 0;
503
504 if (ptt_debug_level >= 2) {
505 text_color_set(DW_COLOR_DEBUG);
506 dw_printf ("Contents of /sys/class/gpio:\n");
507 }
508
509 num_files = scandir ("/sys/class/gpio", &file_list, NULL, alphasort);
510
511 if (num_files < 0) {
512 // Something went wrong. Fill in the simple expected name and keep going.
513
514 text_color_set(DW_COLOR_ERROR);
515 dw_printf ("ERROR! Could not get directory listing for /sys/class/gpio\n");
516
517 snprintf (gpio_name, MAX_GPIO_NAME_LEN, "gpio%d", gpio_num);
518 num_files = 0;
519 ok = 1;
520 }
521 else {
522
523 if (ptt_debug_level >= 2) {
524
525 text_color_set(DW_COLOR_DEBUG);
526
527 for (i = 0; i < num_files; i++) {
528 dw_printf("\t%s\n", file_list[i]->d_name);
529 }
530 }
531
532 // Look for exact name gpioNN
533
534 char lookfor[16];
535 snprintf (lookfor, sizeof(lookfor), "gpio%d", gpio_num);
536
537 for (i = 0; i < num_files && ! ok; i++) {
538 if (strcmp(lookfor, file_list[i]->d_name) == 0) {
539 strlcpy (gpio_name, file_list[i]->d_name, MAX_GPIO_NAME_LEN);
540 ok = 1;
541 }
542 }
543
544 // If not found, Look for gpioNN_*
545
546 snprintf (lookfor, sizeof(lookfor), "gpio%d_", gpio_num);
547
548 for (i = 0; i < num_files && ! ok; i++) {
549 if (strncmp(lookfor, file_list[i]->d_name, strlen(lookfor)) == 0) {
550 strlcpy (gpio_name, file_list[i]->d_name, MAX_GPIO_NAME_LEN);
551 ok = 1;
552 }
553 }
554
555 // Free the storage allocated by scandir().
556
557 for (i = 0; i < num_files; i++) {
558 free (file_list[i]);
559 }
560 free (file_list);
561 }
562
563 /*
564 * We should now have the corresponding node name.
565 */
566 if (ok) {
567
568 if (ptt_debug_level >= 2) {
569 text_color_set(DW_COLOR_DEBUG);
570 dw_printf ("Path for gpio number %d is /sys/class/gpio/%s\n", gpio_num, gpio_name);
571 }
572 }
573 else {
574
575 text_color_set(DW_COLOR_ERROR);
576 dw_printf ("ERROR! Could not find Path for gpio number %d.n", gpio_num);
577 exit (1);
578 }
579
580 /*
581 * Set output direction and initial state
582 */
583
584 snprintf (gpio_direction_path, sizeof(gpio_direction_path), "/sys/class/gpio/%s/direction", gpio_name);
585 get_access_to_gpio (gpio_direction_path);
586
587 fd = open(gpio_direction_path, O_WRONLY);
588 if (fd < 0) {
589 int e = errno;
590 text_color_set(DW_COLOR_ERROR);
591 dw_printf ("Error opening %s\n", stemp);
592 dw_printf ("%s\n", strerror(e));
593 exit (1);
594 }
595
596 char gpio_val[8];
597 if (direction) {
598 if (invert) {
599 strlcpy (gpio_val, "high", sizeof(gpio_val));
600 }
601 else {
602 strlcpy (gpio_val, "low", sizeof(gpio_val));
603 }
604 }
605 else {
606 strlcpy (gpio_val, "in", sizeof(gpio_val));
607 }
608 if (write (fd, gpio_val, strlen(gpio_val)) != strlen(gpio_val)) {
609 int e = errno;
610 text_color_set(DW_COLOR_ERROR);
611 dw_printf ("Error writing initial state to %s\n", stemp);
612 dw_printf ("%s\n", strerror(e));
613 exit (1);
614 }
615 close (fd);
616
617 /*
618 * Make sure that we have access to 'value'.
619 * Do it once here, rather than each time we want to use it.
620 */
621
622 snprintf (gpio_value_path, sizeof(gpio_value_path), "/sys/class/gpio/%s/value", gpio_name);
623 get_access_to_gpio (gpio_value_path);
624 }
625
626 #endif /* not __WIN32__ */
627
628
629 /*-------------------------------------------------------------------
630 *
631 * Name: ptt_init
632 *
633 * Purpose: Open serial port(s) used for PTT signals and set to proper state.
634 *
635 * Inputs: audio_config_p - Structure with communication parameters.
636 *
637 * for each channel we have:
638 *
639 * ptt_method Method for PTT signal.
640 * PTT_METHOD_NONE - not configured. Could be using VOX.
641 * PTT_METHOD_SERIAL - serial (com) port.
642 * PTT_METHOD_GPIO - general purpose I/O.
643 * PTT_METHOD_LPT - Parallel printer port.
644 * PTT_METHOD_HAMLIB - HAMLib rig control.
645 * PTT_METHOD_CM108 - GPIO pins of CM108 etc. USB Audio.
646 *
647 * ptt_device Name of serial port device.
648 * e.g. COM1 or /dev/ttyS0.
649 * HAMLIB can also use hostaddr:port.
650 * Like /dev/hidraw1 for CM108.
651 *
652 * ptt_line RTS or DTR when using serial port.
653 *
654 * out_gpio_num GPIO number. Only used for Linux.
655 * Valid only when ptt_method is PTT_METHOD_GPIO.
656 *
657 * ptt_lpt_bit Bit number for parallel printer port.
658 * Bit 0 = pin 2, ..., bit 7 = pin 9.
659 * Valid only when ptt_method is PTT_METHOD_LPT.
660 *
661 * ptt_invert Invert the signal.
662 * Normally higher voltage means transmit or LED on.
663 *
664 * ptt_model Only for HAMLIB.
665 * 2 to communicate with rigctld.
666 * >= 3 for specific radio model.
667 * -1 guess at what is out there. (AUTO option in config file.)
668 *
669 * Outputs: Remember required information for future use.
670 *
671 * Description:
672 *
673 *--------------------------------------------------------------------*/
674
675
676
677
678 static HANDLE ptt_fd[MAX_CHANS][NUM_OCTYPES];
679 /* Serial port handle or fd. */
680 /* Could be the same for two channels */
681 /* if using both RTS and DTR. */
682 #if USE_HAMLIB
683 static RIG *rig[MAX_CHANS][NUM_OCTYPES];
684 #endif
685
686 static char otnames[NUM_OCTYPES][8];
687
ptt_init(struct audio_s * audio_config_p)688 void ptt_init (struct audio_s *audio_config_p)
689 {
690 int ch;
691 HANDLE fd = INVALID_HANDLE_VALUE;
692 #if __WIN32__
693 #else
694 int using_gpio;
695 #endif
696
697 #if DEBUG
698 text_color_set(DW_COLOR_DEBUG);
699 dw_printf ("ptt_init ( ... )\n");
700 #endif
701
702 save_audio_config_p = audio_config_p;
703
704 strlcpy (otnames[OCTYPE_PTT], "PTT", sizeof(otnames[OCTYPE_PTT]));
705 strlcpy (otnames[OCTYPE_DCD], "DCD", sizeof(otnames[OCTYPE_DCD]));
706 strlcpy (otnames[OCTYPE_CON], "CON", sizeof(otnames[OCTYPE_CON]));
707
708
709 for (ch = 0; ch < MAX_CHANS; ch++) {
710 int ot;
711
712 for (ot = 0; ot < NUM_OCTYPES; ot++) {
713
714 ptt_fd[ch][ot] = INVALID_HANDLE_VALUE;
715 #if USE_HAMLIB
716 rig[ch][ot] = NULL;
717 #endif
718 if (ptt_debug_level >= 2) {
719
720 text_color_set(DW_COLOR_DEBUG);
721 dw_printf ("ch=%d, %s method=%d, device=%s, line=%d, gpio=%d, lpt_bit=%d, invert=%d\n",
722 ch,
723 otnames[ot],
724 audio_config_p->achan[ch].octrl[ot].ptt_method,
725 audio_config_p->achan[ch].octrl[ot].ptt_device,
726 audio_config_p->achan[ch].octrl[ot].ptt_line,
727 audio_config_p->achan[ch].octrl[ot].out_gpio_num,
728 audio_config_p->achan[ch].octrl[ot].ptt_lpt_bit,
729 audio_config_p->achan[ch].octrl[ot].ptt_invert);
730 }
731 }
732 }
733
734 /*
735 * Set up serial ports.
736 */
737
738 for (ch = 0; ch < MAX_CHANS; ch++) {
739
740 if (audio_config_p->achan[ch].medium == MEDIUM_RADIO) {
741 int ot;
742
743 for (ot = 0; ot < NUM_OCTYPES; ot++) {
744
745 if (audio_config_p->achan[ch].octrl[ot].ptt_method == PTT_METHOD_SERIAL) {
746
747 #if __WIN32__
748 #else
749 /* Translate Windows device name into Linux name. */
750 /* COM1 -> /dev/ttyS0, etc. */
751
752 if (strncasecmp(audio_config_p->achan[ch].octrl[ot].ptt_device, "COM", 3) == 0) {
753 int n = atoi (audio_config_p->achan[ch].octrl[ot].ptt_device + 3);
754 text_color_set(DW_COLOR_INFO);
755 dw_printf ("Converted %s device '%s'", audio_config_p->achan[ch].octrl[ot].ptt_device, otnames[ot]);
756 if (n < 1) n = 1;
757 snprintf (audio_config_p->achan[ch].octrl[ot].ptt_device, sizeof(audio_config_p->achan[ch].octrl[ot].ptt_device), "/dev/ttyS%d", n-1);
758 dw_printf (" to Linux equivalent '%s'\n", audio_config_p->achan[ch].octrl[ot].ptt_device);
759 }
760 #endif
761 /* Can't open the same device more than once so we */
762 /* need more logic to look for the case of multiple radio */
763 /* channels using different pins of the same COM port. */
764
765 /* Did some earlier channel use the same device name? */
766
767 int same_device_used = 0;
768 int j, k;
769
770 for (j = ch; j >= 0; j--) {
771 if (audio_config_p->achan[j].medium == MEDIUM_RADIO) {
772 for (k = ((j==ch) ? (ot - 1) : (NUM_OCTYPES-1)); k >= 0; k--) {
773 if (strcmp(audio_config_p->achan[ch].octrl[ot].ptt_device,audio_config_p->achan[j].octrl[k].ptt_device) == 0) {
774 fd = ptt_fd[j][k];
775 same_device_used = 1;
776 }
777 }
778 }
779 }
780
781 if ( ! same_device_used) {
782
783 #if __WIN32__
784 char bettername[50];
785 // Bug fix in release 1.1 - Need to munge name for COM10 and up.
786 // http://support.microsoft.com/kb/115831
787
788 strlcpy (bettername, audio_config_p->achan[ch].octrl[ot].ptt_device, sizeof(bettername));
789 if (strncasecmp(bettername, "COM", 3) == 0) {
790 int n;
791 n = atoi(bettername+3);
792 if (n >= 10) {
793 strlcpy (bettername, "\\\\.\\", sizeof(bettername));
794 strlcat (bettername, audio_config_p->achan[ch].octrl[ot].ptt_device, sizeof(bettername));
795 }
796 }
797 fd = CreateFile(bettername,
798 GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
799 #else
800
801 /* O_NONBLOCK added in version 0.9. */
802 /* Was hanging with some USB-serial adapters. */
803 /* https://bugs.launchpad.net/ubuntu/+source/linux/+bug/661321/comments/12 */
804
805 fd = open (audio_config_p->achan[ch].octrl[ot].ptt_device, O_RDONLY | O_NONBLOCK);
806 #endif
807 }
808
809 if (fd != INVALID_HANDLE_VALUE) {
810 ptt_fd[ch][ot] = fd;
811 }
812 else {
813 #if __WIN32__
814 #else
815 int e = errno;
816 #endif
817 text_color_set(DW_COLOR_ERROR);
818 dw_printf ("ERROR can't open device %s for channel %d PTT control.\n",
819 audio_config_p->achan[ch].octrl[ot].ptt_device, ch);
820 #if __WIN32__
821 #else
822 dw_printf ("%s\n", strerror(e));
823 #endif
824 /* Don't try using it later if device open failed. */
825
826 audio_config_p->achan[ch].octrl[ot].ptt_method = PTT_METHOD_NONE;
827 }
828
829 /*
830 * Set initial state off.
831 * ptt_set will invert output signal if appropriate.
832 */
833 ptt_set (ot, ch, 0);
834
835 } /* if serial method. */
836 } /* for each output type. */
837 } /* if channel valid. */
838 } /* For each channel. */
839
840
841 /*
842 * Set up GPIO - for Linux only.
843 */
844
845 #if __WIN32__
846 #else
847
848 /*
849 * Does any of them use GPIO?
850 */
851
852 using_gpio = 0;
853 for (ch=0; ch<MAX_CHANS; ch++) {
854 if (save_audio_config_p->achan[ch].medium == MEDIUM_RADIO) {
855 int ot;
856 for (ot = 0; ot < NUM_OCTYPES; ot++) {
857 if (audio_config_p->achan[ch].octrl[ot].ptt_method == PTT_METHOD_GPIO) {
858 using_gpio = 1;
859 }
860 }
861 for (ot = 0; ot < NUM_ICTYPES; ot++) {
862 if (audio_config_p->achan[ch].ictrl[ot].method == PTT_METHOD_GPIO) {
863 using_gpio = 1;
864 }
865 }
866 }
867 }
868
869 if (using_gpio) {
870 get_access_to_gpio ("/sys/class/gpio/export");
871 }
872
873 /*
874 * We should now be able to create the device nodes for
875 * the pins we want to use.
876 */
877
878 for (ch = 0; ch < MAX_CHANS; ch++) {
879 if (save_audio_config_p->achan[ch].medium == MEDIUM_RADIO) {
880
881 int ot; // output control type, PTT, DCD, CON, ...
882 int it; // input control type
883
884 for (ot = 0; ot < NUM_OCTYPES; ot++) {
885 if (audio_config_p->achan[ch].octrl[ot].ptt_method == PTT_METHOD_GPIO) {
886 export_gpio(ch, ot, audio_config_p->achan[ch].octrl[ot].ptt_invert, 1);
887 }
888 }
889 for (it = 0; it < NUM_ICTYPES; it++) {
890 if (audio_config_p->achan[ch].ictrl[it].method == PTT_METHOD_GPIO) {
891 export_gpio(ch, it, audio_config_p->achan[ch].ictrl[it].invert, 0);
892 }
893 }
894 }
895 }
896 #endif
897
898
899
900 /*
901 * Set up parallel printer port.
902 *
903 * Restrictions:
904 * Only the primary printer port.
905 * For x86 Linux only.
906 */
907
908 #if ( defined(__i386__) || defined(__x86_64__) ) && ( defined(__linux__) || defined(__unix__) )
909
910 for (ch = 0; ch < MAX_CHANS; ch++) {
911 if (save_audio_config_p->achan[ch].medium == MEDIUM_RADIO) {
912 int ot;
913 for (ot = 0; ot < NUM_OCTYPES; ot++) {
914 if (audio_config_p->achan[ch].octrl[ot].ptt_method == PTT_METHOD_LPT) {
915
916 /* Can't open the same device more than once so we */
917 /* need more logic to look for the case of mutiple radio */
918 /* channels using different pins of the LPT port. */
919
920 /* Did some earlier channel use the same ptt device name? */
921
922 int same_device_used = 0;
923 int j, k;
924
925 for (j = ch; j >= 0; j--) {
926 if (audio_config_p->achan[j].medium == MEDIUM_RADIO) {
927 for (k = ((j==ch) ? (ot - 1) : (NUM_OCTYPES-1)); k >= 0; k--) {
928 if (strcmp(audio_config_p->achan[ch].octrl[ot].ptt_device,audio_config_p->achan[j].octrl[k].ptt_device) == 0) {
929 fd = ptt_fd[j][k];
930 same_device_used = 1;
931 }
932 }
933 }
934 }
935
936 if ( ! same_device_used) {
937 fd = open ("/dev/port", O_RDWR | O_NDELAY);
938 }
939
940 if (fd != INVALID_HANDLE_VALUE) {
941 ptt_fd[ch][ot] = fd;
942 }
943 else {
944
945 int e = errno;
946
947 text_color_set(DW_COLOR_ERROR);
948 dw_printf ("ERROR - Can't open /dev/port for parallel printer port PTT control.\n");
949 dw_printf ("%s\n", strerror(e));
950 dw_printf ("You probably don't have adequate permissions to access I/O ports.\n");
951 dw_printf ("Either run direwolf as root or change these permissions:\n");
952 dw_printf (" sudo chmod go+rw /dev/port\n");
953 dw_printf (" sudo setcap cap_sys_rawio=ep `which direwolf`\n");
954
955 /* Don't try using it later if device open failed. */
956
957 audio_config_p->achan[ch].octrl[ot].ptt_method = PTT_METHOD_NONE;
958 }
959
960
961 /*
962 * Set initial state off.
963 * ptt_set will invert output signal if appropriate.
964 */
965 ptt_set (ot, ch, 0);
966
967 } /* if parallel printer port method. */
968 } /* for each output type */
969 } /* if valid channel. */
970 } /* For each channel. */
971
972
973
974 #endif /* x86 Linux */
975
976 #ifdef USE_HAMLIB
977 for (ch = 0; ch < MAX_CHANS; ch++) {
978 if (save_audio_config_p->achan[ch].medium == MEDIUM_RADIO) {
979 int ot;
980 for (ot = 0; ot < NUM_OCTYPES; ot++) {
981 if (audio_config_p->achan[ch].octrl[ot].ptt_method == PTT_METHOD_HAMLIB) {
982 if (ot == OCTYPE_PTT) {
983
984 /* For "AUTO" model, try to guess what is out there. */
985
986 if (audio_config_p->achan[ch].octrl[ot].ptt_model == -1) {
987 hamlib_port_t hport; // http://hamlib.sourceforge.net/manuals/1.2.15/structhamlib__port__t.html
988
989 memset (&hport, 0, sizeof(hport));
990 strlcpy (hport.pathname, audio_config_p->achan[ch].octrl[ot].ptt_device, sizeof(hport.pathname));
991
992 if (audio_config_p->achan[ch].octrl[ot].ptt_rate > 0) {
993 // Override the default serial port data rate.
994 hport.parm.serial.rate = audio_config_p->achan[ch].octrl[ot].ptt_rate;
995 hport.parm.serial.data_bits = 8;
996 hport.parm.serial.stop_bits = 1;
997 hport.parm.serial.parity = RIG_PARITY_NONE;
998 hport.parm.serial.handshake = RIG_HANDSHAKE_NONE;
999 }
1000
1001 rig_load_all_backends();
1002 audio_config_p->achan[ch].octrl[ot].ptt_model = rig_probe(&hport);
1003
1004 if (audio_config_p->achan[ch].octrl[ot].ptt_model == RIG_MODEL_NONE) {
1005 text_color_set(DW_COLOR_ERROR);
1006 dw_printf ("Hamlib Error: Couldn't guess rig model number for AUTO option. Run \"rigctl --list\" for a list of model numbers.\n");
1007 continue;
1008 }
1009
1010 text_color_set(DW_COLOR_INFO);
1011 dw_printf ("Hamlib AUTO option detected rig model %d. Run \"rigctl --list\" for a list of model numbers.\n",
1012 audio_config_p->achan[ch].octrl[ot].ptt_model);
1013 }
1014
1015 rig[ch][ot] = rig_init(audio_config_p->achan[ch].octrl[ot].ptt_model);
1016 if (rig[ch][ot] == NULL) {
1017 text_color_set(DW_COLOR_ERROR);
1018 dw_printf ("Hamlib error: Unknown rig model %d. Run \"rigctl --list\" for a list of model numbers.\n",
1019 audio_config_p->achan[ch].octrl[ot].ptt_model);
1020 continue;
1021 }
1022
1023 strlcpy (rig[ch][ot]->state.rigport.pathname, audio_config_p->achan[ch].octrl[ot].ptt_device, sizeof(rig[ch][ot]->state.rigport.pathname));
1024
1025 // Issue 290.
1026 // We had a case where hamlib defaulted to 9600 baud for a particular
1027 // radio model but 38400 was needed. Add an option for the configuration
1028 // file to override the hamlib default speed.
1029
1030 text_color_set(DW_COLOR_INFO);
1031 if (audio_config_p->achan[ch].octrl[ot].ptt_model != 2) { // 2 is network, not serial port.
1032 dw_printf ("Hamlib determined CAT control serial port rate of %d.\n", rig[ch][ot]->state.rigport.parm.serial.rate);
1033 }
1034
1035 // Config file can optionally override the rate that hamlib came up with.
1036
1037 if (audio_config_p->achan[ch].octrl[ot].ptt_rate > 0) {
1038 dw_printf ("User configuration overriding hamlib CAT control speed to %d.\n", audio_config_p->achan[ch].octrl[ot].ptt_rate);
1039 rig[ch][ot]->state.rigport.parm.serial.rate = audio_config_p->achan[ch].octrl[ot].ptt_rate;
1040
1041 // Do we want to explicitly set all of these or let it default?
1042 rig[ch][ot]->state.rigport.parm.serial.data_bits = 8;
1043 rig[ch][ot]->state.rigport.parm.serial.stop_bits = 1;
1044 rig[ch][ot]->state.rigport.parm.serial.parity = RIG_PARITY_NONE;
1045 rig[ch][ot]->state.rigport.parm.serial.handshake = RIG_HANDSHAKE_NONE;
1046 }
1047 int err = rig_open(rig[ch][ot]);
1048 if (err != RIG_OK) {
1049 text_color_set(DW_COLOR_ERROR);
1050 dw_printf ("Hamlib Rig open error %d: %s\n", err, rigerror(err));
1051 rig_cleanup (rig[ch][ot]);
1052 rig[ch][ot] = NULL;
1053 continue;
1054 }
1055
1056 /* Successful. Later code should check for rig[ch][ot] not NULL. */
1057 }
1058 else {
1059 text_color_set(DW_COLOR_ERROR);
1060 dw_printf ("HAMLIB can only be used for PTT. Not DCD or other output.\n");
1061 }
1062 }
1063 }
1064 }
1065 }
1066
1067 #endif
1068
1069 /*
1070 * Confirm what is going on with CM108 GPIO output.
1071 * Could use some error checking for overlap.
1072 */
1073
1074 #if USE_CM108
1075
1076 for (ch = 0; ch < MAX_CHANS; ch++) {
1077
1078 if (audio_config_p->achan[ch].medium == MEDIUM_RADIO) {
1079 int ot;
1080 for (ot = 0; ot < NUM_OCTYPES; ot++) {
1081 if (audio_config_p->achan[ch].octrl[ot].ptt_method == PTT_METHOD_CM108) {
1082 text_color_set(DW_COLOR_INFO);
1083 dw_printf ("Using %s GPIO %d for channel %d %s control.\n",
1084 audio_config_p->achan[ch].octrl[ot].ptt_device,
1085 audio_config_p->achan[ch].octrl[ot].out_gpio_num,
1086 ch,
1087 otnames[ot]);
1088 }
1089 }
1090 }
1091 }
1092
1093 #endif
1094
1095
1096 /* Why doesn't it transmit? Probably forgot to specify PTT option. */
1097
1098 for (ch=0; ch<MAX_CHANS; ch++) {
1099 if (audio_config_p->achan[ch].medium == MEDIUM_RADIO) {
1100 if(audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_method == PTT_METHOD_NONE) {
1101 text_color_set(DW_COLOR_INFO);
1102 dw_printf ("Note: PTT not configured for channel %d. (Ignore this if using VOX.)\n", ch);
1103 }
1104 }
1105 }
1106
1107 } /* end ptt_init */
1108
1109
1110 /*-------------------------------------------------------------------
1111 *
1112 * Name: ptt_set
1113 *
1114 * Purpose: Turn output control line on or off.
1115 * Originally this was just for PTT, hence the name.
1116 * Now that it is more general purpose, it should
1117 * probably be renamed something like octrl_set.
1118 *
1119 * Inputs: ot - Output control type:
1120 * OCTYPE_PTT, OCTYPE_DCD, OCTYPE_FUTURE
1121 *
1122 * chan - channel, 0 .. (number of channels)-1
1123 *
1124 * ptt_signal - 1 for transmit, 0 for receive.
1125 *
1126 *
1127 * Assumption: ptt_init was called first.
1128 *
1129 * Description: Set the RTS or DTR line or GPIO pin.
1130 * More positive output corresponds to 1 unless invert is set.
1131 *
1132 *--------------------------------------------------------------------*/
1133
1134
ptt_set(int ot,int chan,int ptt_signal)1135 void ptt_set (int ot, int chan, int ptt_signal)
1136 {
1137
1138 int ptt = ptt_signal;
1139 int ptt2 = ptt_signal;
1140
1141 assert (ot >= 0 && ot < NUM_OCTYPES);
1142 assert (chan >= 0 && chan < MAX_CHANS);
1143
1144 if (ptt_debug_level >= 1) {
1145 text_color_set(DW_COLOR_DEBUG);
1146 dw_printf ("%s %d = %d\n", otnames[ot], chan, ptt_signal);
1147 }
1148
1149 assert (chan >= 0 && chan < MAX_CHANS);
1150
1151 if ( save_audio_config_p->achan[chan].medium != MEDIUM_RADIO) {
1152 text_color_set(DW_COLOR_ERROR);
1153 dw_printf ("Internal error, ptt_set ( %s, %d, %d ), did not expect invalid channel.\n", otnames[ot], chan, ptt);
1154 return;
1155 }
1156
1157 /*
1158 * The data link state machine has an interest in activity on the radio channel.
1159 * This is a very convenient place to get that information.
1160 */
1161
1162 #ifndef TEST
1163 dlq_channel_busy (chan, ot, ptt_signal);
1164 #endif
1165
1166 /*
1167 * Inverted output?
1168 */
1169
1170 if (save_audio_config_p->achan[chan].octrl[ot].ptt_invert) {
1171 ptt = ! ptt;
1172 }
1173 if (save_audio_config_p->achan[chan].octrl[ot].ptt_invert2) {
1174 ptt2 = ! ptt2;
1175 }
1176
1177 /*
1178 * Using serial port?
1179 */
1180 if (save_audio_config_p->achan[chan].octrl[ot].ptt_method == PTT_METHOD_SERIAL &&
1181 ptt_fd[chan][ot] != INVALID_HANDLE_VALUE) {
1182
1183 if (save_audio_config_p->achan[chan].octrl[ot].ptt_line == PTT_LINE_RTS) {
1184
1185 if (ptt) {
1186 RTS_ON(ptt_fd[chan][ot]);
1187 }
1188 else {
1189 RTS_OFF(ptt_fd[chan][ot]);
1190 }
1191 }
1192 else if (save_audio_config_p->achan[chan].octrl[ot].ptt_line == PTT_LINE_DTR) {
1193
1194 if (ptt) {
1195 DTR_ON(ptt_fd[chan][ot]);
1196 }
1197 else {
1198 DTR_OFF(ptt_fd[chan][ot]);
1199 }
1200 }
1201
1202 /*
1203 * Second serial port control line? Typically driven with opposite phase but could be in phase.
1204 */
1205
1206 if (save_audio_config_p->achan[chan].octrl[ot].ptt_line2 == PTT_LINE_RTS) {
1207
1208 if (ptt2) {
1209 RTS_ON(ptt_fd[chan][ot]);
1210 }
1211 else {
1212 RTS_OFF(ptt_fd[chan][ot]);
1213 }
1214 }
1215 else if (save_audio_config_p->achan[chan].octrl[ot].ptt_line2 == PTT_LINE_DTR) {
1216
1217 if (ptt2) {
1218 DTR_ON(ptt_fd[chan][ot]);
1219 }
1220 else {
1221 DTR_OFF(ptt_fd[chan][ot]);
1222 }
1223 }
1224 /* else neither one */
1225
1226 }
1227
1228 /*
1229 * Using GPIO?
1230 */
1231
1232 #if __WIN32__
1233 #else
1234
1235 if (save_audio_config_p->achan[chan].octrl[ot].ptt_method == PTT_METHOD_GPIO) {
1236 int fd;
1237 char gpio_value_path[80];
1238 char stemp[16];
1239
1240 snprintf (gpio_value_path, sizeof(gpio_value_path), "/sys/class/gpio/%s/value", save_audio_config_p->achan[chan].octrl[ot].out_gpio_name);
1241
1242 fd = open(gpio_value_path, O_WRONLY);
1243 if (fd < 0) {
1244 int e = errno;
1245 text_color_set(DW_COLOR_ERROR);
1246 dw_printf ("Error opening %s to set %s signal.\n", stemp, otnames[ot]);
1247 dw_printf ("%s\n", strerror(e));
1248 return;
1249 }
1250
1251 snprintf (stemp, sizeof(stemp), "%d", ptt);
1252
1253 if (write (fd, stemp, 1) != 1) {
1254 int e = errno;
1255 text_color_set(DW_COLOR_ERROR);
1256 dw_printf ("Error setting GPIO %d for %s\n", save_audio_config_p->achan[chan].octrl[ot].out_gpio_num, otnames[ot]);
1257 dw_printf ("%s\n", strerror(e));
1258 }
1259 close (fd);
1260
1261 }
1262 #endif
1263
1264 /*
1265 * Using parallel printer port?
1266 */
1267
1268 #if ( defined(__i386__) || defined(__x86_64__) ) && ( defined(__linux__) || defined(__unix__) )
1269
1270 if (save_audio_config_p->achan[chan].octrl[ot].ptt_method == PTT_METHOD_LPT &&
1271 ptt_fd[chan][ot] != INVALID_HANDLE_VALUE) {
1272
1273 char lpt_data;
1274 //ssize_t n;
1275
1276 lseek (ptt_fd[chan][ot], (off_t)LPT_IO_ADDR, SEEK_SET);
1277 if (read (ptt_fd[chan][ot], &lpt_data, (size_t)1) != 1) {
1278 int e = errno;
1279 text_color_set(DW_COLOR_ERROR);
1280 dw_printf ("Error reading current state of LPT for channel %d %s\n", chan, otnames[ot]);
1281 dw_printf ("%s\n", strerror(e));
1282 }
1283
1284 if (ptt) {
1285 lpt_data |= ( 1 << save_audio_config_p->achan[chan].octrl[ot].ptt_lpt_bit );
1286 }
1287 else {
1288 lpt_data &= ~ ( 1 << save_audio_config_p->achan[chan].octrl[ot].ptt_lpt_bit );
1289 }
1290
1291 lseek (ptt_fd[chan][ot], (off_t)LPT_IO_ADDR, SEEK_SET);
1292 if (write (ptt_fd[chan][ot], &lpt_data, (size_t)1) != 1) {
1293 int e = errno;
1294 text_color_set(DW_COLOR_ERROR);
1295 dw_printf ("Error writing to LPT for channel %d %s\n", chan, otnames[ot]);
1296 dw_printf ("%s\n", strerror(e));
1297 }
1298 }
1299
1300 #endif /* x86 Linux */
1301
1302 #ifdef USE_HAMLIB
1303 /*
1304 * Using hamlib?
1305 */
1306
1307 if (save_audio_config_p->achan[chan].octrl[ot].ptt_method == PTT_METHOD_HAMLIB) {
1308
1309 if (rig[chan][ot] != NULL) {
1310
1311 int retcode = rig_set_ptt(rig[chan][ot], RIG_VFO_CURR, ptt ? RIG_PTT_ON : RIG_PTT_OFF);
1312
1313 if (retcode != RIG_OK) {
1314 text_color_set(DW_COLOR_ERROR);
1315 dw_printf ("Hamlib Error: rig_set_ptt command for channel %d %s\n", chan, otnames[ot]);
1316 dw_printf ("%s\n", rigerror(retcode));
1317 }
1318 }
1319 else {
1320 text_color_set(DW_COLOR_ERROR);
1321 dw_printf ("Hamlib: Can't use rig_set_ptt for channel %d %s because rig_open failed.\n", chan, otnames[ot]);
1322 }
1323 }
1324 #endif
1325
1326 /*
1327 * Using CM108 USB Audio adapter GPIO?
1328 */
1329
1330 #ifdef USE_CM108
1331
1332 if (save_audio_config_p->achan[chan].octrl[ot].ptt_method == PTT_METHOD_CM108) {
1333
1334 if (cm108_set_gpio_pin (save_audio_config_p->achan[chan].octrl[ot].ptt_device,
1335 save_audio_config_p->achan[chan].octrl[ot].out_gpio_num, ptt) != 0) {
1336 text_color_set(DW_COLOR_ERROR);
1337 dw_printf ("ERROR: %s for channel %d has failed. See User Guide for troubleshooting tips.\n", otnames[ot], chan);
1338 }
1339 }
1340 #endif
1341
1342 } /* end ptt_set */
1343
1344 /*-------------------------------------------------------------------
1345 *
1346 * Name: get_input
1347 *
1348 * Purpose: Read the value of an input line
1349 *
1350 * Inputs: it - Input type (ICTYPE_TCINH supported so far)
1351 * chan - Audio channel number
1352 *
1353 * Outputs: 0 = inactive, 1 = active, -1 = error
1354 *
1355 * ------------------------------------------------------------------*/
1356
get_input(int it,int chan)1357 int get_input (int it, int chan)
1358 {
1359 assert (it >= 0 && it < NUM_ICTYPES);
1360 assert (chan >= 0 && chan < MAX_CHANS);
1361
1362 if ( save_audio_config_p->achan[chan].medium != MEDIUM_RADIO) {
1363 text_color_set(DW_COLOR_ERROR);
1364 dw_printf ("Internal error, get_input ( %d, %d ), did not expect invalid channel.\n", it, chan);
1365 return -1;
1366 }
1367
1368 #if __WIN32__
1369 #else
1370 if (save_audio_config_p->achan[chan].ictrl[it].method == PTT_METHOD_GPIO) {
1371 int fd;
1372 char gpio_value_path[80];
1373
1374 snprintf (gpio_value_path, sizeof(gpio_value_path), "/sys/class/gpio/%s/value", save_audio_config_p->achan[chan].ictrl[it].in_gpio_name);
1375
1376 get_access_to_gpio (gpio_value_path);
1377
1378 fd = open(gpio_value_path, O_RDONLY);
1379 if (fd < 0) {
1380 int e = errno;
1381 text_color_set(DW_COLOR_ERROR);
1382 dw_printf ("Error opening %s to check input.\n", gpio_value_path);
1383 dw_printf ("%s\n", strerror(e));
1384 return -1;
1385 }
1386
1387 char vtemp[2];
1388 if (read (fd, vtemp, 1) != 1) {
1389 int e = errno;
1390 text_color_set(DW_COLOR_ERROR);
1391 dw_printf ("Error getting GPIO %d value\n", save_audio_config_p->achan[chan].ictrl[it].in_gpio_num);
1392 dw_printf ("%s\n", strerror(e));
1393 }
1394 close (fd);
1395
1396 vtemp[1] = '\0';
1397 if (atoi(vtemp) != save_audio_config_p->achan[chan].ictrl[it].invert) {
1398 return 1;
1399 }
1400 else {
1401 return 0;
1402 }
1403 }
1404 #endif
1405
1406 return -1; /* Method was none, or something went wrong */
1407 }
1408
1409 /*-------------------------------------------------------------------
1410 *
1411 * Name: ptt_term
1412 *
1413 * Purpose: Make sure PTT and others are turned off when we exit.
1414 *
1415 * Inputs: none
1416 *
1417 * Description:
1418 *
1419 *--------------------------------------------------------------------*/
1420
ptt_term(void)1421 void ptt_term (void)
1422 {
1423 int n;
1424
1425 for (n = 0; n < MAX_CHANS; n++) {
1426 if (save_audio_config_p->achan[n].medium == MEDIUM_RADIO) {
1427 int ot;
1428 for (ot = 0; ot < NUM_OCTYPES; ot++) {
1429 ptt_set (ot, n, 0);
1430 }
1431 }
1432 }
1433
1434 for (n = 0; n < MAX_CHANS; n++) {
1435 if (save_audio_config_p->achan[n].medium == MEDIUM_RADIO) {
1436 int ot;
1437 for (ot = 0; ot < NUM_OCTYPES; ot++) {
1438 if (ptt_fd[n][ot] != INVALID_HANDLE_VALUE) {
1439 #if __WIN32__
1440 CloseHandle (ptt_fd[n][ot]);
1441 #else
1442 close(ptt_fd[n][ot]);
1443 #endif
1444 ptt_fd[n][ot] = INVALID_HANDLE_VALUE;
1445 }
1446 }
1447 }
1448 }
1449
1450 #ifdef USE_HAMLIB
1451
1452 for (n = 0; n < MAX_CHANS; n++) {
1453 if (save_audio_config_p->achan[n].medium == MEDIUM_RADIO) {
1454 int ot;
1455 for (ot = 0; ot < NUM_OCTYPES; ot++) {
1456 if (rig[n][ot] != NULL) {
1457
1458 rig_close(rig[n][ot]);
1459 rig_cleanup(rig[n][ot]);
1460 rig[n][ot] = NULL;
1461 }
1462 }
1463 }
1464 }
1465 #endif
1466 }
1467
1468
1469
1470
1471 /*
1472 * Quick stand-alone test for above.
1473 *
1474 * gcc -DTEST -o ptest ptt.c textcolor.o misc.a ; ./ptest
1475 *
1476 * TODO: Retest this, add CM108 GPIO to test.
1477 */
1478
1479
1480 #if TEST
1481
main()1482 int main ()
1483 {
1484 struct audio_s my_audio_config;
1485 int n;
1486 int chan;
1487
1488 memset (&my_audio_config, 0, sizeof(my_audio_config));
1489
1490 my_audio_config.adev[0].num_channels = 2;
1491
1492 my_audio_config.achan[0].medium = MEDIUM_RADIO;
1493 my_audio_config.achan[0].octrl[OCTYPE_PTT].ptt_method = PTT_METHOD_SERIAL;
1494 // TODO: device should be command line argument.
1495 strlcpy (my_audio_config.achan[0].octrl[OCTYPE_PTT].ptt_device, "COM3", sizeof(my_audio_config.achan[0].octrl[OCTYPE_PTT].ptt_device));
1496 //strlcpy (my_audio_config.achan[0].octrl[OCTYPE_PTT].ptt_device, "/dev/ttyUSB0", sizeof(my_audio_config.achan[0].octrl[OCTYPE_PTT].ptt_device));
1497 my_audio_config.achan[0].octrl[OCTYPE_PTT].ptt_line = PTT_LINE_RTS;
1498
1499 my_audio_config.achan[1].medium = MEDIUM_RADIO;
1500 my_audio_config.achan[1].octrl[OCTYPE_PTT].ptt_method = PTT_METHOD_SERIAL;
1501 strlcpy (my_audio_config.achan[1].octrl[OCTYPE_PTT].ptt_device, "COM3", sizeof(my_audio_config.achan[1].octrl[OCTYPE_PTT].ptt_device));
1502 //strlcpy (my_audio_config.achan[1].octrl[OCTYPE_PTT].ptt_device, "/dev/ttyUSB0", sizeof(my_audio_config.achan[1].octrl[OCTYPE_PTT].ptt_device));
1503 my_audio_config.achan[1].octrl[OCTYPE_PTT].ptt_line = PTT_LINE_DTR;
1504
1505
1506 /* initialize - both off */
1507
1508 ptt_init (&my_audio_config);
1509
1510 SLEEP_SEC(2);
1511
1512 /* flash each a few times. */
1513
1514 dw_printf ("turn on RTS a few times...\n");
1515
1516 chan = 0;
1517 for (n=0; n<3; n++) {
1518 ptt_set (OCTYPE_PTT, chan, 1);
1519 SLEEP_SEC(1);
1520 ptt_set (OCTYPE_PTT, chan, 0);
1521 SLEEP_SEC(1);
1522 }
1523
1524 dw_printf ("turn on DTR a few times...\n");
1525
1526 chan = 1;
1527 for (n=0; n<3; n++) {
1528 ptt_set (OCTYPE_PTT, chan, 1);
1529 SLEEP_SEC(1);
1530 ptt_set (OCTYPE_PTT, chan, 0);
1531 SLEEP_SEC(1);
1532 }
1533
1534 ptt_term();
1535
1536 /* Same thing again but invert RTS. */
1537
1538 my_audio_config.achan[0].octrl[OCTYPE_PTT].ptt_invert = 1;
1539
1540 ptt_init (&my_audio_config);
1541
1542 SLEEP_SEC(2);
1543
1544 dw_printf ("INVERTED - RTS a few times...\n");
1545
1546 chan = 0;
1547 for (n=0; n<3; n++) {
1548 ptt_set (OCTYPE_PTT, chan, 1);
1549 SLEEP_SEC(1);
1550 ptt_set (OCTYPE_PTT, chan, 0);
1551 SLEEP_SEC(1);
1552 }
1553
1554 dw_printf ("turn on DTR a few times...\n");
1555
1556 chan = 1;
1557 for (n=0; n<3; n++) {
1558 ptt_set (OCTYPE_PTT, chan, 1);
1559 SLEEP_SEC(1);
1560 ptt_set (OCTYPE_PTT, chan, 0);
1561 SLEEP_SEC(1);
1562 }
1563
1564 ptt_term ();
1565
1566
1567 /* Test GPIO */
1568
1569 #if __arm__
1570
1571 memset (&my_audio_config, 0, sizeof(my_audio_config));
1572 my_audio_config.adev[0].num_channels = 1;
1573 my_audio_config.achan[0].medium = MEDIUM_RADIO;
1574 my_audio_config.adev[0].octrl[OCTYPE_PTT].ptt_method = PTT_METHOD_GPIO;
1575 my_audio_config.adev[0].octrl[OCTYPE_PTT].out_gpio_num = 25;
1576
1577 dw_printf ("Try GPIO %d a few times...\n", my_audio_config.out_gpio_num[0]);
1578
1579 ptt_init (&my_audio_config);
1580
1581 SLEEP_SEC(2);
1582 chan = 0;
1583 for (n=0; n<3; n++) {
1584 ptt_set (OCTYPE_PTT, chan, 1);
1585 SLEEP_SEC(1);
1586 ptt_set (OCTYPE_PTT, chan, 0);
1587 SLEEP_SEC(1);
1588 }
1589
1590 ptt_term ();
1591 #endif
1592
1593
1594
1595 /* Parallel printer port. */
1596
1597 #if ( defined(__i386__) || defined(__x86_64__) ) && ( defined(__linux__) || defined(__unix__) )
1598
1599 // TODO
1600
1601 #if 0
1602 memset (&my_audio_config, 0, sizeof(my_audio_config));
1603 my_audio_config.num_channels = 2;
1604 my_audio_config.achan[0].medium = MEDIUM_RADIO;
1605 my_audio_config.adev[0].octrl[OCTYPE_PTT].ptt_method = PTT_METHOD_LPT;
1606 my_audio_config.adev[0].octrl[OCTYPE_PTT].ptt_lpt_bit = 0;
1607 my_audio_config.achan[1].medium = MEDIUM_RADIO;
1608 my_audio_config.adev[1].octrl[OCTYPE_PTT].ptt_method = PTT_METHOD_LPT;
1609 my_audio_config.adev[1].octrl[OCTYPE_PTT].ptt_lpt_bit = 1;
1610
1611 dw_printf ("Try LPT bits 0 & 1 a few times...\n");
1612
1613 ptt_init (&my_audio_config);
1614
1615 for (n=0; n<8; n++) {
1616 ptt_set (OCTYPE_PTT, 0, n & 1);
1617 ptt_set (OCTYPE_PTT, 1, (n>>1) & 1);
1618 SLEEP_SEC(1);
1619 }
1620
1621 ptt_term ();
1622 #endif
1623
1624 #endif
1625
1626 return(0);
1627 }
1628
1629 #endif /* TEST */
1630
1631 /* end ptt.c */
1632
1633
1634
1635