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