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