blob: c4cce50884619f8b389e7404d36eacf44af9df81 [file] [log] [blame]
Eddie James21b177e2018-12-11 13:14:46 -06001#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
Jae Hyun Yoo7dfac9f2019-01-15 10:14:59 -080017#include "scancodes.hpp"
Eddie James21b177e2018-12-11 13:14:46 -060018
Jae Hyun Yooc11257d2020-07-22 23:39:18 -070019namespace fs = std::filesystem;
20
Eddie James21b177e2018-12-11 13:14:46 -060021namespace ikvm
22{
23
24using namespace phosphor::logging;
25using namespace sdbusplus::xyz::openbmc_project::Common::File::Error;
26
Jae Hyun Yoo7dfac9f2019-01-15 10:14:59 -080027Input::Input(const std::string& kbdPath, const std::string& ptrPath) :
Jae Hyun Yoo7a89cd22019-08-21 16:52:30 -070028 sendKeyboard(false), sendPointer(false), keyboardFd(-1), pointerFd(-1),
29 keyboardReport{0}, pointerReport{0}, keyboardPath(kbdPath),
30 pointerPath(ptrPath)
Eddie James21b177e2018-12-11 13:14:46 -060031{
Jae Hyun Yooc11257d2020-07-22 23:39:18 -070032 hidUdcStream.exceptions(std::ofstream::failbit | std::ofstream::badbit);
33 hidUdcStream.open(hidUdcPath, std::ios::out | std::ios::app);
34}
35
36Input::~Input()
37{
38 if (keyboardFd >= 0)
39 {
40 close(keyboardFd);
41 }
42
43 if (pointerFd >= 0)
44 {
45 close(pointerFd);
46 }
47
48 disconnect();
49 hidUdcStream.close();
50}
51
52void Input::connect()
53{
54 try
55 {
56 for (const auto& port : fs::directory_iterator(usbVirtualHubPath))
57 {
58 if (fs::is_directory(port) && !fs::is_symlink(port) &&
59 !fs::exists(port.path() / "gadget/suspended"))
60 {
61 const std::string portId = port.path().filename();
62 hidUdcStream << portId << std::endl;
63 break;
64 }
65 }
66 }
67 catch (fs::filesystem_error& e)
68 {
69 log<level::ERR>("Failed to search USB virtual hub port",
70 entry("ERROR=%s", e.what()));
71 return;
72 }
73 catch (std::ofstream::failure& e)
74 {
75 log<level::ERR>("Failed to connect HID gadget",
76 entry("ERROR=%s", e.what()));
77 return;
78 }
79
Jae Hyun Yoo7dfac9f2019-01-15 10:14:59 -080080 if (!keyboardPath.empty())
Eddie James21b177e2018-12-11 13:14:46 -060081 {
Jae Hyun Yoo7dfac9f2019-01-15 10:14:59 -080082 keyboardFd = open(keyboardPath.c_str(), O_RDWR | O_CLOEXEC);
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 }
Eddie James21b177e2018-12-11 13:14:46 -060092 }
93
Jae Hyun Yoo7dfac9f2019-01-15 10:14:59 -080094 if (!pointerPath.empty())
95 {
Eddie James4749f932019-04-18 11:06:39 -050096 pointerFd = open(pointerPath.c_str(), O_RDWR | O_CLOEXEC | O_NONBLOCK);
Jae Hyun Yoo7dfac9f2019-01-15 10:14:59 -080097 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 }
Eddie James21b177e2018-12-11 13:14:46 -0600107}
108
Jae Hyun Yooc11257d2020-07-22 23:39:18 -0700109void Input::disconnect()
Eddie James21b177e2018-12-11 13:14:46 -0600110{
Jae Hyun Yoo7dfac9f2019-01-15 10:14:59 -0800111 if (keyboardFd >= 0)
112 {
113 close(keyboardFd);
Jae Hyun Yooc11257d2020-07-22 23:39:18 -0700114 keyboardFd = -1;
Jae Hyun Yoo7dfac9f2019-01-15 10:14:59 -0800115 }
116
117 if (pointerFd >= 0)
118 {
119 close(pointerFd);
Jae Hyun Yooc11257d2020-07-22 23:39:18 -0700120 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()));
Jae Hyun Yoo7dfac9f2019-01-15 10:14:59 -0800131 }
Eddie James21b177e2018-12-11 13:14:46 -0600132}
133
134void Input::keyEvent(rfbBool down, rfbKeySym key, rfbClientPtr cl)
135{
136 Server::ClientData* cd = (Server::ClientData*)cl->clientData;
137 Input* input = cd->input;
138
139 if (down)
140 {
Jae Hyun Yoo7dfac9f2019-01-15 10:14:59 -0800141 uint8_t sc = keyToScancode(key);
Eddie James21b177e2018-12-11 13:14:46 -0600142
143 if (sc)
144 {
145 if (input->keysDown.find(key) == input->keysDown.end())
146 {
Jae Hyun Yoo7dfac9f2019-01-15 10:14:59 -0800147 for (unsigned int i = 2; i < KEY_REPORT_LENGTH; ++i)
Eddie James21b177e2018-12-11 13:14:46 -0600148 {
149 if (!input->keyboardReport[i])
150 {
151 input->keyboardReport[i] = sc;
152 input->keysDown.insert(std::make_pair(key, i));
153 input->sendKeyboard = true;
154 break;
155 }
156 }
157 }
158 }
159 else
160 {
Jae Hyun Yoo7dfac9f2019-01-15 10:14:59 -0800161 uint8_t mod = keyToMod(key);
Eddie James21b177e2018-12-11 13:14:46 -0600162
163 if (mod)
164 {
Jae Hyun Yoo7dfac9f2019-01-15 10:14:59 -0800165 input->keyboardReport[0] |= mod;
Eddie James21b177e2018-12-11 13:14:46 -0600166 input->sendKeyboard = true;
167 }
168 }
169 }
170 else
171 {
172 auto it = input->keysDown.find(key);
173
174 if (it != input->keysDown.end())
175 {
176 input->keyboardReport[it->second] = 0;
177 input->keysDown.erase(it);
178 input->sendKeyboard = true;
179 }
180 else
181 {
Jae Hyun Yoo7dfac9f2019-01-15 10:14:59 -0800182 uint8_t mod = keyToMod(key);
Eddie James21b177e2018-12-11 13:14:46 -0600183
184 if (mod)
185 {
Jae Hyun Yoo7dfac9f2019-01-15 10:14:59 -0800186 input->keyboardReport[0] &= ~mod;
Eddie James21b177e2018-12-11 13:14:46 -0600187 input->sendKeyboard = true;
188 }
189 }
190 }
191}
192
193void Input::pointerEvent(int buttonMask, int x, int y, rfbClientPtr cl)
194{
195 Server::ClientData* cd = (Server::ClientData*)cl->clientData;
196 Input* input = cd->input;
197 Server* server = (Server*)cl->screen->screenData;
198 const Video& video = server->getVideo();
199
Jae Hyun Yoo33890982019-03-19 10:20:27 -0700200 input->pointerReport[0] = ((buttonMask & 0x4) >> 1) |
201 ((buttonMask & 0x2) << 1) | (buttonMask & 0x1);
Eddie James21b177e2018-12-11 13:14:46 -0600202
203 if (x >= 0 && (unsigned int)x < video.getWidth())
204 {
Jae Hyun Yoo2bc661d2019-02-25 13:52:47 -0800205 uint16_t xx = (uint16_t)(x * (SHRT_MAX + 1) / video.getWidth());
Eddie James21b177e2018-12-11 13:14:46 -0600206
Jae Hyun Yoo7dfac9f2019-01-15 10:14:59 -0800207 memcpy(&input->pointerReport[1], &xx, 2);
Eddie James21b177e2018-12-11 13:14:46 -0600208 }
209
210 if (y >= 0 && (unsigned int)y < video.getHeight())
211 {
Jae Hyun Yoo2bc661d2019-02-25 13:52:47 -0800212 uint16_t yy = (uint16_t)(y * (SHRT_MAX + 1) / video.getHeight());
Eddie James21b177e2018-12-11 13:14:46 -0600213
Jae Hyun Yoo7dfac9f2019-01-15 10:14:59 -0800214 memcpy(&input->pointerReport[3], &yy, 2);
Eddie James21b177e2018-12-11 13:14:46 -0600215 }
216
217 input->sendPointer = true;
218 rfbDefaultPtrAddEvent(buttonMask, x, y, cl);
219}
220
Jae Hyun Yoo7dfac9f2019-01-15 10:14:59 -0800221void Input::sendWakeupPacket()
Eddie James21b177e2018-12-11 13:14:46 -0600222{
Jae Hyun Yooeaf5c5b2019-02-20 15:03:17 -0800223 uint8_t wakeupReport[KEY_REPORT_LENGTH] = {0};
Jae Hyun Yoo7dfac9f2019-01-15 10:14:59 -0800224
Jae Hyun Yooeaf5c5b2019-02-20 15:03:17 -0800225 if (pointerFd >= 0)
Jae Hyun Yoo7dfac9f2019-01-15 10:14:59 -0800226 {
Jae Hyun Yooeaf5c5b2019-02-20 15:03:17 -0800227 uint16_t xy = SHRT_MAX / 2;
228
229 memcpy(&wakeupReport[1], &xy, 2);
230 memcpy(&wakeupReport[3], &xy, 2);
231
Eddie James7cf1f1d2019-09-30 15:05:16 -0500232 writePointer(wakeupReport);
Jae Hyun Yoo7dfac9f2019-01-15 10:14:59 -0800233 }
234
Jae Hyun Yooeaf5c5b2019-02-20 15:03:17 -0800235 if (keyboardFd >= 0)
Eddie James21b177e2018-12-11 13:14:46 -0600236 {
Jae Hyun Yooeaf5c5b2019-02-20 15:03:17 -0800237 memset(&wakeupReport[0], 0, KEY_REPORT_LENGTH);
238
239 wakeupReport[0] = keyToMod(XK_Shift_L);
240
Eddie James7cf1f1d2019-09-30 15:05:16 -0500241 if (!writeKeyboard(wakeupReport))
Jae Hyun Yooeaf5c5b2019-02-20 15:03:17 -0800242 {
Jae Hyun Yooeaf5c5b2019-02-20 15:03:17 -0800243 return;
244 }
245
246 wakeupReport[0] = 0;
247
Eddie James7cf1f1d2019-09-30 15:05:16 -0500248 writeKeyboard(wakeupReport);
Eddie James21b177e2018-12-11 13:14:46 -0600249 }
250}
251
252void Input::sendReport()
253{
Jae Hyun Yoo7dfac9f2019-01-15 10:14:59 -0800254 if (sendKeyboard && keyboardFd >= 0)
Eddie James21b177e2018-12-11 13:14:46 -0600255 {
Eddie James7cf1f1d2019-09-30 15:05:16 -0500256 writeKeyboard(keyboardReport);
Eddie James21b177e2018-12-11 13:14:46 -0600257
258 sendKeyboard = false;
259 }
260
Jae Hyun Yoo7dfac9f2019-01-15 10:14:59 -0800261 if (sendPointer && pointerFd >= 0)
Eddie James21b177e2018-12-11 13:14:46 -0600262 {
Eddie James7cf1f1d2019-09-30 15:05:16 -0500263 writePointer(pointerReport);
Eddie James21b177e2018-12-11 13:14:46 -0600264
265 sendPointer = false;
266 }
267}
268
Jae Hyun Yoo7dfac9f2019-01-15 10:14:59 -0800269uint8_t Input::keyToMod(rfbKeySym key)
Eddie James21b177e2018-12-11 13:14:46 -0600270{
Jae Hyun Yoo7dfac9f2019-01-15 10:14:59 -0800271 uint8_t mod = 0;
Eddie James21b177e2018-12-11 13:14:46 -0600272
273 if (key >= XK_Shift_L && key <= XK_Control_R)
274 {
275 mod = shiftCtrlMap[key - XK_Shift_L];
276 }
277 else if (key >= XK_Meta_L && key <= XK_Alt_R)
278 {
279 mod = metaAltMap[key - XK_Meta_L];
280 }
281
282 return mod;
283}
284
Jae Hyun Yoo7dfac9f2019-01-15 10:14:59 -0800285uint8_t Input::keyToScancode(rfbKeySym key)
Eddie James21b177e2018-12-11 13:14:46 -0600286{
Jae Hyun Yoo7dfac9f2019-01-15 10:14:59 -0800287 uint8_t scancode = 0;
Eddie James21b177e2018-12-11 13:14:46 -0600288
289 if ((key >= 'A' && key <= 'Z') || (key >= 'a' && key <= 'z'))
290 {
291 scancode = USBHID_KEY_A + ((key & 0x5F) - 'A');
292 }
293 else if (key >= '1' && key <= '9')
294 {
295 scancode = USBHID_KEY_1 + (key - '1');
296 }
297 else if (key >= XK_F1 && key <= XK_F12)
298 {
299 scancode = USBHID_KEY_F1 + (key - XK_F1);
300 }
Jae Hyun Yoo513d95e2019-08-20 11:26:53 -0700301 else if (key >= XK_KP_F1 && key <= XK_KP_F4)
302 {
303 scancode = USBHID_KEY_F1 + (key - XK_KP_F1);
304 }
305 else if (key >= XK_KP_1 && key <= XK_KP_9)
306 {
307 scancode = USBHID_KEY_KP_1 + (key - XK_KP_1);
308 }
Eddie James21b177e2018-12-11 13:14:46 -0600309 else
310 {
311 switch (key)
312 {
313 case XK_exclam:
314 scancode = USBHID_KEY_1;
315 break;
316 case XK_at:
317 scancode = USBHID_KEY_2;
318 break;
319 case XK_numbersign:
320 scancode = USBHID_KEY_3;
321 break;
322 case XK_dollar:
323 scancode = USBHID_KEY_4;
324 break;
325 case XK_percent:
326 scancode = USBHID_KEY_5;
327 break;
328 case XK_asciicircum:
329 scancode = USBHID_KEY_6;
330 break;
331 case XK_ampersand:
332 scancode = USBHID_KEY_7;
333 break;
334 case XK_asterisk:
335 scancode = USBHID_KEY_8;
336 break;
337 case XK_parenleft:
338 scancode = USBHID_KEY_9;
339 break;
340 case XK_0:
341 case XK_parenright:
342 scancode = USBHID_KEY_0;
343 break;
344 case XK_Return:
345 scancode = USBHID_KEY_RETURN;
346 break;
347 case XK_Escape:
348 scancode = USBHID_KEY_ESC;
349 break;
350 case XK_BackSpace:
351 scancode = USBHID_KEY_BACKSPACE;
352 break;
353 case XK_Tab:
Jae Hyun Yoo513d95e2019-08-20 11:26:53 -0700354 case XK_KP_Tab:
Eddie James21b177e2018-12-11 13:14:46 -0600355 scancode = USBHID_KEY_TAB;
356 break;
357 case XK_space:
Jae Hyun Yoo513d95e2019-08-20 11:26:53 -0700358 case XK_KP_Space:
Eddie James21b177e2018-12-11 13:14:46 -0600359 scancode = USBHID_KEY_SPACE;
360 break;
361 case XK_minus:
362 case XK_underscore:
363 scancode = USBHID_KEY_MINUS;
364 break;
365 case XK_plus:
366 case XK_equal:
367 scancode = USBHID_KEY_EQUAL;
368 break;
369 case XK_bracketleft:
370 case XK_braceleft:
371 scancode = USBHID_KEY_LEFTBRACE;
372 break;
373 case XK_bracketright:
374 case XK_braceright:
375 scancode = USBHID_KEY_RIGHTBRACE;
376 break;
377 case XK_backslash:
378 case XK_bar:
379 scancode = USBHID_KEY_BACKSLASH;
380 break;
381 case XK_colon:
382 case XK_semicolon:
383 scancode = USBHID_KEY_SEMICOLON;
384 break;
385 case XK_quotedbl:
386 case XK_apostrophe:
387 scancode = USBHID_KEY_APOSTROPHE;
388 break;
389 case XK_grave:
390 case XK_asciitilde:
391 scancode = USBHID_KEY_GRAVE;
392 break;
393 case XK_comma:
394 case XK_less:
395 scancode = USBHID_KEY_COMMA;
396 break;
397 case XK_period:
398 case XK_greater:
399 scancode = USBHID_KEY_DOT;
400 break;
401 case XK_slash:
402 case XK_question:
403 scancode = USBHID_KEY_SLASH;
404 break;
405 case XK_Caps_Lock:
406 scancode = USBHID_KEY_CAPSLOCK;
407 break;
408 case XK_Print:
409 scancode = USBHID_KEY_PRINT;
410 break;
411 case XK_Scroll_Lock:
412 scancode = USBHID_KEY_SCROLLLOCK;
413 break;
414 case XK_Pause:
415 scancode = USBHID_KEY_PAUSE;
416 break;
417 case XK_Insert:
Jae Hyun Yoo513d95e2019-08-20 11:26:53 -0700418 case XK_KP_Insert:
Eddie James21b177e2018-12-11 13:14:46 -0600419 scancode = USBHID_KEY_INSERT;
420 break;
421 case XK_Home:
Jae Hyun Yoo513d95e2019-08-20 11:26:53 -0700422 case XK_KP_Home:
Eddie James21b177e2018-12-11 13:14:46 -0600423 scancode = USBHID_KEY_HOME;
424 break;
425 case XK_Page_Up:
Jae Hyun Yoo513d95e2019-08-20 11:26:53 -0700426 case XK_KP_Page_Up:
Eddie James21b177e2018-12-11 13:14:46 -0600427 scancode = USBHID_KEY_PAGEUP;
428 break;
429 case XK_Delete:
Jae Hyun Yoo513d95e2019-08-20 11:26:53 -0700430 case XK_KP_Delete:
Eddie James21b177e2018-12-11 13:14:46 -0600431 scancode = USBHID_KEY_DELETE;
432 break;
433 case XK_End:
Jae Hyun Yoo513d95e2019-08-20 11:26:53 -0700434 case XK_KP_End:
Eddie James21b177e2018-12-11 13:14:46 -0600435 scancode = USBHID_KEY_END;
436 break;
437 case XK_Page_Down:
Jae Hyun Yoo513d95e2019-08-20 11:26:53 -0700438 case XK_KP_Page_Down:
Eddie James21b177e2018-12-11 13:14:46 -0600439 scancode = USBHID_KEY_PAGEDOWN;
440 break;
441 case XK_Right:
Jae Hyun Yoo513d95e2019-08-20 11:26:53 -0700442 case XK_KP_Right:
Eddie James21b177e2018-12-11 13:14:46 -0600443 scancode = USBHID_KEY_RIGHT;
444 break;
445 case XK_Left:
Jae Hyun Yoo513d95e2019-08-20 11:26:53 -0700446 case XK_KP_Left:
Eddie James21b177e2018-12-11 13:14:46 -0600447 scancode = USBHID_KEY_LEFT;
448 break;
449 case XK_Down:
Jae Hyun Yoo513d95e2019-08-20 11:26:53 -0700450 case XK_KP_Down:
Eddie James21b177e2018-12-11 13:14:46 -0600451 scancode = USBHID_KEY_DOWN;
452 break;
453 case XK_Up:
Jae Hyun Yoo513d95e2019-08-20 11:26:53 -0700454 case XK_KP_Up:
Eddie James21b177e2018-12-11 13:14:46 -0600455 scancode = USBHID_KEY_UP;
456 break;
457 case XK_Num_Lock:
458 scancode = USBHID_KEY_NUMLOCK;
459 break;
Jae Hyun Yoo513d95e2019-08-20 11:26:53 -0700460 case XK_KP_Enter:
461 scancode = USBHID_KEY_KP_ENTER;
462 break;
463 case XK_KP_Equal:
464 scancode = USBHID_KEY_KP_EQUAL;
465 break;
466 case XK_KP_Multiply:
467 scancode = USBHID_KEY_KP_MULTIPLY;
468 break;
469 case XK_KP_Add:
470 scancode = USBHID_KEY_KP_ADD;
471 break;
472 case XK_KP_Subtract:
473 scancode = USBHID_KEY_KP_SUBTRACT;
474 break;
475 case XK_KP_Decimal:
476 scancode = USBHID_KEY_KP_DECIMAL;
477 break;
478 case XK_KP_Divide:
479 scancode = USBHID_KEY_KP_DIVIDE;
480 break;
481 case XK_KP_0:
482 scancode = USBHID_KEY_KP_0;
483 break;
Eddie James21b177e2018-12-11 13:14:46 -0600484 }
485 }
486
487 return scancode;
488}
489
Eddie James7cf1f1d2019-09-30 15:05:16 -0500490bool Input::writeKeyboard(const uint8_t *report)
491{
492 if (write(keyboardFd, report, KEY_REPORT_LENGTH) != KEY_REPORT_LENGTH)
493 {
Jae Hyun Yoo7a89cd22019-08-21 16:52:30 -0700494 if (errno != ESHUTDOWN && errno != EAGAIN)
Eddie James7cf1f1d2019-09-30 15:05:16 -0500495 {
Jae Hyun Yoo7a89cd22019-08-21 16:52:30 -0700496 log<level::ERR>("Failed to write keyboard report",
497 entry("ERROR=%s", strerror(errno)));
Eddie James7cf1f1d2019-09-30 15:05:16 -0500498 }
499
500 return false;
501 }
502
503 return true;
504}
505
506void Input::writePointer(const uint8_t *report)
507{
508 if (write(pointerFd, report, PTR_REPORT_LENGTH) != PTR_REPORT_LENGTH)
509 {
Jae Hyun Yoo7a89cd22019-08-21 16:52:30 -0700510 if (errno != ESHUTDOWN && errno != EAGAIN)
Eddie James7cf1f1d2019-09-30 15:05:16 -0500511 {
512 log<level::ERR>("Failed to write pointer report",
513 entry("ERROR=%s", strerror(errno)));
Eddie James7cf1f1d2019-09-30 15:05:16 -0500514 }
Eddie James7cf1f1d2019-09-30 15:05:16 -0500515 }
516}
517
Eddie James21b177e2018-12-11 13:14:46 -0600518} // namespace ikvm