xref: /openbmc/obmc-ikvm/ikvm_input.cpp (revision 20de859f)
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