diff --git a/Controllers/LenovoControllers/LenovoDevices.h b/Controllers/LenovoControllers/LenovoDevices.h new file mode 100644 index 00000000..ee53dad6 --- /dev/null +++ b/Controllers/LenovoControllers/LenovoDevices.h @@ -0,0 +1,464 @@ +/*-------------------------------------------------------------------*\ +| LenovoDevices.h | +| | +| Describes zones for various Lenovo Legion Devices | +| | +| Cooper Hall (geobot19) 17 Apr 2022 | +| | +| Note: additions here must be adeed to RGBController_LenovoUSB.cpp in| +| the switch statements which are on lines 28 and 60 at time of | +| writing | +\*-------------------------------------------------------------------*/ + #include + #include "RGBControllerKeyNames.h" + #include "RGBController.h" + +#ifndef LENOVODEVICES_H +#define LENOVODEVICES_H +/*-----------------------------------------------------*\ +| Keyboard product IDs | +\*-----------------------------------------------------*/ +#define LEGION_7_6_PID_1 0xC968 +#define LEGION_7_6_PID_2 0xC957 +#define LEGION_7_6_PID_3 0xC956 + +struct lenovo_led +{ + uint8_t zone_num; + uint8_t led_num; + std::string name; +}; + +/*---------*\ +| LED MAPS | +\*---------*/ +const lenovo_led legion_7_g_6_ansi_leds[] +{ + /*----------------*\ + | zone 1, keyboard | + \*----------------*/ + //row 1 + {1, 0x01, KEY_EN_ESCAPE},//0 + {1, 0x02, KEY_EN_F1},//1 + {1, 0x03, KEY_EN_F2},//2 + {1, 0x04, KEY_EN_F3},//3 + {1, 0x05, KEY_EN_F4},//4 + {1, 0x06, KEY_EN_F5},//5 + {1, 0x07, KEY_EN_F6},//6 + {1, 0x08, KEY_EN_F7},//7 + {1, 0x09, KEY_EN_F8},//8 + {1, 0x0A, KEY_EN_F9},//9 + {1, 0x0B, KEY_EN_F10},//10 + {1, 0x0C, KEY_EN_F11},//11 + {1, 0x0D, KEY_EN_F12},//12 + {1, 0x0E, KEY_EN_INSERT},//13 + {1, 0x0F, KEY_EN_PRINT_SCREEN},//14 + {1, 0x10, KEY_EN_DELETE},//15 + {1, 0x11, KEY_EN_HOME},//16 + {1, 0x12, KEY_EN_END},//17 + {1, 0x13, KEY_EN_PAGE_UP},//18 + {1, 0x14, KEY_EN_PAGE_DOWN},//19 + + //row 2 + {1, 0x16, "Key: ~"},//20 + {1, 0x17, "Key: !"},//21 + {1, 0x18, "Key: @"},//22 + {1, 0x19, "Key: #"},//23 + {1, 0x1A, "Key: $"},//24 + {1, 0x1B, "Key: %"},//25 + {1, 0x1C, "Key: ^"},//26 + {1, 0x1D, "Key: &"},//27 + {1, 0x1E, "Key: *"},//28 + {1, 0x1F, "Key: ("},//29 + {1, 0x20, "Key: )"},//30 + {1, 0x21, "Key: _"},//31 + {1, 0x22, "Key: +"},//32 + {1, 0x26, KEY_EN_NUMPAD_LOCK},//33 + {1, 0x27, KEY_EN_NUMPAD_DIVIDE},//34 + {1, 0x28, KEY_EN_NUMPAD_TIMES},//35 + {1, 0x29, KEY_EN_NUMPAD_MINUS},//36 + + //row 3 + {1, 0x2B, KEY_EN_BACK_TICK},//37 + {1, 0x2C, KEY_EN_1},//38 + {1, 0x2D, KEY_EN_2},//39 + {1, 0x2E, KEY_EN_3},//40 + {1, 0x2F, KEY_EN_4},//41 + {1, 0x30, KEY_EN_5},//42 + {1, 0x31, KEY_EN_6},//43 + {1, 0x32, KEY_EN_7},//44 + {1, 0x33, KEY_EN_8},//45 + {1, 0x34, KEY_EN_9},//46 + {1, 0x35, KEY_EN_0},//47 + {1, 0x36, KEY_EN_MINUS},//48 + {1, 0x37, KEY_EN_EQUALS},//49 + {1, 0x38, KEY_EN_BACKSPACE},//50 + {1, 0x39, KEY_EN_BACKSPACE},//51 + {1, 0x3A, KEY_EN_BACKSPACE},//52 + {1, 0x3B, KEY_EN_NUMPAD_LOCK},//53 + + //row 4 + {1, 0x40, KEY_EN_TAB},//54 + {1, 0x42, KEY_EN_Q},//55 + {1, 0x43, KEY_EN_W},//56 + {1, 0x44, KEY_EN_E},//57 + {1, 0x45, KEY_EN_R},//58 + {1, 0x46, KEY_EN_T},//59 + {1, 0x47, KEY_EN_Y},//60 + {1, 0x48, KEY_EN_U},//61 + {1, 0x49, KEY_EN_I},//62 + {1, 0x4A, KEY_EN_O},//63 + {1, 0x4B, KEY_EN_P},//64 + {1, 0x4C, "Key: {"},//65 + {1, 0x4D, "Key: }"},//66 + {1, 0x4E, "Key: |"},//67 + {1, 0x4F, KEY_EN_NUMPAD_7},//68 + {1, 0x50, KEY_EN_NUMPAD_8},//69 + {1, 0x51, KEY_EN_NUMPAD_9},//70 + {1, 0x67, KEY_EN_NUMPAD_PLUS},//71 + + //row 5 + {1, 0x55, KEY_EN_CAPS_LOCK},//72 + {1, 0x56, KEY_EN_CAPS_LOCK},//73 + {1, 0x57, KEY_EN_CAPS_LOCK},//74 + {1, 0x6D, KEY_EN_A},//75 + {1, 0x6E, KEY_EN_S},//76 + {1, 0x58, KEY_EN_D},//77 + {1, 0x59, KEY_EN_F},//78 + {1, 0x5A, KEY_EN_G},//79 + {1, 0x71, KEY_EN_H},//80 + {1, 0x72, KEY_EN_J},//81 + {1, 0x5B, KEY_EN_K},//82 + {1, 0x5C, KEY_EN_L},//83 + {1, 0x5D, "Key: :"},//84 + {1, 0x5E, KEY_EN_SEMICOLON},//85 + {1, 0x5F, "Key: \""},//86 + {1, 0x60, KEY_EN_QUOTE},//87 + {1, 0x77, KEY_EN_ANSI_ENTER},//88 + {1, 0x78, KEY_EN_ANSI_ENTER},//89 + {1, 0x61, KEY_EN_LEFT_BRACKET},//90 + {1, 0x62, KEY_EN_RIGHT_BRACKET},//91 + {1, 0x63, KEY_EN_ANSI_BACK_SLASH},//92 + {1, 0x64, "Key: Numpad Home"},//93 + {1, 0x65, "Key: Numpad Up Arrow"},//94 + {1, 0x66, "Key: Numpad PgUP"},//95 + {1, 0x68, KEY_EN_NUMPAD_PLUS},//96 + + //row 6 + {1, 0x6A, KEY_EN_LEFT_SHIFT},//97 + {1, 0x6B, KEY_EN_LEFT_SHIFT},//98 + {1, 0x82, KEY_EN_Z},//99 + {1, 0x83, KEY_EN_X},//100 + {1, 0x6F, KEY_EN_C},//101 + {1, 0x70, KEY_EN_V},//102 + {1, 0x87, KEY_EN_B},//103 + {1, 0x88, KEY_EN_N},//104 + {1, 0x73, KEY_EN_M},//105 + {1, 0x74, "Key: <"},//106 + {1, 0x75, "Key: >"},//107 + {1, 0x76, "Key: ?"},//108 + {1, 0x8D, KEY_EN_RIGHT_SHIFT},//109 + {1, 0xa2, KEY_EN_RIGHT_SHIFT},//110 + {1, 0x79, KEY_EN_NUMPAD_4},//111 + {1, 0x7A, "Key: Numpad Right Arrow"},//112 + {1, 0x7B, KEY_EN_NUMPAD_5},//113 + {1, 0x7C, KEY_EN_NUMPAD_6},//114 + {1, 0x7D, "Key: Numpad Left Arrow"},//115 + + //row 7 + {1, 0x89, KEY_EN_COMMA},//116 + {1, 0x8A, KEY_EN_PERIOD},//117 + {1, 0x8B, KEY_EN_FORWARD_SLASH},//118 + {1, 0x8E, KEY_EN_NUMPAD_1},//119 + {1, 0x8F, "Key: Numpad End"},//120 + {1, 0x90, KEY_EN_NUMPAD_2},//121 + {1, 0x91, "Key: Numpad Down Arrow"},//122 + {1, 0x92, KEY_EN_NUMPAD_3},//123 + {1, 0x93, "Key: Numpad PgDn"},//124 + {1, 0xa7, KEY_EN_NUMPAD_ENTER},//125 + + //row 8 + {1, 0x7F, KEY_EN_LEFT_CONTROL},//126 + {1, 0x80, KEY_EN_LEFT_FUNCTION},//127 + {1, 0x96, KEY_EN_LEFT_WINDOWS},//128 + {1, 0x97, KEY_EN_LEFT_ALT},//129 + {1, 0x98, KEY_EN_SPACE},//130 + {1, 0x99, KEY_EN_SPACE},//131 + {1, 0x9A, KEY_EN_RIGHT_ALT},//132 + {1, 0x9B, KEY_EN_RIGHT_CONTROL},//133 + {1, 0x9C, KEY_EN_LEFT_ARROW},//134 + {1, 0x9D, KEY_EN_UP_ARROW},//135 + {1, 0x9E, "Key: Up Brightness"},//136 + {1, 0x9F, KEY_EN_DOWN_ARROW},//137 + {1, 0xA0, "Key: Down Brightness"},//138 + {1, 0xA1, KEY_EN_RIGHT_ARROW},//139 + {1, 0xA3, KEY_EN_NUMPAD_0},//140 + {1, 0xA4, "Key: Numpad Insert"},//141 + {1, 0xA5, KEY_EN_NUMPAD_PERIOD},//142 + {1, 0xA6, "Key: Numpad Delete"},//143 + {1, 0xA8, KEY_EN_NUMPAD_ENTER},//144 + + //ISO + {1, 0x6C, "Key: | (ISO)"},//145 + {1, 0x81, KEY_EN_ISO_BACK_SLASH},//146 + + /*----------------*\ + | zone 2, logo | + \*----------------*/ + + {2, 0x01, "Logo Bottom Left"},//147 + {2, 0x02, "Logo LED 2"}, + {2, 0x03, "Logo LED 3"}, + {2, 0x04, "Logo LED 4"}, + {2, 0x05, "Logo LED Top Left"}, + {2, 0x06, "Logo LED 6"}, + {2, 0x07, "Logo LED 7"}, + {2, 0x08, "Logo LED 8"}, + {2, 0x09, "Logo LED Top Right"}, + {2, 0x0A, "Logo LED 10"}, + {2, 0x0B, "Logo LED 11"}, + {2, 0x0C, "Logo LED 12"}, + {2, 0x0D, "Logo Bottom Right"},//159 + + /*----------------*\ + | zone 3, vents | + \*----------------*/ + + //left + {3, 0x01, "Left Vent Front"},//160 + {3, 0x02, "Left Vent LED 2"}, + {3, 0x03, "Left Vent LED 3"}, + {3, 0x04, "Left Vent LED 4"}, + {3, 0x05, "Left Vent LED 5"}, + {3, 0x06, "Left Vent LED 6"}, + {3, 0x07, "Left Vent LED 7"}, + {3, 0x08, "Left Vent LED 8"}, + {3, 0x09, "Left Vent LED 9"}, + {3, 0x0A, "Left Vent LED 10"}, + {3, 0x0B, "Left Vent LED 11"}, + {3, 0x0C, "Left Vent LED 12"}, + {3, 0x0D, "Left Vent LED 13"}, + {3, 0x0E, "Left Vent LED 14"}, + {3, 0x0F, "Left Vent LED 15"}, + {3, 0x10, "Left Vent LED 16"}, + {3, 0x11, "Left Vent LED 17"}, + {3, 0x12, "Left Vent LED 18"}, + {3, 0x13, "Left Vent LED 19"}, + {3, 0x14, "Left Vent LED 20"}, + {3, 0x15, "Left Vent LED 21"}, + {3, 0x16, "Left Vent LED 22"}, + {3, 0x17, "Left Vent LED 23"}, + {3, 0x18, "Left Vent LED 24"}, + {3, 0x19, "Left Vent LED 25"}, + {3, 0x1A, "Left Vent LED 26"}, + {3, 0x1B, "Left Vent Back"},//186 + + //right + {3, 0x1C, "Right Vent Front"},//187 + {3, 0x1D, "Right Vent LED 2"}, + {3, 0x1E, "Right Vent LED 3"}, + {3, 0x1F, "Right Vent LED 4"}, + {3, 0x20, "Right Vent LED 5"}, + {3, 0x21, "Right Vent LED 6"}, + {3, 0x22, "Right Vent LED 7"}, + {3, 0x23, "Right Vent LED 8"}, + {3, 0x24, "Right Vent LED 9"}, + {3, 0x25, "Right Vent LED 10"}, + {3, 0x26, "Right Vent LED 11"}, + {3, 0x27, "Right Vent LED 12"}, + {3, 0x28, "Right Vent LED 13"}, + {3, 0x29, "Right Vent LED 14"}, + {3, 0x2A, "Right Vent LED 15"}, + {3, 0x2B, "Right Vent LED 16"}, + {3, 0x2C, "Right Vent LED 17"}, + {3, 0x2D, "Right Vent LED 18"}, + {3, 0x2E, "Right Vent LED 19"}, + {3, 0x2F, "Right Vent LED 20"}, + {3, 0x30, "Right Vent LED 21"}, + {3, 0x31, "Right Vent LED 22"}, + {3, 0x32, "Right Vent LED 23"}, + {3, 0x33, "Right Vent LED 24"}, + {3, 0x34, "Right Vent LED 25"}, + {3, 0x35, "Right Vent LED 26"}, + {3, 0x36, "Right Vent Back"},//213 + + //back right vent + {3, 0x37, "Back Right Vent Left"},//214 + {3, 0x38, "Back Right Vent 2"}, + {3, 0x39, "Back Right Vent 3"}, + {3, 0x3A, "Back Right Vent 4"}, + {3, 0x3B, "Back Right Vent 5"}, + {3, 0x3C, "Back Right Vent 6"}, + {3, 0x3D, "Back Right Vent 7"}, + {3, 0x3E, "Back Right Vent 8"}, + {3, 0x3F, "Back Right Vent 10"}, + {3, 0x40, "Back Right Vent 11"}, + {3, 0x41, "Back Right Vent 12"}, + {3, 0x42, "Back Right Vent 13"}, + {3, 0x43, "Back Right Vent 14"}, + {3, 0x44, "Back Right Vent 15"}, + {3, 0x45, "Back Right Vent 16"}, + {3, 0x46, "Back Right Vent 17"}, + {3, 0x47, "Back Right Vent 18"}, + {3, 0x48, "Back Right Vent 19"}, + {3, 0x49, "Back Right Vent 20"}, + {3, 0x4A, "Back Right Vent 21"}, + {3, 0x4B, "Back Right Vent 22"}, + {3, 0x4C, "Back Right Vent 23"}, + {3, 0x4D, "Back Right Vent 24"}, + {3, 0x4E, "Back Right Vent 25"}, + {3, 0x4F, "Back Right Vent 26"}, + {3, 0x50, "Back Right Vent Right"},//239 + + //back left vent + {3, 0x51, "Back Left Vent Right"},//240 + {3, 0x52, "Back Left Vent 2"}, + {3, 0x53, "Back Left Vent 3"}, + {3, 0x54, "Back Left Vent 4"}, + {3, 0x55, "Back Left Vent 5"}, + {3, 0x56, "Back Left Vent 6"}, + {3, 0x57, "Back Left Vent 7"}, + {3, 0x58, "Back Left Vent 8"}, + {3, 0x59, "Back Left Vent 10"}, + {3, 0x5A, "Back Left Vent 11"}, + {3, 0x5B, "Back Left Vent 12"}, + {3, 0x5C, "Back Left Vent 13"}, + {3, 0x5D, "Back Left Vent 14"}, + {3, 0x5E, "Back Left Vent 15"}, + {3, 0x5F, "Back Left Vent 16"}, + {3, 0x60, "Back Left Vent 17"}, + {3, 0x61, "Back Left Vent 18"}, + {3, 0x62, "Back Left Vent 19"}, + {3, 0x63, "Back Left Vent 20"}, + {3, 0x64, "Back Left Vent 21"}, + {3, 0x65, "Back Left Vent 22"}, + {3, 0x66, "Back Left Vent 23"}, + {3, 0x67, "Back Left Vent 24"}, + {3, 0x68, "Back Left Vent 25"}, + {3, 0x69, "Back Left Vent 26"}, + {3, 0x6A, "Back Left Vent Left"},//265 + + /*-----------------*\ + | zone 4, neon | + \*-----------------*/ + //left side + {4, 0x01, "Neon LED 1 Top Left"},//266 + {4, 0x02, "Neon LED 2"}, + {4, 0x03, "Neon LED 3"}, + {4, 0x04, "Neon LED 4"}, + {4, 0x05, "Neon LED 5"}, + {4, 0x06, "Neon LED 6"}, + {4, 0x07, "Neon LED 7"}, + {4, 0x08, "Neon LED 8"}, + {4, 0x09, "Neon LED 9"}, + {4, 0x0A, "Neon LED 10"}, + {4, 0x0B, "Neon LED 11"}, + {4, 0x0C, "Neon LED 12"}, + {4, 0x0D, "Neon LED 13"}, + {4, 0x0E, "Neon LED 14"}, + {4, 0x0F, "Neon LED 15"}, + {4, 0x10, "Neon LED 16"}, + {4, 0x11, "Neon LED 17"}, + {4, 0x12, "Neon LED 18"}, + {4, 0x13, "Neon LED 19"}, + {4, 0x14, "Neon LED 20"}, + {4, 0x15, "Neon LED 21 Left Corner"},//286 + + //front + {4, 0x16, "Neon LED 22 Left Corner"},//287 + {4, 0x17, "Neon LED 23"}, + {4, 0x18, "Neon LED 24"}, + {4, 0x19, "Neon LED 25"}, + {4, 0x1A, "Neon LED 26"}, + {4, 0x1B, "Neon LED 27"}, + {4, 0x1C, "Neon LED 28"}, + {4, 0x1D, "Neon LED 29"}, + {4, 0x1E, "Neon LED 30"}, + {4, 0x1F, "Neon LED 31"}, + {4, 0x20, "Neon LED 32"}, + {4, 0x21, "Neon LED 33"}, + {4, 0x22, "Neon LED 34"}, + {4, 0x23, "Neon LED 35"}, + {4, 0x24, "Neon LED 36"}, + {4, 0x25, "Neon LED 37"}, + {4, 0x26, "Neon LED 38"}, + {4, 0x27, "Neon LED 39"}, + {4, 0x28, "Neon LED 40"}, + {4, 0x29, "Neon LED 41"}, + {4, 0x2A, "Neon LED 42"}, + {4, 0x2B, "Neon LED 43"}, + {4, 0x2C, "Neon LED 44"}, + {4, 0x2D, "Neon LED 45"}, + {4, 0x2E, "Neon LED 46"}, + {4, 0x2F, "Neon LED 47"}, + {4, 0x30, "Neon LED 48"}, + {4, 0x31, "Neon LED 49"}, + {4, 0x32, "Neon LED 50"}, + {4, 0x33, "Neon LED 51"}, + {4, 0x34, "Neon LED 52"}, + {4, 0x35, "Neon LED 53"}, + {4, 0x36, "Neon LED 54"}, + {4, 0x37, "Neon LED 55"}, + {4, 0x38, "Neon LED 56"}, + {4, 0x39, "Neon LED 57"}, + {4, 0x3A, "Neon LED 58"}, + {4, 0x3B, "Neon LED 59"}, + {4, 0x3C, "Neon LED 60"}, + {4, 0x3D, "Neon LED 61"}, + {4, 0x3E, "Neon LED 62"}, + {4, 0x3F, "Neon LED 63"}, + {4, 0x40, "Neon LED 64"}, + {4, 0x41, "Neon LED 65"}, + {4, 0x42, "Neon LED 66"}, + {4, 0x43, "Neon LED 67"}, + {4, 0x44, "Neon LED 68"}, + {4, 0x45, "Neon LED 69"}, + {4, 0x46, "Neon LED 70"}, + {4, 0x47, "Neon LED 71"}, + {4, 0x48, "Neon LED 72"}, + {4, 0x49, "Neon LED 73"}, + {4, 0x4A, "Neon LED 74"}, + {4, 0x4B, "Neon LED 75"}, + {4, 0x4C, "Neon LED 76"}, + {4, 0x4D, "Neon LED 77"}, + {4, 0x4E, "Neon LED 78 Right Corner"},//343 + + //right side + {4, 0x4F, "Neon LED 79 Right Corner"},//344 + {4, 0x50, "Neon LED 80"}, + {4, 0x51, "Neon LED 81"}, + {4, 0x52, "Neon LED 82"}, + {4, 0x53, "Neon LED 83"}, + {4, 0x54, "Neon LED 84"}, + {4, 0x55, "Neon LED 85"}, + {4, 0x56, "Neon LED 86"}, + {4, 0x57, "Neon LED 87"}, + {4, 0x58, "Neon LED 88"}, + {4, 0x59, "Neon LED 89"}, + {4, 0x5A, "Neon LED 90"}, + {4, 0x5B, "Neon LED 91"}, + {4, 0x5C, "Neon LED 92"}, + {4, 0x5D, "Neon LED 93"}, + {4, 0x5E, "Neon LED 94"}, + {4, 0x5F, "Neon LED 95"}, + {4, 0x60, "Neon LED 96"}, + {4, 0x61, "Neon LED 97"}, + {4, 0x62, "Neon LED 98"}, + {4, 0x63, "Neon LED 99 Top Right"},//364 +}; + +/*-------------------*\ +| Zone Definitions | +\*-------------------*/ + +struct lenovo_zone +{ + std::string name; + zone_type type; + unsigned int height; + unsigned int width; + unsigned int start; //index to start reading the list of leds + unsigned int end; //end index +}; + +#endif diff --git a/Controllers/LenovoControllers/LenovoUSBController.cpp b/Controllers/LenovoControllers/LenovoUSBController.cpp new file mode 100644 index 00000000..6422be22 --- /dev/null +++ b/Controllers/LenovoControllers/LenovoUSBController.cpp @@ -0,0 +1,118 @@ +/*-------------------------------------------------------------------*\ +| LenovoUSBController.cpp | +| | +| Driver for various Lenovo Devices | +| | +| Cooper Hall (geobot19) 17 Apr 2022 | +| | +\*-------------------------------------------------------------------*/ + +#include "LenovoUSBController.h" +#include "LogManager.h" + +using namespace std; + +LenovoUSBController::LenovoUSBController(hid_device* dev_handle, const char* path, uint16_t in_pid) +{ + const uint8_t sz = HID_MAX_STR; + wchar_t tmp[sz]; + + dev = dev_handle; + location = path; + pid = in_pid; + + hid_get_manufacturer_string(dev, tmp, sz); + std::wstring w_tmp = wstring(tmp); + name = string(w_tmp.begin(), w_tmp.end()); + + hid_get_product_string(dev, tmp, sz); + w_tmp = wstring(tmp); + name.append(" ").append(string(w_tmp.begin(), w_tmp.end())); + + setDeviceSoftwareMode(); +} + +LenovoUSBController::~LenovoUSBController() +{ + hid_close(dev); +} + +uint16_t LenovoUSBController::getPid() +{ + return pid; +} + +string LenovoUSBController::getName() +{ + return name; +} + +string LenovoUSBController::getLocation() +{ + return location; +} + +void LenovoUSBController::setZoneLeds(uint8_t zone_num, vector> &led_colors) +{ + for(size_t curr = 0; curr < led_colors.size();) + { + uint8_t buffer[LENOVO_HID_PACKET_SIZE] = {LENOVO_INSTRUCTION_START, LENOVO_ZONE_ID_0+zone_num, 0, 0}; + + /*---------------------------------------------------------*\ + | Set the colour bytes in the packet | + | buffer[2] is required to know the amount of leds in packet| + | so it is set here as well | + \*---------------------------------------------------------*/ + for(; buffer[2] < LENOVO_MAX_LEDS_PER_PACKET && curr < led_colors.size(); ++buffer[2]) + { + uint8_t offset = (buffer[2] * 4) + 4; + + /*------------------*\ + |write the led number| + \*------------------*/ + buffer[offset] = led_colors[curr].first; + /*--------------*\ + |write the colors| + \*--------------*/ + buffer[offset + 1] = RGBGetRValue(led_colors[curr].second); + buffer[offset + 2] = RGBGetGValue(led_colors[curr].second); + buffer[offset + 3] = RGBGetBValue(led_colors[curr].second); + + ++curr; + } + + hid_send_feature_report(dev, buffer, LENOVO_HID_PACKET_SIZE); + } +} + +void LenovoUSBController::setSingleLED(uint8_t zone_num, uint8_t led_num, RGBColor color) +{ + uint8_t buffer[LENOVO_HID_PACKET_SIZE] = {LENOVO_INSTRUCTION_START, LENOVO_ZONE_ID_0+zone_num, 1, 0, led_num, RGBGetRValue(color), RGBGetGValue(color), RGBGetBValue(color)}; + hid_send_feature_report(dev, buffer, LENOVO_HID_PACKET_SIZE); +} + +void LenovoUSBController::sendBasicInstruction(uint8_t instruction) +{ + uint8_t buffer[LENOVO_HID_PACKET_SIZE] = {LENOVO_INSTRUCTION_START, instruction}; + hid_send_feature_report(dev, buffer, LENOVO_HID_PACKET_SIZE); +} + +void LenovoUSBController::setDeviceSoftwareMode() +{ + /*---------------------------------------*\ + | this is required for the device listen | + | to the software protocol | + \*---------------------------------------*/ + sendBasicInstruction(0xB2); +} + +void LenovoUSBController::setDeviceHardwareMode() +{ + /*---------------------------------------*\ + |releases the device from sofware mode so | + |that onboard controlls can be used | + |this has not been shown to happen between| + |reboots | + \*---------------------------------------*/ + sendBasicInstruction(0xB1); +} \ No newline at end of file diff --git a/Controllers/LenovoControllers/LenovoUSBController.h b/Controllers/LenovoControllers/LenovoUSBController.h new file mode 100644 index 00000000..f3172cc4 --- /dev/null +++ b/Controllers/LenovoControllers/LenovoUSBController.h @@ -0,0 +1,67 @@ +/*-------------------------------------------------------------------*\ +| LenovoUSBController.h | +| | +| Driver for various Lenovo Devices | +| | +| Cooper Hall (geobot19) 17 Apr 2022 | +| | +\*-------------------------------------------------------------------*/ + +#include "RGBController.h" +#include "LogManager.h" + +#include +#include +#include +#include +#include + +#ifndef HID_MAX_STR +#define HID_MAX_STR 255 +#endif + +#ifndef LENOVOUSBCONTROLLER_H +#define LENOVOUSBCONTROLLER_H + +#define LENOVO_INSTRUCTION_START 0x07 +#define LENOVO_ZONE_ID_0 0xA0 + +#define LENOVO_HID_PACKET_SIZE 192 +#define LENOVO_MAX_LEDS_PER_PACKET 0x2F + +class LenovoUSBController +{ + public: + /*--------------*\ + |ctor(s) and dtor| + \*--------------*/ + LenovoUSBController(hid_device* dev_handle, const char* path, uint16_t in_pid); + ~LenovoUSBController(); + + /*--------------*\ + |device functions| + \*--------------*/ + void setZoneLeds(uint8_t zone_num, std::vector> &led_colors); + void setSingleLED(uint8_t zone_num, uint8_t led_num, RGBColor color); + uint16_t getPid(); + std::string getName(); + std::string getLocation(); + void setDeviceSoftwareMode(); + void setDeviceHardwareMode(); + + private: + /*--------------*\ + |data members | + \*--------------*/ + std::string name; + hid_device *dev; + std::string location; + uint16_t pid; + + /*--------------*\ + |device functions| + \*--------------*/ + void sendBasicInstruction(uint8_t instruction); +}; + +#endif diff --git a/Controllers/LenovoControllers/LenovoUSBDetect.cpp b/Controllers/LenovoControllers/LenovoUSBDetect.cpp new file mode 100644 index 00000000..e50f40cf --- /dev/null +++ b/Controllers/LenovoControllers/LenovoUSBDetect.cpp @@ -0,0 +1,50 @@ +/*-------------------------------------------------------------------*\ +| LenovoUSBDetect.h | +| | +| Describes zones for various Lenovo Legion Devices | +| | +| Cooper Hall (geobot19) 17 Apr 2022 | +| | +\*-------------------------------------------------------------------*/ + +#include "Detector.h" +#include "LogManager.h" +#include "RGBController.h" +#include "LenovoUSBController.h" +#include "LenovoDevices.h" +#include "RGBController_LenovoUSB.h" +#include + +/*-----------------------------------------------------*\ +| vendor IDs | +\*-----------------------------------------------------*/ +#define ITE_VID 0x048D + +/*-----------------------------------------------------*\ +| Interface, Usage, and Usage Page | +\*-----------------------------------------------------*/ +enum +{ + LENOVO_PAGE = 0xFF89, + LENOVO_USEAGE = 0x07 +}; + +void DetectLenovoLegionUSBControllers(hid_device_info* info, const std::string& name) +{ + static const char* controller_name = "Lenovo Legion Laptop"; + + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + LenovoUSBController* controller = new LenovoUSBController(dev, info->path, info->product_id); + RGBController_LenovoUSB* rgb_controller = new RGBController_LenovoUSB(controller); + rgb_controller->name = name; + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR_PU("Lenovo Legion 7 gen 6", DetectLenovoLegionUSBControllers, ITE_VID, LEGION_7_6_PID_1, LENOVO_PAGE, LENOVO_USEAGE); +REGISTER_HID_DETECTOR_PU("Lenovo Legion 7 gen 6", DetectLenovoLegionUSBControllers, ITE_VID, LEGION_7_6_PID_2, LENOVO_PAGE, LENOVO_USEAGE); +REGISTER_HID_DETECTOR_PU("Lenovo Legion 7 gen 6", DetectLenovoLegionUSBControllers, ITE_VID, LEGION_7_6_PID_3, LENOVO_PAGE, LENOVO_USEAGE); diff --git a/Controllers/LenovoControllers/RGBController_LenovoUSB.cpp b/Controllers/LenovoControllers/RGBController_LenovoUSB.cpp new file mode 100644 index 00000000..8b38f834 --- /dev/null +++ b/Controllers/LenovoControllers/RGBController_LenovoUSB.cpp @@ -0,0 +1,303 @@ +#include "LenovoDevices.h" +#include "RGBController_LenovoUSB.h" + +#include +#include +#include +using namespace std; + +/*--------------------------------------------------------------------------------------*\ +|note: the RGBController_LenovoUSB constructor determines which list of leds to pull from| +\*--------------------------------------------------------------------------------------*/ + +/**------------------------------------------------------------------*\ + @name Lenovo USB + @category Keyboard + @type USB + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectLenovoLegionUSBControllers + @comment Tested on Lenovo Legion 7/7i gen. 6 ANSI and ISO models + Hardware modes are not implented + PLEASE UPDATE YOUR BIOS IF YOU HAVE ISSUES WITH HARDWARE MODES AFTER CLOSING OPENRGB +\*-------------------------------------------------------------------*/ + +/*--------------------------------------------------------*\ +| Legion 7 gen 6: 6 zones | +| | +|Note: the device has 4 zones in the protocol however, the | +|vent lights have been split into 4 zones to improve ease | +|of use | +\*--------------------------------------------------------*/ + +/*------*\ +|keyboard| +\*------*/ +lenovo_zone lenovo_legion_7_gen_6_kbd +{ + "Keyboard", + ZONE_TYPE_MATRIX, + 10, + 22, + 0, + 146, +}; +/*------*\ +|logo | +\*------*/ +lenovo_zone lenovo_legion_7_gen_6_logo +{ + "Logo", + ZONE_TYPE_LINEAR, + 1, + 13, + 147, + 159, +}; +/*------*\ +|vents | +\*------*/ +lenovo_zone lenovo_legion_7_gen_6_vent_left +{ + "Left vent", + ZONE_TYPE_LINEAR, + 1, + 26, + 160, + 186, +}; +lenovo_zone lenovo_legion_7_gen_6_vent_right +{ + "Right vent", + ZONE_TYPE_LINEAR, + 1, + 26, + 187, + 213, +}; +lenovo_zone lenovo_legion_7_gen_6_vent_back_right +{ + "Back Right vent", + ZONE_TYPE_LINEAR, + 1, + 25, + 214, + 239, +}; +lenovo_zone lenovo_legion_7_gen_6_vent_back_left +{ + "Back Left vent", + ZONE_TYPE_LINEAR, + 1, + 25, + 240, + 265, +}; +/*------*\ +|neon | +\*------*/ +lenovo_zone lenovo_legion_7_gen_6_neon +{ + "Neon", + ZONE_TYPE_LINEAR, + 1, + 99, + 266, + 364, +}; + +RGBController_LenovoUSB::RGBController_LenovoUSB(LenovoUSBController* controller_ptr) +{ + controller = controller_ptr; + + mode Direct; + Direct.name = "Direct"; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + name = controller->getName(); + type = DEVICE_TYPE_KEYBOARD; + vendor = "Lenovo"; + + switch(controller->getPid()) + { + case LEGION_7_6_PID_1: + case LEGION_7_6_PID_2: + case LEGION_7_6_PID_3: + description = "Lenovo Legion 7 gen 6, ANSI VERSION"; + lenovo_leds = legion_7_g_6_ansi_leds; + lenovo_size_of_leds = sizeof(legion_7_g_6_ansi_leds)/sizeof(lenovo_led); + break; + } + + for(unsigned int i = 0; i < lenovo_size_of_leds; i++) + { + leds.push_back({lenovo_leds[i].name, i}); + } + leds.shrink_to_fit(); + + SetupZones(); +} + +RGBController_LenovoUSB::~RGBController_LenovoUSB() +{ + /*--------------------------------*\ + | see LenovoUSBController.cpp for | + | details | + \*--------------------------------*/ + controller->setDeviceHardwareMode(); + + delete controller; +} + +void RGBController_LenovoUSB::SetupZones() +{ + vector lenovo_zones; + + switch(controller->getPid()) + { + case LEGION_7_6_PID_1: + case LEGION_7_6_PID_2: + case LEGION_7_6_PID_3: + lenovo_zones.push_back(lenovo_legion_7_gen_6_kbd); + lenovo_zones.push_back(lenovo_legion_7_gen_6_logo); + lenovo_zones.push_back(lenovo_legion_7_gen_6_vent_left); + lenovo_zones.push_back(lenovo_legion_7_gen_6_vent_right); + lenovo_zones.push_back(lenovo_legion_7_gen_6_vent_back_right); + lenovo_zones.push_back(lenovo_legion_7_gen_6_vent_back_left); + lenovo_zones.push_back(lenovo_legion_7_gen_6_neon); + break; + } + + for(int i = 0; i < lenovo_zones.size(); i++) + { + zone new_zone; + new_zone.name = lenovo_zones[i].name; + new_zone.type = lenovo_zones[i].type; + new_zone.leds_count = lenovo_zones[i].end - lenovo_zones[i].start + 1; + new_zone.leds_max = new_zone.leds_count; + new_zone.leds_min = new_zone.leds_count; + + if(lenovo_zones[i].type == ZONE_TYPE_MATRIX) + { + new_zone.matrix_map = new matrix_map_type; + new_zone.matrix_map->height = lenovo_zones[i].height; + new_zone.matrix_map->width = lenovo_zones[i].width; + new_zone.matrix_map->map = new unsigned int[new_zone.matrix_map->height * new_zone.matrix_map->width]; + + /*----------------------------------------------------*\ + | This section sets up the maps of zones where elements| + | may have an oddly aligned on the matrix | + | please be sure to document in some way which zones | + | in a device you may be implementing have this poperty| + | (Example in the LENOVO_7_6 section bellow) | + \*----------------------------------------------------*/ + + switch(controller->getPid()) + { + case LEGION_7_6_PID_1: + case LEGION_7_6_PID_2: + case LEGION_7_6_PID_3: + /*----------------------------------------------------------------------*\ + | for this device, only the keyboard map (lenovo_legion_7_gen_6_kbd) uses| + | a custom map, so that map is setup here | + \*----------------------------------------------------------------------*/ + + new_zone.matrix_map->map = new unsigned int[lenovo_zones[i].width * lenovo_zones[i].height] + { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, NA, NA, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, NA, NA, NA, NA, 53, 33, 34, 35, 36, + 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, NA, NA, 68, 69, 70, NA, + 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 90, 65, 91, 66, 92, 67, NA, 93, 94, 95, 71, + 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 85, 84, 87, 86, 88, 89, 111, 113, 114, 96, + 97, 98, 99, 100, 101, 102, 103, 104, 105, 116, 106, 117, 107, 118, 108, 109, 110, NA, 112, NA, 115, NA, + 126, 127, 128, 129, 130, NA, NA, NA, NA, 131, 132, 133, NA, NA, NA, 136, NA, NA, 119, 121, 123, 125, + NA, 146, 145, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 135, NA, NA, 120, 122, 124, 144, + NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 134, 137, 139, NA, 140, 141, 143, NA, + NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 138, NA, NA, NA, NA, 142, NA + }; + break; + } + } + else + { + new_zone.matrix_map = NULL; + } + + zones.push_back(new_zone); + } + + SetupColors(); +} + +void RGBController_LenovoUSB::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_LenovoUSB::UpdateSingleLED(int led) +{ + if(led != NA) + { + controller->setSingleLED(lenovo_leds[led].zone_num, lenovo_leds[led].led_num, colors[led]); + } +} + +void RGBController_LenovoUSB::UpdateZoneLEDs(int zone) +{ + uint8_t device_zone = lenovo_leds[zones[zone].start_idx].zone_num; + vector> color_map; + + for(int i = 0; i < zones[zone].leds_count; i++) + { + int index = zones[zone].start_idx+i; + + color_map.push_back({lenovo_leds[index].led_num, colors[index]}); + } + + color_map.shrink_to_fit(); + + controller->setZoneLeds(device_zone, color_map); +} + +void RGBController_LenovoUSB::DeviceUpdateLEDs() +{ + for(unsigned int i = 0; i < lenovo_size_of_leds;) + { + uint8_t curr_device_zone = lenovo_leds[i].zone_num; + vector> curr_color_map; + + for(; i < lenovo_size_of_leds && lenovo_leds[i].zone_num == curr_device_zone && i < colors.size(); i++) + { + curr_color_map.push_back({lenovo_leds[i].led_num, colors[i]}); + } + + controller->setZoneLeds(curr_device_zone, curr_color_map); + } +} + +void RGBController_LenovoUSB::SetCustomMode() +{ + /*---------------------------------------------------------*\ + | This device has only one mode | + \*---------------------------------------------------------*/ + active_mode = 0; +} + +void RGBController_LenovoUSB::DeviceUpdateMode() +{ + /*---------------------------------------------------------*\ + | This device does not support multiple modes | + \*---------------------------------------------------------*/ +} + +void RGBController_LenovoUSB::DeviceSaveMode() +{ + /*---------------------------------------------------------*\ + | This device does not support saving or multiple modes | + \*---------------------------------------------------------*/ +} diff --git a/Controllers/LenovoControllers/RGBController_LenovoUSB.h b/Controllers/LenovoControllers/RGBController_LenovoUSB.h new file mode 100644 index 00000000..357d012f --- /dev/null +++ b/Controllers/LenovoControllers/RGBController_LenovoUSB.h @@ -0,0 +1,44 @@ +/*-------------------------------------------------------------------*\ +| RGBController_LenovoUSB.h | +| | +| interface for various Lenovo Legion Devices | +| | +| Cooper Hall (geobot19) 17 Apr 2022 | +| | +\*-------------------------------------------------------------------*/ + +#include "LenovoDevices.h" +#include "LenovoUSBController.h" +#include "RGBController.h" + +#include + +#ifndef RGBCONTROLLER_LENOVOUSB_H +#define RGBCONTROLLER_LENOVOUSB_H + +#define NA 0xFFFFFFFF + +class RGBController_LenovoUSB : public RGBController +{ + public: + RGBController_LenovoUSB(LenovoUSBController* controller_ptr); + ~RGBController_LenovoUSB(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void SetCustomMode(); + void DeviceUpdateMode(); + void DeviceSaveMode(); + + private: + unsigned int lenovo_size_of_leds; + LenovoUSBController *controller; + const lenovo_led *lenovo_leds; +}; + +#endif diff --git a/OpenRGB.pro b/OpenRGB.pro index 700ff217..8df2671e 100644 --- a/OpenRGB.pro +++ b/OpenRGB.pro @@ -126,6 +126,7 @@ INCLUDEPATH += Controllers/HyperXMouseController/ \ Controllers/HyperXMousematController/ \ Controllers/LEDStripController/ \ + Controllers/LenovoControllers/ \ Controllers/LianLiController/ \ Controllers/LIFXController/ \ Controllers/LogitechController/ \ @@ -408,6 +409,9 @@ HEADERS += Controllers/KeychronKeyboardController/RGBController_KeychronKeyboard.h \ Controllers/LEDStripController/LEDStripController.h \ Controllers/LEDStripController/RGBController_LEDStrip.h \ + Controllers/LenovoControllers/LenovoDevices.h \ + Controllers/LenovoControllers/LenovoUSBController.h \ + Controllers/LenovoControllers/RGBController_LenovoUSB.h \ Controllers/LexipMouseController/LexipMouseController.h \ Controllers/LexipMouseController/RGBController_LexipMouse.h \ Controllers/LIFXController/LIFXController.h \ @@ -897,6 +901,9 @@ SOURCES += Controllers/LEDStripController/LEDStripController.cpp \ Controllers/LEDStripController/LEDStripControllerDetect.cpp \ Controllers/LEDStripController/RGBController_LEDStrip.cpp \ + Controllers/LenovoControllers/LenovoUSBController.cpp \ + Controllers/LenovoControllers/LenovoUSBDetect.cpp \ + Controllers/LenovoControllers/RGBController_LenovoUSB.cpp \ Controllers/LexipMouseController/LexipMouseController.cpp \ Controllers/LexipMouseController/LexipMouseControllerDetect.cpp \ Controllers/LexipMouseController/RGBController_LexipMouse.cpp \