1
0
mirror of https://github.com/MatMoul/g810-led.git synced 2024-12-23 17:26:11 +00:00

Add support for G915 (libusb only)

This commit is contained in:
Marcin 2021-08-23 20:05:52 +02:00
parent 5ee810a520
commit 1c84af312a
8 changed files with 162 additions and 25 deletions

View File

@ -1,6 +1,6 @@
# g810-led</br> # g810-led</br>
Linux led controller for Logitech G213, G410, G413, G512, G513, G610, G810, G815, G910 and GPRO Keyboards.</br> Linux led controller for Logitech G213, G410, G413, G512, G513, G610, G810, G815, G910, G915 and GPRO Keyboards.</br>
## Compatible keyboards :</br> ## Compatible keyboards :</br>
- **G213 Prodigy**</br> - **G213 Prodigy**</br>
@ -14,6 +14,7 @@ Linux led controller for Logitech G213, G410, G413, G512, G513, G610, G810, G815
- **G815 LIGHTSYNC**</br> - **G815 LIGHTSYNC**</br>
- **G910 Orion Spark**</br> - **G910 Orion Spark**</br>
- **G910 Orion Spectrum**</br> - **G910 Orion Spectrum**</br>
- **G915 LIGHTSPEED**</br>
- **GPRO**</br> - **GPRO**</br>
## Contribute and evolution :</br> ## Contribute and evolution :</br>
@ -36,6 +37,7 @@ You can load predefined configurations on startup!
`g810-led --help`</br> `g810-led --help`</br>
`g815-led --help`</br> `g815-led --help`</br>
`g910-led --help`</br> `g910-led --help`</br>
`g915-led --help`</br>
`gpro-led --help`</br> `gpro-led --help`</br>
`g810-led --help-keys`</br> `g810-led --help-keys`</br>

View File

@ -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/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/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/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 @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 sample_profiles/* $(DESTDIR)/etc/$(PROGN)/samples
@cp udev/$(PROGN).rules $(DESTDIR)/etc/udev/rules.d @cp udev/$(PROGN).rules $(DESTDIR)/etc/udev/rules.d
@ -114,6 +115,7 @@ uninstall:
@rm /usr/bin/g610-led @rm /usr/bin/g610-led
@rm /usr/bin/g815-led @rm /usr/bin/g815-led
@rm /usr/bin/g910-led @rm /usr/bin/g910-led
@rm /usr/bin/g915-led
@rm /usr/bin/gpro-led @rm /usr/bin/gpro-led
@rm /usr/bin/$(PROGN) @rm /usr/bin/$(PROGN)

View File

@ -307,8 +307,17 @@ bool LedKeyboard::open(uint16_t vendorID, uint16_t productID, string serial) {
return false; return false;
} }
if(libusb_kernel_driver_active(m_hidHandle, 1) == 1) { int interface_num;
if(libusb_detach_kernel_driver(m_hidHandle, 1) != 0) { 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); libusb_exit(m_ctx);
m_ctx = NULL; m_ctx = NULL;
return false; return false;
@ -316,9 +325,9 @@ bool LedKeyboard::open(uint16_t vendorID, uint16_t productID, string serial) {
m_isKernellDetached = true; m_isKernellDetached = true;
} }
if(libusb_claim_interface(m_hidHandle, 1) < 0) { if(libusb_claim_interface(m_hidHandle, interface_num) < 0) {
if(m_isKernellDetached==true) { if(m_isKernellDetached==true) {
libusb_attach_kernel_driver(m_hidHandle, 1); libusb_attach_kernel_driver(m_hidHandle, interface_num);
m_isKernellDetached = false; m_isKernellDetached = false;
} }
libusb_exit(m_ctx); libusb_exit(m_ctx);
@ -348,9 +357,19 @@ bool LedKeyboard::close() {
return true; return true;
#elif defined(libusb) #elif defined(libusb)
if (m_hidHandle == NULL) return true; 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) { if(m_isKernellDetached==true) {
libusb_attach_kernel_driver(m_hidHandle, 1); libusb_attach_kernel_driver(m_hidHandle, interface_num);
m_isKernellDetached = false; m_isKernellDetached = false;
} }
libusb_close(m_hidHandle); libusb_close(m_hidHandle);
@ -388,6 +407,9 @@ bool LedKeyboard::commit() {
case KeyboardModel::g910: case KeyboardModel::g910:
data = { 0x11, 0xff, 0x0f, 0x5d }; data = { 0x11, 0xff, 0x0f, 0x5d };
break; break;
case KeyboardModel::g915:
data = { 0x11, 0x01, 0x0b, 0x7f };
break;
default: default:
return false; return false;
} }
@ -411,6 +433,18 @@ bool LedKeyboard::setKeys(KeyValueArray keyValues) {
switch (currentDevice.model) { switch (currentDevice.model) {
case KeyboardModel::g815: 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++) { for (uint8_t i = 0; i < keyValues.size(); i++) {
uint32_t colorkey = static_cast<uint32_t>(keyValues[i].color.red | keyValues[i].color.green << 8 | keyValues[i].color.blue << 16 ); uint32_t colorkey = static_cast<uint32_t>(keyValues[i].color.red | keyValues[i].color.green << 8 | keyValues[i].color.blue << 16 );
if (KeyByColors.count(colorkey) == 0) KeyByColors.insert(pair<uint32_t, vector<KeyValue>>(colorkey, {})); if (KeyByColors.count(colorkey) == 0) KeyByColors.insert(pair<uint32_t, vector<KeyValue>>(colorkey, {}));
@ -422,7 +456,7 @@ bool LedKeyboard::setKeys(KeyValueArray keyValues) {
uint8_t gi = 0; uint8_t gi = 0;
while (gi < x.second.size()) { while (gi < x.second.size()) {
size_t data_size = 20; 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.red);
data.push_back(x.second[0].color.green); data.push_back(x.second[0].color.green);
data.push_back(x.second[0].color.blue); data.push_back(x.second[0].color.blue);
@ -693,6 +727,7 @@ bool LedKeyboard::setAllKeys(LedKeyboard::Color color) {
case KeyboardModel::g810: case KeyboardModel::g810:
case KeyboardModel::g815: case KeyboardModel::g815:
case KeyboardModel::g910: case KeyboardModel::g910:
case KeyboardModel::g915:
case KeyboardModel::gpro: case KeyboardModel::gpro:
for (uint8_t i = 0; i < keyGroupLogo.size(); i++) keyValues.push_back({keyGroupLogo[i], color}); 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}); 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; LedKeyboard::byte_buffer_t data;
switch (currentDevice.model) { switch (currentDevice.model) {
case KeyboardModel::g815: 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) { switch (value) {
case 0x00: case 0x00:
case 0x01: case 0x01:
data = { 0x11, 0xff, 0x0c, 0x0c, value }; data = { 0x11, g815_target, g815_feat_idx, 0x0c, value };
data.resize(20, 0x00); data.resize(20, 0x00);
return sendDataInternal(data); return sendDataInternal(data);
default: default:
@ -747,17 +794,29 @@ bool LedKeyboard::setMNKey(uint8_t value) {
LedKeyboard::byte_buffer_t data; LedKeyboard::byte_buffer_t data;
switch (currentDevice.model) { switch (currentDevice.model) {
case KeyboardModel::g815: 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) { switch (value) {
case 0x01: case 0x01:
data = { 0x11, 0xff, 0x0b, 0x1c, 0x01 }; data = { 0x11, g815_target, g815_feat_idx, 0x1c, 0x01 };
data.resize(20, 0x00); data.resize(20, 0x00);
return sendDataInternal(data); return sendDataInternal(data);
case 0x02: case 0x02:
data = { 0x11, 0xff, 0x0b, 0x1c, 0x02 }; data = { 0x11, g815_target, g815_feat_idx, 0x1c, 0x02 };
data.resize(20, 0x00); data.resize(20, 0x00);
return sendDataInternal(data); return sendDataInternal(data);
case 0x03: case 0x03:
data = { 0x11, 0xff, 0x0b, 0x1c, 0x04 }; data = { 0x11, g815_target, g815_feat_idx, 0x1c, 0x04 };
data.resize(20, 0x00); data.resize(20, 0x00);
return sendDataInternal(data); return sendDataInternal(data);
default: default:
@ -791,10 +850,22 @@ bool LedKeyboard::setGKeysMode(uint8_t value) {
LedKeyboard::byte_buffer_t data; LedKeyboard::byte_buffer_t data;
switch (currentDevice.model) { switch (currentDevice.model) {
case KeyboardModel::g815: 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) { switch (value) {
case 0x00: case 0x00:
case 0x01: case 0x01:
data = { 0x11, 0xff, 0x0a, 0x2b, value }; data = { 0x11, g815_target, g815_feat_idx, 0x2b, value };
data.resize(20, 0x00); data.resize(20, 0x00);
return sendDataInternal(data); return sendDataInternal(data);
default: default:
@ -858,7 +929,19 @@ bool LedKeyboard::setOnBoardMode(OnBoardMode onBoardMode) {
byte_buffer_t data; byte_buffer_t data;
switch (currentDevice.model) { switch (currentDevice.model) {
case KeyboardModel::g815: case KeyboardModel::g815:
data = { 0x11, 0xff, 0x11, 0x1a, static_cast<uint8_t>(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<uint8_t>(onBoardMode) };
data.resize(20, 0x00); data.resize(20, 0x00);
return sendDataInternal(data); return sendDataInternal(data);
default: default:
@ -900,6 +983,8 @@ bool LedKeyboard::setNativeEffect(NativeEffect effect, NativeEffectPart part,
setNativeEffect(effect, LedKeyboard::NativeEffectPart::logo, period, color, storage)); setNativeEffect(effect, LedKeyboard::NativeEffectPart::logo, period, color, storage));
} }
unsigned char target = 0xff;
switch (currentDevice.model) { switch (currentDevice.model) {
case KeyboardModel::g213: case KeyboardModel::g213:
case KeyboardModel::g413: case KeyboardModel::g413:
@ -920,6 +1005,10 @@ bool LedKeyboard::setNativeEffect(NativeEffect effect, NativeEffectPart part,
protocolBytes[0] = 0x0f; protocolBytes[0] = 0x0f;
protocolBytes[1] = 0x1c; protocolBytes[1] = 0x1c;
break; break;
case KeyboardModel::g915:
protocolBytes[0] = 0x0a;
protocolBytes[1] = 0x1c;
target = 0x01;
case KeyboardModel::g910: case KeyboardModel::g910:
protocolBytes[0] = 0x10; protocolBytes[0] = 0x10;
protocolBytes[1] = 0x3c; protocolBytes[1] = 0x3c;
@ -929,7 +1018,7 @@ bool LedKeyboard::setNativeEffect(NativeEffect effect, NativeEffectPart part,
} }
byte_buffer_t data = { byte_buffer_t data = {
0x11, 0xff, protocolBytes[0], protocolBytes[1], 0x11, target, protocolBytes[0], protocolBytes[1],
(uint8_t)part, static_cast<uint8_t>(effectGroup), (uint8_t)part, static_cast<uint8_t>(effectGroup),
// color of static-color and breathing effects // color of static-color and breathing effects
color.red, color.green, color.blue, color.red, color.green, color.blue,
@ -951,7 +1040,19 @@ bool LedKeyboard::setNativeEffect(NativeEffect effect, NativeEffectPart part,
bool retval; bool retval;
switch (currentDevice.model) { switch (currentDevice.model) {
case KeyboardModel::g815: 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); setupData.resize(20, 0x00);
retval = sendDataInternal(setupData); retval = sendDataInternal(setupData);
@ -1030,20 +1131,32 @@ bool LedKeyboard::sendDataInternal(byte_buffer_t &data) {
*/ */
return true; return true;
#elif defined(libusb) #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 (! m_isOpen) return false;
if (data.size() > 20) { 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<unsigned char*>(data.data()), data.size(), 2000) < 0) const_cast<unsigned char*>(data.data()), data.size(), 2000) < 0)
return false; return false;
} else { } else {
if(libusb_control_transfer(m_hidHandle, 0x21, 0x09, 0x0211, 1, if(libusb_control_transfer(m_hidHandle, 0x21, 0x09, 0x0211, interface_num,
const_cast<unsigned char*>(data.data()), data.size(), 2000) < 0) const_cast<unsigned char*>(data.data()), data.size(), 2000) < 0)
return false; return false;
} }
usleep(1000); usleep(1000);
unsigned char buffer[64]; unsigned char buffer[64];
int len = 0; 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; return true;
#endif #endif
} }
@ -1089,17 +1202,29 @@ LedKeyboard::byte_buffer_t LedKeyboard::getKeyGroupAddress(LedKeyboard::KeyAddre
} }
break; break;
case KeyboardModel::g815: 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) { switch (keyAddressGroup) {
case LedKeyboard::KeyAddressGroup::logo: case LedKeyboard::KeyAddressGroup::logo:
return { 0x11, 0xff, 0x10, 0x1c }; return { 0x11, g815_target, g815_feat_idx, 0x1c };
case LedKeyboard::KeyAddressGroup::indicators: case LedKeyboard::KeyAddressGroup::indicators:
return { 0x11, 0xff, 0x10, 0x1c }; return { 0x11, g815_target, g815_feat_idx, 0x1c };
case LedKeyboard::KeyAddressGroup::gkeys: case LedKeyboard::KeyAddressGroup::gkeys:
return { 0x11, 0xff, 0x10, 0x1c }; return { 0x11, g815_target, g815_feat_idx, 0x1c };
case LedKeyboard::KeyAddressGroup::multimedia: case LedKeyboard::KeyAddressGroup::multimedia:
return { 0x11, 0xff, 0x10, 0x1c }; return { 0x11, g815_target, g815_feat_idx, 0x1c };
case LedKeyboard::KeyAddressGroup::keys: case LedKeyboard::KeyAddressGroup::keys:
return { 0x11, 0xff, 0x10, 0x1c }; return { 0x11, g815_target, g815_feat_idx, 0x1c };
} }
break; break;
case KeyboardModel::g910: case KeyboardModel::g910:

View File

@ -57,6 +57,7 @@ class LedKeyboard {
{ 0x46d, 0xc33f, (uint16_t)KeyboardModel::g815 }, { 0x46d, 0xc33f, (uint16_t)KeyboardModel::g815 },
{ 0x46d, 0xc32b, (uint16_t)KeyboardModel::g910 }, { 0x46d, 0xc32b, (uint16_t)KeyboardModel::g910 },
{ 0x46d, 0xc335, (uint16_t)KeyboardModel::g910 }, { 0x46d, 0xc335, (uint16_t)KeyboardModel::g910 },
{ 0x46d, 0xc541, (uint16_t)KeyboardModel::g915 },
{ 0x46d, 0xc339, (uint16_t)KeyboardModel::gpro } { 0x46d, 0xc339, (uint16_t)KeyboardModel::gpro }
}; };
@ -71,6 +72,7 @@ class LedKeyboard {
g810, g810,
g815, g815,
g910, g910,
g915,
gpro gpro
}; };
enum class StartupMode : uint8_t { enum class StartupMode : uint8_t {

View File

@ -38,6 +38,7 @@ namespace help {
else if(cmdName == "g810-led") return KeyboardFeatures::g810; else if(cmdName == "g810-led") return KeyboardFeatures::g810;
else if(cmdName == "g815-led") return KeyboardFeatures::g815; else if(cmdName == "g815-led") return KeyboardFeatures::g815;
else if(cmdName == "g910-led") return KeyboardFeatures::g910; else if(cmdName == "g910-led") return KeyboardFeatures::g910;
else if(cmdName == "g915-led") return KeyboardFeatures::g915;
else if(cmdName == "gpro-led") return KeyboardFeatures::gpro; else if(cmdName == "gpro-led") return KeyboardFeatures::gpro;
return KeyboardFeatures::all; return KeyboardFeatures::all;
} }

View File

@ -54,6 +54,7 @@ namespace help {
g810 = rgb | commit | logo1 | numpad | multimedia | setall | setgroup | setkey | setindicators | poweronfx, g810 = rgb | commit | logo1 | numpad | multimedia | setall | setgroup | setkey | setindicators | poweronfx,
g815 = rgb | commit | logo1 | numpad | multimedia | gkeys | setall | setgroup | setkey | setindicators | onboardmode, 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, 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 gpro = rgb | commit | logo1 | setall | setgroup | setkey | setindicators | poweronfx | userstoredlighting
}; };
inline KeyboardFeatures operator|(KeyboardFeatures a, KeyboardFeatures b); inline KeyboardFeatures operator|(KeyboardFeatures a, KeyboardFeatures b);

View File

@ -306,6 +306,9 @@ int main(int argc, char **argv) {
case 4: case 4:
kbd.SupportedKeyboards = { { vendorID, productID, (uint16_t)LedKeyboard::KeyboardModel::g815 } }; kbd.SupportedKeyboards = { { vendorID, productID, (uint16_t)LedKeyboard::KeyboardModel::g815 } };
break; break;
case 5:
kbd.SupportedKeyboards = { { vendorID, productID, (uint16_t)LedKeyboard::KeyboardModel::g915 } };
break;
default: default:
break; break;
} }

View File

@ -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}=="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}=="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}=="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}=="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}=="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" ACTION=="add", SUBSYSTEMS=="usb", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c339", MODE="666" RUN+="/usr/bin/gpro-led -p /etc/g810-led/profile"