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