#include "ikvm_input.hpp" #include "ikvm_server.hpp" #include #include #include #include #include #include #include #include #include #include #include "scancodes.hpp" namespace fs = std::filesystem; namespace ikvm { using namespace phosphor::logging; using namespace sdbusplus::xyz::openbmc_project::Common::File::Error; Input::Input(const std::string& kbdPath, const std::string& ptrPath) : keyboardFd(-1), pointerFd(-1), keyboardReport{0}, pointerReport{0}, keyboardPath(kbdPath), pointerPath(ptrPath) { hidUdcStream.exceptions(std::ofstream::failbit | std::ofstream::badbit); hidUdcStream.open(hidUdcPath, std::ios::out | std::ios::app); } Input::~Input() { if (keyboardFd >= 0) { close(keyboardFd); } if (pointerFd >= 0) { close(pointerFd); } disconnect(); hidUdcStream.close(); } void Input::connect() { try { for (const auto& port : fs::directory_iterator(usbVirtualHubPath)) { if (fs::is_directory(port) && !fs::is_symlink(port) && !fs::exists(port.path() / "gadget/suspended")) { const std::string portId = port.path().filename(); hidUdcStream << portId << std::endl; break; } } } catch (fs::filesystem_error& e) { log("Failed to search USB virtual hub port", entry("ERROR=%s", e.what())); return; } catch (std::ofstream::failure& e) { log("Failed to connect HID gadget", entry("ERROR=%s", e.what())); return; } if (!keyboardPath.empty()) { keyboardFd = open(keyboardPath.c_str(), O_RDWR | O_CLOEXEC | O_NONBLOCK); if (keyboardFd < 0) { log("Failed to open input device", entry("PATH=%s", keyboardPath.c_str()), entry("ERROR=%s", strerror(errno))); elog(xyz::openbmc_project::Common::File::Open::ERRNO(errno), xyz::openbmc_project::Common::File::Open::PATH( keyboardPath.c_str())); } } if (!pointerPath.empty()) { pointerFd = open(pointerPath.c_str(), O_RDWR | O_CLOEXEC | O_NONBLOCK); if (pointerFd < 0) { log("Failed to open input device", entry("PATH=%s", pointerPath.c_str()), entry("ERROR=%s", strerror(errno))); elog(xyz::openbmc_project::Common::File::Open::ERRNO(errno), xyz::openbmc_project::Common::File::Open::PATH( pointerPath.c_str())); } } } void Input::disconnect() { if (keyboardFd >= 0) { close(keyboardFd); keyboardFd = -1; } if (pointerFd >= 0) { close(pointerFd); pointerFd = -1; } try { hidUdcStream << "" << std::endl; } catch (std::ofstream::failure& e) { log("Failed to disconnect HID gadget", entry("ERROR=%s", e.what())); } } void Input::keyEvent(rfbBool down, rfbKeySym key, rfbClientPtr cl) { Server::ClientData* cd = (Server::ClientData*)cl->clientData; Input* input = cd->input; bool sendKeyboard = false; if (input->keyboardFd < 0) { return; } if (down) { uint8_t sc = keyToScancode(key); if (sc) { if (input->keysDown.find(key) == input->keysDown.end()) { for (unsigned int i = 2; i < KEY_REPORT_LENGTH; ++i) { if (!input->keyboardReport[i]) { input->keyboardReport[i] = sc; input->keysDown.insert(std::make_pair(key, i)); sendKeyboard = true; break; } } } } else { uint8_t mod = keyToMod(key); if (mod) { input->keyboardReport[0] |= mod; sendKeyboard = true; } } } else { auto it = input->keysDown.find(key); if (it != input->keysDown.end()) { input->keyboardReport[it->second] = 0; input->keysDown.erase(it); sendKeyboard = true; } else { uint8_t mod = keyToMod(key); if (mod) { input->keyboardReport[0] &= ~mod; sendKeyboard = true; } } } if (sendKeyboard) { input->writeKeyboard(input->keyboardReport); } } void Input::pointerEvent(int buttonMask, int x, int y, rfbClientPtr cl) { Server::ClientData* cd = (Server::ClientData*)cl->clientData; Input* input = cd->input; Server* server = (Server*)cl->screen->screenData; const Video& video = server->getVideo(); if (input->pointerFd < 0) { return; } input->pointerReport[0] = ((buttonMask & 0x4) >> 1) | ((buttonMask & 0x2) << 1) | (buttonMask & 0x1); if (x >= 0 && (unsigned int)x < video.getWidth()) { uint16_t xx = (uint16_t)(x * (SHRT_MAX + 1) / video.getWidth()); memcpy(&input->pointerReport[1], &xx, 2); } if (y >= 0 && (unsigned int)y < video.getHeight()) { uint16_t yy = (uint16_t)(y * (SHRT_MAX + 1) / video.getHeight()); memcpy(&input->pointerReport[3], &yy, 2); } rfbDefaultPtrAddEvent(buttonMask, x, y, cl); input->writePointer(input->pointerReport); } void Input::sendWakeupPacket() { uint8_t wakeupReport[KEY_REPORT_LENGTH] = {0}; if (pointerFd >= 0) { uint16_t xy = SHRT_MAX / 2; memcpy(&wakeupReport[1], &xy, 2); memcpy(&wakeupReport[3], &xy, 2); writePointer(wakeupReport); } if (keyboardFd >= 0) { memset(&wakeupReport[0], 0, KEY_REPORT_LENGTH); wakeupReport[0] = keyToMod(XK_Shift_L); if (!writeKeyboard(wakeupReport)) { return; } wakeupReport[0] = 0; writeKeyboard(wakeupReport); } } uint8_t Input::keyToMod(rfbKeySym key) { uint8_t mod = 0; if (key >= XK_Shift_L && key <= XK_Control_R) { mod = shiftCtrlMap[key - XK_Shift_L]; } else if (key >= XK_Meta_L && key <= XK_Alt_R) { mod = metaAltMap[key - XK_Meta_L]; } return mod; } uint8_t Input::keyToScancode(rfbKeySym key) { uint8_t scancode = 0; if ((key >= 'A' && key <= 'Z') || (key >= 'a' && key <= 'z')) { scancode = USBHID_KEY_A + ((key & 0x5F) - 'A'); } else if (key >= '1' && key <= '9') { scancode = USBHID_KEY_1 + (key - '1'); } else if (key >= XK_F1 && key <= XK_F12) { scancode = USBHID_KEY_F1 + (key - XK_F1); } else if (key >= XK_KP_F1 && key <= XK_KP_F4) { scancode = USBHID_KEY_F1 + (key - XK_KP_F1); } else if (key >= XK_KP_1 && key <= XK_KP_9) { scancode = USBHID_KEY_KP_1 + (key - XK_KP_1); } else { switch (key) { case XK_exclam: scancode = USBHID_KEY_1; break; case XK_at: scancode = USBHID_KEY_2; break; case XK_numbersign: scancode = USBHID_KEY_3; break; case XK_dollar: scancode = USBHID_KEY_4; break; case XK_percent: scancode = USBHID_KEY_5; break; case XK_asciicircum: scancode = USBHID_KEY_6; break; case XK_ampersand: scancode = USBHID_KEY_7; break; case XK_asterisk: scancode = USBHID_KEY_8; break; case XK_parenleft: scancode = USBHID_KEY_9; break; case XK_0: case XK_parenright: scancode = USBHID_KEY_0; break; case XK_Return: scancode = USBHID_KEY_RETURN; break; case XK_Escape: scancode = USBHID_KEY_ESC; break; case XK_BackSpace: scancode = USBHID_KEY_BACKSPACE; break; case XK_Tab: case XK_KP_Tab: scancode = USBHID_KEY_TAB; break; case XK_space: case XK_KP_Space: scancode = USBHID_KEY_SPACE; break; case XK_minus: case XK_underscore: scancode = USBHID_KEY_MINUS; break; case XK_plus: case XK_equal: scancode = USBHID_KEY_EQUAL; break; case XK_bracketleft: case XK_braceleft: scancode = USBHID_KEY_LEFTBRACE; break; case XK_bracketright: case XK_braceright: scancode = USBHID_KEY_RIGHTBRACE; break; case XK_backslash: case XK_bar: scancode = USBHID_KEY_BACKSLASH; break; case XK_colon: case XK_semicolon: scancode = USBHID_KEY_SEMICOLON; break; case XK_quotedbl: case XK_apostrophe: scancode = USBHID_KEY_APOSTROPHE; break; case XK_grave: case XK_asciitilde: scancode = USBHID_KEY_GRAVE; break; case XK_comma: case XK_less: scancode = USBHID_KEY_COMMA; break; case XK_period: case XK_greater: scancode = USBHID_KEY_DOT; break; case XK_slash: case XK_question: scancode = USBHID_KEY_SLASH; break; case XK_Caps_Lock: scancode = USBHID_KEY_CAPSLOCK; break; case XK_Print: scancode = USBHID_KEY_PRINT; break; case XK_Scroll_Lock: scancode = USBHID_KEY_SCROLLLOCK; break; case XK_Pause: scancode = USBHID_KEY_PAUSE; break; case XK_Insert: case XK_KP_Insert: scancode = USBHID_KEY_INSERT; break; case XK_Home: case XK_KP_Home: scancode = USBHID_KEY_HOME; break; case XK_Page_Up: case XK_KP_Page_Up: scancode = USBHID_KEY_PAGEUP; break; case XK_Delete: case XK_KP_Delete: scancode = USBHID_KEY_DELETE; break; case XK_End: case XK_KP_End: scancode = USBHID_KEY_END; break; case XK_Page_Down: case XK_KP_Page_Down: scancode = USBHID_KEY_PAGEDOWN; break; case XK_Right: case XK_KP_Right: scancode = USBHID_KEY_RIGHT; break; case XK_Left: case XK_KP_Left: scancode = USBHID_KEY_LEFT; break; case XK_Down: case XK_KP_Down: scancode = USBHID_KEY_DOWN; break; case XK_Up: case XK_KP_Up: scancode = USBHID_KEY_UP; break; case XK_Num_Lock: scancode = USBHID_KEY_NUMLOCK; break; case XK_KP_Enter: scancode = USBHID_KEY_KP_ENTER; break; case XK_KP_Equal: scancode = USBHID_KEY_KP_EQUAL; break; case XK_KP_Multiply: scancode = USBHID_KEY_KP_MULTIPLY; break; case XK_KP_Add: scancode = USBHID_KEY_KP_ADD; break; case XK_KP_Subtract: scancode = USBHID_KEY_KP_SUBTRACT; break; case XK_KP_Decimal: scancode = USBHID_KEY_KP_DECIMAL; break; case XK_KP_Divide: scancode = USBHID_KEY_KP_DIVIDE; break; case XK_KP_0: scancode = USBHID_KEY_KP_0; break; } } return scancode; } bool Input::writeKeyboard(const uint8_t *report) { std::unique_lock lk(keyMutex); uint retryCount = HID_REPORT_RETRY_MAX; while (retryCount > 0) { if (write(keyboardFd, report, KEY_REPORT_LENGTH) == KEY_REPORT_LENGTH) { break; } if (errno != EAGAIN) { if (errno != ESHUTDOWN) { log("Failed to write keyboard report", entry("ERROR=%s", strerror(errno))); } break; } lk.unlock(); std::this_thread::sleep_for(std::chrono::milliseconds(10)); lk.lock(); retryCount--; } if (!retryCount || errno) { return false; } return true; } void Input::writePointer(const uint8_t *report) { std::unique_lock lk(ptrMutex); uint retryCount = HID_REPORT_RETRY_MAX; while (retryCount > 0) { if (write(pointerFd, report, PTR_REPORT_LENGTH) == PTR_REPORT_LENGTH) { break; } if (errno != EAGAIN) { if (errno != ESHUTDOWN) { log("Failed to write pointer report", entry("ERROR=%s", strerror(errno))); } break; } lk.unlock(); std::this_thread::sleep_for(std::chrono::milliseconds(10)); lk.lock(); retryCount--; } } } // namespace ikvm