Connect HID gadget device dynamically

Connecting HID gadget device statically from the beginning of this
service causes an issue on WHLK test. To prevent the issue, this
commit changes the HID gadget device handling as dynamic so that
the HID gadget device can be connected when this service has at
least one KVM client.

Tested: /dev/hidg0 and /dev/hidg1 created only when at least one
KVM client is connected.

Change-Id: I5f6596b9e4e297fb6b507000499fc041460659f7
Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
diff --git a/ikvm_input.cpp b/ikvm_input.cpp
index df12f27..c4cce50 100644
--- a/ikvm_input.cpp
+++ b/ikvm_input.cpp
@@ -16,6 +16,8 @@
 
 #include "scancodes.hpp"
 
+namespace fs = std::filesystem;
+
 namespace ikvm
 {
 
@@ -27,6 +29,54 @@
     keyboardReport{0}, pointerReport{0}, keyboardPath(kbdPath),
     pointerPath(ptrPath)
 {
+    hidUdcStream.exceptions(std::ofstream::failbit | std::ofstream::badbit);
+    hidUdcStream.open(hidUdcPath, std::ios::out | std::ios::app);
+}
+
+Input::~Input()
+{
+    if (keyboardFd >= 0)
+    {
+        close(keyboardFd);
+    }
+
+    if (pointerFd >= 0)
+    {
+        close(pointerFd);
+    }
+
+    disconnect();
+    hidUdcStream.close();
+}
+
+void Input::connect()
+{
+    try
+    {
+        for (const auto& port : fs::directory_iterator(usbVirtualHubPath))
+        {
+            if (fs::is_directory(port) && !fs::is_symlink(port) &&
+                !fs::exists(port.path() / "gadget/suspended"))
+            {
+                const std::string portId = port.path().filename();
+                hidUdcStream << portId << std::endl;
+                break;
+            }
+        }
+    }
+    catch (fs::filesystem_error& e)
+    {
+        log<level::ERR>("Failed to search USB virtual hub port",
+                        entry("ERROR=%s", e.what()));
+        return;
+    }
+    catch (std::ofstream::failure& e)
+    {
+        log<level::ERR>("Failed to connect HID gadget",
+                        entry("ERROR=%s", e.what()));
+        return;
+    }
+
     if (!keyboardPath.empty())
     {
         keyboardFd = open(keyboardPath.c_str(), O_RDWR | O_CLOEXEC);
@@ -56,16 +106,28 @@
     }
 }
 
-Input::~Input()
+void Input::disconnect()
 {
     if (keyboardFd >= 0)
     {
         close(keyboardFd);
+        keyboardFd = -1;
     }
 
     if (pointerFd >= 0)
     {
         close(pointerFd);
+        pointerFd = -1;
+    }
+
+    try
+    {
+        hidUdcStream << "" << std::endl;
+    }
+    catch (std::ofstream::failure& e)
+    {
+        log<level::ERR>("Failed to disconnect HID gadget",
+                        entry("ERROR=%s", e.what()));
     }
 }