1 #include "ikvm_input.hpp" 2 3 #include "ikvm_server.hpp" 4 #include "scancodes.hpp" 5 6 #include <err.h> 7 #include <errno.h> 8 #include <fcntl.h> 9 #include <rfb/keysym.h> 10 #include <sys/stat.h> 11 #include <sys/types.h> 12 13 #include <phosphor-logging/elog-errors.hpp> 14 #include <phosphor-logging/elog.hpp> 15 #include <phosphor-logging/log.hpp> 16 #include <xyz/openbmc_project/Common/File/error.hpp> 17 18 namespace fs = std::filesystem; 19 20 namespace ikvm 21 { 22 using namespace phosphor::logging; 23 using namespace sdbusplus::xyz::openbmc_project::Common::File::Error; 24 25 Input::Input(const std::string& kbdPath, const std::string& ptrPath, 26 const std::string& udc) : 27 keyboardFd(-1), pointerFd(-1), keyboardReport{0}, pointerReport{0}, 28 keyboardPath(kbdPath), pointerPath(ptrPath), udcName(udc) 29 { 30 hidUdcStream.exceptions(std::ofstream::failbit | std::ofstream::badbit); 31 hidUdcStream.open(hidUdcPath, std::ios::out | std::ios::app); 32 } 33 34 Input::~Input() 35 { 36 if (keyboardFd >= 0) 37 { 38 close(keyboardFd); 39 } 40 41 if (pointerFd >= 0) 42 { 43 close(pointerFd); 44 } 45 46 disconnect(); 47 hidUdcStream.close(); 48 } 49 50 void Input::connect() 51 { 52 try 53 { 54 if (udcName.empty()) 55 { 56 bool found = false; 57 for (const auto& port : fs::directory_iterator(usbVirtualHubPath)) 58 { 59 // /sys/bus/platform/devices/1e6a0000.usb-vhub/1e6a0000.usb-vhub:pX 60 if (fs::is_directory(port) && !fs::is_symlink(port)) 61 { 62 for (const auto& gadget : 63 fs::directory_iterator(port.path())) 64 { 65 // Kernel 6.0: 66 // /sys/.../1e6a0000.usb-vhub:pX/gadget.Y/suspended 67 // Kernel 5.15: 68 // /sys/.../1e6a0000.usb-vhub:pX/gadget/suspended 69 if (fs::is_directory(gadget) && 70 gadget.path().string().find("gadget") != 71 std::string::npos && 72 !fs::exists(gadget.path() / "suspended")) 73 { 74 const std::string portId = port.path().filename(); 75 hidUdcStream << portId << std::endl; 76 found = true; 77 break; 78 } 79 } 80 } 81 if (found) 82 { 83 break; 84 } 85 } 86 } 87 else // If UDC has been specified by '-u' parameter, connect to it. 88 { 89 hidUdcStream << udcName << std::endl; 90 } 91 } 92 catch (fs::filesystem_error& e) 93 { 94 log<level::ERR>("Failed to search USB virtual hub port", 95 entry("ERROR=%s", e.what())); 96 return; 97 } 98 catch (std::ofstream::failure& e) 99 { 100 log<level::ERR>("Failed to connect HID gadget", 101 entry("ERROR=%s", e.what())); 102 return; 103 } 104 105 if (!keyboardPath.empty()) 106 { 107 keyboardFd = 108 open(keyboardPath.c_str(), O_RDWR | O_CLOEXEC | O_NONBLOCK); 109 if (keyboardFd < 0) 110 { 111 log<level::ERR>("Failed to open input device", 112 entry("PATH=%s", keyboardPath.c_str()), 113 entry("ERROR=%s", strerror(errno))); 114 elog<Open>(xyz::openbmc_project::Common::File::Open::ERRNO(errno), 115 xyz::openbmc_project::Common::File::Open::PATH( 116 keyboardPath.c_str())); 117 } 118 } 119 120 if (!pointerPath.empty()) 121 { 122 pointerFd = open(pointerPath.c_str(), O_RDWR | O_CLOEXEC | O_NONBLOCK); 123 if (pointerFd < 0) 124 { 125 log<level::ERR>("Failed to open input device", 126 entry("PATH=%s", pointerPath.c_str()), 127 entry("ERROR=%s", strerror(errno))); 128 elog<Open>(xyz::openbmc_project::Common::File::Open::ERRNO(errno), 129 xyz::openbmc_project::Common::File::Open::PATH( 130 pointerPath.c_str())); 131 } 132 } 133 } 134 135 void Input::disconnect() 136 { 137 if (keyboardFd >= 0) 138 { 139 close(keyboardFd); 140 keyboardFd = -1; 141 } 142 143 if (pointerFd >= 0) 144 { 145 close(pointerFd); 146 pointerFd = -1; 147 } 148 149 try 150 { 151 hidUdcStream << "" << std::endl; 152 } 153 catch (std::ofstream::failure& e) 154 { 155 log<level::ERR>("Failed to disconnect HID gadget", 156 entry("ERROR=%s", e.what())); 157 } 158 } 159 160 void Input::keyEvent(rfbBool down, rfbKeySym key, rfbClientPtr cl) 161 { 162 Server::ClientData* cd = (Server::ClientData*)cl->clientData; 163 Input* input = cd->input; 164 bool sendKeyboard = false; 165 166 if (input->keyboardFd < 0) 167 { 168 return; 169 } 170 171 if (down) 172 { 173 uint8_t sc = keyToScancode(key); 174 175 if (sc) 176 { 177 if (input->keysDown.find(key) == input->keysDown.end()) 178 { 179 for (unsigned int i = 2; i < KEY_REPORT_LENGTH; ++i) 180 { 181 if (!input->keyboardReport[i]) 182 { 183 input->keyboardReport[i] = sc; 184 input->keysDown.insert(std::make_pair(key, i)); 185 sendKeyboard = true; 186 break; 187 } 188 } 189 } 190 } 191 else 192 { 193 uint8_t mod = keyToMod(key); 194 195 if (mod) 196 { 197 input->keyboardReport[0] |= mod; 198 sendKeyboard = true; 199 } 200 } 201 } 202 else 203 { 204 auto it = input->keysDown.find(key); 205 206 if (it != input->keysDown.end()) 207 { 208 input->keyboardReport[it->second] = 0; 209 input->keysDown.erase(it); 210 sendKeyboard = true; 211 } 212 else 213 { 214 uint8_t mod = keyToMod(key); 215 216 if (mod) 217 { 218 input->keyboardReport[0] &= ~mod; 219 sendKeyboard = true; 220 } 221 } 222 } 223 224 if (sendKeyboard) 225 { 226 input->writeKeyboard(input->keyboardReport); 227 } 228 } 229 230 void Input::pointerEvent(int buttonMask, int x, int y, rfbClientPtr cl) 231 { 232 Server::ClientData* cd = (Server::ClientData*)cl->clientData; 233 Input* input = cd->input; 234 Server* server = (Server*)cl->screen->screenData; 235 const Video& video = server->getVideo(); 236 237 if (input->pointerFd < 0) 238 { 239 return; 240 } 241 242 if (buttonMask > 4) 243 { 244 input->pointerReport[0] = 0; 245 if (buttonMask == 8) 246 { 247 input->pointerReport[5] = 1; 248 } 249 else if (buttonMask == 16) 250 { 251 input->pointerReport[5] = 0xff; 252 } 253 } 254 else 255 { 256 input->pointerReport[0] = 257 ((buttonMask & 0x4) >> 1) | ((buttonMask & 0x2) << 1) | 258 (buttonMask & 0x1); 259 input->pointerReport[5] = 0; 260 } 261 262 if (x >= 0 && (unsigned int)x < video.getWidth()) 263 { 264 uint16_t xx = (uint16_t)(x * (SHRT_MAX + 1) / video.getWidth()); 265 266 memcpy(&input->pointerReport[1], &xx, 2); 267 } 268 269 if (y >= 0 && (unsigned int)y < video.getHeight()) 270 { 271 uint16_t yy = (uint16_t)(y * (SHRT_MAX + 1) / video.getHeight()); 272 273 memcpy(&input->pointerReport[3], &yy, 2); 274 } 275 276 rfbDefaultPtrAddEvent(buttonMask, x, y, cl); 277 input->writePointer(input->pointerReport); 278 } 279 280 void Input::sendWakeupPacket() 281 { 282 uint8_t wakeupReport[KEY_REPORT_LENGTH] = {0}; 283 284 if (pointerFd >= 0) 285 { 286 uint16_t xy = SHRT_MAX / 2; 287 288 memcpy(&wakeupReport[1], &xy, 2); 289 memcpy(&wakeupReport[3], &xy, 2); 290 291 writePointer(wakeupReport); 292 } 293 294 if (keyboardFd >= 0) 295 { 296 memset(&wakeupReport[0], 0, KEY_REPORT_LENGTH); 297 298 wakeupReport[0] = keyToMod(XK_Shift_L); 299 300 if (!writeKeyboard(wakeupReport)) 301 { 302 return; 303 } 304 305 wakeupReport[0] = 0; 306 307 writeKeyboard(wakeupReport); 308 } 309 } 310 311 uint8_t Input::keyToMod(rfbKeySym key) 312 { 313 uint8_t mod = 0; 314 315 if (key >= XK_Shift_L && key <= XK_Control_R) 316 { 317 mod = shiftCtrlMap[key - XK_Shift_L]; 318 } 319 else if (key >= XK_Meta_L && key <= XK_Alt_R) 320 { 321 mod = metaAltMap[key - XK_Meta_L]; 322 } 323 324 return mod; 325 } 326 327 uint8_t Input::keyToScancode(rfbKeySym key) 328 { 329 uint8_t scancode = 0; 330 331 if ((key >= 'A' && key <= 'Z') || (key >= 'a' && key <= 'z')) 332 { 333 scancode = USBHID_KEY_A + ((key & 0x5F) - 'A'); 334 } 335 else if (key >= '1' && key <= '9') 336 { 337 scancode = USBHID_KEY_1 + (key - '1'); 338 } 339 else if (key >= XK_F1 && key <= XK_F12) 340 { 341 scancode = USBHID_KEY_F1 + (key - XK_F1); 342 } 343 else if (key >= XK_KP_F1 && key <= XK_KP_F4) 344 { 345 scancode = USBHID_KEY_F1 + (key - XK_KP_F1); 346 } 347 else if (key >= XK_KP_1 && key <= XK_KP_9) 348 { 349 scancode = USBHID_KEY_KP_1 + (key - XK_KP_1); 350 } 351 else 352 { 353 switch (key) 354 { 355 case XK_exclam: 356 scancode = USBHID_KEY_1; 357 break; 358 case XK_at: 359 scancode = USBHID_KEY_2; 360 break; 361 case XK_numbersign: 362 scancode = USBHID_KEY_3; 363 break; 364 case XK_dollar: 365 scancode = USBHID_KEY_4; 366 break; 367 case XK_percent: 368 scancode = USBHID_KEY_5; 369 break; 370 case XK_asciicircum: 371 scancode = USBHID_KEY_6; 372 break; 373 case XK_ampersand: 374 scancode = USBHID_KEY_7; 375 break; 376 case XK_asterisk: 377 scancode = USBHID_KEY_8; 378 break; 379 case XK_parenleft: 380 scancode = USBHID_KEY_9; 381 break; 382 case XK_0: 383 case XK_parenright: 384 scancode = USBHID_KEY_0; 385 break; 386 case XK_Return: 387 scancode = USBHID_KEY_RETURN; 388 break; 389 case XK_Escape: 390 scancode = USBHID_KEY_ESC; 391 break; 392 case XK_BackSpace: 393 scancode = USBHID_KEY_BACKSPACE; 394 break; 395 case XK_Tab: 396 case XK_KP_Tab: 397 scancode = USBHID_KEY_TAB; 398 break; 399 case XK_space: 400 case XK_KP_Space: 401 scancode = USBHID_KEY_SPACE; 402 break; 403 case XK_minus: 404 case XK_underscore: 405 scancode = USBHID_KEY_MINUS; 406 break; 407 case XK_plus: 408 case XK_equal: 409 scancode = USBHID_KEY_EQUAL; 410 break; 411 case XK_bracketleft: 412 case XK_braceleft: 413 scancode = USBHID_KEY_LEFTBRACE; 414 break; 415 case XK_bracketright: 416 case XK_braceright: 417 scancode = USBHID_KEY_RIGHTBRACE; 418 break; 419 case XK_backslash: 420 case XK_bar: 421 scancode = USBHID_KEY_BACKSLASH; 422 break; 423 case XK_colon: 424 case XK_semicolon: 425 scancode = USBHID_KEY_SEMICOLON; 426 break; 427 case XK_quotedbl: 428 case XK_apostrophe: 429 scancode = USBHID_KEY_APOSTROPHE; 430 break; 431 case XK_grave: 432 case XK_asciitilde: 433 scancode = USBHID_KEY_GRAVE; 434 break; 435 case XK_comma: 436 case XK_less: 437 scancode = USBHID_KEY_COMMA; 438 break; 439 case XK_period: 440 case XK_greater: 441 scancode = USBHID_KEY_DOT; 442 break; 443 case XK_slash: 444 case XK_question: 445 scancode = USBHID_KEY_SLASH; 446 break; 447 case XK_Caps_Lock: 448 scancode = USBHID_KEY_CAPSLOCK; 449 break; 450 case XK_Print: 451 scancode = USBHID_KEY_PRINT; 452 break; 453 case XK_Scroll_Lock: 454 scancode = USBHID_KEY_SCROLLLOCK; 455 break; 456 case XK_Pause: 457 scancode = USBHID_KEY_PAUSE; 458 break; 459 case XK_Insert: 460 case XK_KP_Insert: 461 scancode = USBHID_KEY_INSERT; 462 break; 463 case XK_Home: 464 case XK_KP_Home: 465 scancode = USBHID_KEY_HOME; 466 break; 467 case XK_Page_Up: 468 case XK_KP_Page_Up: 469 scancode = USBHID_KEY_PAGEUP; 470 break; 471 case XK_Delete: 472 case XK_KP_Delete: 473 scancode = USBHID_KEY_DELETE; 474 break; 475 case XK_End: 476 case XK_KP_End: 477 scancode = USBHID_KEY_END; 478 break; 479 case XK_Page_Down: 480 case XK_KP_Page_Down: 481 scancode = USBHID_KEY_PAGEDOWN; 482 break; 483 case XK_Right: 484 case XK_KP_Right: 485 scancode = USBHID_KEY_RIGHT; 486 break; 487 case XK_Left: 488 case XK_KP_Left: 489 scancode = USBHID_KEY_LEFT; 490 break; 491 case XK_Down: 492 case XK_KP_Down: 493 scancode = USBHID_KEY_DOWN; 494 break; 495 case XK_Up: 496 case XK_KP_Up: 497 scancode = USBHID_KEY_UP; 498 break; 499 case XK_Num_Lock: 500 scancode = USBHID_KEY_NUMLOCK; 501 break; 502 case XK_KP_Enter: 503 scancode = USBHID_KEY_KP_ENTER; 504 break; 505 case XK_KP_Equal: 506 scancode = USBHID_KEY_KP_EQUAL; 507 break; 508 case XK_KP_Multiply: 509 scancode = USBHID_KEY_KP_MULTIPLY; 510 break; 511 case XK_KP_Add: 512 scancode = USBHID_KEY_KP_ADD; 513 break; 514 case XK_KP_Subtract: 515 scancode = USBHID_KEY_KP_SUBTRACT; 516 break; 517 case XK_KP_Decimal: 518 scancode = USBHID_KEY_KP_DECIMAL; 519 break; 520 case XK_KP_Divide: 521 scancode = USBHID_KEY_KP_DIVIDE; 522 break; 523 case XK_KP_0: 524 scancode = USBHID_KEY_KP_0; 525 break; 526 } 527 } 528 529 return scancode; 530 } 531 532 bool Input::writeKeyboard(const uint8_t* report) 533 { 534 std::unique_lock<std::mutex> lk(keyMutex); 535 uint retryCount = HID_REPORT_RETRY_MAX; 536 537 while (retryCount > 0) 538 { 539 if (write(keyboardFd, report, KEY_REPORT_LENGTH) == KEY_REPORT_LENGTH) 540 { 541 return true; 542 } 543 544 if (errno != EAGAIN) 545 { 546 if (errno != ESHUTDOWN) 547 { 548 log<level::ERR>("Failed to write keyboard report", 549 entry("ERROR=%s", strerror(errno))); 550 } 551 552 break; 553 } 554 555 lk.unlock(); 556 std::this_thread::sleep_for(std::chrono::milliseconds(10)); 557 lk.lock(); 558 retryCount--; 559 } 560 561 return false; 562 } 563 564 void Input::writePointer(const uint8_t* report) 565 { 566 std::unique_lock<std::mutex> lk(ptrMutex); 567 uint retryCount = HID_REPORT_RETRY_MAX; 568 569 while (retryCount > 0) 570 { 571 if (write(pointerFd, report, PTR_REPORT_LENGTH) == PTR_REPORT_LENGTH) 572 { 573 break; 574 } 575 576 if (errno != EAGAIN) 577 { 578 if (errno != ESHUTDOWN) 579 { 580 log<level::ERR>("Failed to write pointer report", 581 entry("ERROR=%s", strerror(errno))); 582 } 583 584 break; 585 } 586 587 lk.unlock(); 588 std::this_thread::sleep_for(std::chrono::milliseconds(10)); 589 lk.lock(); 590 retryCount--; 591 } 592 } 593 594 } // namespace ikvm 595