diff --git a/99-direwolf-cmedia.rules b/99-direwolf-cmedia.rules new file mode 100644 index 0000000..587f616 --- /dev/null +++ b/99-direwolf-cmedia.rules @@ -0,0 +1,30 @@ +# Normally, all of /dev/hidraw* are accessible only by root. +# +# $ ls -l /dev/hidraw* +# crw------- 1 root root 247, 0 Sep 24 09:40 /dev/hidraw0 +# +# An ordinary user, trying to acccess it will be denied. +# +# Unnecessarily running applications as root is generally a bad idea because it makes it too easy +# to accidentally trash your system. We need to relax the restrictions so ordinary users can use these devices. +# +# If all went well with installation, the /etc/udev/rules.d directory should contain a file called +# 99-direwolf-cmedia.rules containing: +# + +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="0d8c", GROUP="audio", MODE="0660" + +# +# I used the "audio" group, mimicking the permissions on the sound side of the device. +# +# $ ls -l /dev/snd/pcm* +# crw-rw----+ 1 root audio 116, 16 Sep 24 09:40 /dev/snd/pcmC0D0p +# crw-rw----+ 1 root audio 116, 17 Sep 24 09:40 /dev/snd/pcmC0D1p +# +# You should see something similar to this where someone in the "audio" group has read-write access. +# +# $ ls -l /dev/hidraw* +# crw-rw---- 1 root audio 247, 0 Oct 6 19:24 /dev/hidraw0 +# +# Read the User Guide and run the "cm108" application for more information. +# diff --git a/CHANGES.md b/CHANGES.md index 2f95bc4..dbaf01d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -11,6 +11,8 @@ This is a snapshot of ongoing development towards version of 1.5. Some features - KISS "Set Hardware" command to report transmit queue length. +- PTT using GPIO pin of CM108/CM119 (e.g. DMK URI, RB-USB RIM) + ### Bugs Fixed: ### diff --git a/Makefile.linux b/Makefile.linux index a81542f..aa5a90b 100644 --- a/Makefile.linux +++ b/Makefile.linux @@ -4,7 +4,7 @@ -APPS := direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients atest log2gpx gen_packets ttcalc kissutil +APPS := direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients atest log2gpx gen_packets ttcalc kissutil cm108 all : $(APPS) direwolf.desktop direwolf.conf @echo " " @@ -292,6 +292,21 @@ LDFLAGS += -lhamlib endif +# If, for some reason, can obtain the libudev-dev package, or +# don't want to install it, comment out the next 3 lines. + +ifeq ($(wildcard /usr/include/libudev.h),) +$(error /usr/include/libudev.h does not exist. Install it with "sudo apt-get install libudev-dev" or "sudo yum install libudev-dev" ) +endif + + +# Enable cm108 PTT support if libudev header file is present. + +enable_cm108 := $(wildcard /usr/include/libudev.h) +ifneq ($(enable_cm108),) +CFLAGS += -DUSE_CM108 +LDFLAGS += -ludev +endif # Name of current directory. @@ -312,7 +327,7 @@ direwolf : direwolf.o config.o recv.o demod.o dsp.o demod_afsk.o demod_psk.o dem gen_tone.o audio.o audio_stats.o digipeater.o cdigipeater.o pfilter.o dedupe.o tq.o xmit.o morse.o \ ptt.o beacon.o encode_aprs.o latlong.o encode_aprs.o latlong.o textcolor.o \ dtmf.o aprs_tt.o tt_user.o tt_text.o igate.o waypoint.o serial_port.o log.o telemetry.o \ - dwgps.o dwgpsnmea.o dwgpsd.o dtime_now.o mheard.o ax25_link.o \ + dwgps.o dwgpsnmea.o dwgpsd.o dtime_now.o mheard.o ax25_link.o cm108.o \ misc.a geotranz.a $(CC) -o $@ $^ $(LDFLAGS) @echo " " @@ -325,6 +340,11 @@ ifneq ($(enable_hamlib),) @echo "\t>\tThis includes support for hamlib." else @echo "\t>\tThis does NOT include support for hamlib." +endif +ifneq ($(enable_cm108),) + @echo "\t>\tThis includes support for CM108/CM119 PTT." +else + @echo "\t>\tThis does NOT include support for CM108/CM119 PTT." endif @echo " " @@ -435,6 +455,12 @@ kissutil : kissutil.c kiss_frame.c ax25_pad.o fcs_calc.o textcolor.o serial_port $(CC) $(CFLAGS) -g -DKISSUTIL -o $@ $^ $(LDFLAGS) +# List USB audio adapters than can use GPIO for PTT. + +cm108 : cm108.c textcolor.o misc.a + $(CC) $(CFLAGS) -g -DCM108_MAIN -o $@ $^ $(LDFLAGS) + + # Touch Tone to Speech sample application. ttcalc : ttcalc.o ax25_pad.o fcs_calc.o textcolor.o misc.a @@ -569,6 +595,7 @@ install : $(APPS) direwolf.conf tocalls.txt symbols-new.txt symbolsX.txt dw-icon $(INSTALL) atest $(INSTALLDIR)/bin $(INSTALL) ttcalc $(INSTALLDIR)/bin $(INSTALL) kissutil $(INSTALLDIR)/bin + $(INSTALL) cm108 $(INSTALLDIR)/bin $(INSTALL) dwespeak.sh $(INSTALLDIR)/bin # # Telemetry Toolkit executables. Other .conf and .txt files will go into doc directory. @@ -643,6 +670,11 @@ install : $(APPS) direwolf.conf tocalls.txt symbols-new.txt symbolsX.txt dw-icon $(INSTALL) -D --mode=644 man1/text2tt.1 $(INSTALLDIR)/man/man1/text2tt.1 $(INSTALL) -D --mode=644 man1/tt2text.1 $(INSTALLDIR)/man/man1/tt2text.1 $(INSTALL) -D --mode=644 man1/utm2ll.1 $(INSTALLDIR)/man/man1/utm2ll.1 +# +# Set group and mode of HID devices corresponding to C-Media USB Audio adapters. +# This will allow us to use the CM108/CM119 GPIO pins for PTT. +# + $(INSTALL) -D --mode=644 99-direwolf-cmedia.rules /etc/udev/rules.d/99-direwolf-cmedia.rules # @echo " " @echo "If this is your first install, not an upgrade, type this to put a copy" diff --git a/audio.h b/audio.h index 6afda51..13fb22c 100644 --- a/audio.h +++ b/audio.h @@ -29,8 +29,9 @@ enum ptt_method_e { PTT_METHOD_NONE, /* VOX or no transmit. */ PTT_METHOD_SERIAL, /* Serial port RTS or DTR. */ PTT_METHOD_GPIO, /* General purpose I/O, Linux only. */ - PTT_METHOD_LPT, /* Parallel printer port, Linux only. */ - PTT_METHOD_HAMLIB }; /* HAMLib, Linux only. */ + PTT_METHOD_LPT, /* Parallel printer port, Linux only. */ + PTT_METHOD_HAMLIB, /* HAMLib, Linux only. */ + PTT_METHOD_CM108 }; /* GPIO pin of CM108/CM119/etc. Linux only. */ typedef enum ptt_method_e ptt_method_t; @@ -191,13 +192,17 @@ struct audio_s { struct { - ptt_method_t ptt_method; /* none, serial port, GPIO, LPT, HAMLIB. */ + 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 */ /* Also used for HAMLIB. Could be host:port when model is 1. */ - /* For years, 20 characters was plenty and then along comes this crazy name: */ + /* 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. */ 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. */ @@ -205,8 +210,9 @@ 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. */ -#define MAX_GPIO_NAME_LEN 16 // 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 char out_gpio_name[MAX_GPIO_NAME_LEN]; /* orginally, gpio number NN was assumed to simply */ @@ -215,6 +221,8 @@ struct audio_s { /* This is filled in by ptt_init so we don't have to */ /* recalculate it each time we access it. */ + /* This could probably be collapsed into ptt_device instead of being separate. */ + int ptt_lpt_bit; /* Bit number for parallel printer port. */ /* Bit 0 = pin 2, ..., bit 7 = pin 9. */ diff --git a/cm108.c b/cm108.c new file mode 100644 index 0000000..5454e63 --- /dev/null +++ b/cm108.c @@ -0,0 +1,706 @@ +// +// This file is part of Dire Wolf, an amateur radio packet TNC. +// +// Copyright (C) 2017 John Langner, WB2OSZ +// +// Parts of this were adapted from "hamlib" which contains the notice: +// +// * Copyright (c) 2000-2012 by Stephane Fillod +// * Copyright (c) 2011 by Andrew Errington +// * CM108 detection code Copyright (c) Thomas Sailer used with permission +// +// 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 +// the Free Software Foundation, either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +/*------------------------------------------------------------------ + * + * Module: cm108.c + * + * Purpose: Use the CM108/CM119 (or compatible) GPIO pins for the Push To Talk (PTT) Control. + * + * Description: + * + * There is an incresing demand for using the GPIO pins of USB audio devices for PTT. + * We have a couple commercial products: + * + * DMK URI http://www.dmkeng.com/URI_Order_Page.htm + * RB-USB RIM http://www.repeater-builder.com/products/usb-rim-lite.html + * + * and homebrew projects which are all very similar. + * + * http://www.qsl.net/kb9mwr/projects/voip/usbfob-119.pdf + * http://rtpdir.weebly.com/uploads/1/6/8/7/1687703/usbfob.pdf + * 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. + * + * 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 + * roundabout way. This is documented in the User Guide, section called, + * "Hamlib PTT Example 2: Use GPIO of USB audio adapter. (e.g. DMK URI)" + * + * It's rather involved and the explantion doesn't cover the case of multiple + * USB-Audio adapters. It is not as straightforward as you might expect. Here we have + * an example of 3 C-Media USB adapters, a SignaLink USB, a keyboard, and a mouse. + * + * + * VID PID Product Sound ADEVICE HID [ptt] + * --- --- ------- ----- ------- --------- + * ** 0d8c 000c C-Media USB Headphone Set /dev/snd/pcmC1D0c plughw:1,0 /dev/hidraw0 + * ** 0d8c 000c C-Media USB Headphone Set /dev/snd/pcmC1D0p plughw:1,0 /dev/hidraw0 + * ** 0d8c 000c C-Media USB Headphone Set /dev/snd/controlC1 /dev/hidraw0 + * 08bb 2904 USB Audio CODEC /dev/snd/pcmC2D0c plughw:2,0 /dev/hidraw2 + * 08bb 2904 USB Audio CODEC /dev/snd/pcmC2D0p plughw:2,0 /dev/hidraw2 + * 08bb 2904 USB Audio CODEC /dev/snd/controlC2 /dev/hidraw2 + * ** 0d8c 000c C-Media USB Headphone Set /dev/snd/pcmC0D0c plughw:0,0 /dev/hidraw1 + * ** 0d8c 000c C-Media USB Headphone Set /dev/snd/pcmC0D0p plughw:0,0 /dev/hidraw1 + * ** 0d8c 000c C-Media USB Headphone Set /dev/snd/controlC0 /dev/hidraw1 + * ** 0d8c 0008 C-Media USB Audio Device /dev/snd/pcmC4D0c plughw:4,0 /dev/hidraw6 + * ** 0d8c 0008 C-Media USB Audio Device /dev/snd/pcmC4D0p plughw:4,0 /dev/hidraw6 + * ** 0d8c 0008 C-Media USB Audio Device /dev/snd/controlC4 /dev/hidraw6 + * 413c 2010 Dell USB Keyboard /dev/hidraw4 + * 0461 4d15 USB Optical Mouse /dev/hidraw5 + * + * + * The USB soundcards (/dev/snd/pcm...) have an associated Human Interface Device (HID) + * corresponding to the GPIO pins which are sometimes connected to pushbuttons. + * The mapping has no obvious pattern. + * + * Sound Card 0 HID 1 + * Sound Card 1 HID 0 + * Sound Card 2 HID 2 + * Sound Card 4 HID 6 + * + * That would be a real challenge if you had to figure that all out and configure manually. + * 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. + * + *---------------------------------------------------------------*/ + +#ifndef USE_CM108 + +#ifdef CM108_MAIN + +#include "direwolf.h" +#include "textcolor.h" + +int main (void) +{ + text_color_init (0); // Turn off text color. + dw_printf ("CM108 PTT support was disabled in Makefile.linux.\n"); + return (0); +} + +#endif + +#else // USE_CM108 is defined. + +#include "direwolf.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include // ioctl, _IOR +#include +#include +#include // for HIDIOCGRAWINFO + +#include "textcolor.h" +#include "cm108.h" + +static int cm108_write (char *name, int iomask, int iodata); + + +// The CM108, CM109, and CM119 datasheets all say that idProduct can be in the range +// of 0008 to 000f programmable by MSEL and MODE pin. How can we tell the difference? + +// CM108B is 0012. +// CM119B is 0013. +// CM108AH is 0139 programmable by MSEL and MODE pin. +// CM119A is 013A programmable by MSEL and MODE pin. + +// To make matters even more confusing, these can be overridden +// with an external EEPROM. Some have 8, rather than 4 GPIO. + +#define CMEDIA_VID 0xd8c // Vendor ID +#define CMEDIA_PID1_MIN 0x0008 // range for CM108, CM109, CM119 (no following letters) +#define CMEDIA_PID1_MAX 0x000f + +#define CMEDIA_PID2 0x013a // CM119A + +#define CMEDIA_PID_CM108AH 0x0139 // CM108AH +#define CMEDIA_PID_CM108B 0x0012 // CM108B +#define CMEDIA_PID_CM119A 0x013a // CM119A +#define CMEDIA_PID_CM119B 0x0013 // CM119B +#define CMEDIA_PID_HS100 0x013c // HS100 + +// The SSS chips seem to be pretty much compatible but they have only two GPIO. +// https://irongarment.wordpress.com/2011/03/29/cm108-compatible-chips-with-gpio/ +// Data sheet says VID/PID is from an EEPROM but mentions no default. + +#define SSS_VID 0x0c76 // SSS1621, SSS1623 +#define SSS_PID1 0x1605 +#define SSS_PID2 0x1607 +#define SSS_PID3 0x160b + + +// Device VID PID Number of GPIO +// ------ --- --- -------------- +// CM108 0d8c 0008-000f * 4 +// CM108AH 0d8c 0139 * 3 Has GPIO 1,3,4 but not 2 +// CM108B 0d8c 0012 3 Has GPIO 1,3,4 but not 2 +// CM109 0d8c 0008-000f * 8 +// CM119 0d8c 0008-000f * 8 +// CM119A 0d8c 013a * 8 +// CM119B 0d8c 0013 8 +// HS100 0d8c 013c 0 +// +// SSS1621 0c76 1605 2 per ZL3AME, Can't find data sheet +// SSS1623 0c76 1607,160b 2 per ZL3AME, Not in data sheet. +// +// * idProduct programmable by MSEL and MODE pin. +// + +// CMedia pin GPIO Notes +// ---------- ---- ----- +// 43 1 +// 11 2 N.C. for CM108AH, CM108B +// 13 3 Most popular for PTT because it is on the end. +// 15 4 +// 16 5 CM109, CM119, CM119A, CM119B only +// 17 6 " +// 20 7 " +// 22 8 " + +// Test for supported devices. + +#define GOOD_DEVICE(v,p) ( (v == CMEDIA_VID && ((p >= CMEDIA_PID1_MIN && p <= CMEDIA_PID1_MAX) || p == CMEDIA_PID2)) || \ + (v == SSS_VID && (p == SSS_PID1 || p == SSS_PID2 || p == SSS_PID3)) ) + +// Look out for null source pointer, and avoid buffer overflow on destination. + +#define SAFE_STRCPY(to,from) { if (from != NULL) { strncpy(to,from,sizeof(to)); to[sizeof(to)-1] = '\0'; } } + + +// Used to process regular expression matching results. + +static void inline substr_se (char *dest, const char *src, int start, int endp1) +{ + int len = endp1 - start; + + if (start < 0 || endp1 < 0 || len <= 0) { + dest[0] = '\0'; + return; + } + memcpy (dest, src + start, len); + dest[len] = '\0'; + +} /* end substr_se */ + + +/* + * Result of taking inventory of USB soundcards and USB HIDs. + */ + +struct thing_s { + int vid; // vendor id + int pid; // product id + char product[32]; // product name (e.g. manufacturer, model) + char devnode_sound[22]; // e.g. /dev/snd/pcmC0D0p + char plughw[15]; // Above in more familiar format e.g. plughw:0,0 + char devnode_hidraw[17]; // e.g. /dev/hidraw3 + char devnode_usb[25]; // e.g. /dev/bus/usb/001/012 +}; + +int cm108_inventory (struct thing_s *things, int max_things); + + +/*------------------------------------------------------------------- + * + * Name: main + * + * Purpose: Test program to list USB audio and HID devices. + * + * sudo apt-get install libudev-dev + * gcc -DCM108_MAIN textcolor.c -l udev + * + *------------------------------------------------------------------*/ + +#define MAXX_THINGS 60 + +#ifdef CM108_MAIN + +int main (void) +{ + struct thing_s things[MAXX_THINGS]; + int num_things; + int i; + + text_color_init (0); // Turn off text color. + + num_things = cm108_inventory (things, MAXX_THINGS); + + dw_printf (" VID PID %-*s %-*s %-*s %-*s" +#if EXTRA + " %-*s" +#endif + "\n", (int)sizeof(things[0].product), "Product", + (int)sizeof(things[0].devnode_sound), "Sound", + (int)sizeof(things[0].plughw), "ADEVICE", + (int)sizeof(things[0].devnode_hidraw), "HID [ptt]" +#if EXTRA + , (int)sizeof(things[0].devnode_usb), "USB" +#endif + ); + + dw_printf (" --- --- %-*s %-*s %-*s %-*s" +#if EXTRA + " %-*s" +#endif + "\n", (int)sizeof(things[0].product), "-------", + (int)sizeof(things[0].devnode_sound), "-----", + (int)sizeof(things[0].plughw), "-------", + (int)sizeof(things[0].devnode_hidraw), "---------" +#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" +#if EXTRA + " %-*s" +#endif + "\n", + GOOD_DEVICE(things[i].vid,things[i].pid) ? "**" : " ", + things[i].vid, things[i].pid, + (int)sizeof(things[i].product), things[i].product, + (int)sizeof(things[i].devnode_sound), things[i].devnode_sound, + (int)sizeof(things[0].plughw), things[i].plughw, + (int)sizeof(things[i].devnode_hidraw), things[i].devnode_hidraw +#if EXTRA + , (int)sizeof(things[i].devnode_usb), things[i].devnode_usb +#endif + ); + } + + return (0); +} + +#endif // CM108_MAIN + + + +/*------------------------------------------------------------------- + * + * Name: cm108_inventory + * + * Purpose: Take inventory of USB audio and HID. + * + * Inputs: max_things - Maximum number of items to collect. + * + * Outputs: things - Array of items collected. + * Corresponding sound device and HID are merged into one item. + * + * Returns: Number of items placed in things array. + * Should be in the range of 0 thru max_things. + * -1 for a bad unexpected error. + * + *------------------------------------------------------------------*/ + + +int cm108_inventory (struct thing_s *things, int max_things) +{ + struct udev *udev; + struct udev_enumerate *enumerate; + struct udev_list_entry *devices, *dev_list_entry; + struct udev_device *dev; + struct udev_device *parentdev; + + int num_things = 0; + memset (things, 0, sizeof(struct thing_s) * max_things); + +/* + * First get a list of the USB audio devices. + * This is based on the example in http://www.signal11.us/oss/udev/ + */ + udev = udev_new(); + if (!udev) { + text_color_set(DW_COLOR_ERROR); + dw_printf("INTERNAL ERROR: Can't create udev.\n"); + return (-1); + } + + enumerate = udev_enumerate_new(udev); + udev_enumerate_add_match_subsystem(enumerate, "sound"); + udev_enumerate_scan_devices(enumerate); + devices = udev_enumerate_get_list_entry(enumerate); + udev_list_entry_foreach(dev_list_entry, devices) { + const char *path; + path = udev_list_entry_get_name(dev_list_entry); + dev = udev_device_new_from_syspath(udev, path); + char const *devnode = udev_device_get_devnode(dev); + if (devnode != NULL) { + parentdev = udev_device_get_parent_with_subsystem_devtype( dev, "usb", "usb_device"); + if (parentdev != NULL) { + char const *p; + int vid = 0; + int pid = 0; + + p = udev_device_get_sysattr_value(parentdev,"idVendor"); + if (p != NULL) vid = strtol(p, NULL, 16); + p = udev_device_get_sysattr_value(parentdev,"idProduct"); + if (p != NULL) pid = strtol(p, NULL, 16); + + if (num_things < max_things) { + things[num_things].vid = vid; + things[num_things].pid = pid; + SAFE_STRCPY (things[num_things].product, udev_device_get_sysattr_value(parentdev,"product")); + SAFE_STRCPY (things[num_things].devnode_sound, devnode); + SAFE_STRCPY (things[num_things].devnode_usb, udev_device_get_devnode(parentdev)); + num_things++; + } + udev_device_unref(parentdev); + } + } + } + udev_enumerate_unref(enumerate); + udev_unref(udev); + +/* + * Now merge in all of the USB HID. + */ + udev = udev_new(); + if (!udev) { + text_color_set(DW_COLOR_ERROR); + dw_printf("INTERNAL ERROR: Can't create udev.\n"); + return (-1); + } + + enumerate = udev_enumerate_new(udev); + udev_enumerate_add_match_subsystem(enumerate, "hidraw"); + udev_enumerate_scan_devices(enumerate); + devices = udev_enumerate_get_list_entry(enumerate); + udev_list_entry_foreach(dev_list_entry, devices) { + const char *path; + path = udev_list_entry_get_name(dev_list_entry); + dev = udev_device_new_from_syspath(udev, path); + char const *devnode = udev_device_get_devnode(dev); + if (devnode != NULL) { + parentdev = udev_device_get_parent_with_subsystem_devtype( dev, "usb", "usb_device"); + if (parentdev != NULL) { + char const *p; + int vid = 0; + int pid = 0; + + p = udev_device_get_sysattr_value(parentdev,"idVendor"); + if (p != NULL) vid = strtol(p, NULL, 16); + p = udev_device_get_sysattr_value(parentdev,"idProduct"); + if (p != NULL) pid = strtol(p, NULL, 16); + + int j, matched = 0; + char const *usb = udev_device_get_devnode(parentdev); + + // Add hidraw name to any matching existing. + for (j = 0; j < num_things; j++) { + if (things[j].vid == vid && things[j].pid == pid && usb != NULL && strcmp(things[j].devnode_usb,usb) == 0) { + matched = 1; + SAFE_STRCPY (things[j].devnode_hidraw, devnode); + } + } + + // If it did not match to existing, add new entry. + if (matched == 0 && num_things < max_things) { + things[num_things].vid = vid; + things[num_things].pid = pid; + SAFE_STRCPY (things[num_things].product, udev_device_get_sysattr_value(parentdev,"product")); + SAFE_STRCPY (things[num_things].devnode_hidraw, devnode); + SAFE_STRCPY (things[num_things].devnode_usb, usb); + num_things++; + } + udev_device_unref(parentdev); + } + } + } + udev_enumerate_unref(enumerate); + udev_unref(udev); + +/* + * Seeing the form /dev/snd/pcmC4D0p will be confusing to many because we + * would generally something like plughw:4,0 for in the direwolf configuration file. + * Construct the more familiar form. + */ + int i; + regex_t pcm_re; + char emsg[100]; + int e = regcomp (&pcm_re, "pcmC([0-9]+)D([0-9]+)[cp]", REG_EXTENDED); + if (e) { + regerror (e, &pcm_re, emsg, sizeof(emsg)); + text_color_set(DW_COLOR_ERROR); + dw_printf("INTERNAL ERROR: %s:%d: %s\n", __FILE__, __LINE__, emsg); + return (-1); + } + + for (i = 0; i < num_things; i++) { + regmatch_t match[3]; + + if (regexec (&pcm_re, things[i].devnode_sound, 3, match, 0) == 0) { + char c[32], d[32]; + substr_se (c, things[i].devnode_sound, match[1].rm_so, match[1].rm_eo); + substr_se (d, things[i].devnode_sound, match[2].rm_so, match[2].rm_eo); + snprintf (things[i].plughw, sizeof(things[i].plughw), "plughw:%s,%s", c, d); + } + } + + return (num_things); + +} /* end cm108_inventory */ + + +/*------------------------------------------------------------------- + * + * Name: cm108_find_ptt + * + * Purpose: Try to find /dev/hidraw corresponding to plughw:n,n + * + * Inputs: output_audio_device + * - Device name used in the ADEVICE configuration. + * This would generally be something like plughw:1,0 + * + * ptt_device_size - Size of result area to avoid buffer overflow. + * + * Outputs: ptt_device - Device name, something like /dev/hidraw2. + * Will be emptry string if no match found. + * + * Returns: none + * + *------------------------------------------------------------------*/ + +void cm108_find_ptt (char *output_audio_device, char *ptt_device, int ptt_device_size) +{ + struct thing_s things[MAXX_THINGS]; + int num_things; + int i; + + strlcpy (ptt_device, "", ptt_device_size); + num_things = cm108_inventory (things, MAXX_THINGS); + + for (i = 0; i < num_things; i++) { + + if (GOOD_DEVICE(things[i].vid,things[i].pid) ) { + if (strcmp(output_audio_device, things[i].plughw) == 0) { + strlcpy (ptt_device, things[i].devnode_hidraw, ptt_device_size); + } + } + } + +} /* end cm108_find_ptt */ + + + +/*------------------------------------------------------------------- + * + * Name: cm108_set_gpio_pin + * + * Purpose: Set one GPIO pin of the CM108 or similar. + * + * Inputs: name - Name of device such as /dev/hidraw2. + * + * num - GPIO number, range 1 thru 8. + * + * state - 1 for on, 0 for off. + * + * Returns: 0 for success. -1 for error. + * + * Errors: A descriptive error message will be printed for any problem. + * + * Future: For our initial implementation we are making the simplifying + * restriction of using only one GPIO pin per device and limit + * configuratin to PTT only. + * Longer term, we might want to have DCD, and maybe other + * controls thru the same chip. + * In this case, we would need to retain bit masks for each + * device so new data can be merged with old before sending it out. + * + *------------------------------------------------------------------*/ + +int cm108_set_gpio_pin (char *name, int num, int state) +{ + int iomask; + int iodata; + + if (num < 1 || num > 8) { + text_color_set(DW_COLOR_ERROR); + dw_printf("%s CM108 GPIO number %d must be in range of 1 thru 8.\n", name, num); + return (-1); + } + + if (state != 0 && state != 1) { + text_color_set(DW_COLOR_ERROR); + dw_printf("%s CM108 GPIO state %d must be 0 or 1.\n", name, state); + return (-1); + } + + iomask = 1 << (num - 1); + iodata = state << (num - 1); + + return (cm108_write (name, iomask, iodata)); + +} /* end cm108_set_gpio_pin */ + + +/*------------------------------------------------------------------- + * + * Name: cm108_write + * + * Purpose: Set the GPIO pins of the CM108 or similar. + * + * Inputs: name - Name of device such as /dev/hidraw2. + * + * iomask - Bit mask for I/O direction. + * LSB is GPIO1, bit 1 is GPIO2, etc. + * 1 for output, 0 for input. + * + * iodata - Output data, same bit order as iomask. + * + * Returns: 0 for success. -1 for error. + * + * Errors: A descriptive error message will be printed for any problem. + * + * Description: This is the lowest level function. + * An application probably wants to use cm108_set_gpio_pin. + * + *------------------------------------------------------------------*/ + +static int cm108_write (char *name, int iomask, int iodata) +{ + int fd; + struct hidraw_devinfo info; + char io[5]; + int n; + + //text_color_set(DW_COLOR_DEBUG); + //dw_printf ("TEMP DEBUG cm108_write: %s %d %d\n", name, iomask, iodata); + +/* + * By default, the USB HID are accessible only by root: + * + * crw------- 1 root root 249, 1 ... /dev/hidraw1 + * + * How should we handle this? + * Manually changing it will revert back on the next reboot or + * when the device is removed and reinserted. + * + * According to various articles on the Internet, we should be able to + * add a file to /etc/udev/rules.d. "99-direwolf-cmedia.rules" would be a + * suitable name. The leading number is the order. We want this to be + * near the end. I think the file extension must be ".rules." + * + * We could completely open it up to everyone like this: + * + * # Allow ordinary user to access CMedia GPIO for PTT. + * SUBSYSTEM=="hidraw", ATTRS{idVendor}=="0d8c", MODE="0666" + * + * Whenever we have CMedia USB audio adapter, it should be accessible by everyone. + * This would not apply to other /dev/hidraw* corresponding to keyboard, mouse, etc. + * + * Notice the == (double =) for testing and := for setting a property. + * + * If you are concerned about security, you could restrict access to + * a particular group, something like this: + * + * SUBSYSTEM=="hidraw", ATTRS{idVendor}=="0d8c", GROUP="audio", MODE="0660" + * + * I figure "audio" makes more sense than "gpio" because we need to be part of + * audio group to use the USB Audio adapter for sound. + */ + + fd = open (name, O_WRONLY); + if (fd == -1) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("Could not open %s for write, errno=%d\n", name, errno); + if (errno == EACCES) { // 13 + dw_printf ("Type \"ls -l %s\" and verify that it has audio group rw similar to this:\n", name); + dw_printf (" crw-rw---- 1 root audio 247, 0 Oct 6 19:24 %s\n", name); + dw_printf ("rather than root-only access like this:\n"); + dw_printf (" crw------- 1 root root 247, 0 Sep 24 09:40 %s\n", name); + } + return (-1); + } + + // Just for fun, let's get the device information. + +#if 1 + n = ioctl(fd, HIDIOCGRAWINFO, &info); + if (n == 0) { + if ( ! GOOD_DEVICE(info.vendor, info.product)) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("ioctl HIDIOCGRAWINFO failed for %s. errno = %d.\n", name, errno); + } + } + else { + text_color_set(DW_COLOR_ERROR); + dw_printf ("%s is not a supported device type. Proceed at your own risk. vid=%04x pid=%04x\n", name, info.vendor, info.product); + } +#endif + // To make a long story short, I think we need 0 for the first two bytes. + + io[0] = 0; + io[1] = 0; + io[2] = iomask; + io[3] = iodata; + io[4] = 0; + + // Writing 4 bytes fails with errno 32, EPIPE, "broken pipe." + // Hamlib writes 5 bytes which I don't understand. + // Writing 5 bytes works. + // I have no idea why. From the CMedia datasheet it looks like we need 4. + + n = write (fd, io, sizeof(io)); + if (n != sizeof(io)) { + // Errors observed during development. + // as pi EACCES 13 /* Permission denied */ + // as root EPIPE 32 /* Broken pipe - Happens if we send 4 bytes */ + + text_color_set(DW_COLOR_ERROR); + dw_printf ("Write to %s failed, n=%d, errno=%d\n", name, n, errno); + + if (errno == EACCES) { + dw_printf ("Type \"ls -l %s\" and verify that it has audio group rw similar to this:\n", name); + dw_printf (" crw-rw---- 1 root audio 247, 0 Oct 6 19:24 %s\n", name); + dw_printf ("rather than root-only access like this:\n"); + dw_printf (" crw------- 1 root root 247, 0 Sep 24 09:40 %s\n", name); + } + + close (fd); + return (-1); + } + + close (fd); + return (0); + +} /* end cm108_write */ + +#endif // ifdef USE_CM108 + +/* end cm108.c */ + + \ No newline at end of file diff --git a/cm108.h b/cm108.h new file mode 100644 index 0000000..2def77a --- /dev/null +++ b/cm108.h @@ -0,0 +1,5 @@ +/* Dire Wolf cm108.h */ + +extern void cm108_find_ptt (char *output_audio_device, char *ptt_device, int ptt_device_size); + +extern int cm108_set_gpio_pin (char *name, int num, int state); \ No newline at end of file diff --git a/config.c b/config.c index 3c1d4e1..ed8a20e 100644 --- a/config.c +++ b/config.c @@ -63,6 +63,10 @@ #include "tt_text.h" #include "ax25_link.h" +#ifdef USE_CM108 // Linux only +#include "cm108.h" +#endif + // geotranz #include "utm.h" @@ -1613,6 +1617,7 @@ void config_init (char *fname, struct audio_s *p_audio_config, * xxx LPT [-]bit-num * PTT RIG model port * PTT RIG AUTO port + * PTT CM108 [ [-]bit-num ] [ hid-device ] * * When model is 2, port would host:port like 127.0.0.1:4532 * Otherwise, port would be a serial port like /dev/ttyS0 @@ -1737,16 +1742,97 @@ void config_init (char *fname, struct audio_s *p_audio_config, p_audio_config->achan[channel].octrl[ot].ptt_method = PTT_METHOD_HAMLIB; +#else +#if __WIN32__ + text_color_set(DW_COLOR_ERROR); + dw_printf ("Config file line %d: Windows version of direwolf does not support HAMLIB.\n", line); + exit (EXIT_FAILURE); #else text_color_set(DW_COLOR_ERROR); dw_printf ("Config file line %d: %s with RIG is only available when hamlib support is enabled.\n", line, otname); -#if __WIN32__ - dw_printf ("Hamlib is not currently supported on Windows.\n"); -#else dw_printf ("You must rebuild direwolf with hamlib support.\n"); dw_printf ("See User Guide for details.\n"); #endif +#endif + } + if (strcasecmp(t, "CM108") == 0) { + +/* CM108 - GPIO of USB sound card. case, Linux only. */ + +#ifdef USE_CM108 + + if (ot != OCTYPE_PTT) { + // Future project: Allow DCD and CON via the same device. + // This gets more complicated because we can't selectively change a single GPIO bit. + // We would need to keep track of what is currently there, change one bit, in our local + // copy of the status and then write out the byte for all of the pins. + // Let's keep it simple with just PTT for the first stab at this. + + text_color_set(DW_COLOR_ERROR); + dw_printf ("Config file line %d: PTT CM108 option is only valid for PTT, not %s.\n", line, otname); + continue; + } + + p_audio_config->achan[channel].octrl[ot].out_gpio_num = 3; // All known designs use GPIO 3. + // User can override for special cases. + p_audio_config->achan[channel].octrl[ot].ptt_invert = 0; // High for transmit. + strcpy (p_audio_config->achan[channel].octrl[ot].ptt_device, ""); + + // Try to find PTT device for audio output device. + // Simplifiying assumption is that we have one radio per USB Audio Adapter. + // Failure at this point is not an error. + // See if config file sets it explicitly before complaining. + + cm108_find_ptt (p_audio_config->adev[ACHAN2ADEV(channel)].adevice_out, + p_audio_config->achan[channel].octrl[ot].ptt_device, + (int)sizeof(p_audio_config->achan[channel].octrl[ot].ptt_device)); + + while ((t = split(NULL,0)) != NULL) { + if (*t == '-') { + p_audio_config->achan[channel].octrl[ot].out_gpio_num = atoi(t+1); + p_audio_config->achan[channel].octrl[ot].ptt_invert = 1; + } + else if (isdigit(*t)) { + p_audio_config->achan[channel].octrl[ot].out_gpio_num = atoi(t); + p_audio_config->achan[channel].octrl[ot].ptt_invert = 0; + } + else if (*t == '/') { + strlcpy (p_audio_config->achan[channel].octrl[ot].ptt_device, t, sizeof(p_audio_config->achan[channel].octrl[ot].ptt_device)); + } + else { + text_color_set(DW_COLOR_ERROR); + dw_printf ("Config file line %d: Found \"%s\" when expecting GPIO number or device name like /dev/hidraw1.\n", line, t); + continue; + } + } + if (p_audio_config->achan[channel].octrl[ot].out_gpio_num < 1 || p_audio_config->achan[channel].octrl[ot].out_gpio_num > 8) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("Config file line %d: CM108 GPIO number %d is not in range of 1 thru 8.\n", line, + p_audio_config->achan[channel].octrl[ot].out_gpio_num); + continue; + } + if (strlen(p_audio_config->achan[channel].octrl[ot].ptt_device) == 0) { + 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); + dw_printf ("You must explicitly mention a device name such as /dev/hidraw1.\n"); + dw_printf ("See User 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 + exit (EXIT_FAILURE); #endif } else { diff --git a/doc/Raspberry-Pi-APRS.pdf b/doc/Raspberry-Pi-APRS.pdf index 774526d..328d269 100644 Binary files a/doc/Raspberry-Pi-APRS.pdf and b/doc/Raspberry-Pi-APRS.pdf differ diff --git a/doc/User-Guide.pdf b/doc/User-Guide.pdf index 5e337cc..da393db 100644 Binary files a/doc/User-Guide.pdf and b/doc/User-Guide.pdf differ diff --git a/ptt.c b/ptt.c index da5429a..c070e5a 100644 --- a/ptt.c +++ b/ptt.c @@ -52,6 +52,9 @@ * * Handle more complicated gpio node names for CubieBoard, etc. * + * Version 1.5: Ability to use GPIO pins of CM108/CM119 for PTT signal. + * + * * References: http://www.robbayer.com/files/serial-win.pdf * * https://www.kernel.org/doc/Documentation/gpio.txt @@ -59,16 +62,20 @@ *---------------------------------------------------------------*/ /* - Idea for future enhancement: + A growing number of people have been asking about support for the DMK URI + or the similar RB-USB RIM. - A growing number of people have been asking about support for the DMK URI. - This uses a C-Media CM108/CM119 with one interesting addition, a GPIO + These use a C-Media CM108/CM119 with an interesting addition, a GPIO pin is used to drive PTT. Here is some related information. DMK URI: http://www.dmkeng.com/URI_Order_Page.htm http://dmkeng.com/images/URI%20Schematic.pdf + + RB-USB RIM: + + http://www.repeater-builder.com/products/usb-rim-lite.html http://www.repeater-builder.com/voip/pdf/cm119-datasheet.pdf Homebrew versions of the same idea: @@ -83,6 +90,7 @@ http://docs.allstarlink.org/drupal/ http://soundmodem.sourcearchive.com/documentation/0.16-1/ptt_8c_source.html https://github.com/N0NB/hamlib/blob/master/src/cm108.c#L190 + http://permalink.gmane.org/gmane.linux.hams.hamlib.devel/3420 Information about the "hidraw" device: @@ -92,12 +100,31 @@ https://github.com/signal11/hidapi/blob/master/libusb/hid.c http://stackoverflow.com/questions/899008/howto-write-to-the-gpio-pin-of-the-cm108-chip-in-linux https://www.kernel.org/doc/Documentation/hid/hidraw.txt + https://github.com/torvalds/linux/blob/master/samples/hidraw/hid-example.c - In version 1.3, we add HAMLIB support which should be able to do this. - (Linux only & haven't verified that it actually works yet!) + Similar chips: SSS1621, SSS1623 + + https://irongarment.wordpress.com/2011/03/29/cm108-compatible-chips-with-gpio/ + + Here is an attempt to add direct CM108 support. + Seems to be hardcoded for only a single USB audio adapter. + + https://github.com/donothingloop/direwolf_cm108 + + In version 1.3, we add HAMLIB support which should be able to do this in a roundabout way. + (Linux only at this point.) + + This is documented in the User Guide, section called, + "Hamlib PTT Example 2: Use GPIO of USB audio adapter. (e.g. DMK URI)" + + It's rather involved and the explantion doesn't cover the case of multiple + USB-Audio adapters. It would be nice to have a little script which lists all + of the USB-Audio adapters and the corresponding /dev/hidraw device. + ( We now have it. The included "cm108" application. ) - Might want to have CM108 GPIO support built in, someday, for simpler building & configuration. - Maybe even for Windows. ;-) + In version 1.5 we have a flexible, easy to use implementation for Linux. + Windows would be a lot of extra work because USB devices are nothing like Linux. + We'd be starting from scratch to figure out how to do it. */ @@ -126,6 +153,10 @@ #include #endif +#ifdef USE_CM108 +#include "cm108.h" +#endif + /* So we can have more common code for fd. */ typedef int HANDLE; #define INVALID_HANDLE_VALUE (-1) @@ -157,10 +188,6 @@ typedef int HANDLE; #endif -#if TEST -#define dw_printf printf -#endif - static struct audio_s *save_audio_config_p; /* Save config information for later use. */ @@ -603,10 +630,12 @@ void export_gpio(int ch, int ot, int invert, int direction) * PTT_METHOD_GPIO - general purpose I/O. * PTT_METHOD_LPT - Parallel printer port. * PTT_METHOD_HAMLIB - HAMLib rig control. + * PTT_METHOD_CM108 - GPIO pins of CM108 etc. USB Audio. * * ptt_device Name of serial port device. * e.g. COM1 or /dev/ttyS0. * HAMLIB can also use hostaddr:port. + * Like /dev/hidraw1 for CM108. * * ptt_line RTS or DTR when using serial port. * @@ -992,6 +1021,32 @@ void ptt_init (struct audio_s *audio_config_p) #endif +/* + * Confirm what is going on with CM108 GPIO output. + * Could use some error checking for overlap. + */ + +#if USE_CM108 + + for (ch = 0; ch < MAX_CHANS; ch++) { + + if (audio_config_p->achan[ch].valid) { + int ot; + for (ot = 0; ot < NUM_OCTYPES; ot++) { + if (audio_config_p->achan[ch].octrl[ot].ptt_method == PTT_METHOD_CM108) { + text_color_set(DW_COLOR_INFO); + dw_printf ("Using %s GPIO %d for channel %d %s control.\n", + audio_config_p->achan[ch].octrl[ot].ptt_device, + audio_config_p->achan[ch].octrl[ot].out_gpio_num, + ch, + otnames[ot]); + } + } + } + } + +#endif + /* Why doesn't it transmit? Probably forgot to specify PTT option. */ @@ -1059,7 +1114,9 @@ void ptt_set (int ot, int chan, int ptt_signal) * This is a very convenient place to get that information. */ +#ifndef TEST dlq_channel_busy (chan, ot, ptt_signal); +#endif /* * Inverted output? @@ -1221,6 +1278,21 @@ void ptt_set (int ot, int chan, int ptt_signal) } #endif +/* + * Using CM108 USB Audio adapter GPIO? + */ + +#ifdef USE_CM108 + + if (save_audio_config_p->achan[chan].octrl[ot].ptt_method == PTT_METHOD_CM108) { + + if (cm108_set_gpio_pin (save_audio_config_p->achan[chan].octrl[ot].ptt_device, + save_audio_config_p->achan[chan].octrl[ot].out_gpio_num, ptt) != 0) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("ERROR: %s for channel %d has failed. See User Guide for troubleshooting tips.\n", otnames[ot], chan); + } + } +#endif } /* end ptt_set */ @@ -1354,18 +1426,15 @@ void ptt_term (void) /* * Quick stand-alone test for above. * - * gcc -DTEST -o ptest ptt.c ; ./ptest + * gcc -DTEST -o ptest ptt.c textcolor.o misc.a ; ./ptest * + * TODO: Retest this, add CM108 GPIO to test. */ #if TEST -void text_color_set (dw_color_t c) { } - -#define dw_printf printf - -main () +int main () { struct audio_s my_audio_config; int n; @@ -1375,17 +1444,18 @@ main () my_audio_config.adev[0].num_channels = 2; - my_audio_config.valid[0] = 1; - my_audio_config.adev[0].octrl[OCTYPE_PTT].ptt_method = PTT_METHOD_SERIAL; - //strlcpy (my_audio_config.ptt_device, "COM1", sizeof(my_audio_config.ptt_device)); - strlcpy (my_audio_config.ptt_device, "/dev/ttyUSB0", sizeof(my_audio_config.ptt_device)); - my_audio_config.adev[0].octrl[OCTYPE_PTT].ptt_line = PTT_LINE_RTS; + my_audio_config.achan[0].valid = 1; + my_audio_config.achan[0].octrl[OCTYPE_PTT].ptt_method = PTT_METHOD_SERIAL; +// TODO: device should be command line argument. + strlcpy (my_audio_config.achan[0].octrl[OCTYPE_PTT].ptt_device, "COM3", sizeof(my_audio_config.achan[0].octrl[OCTYPE_PTT].ptt_device)); + //strlcpy (my_audio_config.achan[0].octrl[OCTYPE_PTT].ptt_device, "/dev/ttyUSB0", sizeof(my_audio_config.achan[0].octrl[OCTYPE_PTT].ptt_device)); + my_audio_config.achan[0].octrl[OCTYPE_PTT].ptt_line = PTT_LINE_RTS; - my_audio_config.valid[1] = 1; - my_audio_config.adev[1].octrl[OCTYPE_PTT].ptt_method = PTT_METHOD_SERIAL; - //strlcpy (my_audio_config.adev[1].octrl[OCTYPE_PTT].ptt_device, "COM1", sizeof(my_audio_config.adev[1].octrl[OCTYPE_PTT].ptt_device)); - strlcpy (my_audio_config.adev[1].octrl[OCTYPE_PTT].ptt_device, "/dev/ttyUSB0", sizeof(my_audio_config.adev[1].octrl[OCTYPE_PTT].ptt_device)); - my_audio_config.adev[1].octrl[OCTYPE_PTT].ptt_line = PTT_LINE_DTR; + my_audio_config.achan[1].valid = 1; + my_audio_config.achan[1].octrl[OCTYPE_PTT].ptt_method = PTT_METHOD_SERIAL; + strlcpy (my_audio_config.achan[1].octrl[OCTYPE_PTT].ptt_device, "COM3", sizeof(my_audio_config.achan[1].octrl[OCTYPE_PTT].ptt_device)); + //strlcpy (my_audio_config.achan[1].octrl[OCTYPE_PTT].ptt_device, "/dev/ttyUSB0", sizeof(my_audio_config.achan[1].octrl[OCTYPE_PTT].ptt_device)); + my_audio_config.achan[1].octrl[OCTYPE_PTT].ptt_line = PTT_LINE_DTR; /* initialize - both off */ @@ -1420,7 +1490,7 @@ main () /* Same thing again but invert RTS. */ - my_audio_config.adev[0].octrl[OCTYPE_PTT].ptt_invert = 1; + my_audio_config.achan[0].octrl[OCTYPE_PTT].ptt_invert = 1; ptt_init (&my_audio_config); @@ -1476,6 +1546,14 @@ main () #endif + +/* Parallel printer port. */ + +#if ( defined(__i386__) || defined(__x86_64__) ) && ( defined(__linux__) || defined(__unix__) ) + + // TODO + +#if 0 memset (&my_audio_config, 0, sizeof(my_audio_config)); my_audio_config.num_channels = 2; my_audio_config.valid[0] = 1; @@ -1496,16 +1574,11 @@ main () } ptt_term (); - -/* Parallel printer port. */ - -#if ( defined(__i386__) || defined(__x86_64__) ) && ( defined(__linux__) || defined(__unix__) ) - - // TODO +#endif #endif - + return(0); } #endif /* TEST */