Limited support for CM108/CM119 GPIO PTT on Windows.

This commit is contained in:
wb2osz 2021-02-07 16:19:34 -05:00
parent 667e9caaf2
commit a1afcbbafe
10 changed files with 325 additions and 90 deletions

View File

@ -7,6 +7,8 @@
### New Features: ### ### New Features: ###
- Limited support for CM109/CM119 GPIO PTT on Windows.
- Dire Wolf now advertises itself using DNS Service Discovery. This allows suitable APRS / Packet Radio applications to find a network KISS TNC without knowing the IP address or TCP port. Thanks to Hessu for providing this. Currently available only for Linux and Mac OSX. [Read all about it here.](https://github.com/hessu/aprs-specs/blob/master/TCP-KISS-DNS-SD.md) - Dire Wolf now advertises itself using DNS Service Discovery. This allows suitable APRS / Packet Radio applications to find a network KISS TNC without knowing the IP address or TCP port. Thanks to Hessu for providing this. Currently available only for Linux and Mac OSX. [Read all about it here.](https://github.com/hessu/aprs-specs/blob/master/TCP-KISS-DNS-SD.md)
- The transmit calibration tone (-x) command line option now accepts a radio channel number and/or a single letter mode: a = alternate tones, m = mark tone, s = space tone, p = PTT only no sound. - The transmit calibration tone (-x) command line option now accepts a radio channel number and/or a single letter mode: a = alternate tones, m = mark tone, s = space tone, p = PTT only no sound.

View File

@ -72,6 +72,7 @@ set(CUSTOM_SRC_DIR "${CMAKE_SOURCE_DIR}/src")
set(CUSTOM_EXTERNAL_DIR "${CMAKE_SOURCE_DIR}/external") set(CUSTOM_EXTERNAL_DIR "${CMAKE_SOURCE_DIR}/external")
set(CUSTOM_MISC_DIR "${CUSTOM_EXTERNAL_DIR}/misc") set(CUSTOM_MISC_DIR "${CUSTOM_EXTERNAL_DIR}/misc")
set(CUSTOM_REGEX_DIR "${CUSTOM_EXTERNAL_DIR}/regex") set(CUSTOM_REGEX_DIR "${CUSTOM_EXTERNAL_DIR}/regex")
set(CUSTOM_HIDAPI_DIR "${CUSTOM_EXTERNAL_DIR}/hidapi")
set(CUSTOM_GEOTRANZ_DIR "${CUSTOM_EXTERNAL_DIR}/geotranz") set(CUSTOM_GEOTRANZ_DIR "${CUSTOM_EXTERNAL_DIR}/geotranz")
set(CUSTOM_DATA_DIR "${CMAKE_SOURCE_DIR}/data") set(CUSTOM_DATA_DIR "${CMAKE_SOURCE_DIR}/data")
set(CUSTOM_SCRIPTS_DIR "${CMAKE_SOURCE_DIR}/scripts") set(CUSTOM_SCRIPTS_DIR "${CMAKE_SOURCE_DIR}/scripts")
@ -321,6 +322,7 @@ add_subdirectory(data)
# external libraries # external libraries
add_subdirectory(${CUSTOM_GEOTRANZ_DIR}) add_subdirectory(${CUSTOM_GEOTRANZ_DIR})
add_subdirectory(${CUSTOM_REGEX_DIR}) add_subdirectory(${CUSTOM_REGEX_DIR})
add_subdirectory(${CUSTOM_HIDAPI_DIR})
add_subdirectory(${CUSTOM_MISC_DIR}) add_subdirectory(${CUSTOM_MISC_DIR})
# direwolf source code and utilities # direwolf source code and utilities

View File

@ -78,7 +78,6 @@ endfunction()
# The default will be set for maximum portability so packagers won't need to # The default will be set for maximum portability so packagers won't need to
# to anything special. # to anything special.
# #
set(FORCE_SSE 1)
# #
# While ENABLE_GENERIC also had the desired result (for x86_64), I don't think # While ENABLE_GENERIC also had the desired result (for x86_64), I don't think
# it is the right approach. It prevents the detection of the architecture, # it is the right approach. It prevents the detection of the architecture,

View File

@ -263,10 +263,22 @@
%C% %C%
%C%#DTMF %C%#DTMF
%C% %C%
%C%# %L%# If using a C-Media CM108/CM119 or similar USB Audio Adapter,
%C%# If not using a VOX circuit, the transmitter Push to Talk (PTT) %L%# you can use a GPIO pin for PTT control. This is very convenient
%C%# control is usually wired to a serial port with a suitable interface circuit. %L%# because a single USB connection is used for both audio and PTT.
%C%# DON'T connect it directly! %L%# Example:
%L%
%L%#PTT CM108
%L%
%W%# If using a C-Media CM108/CM119 or similar USB Audio Adapter,
%W%# you can use a GPIO pin for PTT control. This is very convenient
%W%# because a single USB connection is used for both audio and PTT.
%W%# Example:
%W%
%W%#PTT CM108
%W%%C%#
%C%# The transmitter Push to Talk (PTT) control can be wired to a serial port
%C%# with a suitable interface circuit. DON'T connect it directly!
%C%# %C%#
%C%# For the PTT command, specify the device and either RTS or DTR. %C%# For the PTT command, specify the device and either RTS or DTR.
%C%# RTS or DTR may be preceded by "-" to invert the signal. %C%# RTS or DTR may be preceded by "-" to invert the signal.

View File

@ -9,6 +9,7 @@ include_directories(
${UDEV_INCLUDE_DIRS} ${UDEV_INCLUDE_DIRS}
${PORTAUDIO_INCLUDE_DIRS} ${PORTAUDIO_INCLUDE_DIRS}
${CUSTOM_GEOTRANZ_DIR} ${CUSTOM_GEOTRANZ_DIR}
${CUSTOM_HIDAPI_DIR}
) )
if(WIN32 OR CYGWIN) if(WIN32 OR CYGWIN)
@ -106,6 +107,7 @@ if(LINUX)
elseif(WIN32 OR CYGWIN) # windows elseif(WIN32 OR CYGWIN) # windows
list(APPEND direwolf_SOURCES list(APPEND direwolf_SOURCES
audio_win.c audio_win.c
cm108.c
# icon # icon
# require plain gcc binary or link # require plain gcc binary or link
@ -134,6 +136,7 @@ target_link_libraries(direwolf
${GEOTRANZ_LIBRARIES} ${GEOTRANZ_LIBRARIES}
${MISC_LIBRARIES} ${MISC_LIBRARIES}
${REGEX_LIBRARIES} ${REGEX_LIBRARIES}
${HIDAPI_LIBRARIES}
Threads::Threads Threads::Threads
${GPSD_LIBRARIES} ${GPSD_LIBRARIES}
${HAMLIB_LIBRARIES} ${HAMLIB_LIBRARIES}
@ -147,7 +150,7 @@ if(WIN32 OR CYGWIN)
set_target_properties(direwolf set_target_properties(direwolf
PROPERTIES COMPILE_FLAGS "-DUSE_REGEX_STATIC" PROPERTIES COMPILE_FLAGS "-DUSE_REGEX_STATIC"
) )
target_link_libraries(direwolf winmm ws2_32) target_link_libraries(direwolf winmm ws2_32 setupapi)
endif() endif()
# decode_aprs # decode_aprs
@ -413,8 +416,11 @@ endif()
# List USB audio adapters than can use GPIO for PTT. # List USB audio adapters than can use GPIO for PTT.
# Originally for Linux only (using udev).
# Version 1.7 adds it for Windows. Needs hidapi library.
# cm108 # cm108
if(UDEV_FOUND) if(UDEV_FOUND OR WIN32 OR CYGWIN)
list(APPEND cm108_SOURCES list(APPEND cm108_SOURCES
cm108.c cm108.c
textcolor.c textcolor.c
@ -430,8 +436,21 @@ if(UDEV_FOUND)
target_link_libraries(cm108 target_link_libraries(cm108
${MISC_LIBRARIES} ${MISC_LIBRARIES}
${UDEV_LIBRARIES}
) )
if (LINUX)
target_link_libraries(cm108
${UDEV_LIBRARIES}
)
endif()
if (WIN32 OR CYGWIN)
target_link_libraries(cm108
${HIDAPI_LIBRARIES}
ws2_32
setupapi
)
endif()
endif() endif()
@ -496,6 +515,6 @@ install(TARGETS atest DESTINATION ${INSTALL_BIN_DIR})
install(TARGETS ttcalc DESTINATION ${INSTALL_BIN_DIR}) install(TARGETS ttcalc DESTINATION ${INSTALL_BIN_DIR})
install(TARGETS kissutil DESTINATION ${INSTALL_BIN_DIR}) install(TARGETS kissutil DESTINATION ${INSTALL_BIN_DIR})
install(TARGETS appserver DESTINATION ${INSTALL_BIN_DIR}) install(TARGETS appserver DESTINATION ${INSTALL_BIN_DIR})
if(UDEV_FOUND) if(UDEV_FOUND OR WIN32 OR CYGWIN)
install(TARGETS cm108 DESTINATION ${INSTALL_BIN_DIR}) install(TARGETS cm108 DESTINATION ${INSTALL_BIN_DIR})
endif() endif()

View File

@ -240,15 +240,17 @@ struct audio_s {
ptt_method_t ptt_method; /* none, serial port, GPIO, LPT, HAMLIB, CM108. */ ptt_method_t ptt_method; /* none, serial port, GPIO, LPT, HAMLIB, CM108. */
char ptt_device[100]; /* Serial device name for PTT. e.g. COM1 or /dev/ttyS0 */ char ptt_device[128]; /* Serial device name for PTT. e.g. COM1 or /dev/ttyS0 */
/* Also used for HAMLIB. Could be host:port when model is 1. */ /* Also used for HAMLIB. Could be host:port when model is 1. */
/* For years, 20 characters was plenty then we start getting extreme names like this: */ /* For years, 20 characters was plenty then we start getting extreme names like this: */
/* /dev/serial/by-id/usb-FTDI_Navigator__CAT___2nd_PTT__00000000-if00-port0 */ /* /dev/serial/by-id/usb-FTDI_Navigator__CAT___2nd_PTT__00000000-if00-port0 */
/* /dev/serial/by-id/usb-Prolific_Technology_Inc._USB-Serial_Controller_D-if00-port0 */ /* /dev/serial/by-id/usb-Prolific_Technology_Inc._USB-Serial_Controller_D-if00-port0 */
/* Issue 104, changed to 100 bytes in version 1.5. */ /* Issue 104, changed to 100 bytes in version 1.5. */
/* This same field is also used for CM108 GPIO PTT which will */ /* This same field is also used for CM108/CM119 GPIO PTT which will */
/* have a name like /dev/hidraw1. */ /* have a name like /dev/hidraw1 for Linux or */
/* \\?\hid#vid_0d8c&pid_0008&mi_03#8&39d3555&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030} */
/* for Windows. Largest observed was 95 but add some extra to be safe. */
ptt_line_t ptt_line; /* Control line when using serial port. PTT_LINE_RTS, PTT_LINE_DTR. */ ptt_line_t ptt_line; /* Control line when using serial port. PTT_LINE_RTS, PTT_LINE_DTR. */
ptt_line_t ptt_line2; /* Optional second one: PTT_LINE_NONE when not used. */ ptt_line_t ptt_line2; /* Optional second one: PTT_LINE_NONE when not used. */
@ -256,7 +258,7 @@ struct audio_s {
int out_gpio_num; /* GPIO number. Originally this was only for PTT. */ int out_gpio_num; /* GPIO number. Originally this was only for PTT. */
/* It is now more general. */ /* It is now more general. */
/* octrl array is indexed by PTT, DCD, or CONnected indicator. */ /* octrl array is indexed by PTT, DCD, or CONnected indicator. */
/* For CM108, this should be in range of 1-8. */ /* For CM108/CM119, this should be in range of 1-8. */
#define MAX_GPIO_NAME_LEN 20 // 12 would cover any case I've seen so this should be safe #define MAX_GPIO_NAME_LEN 20 // 12 would cover any case I've seen so this should be safe

View File

@ -1,7 +1,7 @@
// //
// This file is part of Dire Wolf, an amateur radio packet TNC. // This file is part of Dire Wolf, an amateur radio packet TNC.
// //
// Copyright (C) 2017,2019 John Langner, WB2OSZ // Copyright (C) 2017,2019,2021 John Langner, WB2OSZ
// //
// Parts of this were adapted from "hamlib" which contains the notice: // Parts of this were adapted from "hamlib" which contains the notice:
// //
@ -33,6 +33,7 @@
* There is an incresing demand for using the GPIO pins of USB audio devices for PTT. * There is an incresing demand for using the GPIO pins of USB audio devices for PTT.
* We have a few commercial products: * We have a few commercial products:
* *
* DINAH https://hamprojects.info/dinah/
* DMK URI http://www.dmkeng.com/URI_Order_Page.htm * DMK URI http://www.dmkeng.com/URI_Order_Page.htm
* RB-USB RIM http://www.repeater-builder.com/products/usb-rim-lite.html * RB-USB RIM http://www.repeater-builder.com/products/usb-rim-lite.html
* RA-35 http://www.masterscommunications.com/products/radio-adapter/ra35.html * RA-35 http://www.masterscommunications.com/products/radio-adapter/ra35.html
@ -44,7 +45,8 @@
* http://www.repeater-builder.com/projects/fob/USB-Fob-Construction.pdf * http://www.repeater-builder.com/projects/fob/USB-Fob-Construction.pdf
* https://irongarment.wordpress.com/2011/03/29/cm108-compatible-chips-with-gpio/ * https://irongarment.wordpress.com/2011/03/29/cm108-compatible-chips-with-gpio/
* *
* Usually GPIO 3 is used because it is easier to tack solder a wire to a pin on the end. * Homebrew plans all use GPIO 3 because it is easier to tack solder a wire to a pin on the end.
* All of the products, that I have seen, also use the same pin so this is the default.
* *
* Soundmodem and hamlib paved the way but didn't get too far. * Soundmodem and hamlib paved the way but didn't get too far.
* Dire Wolf 1.3 added HAMLIB support (Linux only) which theoretically allows this in a * Dire Wolf 1.3 added HAMLIB support (Linux only) which theoretically allows this in a
@ -87,23 +89,28 @@
* Dire Wolf version 1.5 makes this much more flexible and easier to use by supporting multiple * Dire Wolf version 1.5 makes this much more flexible and easier to use by supporting multiple
* sound devices and automatically determining the corresponding HID for the PTT signal. * sound devices and automatically determining the corresponding HID for the PTT signal.
* *
* In version 1.7, we add a half-backed solution for Windows. It's fine for situations
* with a single USB Audio Adapter, but does not automatically handle the multiple device case.
* Manual configuration needs to be used in this case.
*
*---------------------------------------------------------------*/ *---------------------------------------------------------------*/
#include "direwolf.h"
#ifndef USE_CM108 #ifndef USE_CM108
#ifdef CM108_MAIN #ifdef CM108_MAIN
#include "direwolf.h"
#include "textcolor.h" #include "textcolor.h"
int main (void) int main (void)
{ {
text_color_init (0); // Turn off text color. text_color_init (0); // Turn off text color.
#if defined(__OpenBSD__) || defined(__FreeBSD__) #if defined(__OpenBSD__) || defined(__FreeBSD__)
dw_printf ("CM108 PTT support is not available for BSD.\n"); dw_printf ("CM108 PTT support is not available for this operating system.\n");
#else #else
dw_printf ("CM108 PTT support was disabled in Makefile.linux.\n"); dw_printf ("CM108 PTT support was excluded because /usr/include/libudev.h was missing.\n");
dw_printf ("It was excluded because /usr/include/libudev.h was missing.\n");
dw_printf ("Install it with \"sudo apt-get install libudev-dev\" or\n"); dw_printf ("Install it with \"sudo apt-get install libudev-dev\" or\n");
dw_printf ("\"sudo yum install libudev-devel\" then rebuild.\n"); dw_printf ("\"sudo yum install libudev-devel\" then rebuild.\n");
#endif #endif
@ -112,11 +119,8 @@ int main (void)
#endif #endif
#else // USE_CM108 is defined. #else // USE_CM108 is defined
#include "direwolf.h"
#include <libudev.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <locale.h> #include <locale.h>
@ -124,12 +128,18 @@ int main (void)
#include <string.h> #include <string.h>
#include <regex.h> #include <regex.h>
#if __WIN32__
#include <wchar.h>
#include "hidapi.h"
#else
#include <libudev.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/ioctl.h> // ioctl, _IOR #include <sys/ioctl.h> // ioctl, _IOR
#include <fcntl.h> #include <fcntl.h>
#include <errno.h> #include <errno.h>
#include <linux/hidraw.h> // for HIDIOCGRAWINFO #include <linux/hidraw.h> // for HIDIOCGRAWINFO
#endif
#include "textcolor.h" #include "textcolor.h"
#include "cm108.h" #include "cm108.h"
@ -216,6 +226,8 @@ static int cm108_write (char *name, int iomask, int iodata);
// Used to process regular expression matching results. // Used to process regular expression matching results.
#ifndef __WIN32__
static void substr_se (char *dest, const char *src, int start, int endp1) static void substr_se (char *dest, const char *src, int start, int endp1)
{ {
int len = endp1 - start; int len = endp1 - start;
@ -229,6 +241,7 @@ static void substr_se (char *dest, const char *src, int start, int endp1)
} /* end substr_se */ } /* end substr_se */
#endif
/* /*
* Result of taking inventory of USB soundcards and USB HIDs. * Result of taking inventory of USB soundcards and USB HIDs.
@ -237,15 +250,17 @@ static void substr_se (char *dest, const char *src, int start, int endp1)
struct thing_s { struct thing_s {
int vid; // vendor id, displayed as four hexadecimal digits. int vid; // vendor id, displayed as four hexadecimal digits.
int pid; // product id, displayed as four hexadecimal digits. int pid; // product id, displayed as four hexadecimal digits.
char card_number[8]; // Number. e.g. 2 for plughw:2,0 char card_number[8]; // "Card" Number. e.g. 2 for plughw:2,0
char card_name[32]; // Name, assigned by system (e.g. Device_1) or by udev rule. char card_name[32]; // Audio Card Name, assigned by system (e.g. Device_1) or by udev rule.
char product[32]; // product name (e.g. manufacturer, model) char product[32]; // product name (e.g. manufacturer, model)
char devnode_sound[22]; // e.g. /dev/snd/pcmC0D0p char devnode_sound[22]; // e.g. /dev/snd/pcmC0D0p
char plughw[72]; // Above in more familiar format e.g. plughw:0,0 char plughw[72]; // Above in more familiar format e.g. plughw:0,0
// Oversized to silence a compiler warning. // Oversized to silence a compiler warning.
char plughw2[72]; // With name rather than number. char plughw2[72]; // With name rather than number.
char devpath[128]; // Kernel dev path. Does not include /sys mount point. char devpath[128]; // Kernel dev path. Does not include /sys mount point.
char devnode_hidraw[17]; // e.g. /dev/hidraw3 char devnode_hidraw[128]; // e.g. /dev/hidraw3 - for Linux - was length 17
// The Windows path for a HID looks like this, lengths up to 95 seen.
// \\?\hid#vid_0d8c&pid_000c&mi_03#8&164d11c9&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}
char devnode_usb[25]; // e.g. /dev/bus/usb/001/012 char devnode_usb[25]; // e.g. /dev/bus/usb/001/012
// This is what we use to match up audio and HID. // This is what we use to match up audio and HID.
}; };
@ -259,6 +274,13 @@ int cm108_inventory (struct thing_s *things, int max_things);
* *
* Purpose: Useful utility to list USB audio and HID devices. * Purpose: Useful utility to list USB audio and HID devices.
* *
* Optional command line arguments:
*
* HID path
* GPIO number (default 3)
*
* When specified the pin will be set high and low until interrupted.
*
*------------------------------------------------------------------*/ *------------------------------------------------------------------*/
//#define EXTRA 1 //#define EXTRA 1
@ -267,18 +289,111 @@ int cm108_inventory (struct thing_s *things, int max_things);
#ifdef CM108_MAIN #ifdef CM108_MAIN
int main (void) static void usage(void)
{
text_color_set(DW_COLOR_ERROR);
dw_printf ("\n");
dw_printf ("Usage: cm108 [ device-path [ gpio-num ] ]\n");
dw_printf ("\n");
dw_printf ("With no command line arguments, this will produce a list of\n");
#if __WIN32__
dw_printf ("Human Interface Devices (HID) and indicate which ones can be\n");
dw_printf ("used for GPIO PTT.\n");
#else
dw_printf ("Audio devices and Human Interface Devices (HID) and indicate\n");
dw_printf ("which ones can be used for GPIO PTT.\n");
#endif
dw_printf ("\n");
dw_printf ("Specify the HID device path to test the PTT fuction.\n");
dw_printf ("Its state should change once per second.\n");
#if __WIN32__
dw_printf ("You might need to quote the path depending on the command processor.\n");
#endif
dw_printf ("GPIO 3 is the default. A different number can be optionally specified.\n");
exit (EXIT_FAILURE);
}
int main (int argc, char **argv)
{ {
struct thing_s things[MAXX_THINGS]; struct thing_s things[MAXX_THINGS];
int num_things; int num_things;
int i; int i;
text_color_init (0); // Turn off text color. text_color_init (0); // Turn off text color.
text_color_set(DW_COLOR_INFO);
if (argc >=2) {
char path[128];
strlcpy(path, argv[1], sizeof(path));
int gpio = 3;
if (argc >= 3) {
gpio = atoi(argv[2]);
}
if (gpio < 1 || gpio > 8) {
dw_printf ("GPIO number must be in range of 1 - 8.\n");
usage();
exit (EXIT_FAILURE);
}
int state = 0;
while (1) {
dw_printf ("%d", state);
fflush (stdout);
int err = cm108_set_gpio_pin (path, gpio, state);
if (err != 0) {
dw_printf ("\nWRITE ERROR for USB Audio Adapter GPIO!\n");
usage();
exit (EXIT_FAILURE);
}
SLEEP_SEC(1);
state = ! state;
}
}
// Take inventory of USB Audio adapters and other HID devices. // Take inventory of USB Audio adapters and other HID devices.
num_things = cm108_inventory (things, MAXX_THINGS); num_things = cm108_inventory (things, MAXX_THINGS);
#if __WIN32__
/////////////////////////////////////////////////////
// Windows - Remove the sound related columns for now.
/////////////////////////////////////////////////////
dw_printf (" VID PID %-*s %-*s"
"\n", (int)sizeof(things[0].product), "Product",
17, "HID [ptt]"
);
dw_printf (" --- --- %-*s %-*s"
"\n", (int)sizeof(things[0].product), "-------",
17, "---------"
);
for (i = 0; i < num_things; i++) {
dw_printf ("%2s %04x %04x %-*s %s"
"\n",
GOOD_DEVICE(things[i].vid,things[i].pid) ? "**" : " ",
things[i].vid, things[i].pid,
(int)sizeof(things[i].product), things[i].product,
things[i].devnode_hidraw
);
}
dw_printf ("\n");
dw_printf ("** = Can use Audio Adapter GPIO for PTT.\n");
dw_printf ("\n");
// T.B.D. - additional text ???
#else
/////////////////////////////////////////////
// Linux
/////////////////////////////////////////////
dw_printf (" VID PID %-*s %-*s %-*s %-*s %-*s" dw_printf (" VID PID %-*s %-*s %-*s %-*s %-*s"
#if EXTRA #if EXTRA
" %-*s" " %-*s"
@ -287,7 +402,7 @@ int main (void)
(int)sizeof(things[0].devnode_sound), "Sound", (int)sizeof(things[0].devnode_sound), "Sound",
(int)sizeof(things[0].plughw)/5, "ADEVICE", (int)sizeof(things[0].plughw)/5, "ADEVICE",
(int)sizeof(things[0].plughw2)/4, "ADEVICE", (int)sizeof(things[0].plughw2)/4, "ADEVICE",
(int)sizeof(things[0].devnode_hidraw), "HID [ptt]" 17, "HID [ptt]"
#if EXTRA #if EXTRA
, (int)sizeof(things[0].devnode_usb), "USB" , (int)sizeof(things[0].devnode_usb), "USB"
#endif #endif
@ -301,14 +416,14 @@ int main (void)
(int)sizeof(things[0].devnode_sound), "-----", (int)sizeof(things[0].devnode_sound), "-----",
(int)sizeof(things[0].plughw)/5, "-------", (int)sizeof(things[0].plughw)/5, "-------",
(int)sizeof(things[0].plughw2)/4, "-------", (int)sizeof(things[0].plughw2)/4, "-------",
(int)sizeof(things[0].devnode_hidraw), "---------" 17, "---------"
#if EXTRA #if EXTRA
, (int)sizeof(things[0].devnode_usb), "---" , (int)sizeof(things[0].devnode_usb), "---"
#endif #endif
); );
for (i = 0; i < num_things; i++) { for (i = 0; i < num_things; i++) {
dw_printf ("%2s %04x %04x %-*s %-*s %-*s %-*s %-*s" dw_printf ("%2s %04x %04x %-*s %-*s %-*s %-*s %s"
#if EXTRA #if EXTRA
" %-*s" " %-*s"
#endif #endif
@ -319,22 +434,22 @@ int main (void)
(int)sizeof(things[i].devnode_sound), things[i].devnode_sound, (int)sizeof(things[i].devnode_sound), things[i].devnode_sound,
(int)sizeof(things[0].plughw)/5, things[i].plughw, (int)sizeof(things[0].plughw)/5, things[i].plughw,
(int)sizeof(things[0].plughw2)/4, things[i].plughw2, (int)sizeof(things[0].plughw2)/4, things[i].plughw2,
(int)sizeof(things[i].devnode_hidraw), things[i].devnode_hidraw things[i].devnode_hidraw
#if EXTRA #if EXTRA
, (int)sizeof(things[i].devnode_usb), things[i].devnode_usb , (int)sizeof(things[i].devnode_usb), things[i].devnode_usb
#endif #endif
); );
//dw_printf (" %-*s\n", (int)sizeof(things[i].devpath), things[i].devpath); //dw_printf (" %-*s\n", (int)sizeof(things[i].devpath), things[i].devpath);
} }
dw_printf ("\n");
dw_printf ("** = Can use Audio Adapter GPIO for PTT.\n");
dw_printf ("\n");
static const char *suggested_names[] = {"Fred", "Wilma", "Pebbles", "Dino", "Barney", "Betty", "Bamm_Bamm" }; static const char *suggested_names[] = {"Fred", "Wilma", "Pebbles", "Dino", "Barney", "Betty", "Bamm_Bamm", "Chip", "Roxy" };
int iname = 0; int iname = 0;
// From example in https://alsa.opensrc.org/Udev // From example in https://alsa.opensrc.org/Udev
dw_printf ("\n");
dw_printf ("** = Can use Audio Adapter GPIO for PTT.\n");
dw_printf ("\n");
dw_printf ("Notice that each USB Audio adapter is assigned a number and a name. These are not predictable so you could\n"); dw_printf ("Notice that each USB Audio adapter is assigned a number and a name. These are not predictable so you could\n");
dw_printf ("end up using the wrong adapter after adding or removing other USB devices or after rebooting. You can assign a\n"); dw_printf ("end up using the wrong adapter after adding or removing other USB devices or after rebooting. You can assign a\n");
dw_printf ("name to each USB adapter so you can refer to the same one each time. This can be based on any characteristics\n"); dw_printf ("name to each USB adapter so you can refer to the same one each time. This can be based on any characteristics\n");
@ -372,7 +487,7 @@ int main (void)
} }
dw_printf ("LABEL=\"my_usb_audio_end\"\n"); dw_printf ("LABEL=\"my_usb_audio_end\"\n");
dw_printf ("\n"); dw_printf ("\n");
#endif
return (0); return (0);
} }
@ -400,6 +515,47 @@ int main (void)
int cm108_inventory (struct thing_s *things, int max_things) int cm108_inventory (struct thing_s *things, int max_things)
{ {
int num_things = 0;
memset (things, 0, sizeof(struct thing_s) * max_things);
#if __WIN32__
struct hid_device_info *devs, *cur_dev;
if (hid_init()) {
text_color_set(DW_COLOR_ERROR);
dw_printf("cm108_inventory: hid_init() failed.\n");
return (-1);
}
devs = hid_enumerate(0x0, 0x0);
cur_dev = devs;
while (cur_dev) {
#if 0
printf("Device Found\n type: %04hx %04hx\n path: %s\n serial_number: %ls", cur_dev->vendor_id, cur_dev->product_id, cur_dev->path, cur_dev->serial_number);
printf("\n");
printf(" Manufacturer: %ls\n", cur_dev->manufacturer_string);
printf(" Product: %ls\n", cur_dev->product_string);
printf(" Release: %hx\n", cur_dev->release_number);
printf(" Interface: %d\n", cur_dev->interface_number);
printf(" Usage (page): 0x%hx (0x%hx)\n", cur_dev->usage, cur_dev->usage_page);
printf("\n");
#endif
if (num_things < max_things && cur_dev->vendor_id != 0x051d) { // FIXME - remove exception
things[num_things].vid = cur_dev->vendor_id;
things[num_things].pid = cur_dev->product_id;
wcstombs (things[num_things].product, cur_dev->product_string, sizeof(things[num_things].product));
things[num_things].product[sizeof(things[num_things].product) - 1] = '\0';
strlcpy (things[num_things].devnode_hidraw, cur_dev->path, sizeof(things[num_things].devnode_hidraw));
num_things++;
}
cur_dev = cur_dev->next;
}
hid_free_enumeration(devs);
#else // Linux, with udev
struct udev *udev; struct udev *udev;
struct udev_enumerate *enumerate; struct udev_enumerate *enumerate;
struct udev_list_entry *devices, *dev_list_entry; struct udev_list_entry *devices, *dev_list_entry;
@ -410,8 +566,6 @@ int cm108_inventory (struct thing_s *things, int max_things)
char const *pattrs_number = NULL; char const *pattrs_number = NULL;
char card_devpath[128] = ""; char card_devpath[128] = "";
int num_things = 0;
memset (things, 0, sizeof(struct thing_s) * max_things);
/* /*
* First get a list of the USB audio devices. * First get a list of the USB audio devices.
@ -561,6 +715,8 @@ int cm108_inventory (struct thing_s *things, int max_things)
} }
} }
#endif // end Linux
return (num_things); return (num_things);
} /* end cm108_inventory */ } /* end cm108_inventory */
@ -594,13 +750,48 @@ void cm108_find_ptt (char *output_audio_device, char *ptt_device, int ptt_devic
{ {
struct thing_s things[MAXX_THINGS]; struct thing_s things[MAXX_THINGS];
int num_things; int num_things;
int i;
//dw_printf ("DEBUG: cm108_find_ptt('%s')\n", output_audio_device); //dw_printf ("DEBUG: cm108_find_ptt('%s')\n", output_audio_device);
strlcpy (ptt_device, "", ptt_device_size); strlcpy (ptt_device, "", ptt_device_size);
// Possible improvement: Skip if inventory already taken.
num_things = cm108_inventory (things, MAXX_THINGS); num_things = cm108_inventory (things, MAXX_THINGS);
#if __WIN32__
// FIXME - This is just a half baked implementation.
// I have not been able to figure out how to find the connection
// between the audio device and HID in the same package.
// This is fine for a single USB Audio Adapter, good enough for most people.
// Those with multiple devices will need to manually configure PTT device path.
// Count how many good devices we have.
int good_devices = 0;
for (int i = 0; i < num_things; i++) {
if (GOOD_DEVICE(things[i].vid,things[i].pid) ) {
good_devices++;
//dw_printf ("DEBUG: success! returning '%s'\n", things[i].devnode_hidraw);
strlcpy (ptt_device, things[i].devnode_hidraw, ptt_device_size);
}
}
if (good_devices == 0) return; // None found - caller will print a message.
if (good_devices == 1) return; // Success - Only one candidate device.
text_color_set(DW_COLOR_ERROR);
dw_printf ("There are multiple USB Audio Devices with GPIO capability.\n");
dw_printf ("Explicitly specify one of them for more predictable results:\n");
for (int i = 0; i < num_things; i++) {
if (GOOD_DEVICE(things[i].vid,things[i].pid) ) {
dw_printf (" \"%s\"\n", things[i].devnode_hidraw);
}
}
dw_printf ("Run the \"cm108\" utility for more details.\n");
text_color_set(DW_COLOR_INFO);
#else
regex_t sound_re; regex_t sound_re;
char emsg[100]; char emsg[100];
int e = regcomp (&sound_re, ".+:(CARD=)?([A-Za-z0-9_]+)(,.*)?", REG_EXTENDED); int e = regcomp (&sound_re, ".+:(CARD=)?([A-Za-z0-9_]+)(,.*)?", REG_EXTENDED);
@ -625,7 +816,7 @@ void cm108_find_ptt (char *output_audio_device, char *ptt_device, int ptt_devic
return; return;
} }
for (i = 0; i < num_things; i++) { for (int i = 0; i < num_things; i++) {
//dw_printf ("DEBUG: i=%d, card_name='%s', card_number='%s'\n", i, things[i].card_name, things[i].card_number); //dw_printf ("DEBUG: i=%d, card_name='%s', card_number='%s'\n", i, things[i].card_name, things[i].card_number);
if (strcmp(num_or_name,things[i].card_name) == 0 || strcmp(num_or_name,things[i].card_number) == 0) { if (strcmp(num_or_name,things[i].card_name) == 0 || strcmp(num_or_name,things[i].card_number) == 0) {
//dw_printf ("DEBUG: success! returning '%s'\n", things[i].devnode_hidraw); //dw_printf ("DEBUG: success! returning '%s'\n", things[i].devnode_hidraw);
@ -638,6 +829,7 @@ void cm108_find_ptt (char *output_audio_device, char *ptt_device, int ptt_devic
return; return;
} }
} }
#endif
} /* end cm108_find_ptt */ } /* end cm108_find_ptt */
@ -649,7 +841,8 @@ void cm108_find_ptt (char *output_audio_device, char *ptt_device, int ptt_devic
* *
* Purpose: Set one GPIO pin of the CM108 or similar. * Purpose: Set one GPIO pin of the CM108 or similar.
* *
* Inputs: name - Name of device such as /dev/hidraw2. * Inputs: name - Name of device such as /dev/hidraw2 or
* \\?\hid#vid_0d8c&pid_0008&mi_03#8&39d3555&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}
* *
* num - GPIO number, range 1 thru 8. * num - GPIO number, range 1 thru 8.
* *
@ -669,44 +862,6 @@ void cm108_find_ptt (char *output_audio_device, char *ptt_device, int ptt_devic
* *
*------------------------------------------------------------------*/ *------------------------------------------------------------------*/
#if TESTCM
// Switch pin between input, output-low, and output-high.
// gcc -DTESTCM=1 -DUSE_CM108 cm108.c textcolor.c misc.a -ludev
int main (int argc, char *argv[])
{
#define MODE_IN 0
#define MODE_OUT 0x04 // GPIO 3 = bit 2
#define OUT_LOW 0
#define OUT_HIGH 0x04
if (argc != 2) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Specify HID path on command line.\n");
exit (1);
}
while (1) {
text_color_set(DW_COLOR_INFO);
dw_printf ("Input-L\n");
cm108_write (argv[1], MODE_IN, OUT_LOW);
sleep(5);
dw_printf ("Input-H\n");
cm108_write (argv[1], MODE_IN, OUT_HIGH);
sleep(5);
dw_printf ("Out-LOW\n");
cm108_write (argv[1], MODE_OUT, OUT_LOW);
sleep(5);
dw_printf ("out-HIGH\n");
cm108_write (argv[1], MODE_OUT, OUT_HIGH);
sleep(5);
}
}
#endif
int cm108_set_gpio_pin (char *name, int num, int state) int cm108_set_gpio_pin (char *name, int num, int state)
{ {
@ -758,6 +913,36 @@ int cm108_set_gpio_pin (char *name, int num, int state)
static int cm108_write (char *name, int iomask, int iodata) static int cm108_write (char *name, int iomask, int iodata)
{ {
#if __WIN32__
//text_color_set(DW_COLOR_DEBUG);
//dw_printf ("TEMP DEBUG cm108_write: %s %d %d\n", name, iomask, iodata);
hid_device *handle = hid_open_path(name);
if (handle == NULL) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Could not open %s for write\n", name);
return (-1);
}
unsigned char io[5];
io[0] = 0;
io[1] = 0;
io[2] = iodata;
io[3] = iomask;
io[4] = 0;
int res = hid_write(handle, io, sizeof(io));
if (res < 0) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Write failed to %s\n", name);
return (-1);
}
hid_close(handle);
#else
int fd; int fd;
struct hidraw_devinfo info; struct hidraw_devinfo info;
char io[5]; char io[5];
@ -862,6 +1047,8 @@ static int cm108_write (char *name, int iomask, int iodata)
} }
close (fd); close (fd);
#endif
return (0); return (0);
} /* end cm108_write */ } /* end cm108_write */

View File

@ -63,7 +63,7 @@
#include "tt_text.h" #include "tt_text.h"
#include "ax25_link.h" #include "ax25_link.h"
#ifdef USE_CM108 // Linux only #if USE_CM108 // Current Linux or Windows only
#include "cm108.h" #include "cm108.h"
#endif #endif
@ -1854,9 +1854,9 @@ void config_init (char *fname, struct audio_s *p_audio_config,
} }
else if (strcasecmp(t, "CM108") == 0) { else if (strcasecmp(t, "CM108") == 0) {
/* CM108 - GPIO of USB sound card. case, Linux only. */ /* CM108 - GPIO of USB sound card. case, Linux and Windows only. */
#ifdef USE_CM108 #if USE_CM108
if (ot != OCTYPE_PTT) { if (ot != OCTYPE_PTT) {
// Future project: Allow DCD and CON via the same device. // Future project: Allow DCD and CON via the same device.
@ -1912,22 +1912,23 @@ void config_init (char *fname, struct audio_s *p_audio_config,
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf ("Config file line %d: Could not determine USB Audio GPIO PTT device for audio output %s.\n", line, dw_printf ("Config file line %d: Could not determine USB Audio GPIO PTT device for audio output %s.\n", line,
p_audio_config->adev[ACHAN2ADEV(channel)].adevice_out); p_audio_config->adev[ACHAN2ADEV(channel)].adevice_out);
#if __WIN32__
dw_printf ("You must explicitly mention a HID path.\n");
#else
dw_printf ("You must explicitly mention a device name such as /dev/hidraw1.\n"); dw_printf ("You must explicitly mention a device name such as /dev/hidraw1.\n");
dw_printf ("See User Guide for details.\n"); #endif
dw_printf ("Run \"cm108\" utility to get a list.\n");
dw_printf ("See Interface Guide for details.\n");
continue; continue;
} }
p_audio_config->achan[channel].octrl[ot].ptt_method = PTT_METHOD_CM108; p_audio_config->achan[channel].octrl[ot].ptt_method = PTT_METHOD_CM108;
#else
#if __WIN32__
text_color_set(DW_COLOR_ERROR);
dw_printf ("Config file line %d: CM108 USB Audio GPIO PTT is not available for Windows.\n", line);
#else #else
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf ("Config file line %d: %s with CM108 is only available when USB Audio GPIO support is enabled.\n", line, otname); dw_printf ("Config file line %d: %s with CM108 is only available when USB Audio GPIO support is enabled.\n", line, otname);
dw_printf ("You must rebuild direwolf with CM108 Audio Adapter GPIO PTT support.\n"); dw_printf ("You must rebuild direwolf with CM108 Audio Adapter GPIO PTT support.\n");
dw_printf ("See User Guide for details.\n"); dw_printf ("See Interface Guide for details.\n");
#endif
exit (EXIT_FAILURE); exit (EXIT_FAILURE);
#endif #endif
} }

View File

@ -1,7 +1,7 @@
// //
// This file is part of Dire Wolf, an amateur radio packet TNC. // This file is part of Dire Wolf, an amateur radio packet TNC.
// //
// Copyright (C) 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2019, 2020 John Langner, WB2OSZ // Copyright (C) 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2019, 2020, 2021 John Langner, WB2OSZ
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
@ -1207,6 +1207,11 @@ void app_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alev
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf ("Audio input level is too high. Reduce so most stations are around 50.\n"); dw_printf ("Audio input level is too high. Reduce so most stations are around 50.\n");
} }
else if (alevel.rec < 5) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Audio input level is too low. Increase so most stations are around 50.\n");
}
// Display non-APRS packets in a different color. // Display non-APRS packets in a different color.

View File

@ -37,6 +37,12 @@
#endif #endif
// Version 1.7 supports CM108/CM119 GPIO PTT for Windows.
// Define it here so we don't need to have separate Windows
// check in all the places that test this.
#if __WIN32__
#define USE_CM108 1
#endif
/* /*
* Previously, we could handle only a single audio device. * Previously, we could handle only a single audio device.