Add initial kcsbridge package

kcsbridge implements a KCM ipmi bridge for openbmc in a similar way to
btbridged.

Change-Id: I7f89e4ef3ccaef5b08ecef150a9672f5bd1ea85d
Signed-off-by: Haiyue Wang <haiyue.wang@linux.intel.com>
Signed-off-by: Ed Tanous <ed.tanous@intel.com>
diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000..e479d3f
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,22 @@
+---
+BasedOnStyle: LLVM
+Language: Cpp
+IndentWidth: 8
+UseTab: Always
+BreakBeforeBraces: Linux
+AlwaysBreakBeforeMultilineStrings: true
+AllowShortIfStatementsOnASingleLine: false
+AllowShortLoopsOnASingleLine: false
+AllowShortFunctionsOnASingleLine: false
+IndentCaseLabels: false
+AlignEscapedNewlinesLeft: false
+AlignTrailingComments: true
+AllowAllParametersOfDeclarationOnNextLine: false
+AlignAfterOpenBracket: true
+SpaceAfterCStyleCast: false
+MaxEmptyLinesToKeep: 2
+BreakBeforeBinaryOperators: NonAssignment
+BreakStringLiterals: false
+SortIncludes:    false
+ContinuationIndentWidth: 8
+
diff --git a/LICENSE b/LICENSE
index 261eeb9..9955994 100644
--- a/LICENSE
+++ b/LICENSE
@@ -178,7 +178,7 @@
    APPENDIX: How to apply the Apache License to your work.
 
       To apply the Apache License to your work, attach the following
-      boilerplate notice, with the fields enclosed by brackets "[]"
+      boilerplate notice, with the fields enclosed by brackets "{}"
       replaced with your own identifying information. (Don't include
       the brackets!)  The text should be enclosed in the appropriate
       comment syntax for the file format. We also recommend that a
@@ -186,7 +186,7 @@
       same "printed page" as the copyright notice for easier
       identification within third-party archives.
 
-   Copyright [yyyy] [name of copyright owner]
+   Copyright 2018 Intel Corporation
 
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
diff --git a/MAINTAINERS b/MAINTAINERS
new file mode 100644
index 0000000..19bcf20
--- /dev/null
+++ b/MAINTAINERS
@@ -0,0 +1,46 @@
+How to use this list:
+    Find the most specific section entry (described below) that matches where
+    your change lives and add the reviewers (R) and maintainers (M) as
+    reviewers. You can use the same method to track down who knows a particular
+    code base best.
+
+    Your change/query may span multiple entries; that is okay.
+
+    If you do not find an entry that describes your request at all, someone
+    forgot to update this list; please at least file an issue or send an email
+    to a maintainer, but preferably you should just update this document.
+
+Description of section entries:
+
+    Section entries are structured according to the following scheme:
+
+    X:  NAME <EMAIL_USERNAME@DOMAIN> <IRC_USERNAME!>
+    X:  ...
+    .
+    .
+    .
+
+    Where REPO_NAME is the name of the repository within the OpenBMC GitHub
+    organization; FILE_PATH is a file path within the repository, possibly with
+    wildcards; X is a tag of one of the following types:
+
+    M:  Denotes maintainer; has fields NAME <EMAIL_USERNAME@DOMAIN> <IRC_USERNAME!>;
+        if omitted from an entry, assume one of the maintainers from the
+        MAINTAINERS entry.
+    R:  Denotes reviewer; has fields NAME <EMAIL_USERNAME@DOMAIN> <IRC_USERNAME!>;
+        these people are to be added as reviewers for a change matching the repo
+        path.
+    F:  Denotes forked from an external repository; has fields URL.
+
+    Line comments are to be denoted "# SOME COMMENT" (typical shell style
+    comment); it is important to follow the correct syntax and semantics as we
+    may want to use automated tools with this file in the future.
+
+    A change cannot be added to an OpenBMC repository without a MAINTAINER's
+    approval; thus, a MAINTAINER should always be listed as a reviewer.
+
+START OF MAINTAINERS LIST
+-------------------------
+
+M:  Ed Tanous <ed.tanous@intel.com> <edtanous!>
+M:  Vernon Mauery <veron.mauery@linux.intel.com> <vmauery!>
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..5ae0dcd
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,5 @@
+sbin_PROGRAMS = kcsbridged
+
+kcsbridged_SOURCES = kcsbridged.c
+kcsbridged_LDFLAGS = $(SYSTEMD_LIBS)
+kcsbridged_CFLAGS = $(SYSTEMD_CFLAGS)
diff --git a/README.md b/README.md
index 3617144..eb16f23 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,9 @@
-# kcsbridge
\ No newline at end of file
+# kcsbridge
+## To Build
+To build this package, do the following steps:
+
+    1. ./bootstrap.sh
+    2. ./configure ${CONFIGURE_FLAGS}
+    3. make
+
+To full clean the repository again run `./bootstrap.sh clean`.
diff --git a/bootstrap.sh b/bootstrap.sh
new file mode 100755
index 0000000..7747fb6
--- /dev/null
+++ b/bootstrap.sh
@@ -0,0 +1,20 @@
+#!/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
+        test -f linux/ipmi_bmc.h && rm -rf linux/ipmi_bmc.h
+        test -d linux && find linux -type d -empty | xargs -r rm -rf
+        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..f0eb279
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,55 @@
+# Initialization
+AC_PREREQ([2.69])
+AC_INIT([kcsbridge], [1.0], [https://github.com/openbmc/kcsbridge/issues])
+AC_CONFIG_HEADERS([config.h])
+AM_INIT_AUTOMAKE([subdir-objects -Wall -Werror foreign dist-xz])
+AM_SILENT_RULES([yes])
+
+# Checks for programs.
+AC_PROG_CC
+AM_PROG_AR
+AC_PROG_INSTALL
+AC_PROG_MAKE_SET
+
+# Checks for libraries.
+PKG_CHECK_MODULES([SYSTEMD], [libsystemd >= 221])
+
+# Checks for header files.
+AC_CHECK_HEADER(systemd/sd-bus.h, ,[AC_MSG_ERROR([Could not find systemd/sd-bus.h...systemd developement package required])])
+AC_CHECK_HEADER(linux/ipmi_bmc.h,[HAVE_LINUX_IPMI_BMC_H=""],[HAVE_LINUX_IPMI_BMC_H="-I linux/ipmi_bmc.h"])
+AS_IF([test "$HAVE_LINUX_IPMI_BMC_H" != ""],
+    AC_MSG_WARN([Could not find linux/ipmi-bmc.h: Attempting to download locally for building from https://raw.githubusercontent.com/torvalds/linux/master/include/uapi/linux/ipmi_bmc.h])
+    AC_SUBST([IPMI_BMC_DL],[`mkdir -p linux;wget https://raw.githubusercontent.com/torvalds/linux/master/include/uapi/linux/ipmi_bmc.h -O linux/ipmi_bmc.h`])
+)
+
+# Checks for typedefs, structures, and compiler characteristics.
+AX_APPEND_COMPILE_FLAGS([-fpic -Wall -Werror], [CFLAGS])
+
+# Checks for library functions.
+LT_INIT # Removes 'unrecognized options: --with-libtool-sysroot'
+
+# Check/set gtest specific functions.
+AX_PTHREAD([GTEST_CPPFLAGS="-DGTEST_HAS_PTHREAD=1"],[GTEST_CPPFLAGS="-DGTEST_HAS_PTHREAD=0"])
+AC_SUBST(GTEST_CPPFLAGS)
+
+AC_ARG_ENABLE([oe-sdk],
+    AS_HELP_STRING([--enable-oe-sdk], [Link testcases absolutely against OE SDK so they can be ran within it.])
+)
+AC_ARG_VAR(OECORE_TARGET_SYSROOT,
+    [Path to the OE SDK SYSROOT])
+AS_IF([test "x$enable_oe_sdk" == "xyes"],
+    AS_IF([test "x$OECORE_TARGET_SYSROOT" == "x"],
+          AC_MSG_ERROR([OECORE_TARGET_SYSROOT must be set with --enable-oe-sdk])
+    )
+    AC_MSG_NOTICE([Enabling OE-SDK at $OECORE_TARGET_SYSROOT])
+    [
+        testcase_flags="-Wl,-rpath,\${OECORE_TARGET_SYSROOT}/lib"
+        testcase_flags="${testcase_flags} -Wl,-rpath,\${OECORE_TARGET_SYSROOT}/usr/lib"
+        testcase_flags="${testcase_flags} -Wl,-dynamic-linker,`find \${OECORE_TARGET_SYSROOT}/lib/ld-*.so | sort -r -n | head -n1`"
+    ]
+    AC_SUBST([OESDK_TESTCASE_FLAGS], [$testcase_flags])
+)
+
+# Create configured output
+AC_CONFIG_FILES([Makefile])
+AC_OUTPUT
diff --git a/kcsbridged.c b/kcsbridged.c
new file mode 100644
index 0000000..2234501
--- /dev/null
+++ b/kcsbridged.c
@@ -0,0 +1,538 @@
+/* Copyright 2017 - 2018 Intel
+ *
+ * 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 <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <limits.h>
+#include <linux/ipmi_bmc.h>
+#include <poll.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <sys/mman.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/timerfd.h>
+#include <systemd/sd-bus.h>
+#include <time.h>
+#include <unistd.h>
+
+#define DBUS_ERR "org.openbmc.error"
+#define DBUS_NAME "org.openbmc.HostIpmi"
+#define OBJ_NAME "/org/openbmc/HostIpmi/1"
+
+#define LOG_PREFIX "KCSBRIDGED"
+
+#define KCS_TIMEOUT_IN_SEC 5
+#define KCS_MESSAGE_SIZE 256
+
+#define SD_BUS_FD 0
+#define KCS_FD 1
+#define TIMER_FD 2
+#define TOTAL_FDS 3
+
+struct kcs_msg_req {
+	uint8_t netfn;
+	uint8_t lun;
+	uint8_t cmd;
+	uint8_t *data;
+	size_t data_len;
+};
+
+struct kcsbridged_context {
+	struct pollfd fds[TOTAL_FDS];
+	struct sd_bus *bus;
+
+	/*
+	 * Request and Response Messages are paired together as a Write Transfer
+	 * to the BMC to send the request followed by a Read Transfer from the
+	 * BMC to get the response.
+	 */
+	int expired;
+	uint8_t seqnum;
+	struct kcs_msg_req req;
+};
+
+enum { KCS_LOG_NONE = 0, KCS_LOG_VERBOSE, KCS_LOG_DEBUG };
+
+static void (*kcs_vlog)(int p, const char *fmt, va_list args);
+static int verbosity = KCS_LOG_NONE;
+
+#define MSG_OUT(f_, ...)                                                       \
+	do {                                                                   \
+		if (verbosity != KCS_LOG_NONE)                                 \
+			kcs_log(LOG_INFO, f_, ##__VA_ARGS__);                  \
+	} while (0)
+
+#define MSG_ERR(f_, ...)                                                       \
+	do {                                                                   \
+		if (verbosity != KCS_LOG_NONE)                                 \
+			kcs_log(LOG_ERR, f_, ##__VA_ARGS__);                   \
+	} while (0)
+
+static void kcs_log_console(int p, const char *fmt, va_list args)
+{
+	vfprintf(stderr, fmt, args);
+}
+
+__attribute__((format(printf, 2, 3))) static void kcs_log(int p,
+							  const char *fmt, ...)
+{
+	va_list args;
+
+	va_start(args, fmt);
+	kcs_vlog(p, fmt, args);
+	va_end(args);
+}
+
+static void kcs_dump_data(uint8_t *data, size_t data_len)
+{
+	size_t i;
+	int str_len;
+	char str[64];
+
+	str_len = 0;
+	for (i = 0; i < data_len; i++) {
+		if (i % 8 == 0) {
+			if (i != 0) {
+				kcs_log(LOG_INFO, "%s\n", str);
+				str_len = 0;
+			}
+			str_len += sprintf(&str[str_len], "\t");
+		}
+
+		str_len += sprintf(&str[str_len], "0x%02x ", data[i]);
+	}
+
+	if (str_len != 0)
+		kcs_log(LOG_INFO, "%s\n", str);
+}
+
+static void kcs_set_timer(struct kcsbridged_context *context, int seconds)
+{
+	struct itimerspec ts;
+	int r;
+
+	ts.it_interval.tv_sec = 0;
+	ts.it_interval.tv_nsec = 0;
+	ts.it_value.tv_nsec = 0;
+	ts.it_value.tv_sec = seconds;
+
+	r = timerfd_settime(context->fds[TIMER_FD].fd, 0, &ts, NULL);
+	if (r == -1)
+		MSG_ERR("Couldn't set timerfd: %s\n", strerror(errno));
+}
+
+static int handle_kcs_request(struct kcsbridged_context *context, uint8_t *msg,
+			      size_t msglen)
+{
+	struct kcs_msg_req *req;
+
+	if (msglen < 2) {
+		MSG_ERR("KCS message with a short length (%zd)\n", msglen);
+		return -1;
+	}
+
+	context->expired = 0;
+	context->seqnum++;
+
+	req = &context->req;
+	req->netfn = msg[0] >> 2;
+	req->lun = msg[0] & 0x3;
+	req->cmd = msg[1];
+	req->data = msg + 2;
+	req->data_len = msglen - 2;
+
+	return 0;
+}
+
+static int method_send_message(sd_bus_message *msg, void *userdata,
+			       sd_bus_error *err)
+{
+	struct kcsbridged_context *context = userdata;
+	uint8_t netfn, lun, seqnum, cmd, cc;
+	struct kcs_msg_req *req;
+	uint8_t *data;
+	size_t data_sz;
+	int r;
+	uint8_t rsp[KCS_MESSAGE_SIZE];
+
+	if (!context || context->expired) {
+		sd_bus_error_set_const(err, DBUS_ERR, "Internal error");
+		r = 0;
+		goto out;
+	}
+
+	r = sd_bus_message_read(msg, "yyyyy", &seqnum, &netfn, &lun, &cmd, &cc);
+	if (r < 0) {
+		sd_bus_error_set_const(err, DBUS_ERR, "Bad message");
+		r = -EINVAL;
+		goto out;
+	}
+
+	req = &context->req;
+	if (context->seqnum != seqnum || (req->netfn | 1) != netfn
+	    || req->lun != lun || req->cmd != cmd) {
+		sd_bus_error_set_const(err, DBUS_ERR, "No matching request");
+		r = -EINVAL;
+		goto out;
+	}
+
+	kcs_set_timer(context, 0); /* Stop the timer. */
+
+	r = sd_bus_message_read_array(msg, 'y', (const void **)&data, &data_sz);
+	if (r < 0 || data_sz > sizeof(rsp) - 3) {
+		sd_bus_error_set_const(err, DBUS_ERR, "Bad message data");
+		r = -EINVAL;
+		goto out;
+	}
+
+	rsp[0] = (netfn << 2) | (lun & 0x3);
+	rsp[1] = cmd;
+	rsp[2] = cc;
+	if (data_sz)
+		memcpy(rsp + 3, data, data_sz);
+
+	r = write(context->fds[KCS_FD].fd, rsp, 3 + data_sz);
+	if (r > 0)
+		r = 0;
+
+	MSG_OUT("Send rsp msg <- seq=0x%02x netfn=0x%02x lun=0x%02x cmd=0x%02x cc=0x%02x\n",
+		seqnum, netfn, lun, cmd, cc);
+
+	if (verbosity == KCS_LOG_DEBUG && data_sz != 0)
+		kcs_dump_data(data, data_sz);
+
+out:
+	return sd_bus_reply_method_return(msg, "x", r);
+}
+
+static int method_set_sms_atn(sd_bus_message *msg, void *userdata,
+			      sd_bus_error *err)
+{
+	struct kcsbridged_context *context = userdata;
+	int r;
+
+	MSG_OUT("Sending SET_SMS_ATN\n");
+
+	r = ioctl(context->fds[KCS_FD].fd, IPMI_BMC_IOCTL_SET_SMS_ATN);
+	if (r == -1) {
+		r = errno;
+		MSG_ERR("Couldn't SET_SMS_ATN: %s\n", strerror(r));
+		return sd_bus_reply_method_errno(msg, errno, err);
+	}
+
+	r = 0;
+	return sd_bus_reply_method_return(msg, "x", r);
+}
+
+static int method_clear_sms_atn(sd_bus_message *msg, void *userdata,
+				sd_bus_error *err)
+{
+	struct kcsbridged_context *context = userdata;
+	int r;
+
+	MSG_OUT("Sending CLEAR_SMS_ATN\n");
+
+	r = ioctl(context->fds[KCS_FD].fd, IPMI_BMC_IOCTL_CLEAR_SMS_ATN);
+	if (r == -1) {
+		r = errno;
+		MSG_ERR("Couldn't CLEAR_SMS_ATN: %s\n", strerror(r));
+		return sd_bus_reply_method_errno(msg, errno, err);
+	}
+
+	r = 0;
+	return sd_bus_reply_method_return(msg, "x", r);
+}
+
+static int method_force_abort(sd_bus_message *msg, void *userdata,
+			      sd_bus_error *err)
+{
+	struct kcsbridged_context *context = userdata;
+	int r;
+
+	MSG_OUT("Sending FORCE_ABORT\n");
+
+	r = ioctl(context->fds[KCS_FD].fd, IPMI_BMC_IOCTL_FORCE_ABORT);
+	if (r == -1) {
+		r = errno;
+		MSG_ERR("Couldn't FORCE_ABORT: %s\n", strerror(r));
+		return sd_bus_reply_method_errno(msg, errno, err);
+	}
+
+	r = 0;
+	return sd_bus_reply_method_return(msg, "x", r);
+}
+
+static int dispatch_sd_bus(struct kcsbridged_context *context)
+{
+	int r = 0;
+
+	if (context->fds[SD_BUS_FD].revents) {
+		r = sd_bus_process(context->bus, NULL);
+		if (r > 0)
+			MSG_OUT("Processed %d dbus events\n", r);
+	}
+
+	return r;
+}
+
+static int dispatch_timer(struct kcsbridged_context *context)
+{
+	if (context->fds[TIMER_FD].revents & POLLIN) {
+		struct kcs_msg_req *req;
+		uint8_t rsp[3];
+
+		MSG_OUT("Timeout on msg with seq: 0x%02x\n", context->seqnum);
+
+		context->expired = 1;
+
+		req = &context->req;
+		rsp[0] = ((req->netfn | 1) << 2) | (req->lun & 0x3);
+		rsp[1] = req->cmd;
+		rsp[2] = 0xce; /* Command response could not be provided */
+		if (write(context->fds[KCS_FD].fd, rsp, 3) < 0)
+			MSG_ERR("Failed to send the timeout response!\n");
+	}
+
+	return 0;
+}
+
+static int dispatch_kcs(struct kcsbridged_context *context)
+{
+	struct kcs_msg_req *req = &context->req;
+	sd_bus_message *msg;
+	int r = 0, len;
+	uint8_t data[KCS_MESSAGE_SIZE];
+
+	if (!(context->fds[KCS_FD].revents & POLLIN))
+		goto out;
+
+	len = read(context->fds[KCS_FD].fd, data, sizeof(data));
+	if (len < 0 || handle_kcs_request(context, data, len))
+		goto out;
+
+	r = sd_bus_message_new_signal(context->bus, &msg, OBJ_NAME, DBUS_NAME,
+				      "ReceivedMessage");
+	if (r < 0) {
+		MSG_ERR("Failed to create signal: %s\n", strerror(-r));
+		goto out;
+	}
+
+	r = sd_bus_message_append(msg, "yyyy", context->seqnum, req->netfn,
+				  req->lun, req->cmd);
+	if (r < 0) {
+		MSG_ERR("Couldn't append header to signal: %s\n", strerror(-r));
+		goto bail;
+	}
+
+	r = sd_bus_message_append_array(msg, 'y', req->data, req->data_len);
+	if (r < 0) {
+		MSG_ERR("Couldn't append array to signal: %s\n", strerror(-r));
+		goto bail;
+	}
+
+	r = sd_bus_send(context->bus, msg, NULL);
+	if (r < 0) {
+		MSG_ERR("Couldn't emit dbus signal: %s\n", strerror(-r));
+		goto bail;
+	}
+
+	kcs_set_timer(context, KCS_TIMEOUT_IN_SEC);
+
+	MSG_OUT("Recv req msg -> seq=0x%02x netfn=0x%02x lun=0x%02x cmd=0x%02x\n",
+		context->seqnum, req->netfn, req->lun, req->cmd);
+
+	if (verbosity == KCS_LOG_DEBUG && req->data_len != 0)
+		kcs_dump_data(req->data, req->data_len);
+
+bail:
+	sd_bus_message_unref(msg);
+out:
+	return r;
+}
+
+static void usage(const char *name)
+{
+	fprintf(stderr,
+		"Usage %s [--v[v] | --syslog] --d <DEVICE>\n"
+		"--v                     Be verbose\n"
+		"--vv                    Be verbose and dump entire messages\n"
+		"--s, --syslog           Log output to syslog (pointless without --verbose)\n"
+		"--d, --device <DEVICE>  use <DEVICE> file.\n\n",
+		name);
+}
+
+static const sd_bus_vtable ipmid_vtable[] = {
+	SD_BUS_VTABLE_START(0),
+	SD_BUS_METHOD("sendMessage", "yyyyyay", "x", &method_send_message,
+		      SD_BUS_VTABLE_UNPRIVILEGED),
+	SD_BUS_METHOD("setAttention", "", "x", &method_set_sms_atn,
+		      SD_BUS_VTABLE_UNPRIVILEGED),
+	SD_BUS_METHOD("clearAttention", "", "x", &method_clear_sms_atn,
+		      SD_BUS_VTABLE_UNPRIVILEGED),
+	SD_BUS_METHOD("forceAbort", "", "x", &method_force_abort,
+		      SD_BUS_VTABLE_UNPRIVILEGED),
+	SD_BUS_SIGNAL("ReceivedMessage", "yyyyay", 0),
+	SD_BUS_VTABLE_END};
+
+int main(int argc, char *argv[])
+{
+	struct kcsbridged_context *context;
+	const char *kcs_bmc_device = NULL;
+	const char *name = argv[0];
+	int opt, polled, r;
+	static const struct option long_options[] = {
+		{"device", required_argument, 0, 'd'},
+		{"v", no_argument, &verbosity, KCS_LOG_VERBOSE},
+		{"vv", no_argument, &verbosity, KCS_LOG_DEBUG},
+		{"syslog", no_argument, 0, 's'},
+		{0, 0, 0, 0}};
+
+	context = calloc(1, sizeof(*context));
+	if (!context) {
+		fprintf(stderr, "OOM!\n");
+		return -1;
+	}
+
+	kcs_vlog = &kcs_log_console;
+	while ((opt = getopt_long(argc, argv, "", long_options, NULL)) != -1) {
+		switch (opt) {
+		case 0:
+			break;
+
+		case 'd':
+			kcs_bmc_device = optarg;
+			break;
+
+		case 's':
+			if (kcs_vlog != &vsyslog) {
+				openlog(LOG_PREFIX, LOG_ODELAY, LOG_DAEMON);
+				kcs_vlog = &vsyslog;
+			}
+			break;
+
+		default:
+			usage(name);
+			exit(EXIT_FAILURE);
+		}
+	}
+
+	if (!kcs_bmc_device) {
+		usage(name);
+		exit(EXIT_FAILURE);
+	}
+
+	if (verbosity == KCS_LOG_VERBOSE)
+		MSG_OUT("Verbose logging\n");
+	else if (verbosity == KCS_LOG_DEBUG)
+		MSG_OUT("Debug logging\n");
+
+	MSG_OUT("Starting\n");
+	r = sd_bus_default_system(&context->bus);
+	if (r < 0) {
+		MSG_ERR("Failed to connect to system bus: %s\n", strerror(-r));
+		goto finish;
+	}
+
+	MSG_OUT("Registering dbus methods/signals\n");
+	r = sd_bus_add_object_vtable(context->bus, NULL, OBJ_NAME, DBUS_NAME,
+				     ipmid_vtable, context);
+	if (r < 0) {
+		MSG_ERR("Failed to issue method call: %s\n", strerror(-r));
+		goto finish;
+	}
+
+	MSG_OUT("Requesting dbus name: %s\n", DBUS_NAME);
+	r = sd_bus_request_name(context->bus, DBUS_NAME,
+				SD_BUS_NAME_ALLOW_REPLACEMENT
+					| SD_BUS_NAME_REPLACE_EXISTING);
+	if (r < 0) {
+		MSG_ERR("Failed to acquire service name: %s\n", strerror(-r));
+		goto finish;
+	}
+
+	MSG_OUT("Getting dbus file descriptors\n");
+	context->fds[SD_BUS_FD].fd = sd_bus_get_fd(context->bus);
+	if (context->fds[SD_BUS_FD].fd < 0) {
+		r = -errno;
+		MSG_OUT("Couldn't get the bus file descriptor: %s\n",
+			strerror(errno));
+		goto finish;
+	}
+
+	MSG_OUT("Opening %s\n", kcs_bmc_device);
+	context->fds[KCS_FD].fd = open(kcs_bmc_device, O_RDWR | O_NONBLOCK);
+	if (context->fds[KCS_FD].fd < 0) {
+		r = -errno;
+		MSG_ERR("Couldn't open %s with flags O_RDWR: %s\n",
+			kcs_bmc_device, strerror(errno));
+		goto finish;
+	}
+
+	MSG_OUT("Creating timer fd\n");
+	context->fds[TIMER_FD].fd = timerfd_create(CLOCK_MONOTONIC, 0);
+	if (context->fds[TIMER_FD].fd < 0) {
+		r = -errno;
+		MSG_ERR("Couldn't create timer fd: %s\n", strerror(errno));
+		goto finish;
+	}
+	context->fds[SD_BUS_FD].events = POLLIN;
+	context->fds[KCS_FD].events = POLLIN;
+	context->fds[TIMER_FD].events = POLLIN;
+
+	MSG_OUT("Entering polling loop\n");
+
+	while (1) {
+		polled = poll(context->fds, TOTAL_FDS, 5000);
+		if (polled == 0)
+			continue;
+		if (polled < 0) {
+			r = -errno;
+			MSG_ERR("Error from poll(): %s\n", strerror(errno));
+			goto finish;
+		}
+
+		r = dispatch_sd_bus(context);
+		if (r < 0) {
+			MSG_ERR("Error handling dbus event: %s\n",
+				strerror(-r));
+			goto finish;
+		}
+		r = dispatch_kcs(context);
+		if (r < 0) {
+			MSG_ERR("Error handling KCS event: %s\n", strerror(-r));
+			goto finish;
+		}
+		r = dispatch_timer(context);
+		if (r < 0) {
+			MSG_ERR("Error handling timer event: %s\n",
+				strerror(-r));
+			goto finish;
+		}
+	}
+
+finish:
+	close(context->fds[KCS_FD].fd);
+	close(context->fds[TIMER_FD].fd);
+	sd_bus_unref(context->bus);
+	free(context);
+
+	return r;
+}
diff --git a/kcsbridged.service b/kcsbridged.service
new file mode 100644
index 0000000..cac223f
--- /dev/null
+++ b/kcsbridged.service
@@ -0,0 +1,11 @@
+[Unit]
+Description=KCS Bridge Daemon
+
+[Service]
+Type=dbus
+BusName=org.openbmc.HostIpmi
+ExecStart=/bin/kcsbridged
+Restart=on-abort
+
+[Install]
+WantedBy=multi-user.target
diff --git a/org.openbmc.HostIpmi.conf b/org.openbmc.HostIpmi.conf
new file mode 100644
index 0000000..b1d30ed
--- /dev/null
+++ b/org.openbmc.HostIpmi.conf
@@ -0,0 +1,25 @@
+<?xml version="1.0"?> <!--*-nxml-*-->
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration
+1.0//EN"
+        "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+
+<!--
+	This file is need to run openbmc kcs bridge daemon.
+	Place this file in /etc/dbus-1/system.d/
+-->
+
+<busconfig>
+
+        <policy user="root">
+                <allow own="org.openbmc.HostIpmi"/>
+                <allow send_destination="org.openbmc.HostIpmi"/>
+                <allow receive_sender="org.openbmc.HostIpmi"/>
+        </policy>
+
+        <policy context="default">
+                <allow send_destination="org.openbmc.HostIpmi"/>
+                <allow receive_sender="org.openbmc.HostIpmi"/>
+        </policy>
+
+</busconfig>
+