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
Input(const std::string & kbdPath,const std::string & ptrPath,const std::string & udc)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
~Input()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
connect()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
disconnect()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
keyEvent(rfbBool down,rfbKeySym key,rfbClientPtr cl)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 std::unique_lock<std::mutex> keLock(input->keyEventMutex);
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 keLock.unlock();
227
228 if (sendKeyboard)
229 {
230 input->writeKeyboard(input->keyboardReport);
231 }
232 }
233
pointerEvent(int buttonMask,int x,int y,rfbClientPtr cl)234 void Input::pointerEvent(int buttonMask, int x, int y, rfbClientPtr cl)
235 {
236 Server::ClientData* cd = (Server::ClientData*)cl->clientData;
237 Input* input = cd->input;
238 Server* server = (Server*)cl->screen->screenData;
239 const Video& video = server->getVideo();
240
241 if (input->pointerFd < 0)
242 {
243 return;
244 }
245
246 if (buttonMask > 4)
247 {
248 input->pointerReport[0] = 0;
249 if (buttonMask == 8)
250 {
251 input->pointerReport[5] = 1;
252 }
253 else if (buttonMask == 16)
254 {
255 input->pointerReport[5] = 0xff;
256 }
257 }
258 else
259 {
260 input->pointerReport[0] =
261 ((buttonMask & 0x4) >> 1) | ((buttonMask & 0x2) << 1) |
262 (buttonMask & 0x1);
263 input->pointerReport[5] = 0;
264 }
265
266 if (x >= 0 && (unsigned int)x < video.getWidth())
267 {
268 uint16_t xx = (uint16_t)(x * (SHRT_MAX + 1) / video.getWidth());
269
270 memcpy(&input->pointerReport[1], &xx, 2);
271 }
272
273 if (y >= 0 && (unsigned int)y < video.getHeight())
274 {
275 uint16_t yy = (uint16_t)(y * (SHRT_MAX + 1) / video.getHeight());
276
277 memcpy(&input->pointerReport[3], &yy, 2);
278 }
279
280 rfbDefaultPtrAddEvent(buttonMask, x, y, cl);
281 input->writePointer(input->pointerReport);
282 }
283
sendWakeupPacket()284 void Input::sendWakeupPacket()
285 {
286 uint8_t wakeupReport[KEY_REPORT_LENGTH] = {0};
287
288 if (pointerFd >= 0)
289 {
290 uint16_t xy = SHRT_MAX / 2;
291
292 memcpy(&wakeupReport[1], &xy, 2);
293 memcpy(&wakeupReport[3], &xy, 2);
294
295 writePointer(wakeupReport);
296 }
297
298 if (keyboardFd >= 0)
299 {
300 memset(&wakeupReport[0], 0, KEY_REPORT_LENGTH);
301
302 wakeupReport[0] = keyToMod(XK_Shift_L);
303
304 if (!writeKeyboard(wakeupReport))
305 {
306 return;
307 }
308
309 wakeupReport[0] = 0;
310
311 writeKeyboard(wakeupReport);
312 }
313 }
314
keyToMod(rfbKeySym key)315 uint8_t Input::keyToMod(rfbKeySym key)
316 {
317 uint8_t mod = 0;
318
319 if (key >= XK_Shift_L && key <= XK_Control_R)
320 {
321 mod = shiftCtrlMap[key - XK_Shift_L];
322 }
323 else if (key >= XK_Meta_L && key <= XK_Alt_R)
324 {
325 mod = metaAltMap[key - XK_Meta_L];
326 }
327
328 return mod;
329 }
330
keyToScancode(rfbKeySym key)331 uint8_t Input::keyToScancode(rfbKeySym key)
332 {
333 uint8_t scancode = 0;
334
335 if ((key >= 'A' && key <= 'Z') || (key >= 'a' && key <= 'z'))
336 {
337 scancode = USBHID_KEY_A + ((key & 0x5F) - 'A');
338 }
339 else if (key >= '1' && key <= '9')
340 {
341 scancode = USBHID_KEY_1 + (key - '1');
342 }
343 else if (key >= XK_F1 && key <= XK_F12)
344 {
345 scancode = USBHID_KEY_F1 + (key - XK_F1);
346 }
347 else if (key >= XK_KP_F1 && key <= XK_KP_F4)
348 {
349 scancode = USBHID_KEY_F1 + (key - XK_KP_F1);
350 }
351 else if (key >= XK_KP_1 && key <= XK_KP_9)
352 {
353 scancode = USBHID_KEY_KP_1 + (key - XK_KP_1);
354 }
355 else
356 {
357 switch (key)
358 {
359 case XK_exclam:
360 scancode = USBHID_KEY_1;
361 break;
362 case XK_at:
363 scancode = USBHID_KEY_2;
364 break;
365 case XK_numbersign:
366 scancode = USBHID_KEY_3;
367 break;
368 case XK_dollar:
369 scancode = USBHID_KEY_4;
370 break;
371 case XK_percent:
372 scancode = USBHID_KEY_5;
373 break;
374 case XK_asciicircum:
375 scancode = USBHID_KEY_6;
376 break;
377 case XK_ampersand:
378 scancode = USBHID_KEY_7;
379 break;
380 case XK_asterisk:
381 scancode = USBHID_KEY_8;
382 break;
383 case XK_parenleft:
384 scancode = USBHID_KEY_9;
385 break;
386 case XK_0:
387 case XK_parenright:
388 scancode = USBHID_KEY_0;
389 break;
390 case XK_Return:
391 scancode = USBHID_KEY_RETURN;
392 break;
393 case XK_Escape:
394 scancode = USBHID_KEY_ESC;
395 break;
396 case XK_BackSpace:
397 scancode = USBHID_KEY_BACKSPACE;
398 break;
399 case XK_Tab:
400 case XK_KP_Tab:
401 scancode = USBHID_KEY_TAB;
402 break;
403 case XK_space:
404 case XK_KP_Space:
405 scancode = USBHID_KEY_SPACE;
406 break;
407 case XK_minus:
408 case XK_underscore:
409 scancode = USBHID_KEY_MINUS;
410 break;
411 case XK_plus:
412 case XK_equal:
413 scancode = USBHID_KEY_EQUAL;
414 break;
415 case XK_bracketleft:
416 case XK_braceleft:
417 scancode = USBHID_KEY_LEFTBRACE;
418 break;
419 case XK_bracketright:
420 case XK_braceright:
421 scancode = USBHID_KEY_RIGHTBRACE;
422 break;
423 case XK_backslash:
424 case XK_bar:
425 scancode = USBHID_KEY_BACKSLASH;
426 break;
427 case XK_colon:
428 case XK_semicolon:
429 scancode = USBHID_KEY_SEMICOLON;
430 break;
431 case XK_quotedbl:
432 case XK_apostrophe:
433 scancode = USBHID_KEY_APOSTROPHE;
434 break;
435 case XK_grave:
436 case XK_asciitilde:
437 scancode = USBHID_KEY_GRAVE;
438 break;
439 case XK_comma:
440 case XK_less:
441 scancode = USBHID_KEY_COMMA;
442 break;
443 case XK_period:
444 case XK_greater:
445 scancode = USBHID_KEY_DOT;
446 break;
447 case XK_slash:
448 case XK_question:
449 scancode = USBHID_KEY_SLASH;
450 break;
451 case XK_Caps_Lock:
452 scancode = USBHID_KEY_CAPSLOCK;
453 break;
454 case XK_Print:
455 scancode = USBHID_KEY_PRINT;
456 break;
457 case XK_Scroll_Lock:
458 scancode = USBHID_KEY_SCROLLLOCK;
459 break;
460 case XK_Pause:
461 scancode = USBHID_KEY_PAUSE;
462 break;
463 case XK_Insert:
464 case XK_KP_Insert:
465 scancode = USBHID_KEY_INSERT;
466 break;
467 case XK_Home:
468 case XK_KP_Home:
469 scancode = USBHID_KEY_HOME;
470 break;
471 case XK_Page_Up:
472 case XK_KP_Page_Up:
473 scancode = USBHID_KEY_PAGEUP;
474 break;
475 case XK_Delete:
476 case XK_KP_Delete:
477 scancode = USBHID_KEY_DELETE;
478 break;
479 case XK_End:
480 case XK_KP_End:
481 scancode = USBHID_KEY_END;
482 break;
483 case XK_Page_Down:
484 case XK_KP_Page_Down:
485 scancode = USBHID_KEY_PAGEDOWN;
486 break;
487 case XK_Right:
488 case XK_KP_Right:
489 scancode = USBHID_KEY_RIGHT;
490 break;
491 case XK_Left:
492 case XK_KP_Left:
493 scancode = USBHID_KEY_LEFT;
494 break;
495 case XK_Down:
496 case XK_KP_Down:
497 scancode = USBHID_KEY_DOWN;
498 break;
499 case XK_Up:
500 case XK_KP_Up:
501 scancode = USBHID_KEY_UP;
502 break;
503 case XK_Num_Lock:
504 scancode = USBHID_KEY_NUMLOCK;
505 break;
506 case XK_KP_Enter:
507 scancode = USBHID_KEY_KP_ENTER;
508 break;
509 case XK_KP_Equal:
510 scancode = USBHID_KEY_KP_EQUAL;
511 break;
512 case XK_KP_Multiply:
513 scancode = USBHID_KEY_KP_MULTIPLY;
514 break;
515 case XK_KP_Add:
516 scancode = USBHID_KEY_KP_ADD;
517 break;
518 case XK_KP_Subtract:
519 scancode = USBHID_KEY_KP_SUBTRACT;
520 break;
521 case XK_KP_Decimal:
522 scancode = USBHID_KEY_KP_DECIMAL;
523 break;
524 case XK_KP_Divide:
525 scancode = USBHID_KEY_KP_DIVIDE;
526 break;
527 case XK_KP_0:
528 scancode = USBHID_KEY_KP_0;
529 break;
530 }
531 }
532
533 return scancode;
534 }
535
writeKeyboard(const uint8_t * report)536 bool Input::writeKeyboard(const uint8_t* report)
537 {
538 std::unique_lock<std::mutex> lk(keyMutex);
539 uint retryCount = HID_REPORT_RETRY_MAX;
540
541 while (retryCount > 0)
542 {
543 if (write(keyboardFd, report, KEY_REPORT_LENGTH) == KEY_REPORT_LENGTH)
544 {
545 return true;
546 }
547
548 if (errno != EAGAIN)
549 {
550 if (errno != ESHUTDOWN)
551 {
552 log<level::ERR>("Failed to write keyboard report",
553 entry("ERROR=%s", strerror(errno)));
554 }
555
556 break;
557 }
558
559 lk.unlock();
560 std::this_thread::sleep_for(std::chrono::milliseconds(10));
561 lk.lock();
562 retryCount--;
563 }
564
565 return false;
566 }
567
writePointer(const uint8_t * report)568 void Input::writePointer(const uint8_t* report)
569 {
570 std::unique_lock<std::mutex> lk(ptrMutex);
571 uint retryCount = HID_REPORT_RETRY_MAX;
572
573 while (retryCount > 0)
574 {
575 if (write(pointerFd, report, PTR_REPORT_LENGTH) == PTR_REPORT_LENGTH)
576 {
577 break;
578 }
579
580 if (errno != EAGAIN)
581 {
582 if (errno != ESHUTDOWN)
583 {
584 log<level::ERR>("Failed to write pointer report",
585 entry("ERROR=%s", strerror(errno)));
586 }
587
588 break;
589 }
590
591 lk.unlock();
592 std::this_thread::sleep_for(std::chrono::milliseconds(10));
593 lk.lock();
594 retryCount--;
595 }
596 }
597
598 } // namespace ikvm
599