Initial check-in of LPC Snoop Broadcast Daemon

This is a simple daemon which reads a file interface
from an lpc-snoop driver and broadcasts the values read.

It presently assumes there's /dev/aspeed-lpc-snoop0, however
this could be made a command line parameter.

Change-Id: Ic8e7511de619d93bf1cffd9a096c92315f870946
Signed-off-by: Patrick Venture <venture@google.com>
diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000..309a9d6
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,85 @@
+---
+Language:        Cpp
+# BasedOnStyle:  LLVM
+AccessModifierOffset: -2
+AlignAfterOpenBracket: Align
+AlignConsecutiveAssignments: false
+AlignConsecutiveDeclarations: false
+AlignEscapedNewlinesLeft: false
+AlignOperands:   true
+AlignTrailingComments: true
+AllowAllParametersOfDeclarationOnNextLine: true
+AllowShortBlocksOnASingleLine: false
+AllowShortCaseLabelsOnASingleLine: false
+AllowShortFunctionsOnASingleLine: None
+AllowShortIfStatementsOnASingleLine: false
+AllowShortLoopsOnASingleLine: false
+AlwaysBreakAfterDefinitionReturnType: None
+AlwaysBreakAfterReturnType: None
+AlwaysBreakBeforeMultilineStrings: false
+AlwaysBreakTemplateDeclarations: false
+BinPackArguments: true
+BinPackParameters: true
+BraceWrapping:
+  AfterClass:      true
+  AfterControlStatement: true
+  AfterEnum:       true
+  AfterFunction:   true
+  AfterNamespace:  true
+  AfterObjCDeclaration: true
+  AfterStruct:     true
+  AfterUnion:      true
+  BeforeCatch:     true
+  BeforeElse:      true
+  IndentBraces:    false
+BreakBeforeBinaryOperators: None
+BreakBeforeBraces: Custom
+BreakBeforeTernaryOperators: true
+BreakConstructorInitializers: AfterColon
+ColumnLimit:     80
+CommentPragmas:  '^ IWYU pragma:'
+ConstructorInitializerAllOnOneLineOrOnePerLine: false
+ConstructorInitializerIndentWidth: 4
+ContinuationIndentWidth: 4
+Cpp11BracedListStyle: true
+DerivePointerAlignment: true
+PointerAlignment: Left
+DisableFormat:   false
+ExperimentalAutoDetectBinPacking: false
+FixNamespaceComments: true
+ForEachMacros:   [ foreach, Q_FOREACH, BOOST_FOREACH ]
+IndentCaseLabels: true
+IndentWidth:     4
+IndentWrappedFunctionNames: false
+KeepEmptyLinesAtTheStartOfBlocks: true
+MacroBlockBegin: ''
+MacroBlockEnd:   ''
+MaxEmptyLinesToKeep: 1
+NamespaceIndentation: None
+ObjCBlockIndentWidth: 2
+ObjCSpaceAfterProperty: false
+ObjCSpaceBeforeProtocolList: true
+PenaltyBreakBeforeFirstCallParameter: 19
+PenaltyBreakComment: 300
+PenaltyBreakFirstLessLess: 120
+PenaltyBreakString: 1000
+PenaltyExcessCharacter: 1000000
+PenaltyReturnTypeOnItsOwnLine: 60
+PointerAlignment: Right
+ReflowComments:  true
+SortIncludes:    false
+SpaceAfterCStyleCast: false
+SpaceBeforeAssignmentOperators: true
+SpaceBeforeParens: ControlStatements
+SpaceInEmptyParentheses: false
+SpacesBeforeTrailingComments: 1
+SpacesInAngles:  false
+SpacesInContainerLiterals: true
+SpacesInCStyleCastParentheses: false
+SpacesInParentheses: false
+SpacesInSquareBrackets: false
+Standard:        Cpp11
+TabWidth:        4
+UseTab:          Never
+...
+
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..950ebac
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,16 @@
+AM_DEFAULT_SOURCE_EXT = .cpp
+
+sbin_PROGRAMS = snoopd snooper
+
+snoopd_SOURCES = main.cpp
+snoopd_LDADD = $(SDBUSPLUS_LIBS) $(PHOSPHOR_DBUS_INTERFACES_LIBS) -lpthread
+snoopd_CXXFLAGS = $(SDBUSPLUS_CFLAGS) $(PHOSPHOR_DBUS_INTERFACES_CFLAGS)
+
+snooper_SOURCES = example.cpp
+snooper_LDADD = $(SDBUSPLUS_LIBS) $(PHOSPHOR_DBUS_INTERFACES_LIBS)
+snooper_CXXFLAGS = $(SDBUSPLUS_CFLAGS) $(PHOSPHOR_DBUS_INTERFACES_CFLAGS)
+
+# Installs in $(includedir)/lpcsnoop/
+nobase_include_HEADERS = lpcsnoop/snoop.hpp lpcsnoop/snoop_listen.hpp
+
+SUBDIRS = .
diff --git a/bootstrap.sh b/bootstrap.sh
new file mode 100755
index 0000000..fce7cfa
--- /dev/null
+++ b/bootstrap.sh
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+AUTOCONF_FILES="Makefile.in aclocal.m4 ar-lib autom4te.cache compile \
+        config.guess config.h.in config.sub configure depcomp install-sh \
+        ltmain.sh missing *libtool test-driver"
+
+case $1 in
+    clean)
+        test -f Makefile && make maintainer-clean
+        for file in ${AUTOCONF_FILES}; do
+            find -name "$file" | xargs -r rm -rf
+        done
+        exit 0
+        ;;
+esac
+
+autoreconf -i
+echo 'Run "./configure ${CONFIGURE_FLAGS} && make"'
+
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..c7c49fa
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,32 @@
+# Initialization
+AC_PREREQ([2.69])
+AC_INIT([phosphor-host-postd], [1.0], [https://github.com/openbmc/phosphor-host-postd/issues])
+AC_LANG([C++])
+AC_CONFIG_HEADERS([config.h])
+AM_INIT_AUTOMAKE([subdir-objects -Wall -Werror foreign dist-xz])
+AM_SILENT_RULES([yes])
+
+# Checks for programs.
+AC_PROG_CXX
+AM_PROG_AR
+AC_PROG_INSTALL
+AC_PROG_MAKE_SET
+
+# Checks for typedefs, structures, and compiler characteristics.
+AX_CXX_COMPILE_STDCXX_14([noext])
+AX_APPEND_COMPILE_FLAGS([-Wall -Werror], [CXXFLAGS])
+
+# Checks for libraries.
+# AC_CHECK_LIB([mapper], [mapper_get_service], ,[AC_MSG_ERROR([Could not find libmapper...openbmc/phosphor-objmgr package required])])
+PKG_CHECK_MODULES([SYSTEMD], [libsystemd >= 221], [], [AC_MSG_ERROR(["systemd required and not found"])])
+PKG_CHECK_MODULES([SDBUSPLUS], [sdbusplus], ,[AC_MSG_ERROR([The openbmc/sdbusplus package is required])])
+PKG_CHECK_MODULES([PHOSPHOR_DBUS_INTERFACES], [phosphor-dbus-interfaces], [], [AC_MSG_ERROR(["phosphor-dbus-interfaces required and not found."])])
+AC_CHECK_HEADER(experimental/any, ,[AC_MSG_ERROR([Could not find experimental/any...libstdc++fs developement package required])])
+
+# Checks for library functions.
+LT_INIT # Required for systemd linking
+
+# Create configured output
+AC_CONFIG_FILES([Makefile])
+AC_OUTPUT
+
diff --git a/example.cpp b/example.cpp
new file mode 100644
index 0000000..2fb1e83
--- /dev/null
+++ b/example.cpp
@@ -0,0 +1,100 @@
+/**
+ * Copyright 2017 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cstdio>
+#include <iostream>
+#include <memory>
+
+#include <sdbusplus/bus.hpp>
+#include <sdbusplus/message.hpp>
+#include <sdbusplus/server.hpp>
+
+#include "lpcsnoop/snoop.hpp"
+
+/*
+ * Handle incoming dbus signal we care about.
+ */
+static int DbusHandleSignal(sd_bus_message* msg, void* data, sd_bus_error* err);
+
+/*
+ * Get the match signal for dbus.
+ */
+static std::string GetMatch(void);
+
+// Example object that listens for dbus updates.
+class SnoopListen
+{
+  public:
+    SnoopListen(sdbusplus::bus::bus& bus) :
+        _bus(bus), _signal(bus, GetMatch().c_str(), DbusHandleSignal, this)
+    {
+    }
+
+  private:
+    sdbusplus::bus::bus& _bus;
+    sdbusplus::server::match::match _signal;
+};
+
+/*
+ * This is the entry point for the application.
+ *
+ * This application simply creates an object that registers for incoming value
+ * updates for the POST code dbus object.
+ */
+int main(int argc, char* argv[])
+{
+    auto ListenBus = sdbusplus::bus::new_default();
+    SnoopListen snoop(ListenBus);
+
+    while (true)
+    {
+        ListenBus.process_discard();
+        ListenBus.wait();
+    }
+
+    return 0;
+}
+
+static int DbusHandleSignal(sd_bus_message* msg, void* data, sd_bus_error* err)
+{
+    auto sdbpMsg = sdbusplus::message::message(msg);
+
+    std::string msgSensor, busName{SNOOP_BUSNAME};
+    std::map<std::string, sdbusplus::message::variant<uint64_t>> msgData;
+    sdbpMsg.read(msgSensor, msgData);
+
+    if (msgSensor == busName)
+    {
+        auto valPropMap = msgData.find("Value");
+        if (valPropMap != msgData.end())
+        {
+            uint64_t rawValue = sdbusplus::message::variant_ns::get<uint64_t>(
+                valPropMap->second);
+
+            /* Print output to verify the example program is receiving values.
+             */
+            std::printf("recv: 0x%x\n", static_cast<uint8_t>(rawValue));
+        }
+    }
+
+    return 0;
+}
+
+static std::string GetMatch(void)
+{
+    return "type='signal',interface='org.freedesktop.DBus.Properties',"
+           "member='PropertiesChanged',path='" SNOOP_OBJECTPATH "'";
+}
diff --git a/lpcsnoop/snoop.hpp b/lpcsnoop/snoop.hpp
new file mode 100644
index 0000000..d1dd847
--- /dev/null
+++ b/lpcsnoop/snoop.hpp
@@ -0,0 +1,6 @@
+#pragma once
+
+/* The LPC snoop on port 80h is mapped to this dbus path. */
+#define SNOOP_OBJECTPATH "/xyz/openbmc_project/state/boot/raw"
+/* The LPC snoop on port 80h is mapped to this dbus service. */
+#define SNOOP_BUSNAME "xyz.openbmc_project.State.Boot.Raw"
diff --git a/lpcsnoop/snoop_listen.hpp b/lpcsnoop/snoop_listen.hpp
new file mode 100644
index 0000000..15b8c0f
--- /dev/null
+++ b/lpcsnoop/snoop_listen.hpp
@@ -0,0 +1,57 @@
+/**
+ * Copyright 2017 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sdbusplus/bus.hpp>
+#include <sdbusplus/message.hpp>
+#include <sdbusplus/server.hpp>
+
+#include "lpcsnoop/snoop.hpp"
+
+namespace lpcsnoop
+{
+
+using DbusSignalHandler = int (*)(sd_bus_message*, void*, sd_bus_error*);
+
+/* Returns matching string for what signal to listen on Dbus */
+static const std::string GetMatchRule()
+{
+    return "type='signal',"
+           "interface='org.freedesktop.DBus.Properties',"
+           "member='PropertiesChanged',"
+           "path='" SNOOP_OBJECTPATH "'";
+}
+
+class SnoopListen
+{
+  public:
+    SnoopListen(sdbusplus::bus::bus& busIn, DbusSignalHandler handler) :
+        bus(busIn), signal(busIn, GetMatchRule().c_str(), handler, this)
+    {
+    }
+
+    SnoopListen() = delete; // no default constructor
+    ~SnoopListen() = default;
+    SnoopListen(const SnoopListen&) = delete;
+    SnoopListen& operator=(const SnoopListen&) = delete;
+    SnoopListen(SnoopListen&&) = default;
+    SnoopListen& operator=(SnoopListen&&) = default;
+
+  private:
+    sdbusplus::bus::bus& bus;
+    sdbusplus::server::match::match signal;
+};
+
+} // namespace lpcsnoop
diff --git a/main.cpp b/main.cpp
new file mode 100644
index 0000000..aed5285
--- /dev/null
+++ b/main.cpp
@@ -0,0 +1,175 @@
+/**
+ * Copyright 2017 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fcntl.h>
+#include <poll.h>
+#include <unistd.h>
+
+#include <array>
+#include <cstdint>
+#include <iostream>
+#include <memory>
+#include <thread>
+
+#include <sdbusplus/bus.hpp>
+#include <sdbusplus/server.hpp>
+#include "xyz/openbmc_project/State/Boot/Raw/server.hpp"
+
+#include "lpcsnoop/snoop.hpp"
+
+template <typename... T>
+using ServerObject = typename sdbusplus::server::object::object<T...>;
+using PostInterface = sdbusplus::xyz::openbmc_project::State::Boot::server::Raw;
+using PostObject = ServerObject<PostInterface>;
+
+class PostReporter : public PostObject
+{
+  public:
+    PostReporter(sdbusplus::bus::bus& bus, const char* objPath, bool defer) :
+        PostObject(bus, objPath, defer)
+    {
+    }
+};
+
+/*
+ * 256 bytes is a nice amount.  It's improbable we'd need this many, but its
+ * gives us leg room in the event the driver poll doesn't return in a timely
+ * fashion.  So, mostly arbitrarily chosen.
+ */
+static constexpr size_t BUFFER_SIZE = 256;
+
+/*
+ * Process any incoming dbus inquiries, which include introspection etc.
+ */
+void ProcessDbus(sdbusplus::bus::bus& bus)
+{
+    while (true)
+    {
+        bus.process_discard();
+        bus.wait(); // wait indefinitely
+    }
+
+    return;
+}
+
+/*
+ * TODO(venture): this only listens one of the possible snoop ports, but
+ * doesn't share the namespace.
+ *
+ * This polls() the lpc snoop character device and it owns the dbus object
+ * whose value is the latest port 80h value.
+ */
+int main(int argc, char* argv[])
+{
+    int rc = 0;
+    struct pollfd pollset;
+    int pollr;
+    int readb;
+    int postFd = -1;
+    std::array<uint8_t, BUFFER_SIZE> buffer;
+
+    /*
+     * These string constants are only used in this method within this object
+     * and this object is the only object feeding into the final binary.
+     *
+     * If however, another object is added to this binary it would be proper
+     * to move these declarations to be global and extern to the other object.
+     */
+    const char* snoopObject = SNOOP_OBJECTPATH;
+    const char* snoopDbus = SNOOP_BUSNAME;
+    /*
+     * The following string would be promoted if used in another file under the
+     * same header.
+     */
+    auto snoopFilename = "/dev/aspeed-lpc-snoop0";
+    bool deferSignals = true;
+
+    postFd = open(snoopFilename, 0);
+    if (postFd < 0)
+    {
+        fprintf(stderr, "Unable to open: %s\n", snoopFilename);
+        return -1;
+    }
+
+    pollset.fd = postFd;
+    pollset.events |= POLLIN;
+
+    auto bus = sdbusplus::bus::new_default();
+
+    // Add systemd object manager.
+    sdbusplus::server::manager::manager(bus, snoopObject);
+
+    PostReporter reporter(bus, snoopObject, deferSignals);
+    reporter.emit_object_added();
+
+    bus.request_name(snoopDbus);
+
+    /*
+     * I don't see a public interface for getting the underlying sd_bus*
+     * so instead of poll(bus, driver), I'll just create a separate thread.
+     *
+     * TODO(venture): There may be a way to use sdevent to poll both the file
+     * and the dbus in the same event loop.  If I could get the sdbus pointer
+     * from bus directly, I'd grab a file handler from it, and then just poll on
+     * both in one loop.  From a cursory look at sdevent, I should be able to do
+     * something similar with that at some point.
+     */
+    std::thread lt(ProcessDbus, std::ref(bus));
+
+    /* infinitely listen for POST codes and broadcast. */
+    while (true)
+    {
+        pollr = poll(&pollset, 1, -1); /* polls indefinitely. */
+        if (pollr < 0)
+        {
+            /* poll returned error. */
+            rc = -errno;
+            goto exit;
+        }
+
+        if (pollr > 0)
+        {
+            if (pollset.revents & POLLIN)
+            {
+                readb = read(postFd, buffer.data(), buffer.size());
+                if (readb < 0)
+                {
+                    /* Read failure. */
+                    rc = readb;
+                    goto exit;
+                }
+                else
+                {
+                    /* Broadcast the bytes read. */
+                    for (int i = 0; i < readb; i++)
+                    {
+                        reporter.value(buffer[i]);
+                    }
+                }
+            }
+        }
+    }
+
+exit:
+    if (postFd > -1)
+    {
+        close(postFd);
+    }
+
+    lt.join();
+
+    return rc;
+}