diff --git a/README.md b/README.md index 40ca3cb..0477922 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # g810-led
-Linux led controller for Logitech G213, G410, G413, G512, G513, G610, G810, G815, G910 and GPRO Keyboards.
+Linux led controller for Logitech G213, G410, G413, G512, G513, G610, G810, G815, G910, G915 and GPRO Keyboards.
## Compatible keyboards :
- **G213 Prodigy**
@@ -14,6 +14,7 @@ Linux led controller for Logitech G213, G410, G413, G512, G513, G610, G810, G815 - **G815 LIGHTSYNC**
- **G910 Orion Spark**
- **G910 Orion Spectrum**
+- **G915 LIGHTSPEED**
- **GPRO**
## Contribute and evolution :
@@ -36,6 +37,7 @@ You can load predefined configurations on startup! `g810-led --help`
`g815-led --help`
`g910-led --help`
+`g915-led --help`
`gpro-led --help`
`g810-led --help-keys`
diff --git a/makefile b/makefile index 8d6c5d7..fa31907 100644 --- a/makefile +++ b/makefile @@ -66,6 +66,7 @@ setup: @test -s $(DESTDIR)/usr/bin/g610-led || ln -s /usr/bin/$(PROGN) $(DESTDIR)/usr/bin/g610-led @test -s $(DESTDIR)/usr/bin/g815-led || ln -s /usr/bin/$(PROGN) $(DESTDIR)/usr/bin/g815-led @test -s $(DESTDIR)/usr/bin/g910-led || ln -s /usr/bin/$(PROGN) $(DESTDIR)/usr/bin/g910-led + @test -s $(DESTDIR)/usr/bin/g915-led || ln -s /usr/bin/$(PROGN) $(DESTDIR)/usr/bin/g915-led @test -s $(DESTDIR)/usr/bin/gpro-led || ln -s /usr/bin/$(PROGN) $(DESTDIR)/usr/bin/gpro-led @cp sample_profiles/* $(DESTDIR)/etc/$(PROGN)/samples @cp udev/$(PROGN).rules $(DESTDIR)/etc/udev/rules.d @@ -114,6 +115,7 @@ uninstall: @rm /usr/bin/g610-led @rm /usr/bin/g815-led @rm /usr/bin/g910-led + @rm /usr/bin/g915-led @rm /usr/bin/gpro-led @rm /usr/bin/$(PROGN) diff --git a/src/classes/Keyboard.cpp b/src/classes/Keyboard.cpp index b34b74d..f6c1ec0 100644 --- a/src/classes/Keyboard.cpp +++ b/src/classes/Keyboard.cpp @@ -306,9 +306,18 @@ bool LedKeyboard::open(uint16_t vendorID, uint16_t productID, string serial) { m_ctx = NULL; return false; } - - if(libusb_kernel_driver_active(m_hidHandle, 1) == 1) { - if(libusb_detach_kernel_driver(m_hidHandle, 1) != 0) { + + int interface_num; + switch (currentDevice.model) { + case KeyboardModel::g915: + interface_num = 2; + break; + default: + interface_num = 1; + } + + if(libusb_kernel_driver_active(m_hidHandle, interface_num) == 1) { + if(libusb_detach_kernel_driver(m_hidHandle, interface_num) != 0) { libusb_exit(m_ctx); m_ctx = NULL; return false; @@ -316,9 +325,9 @@ bool LedKeyboard::open(uint16_t vendorID, uint16_t productID, string serial) { m_isKernellDetached = true; } - if(libusb_claim_interface(m_hidHandle, 1) < 0) { + if(libusb_claim_interface(m_hidHandle, interface_num) < 0) { if(m_isKernellDetached==true) { - libusb_attach_kernel_driver(m_hidHandle, 1); + libusb_attach_kernel_driver(m_hidHandle, interface_num); m_isKernellDetached = false; } libusb_exit(m_ctx); @@ -348,9 +357,19 @@ bool LedKeyboard::close() { return true; #elif defined(libusb) if (m_hidHandle == NULL) return true; - if(libusb_release_interface(m_hidHandle, 1) != 0) return false; + + int interface_num; + switch (currentDevice.model) { + case KeyboardModel::g915: + interface_num = 2; + break; + default: + interface_num = 1; + } + + if(libusb_release_interface(m_hidHandle, interface_num) != 0) return false; if(m_isKernellDetached==true) { - libusb_attach_kernel_driver(m_hidHandle, 1); + libusb_attach_kernel_driver(m_hidHandle, interface_num); m_isKernellDetached = false; } libusb_close(m_hidHandle); @@ -388,6 +407,9 @@ bool LedKeyboard::commit() { case KeyboardModel::g910: data = { 0x11, 0xff, 0x0f, 0x5d }; break; + case KeyboardModel::g915: + data = { 0x11, 0x01, 0x0b, 0x7f }; + break; default: return false; } @@ -411,6 +433,18 @@ bool LedKeyboard::setKeys(KeyValueArray keyValues) { switch (currentDevice.model) { case KeyboardModel::g815: + case KeyboardModel::g915: + unsigned char g815_target; + unsigned char g815_feat_idx; + switch (currentDevice.model) { + case KeyboardModel::g915: + g815_target = 0x01; + g815_feat_idx = 0x0b; + break; + default: + g815_target = 0xff; + g815_feat_idx = 0x10; + } for (uint8_t i = 0; i < keyValues.size(); i++) { uint32_t colorkey = static_cast(keyValues[i].color.red | keyValues[i].color.green << 8 | keyValues[i].color.blue << 16 ); if (KeyByColors.count(colorkey) == 0) KeyByColors.insert(pair>(colorkey, {})); @@ -422,7 +456,7 @@ bool LedKeyboard::setKeys(KeyValueArray keyValues) { uint8_t gi = 0; while (gi < x.second.size()) { size_t data_size = 20; - byte_buffer_t data = { 0x11, 0xff, 0x10, 0x6c }; + byte_buffer_t data = { 0x11, g815_target, g815_feat_idx, 0x6c }; data.push_back(x.second[0].color.red); data.push_back(x.second[0].color.green); data.push_back(x.second[0].color.blue); @@ -693,6 +727,7 @@ bool LedKeyboard::setAllKeys(LedKeyboard::Color color) { case KeyboardModel::g810: case KeyboardModel::g815: case KeyboardModel::g910: + case KeyboardModel::g915: case KeyboardModel::gpro: for (uint8_t i = 0; i < keyGroupLogo.size(); i++) keyValues.push_back({keyGroupLogo[i], color}); for (uint8_t i = 0; i < keyGroupIndicators.size(); i++) keyValues.push_back({keyGroupIndicators[i], color}); @@ -716,10 +751,22 @@ bool LedKeyboard::setMRKey(uint8_t value) { LedKeyboard::byte_buffer_t data; switch (currentDevice.model) { case KeyboardModel::g815: + case KeyboardModel::g915: + unsigned char g815_target; + unsigned char g815_feat_idx; + switch (currentDevice.model) { + case KeyboardModel::g915: + g815_target = 0x01; + g815_feat_idx = 0x13; + break; + default: + g815_target = 0xff; + g815_feat_idx = 0x0c; + } switch (value) { case 0x00: case 0x01: - data = { 0x11, 0xff, 0x0c, 0x0c, value }; + data = { 0x11, g815_target, g815_feat_idx, 0x0c, value }; data.resize(20, 0x00); return sendDataInternal(data); default: @@ -747,17 +794,29 @@ bool LedKeyboard::setMNKey(uint8_t value) { LedKeyboard::byte_buffer_t data; switch (currentDevice.model) { case KeyboardModel::g815: + case KeyboardModel::g915: + unsigned char g815_target; + unsigned char g815_feat_idx; + switch (currentDevice.model) { + case KeyboardModel::g915: + g815_target = 0x01; + g815_feat_idx = 0x12; + break; + default: + g815_target = 0xff; + g815_feat_idx = 0x0b; + } switch (value) { case 0x01: - data = { 0x11, 0xff, 0x0b, 0x1c, 0x01 }; + data = { 0x11, g815_target, g815_feat_idx, 0x1c, 0x01 }; data.resize(20, 0x00); return sendDataInternal(data); case 0x02: - data = { 0x11, 0xff, 0x0b, 0x1c, 0x02 }; + data = { 0x11, g815_target, g815_feat_idx, 0x1c, 0x02 }; data.resize(20, 0x00); return sendDataInternal(data); case 0x03: - data = { 0x11, 0xff, 0x0b, 0x1c, 0x04 }; + data = { 0x11, g815_target, g815_feat_idx, 0x1c, 0x04 }; data.resize(20, 0x00); return sendDataInternal(data); default: @@ -791,10 +850,22 @@ bool LedKeyboard::setGKeysMode(uint8_t value) { LedKeyboard::byte_buffer_t data; switch (currentDevice.model) { case KeyboardModel::g815: + case KeyboardModel::g915: + unsigned char g815_target; + unsigned char g815_feat_idx; + switch (currentDevice.model) { + case KeyboardModel::g915: + g815_target = 0x01; + g815_feat_idx = 0x11; + break; + default: + g815_target = 0xff; + g815_feat_idx = 0x0a; + } switch (value) { case 0x00: case 0x01: - data = { 0x11, 0xff, 0x0a, 0x2b, value }; + data = { 0x11, g815_target, g815_feat_idx, 0x2b, value }; data.resize(20, 0x00); return sendDataInternal(data); default: @@ -858,7 +929,19 @@ bool LedKeyboard::setOnBoardMode(OnBoardMode onBoardMode) { byte_buffer_t data; switch (currentDevice.model) { case KeyboardModel::g815: - data = { 0x11, 0xff, 0x11, 0x1a, static_cast(onBoardMode) }; + case KeyboardModel::g915: + unsigned char g815_target; + unsigned char g815_feat_idx; + switch (currentDevice.model) { + case KeyboardModel::g915: + g815_target = 0x01; + g815_feat_idx = 0x15; + break; + default: + g815_target = 0xff; + g815_feat_idx = 0x11; + } + data = { 0x11, g815_target, g815_feat_idx, 0x1a, static_cast(onBoardMode) }; data.resize(20, 0x00); return sendDataInternal(data); default: @@ -900,6 +983,8 @@ bool LedKeyboard::setNativeEffect(NativeEffect effect, NativeEffectPart part, setNativeEffect(effect, LedKeyboard::NativeEffectPart::logo, period, color, storage)); } + unsigned char target = 0xff; + switch (currentDevice.model) { case KeyboardModel::g213: case KeyboardModel::g413: @@ -920,6 +1005,10 @@ bool LedKeyboard::setNativeEffect(NativeEffect effect, NativeEffectPart part, protocolBytes[0] = 0x0f; protocolBytes[1] = 0x1c; break; + case KeyboardModel::g915: + protocolBytes[0] = 0x0a; + protocolBytes[1] = 0x1c; + target = 0x01; case KeyboardModel::g910: protocolBytes[0] = 0x10; protocolBytes[1] = 0x3c; @@ -929,7 +1018,7 @@ bool LedKeyboard::setNativeEffect(NativeEffect effect, NativeEffectPart part, } byte_buffer_t data = { - 0x11, 0xff, protocolBytes[0], protocolBytes[1], + 0x11, target, protocolBytes[0], protocolBytes[1], (uint8_t)part, static_cast(effectGroup), // color of static-color and breathing effects color.red, color.green, color.blue, @@ -951,7 +1040,19 @@ bool LedKeyboard::setNativeEffect(NativeEffect effect, NativeEffectPart part, bool retval; switch (currentDevice.model) { case KeyboardModel::g815: - setupData = { 0x11, 0xff, 0x0f, 0x5c, 0x01, 0x03, 0x03 }; + case KeyboardModel::g915: + unsigned char g815_target; + unsigned char g815_feat_idx; + switch (currentDevice.model) { + case KeyboardModel::g915: + g815_target = 0x01; + g815_feat_idx = 0x0a; + break; + default: + g815_target = 0xff; + g815_feat_idx = 0x0f; + } + setupData = { 0x11, g815_target, g815_feat_idx, 0x5c, 0x01, 0x03, 0x03 }; setupData.resize(20, 0x00); retval = sendDataInternal(setupData); @@ -1030,20 +1131,32 @@ bool LedKeyboard::sendDataInternal(byte_buffer_t &data) { */ return true; #elif defined(libusb) + int interface_num; + int interrupt_endpoint; + switch (currentDevice.model) { + case KeyboardModel::g915: + interface_num = 2; + interrupt_endpoint = 0x83; + break; + default: + interface_num = 1; + interrupt_endpoint = 0x82; + } + if (! m_isOpen) return false; if (data.size() > 20) { - if(libusb_control_transfer(m_hidHandle, 0x21, 0x09, 0x0212, 1, + if(libusb_control_transfer(m_hidHandle, 0x21, 0x09, 0x0212, interface_num, const_cast(data.data()), data.size(), 2000) < 0) return false; } else { - if(libusb_control_transfer(m_hidHandle, 0x21, 0x09, 0x0211, 1, + if(libusb_control_transfer(m_hidHandle, 0x21, 0x09, 0x0211, interface_num, const_cast(data.data()), data.size(), 2000) < 0) return false; } usleep(1000); unsigned char buffer[64]; int len = 0; - libusb_interrupt_transfer(m_hidHandle, 0x82, buffer, sizeof(buffer), &len, 1); + libusb_interrupt_transfer(m_hidHandle, interrupt_endpoint, buffer, sizeof(buffer), &len, 1); return true; #endif } @@ -1089,17 +1202,29 @@ LedKeyboard::byte_buffer_t LedKeyboard::getKeyGroupAddress(LedKeyboard::KeyAddre } break; case KeyboardModel::g815: + case KeyboardModel::g915: + unsigned char g815_target; + unsigned char g815_feat_idx; + switch (currentDevice.model) { + case KeyboardModel::g915: + g815_target = 0x01; + g815_feat_idx = 0x0b; + break; + default: + g815_target = 0xff; + g815_feat_idx = 0x10; + } switch (keyAddressGroup) { case LedKeyboard::KeyAddressGroup::logo: - return { 0x11, 0xff, 0x10, 0x1c }; + return { 0x11, g815_target, g815_feat_idx, 0x1c }; case LedKeyboard::KeyAddressGroup::indicators: - return { 0x11, 0xff, 0x10, 0x1c }; + return { 0x11, g815_target, g815_feat_idx, 0x1c }; case LedKeyboard::KeyAddressGroup::gkeys: - return { 0x11, 0xff, 0x10, 0x1c }; + return { 0x11, g815_target, g815_feat_idx, 0x1c }; case LedKeyboard::KeyAddressGroup::multimedia: - return { 0x11, 0xff, 0x10, 0x1c }; + return { 0x11, g815_target, g815_feat_idx, 0x1c }; case LedKeyboard::KeyAddressGroup::keys: - return { 0x11, 0xff, 0x10, 0x1c }; + return { 0x11, g815_target, g815_feat_idx, 0x1c }; } break; case KeyboardModel::g910: diff --git a/src/classes/Keyboard.h b/src/classes/Keyboard.h index ccb2837..7344763 100644 --- a/src/classes/Keyboard.h +++ b/src/classes/Keyboard.h @@ -57,6 +57,7 @@ class LedKeyboard { { 0x46d, 0xc33f, (uint16_t)KeyboardModel::g815 }, { 0x46d, 0xc32b, (uint16_t)KeyboardModel::g910 }, { 0x46d, 0xc335, (uint16_t)KeyboardModel::g910 }, + { 0x46d, 0xc541, (uint16_t)KeyboardModel::g915 }, { 0x46d, 0xc339, (uint16_t)KeyboardModel::gpro } }; @@ -71,6 +72,7 @@ class LedKeyboard { g810, g815, g910, + g915, gpro }; enum class StartupMode : uint8_t { diff --git a/src/helpers/help.cpp b/src/helpers/help.cpp index aac491a..e673738 100644 --- a/src/helpers/help.cpp +++ b/src/helpers/help.cpp @@ -38,6 +38,7 @@ namespace help { else if(cmdName == "g810-led") return KeyboardFeatures::g810; else if(cmdName == "g815-led") return KeyboardFeatures::g815; else if(cmdName == "g910-led") return KeyboardFeatures::g910; + else if(cmdName == "g915-led") return KeyboardFeatures::g915; else if(cmdName == "gpro-led") return KeyboardFeatures::gpro; return KeyboardFeatures::all; } diff --git a/src/helpers/help.h b/src/helpers/help.h index 1d176f0..cb8fd99 100644 --- a/src/helpers/help.h +++ b/src/helpers/help.h @@ -54,6 +54,7 @@ namespace help { g810 = rgb | commit | logo1 | numpad | multimedia | setall | setgroup | setkey | setindicators | poweronfx, g815 = rgb | commit | logo1 | numpad | multimedia | gkeys | setall | setgroup | setkey | setindicators | onboardmode, g910 = rgb | commit | logo1 | logo2 | numpad | multimedia | gkeys | setall | setgroup | setkey | setindicators | poweronfx | userstoredlighting, + g915 = rgb | commit | logo1 | numpad | multimedia | gkeys | setall | setgroup | setkey | setindicators | onboardmode, gpro = rgb | commit | logo1 | setall | setgroup | setkey | setindicators | poweronfx | userstoredlighting }; inline KeyboardFeatures operator|(KeyboardFeatures a, KeyboardFeatures b); diff --git a/src/main.cpp b/src/main.cpp index e867327..304af19 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -306,6 +306,9 @@ int main(int argc, char **argv) { case 4: kbd.SupportedKeyboards = { { vendorID, productID, (uint16_t)LedKeyboard::KeyboardModel::g815 } }; break; + case 5: + kbd.SupportedKeyboards = { { vendorID, productID, (uint16_t)LedKeyboard::KeyboardModel::g915 } }; + break; default: break; } diff --git a/udev/g810-led.rules b/udev/g810-led.rules index 90b743b..5e1cfd6 100644 --- a/udev/g810-led.rules +++ b/udev/g810-led.rules @@ -8,6 +8,7 @@ ACTION=="add", SUBSYSTEMS=="usb", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c3 ACTION=="add", SUBSYSTEMS=="usb", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c331", MODE="666" RUN+="/usr/bin/g810-led -p /etc/g810-led/profile" ACTION=="add", SUBSYSTEMS=="usb", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c337", MODE="666" RUN+="/usr/bin/g810-led -p /etc/g810-led/profile" ACTION=="add", SUBSYSTEMS=="usb", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c33f", MODE="666" RUN+="/usr/bin/g815-led -p /etc/g810-led/profile" +ACTION=="add", SUBSYSTEMS=="usb", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c541", MODE="666" RUN+="/usr/bin/g915-led -p /etc/g810-led/profile" ACTION=="add", SUBSYSTEMS=="usb", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c32b", MODE="666" RUN+="/usr/bin/g910-led -p /etc/g810-led/profile" ACTION=="add", SUBSYSTEMS=="usb", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c335", MODE="666" RUN+="/usr/bin/g910-led -p /etc/g810-led/profile" ACTION=="add", SUBSYSTEMS=="usb", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c339", MODE="666" RUN+="/usr/bin/gpro-led -p /etc/g810-led/profile"