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