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 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
pointerEvent(int buttonMask,int x,int y,rfbClientPtr cl)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
sendWakeupPacket()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
keyToMod(rfbKeySym key)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
keyToScancode(rfbKeySym key)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
writeKeyboard(const uint8_t * report)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
writePointer(const uint8_t * report)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