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