From a1afcbbafed9c4b368acd360bbc06afb9b96ea3c Mon Sep 17 00:00:00 2001 From: wb2osz Date: Sun, 7 Feb 2021 16:19:34 -0500 Subject: [PATCH] Limited support for CM108/CM119 GPIO PTT on Windows. --- CHANGES.md | 2 + CMakeLists.txt | 2 + cmake/modules/FindCPUflags.cmake | 1 - conf/generic.conf | 20 +- src/CMakeLists.txt | 27 ++- src/audio.h | 10 +- src/cm108.c | 319 ++++++++++++++++++++++++------- src/config.c | 21 +- src/direwolf.c | 7 +- src/direwolf.h | 6 + 10 files changed, 325 insertions(+), 90 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 029bf10..19ef887 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -7,6 +7,8 @@ ### 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) - 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. diff --git a/CMakeLists.txt b/CMakeLists.txt index 78df609..4b67faf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -72,6 +72,7 @@ set(CUSTOM_SRC_DIR "${CMAKE_SOURCE_DIR}/src") set(CUSTOM_EXTERNAL_DIR "${CMAKE_SOURCE_DIR}/external") set(CUSTOM_MISC_DIR "${CUSTOM_EXTERNAL_DIR}/misc") 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_DATA_DIR "${CMAKE_SOURCE_DIR}/data") set(CUSTOM_SCRIPTS_DIR "${CMAKE_SOURCE_DIR}/scripts") @@ -321,6 +322,7 @@ add_subdirectory(data) # external libraries add_subdirectory(${CUSTOM_GEOTRANZ_DIR}) add_subdirectory(${CUSTOM_REGEX_DIR}) +add_subdirectory(${CUSTOM_HIDAPI_DIR}) add_subdirectory(${CUSTOM_MISC_DIR}) # direwolf source code and utilities diff --git a/cmake/modules/FindCPUflags.cmake b/cmake/modules/FindCPUflags.cmake index b4be791..abb9e18 100644 --- a/cmake/modules/FindCPUflags.cmake +++ b/cmake/modules/FindCPUflags.cmake @@ -78,7 +78,6 @@ endfunction() # The default will be set for maximum portability so packagers won't need to # to anything special. # -set(FORCE_SSE 1) # # 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, diff --git a/conf/generic.conf b/conf/generic.conf index b77d5df..8630ed5 100644 --- a/conf/generic.conf +++ b/conf/generic.conf @@ -263,10 +263,22 @@ %C% %C%#DTMF %C% -%C%# -%C%# If not using a VOX circuit, the transmitter Push to Talk (PTT) -%C%# control is usually wired to a serial port with a suitable interface circuit. -%C%# DON'T connect it directly! +%L%# If using a C-Media CM108/CM119 or similar USB Audio Adapter, +%L%# you can use a GPIO pin for PTT control. This is very convenient +%L%# because a single USB connection is used for both audio and PTT. +%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%# For the PTT command, specify the device and either RTS or DTR. %C%# RTS or DTR may be preceded by "-" to invert the signal. diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 16121b8..7fba03b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -9,6 +9,7 @@ include_directories( ${UDEV_INCLUDE_DIRS} ${PORTAUDIO_INCLUDE_DIRS} ${CUSTOM_GEOTRANZ_DIR} + ${CUSTOM_HIDAPI_DIR} ) if(WIN32 OR CYGWIN) @@ -106,6 +107,7 @@ if(LINUX) elseif(WIN32 OR CYGWIN) # windows list(APPEND direwolf_SOURCES audio_win.c + cm108.c # icon # require plain gcc binary or link @@ -134,6 +136,7 @@ target_link_libraries(direwolf ${GEOTRANZ_LIBRARIES} ${MISC_LIBRARIES} ${REGEX_LIBRARIES} + ${HIDAPI_LIBRARIES} Threads::Threads ${GPSD_LIBRARIES} ${HAMLIB_LIBRARIES} @@ -147,7 +150,7 @@ if(WIN32 OR CYGWIN) set_target_properties(direwolf PROPERTIES COMPILE_FLAGS "-DUSE_REGEX_STATIC" ) - target_link_libraries(direwolf winmm ws2_32) + target_link_libraries(direwolf winmm ws2_32 setupapi) endif() # decode_aprs @@ -413,8 +416,11 @@ endif() # 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 -if(UDEV_FOUND) +if(UDEV_FOUND OR WIN32 OR CYGWIN) list(APPEND cm108_SOURCES cm108.c textcolor.c @@ -430,8 +436,21 @@ if(UDEV_FOUND) target_link_libraries(cm108 ${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() @@ -496,6 +515,6 @@ install(TARGETS atest DESTINATION ${INSTALL_BIN_DIR}) install(TARGETS ttcalc DESTINATION ${INSTALL_BIN_DIR}) install(TARGETS kissutil 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}) endif() diff --git a/src/audio.h b/src/audio.h index 61dec9d..32868c5 100644 --- a/src/audio.h +++ b/src/audio.h @@ -240,15 +240,17 @@ struct audio_s { 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. */ /* 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-Prolific_Technology_Inc._USB-Serial_Controller_D-if00-port0 */ /* Issue 104, changed to 100 bytes in version 1.5. */ - /* This same field is also used for CM108 GPIO PTT which will */ - /* have a name like /dev/hidraw1. */ + /* This same field is also used for CM108/CM119 GPIO PTT which will */ + /* 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_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. */ /* It is now more general. */ /* 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 diff --git a/src/cm108.c b/src/cm108.c index ad3b0b8..cebe20e 100644 --- a/src/cm108.c +++ b/src/cm108.c @@ -1,7 +1,7 @@ // // 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: // @@ -33,6 +33,7 @@ * There is an incresing demand for using the GPIO pins of USB audio devices for PTT. * We have a few commercial products: * + * DINAH https://hamprojects.info/dinah/ * DMK URI http://www.dmkeng.com/URI_Order_Page.htm * RB-USB RIM http://www.repeater-builder.com/products/usb-rim-lite.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 * 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. * 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 * 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 #ifdef CM108_MAIN -#include "direwolf.h" + #include "textcolor.h" int main (void) { text_color_init (0); // Turn off text color. #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 - dw_printf ("CM108 PTT support was disabled in Makefile.linux.\n"); - dw_printf ("It was excluded because /usr/include/libudev.h was missing.\n"); + dw_printf ("CM108 PTT support 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 ("\"sudo yum install libudev-devel\" then rebuild.\n"); #endif @@ -112,11 +119,8 @@ int main (void) #endif -#else // USE_CM108 is defined. +#else // USE_CM108 is defined -#include "direwolf.h" - -#include #include #include #include @@ -124,12 +128,18 @@ int main (void) #include #include +#if __WIN32__ +#include +#include "hidapi.h" +#else +#include #include #include #include // ioctl, _IOR #include #include #include // for HIDIOCGRAWINFO +#endif #include "textcolor.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. +#ifndef __WIN32__ + static void substr_se (char *dest, const char *src, int start, int endp1) { int len = endp1 - start; @@ -229,6 +241,7 @@ static void substr_se (char *dest, const char *src, int start, int endp1) } /* end substr_se */ +#endif /* * 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 { int vid; // vendor 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_name[32]; // Name, assigned by system (e.g. Device_1) or by udev rule. + char card_number[8]; // "Card" Number. e.g. 2 for plughw:2,0 + 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 devnode_sound[22]; // e.g. /dev/snd/pcmC0D0p char plughw[72]; // Above in more familiar format e.g. plughw:0,0 // Oversized to silence a compiler warning. char plughw2[72]; // With name rather than number. 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 // 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. * + * 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 @@ -267,18 +289,111 @@ int cm108_inventory (struct thing_s *things, int max_things); #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]; int num_things; int i; 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. 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" #if EXTRA " %-*s" @@ -287,7 +402,7 @@ int main (void) (int)sizeof(things[0].devnode_sound), "Sound", (int)sizeof(things[0].plughw)/5, "ADEVICE", (int)sizeof(things[0].plughw2)/4, "ADEVICE", - (int)sizeof(things[0].devnode_hidraw), "HID [ptt]" + 17, "HID [ptt]" #if EXTRA , (int)sizeof(things[0].devnode_usb), "USB" #endif @@ -301,14 +416,14 @@ int main (void) (int)sizeof(things[0].devnode_sound), "-----", (int)sizeof(things[0].plughw)/5, "-------", (int)sizeof(things[0].plughw2)/4, "-------", - (int)sizeof(things[0].devnode_hidraw), "---------" + 17, "---------" #if EXTRA , (int)sizeof(things[0].devnode_usb), "---" #endif ); 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 " %-*s" #endif @@ -319,22 +434,22 @@ int main (void) (int)sizeof(things[i].devnode_sound), things[i].devnode_sound, (int)sizeof(things[0].plughw)/5, things[i].plughw, (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 , (int)sizeof(things[i].devnode_usb), things[i].devnode_usb #endif ); //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; // 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 ("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"); @@ -372,7 +487,7 @@ int main (void) } dw_printf ("LABEL=\"my_usb_audio_end\"\n"); dw_printf ("\n"); - +#endif return (0); } @@ -400,6 +515,47 @@ int main (void) 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_enumerate *enumerate; 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 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. @@ -561,6 +715,8 @@ int cm108_inventory (struct thing_s *things, int max_things) } } +#endif // end Linux + return (num_things); } /* 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]; int num_things; - int i; //dw_printf ("DEBUG: cm108_find_ptt('%s')\n", output_audio_device); strlcpy (ptt_device, "", ptt_device_size); + + // Possible improvement: Skip if inventory already taken. 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; char emsg[100]; 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; } - 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); 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); @@ -638,6 +829,7 @@ void cm108_find_ptt (char *output_audio_device, char *ptt_device, int ptt_devic return; } } +#endif } /* 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. * - * 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. * @@ -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) { @@ -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) { + +#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; struct hidraw_devinfo info; char io[5]; @@ -862,6 +1047,8 @@ static int cm108_write (char *name, int iomask, int iodata) } close (fd); + +#endif return (0); } /* end cm108_write */ diff --git a/src/config.c b/src/config.c index df2061d..0e5d9b4 100644 --- a/src/config.c +++ b/src/config.c @@ -63,7 +63,7 @@ #include "tt_text.h" #include "ax25_link.h" -#ifdef USE_CM108 // Linux only +#if USE_CM108 // Current Linux or Windows only #include "cm108.h" #endif @@ -1854,9 +1854,9 @@ void config_init (char *fname, struct audio_s *p_audio_config, } 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) { // 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); 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); +#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 ("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; } 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 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 ("You must rebuild direwolf with CM108 Audio Adapter GPIO PTT support.\n"); - dw_printf ("See User Guide for details.\n"); -#endif + dw_printf ("See Interface Guide for details.\n"); + exit (EXIT_FAILURE); #endif } diff --git a/src/direwolf.c b/src/direwolf.c index 9b2589e..10c6cb1 100644 --- a/src/direwolf.c +++ b/src/direwolf.c @@ -1,7 +1,7 @@ // // 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 // 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); 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. diff --git a/src/direwolf.h b/src/direwolf.h index b43e5ff..41a7909 100644 --- a/src/direwolf.h +++ b/src/direwolf.h @@ -37,6 +37,12 @@ #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.