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