Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ sudo udevadm control --reload-rules && sudo udevadm trigger
| Audeze Maxwell 2 | All | x | x | | | x | x | x | | x | | | | | | | | x | |
| Lenovo Wireless VoIP Headset | All | x | x | | | x | | x | x | x | | | x | | x | | | | |
| Plantronics Voyager 8200 UC (BT600) | L/W | x | x | | x | | | x | | | | | | | x | | | | |
| Jabra Link 380 (paired headset) | L/M | x | x | | x | x | | x | | | | | | | x | | | | x |
| Jabra Link 390 (paired headset) | L/M | x | x | | x | x | | x | | | | | | | x | | | | x |
| Jabra Evolve2 65 Flex (USB) | L/M | x | x | | x | x | | x | | | | | | | x | | | | x |
| Sony INZONE Buds | All | | x | | | | | | | | | | | | | | | | |
Expand Down
2 changes: 2 additions & 0 deletions lib/device_registry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@

// Jabra devices
#include "devices/jabra_evolve2_65_flex.hpp"
#include "devices/jabra_link_380.hpp"
#include "devices/jabra_link_390.hpp"

// Sony devices
Expand Down Expand Up @@ -163,6 +164,7 @@ void DeviceRegistry::initialize()
registerDevice(std::make_unique<PlantronicsBT600>());

// Jabra devices
registerDevice(std::make_unique<JabraLink380>());
registerDevice(std::make_unique<JabraLink390>());
registerDevice(std::make_unique<JabraEvolve265Flex>());

Expand Down
42 changes: 42 additions & 0 deletions lib/devices/jabra_link_380.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#pragma once

#include "protocols/jabra_gnp_protocol.hpp"
#include <array>
#include <string_view>
#include <vector>

using namespace std::string_view_literals;

namespace headsetcontrol {

/**
* @brief Jabra Link 380 USB dongle, controlling the Jabra headset paired to it
*
* Same GN Protocol (GNP) as the Link 390; the headset is reached at GNP address
* 0x04 and the dongle's vendor collection (usage page 0xFF00) carries report 5
* on usage 0x01. Verified on Linux with a Link 380 (0x24c7, fw 1.16) paired
* with a Jabra Evolve2 65.
*/
class JabraLink380 : public protocols::JabraGNPDevice<0x04, 0x01> {
public:
static constexpr std::array<uint16_t, 1> PRODUCT_IDS { 0x24C7 };

constexpr std::vector<uint16_t> getProductIds() const override
{
return { PRODUCT_IDS.begin(), PRODUCT_IDS.end() };
}

constexpr std::string_view getDeviceName() const override
{
return "Jabra Link 380 (paired headset)"sv;
}

// Windows is left out because it is untested, not because the protocol needs
// anything different there: getCapabilityDetail() reports usagepage/usageid.
constexpr uint8_t getSupportedPlatforms() const override
{
return PLATFORM_LINUX | PLATFORM_MACOS;
}
};

} // namespace headsetcontrol
14 changes: 4 additions & 10 deletions lib/hid_utility.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,10 @@ std::optional<std::string> get_hid_path(uint16_t vid, uint16_t pid, int iid, uin
iid = 0;
#endif

// usageid is more specific to interface id, so we try it first
// It is a good idea, to do it on all platforms, however googling shows
// that older versions of hidapi have a bug where the value is not correctly
// set on non-Windows.
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32)
// usageid is more specific to interface id, so we try it first.
// Older hidapi versions on non-Windows did not populate usage_page/usage, so
// we only trust the match when both values are actually present; otherwise
// we fall through to the interface_number fallback below.
if (usageid && usagepageid) // ignore when one of them 0
{
struct hid_device_info* cur_dev = devs;
Expand All @@ -42,11 +41,6 @@ std::optional<std::string> get_hid_path(uint16_t vid, uint16_t pid, int iid, uin
cur_dev = cur_dev->next;
}
}
#else
// ignore unused parameter warning
(void)(usageid);
(void)(usagepageid);
#endif

if (!result) // only when we didn't yet find something
{
Expand Down
23 changes: 23 additions & 0 deletions tests/test_device_registry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,28 @@ void testLookupJabraLink390()
std::cout << " PASS" << std::endl;
}

void testLookupJabraLink380()
{
std::cout << " Testing lookup of Jabra Link 380 (0x24c7)..." << std::endl;

auto& registry = DeviceRegistry::instance();
registry.initialize();

auto* device = registry.getDevice(0x0b0e, 0x24c7);
ASSERT_NOT_NULL(device, "Jabra Link 380 should be found");
ASSERT_EQ("Jabra Link 380 (paired headset)", std::string(device->getDeviceName()), "Device name should match");
ASSERT_TRUE((device->getCapabilities() & B(CAP_SIDETONE)) != 0, "Link 380 should expose sidetone capability");
ASSERT_TRUE((device->getCapabilities() & B(CAP_BATTERY_STATUS)) != 0, "Link 380 should expose battery capability");
ASSERT_TRUE((device->getCapabilities() & B(CAP_LIGHTS)) != 0, "Link 380 should expose lights capability");
ASSERT_TRUE((device->getCapabilities() & B(CAP_INACTIVE_TIME)) != 0, "Link 380 should expose inactive time capability");

auto* link390 = registry.getDevice(0x0b0e, 0x2e57);
ASSERT_NOT_NULL(link390, "Jabra Link 390 should be found");
ASSERT_EQ(device->getCapabilities(), link390->getCapabilities(), "Link 380 and Link 390 share the GNP feature set");

std::cout << " PASS" << std::endl;
}

void testLookupPlantronicsBT600()
{
std::cout << " Testing lookup of Plantronics BT600 (0x02ee)..." << std::endl;
Expand Down Expand Up @@ -538,6 +560,7 @@ void runAllDeviceRegistryTests()
runTest("Lookup Logitech PRO X2 LIGHTSPEED", testLookupLogitechProX2Lightspeed);
runTest("Lookup Plantronics BT600", testLookupPlantronicsBT600);
runTest("Lookup Jabra Link 390", testLookupJabraLink390);
runTest("Lookup Jabra Link 380", testLookupJabraLink380);
runTest("Lookup Non-Existent", testLookupNonExistentDevice);
runTest("Lookup Wrong Product ID", testLookupWithWrongProductId);
runTest("Lookup Wrong Vendor ID", testLookupWithWrongVendorId);
Expand Down
Loading