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