-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Extract HID DeviceInfo from HidController/HidEnumerator
- Loading branch information
Showing
8 changed files
with
393 additions
and
262 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,86 +1,24 @@ | ||
/** | ||
* @file hidcontroller.cpp | ||
* @author Sean M. Pappalardo [email protected] | ||
* @date Sun May 1 2011 | ||
* @brief HID controller backend | ||
* | ||
*/ | ||
|
||
#include <wchar.h> | ||
#include <string.h> | ||
|
||
#include "util/path.h" // for PATH_MAX on Windows | ||
#include "controllers/hid/hidcontroller.h" | ||
#include "controllers/defs_controllers.h" | ||
#include "util/trace.h" | ||
|
||
#include "controllers/controllerdebug.h" | ||
#include "controllers/defs_controllers.h" | ||
#include "controllers/hid/hidcontrollerpresetfilehandler.h" | ||
#include "util/string.h" | ||
#include "util/time.h" | ||
|
||
ControllerJSProxy* HidController::jsProxy() { | ||
return new HidControllerJSProxy(this); | ||
} | ||
#include "util/trace.h" | ||
|
||
namespace { | ||
constexpr int kReportIdSize = 1; | ||
constexpr int kMaxHidErrorMessageSize = 512; | ||
} // namespace | ||
|
||
HidController::HidController(const hid_device_info& deviceInfo) | ||
: Controller(), | ||
HidController::HidController( | ||
mixxx::hid::DeviceInfo&& deviceInfo) | ||
: m_deviceInfo(std::move(deviceInfo)), | ||
m_pHidDevice(nullptr), | ||
m_iPollingBufferIndex(0) { | ||
// Copy required variables from deviceInfo, which will be freed after | ||
// this class is initialized by caller. | ||
hid_vendor_id = deviceInfo.vendor_id; | ||
hid_product_id = deviceInfo.product_id; | ||
hid_interface_number = deviceInfo.interface_number; | ||
if (hid_interface_number == -1) { | ||
// OS/X and windows don't use interface numbers, but usage_page/usage | ||
hid_usage_page = deviceInfo.usage_page; | ||
hid_usage = deviceInfo.usage; | ||
} else { | ||
// Linux hidapi does not set value for usage_page or usage and uses | ||
// interface number to identify subdevices | ||
hid_usage_page = 0; | ||
hid_usage = 0; | ||
} | ||
|
||
// Don't trust path to be null terminated. | ||
hid_path = new char[PATH_MAX + 1]; | ||
strncpy(hid_path, deviceInfo.path, PATH_MAX); | ||
hid_path[PATH_MAX] = 0; | ||
|
||
hid_serial_raw = NULL; | ||
if (deviceInfo.serial_number != NULL) { | ||
size_t serial_max_length = 512; | ||
hid_serial_raw = new wchar_t[serial_max_length+1]; | ||
wcsncpy(hid_serial_raw, deviceInfo.serial_number, serial_max_length); | ||
hid_serial_raw[serial_max_length] = 0; | ||
} | ||
|
||
hid_serial = safeDecodeWideString(deviceInfo.serial_number, 512); | ||
hid_manufacturer = safeDecodeWideString(deviceInfo.manufacturer_string, 512); | ||
hid_product = safeDecodeWideString(deviceInfo.product_string, 512); | ||
|
||
guessDeviceCategory(); | ||
|
||
// Set the Unique Identifier to the serial_number | ||
m_sUID = hid_serial; | ||
|
||
//Note: We include the last 4 digits of the serial number and the | ||
// interface number to allow the user (and Mixxx!) to keep track of | ||
// which is which | ||
if (hid_interface_number < 0) { | ||
setDeviceName( | ||
QString("%1 %2").arg(hid_product) | ||
.arg(hid_serial.right(4))); | ||
} else { | ||
setDeviceName( | ||
QString("%1 %2_%3").arg(hid_product) | ||
.arg(hid_serial.right(4)) | ||
.arg(QString::number(hid_interface_number))); | ||
m_sUID.append(QString::number(hid_interface_number)); | ||
} | ||
setDeviceCategory(mixxx::hid::DeviceCategory::guessFromDeviceInfo(m_deviceInfo)); | ||
setDeviceName(m_deviceInfo.formatName()); | ||
|
||
// All HID devices are full-duplex | ||
setInputDevice(true); | ||
|
@@ -91,8 +29,6 @@ HidController::~HidController() { | |
if (isOpen()) { | ||
close(); | ||
} | ||
delete [] hid_path; | ||
delete [] hid_serial_raw; | ||
} | ||
|
||
QString HidController::presetExtension() { | ||
|
@@ -114,76 +50,11 @@ void HidController::visit(const HidControllerPreset* preset) { | |
bool HidController::matchPreset(const PresetInfo& preset) { | ||
const QList<ProductInfo>& products = preset.getProducts(); | ||
for (const auto& product : products) { | ||
if (matchProductInfo(product)) | ||
if (m_deviceInfo.matchProductInfo(product)) { | ||
return true; | ||
} | ||
return false; | ||
} | ||
|
||
bool HidController::matchProductInfo(const ProductInfo& product) { | ||
int value; | ||
bool ok; | ||
// Product and vendor match is always required | ||
value = product.vendor_id.toInt(&ok,16); | ||
if (!ok || hid_vendor_id!=value) return false; | ||
value = product.product_id.toInt(&ok,16); | ||
if (!ok || hid_product_id!=value) return false; | ||
|
||
// Optionally check against interface_number / usage_page && usage | ||
if (hid_interface_number!=-1) { | ||
value = product.interface_number.toInt(&ok,16); | ||
if (!ok || hid_interface_number!=value) return false; | ||
} else { | ||
value = product.usage_page.toInt(&ok,16); | ||
if (!ok || hid_usage_page!=value) return false; | ||
|
||
value = product.usage.toInt(&ok,16); | ||
if (!ok || hid_usage!=value) return false; | ||
} | ||
// Match found | ||
return true; | ||
} | ||
|
||
void HidController::guessDeviceCategory() { | ||
// This should be done somehow else, I know. But at least we get started with | ||
// the idea of mapping this information | ||
QString info; | ||
if (hid_interface_number==-1) { | ||
if (hid_usage_page==0x1) { | ||
switch (hid_usage) { | ||
case 0x2: info = tr("Generic HID Mouse"); break; | ||
case 0x4: info = tr("Generic HID Joystick"); break; | ||
case 0x5: info = tr("Generic HID Gamepad"); break; | ||
case 0x6: info = tr("Generic HID Keyboard"); break; | ||
case 0x8: info = tr("Generic HID Multiaxis Controller"); break; | ||
default: info = tr("Unknown HID Desktop Device") + | ||
QStringLiteral(" 0x") + QString::number(hid_usage_page, 16) + | ||
QStringLiteral("/0x") + QString::number(hid_usage, 16); | ||
break; | ||
} | ||
} else if (hid_vendor_id==0x5ac) { | ||
// Apple laptop special HID devices | ||
if (hid_product_id==0x8242) { | ||
info = tr("HID Infrared Control"); | ||
} else { | ||
info = tr("Unknown Apple HID Device") + | ||
QStringLiteral(" 0x") + QString::number(hid_usage_page, 16) + | ||
QStringLiteral("/0x") + QString::number(hid_usage, 16); | ||
} | ||
} else { | ||
// Fill in the usage page and usage fields for debugging info | ||
info = tr("HID Unknown Device") + | ||
QStringLiteral(" 0x") + QString::number(hid_usage_page, 16) + | ||
QStringLiteral("/0x") + QString::number(hid_usage, 16); | ||
} | ||
} else { | ||
// Guess linux device types somehow as well. Or maybe just fill in the | ||
// interface number? | ||
info = tr("HID Interface Number") + | ||
QStringLiteral(" 0x") + QString::number(hid_usage_page, 16) + | ||
QStringLiteral("/0x") + QString::number(hid_usage, 16); | ||
} | ||
setDeviceCategory(info); | ||
return false; | ||
} | ||
|
||
int HidController::open() { | ||
|
@@ -193,29 +64,36 @@ int HidController::open() { | |
} | ||
|
||
// Open device by path | ||
controllerDebug("Opening HID device" << getName() << "by HID path" << hid_path); | ||
controllerDebug("Opening HID device" << getName() << "by HID path" | ||
<< m_deviceInfo.pathRaw()); | ||
|
||
m_pHidDevice = hid_open_path(hid_path); | ||
m_pHidDevice = hid_open_path(m_deviceInfo.pathRaw()); | ||
|
||
// If that fails, try to open device with vendor/product/serial # | ||
if (m_pHidDevice == NULL) { | ||
if (!m_pHidDevice) { | ||
controllerDebug("Failed. Trying to open with make, model & serial no:" | ||
<< hid_vendor_id << hid_product_id << hid_serial); | ||
m_pHidDevice = hid_open(hid_vendor_id, hid_product_id, hid_serial_raw); | ||
<< m_deviceInfo.vendorId() << m_deviceInfo.productId() | ||
<< m_deviceInfo.serialNumber()); | ||
m_pHidDevice = hid_open( | ||
m_deviceInfo.vendorId(), | ||
m_deviceInfo.productId(), | ||
m_deviceInfo.serialNumberRaw()); | ||
} | ||
|
||
// If it does fail, try without serial number WARNING: This will only open | ||
// one of multiple identical devices | ||
if (m_pHidDevice == NULL) { | ||
if (!m_pHidDevice) { | ||
qWarning() << "Unable to open specific HID device" << getName() | ||
<< "Trying now with just make and model." | ||
<< "(This may only open the first of multiple identical devices.)"; | ||
m_pHidDevice = hid_open(hid_vendor_id, hid_product_id, NULL); | ||
m_pHidDevice = hid_open(m_deviceInfo.vendorId(), | ||
m_deviceInfo.productId(), | ||
nullptr); | ||
} | ||
|
||
// If that fails, we give up! | ||
if (m_pHidDevice == NULL) { | ||
qWarning() << "Unable to open HID device" << getName(); | ||
if (!m_pHidDevice) { | ||
qWarning() << "Unable to open HID device" << getName(); | ||
return -1; | ||
} | ||
|
||
|
@@ -325,15 +203,19 @@ void HidController::sendBytesReport(QByteArray data, unsigned int reportID) { | |
if (result == -1) { | ||
if (ControllerDebug::enabled()) { | ||
qWarning() << "Unable to send data to" << getName() | ||
<< "serial #" << hid_serial << ":" | ||
<< safeDecodeWideString(hid_error(m_pHidDevice), kMaxHidErrorMessageSize); | ||
<< "serial #" << m_deviceInfo.serialNumber() << ":" | ||
<< mixxx::convertWCStringToQString( | ||
hid_error(m_pHidDevice), | ||
kMaxHidErrorMessageSize); | ||
} else { | ||
qWarning() << "Unable to send data to" << getName() << ":" | ||
<< safeDecodeWideString(hid_error(m_pHidDevice), kMaxHidErrorMessageSize); | ||
<< mixxx::convertWCStringToQString( | ||
hid_error(m_pHidDevice), | ||
kMaxHidErrorMessageSize); | ||
} | ||
} else { | ||
controllerDebug(result << "bytes sent to" << getName() | ||
<< "serial #" << hid_serial | ||
<< "serial #" << m_deviceInfo.serialNumber() | ||
<< "(including report ID of" << reportID << ")"); | ||
} | ||
} | ||
|
@@ -354,33 +236,19 @@ void HidController::sendFeatureReport( | |
reinterpret_cast<const unsigned char*>(dataArray.constData()), | ||
dataArray.size()); | ||
if (result == -1) { | ||
qWarning() << "sendFeatureReport is unable to send data to" << getName() | ||
<< "serial #" << hid_serial << ":" | ||
<< safeDecodeWideString(hid_error(m_pHidDevice), kMaxHidErrorMessageSize); | ||
qWarning() << "sendFeatureReport is unable to send data to" | ||
<< getName() << "serial #" << m_deviceInfo.serialNumber() | ||
<< ":" | ||
<< mixxx::convertWCStringToQString( | ||
hid_error(m_pHidDevice), | ||
kMaxHidErrorMessageSize); | ||
} else { | ||
controllerDebug(result << "bytes sent by sendFeatureReport to" << getName() | ||
<< "serial #" << hid_serial | ||
<< "serial #" << m_deviceInfo.serialNumber() | ||
<< "(including report ID of" << reportID << ")"); | ||
} | ||
} | ||
|
||
//static | ||
QString HidController::safeDecodeWideString(const wchar_t* pStr, size_t max_length) { | ||
if (pStr == NULL) { | ||
return QString(); | ||
} | ||
// find a terminating 0 or take all chars | ||
int size = 0; | ||
while ((size < (int)max_length) && (pStr[size] != 0)) { | ||
++size; | ||
} | ||
// inlining QString::fromWCharArray() | ||
// We cannot use Qts wchar_t functions, since they may work or not | ||
// depending on the '/Zc:wchar_t-' build flag in the Qt configs | ||
// on Windows build | ||
if (sizeof(wchar_t) == sizeof(QChar)) { | ||
return QString::fromUtf16((const ushort *)pStr, size); | ||
} else { | ||
return QString::fromUcs4((uint *)pStr, size); | ||
} | ||
ControllerJSProxy* HidController::jsProxy() { | ||
return new HidControllerJSProxy(this); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,36 +1,24 @@ | ||
/** | ||
* @file hidcontroller.h | ||
* @author Sean M. Pappalardo [email protected] | ||
* @date Sun May 1 2011 | ||
* @brief HID controller backend | ||
*/ | ||
|
||
#ifndef HIDCONTROLLER_H | ||
#define HIDCONTROLLER_H | ||
|
||
#include <hidapi.h> | ||
|
||
#include <QAtomicInt> | ||
#pragma once | ||
|
||
#include "controllers/controller.h" | ||
#include "controllers/hid/hidcontrollerpreset.h" | ||
#include "controllers/hid/hidcontrollerpresetfilehandler.h" | ||
#include "controllers/hid/hiddevice.h" | ||
#include "util/duration.h" | ||
|
||
class HidController final : public Controller { | ||
Q_OBJECT | ||
public: | ||
HidController(const hid_device_info& deviceInfo); | ||
explicit HidController( | ||
mixxx::hid::DeviceInfo&& deviceInfo); | ||
~HidController() override; | ||
|
||
ControllerJSProxy* jsProxy() override; | ||
|
||
QString presetExtension() override; | ||
|
||
ControllerPresetPointer getPreset() const override { | ||
HidControllerPreset* pClone = new HidControllerPreset(); | ||
*pClone = m_preset; | ||
return ControllerPresetPointer(pClone); | ||
return ControllerPresetPointer( | ||
new HidControllerPreset(m_preset)); | ||
} | ||
|
||
void visit(const MidiControllerPreset* preset) override; | ||
|
@@ -48,8 +36,6 @@ class HidController final : public Controller { | |
|
||
bool matchPreset(const PresetInfo& preset) override; | ||
|
||
static QString safeDecodeWideString(const wchar_t* pStr, size_t max_length); | ||
|
||
protected: | ||
void sendReport(QList<int> data, unsigned int length, unsigned int reportID); | ||
|
||
|
@@ -73,22 +59,8 @@ class HidController final : public Controller { | |
return &m_preset; | ||
} | ||
|
||
bool matchProductInfo(const ProductInfo& product); | ||
void guessDeviceCategory(); | ||
|
||
// Local copies of things we need from hid_device_info | ||
int hid_interface_number; | ||
unsigned short hid_vendor_id; | ||
unsigned short hid_product_id; | ||
unsigned short hid_usage_page; | ||
unsigned short hid_usage; | ||
char* hid_path; | ||
wchar_t* hid_serial_raw; | ||
QString hid_serial; | ||
QString hid_manufacturer; | ||
QString hid_product; | ||
|
||
QString m_sUID; | ||
const mixxx::hid::DeviceInfo m_deviceInfo; | ||
|
||
hid_device* m_pHidDevice; | ||
HidControllerPreset m_preset; | ||
|
||
|
@@ -125,5 +97,3 @@ class HidControllerJSProxy : public ControllerJSProxy { | |
private: | ||
HidController* m_pHidController; | ||
}; | ||
|
||
#endif |
Oops, something went wrong.