You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

261 lines
6.6 KiB

/*
* Copyright (C) 2009-2013 by Jonathan Naylor, G4KLX
*
* 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; version 2 of the License.
*
* 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.
*/
#include "URIUSBController.h"
#include <cassert>
#include <cstdio>
#if !defined(_WIN32)
const unsigned int C108_VENDOR_ID = 0x0D8CU;
const unsigned int C108_PRODUCT_ID = 0x000CU;
const unsigned int C108AH_PRODUCT_ID = 0x013CU;
const unsigned int C119_PRODUCT_ID = 0x0008U;
const unsigned int C119A_PRODUCT_ID = 0x013AU;
const char REPORT_ID = 0x00;
const char HID_OR0 = 0x00;
const char HID_OR1 = 0x00;
const char HID_OR2 = 0x0F; // Set GPIO1 to GPIO4 to output
const char HID_OR3 = 0x00;
const char HID_OR1_GPIO4 = 0x08; // GPIO4, Switched
const char HID_OR1_GPIO3 = 0x04; // GPIO3, PTT
const char HID_OR1_GPIO2 = 0x02; // GPIO2, Switched
const char HID_OR1_GPIO1 = 0x01; // GPIO1, Heartbeat
const char HID_IR0_RM = 0x08; // Record-Mute, Disable
const char HID_IR0_PM = 0x04; // Playback-Mute, Battery
const char HID_IR0_VD = 0x02; // Volume-Down, COR_DET
const char HID_IR0_VU = 0x01; // Volume-Up, CTCSS_DET
const unsigned int C108_HID_INTERFACE = 3U;
const unsigned int HID_REPORT_GET = 0x01U;
const unsigned int HID_REPORT_SET = 0x09U;
const unsigned int HID_RT_INPUT = 0x01U;
const unsigned int HID_RT_OUTPUT = 0x02U;
const int USB_BUFSIZE = 4;
const int USB_TIMEOUT = 5000;
CURIUSBController::CURIUSBController(unsigned int address, bool checkInput) :
m_address(address),
m_checkInput(checkInput),
m_outp1(false),
m_outp3(false),
m_outp5(false),
m_outp6(false),
m_context(nullptr),
m_handle(nullptr)
{
::libusb_init(&m_context);
}
CURIUSBController::~CURIUSBController()
{
assert(m_context != nullptr);
::libusb_exit(m_context);
}
bool CURIUSBController::open()
{
assert(m_context != nullptr);
assert(m_handle == nullptr);
libusb_device** list;
ssize_t cnt = ::libusb_get_device_list(nullptr, &list);
if (cnt <= 0) {
::fprintf(stderr, "Cannot find any USB devices!\n");
return false;
}
int err = 0;
unsigned int count = 0U;
for (ssize_t i = 0; i < cnt; i++) {
libusb_device_descriptor desc;
err = ::libusb_get_device_descriptor(list[i], &desc);
if (err < 0)
continue;
if (desc.idVendor == C108_VENDOR_ID &&
(desc.idProduct == C108_PRODUCT_ID ||
desc.idProduct == C108AH_PRODUCT_ID ||
desc.idProduct == C119_PRODUCT_ID ||
desc.idProduct == C119A_PRODUCT_ID)) {
count++;
// Is this the correct device?
if (count == m_address) {
err = ::libusb_open(list[i], &m_handle);
break;
}
}
}
if (err != 0) {
::fprintf(stderr, "libusb_open failed, err=%d\n", err);
::libusb_free_device_list(list, 1);
return false;
}
if (m_handle == nullptr) {
::fprintf(stderr, "Could not find a suitable CM108 based device\n");
::libusb_free_device_list(list, 1);
return false;
}
::libusb_free_device_list(list, 1);
int res = ::libusb_claim_interface(m_handle, C108_HID_INTERFACE);
if (res != 0) {
res = ::libusb_detach_kernel_driver(m_handle, C108_HID_INTERFACE);
if (res != 0) {
::fprintf(stderr, "libusb_detach_kernel_driver failed, err=%d\n", res);
::libusb_close(m_handle);
m_handle = nullptr;
return false;
}
res = ::libusb_claim_interface(m_handle, C108_HID_INTERFACE);
if (res != 0) {
::fprintf(stderr, "libusb_claim_interface failed, err=%d\n", res);
::libusb_close(m_handle);
m_handle = nullptr;
return false;
}
}
setDigitalOutputs(false, false, false, false, false, false, false, false);
return true;
}
void CURIUSBController::getDigitalInputs(bool& inp1, bool& inp2, bool& inp3, bool& inp4, bool& inp5)
{
assert(m_handle != nullptr);
if (!m_checkInput) {
inp1 = inp2 = inp3 = inp4 = inp5 = false;
return;
}
unsigned char buffer[USB_BUFSIZE];
buffer[0] = 0x00;
buffer[1] = 0x00;
buffer[2] = 0x00;
buffer[3] = 0x00;
int res = ::libusb_control_transfer(m_handle, LIBUSB_ENDPOINT_IN + LIBUSB_REQUEST_TYPE_CLASS + LIBUSB_RECIPIENT_INTERFACE, HID_REPORT_GET, REPORT_ID + (HID_RT_INPUT << 8), C108_HID_INTERFACE, buffer, USB_BUFSIZE, USB_TIMEOUT);
if (res < 0) {
::fprintf(stderr, "Error from libusb_control_transfer: err=%d\n", res);
return;
}
if (res < USB_BUFSIZE)
return;
inp3 = false;
inp5 = (buffer[0] & HID_IR0_RM) == HID_IR0_RM; // Disable
inp4 = (buffer[0] & HID_IR0_PM) == HID_IR0_PM; // Battery
inp2 = (buffer[0] & HID_IR0_VU) == HID_IR0_VU; // Squelch 2
inp1 = (buffer[0] & HID_IR0_VD) == HID_IR0_VD; // Squelch 1
}
void CURIUSBController::setDigitalOutputs(bool outp1, bool, bool outp3, bool outp4, bool outp5, bool outp6, bool, bool)
{
assert(m_handle != nullptr);
if (outp1 == m_outp1 && outp3 == m_outp3 && outp5 == m_outp5 && outp6 == m_outp6)
return;
unsigned char buffer[USB_BUFSIZE];
buffer[0] = HID_OR0;
buffer[1] = HID_OR1;
buffer[2] = HID_OR2;
buffer[3] = HID_OR3;
if (outp1) // Transmit
buffer[1] |= HID_OR1_GPIO3;
if (outp3) // Heartbeat
buffer[1] |= HID_OR1_GPIO1;
if (outp4) // Active
buffer[1] |= HID_OR1_GPIO2;
if (outp5) // Output 1
buffer[1] |= HID_OR1_GPIO4;
int res = ::libusb_control_transfer(m_handle, LIBUSB_ENDPOINT_OUT + LIBUSB_REQUEST_TYPE_CLASS + LIBUSB_RECIPIENT_INTERFACE, HID_REPORT_SET, REPORT_ID + (HID_RT_OUTPUT << 8), C108_HID_INTERFACE, buffer, USB_BUFSIZE, USB_TIMEOUT);
if (res < 0) {
::fprintf(stderr, "Error from libusb_control_transfer: err=%d\n", res);
return;
}
if (res < USB_BUFSIZE)
return;
m_outp1 = outp1;
m_outp3 = outp3;
m_outp5 = outp5;
m_outp6 = outp6;
}
void CURIUSBController::close()
{
assert(m_handle != nullptr);
setDigitalOutputs(false, false, false, false, false, false, false, false);
::libusb_release_interface(m_handle, C108_HID_INTERFACE);
::libusb_close(m_handle);
m_handle = nullptr;
}
#else
// Windows stub implementation
CURIUSBController::CURIUSBController(unsigned int address, bool checkInput) :
m_address(address),
m_checkInput(checkInput)
{
}
CURIUSBController::~CURIUSBController()
{
}
bool CURIUSBController::open()
{
::fprintf(stderr, "K8055 not supported on Windows without Velleman DLL\n");
return false;
}
void CURIUSBController::getDigitalInputs(bool& inp1, bool& inp2, bool& inp3, bool& inp4, bool& inp5)
{
inp1 = inp2 = inp3 = inp4 = inp5 = false;
}
void CURIUSBController::setDigitalOutputs(bool, bool, bool, bool, bool, bool, bool, bool)
{
}
void CURIUSBController::close()
{
}
#endif

Powered by TurnKey Linux.