/*-----------------------------------------*\ | DasKeyboardController.cpp | | | | Driver for Das Keyboard RGB keyboard | | lighting controller | | | | Frank Niessen (denk_mal) 12/16/2020 | \*-----------------------------------------*/ #include #include "DasKeyboardController.h" using namespace std::chrono_literals; DasKeyboardController::DasKeyboardController(hid_device *dev_handle, const char *path) { dev = dev_handle; location = path; version = ""; useTraditionalSendData = false; SendInitialize(); } DasKeyboardController::~DasKeyboardController() { hid_close(dev); } std::string DasKeyboardController::GetDeviceLocation() { return "HID: " + location; } std::string DasKeyboardController::GetSerialString() { wchar_t serial_string[128] = {}; int err = hid_get_serial_number_string(dev, serial_string, 128); std::string return_string; if(err == 0) { std::wstring return_wstring = serial_string; return_string = std::string(return_wstring.begin(), return_wstring.end()); } if(return_string.empty()) { return_string = version; } return return_string; } std::string DasKeyboardController::GetVersionString() { if(version.length() < 17) { return version; } std::string fw_version = "V"; fw_version += version.substr(6, 2); fw_version += "."; fw_version += version.substr(15, 2); fw_version += ".0"; return fw_version; } std::string DasKeyboardController::GetLayoutString() { /*-----------------------------------------------------*\ | Experimental for now; should be '16' for US and '28' | | for EU layout | \*-----------------------------------------------------*/ if(version.length() < 17) { return "NONE"; } std::string layout_id = version.substr(3, 2); if(layout_id == "16") { return "US"; } return "EU"; } void DasKeyboardController::SendColors(unsigned char key_id, unsigned char mode, unsigned char red, unsigned char green, unsigned char blue) { if(key_id < 130) { unsigned char usb_buf[] = {0xEA, 0x08, 0x78, 0x08, static_cast(key_id), mode, red, green, blue}; SendData(usb_buf, sizeof(usb_buf)); } else { /*-----------------------------------------------------*\ | Special handling for the Q-Button; only color, no mode| \*-----------------------------------------------------*/ unsigned char usb_buf[] = {0xEA, 0x06, 0x78, 0x06, red, green, blue}; SendData(usb_buf, sizeof(usb_buf)); } } void DasKeyboardController::SendInitialize() { unsigned char usb_buf[65]; /*-----------------------------------------------------*\ | Zero out buffer | \*-----------------------------------------------------*/ memset(usb_buf, 0x00, sizeof(usb_buf)); int cnt_receive = 0; while(!cnt_receive) { /*-----------------------------------------------------*\ | Set up Initialize connection | \*-----------------------------------------------------*/ unsigned char usb_init[] = {0xEA, 0x02, 0xB0}; SendData(usb_init, sizeof(usb_init)); /*-----------------------------------------------------*\ | Get Version String | \*-----------------------------------------------------*/ cnt_receive = ReceiveData(usb_buf, sizeof(usb_buf)); /*-----------------------------------------------------*\ | check if the faster modern transfer method is working | \*-----------------------------------------------------*/ if(!cnt_receive) { if(useTraditionalSendData) { break; } useTraditionalSendData = true; } } std::string fw_version(reinterpret_cast(&usb_buf[2])); version = fw_version; } void DasKeyboardController::SendApply() { /*-----------------------------------------------------*\ | Set up Terminate Color packet | \*-----------------------------------------------------*/ unsigned char usb_buf_send[] = {0xEA, 0x03, 0x78, 0x0a}; unsigned char usb_buf_receive[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; SendData(usb_buf_send, sizeof(usb_buf_send)); ReceiveData(usb_buf_receive, sizeof(usb_buf_receive)); } void DasKeyboardController::SendData(const unsigned char *data, const unsigned int length) { if(useTraditionalSendData) { SendDataTraditional(data, length); } else { SendDataModern(data, length); } } void DasKeyboardController::SendDataModern(const unsigned char *data, const unsigned int length) { /*-----------------------------------------------------*\ | modern SendData (send whole bytes in one transfer) | \*-----------------------------------------------------*/ unsigned char usb_buf[65]; unsigned int err_cnt = 3; int res = -1; while(res == -1) { /*-----------------------------------------------------*\ | Fill data into send buffer | \*-----------------------------------------------------*/ unsigned int chk_sum = 0; usb_buf[0] = 1; for(unsigned int idx = 0; idx < length; idx++) { usb_buf[idx + 1] = data[idx]; chk_sum ^= data[idx]; } usb_buf[length + 1] = chk_sum; res = hid_send_feature_report(dev, usb_buf, length + 2); if(res == -1) { if(!err_cnt--) { return; } } /*-----------------------------------------------------*\ | Hack to work around a firmware bug in v21.27.0 | \*-----------------------------------------------------*/ std::this_thread::sleep_for(0.3ms); } } void DasKeyboardController::SendDataTraditional(const unsigned char *data, const unsigned int length) { /*-----------------------------------------------------*\ | traditional SendData (split into chunks of 8 byte) | \*-----------------------------------------------------*/ unsigned char usb_buf[9]; /*-----------------------------------------------------*\ | Fill data into send buffer | \*-----------------------------------------------------*/ unsigned int err_cnt = 3; unsigned int chk_sum = 0; usb_buf[8] = 0; for(unsigned int idx = 0; idx < length + 1; idx += 7) { usb_buf[0] = 1; for(unsigned int fld_idx = 1; fld_idx < 8; fld_idx++) { unsigned int tmp_idx = idx + fld_idx - 1; if(tmp_idx < length) { usb_buf[fld_idx] = data[tmp_idx]; chk_sum ^= data[tmp_idx]; } else if(tmp_idx == length) { usb_buf[fld_idx] = chk_sum; } else { usb_buf[fld_idx] = 0; } } int res = hid_send_feature_report(dev, usb_buf, 8); if(res == -1) { idx = 0; if(!err_cnt--) { return; } } /*-----------------------------------------------------*\ | Hack to work around a firmware bug in v21.27.0 | \*-----------------------------------------------------*/ std::this_thread::sleep_for(0.3ms); } } int DasKeyboardController::ReceiveData(unsigned char *data, const unsigned int max_length) { unsigned char usb_buf[9]; std::vector receive_buf; /*-----------------------------------------------------*\ | Fill data from receive buffer | \*-----------------------------------------------------*/ unsigned int chk_sum = 0; do { memset(usb_buf, 0x00, sizeof(usb_buf)); usb_buf[0x00] = 0x01; int res = hid_get_feature_report(dev, usb_buf, 8); if(res == -1) { break; } if(usb_buf[0]) { for(unsigned int ii = 0; ii < 8; ii++) { receive_buf.push_back(usb_buf[ii]); chk_sum ^= usb_buf[ii]; } } } while(usb_buf[0]); /*-----------------------------------------------------*\ | clean up data buffer | \*-----------------------------------------------------*/ for(unsigned int ii = 0; ii < max_length; ii++) { data[ii] = 0; } /*-----------------------------------------------------*\ | If checksum is not correct, return with empty buffer | \*-----------------------------------------------------*/ if(chk_sum) { return -1; } size_t response_size = 0; if(receive_buf.size() > 1) { response_size = receive_buf.at(1); if(response_size + 2 > receive_buf.size()) { return -1; } if(response_size > max_length) { response_size = static_cast(max_length); } /*-----------------------------------------------------*\ | Remove first two bytes (signature?) and content length| \*-----------------------------------------------------*/ for(size_t ii = 0; ii < response_size - 1; ii++) { data[ii] = receive_buf.at(ii + 2); } } return response_size; }