1 // ----------------------------------------------------------------------------
2 //
3 //    ptt.cxx --  PTT control
4 //
5 // Copyright (C) 2006-2009
6 //		Dave Freese, W1HKJ
7 // Copyright (C) 2008-2009
8 //		Stelios Bounanos, M0GLD
9 // Copyright (C) 2009
10 //		Diane Bruce, VA3DB
11 //
12 // Added gpio for PTT (Lior KK6BWA)
13 //
14 // This file is part of fldigi.  Adapted from code contained in gmfsk source code
15 // distribution.
16 //  gmfsk Copyright (C) 2001, 2002, 2003
17 //  Tomi Manninen (oh2bns@sral.fi)
18 //  Copyright (C) 2004
19 //  Lawrence Glaister (ve7it@shaw.ca)
20 //
21 // Fldigi is free software: you can redistribute it and/or modify
22 // it under the terms of the GNU General Public License as published by
23 // the Free Software Foundation, either version 3 of the License, or
24 // (at your option) any later version.
25 //
26 // Fldigi is distributed in the hope that it will be useful,
27 // but WITHOUT ANY WARRANTY; without even the implied warranty of
28 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
29 // GNU General Public License for more details.
30 //
31 // You should have received a copy of the GNU General Public License
32 // along with fldigi.  If not, see <http://www.gnu.org/licenses/>.
33 // ----------------------------------------------------------------------------
34 
35 #include <config.h>
36 
37 #include <iostream>
38 
39 #include <unistd.h>
40 #include <sys/types.h>
41 #if HAVE_SYS_SELECT_H
42 #  include <sys/select.h>
43 #endif
44 #include <sys/stat.h>
45 #include <fcntl.h>
46 #if HAVE_SYS_IOCTL_H
47 #  include <sys/ioctl.h>
48 #endif
49 #if HAVE_TERMIOS_H
50 #  include <termios.h>
51 #endif
52 #include <errno.h>
53 #include <cstring>
54 #include <stdint.h>
55 
56 #include "trx.h"
57 #include "ptt.h"
58 #include "configuration.h"
59 #include "rigio.h"
60 #if USE_HAMLIB
61 	#include "hamlib.h"
62 #endif
63 #include "serial.h"
64 #include "re.h"
65 #include "debug.h"
66 
67 #include "fl_digi.h"
68 #include "confdialog.h"
69 
70 #include "n3fjp_logger.h"
71 
72 //#include "cmedia.h"
73 
74 LOG_FILE_SOURCE(debug::LOG_RIGCONTROL);
75 
76 using namespace std;
77 
78 extern Cserial CW_KEYLINE_serial;
79 
80 int cmedia_fd = -1;
81 
PTT(ptt_t dev)82 PTT::PTT(ptt_t dev) : pttdev(PTT_INVALID), oldtio(0)
83 {
84 	reset(dev);
85 }
86 
~PTT()87 PTT::~PTT()
88 {
89 	close_all();
90 }
91 
reset(ptt_t dev)92 void PTT::reset(ptt_t dev)
93 {
94 	close_all();
95 
96 	switch (pttdev = dev) {
97 #if HAVE_UHROUTER
98         case PTT_UHROUTER:
99 		if (progdefaults.PTTdev.find(UHROUTER_FIFO_PREFIX) == 0) {
100 			pttdev = PTT_UHROUTER;
101 			open_uhrouter();
102 			break;
103 		}
104 		else {
105 		        pttdev = PTT_NONE;
106 		        break;
107 		}
108 #endif
109 #if HAVE_PARPORT
110         case PTT_PARPORT:
111 		open_parport();
112 		if (pttfd < 0)
113 		        pttdev = PTT_NONE;
114 		break;
115 #endif
116 	case PTT_TTY:
117 		open_tty();
118 		break;
119 #if 0
120 	case PTT_CMEDIA:
121 		cmedia_fd = open_cmedia(progdefaults.cmedia_device);
122 		break;
123 #endif
124 	default:
125 		break; // nothing to open
126 	}
127 	open_gpio();
128 	set(false);
129 }
130 
set(bool ptt)131 void PTT::set(bool ptt)
132 {
133 	string ptt_temp =
134 		pttdev == PTT_NONE ? "NONE" :
135 		pttdev == PTT_HAMLIB ? "HAMLIB" :
136 		pttdev == PTT_RIGCAT ? "RIGCAT" :
137 		pttdev == PTT_TTY ? "TTY" :
138 		pttdev == PTT_GPIO ? "GPIO" :
139 		pttdev == PTT_CMEDIA ? "CMEDIA" :
140 		pttdev == PTT_PARPORT ? "PARPORT" :
141 		pttdev == PTT_UHROUTER ? "UHROUTER" : "UNKNOWN";
142 	LOG_INFO("PTT via %s : %s", ptt_temp.c_str(), ptt ? "ON" : "OFF");
143 
144 // add milliseconds - no audio to clear virtual audio card used by Flex systems
145 	if (!ptt && progdefaults.PTT_off_delay)
146 		MilliSleep(progdefaults.PTT_off_delay);
147 
148 	if (active_modem == cw_modem && (CW_KEYLINE_isopen || progdefaults.CW_KEYLINE_on_cat_port)) {
149 		guard_lock lk(&cwio_ptt_mutex);
150 	}
151 
152 	switch (pttdev) {
153 	case PTT_NONE:
154 		noCAT_setPTT(ptt);
155 		break;
156 #if USE_HAMLIB
157 	case PTT_HAMLIB:
158 		hamlib_set_ptt(ptt);
159 		break;
160 #endif
161 	case PTT_RIGCAT:
162 		rigCAT_set_ptt(ptt);
163 		break;
164 	case PTT_TTY:
165 		set_tty(ptt);
166 		break;
167 	case PTT_GPIO:
168 		set_gpio(ptt);
169 		break;
170 #if HAVE_PARPORT
171 	case PTT_PARPORT:
172 		set_parport(ptt);
173 		break;
174 #else
175 	btnUsePPortPTT->hide();
176 #endif
177 #if HAVE_UHROUTER
178 	case PTT_UHROUTER:
179 		set_uhrouter(ptt);
180 		break;
181 #endif
182 #if 0
183 	case PTT_CMEDIA:
184 		if (cmedia_fd != -1) {
185 			int bitnbr = 2;
186 			if (progdefaults.cmedia_gpio_line == "GPIO-1") bitnbr = 0;
187 			else if (progdefaults.cmedia_gpio_line == "GPIO-2") bitnbr = 1;
188 			else if (progdefaults.cmedia_gpio_line == "GPIO-3") bitnbr = 2;
189 			else if (progdefaults.cmedia_gpio_line == "GPIO-4") bitnbr = 3;
190 			set_cmedia(bitnbr, ptt);
191 		}
192 		break;
193 #endif
194 	default:
195 		{
196 			nano_PTT(ptt);
197 			if (n3fjp_connected)
198 				n3fjp_set_ptt(ptt);
199 		}
200 	}
201 
202 	if (ptt && progdefaults.PTT_on_delay)
203 		MilliSleep(progdefaults.PTT_on_delay);
204 
205 	if (ptt) start_tx_timer();
206 	else     stop_tx_timer();
207 }
208 
close_all(void)209 void PTT::close_all(void)
210 {
211 	set(false);
212 
213 	switch (pttdev) {
214 	case PTT_TTY:
215 		close_tty();
216 		break;
217 #if HAVE_PARPORT
218 	case PTT_PARPORT:
219 		close_parport();
220 		break;
221 #endif
222 #if HAVE_UHROUTER
223 	case PTT_UHROUTER:
224 		close_uhrouter();
225 		break;
226 #endif
227 	default:
228 		break;
229 	}
230 	close_gpio();
231 	pttfd = -1;
232 }
233 
234 //-------------------- gpio port PTT --------------------//
235 #ifndef __MINGW32__
gpioEXEC(std::string execstr)236 static void gpioEXEC(std::string execstr)
237 {
238 	int pfd[2];
239 	if (pipe(pfd) == -1) {
240 		LOG_PERROR("pipe");
241 		return;
242 	}
243 	pid_t pid;
244 	switch (pid = fork()) {
245 		case -1:
246 			LOG_PERROR("fork");
247 			return;
248 		case 0: // child
249 			close(pfd[0]);
250 			if (dup2(pfd[1], STDOUT_FILENO) != STDOUT_FILENO) {
251 				LOG_PERROR("dup2");
252 				exit(EXIT_FAILURE);
253 			}
254 			close(pfd[1]);
255 			execl("/bin/sh", "sh", "-c", execstr.c_str(), (char *)NULL);
256 			perror("execl");
257 			exit(EXIT_FAILURE);
258 	}
259 
260 	// parent
261 	close(pfd[1]);
262 
263 }
264 #else // !__MINGW32__
265 
gpioEXEC(std::string execstr)266 static void gpioEXEC(std::string execstr)
267 {
268 	char* cmd = strdup(execstr.c_str());
269 
270 	STARTUPINFO si;
271 	PROCESS_INFORMATION pi;
272 	memset(&si, 0, sizeof(si));
273 	si.cb = sizeof(si);
274 	memset(&pi, 0, sizeof(pi));
275 	if (!CreateProcess(NULL, cmd, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi))
276 		LOG_ERROR("CreateProcess failed with error code %ld", GetLastError());
277 	CloseHandle(pi.hProcess);
278 	CloseHandle(pi.hThread);
279 	free(cmd);
280 
281 }
282 #endif // !__MINGW32__
283 
284 
285 static const char *gpio_name[] = {
286 		"17", "18", "27", "22", "23",
287 		"24", "25", "4",  "5",  "6",
288 		"13", "19", "26", "12", "16",
289 		"20", "21"};
290 
export_gpio(int bcm)291 void export_gpio(int bcm)
292 {
293 	if (bcm < 0 || bcm > 16) return;
294 	string exec_str = "gpio export ";
295 	exec_str.append(gpio_name[bcm]).append(" out");
296 	gpioEXEC(exec_str);
297 	LOG_INFO("%s", exec_str.c_str());
298 }
299 
unexport_gpio(int bcm)300 void unexport_gpio(int bcm)
301 {
302 	if (bcm < 0 || bcm > 16) return;
303 	string exec_str = "gpio unexport ";
304 	exec_str.append(gpio_name[bcm]);
305 	gpioEXEC(exec_str);
306 	LOG_INFO("%s", exec_str.c_str());
307 }
308 
open_gpio(void)309 void PTT::open_gpio(void)
310 {
311 	bool enabled = false;
312 	for (int i = 0; i < 17; i++) {
313 		enabled = (progdefaults.enable_gpio >> i) & 0x01;
314 		if (enabled) export_gpio(i);
315 	}
316 }
317 
close_gpio(void)318 void PTT::close_gpio(void)
319 {
320 	bool enabled = false;
321 	for (int i = 0; i < 17; i++) {
322 		enabled = (progdefaults.enable_gpio >> i) & 0x01;
323 		if (enabled) unexport_gpio(i);
324 	}
325 }
326 
set_gpio(bool ptt)327 void PTT::set_gpio(bool ptt)
328 {
329 #define VALUE_MAX 30
330 	static const char s_values_str[] = "01";
331 
332 	string portname = "/sys/class/gpio/gpio";
333 	string ctrlport;
334 	bool enabled = false;
335 	int val = 0;
336 	int fd;
337 
338 	for (int i = 0; i < 17; i++) {
339 		enabled = (progdefaults.enable_gpio >> i) & 0x01;
340 
341 		if (enabled) {
342 			val = (progdefaults.gpio_on >> i) & 0x01;
343 			ctrlport = portname;
344 			ctrlport.append(gpio_name[i]);
345 			ctrlport.append("/value");
346 			fd = fl_open(ctrlport.c_str(), O_WRONLY);
347 
348 			bool ok = false;
349 			if (fd == -1) {
350 				LOG_ERROR("Failed to open gpio (%s) for writing!", ctrlport.c_str());
351 			} else {
352 				if (progdefaults.gpio_pulse_width == 0) {
353 					if (ptt) { if (val == 1) val = 1; else val = 0;}
354 					if (!ptt)  { if (val == 1) val = 0; else val = 1;}
355 					if (write(fd, &s_values_str[val], 1) == 1)
356 						ok = true;
357 				} else {
358 					if (write(fd, &s_values_str[val], 1) == 1) {
359 						MilliSleep(progdefaults.gpio_pulse_width);
360 						if (write(fd, &s_values_str[val == 0 ? 1 : 0], 1) == 1)
361 							ok = true;
362 					}
363 				}
364 				if (ok)
365 					LOG_INFO("Set GPIO ptt on %s %s%s",
366 						ctrlport.c_str(),
367 						(progdefaults.gpio_pulse_width > 0) ?
368 							"pulsed " : "",
369 						(val == 1 ? "HIGH" : "LOW")
370 					);
371 				else
372 					LOG_ERROR("Failed to write value!");
373 
374 				close(fd);
375 			}
376 		}
377 	}
378 }
379 
380 //-------------------- serial port PTT --------------------//
381 
open_tty(void)382 void PTT::open_tty(void)
383 {
384 	serPort.Baud(progdefaults.BaudRate(progdefaults.XmlRigBaudrate));
385 	serPort.Device(progdefaults.PTTdev);
386 	serPort.RTS(progdefaults.RTSplus);
387 	serPort.DTR(progdefaults.DTRplus);
388 	serPort.RTSptt(progdefaults.RTSptt);
389 	serPort.DTRptt(progdefaults.DTRptt);
390 	if (progdefaults.SCU_17) serPort.Stopbits(0);//1);
391 	else serPort.Stopbits(2);
392 	if (serPort.OpenPort() == false) {
393 		LOG_ERROR("Cannot open serial port %s", rigio.Device().c_str());
394 		pttfd = -1;
395 		return;
396 	}
397 	LOG_INFO("Serial port %s open", progdefaults.PTTdev.c_str());
398 }
399 
close_tty(void)400 void PTT::close_tty(void)
401 {
402 	serPort.ClosePort();
403 	pttfd = -1;
404 	LOG_DEBUG("Serial port %s closed", progdefaults.PTTdev.c_str());
405 }
406 
set_tty(bool ptt)407 void PTT::set_tty(bool ptt)
408 {
409 	serPort.SetPTT(ptt);
410 }
411 
412 #if HAVE_PARPORT
413 //-------------------- parallel port PTT --------------------//
414 
415 #if HAVE_LINUX_PPDEV_H
416 #  include <linux/ppdev.h>
417 #  include <linux/parport.h>
418 #elif HAVE_DEV_PPBUS_PPI_H
419 #  include <dev/ppbus/ppi.h>
420 #  include <dev/ppbus/ppbconf.h>
421 #endif
422 
open_parport(void)423 void PTT::open_parport(void)
424 {
425     if (progdefaults.PTTdev.find("tty") != string::npos) return;
426 
427 	int oflags = O_RDWR | O_NDELAY;
428 #	ifdef HAVE_O_CLOEXEC
429 		oflags = oflags | O_CLOEXEC;
430 #	endif
431 
432 	if ((pttfd = fl_open(progdefaults.PTTdev.c_str(),  oflags)) == -1) {
433 		LOG_ERROR("Could not open %s: %s", progdefaults.PTTdev.c_str(), strerror(errno));
434 		return;
435 	}
436 
437 	bool isparport = false;
438 
439 	struct stat st;
440 	int status;
441 
442 #if HAVE_LINUX_PPDEV_H     // Linux (ppdev)
443 	isparport = (fstat(pttfd, &st) == 0 && S_ISCHR(st.st_mode) &&
444 		     ioctl(pttfd, PPGETMODE, &status) != -1);
445 #elif HAVE_DEV_PPBUS_PPI_H // FreeBSD (ppbus/ppi) */
446 	isparport = (fstat(pttfd, &st) == 0 && S_ISCHR(st.st_mode) &&
447 		     ioctl(pttfd, PPISSTATUS, &status) != -1);
448 #else                      // Fallback (nothing)
449 	isparport = false;
450 #endif
451 
452 	if (!isparport) {
453 		LOG_VERBOSE("%s: not a supported parallel port device", progdefaults.PTTdev.c_str());
454 		close_parport();
455 		pttfd = -1;
456 	}
457 }
458 
close_parport(void)459 void PTT::close_parport(void)
460 {
461 	close(pttfd);
462 }
463 
set_parport(bool ptt)464 void PTT::set_parport(bool ptt)
465 {
466 #ifdef HAVE_LINUX_PPDEV_H
467 	struct ppdev_frob_struct frob;
468 
469 	frob.mask = PARPORT_CONTROL_INIT;
470 	frob.val = !ptt;
471 	ioctl(pttfd, PPFCONTROL, &frob);
472 #elif HAVE_DEV_PPBUS_PPI_H
473 	u_int8_t val;
474 
475 	ioctl(pttfd, PPIGCTRL, &val);
476 	if (ptt)
477 		val |= nINIT;
478 	else
479 		val &= ~nINIT;
480 	ioctl(pttfd, PPISCTRL, &val);
481 #endif
482 }
483 #endif // HAVE_PARPORT
484 
485 
486 #if HAVE_UHROUTER
487 //-------------------- uhRouter PTT --------------------//
488 
489 // See interface documentation at:
490 // http://homepage.mac.com/chen/w7ay/Router/Contents/routerInterface.html
491 
492 #define	FUNCTIONMASK     0x1f
493 
494 #define ROUTERFUNCTION   0x80
495 #define OPENMICROKEYER   (ROUTERFUNCTION + 0x01) // get a port to the microKEYER router
496 #define OPENCWKEYER      (ROUTERFUNCTION + 0x02) // get a port to the CW KEYER router
497 #define OPENDIGIKEYER    (ROUTERFUNCTION + 0x03) // get a port to the DIGI KEYER router
498 #define QUITIFNOKEYER    (ROUTERFUNCTION + 0x1f) // quit if there are no keyers
499 #define QUITIFNOTINUSE   (ROUTERFUNCTION + 0x1e) // quit if not connected
500 #define QUITALWAYS       (ROUTERFUNCTION + 0x1d) // quit
501 #define CLOSEKEYER       (ROUTERFUNCTION + FUNCTIONMASK)
502 
503 #define KEYERFUNCTION    0x40
504 #define OPENPTT          (KEYERFUNCTION + 0x04)  // get a port to the PTT flag bit
505 
506 #ifndef PATH_MAX
507 #  define PATH_MAX 1024
508 #endif
509 
tm_read(int fd,void * buf,size_t len,const struct timeval * to)510 static ssize_t tm_read(int fd, void* buf, size_t len, const struct timeval* to)
511 {
512 	fd_set s;
513 	FD_ZERO(&s);
514 	FD_SET(fd, &s);
515 
516 	struct timeval t;
517 	memcpy(&t, to, sizeof(t));
518 
519 	ssize_t n;
520 	if ((n = select(fd + 1, &s, 0, 0, &t)) != 1)
521 		return n;
522 
523 	return read(fd, buf, len);
524 }
525 
tm_write(int fd,const void * buf,size_t len,const struct timeval * to)526 static ssize_t tm_write(int fd, const void* buf, size_t len, const struct timeval* to)
527 {
528 	fd_set s;
529 	FD_ZERO(&s);
530 	FD_SET(fd, &s);
531 
532 	struct timeval t;
533 	memcpy(&t, to, sizeof(t));
534 
535 	ssize_t n;
536 	if ((n = select(fd + 1, 0, &s, 0, &t)) != 1)
537 		return n;
538 
539 	return write(fd, buf, len);
540 }
541 
open_fifos(const char * base,int fd[2])542 static bool open_fifos(const char* base, int fd[2])
543 {
544 	struct stat st;
545 	string fifo = base;
546 	size_t len = fifo.length();
547 
548 	fifo += "Read";
549 	if (stat(fifo.c_str(), &st) == -1 || !S_ISFIFO(st.st_mode)) {
550 		LOG_ERROR("%s is not a fifo", fifo.c_str());
551 		return false;
552 	}
553 
554 	int oflags = O_RDONLY | O_NONBLOCK;
555 #	ifdef HAVE_O_CLOEXEC
556 		oflags = oflags | O_CLOEXEC;
557 #	endif
558 
559 	if ((fd[0] = fl_open(fifo.c_str(), oflags)) == -1) {
560 		LOG_ERROR("Could not open %s: %s", fifo.c_str(), strerror(errno));
561 		return false;
562 	}
563 
564 	fifo.erase(len);
565 	fifo += "Write";
566 	if (stat(fifo.c_str(), &st) == -1 || !S_ISFIFO(st.st_mode)) {
567 		LOG_ERROR("%s is not a fifo", fifo.c_str());
568 		return false;
569 	}
570 	oflags = O_WRONLY | O_NONBLOCK;
571 
572 #	ifdef HAVE_O_CLOEXEC
573 		oflags = oflags | O_CLOEXEC;
574 #	endif
575 
576 	if ((fd[1] = fl_open(fifo.c_str(), oflags)) == -1) {
577 		LOG_ERROR("Could not open %s: %s", fifo.c_str(), strerror(errno));
578 		return false;
579 	}
580 
581 	return true;
582 }
583 
get_fifos(const int fd[2],const unsigned char * msg,size_t msglen,char * base,size_t baselen)584 static bool get_fifos(const int fd[2], const unsigned char* msg, size_t msglen, char* base, size_t baselen)
585 {
586 	struct timeval to = { 2, 0 };
587 	if (tm_write(fd[1], msg, msglen, &to) < (ssize_t)msglen) {
588 		LOG_PERROR("Could not write request");
589 		return false;
590 	}
591 	ssize_t r;
592 	if ((r = tm_read(fd[0], base, baselen-1, &to)) <= 0) {
593 		LOG_PERROR("Could not read FIFO name");
594 		return false;
595 	}
596 	base[r] = '\0';
597 	return true;
598 }
599 
600 #ifdef __APPLE__
601 #include <stdio.h>
602 #include <stdlib.h>
603 #include <unistd.h>
604 #endif
605 
start_uhrouter(void)606 static bool start_uhrouter(void)
607 {
608 	bool found = false;
609 #ifdef __APPLE__
610 	FILE *fd = (FILE *)0;
611 	std::string appPath;
612 	std::string buffer;
613 
614 	appPath.assign("/Applications/µH Router.app/Contents/MacOS/µH Router");
615 
616 	fd = fl_fopen(appPath.c_str(), "r");
617 	if(fd) {
618 		found = true;
619 		fclose(fd);
620 	}
621 
622 	if(found) {
623 		buffer.clear();
624 		buffer.assign("\"");
625 		buffer.append(appPath);
626 		buffer.append("\" & ");
627 		system(buffer.c_str());
628 	} else {
629 		LOG_ERROR("File: /Applications/\265H Router.app Not Found!");
630 	}
631 
632 #endif // __APPLE__
633 
634 	return found;
635 }
636 
open_uhrouter(void)637 void PTT::open_uhrouter(void)
638 {
639 	struct {
640 		unsigned char keyer;
641 		const char* name;
642 		const char* abbrev;
643 	} keyers[] = {
644 		{ OPENMICROKEYER, "microKeyer", "MK" },
645 		{ OPENCWKEYER,    "CWKeyer",    "CK" },
646 		{ OPENDIGIKEYER,  "DigiKeyer",  "DK" }
647 	};
648 	size_t start = 0, end = sizeof(keyers)/sizeof(*keyers);
649 
650 	// If the device string is something like /tmp/microHamRouter/microKeyer,
651 	// or /tmp/microHamRouter/MK, try that keyer only.
652 	re_t keyer_re("^" UHROUTER_FIFO_PREFIX "/(.+)$", REG_EXTENDED);
653 	if (keyer_re.match(progdefaults.PTTdev.c_str()) && keyer_re.nsub() == 2) {
654 		const char* keyer = keyer_re.submatch(1).c_str();
655 		// do we recognise this keyer name?
656 		for (size_t i = 0; i < sizeof(keyers)/sizeof(*keyers); i++) {
657 			if (!strcasecmp(keyers[i].name, keyer) || !strcasecmp(keyers[i].abbrev, keyer)) {
658 				start = i;
659 				end = start + 1;
660 				break;
661 			}
662 		}
663 	}
664 	LOG_VERBOSE("Will try %s", (start == end ? keyers[start].name : "all keyers"));
665 
666 	int uhrfd[2];
667 	uhrfd[0] = uhrfd[1] = uhkfd[0] = uhkfd[1] = uhfd[0] = uhfd[1] = -1;
668 
669 	if (!open_fifos(UHROUTER_FIFO_PREFIX, uhrfd)) {
670 		// if we just started uhrouter we will retry open_fifos a few times
671 		unsigned retries = start_uhrouter() ? 30 : 0;
672 		while (retries-- && !open_fifos(UHROUTER_FIFO_PREFIX, uhrfd))
673 			MilliSleep(100);
674 		if (uhrfd[0] == -1 || uhrfd[1] == -1) {
675 			LOG_ERROR("Could not open router");
676 			return;
677 		}
678 	}
679 	char fifo_name[PATH_MAX];
680 	size_t len = PATH_MAX - 8;
681 	memset(fifo_name, 0, sizeof(fifo_name));
682 	for (size_t i = start; i < end; i++) {
683 		// open keyer
684 		if (!get_fifos(uhrfd, &keyers[i].keyer, 1, fifo_name, len) || *fifo_name == '\0') {
685 			LOG_VERBOSE("Keyer \"%s\" not found", keyers[i].name);
686 			continue;
687 		}
688 
689 		// open ptt port
690 		if (!open_fifos(fifo_name, uhkfd)) {
691 			LOG_ERROR("Could not open keyer %s", keyers[i].name);
692 			continue;
693 		}
694 		LOG_VERBOSE("Opened keyer %s", keyers[i].name);
695 
696 		unsigned char port = OPENPTT;
697 		if (!get_fifos(uhkfd, &port, 1, fifo_name, len)) {
698 			LOG_ERROR("Could not get PTT port");
699 			continue;
700 		}
701 		if (!open_fifos(fifo_name, uhfd)) {
702 			LOG_ERROR("Could not open PTT port %s", fifo_name);
703 			continue;
704 		}
705 
706 		LOG_VERBOSE("Successfully opened PTT port of keyer %s", keyers[i].name);
707 		break;
708 	}
709 
710 	// close router FIFOs
711 	close(uhrfd[0]);
712 	close(uhrfd[1]);
713 }
714 
close_uhrouter(void)715 void PTT::close_uhrouter(void)
716 {
717 	close(uhfd[0]);
718 	close(uhfd[1]);
719 
720 	unsigned char c = QUITIFNOTINUSE;
721 	write(uhkfd[1], &c, 1);
722 	close(uhkfd[0]);
723 	close(uhkfd[1]);
724 }
725 
set_uhrouter(bool ptt)726 void PTT::set_uhrouter(bool ptt)
727 {
728 	if (uhfd[0] == -1 || uhfd[1] == -1)
729 		return;
730 
731 	unsigned char buf[_POSIX_PIPE_BUF];
732 	// empty the fifo
733 	while (read(uhfd[0], buf, sizeof(buf)) > 0);
734 
735 	// send command
736 	*buf = '0' + ptt;
737 	LOG_VERBOSE("Sending PTT=%uc", *buf);
738 	struct timeval t = { 2, 0 };
739 	if (tm_write(uhfd[1], buf, 1, &t) != 1) {
740 		LOG_ERROR("Could not set PTT: %s", strerror(errno));
741 		return;
742 	}
743 
744 	// wait for status
745 	ssize_t n = tm_read(uhfd[0], buf, sizeof(buf), &t);
746 	switch (n) {
747 	case -1:
748 		LOG_PERROR("tm_read");
749 		break;
750 	case 0:
751 		LOG_ERROR("No reply to PTT command within %jd seconds", (intmax_t)t.tv_sec);
752 		break;
753 	default:
754 		LOG_VERBOSE("Received \"%s\"", str2hex(buf, n));
755 		// last received char should be '1'(?)
756 		break;
757 	}
758 }
759 
760 #endif // HAVE_UHROUTER
761