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;
+}