/* * Hamlib Interface - CM108 HID communication low-level support * Copyright (c) 2000-2012 by Stephane Fillod * Copyright (c) 2011 by Andrew Errington * CM108 detection code Copyright (c) Thomas Sailer used with permission * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Library 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 Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ /** * \addtogroup rig_internal * @{ */ /** * \file cm108.c * \brief CM108 GPIO support. * * CM108 Audio chips found on many USB audio interfaces have controllable * General Purpose Input/Output pins. */ #include #include #include /* String function definitions */ #include /* UNIX standard function definitions */ #include /* File control definitions */ #include /* Error number definitions */ #include #ifdef HAVE_SYS_IOCTL_H # include #endif #ifdef HAVE_SYS_PARAM_H # include #endif #ifdef HAVE_WINDOWS_H # include # include "par_nt.h" #endif #ifdef HAVE_WINIOCTL_H # include #endif #ifdef HAVE_WINBASE_H # include #endif #ifdef HAVE_LINUX_HIDRAW_H # include #endif #include "cm108.h" /** * \brief Open CM108 HID port (/dev/hidrawX). * * \param port The port structure. * * \return File descriptor, otherwise a **negative value** if an error * occurred (in which case, cause is set appropriately). * * \retval RIG_EINVAL The port pathname is empty or no CM108 device detected. * \retval RIG_EIO The `open`(2) system call returned a **negative value**. */ int cm108_open(hamlib_port_t *port) { int fd; #ifdef HAVE_LINUX_HIDRAW_H struct hidraw_devinfo hiddevinfo; #endif rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); if (!port->pathname[0]) { return -RIG_EINVAL; } fd = open(port->pathname, O_RDWR); if (fd < 0) { rig_debug(RIG_DEBUG_ERR, "%s: opening device \"%s\": %s\n", __func__, port->pathname, strerror(errno)); return -RIG_EIO; } #ifdef HAVE_LINUX_HIDRAW_H // CM108 detection copied from Thomas Sailer's soundmodem code rig_debug(RIG_DEBUG_VERBOSE, "%s: checking for cm108 (or compatible) device\n", __func__); if (!ioctl(fd, HIDIOCGRAWINFO, &hiddevinfo) && ((hiddevinfo.vendor == 0x0d8c // CM108/108B/109/119/119A/119B && ((hiddevinfo.product >= 0x0008 && hiddevinfo.product <= 0x000f) || hiddevinfo.product == 0x0012 || hiddevinfo.product == 0x013a || hiddevinfo.product == 0x013c || hiddevinfo.product == 0x0013)) // SSS1621/23 || (hiddevinfo.vendor == 0x0c76 && (hiddevinfo.product == 0x1605 || hiddevinfo.product == 0x1607 || hiddevinfo.product == 0x160b)))) { rig_debug(RIG_DEBUG_VERBOSE, "%s: cm108 compatible device detected\n", __func__); } else { close(fd); rig_debug(RIG_DEBUG_VERBOSE, "%s: no cm108 (or compatible) device detected\n", __func__); return -RIG_EINVAL; } #endif port->fd = fd; return fd; } /** * \brief Close a CM108 HID port. * * \param port The port structure * * \return Zero if the port was closed successfully, otherwise -1 if an error * occurred (in which case, the system `errno`(3) is set appropriately). * * \sa The `close`(2) system call. */ int cm108_close(hamlib_port_t *port) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); return close(port->fd); } /** * \brief Set or unset the Push To Talk bit on a CM108 GPIO. * * \param p The port structure. * \param pttx RIG_PTT_ON --> Set PTT, else unset PTT. * * \return RIG_OK on success, otherwise a **negative value** if an error * occurred (in which case, cause is set appropriately). * * \retval RIG_OK Setting or unsetting the PTT was successful. * \retval RIG_EINVAL The file descriptor is invalid or the PTT type is * unsupported. * \retval RIG_EIO The `write`(2) system call returned a **negative value**. */ int cm108_ptt_set(hamlib_port_t *p, ptt_t pttx) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); // For a CM108 USB audio device PTT is wired up to one of the GPIO // pins. Usually this is GPIO3 (bit 2 of the GPIO register) because it // is on the corner of the chip package (pin 13) so it's easily accessible. // Some CM108 chips are epoxy-blobbed onto the PCB, so no GPIO // pins are accessible. The SSS1623 chips have a different pinout, so // we make the GPIO bit number configurable. switch (p->type.ptt) { case RIG_PTT_CM108: { ssize_t bytes; char out_rep[] = { 0x00, // report number // HID output report 0x00, (pttx == RIG_PTT_ON) ? (1 << p->parm.cm108.ptt_bitnum) : 0, // set GPIO 1 << p->parm.cm108.ptt_bitnum, // Data direction register (1=output) 0x00 }; // Build a packet for CM108 HID to turn GPIO bit on or off. // Packet is 4 bytes, preceded by a 'report number' byte // 0x00 report number // Write data packet (from CM108 documentation) // byte 0: 00xx xxxx Write GPIO // byte 1: xxxx dcba GPIO3-0 output values (1=high) // byte 2: xxxx dcba GPIO3-0 data-direction register (1=output) // byte 3: xxxx xxxx SPDIF rig_debug(RIG_DEBUG_VERBOSE, "%s: bit number %d to state %d\n", __func__, p->parm.cm108.ptt_bitnum, (pttx == RIG_PTT_ON) ? 1 : 0); if (p->fd == -1) { return -RIG_EINVAL; } // Send the HID packet bytes = write(p->fd, out_rep, sizeof(out_rep)); if (bytes < 0) { return -RIG_EIO; } return RIG_OK; } default: rig_debug(RIG_DEBUG_ERR, "%s: unsupported PTT type %d\n", __func__, p->type.ptt); return -RIG_EINVAL; } return RIG_OK; } /** * \brief Get the state of Push To Talk from a CM108 GPIO. * * \param p The port structure. * \param pttx Return value (must be non NULL). * * \return RIG_OK on success, otherwise a **negative value** if an error * occurred (in which case, cause is set appropriately). * * \retval RIG_OK Getting the PTT state was successful. * \retval RIG_ENIMPL Getting the state is not yet implemented. * \retval RIG_ENAVAIL Getting the state is not available for this PTT type. */ int cm108_ptt_get(hamlib_port_t *p, ptt_t *pttx) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); switch (p->type.ptt) { case RIG_PTT_CM108: { return -RIG_ENIMPL; } default: rig_debug(RIG_DEBUG_ERR, "%s: unsupported PTT type %d\n", __func__, p->type.ptt); return -RIG_ENAVAIL; } return RIG_OK; } #ifdef XXREMOVEXX // Not referenced anywhere /** * \brief get Data Carrier Detect (squelch) from CM108 GPIO * \param p * \param dcdx return value (Must be non NULL) * \return RIG_OK or < 0 error */ int cm108_dcd_get(hamlib_port_t *p, dcd_t *dcdx) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); return RIG_OK; } #endif int cm108_set_bit(hamlib_port_t *p, enum GPIO gpio, int bit) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); ssize_t bytes; char out_rep[] = { 0x00, // report number // HID output report 0x00, bit ? (1 << (gpio - 1)) : 0, // set GPIO 1 << (gpio - 1), // Data direction register (1=output) 0x00 }; rig_debug(RIG_DEBUG_VERBOSE, "%s: out_rep = 0x%02x 0x%02x\n", __func__, out_rep[2], out_rep[3]); // Build a packet for CM108 HID to turn GPIO bit on or off. // Packet is 4 bytes, preceded by a 'report number' byte // 0x00 report number // Write data packet (from CM108 documentation) // byte 0: 00xx xxxx Write GPIO // byte 1: xxxx dcba GPIO3-0 output values (1=high) // byte 2: xxxx dcba GPIO3-0 data-direction register (1=output) // byte 3: xxxx xxxx SPDIF // Send the HID packet bytes = write(p->fd, out_rep, sizeof(out_rep)); if (bytes < 0) { return -RIG_EIO; } return RIG_OK; } int cm108_get_bit(hamlib_port_t *p, enum GPIO gpio, int *bit) { ssize_t bytes; char reply[4]; rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); char in_rep[] = { 0x00, // report number 0x01, // HID input report 0x00, 0x00 }; // Send the HID packet bytes = write(p->fd, in_rep, sizeof(in_rep)); if (bytes < 0) { return -RIG_EIO; } bytes = read(p->fd, reply, sizeof(reply)); if (bytes <= 0) { rig_debug(RIG_DEBUG_ERR, "%s: read error: %s\n", __func__, strerror(errno)); return -RIG_EPROTO; } int mask = 1 << (gpio - 1); *bit = (reply[1] & mask) != 0; rig_debug(RIG_DEBUG_VERBOSE, "%s: mask=0x%02x, reply=0x%02x 0x%02x 0x%02x 0x%02x, bit=%d\n", __func__, mask, reply[0], reply[1], reply[2], reply[3], *bit); return RIG_OK; } int rig_cm108_get_bit(hamlib_port_t *p, enum GPIO gpio, int *bit) { if (gpio < 1 || gpio > 4) { rig_debug(RIG_DEBUG_ERR, "%s: gpio must be 1,2,3,4 for cm108\n", __func__); return -RIG_EINVAL; } cm108_get_bit(p, gpio, bit); rig_debug(RIG_DEBUG_TRACE, "%s: gpio=%d bit=%d\n", __func__, gpio, *bit); return RIG_OK; } int rig_cm108_set_bit(hamlib_port_t *p, enum GPIO gpio, int bit) { if (gpio < 1 || gpio > 4) { rig_debug(RIG_DEBUG_ERR, "%s: gpio must be 1,2,3,4 for cm108\n", __func__); return -RIG_EINVAL; } int retval = cm108_set_bit(p, gpio, bit); if (retval != RIG_OK) { rig_debug(RIG_DEBUG_ERR, "%s: cm108_set_bit: %s\n", __func__, strerror(retval)); return retval; } rig_debug(RIG_DEBUG_TRACE, "%s: simulated gpio=%d bit=%d\n", __func__, gpio, bit); return RIG_OK; } /** @} */