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