diff options
Diffstat (limited to 'src/plugins/serial')
-rw-r--r-- | src/plugins/serial/Makefile.am | 38 | ||||
-rw-r--r-- | src/plugins/serial/Makefile.in | 539 | ||||
-rw-r--r-- | src/plugins/serial/serial_basic.c | 1029 | ||||
-rw-r--r-- | src/plugins/serial/serial_terminal.c | 919 |
4 files changed, 2525 insertions, 0 deletions
diff --git a/src/plugins/serial/Makefile.am b/src/plugins/serial/Makefile.am new file mode 100644 index 0000000..5bfbd0d --- /dev/null +++ b/src/plugins/serial/Makefile.am @@ -0,0 +1,38 @@ +# * Copyright (c) 2012 Pigeon Point Systems. All Rights Reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistribution of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistribution in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# Neither the name of Pigeon Point Systems, Inc. or the names of +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# This software is provided "AS IS," without a warranty of any kind. +# ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, +# INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A +# PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. +# PIGEON POINT SYSTEMS ("PPS") AND ITS LICENSORS SHALL NOT BE LIABLE +# FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING +# OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL +# PPS OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, +# OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR +# PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF +# LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, +# EVEN IF PPS HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +MAINTAINERCLEANFILES = Makefile.in + +INCLUDES = -I$(top_srcdir)/include + +EXTRA_LTLIBRARIES = libintf_serial.la +noinst_LTLIBRARIES = @INTF_SERIAL_LIB@ +libintf_serial_la_LIBADD = $(top_builddir)/lib/libipmitool.la +libintf_serial_la_SOURCES = serial_terminal.c serial_basic.c diff --git a/src/plugins/serial/Makefile.in b/src/plugins/serial/Makefile.in new file mode 100644 index 0000000..5abad44 --- /dev/null +++ b/src/plugins/serial/Makefile.in @@ -0,0 +1,539 @@ +# Makefile.in generated by automake 1.11.3 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software +# Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# * Copyright (c) 2012 Pigeon Point Systems. All Rights Reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistribution of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistribution in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# Neither the name of Pigeon Point Systems, Inc. or the names of +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# This software is provided "AS IS," without a warranty of any kind. +# ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, +# INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A +# PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. +# PIGEON POINT SYSTEMS ("PPS") AND ITS LICENSORS SHALL NOT BE LIABLE +# FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING +# OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL +# PPS OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, +# OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR +# PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF +# LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, +# EVEN IF PPS HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +subdir = src/plugins/serial +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +LTLIBRARIES = $(noinst_LTLIBRARIES) +libintf_serial_la_DEPENDENCIES = $(top_builddir)/lib/libipmitool.la +am_libintf_serial_la_OBJECTS = serial_terminal.lo serial_basic.lo +libintf_serial_la_OBJECTS = $(am_libintf_serial_la_OBJECTS) +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +SOURCES = $(libintf_serial_la_SOURCES) +DIST_SOURCES = $(libintf_serial_la_SOURCES) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AR = @AR@ +ARCH = @ARCH@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BASEDIR = @BASEDIR@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DISTRO = @DISTRO@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INTF_BMC = @INTF_BMC@ +INTF_BMC_LIB = @INTF_BMC_LIB@ +INTF_DUMMY = @INTF_DUMMY@ +INTF_DUMMY_LIB = @INTF_DUMMY_LIB@ +INTF_FREE = @INTF_FREE@ +INTF_FREE_LIB = @INTF_FREE_LIB@ +INTF_IMB = @INTF_IMB@ +INTF_IMB_LIB = @INTF_IMB_LIB@ +INTF_LAN = @INTF_LAN@ +INTF_LANPLUS = @INTF_LANPLUS@ +INTF_LANPLUS_LIB = @INTF_LANPLUS_LIB@ +INTF_LAN_LIB = @INTF_LAN_LIB@ +INTF_LIPMI = @INTF_LIPMI@ +INTF_LIPMI_LIB = @INTF_LIPMI_LIB@ +INTF_OPEN = @INTF_OPEN@ +INTF_OPEN_LIB = @INTF_OPEN_LIB@ +INTF_SERIAL = @INTF_SERIAL@ +INTF_SERIAL_LIB = @INTF_SERIAL_LIB@ +IPMITOOL_INTF_LIB = @IPMITOOL_INTF_LIB@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OS = @OS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +POW_LIB = @POW_LIB@ +PSTAMP = @PSTAMP@ +RANLIB = @RANLIB@ +RPMBUILD = @RPMBUILD@ +RPM_RELEASE = @RPM_RELEASE@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_configure_args = @ac_configure_args@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +MAINTAINERCLEANFILES = Makefile.in +INCLUDES = -I$(top_srcdir)/include +EXTRA_LTLIBRARIES = libintf_serial.la +noinst_LTLIBRARIES = @INTF_SERIAL_LIB@ +libintf_serial_la_LIBADD = $(top_builddir)/lib/libipmitool.la +libintf_serial_la_SOURCES = serial_terminal.c serial_basic.c +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/plugins/serial/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/plugins/serial/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test "$$dir" != "$$p" || dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done +libintf_serial.la: $(libintf_serial_la_OBJECTS) $(libintf_serial_la_DEPENDENCIES) $(EXTRA_libintf_serial_la_DEPENDENCIES) + $(LINK) $(libintf_serial_la_OBJECTS) $(libintf_serial_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/serial_basic.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/serial_terminal.Plo@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-libtool clean-noinstLTLIBRARIES ctags distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags uninstall uninstall-am + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/plugins/serial/serial_basic.c b/src/plugins/serial/serial_basic.c new file mode 100644 index 0000000..5f4b926 --- /dev/null +++ b/src/plugins/serial/serial_basic.c @@ -0,0 +1,1029 @@ +/* + * Copyright (c) 2012 Pigeon Point Systems. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Pigeon Point Systems nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * PIGEON POINT SYSTEMS ("PPS") AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * PPS OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF PPS HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +/* Serial Interface, Basic Mode plugin. */ + +#include <stdio.h> +#include <fcntl.h> +#include <time.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/poll.h> +#include <termios.h> + +#include <ipmitool/ipmi.h> +#include <ipmitool/ipmi_intf.h> +#include <ipmitool/helper.h> +#include <ipmitool/log.h> + +#if defined(HAVE_CONFIG_H) +# include <config.h> +#endif + +#define SERIAL_BM_MAX_MSG_SIZE 47 +#define SERIAL_BM_MAX_RQ_SIZE 33 /* 40 - 7 */ +#define SERIAL_BM_MAX_RS_SIZE 32 /* 40 - 8 */ +#define SERIAL_BM_TIMEOUT 5 +#define SERIAL_BM_RETRY_COUNT 5 +#define SERIAL_BM_MAX_BUFFER_SIZE 250 + +#define BM_START 0xA0 +#define BM_STOP 0xA5 +#define BM_HANDSHAKE 0xA6 +#define BM_ESCAPE 0xAA + +/* + * IPMB message header + */ +struct ipmb_msg_hdr { + unsigned char rsSA; + unsigned char netFn; /* NET FN | RS LUN */ + unsigned char csum1; + unsigned char rqSA; + unsigned char rqSeq; /* RQ SEQ | RQ LUN */ + unsigned char cmd; + unsigned char data[0]; +}; + +/* + * Send Message command request for IPMB-format + */ +struct ipmi_send_message_rq { + unsigned char channel; + struct ipmb_msg_hdr msg; +}; + +/* + * Get Message command response for IPMB-format + */ +struct ipmi_get_message_rp { + unsigned char completion; + unsigned char channel; + unsigned char netFn; + unsigned char csum1; + unsigned char rsSA; + unsigned char rqSeq; + unsigned char cmd; + unsigned char data[0]; +}; + +/* + * State for the received message + */ +enum { + MSG_NONE, + MSG_IN_PROGRESS, + MSG_DONE +}; + +/* + * Message parsing context + */ +struct serial_bm_parse_ctx{ + int state; + uint8_t * msg; + size_t msg_len; + size_t max_len; + int escape; +}; + +/* + * Receiving context + */ +struct serial_bm_recv_ctx { + char buffer[SERIAL_BM_MAX_BUFFER_SIZE]; + size_t buffer_size; + size_t max_buffer_size; +}; + +/* + * Sending context + */ +struct serial_bm_request_ctx { + uint8_t rsSA; + uint8_t netFn; + uint8_t rqSA; + uint8_t rqSeq; + uint8_t cmd; +}; + +/* + * Table of supported baud rates + */ +static const struct { + int baudinit; + int baudrate; +} rates[] = { + { B2400, 2400 }, + { B9600, 9600 }, + { B19200, 19200 }, + { B38400, 38400 }, + { B57600, 57600 }, + { B115200, 115200 }, + { B230400, 230400 }, +#ifdef B460800 + { B460800, 460800 }, +#endif +}; + +/* + * Table of special characters + */ +static const struct { + uint8_t character; + uint8_t escape; +} characters[] = { + { BM_START, 0xB0 }, /* start */ + { BM_STOP, 0xB5 }, /* stop */ + { BM_HANDSHAKE, 0xB6 }, /* packet handshake */ + { BM_ESCAPE, 0xBA }, /* data escape */ + { 0x1B, 0x3B } /* escape */ +}; + +static int is_system; + +/* + * Setup serial interface + */ +static int +serial_bm_setup(struct ipmi_intf * intf) +{ + intf->session = malloc(sizeof(struct ipmi_session)); + if (intf->session == NULL) { + lprintf(LOG_ERR, "ipmitool: malloc failure"); + return -1; + } + memset(intf->session, 0, sizeof(struct ipmi_session)); + + /* setup default LAN maximum request and response sizes */ + intf->max_request_data_size = SERIAL_BM_MAX_RQ_SIZE; + intf->max_response_data_size = SERIAL_BM_MAX_RS_SIZE; + return 0; +} + +/* + * Open serial interface + */ +static int +serial_bm_open(struct ipmi_intf * intf) +{ + struct termios ti; + unsigned int rate = 9600; + char *p; + int i; + + if (!intf->devfile) { + lprintf(LOG_ERR, "Serial device is not specified"); + return -1; + } + + is_system = 0; + + /* check if baud rate is specified */ + if ((p = strchr(intf->devfile, ':'))) { + char * pp; + + /* separate device name from baud rate */ + *p++ = '\0'; + + /* check for second colon */ + if ((pp = strchr(p, ':'))) { + /* this is needed to normally acquire baud rate */ + *pp++ = '\0'; + + /* check if it is a system interface */ + if (pp[0] == 'S' || pp[0] == 's') { + is_system = 1; + } + } + + if (str2uint(p, &rate)) { + lprintf(LOG_ERR, "Invalid baud rate specified\n"); + return -1; + } + } + + intf->fd = open(intf->devfile, O_RDWR | O_NONBLOCK, 0); + if (intf->fd < 0) { + lperror(LOG_ERR, "Could not open device at %s", intf->devfile); + return -1; + } + + for (i = 0; i < sizeof(rates) / sizeof(rates[0]); i++) { + if (rates[i].baudrate == rate) { + break; + } + } + if (i >= sizeof(rates) / sizeof(rates[0])) { + lprintf(LOG_ERR, "Unsupported baud rate %i specified", rate); + return -1; + } + + tcgetattr(intf->fd, &ti); + + cfsetispeed(&ti, rates[i].baudinit); + cfsetospeed(&ti, rates[i].baudinit); + + /* 8N1 */ + ti.c_cflag &= ~PARENB; + ti.c_cflag &= ~CSTOPB; + ti.c_cflag &= ~CSIZE; + ti.c_cflag |= CS8; + + /* enable the receiver and set local mode */ + ti.c_cflag |= (CLOCAL | CREAD); + + /* no flow control */ + ti.c_cflag &= ~CRTSCTS; + ti.c_iflag &= ~(IGNBRK | IGNCR | INLCR | ICRNL | INPCK | ISTRIP + | IXON | IXOFF | IXANY); +#ifdef IUCLC + /* Only disable uppercase-to-lowercase mapping on input for + platforms supporting the flag. */ + ti.c_iflag &= ~(IUCLC); +#endif + + + ti.c_oflag &= ~(OPOST); + ti.c_lflag &= ~(ICANON | ISIG | ECHO | ECHONL | NOFLSH); + + /* set the new options for the port with flushing */ + tcsetattr(intf->fd, TCSAFLUSH, &ti); + + if (intf->session->timeout == 0) + intf->session->timeout = SERIAL_BM_TIMEOUT; + if (intf->session->retry == 0) + intf->session->retry = SERIAL_BM_RETRY_COUNT; + + intf->opened = 1; + + return 0; +} + +/* + * Close serial interface + */ +static void +serial_bm_close(struct ipmi_intf * intf) +{ + if (intf->opened) { + close(intf->fd); + intf->fd = -1; + } + + if (intf->session) { + free(intf->session); + intf->session = NULL; + } + + intf->opened = 0; +} + +/* + * Allocate sequence number for tracking + */ +static uint8_t +serial_bm_alloc_seq(void) +{ + static uint8_t seq = 0; + if (++seq == 64) { + seq = 0; + } + return seq; +} + +/* + * Flush the buffers + */ +static int +serial_bm_flush(struct ipmi_intf * intf) +{ +#if defined(TCFLSH) + return ioctl(intf->fd, TCFLSH, TCIOFLUSH); +#elif defined(TIOCFLUSH) + return ioctl(intf->fd, TIOCFLUSH); +#else +# error "unsupported platform, missing flush support (TCFLSH/TIOCFLUSH)" +#endif +} + +/* + * Return escaped character for the given one + */ +static inline uint8_t +serial_bm_get_escaped_char(uint8_t c) +{ + int i; + + for (i = 0; i < 5; i++) { + if (characters[i].character == c) { + return characters[i].escape; + } + } + + return c; +} + +/* + * Return unescaped character for the given one + */ +static inline uint8_t +serial_bm_get_unescaped_char(uint8_t c) +{ + int i; + + for (i = 0; i < 5; i++) { + if (characters[i].escape == c) { + return characters[i].character; + } + } + + return c; +} + +/* + * Send message to serial port + */ +static int +serial_bm_send_msg(struct ipmi_intf * intf, uint8_t * msg, int msg_len) +{ + int i, size, tmp = 0; + uint8_t * buf, * data; + + if (verbose > 3) { + fprintf(stderr, "Sending request:\n"); + fprintf(stderr, " rsSA = 0x%x\n", msg[0]); + fprintf(stderr, " NetFN/rsLUN = 0x%x\n", msg[1]); + fprintf(stderr, " rqSA = 0x%x\n", msg[3]); + fprintf(stderr, " rqSeq/rqLUN = 0x%x\n", msg[4]); + fprintf(stderr, " cmd = 0x%x\n", msg[5]); + if (msg_len > 7) { + fprintf(stderr, " data_len = %d\n", msg_len - 7); + fprintf(stderr, " data = %s\n", + buf2str(msg + 6, msg_len - 7)); + } + } + + if (verbose > 4) { + fprintf(stderr, "Message data:\n"); + fprintf(stderr, " %s\n", buf2str(msg, msg_len)); + } + + /* calculate escaped characters number */ + for (i = 0; i < msg_len; i++) { + if (serial_bm_get_escaped_char(msg[i]) != msg[i]) { + tmp++; + } + } + + /* calculate required buffer size */ + size = msg_len + tmp + 2; + + /* allocate buffer for output data */ + buf = data = (uint8_t *) alloca(size); + + if (!buf) { + lperror(LOG_ERR, "ipmitool: alloca error"); + return -1; + } + + /* start character */ + *buf++ = 0xA0; + + for (i = 0; i < msg_len; i++) { + tmp = serial_bm_get_escaped_char(msg[i]); + if (tmp != msg[i]) { + *buf++ = 0xAA; + } + + *buf++ = tmp; + } + + /* stop character */ + *buf++ = 0xA5; + + if (verbose > 5) { + fprintf(stderr, "Sent serial data:\n %s\n", buf2str(data, size)); + } + + /* write data to serial port */ + tmp = write(intf->fd, data, size); + if (tmp <= 0) { + lperror(LOG_ERR, "ipmitool: write error"); + return -1; + } + + return 0; +} + +/* + * This function waits for incoming data + */ +static int +serial_bm_wait_for_data(struct ipmi_intf * intf) +{ + int n; + struct pollfd pfd; + + pfd.fd = intf->fd; + pfd.events = POLLIN; + pfd.revents = 0; + + n = poll(&pfd, 1, intf->session->timeout*1000); + if (n < 0) { + lperror(LOG_ERR, "Poll for serial data failed"); + return -1; + } else if (!n) { + return -1; + } + return 0; +} + +/* + * This function parses incoming data in basic mode format to IPMB message + */ +static int +serial_bm_parse_buffer(const uint8_t * data, int data_len, + struct serial_bm_parse_ctx * ctx) +{ + int i, tmp; + + for (i = 0; i < data_len; i++) { + /* check for start of new message */ + if (data[i] == BM_START) { + ctx->state = MSG_IN_PROGRESS; + ctx->escape = 0; + ctx->msg_len = 0; + /* check if message is not started */ + } else if (ctx->state != MSG_IN_PROGRESS) { + /* skip character */ + continue; + /* continue escape sequence */ + } else if (ctx->escape) { + /* get original character */ + tmp = serial_bm_get_unescaped_char(data[i]); + + /* check if not special character */ + if (tmp == data[i]) { + lprintf(LOG_ERR, "ipmitool: bad response"); + /* reset message state */ + ctx->state = MSG_NONE; + continue; + } + + /* check message length */ + if (ctx->msg_len >= ctx->max_len) { + lprintf(LOG_ERR, "ipmitool: response is too long"); + /* reset message state */ + ctx->state = MSG_NONE; + continue; + } + + /* add parsed character */ + ctx->msg[ctx->msg_len++] = tmp; + + /* clear escape flag */ + ctx->escape = 0; + /* check for escape character */ + } else if (data[i] == BM_ESCAPE) { + ctx->escape = 1; + continue; + /* check for stop character */ + } else if (data[i] == BM_STOP) { + ctx->state = MSG_DONE; + return i + 1; + /* check for packet handshake character */ + } else if (data[i] == BM_HANDSHAKE) { + /* just skip it */ + continue; + } else { + /* check message length */ + if (ctx->msg_len >= ctx->max_len) { + lprintf(LOG_ERR, "ipmitool: response is too long"); + return -1; + } + + /* add parsed character */ + ctx->msg[ctx->msg_len++] = data[i]; + } + } + + /* return number of parsed characters */ + return i; +} + +/* + * Read and parse data from serial port + */ +static int +serial_bm_recv_msg(struct ipmi_intf * intf, + struct serial_bm_recv_ctx * recv_ctx, + uint8_t * msg_data, size_t msg_len) +{ + struct serial_bm_parse_ctx parse_ctx; + int rv; + + parse_ctx.state = MSG_NONE; + parse_ctx.msg = msg_data; + parse_ctx.max_len = msg_len; + + do { + /* wait for data in the port */ + if (serial_bm_wait_for_data(intf)) { + return 0; + } + + /* read data into buffer */ + rv = read(intf->fd, recv_ctx->buffer + recv_ctx->buffer_size, + recv_ctx->max_buffer_size - recv_ctx->buffer_size); + + if (rv < 0) { + lperror(LOG_ERR, "ipmitool: read error"); + return -1; + } + + if (verbose > 5) { + fprintf(stderr, "Received serial data:\n %s\n", + buf2str(recv_ctx->buffer + recv_ctx->buffer_size, rv)); + } + + /* increment buffer size */ + recv_ctx->buffer_size += rv; + + /* parse buffer */ + rv = serial_bm_parse_buffer(recv_ctx->buffer, + recv_ctx->buffer_size, &parse_ctx); + + if (rv < recv_ctx->buffer_size) { + /* move non-parsed part of the buffer to the beginning */ + memmove(recv_ctx->buffer, recv_ctx->buffer + rv, + recv_ctx->buffer_size - rv); + } + + /* decrement buffer size */ + recv_ctx->buffer_size -= rv; + } while (parse_ctx.state != MSG_DONE); + + if (verbose > 4) { + printf("Received message:\n %s\n", + buf2str(msg_data, parse_ctx.msg_len)); + } + + /* received a message */ + return parse_ctx.msg_len; +} + +/* + * Build IPMB message to be transmitted + */ +static int +serial_bm_build_msg(const struct ipmi_intf * intf, + const struct ipmi_rq * req, uint8_t * msg, size_t max_len, + struct serial_bm_request_ctx * ctx, int * msg_len + ) +{ + uint8_t * data = msg, seq; + struct ipmb_msg_hdr * hdr = (struct ipmb_msg_hdr *) msg; + struct ipmi_send_message_rq * inner_rq = NULL, * outer_rq = NULL; + int bridging_level; + + /* acquire bridging level */ + if (intf->target_addr && intf->target_addr != intf->my_addr) { + if (intf->transit_addr != 0) { + bridging_level = 2; + } else { + bridging_level = 1; + } + } else { + bridging_level = 0; + } + + /* check overall packet length */ + if(req->msg.data_len + 7 + bridging_level * 8 > max_len) { + lprintf(LOG_ERR, "ipmitool: Message data is too long"); + return -1; + } + + /* allocate new sequence number */ + seq = serial_bm_alloc_seq() << 2; + + if (bridging_level) { + /* compose send message request */ + hdr->netFn = 0x18; + hdr->cmd = 0x34; + + /* set pointer to send message request data */ + outer_rq = (struct ipmi_send_message_rq *) (hdr + 1); + + /* compose the outer send message request */ + if (bridging_level == 2) { + outer_rq->channel = intf->transit_channel | 0x40; + outer_rq->msg.rsSA = intf->transit_addr; + outer_rq->msg.netFn = 0x18; + outer_rq->msg.csum1 = -(outer_rq->msg.rsSA + outer_rq->msg.netFn); + outer_rq->msg.rqSA = intf->my_addr; + outer_rq->msg.rqSeq = seq; + outer_rq->msg.cmd = 0x34; + + /* inner send message request is further */ + inner_rq = (outer_rq + 1); + } else { + /* there is only outer send message reuest */ + inner_rq = outer_rq; + } + + /* compose the inner send message request */ + inner_rq->channel = intf->target_channel | 0x40; + inner_rq->msg.rsSA = intf->target_addr; + inner_rq->msg.netFn = (req->msg.netfn << 2) | req->msg.lun; + inner_rq->msg.csum1 = -(inner_rq->msg.rsSA + inner_rq->msg.netFn); + inner_rq->msg.rqSA = intf->my_addr; + inner_rq->msg.rqSeq = seq; + inner_rq->msg.cmd = req->msg.cmd; + + /* check if interface is the system one */ + if (is_system) { + /* need response to LUN 2 */ + outer_rq->msg.rqSeq |= 2; + + /* do not track response */ + outer_rq->channel &= ~0x40; + + /* restore BMC SA if bridging not to primary IPMB channel */ + if (outer_rq->channel) { + outer_rq->msg.rqSA = IPMI_BMC_SLAVE_ADDR; + } + } + + /* fill-in the second context */ + ctx[1].rsSA = outer_rq->msg.rsSA; + ctx[1].netFn = outer_rq->msg.netFn; + ctx[1].rqSA = outer_rq->msg.rqSA; + ctx[1].rqSeq = outer_rq->msg.rqSeq; + ctx[1].cmd = outer_rq->msg.cmd; + + /* move write pointer */ + msg = (uint8_t *)(inner_rq + 1); + } else { + /* compose direct request */ + hdr->netFn = (req->msg.netfn << 2) | req->msg.lun; + hdr->cmd = req->msg.cmd; + + /* move write pointer */ + msg = (uint8_t *)(hdr + 1); + } + + /* fill-in the rest header fields */ + hdr->rsSA = IPMI_BMC_SLAVE_ADDR; + hdr->csum1 = -(hdr->rsSA + hdr->netFn); + hdr->rqSA = IPMI_REMOTE_SWID; + hdr->rqSeq = seq; + + /* fill-in the first context */ + ctx[0].rsSA = hdr->rsSA; + ctx[0].netFn = hdr->netFn; + ctx[0].rqSA = hdr->rqSA; + ctx[0].rqSeq = hdr->rqSeq; + ctx[0].cmd = hdr->cmd; + + /* write request data */ + memcpy(msg, req->msg.data, req->msg.data_len); + + /* move write pointer */ + msg += req->msg.data_len; + + if (bridging_level) { + /* write inner message checksum */ + *msg++ = ipmi_csum(&inner_rq->msg.rqSA, req->msg.data_len + 3); + + /* check for double bridging */ + if (bridging_level == 2) { + /* write outer message checksum */ + *msg++ = ipmi_csum(&outer_rq->msg.rqSA, 4); + } + + /* write overall message checksum */ + *msg++ = ipmi_csum(&hdr->rqSA, 4); + } else { + /* write overall message checksum */ + *msg++ = ipmi_csum(&hdr->rqSA, req->msg.data_len + 3); + } + + /* save message length */ + *msg_len = msg - data; + + /* return bridging level */ + return bridging_level; +} + +/* + * Wait for request response + */ +static int +serial_bm_wait_response(struct ipmi_intf * intf, + struct serial_bm_request_ctx * req_ctx, struct serial_bm_recv_ctx * read_ctx, + uint8_t * msg, size_t max_len) +{ + struct ipmb_msg_hdr * hdr = (struct ipmb_msg_hdr *) msg; + int msg_len, netFn, rqSeq; + + /* receive and match message */ + while ((msg_len = serial_bm_recv_msg(intf, read_ctx, msg, max_len)) > 0) { + /* validate message size */ + if (msg_len < 8) { + lprintf(LOG_ERR, "ipmitool: response is too short"); + continue; + } + + /* validate checksum 1 */ + if (ipmi_csum(msg, 3)) { + lprintf(LOG_ERR, "ipmitool: bad checksum 1"); + continue; + } + + /* validate checksum 2 */ + if (ipmi_csum(msg + 3, msg_len - 3)) { + lprintf(LOG_ERR, "ipmitool: bad checksum 2"); + continue; + } + + /* swap requester and responder LUNs */ + netFn = ((req_ctx->netFn|4) & ~3) | (req_ctx->rqSeq & 3); + rqSeq = (req_ctx->rqSeq & ~3) | (req_ctx->netFn & 3); + + /* check for the waited response */ + if (hdr->rsSA == req_ctx->rqSA + && hdr->netFn == netFn + && hdr->rqSA == req_ctx->rsSA + && hdr->rqSeq == rqSeq + && hdr->cmd == req_ctx->cmd) { + /* check if something new has been parsed */ + if (verbose > 3) { + fprintf(stderr, "Got response:\n"); + fprintf(stderr, " rsSA = 0x%x\n", msg[0]); + fprintf(stderr, " NetFN/rsLUN = 0x%x\n", msg[1]); + fprintf(stderr, " rqSA = 0x%x\n", msg[3]); + fprintf(stderr, " rqSeq/rqLUN = 0x%x\n", msg[4]); + fprintf(stderr, " cmd = 0x%x\n", msg[5]); + fprintf(stderr, " completion code = 0x%x\n", msg[6]); + if (msg_len > 8) { + fprintf(stderr, " data_len = %d\n", msg_len - 8); + fprintf(stderr, " data = %s\n", + buf2str(msg + 7, msg_len - 8)); + } + } + + /* copy only completion and response data */ + memmove(msg, hdr + 1, msg_len - sizeof (*hdr) - 1); + + /* update message length */ + msg_len -= sizeof (*hdr) + 1; + + /* the waited one */ + break; + } + } + + return msg_len; +} + +/* + * Get message from receive message queue + */ +static int +serial_bm_get_message(struct ipmi_intf * intf, + struct serial_bm_request_ctx * req_ctx, + struct serial_bm_recv_ctx * read_ctx, + uint8_t * msg, size_t max_len) +{ + uint8_t data[SERIAL_BM_MAX_MSG_SIZE]; + struct serial_bm_request_ctx tmp_ctx; + struct ipmi_get_message_rp * rp = (struct ipmi_get_message_rp *) data; + struct ipmb_msg_hdr * hdr = (struct ipmb_msg_hdr *) data; + clock_t start, tm; + int rv, netFn, rqSeq; + + start = clock(); + + do { + /* fill-in request context */ + tmp_ctx.rsSA = IPMI_BMC_SLAVE_ADDR; + tmp_ctx.netFn = 0x18; + tmp_ctx.rqSA = IPMI_REMOTE_SWID; + tmp_ctx.rqSeq = serial_bm_alloc_seq() << 2; + tmp_ctx.cmd = 0x33; + + /* fill-in request data */ + hdr->rsSA = tmp_ctx.rsSA; + hdr->netFn = tmp_ctx.netFn; + hdr->csum1 = ipmi_csum(data, 2); + hdr->rqSA = tmp_ctx.rqSA; + hdr->rqSeq = tmp_ctx.rqSeq; + hdr->cmd = tmp_ctx.cmd; + hdr->data[0] = ipmi_csum(&hdr->rqSA, 3); + + /* send request */ + serial_bm_flush(intf); + serial_bm_send_msg(intf, data, 7); + + /* wait for response */ + rv = serial_bm_wait_response(intf, &tmp_ctx, read_ctx, + data, sizeof (data)); + + /* check for IO error or timeout */ + if (rv <= 0) { + return rv; + } + + /* check completion code */ + if (rp->completion == 0) { + /* swap requester and responder LUNs */ + netFn = ((req_ctx->netFn|4) & ~3) | (req_ctx->rqSeq & 3); + rqSeq = (req_ctx->rqSeq & ~3) | (req_ctx->netFn & 3); + + /* check for the waited response */ + if (rp->netFn == netFn + && rp->rsSA == req_ctx->rsSA + && rp->rqSeq == rqSeq + && rp->cmd == req_ctx->cmd) { + /* copy the rest of message */ + memcpy(msg, rp->data, rv - sizeof (*rp) - 1); + return rv - sizeof (*rp) - 1; + } + } else if (rp->completion != 0x80) { + return 0; + } + + tm = clock() - start; + + tm /= CLOCKS_PER_SEC; + } while (tm < intf->session->timeout); + + return 0; +} + +static struct ipmi_rs * +serial_bm_send_request(struct ipmi_intf * intf, struct ipmi_rq * req) +{ + static struct ipmi_rs rsp; + uint8_t msg[SERIAL_BM_MAX_MSG_SIZE], * resp = msg; + struct serial_bm_request_ctx req_ctx[3]; + struct serial_bm_recv_ctx read_ctx; + int retry, rv, msg_len, bridging_level; + + if (!intf->opened && intf->open && intf->open(intf) < 0) { + return NULL; + } + + /* reset receive context */ + read_ctx.buffer_size = 0; + read_ctx.max_buffer_size = SERIAL_BM_MAX_BUFFER_SIZE; + + /* Send the message and receive the answer */ + for (retry = 0; retry < intf->session->retry; retry++) { + /* build output message */ + bridging_level = serial_bm_build_msg(intf, req, msg, + sizeof (msg), req_ctx, &msg_len); + if (msg_len < 0) { + return NULL; + } + + /* send request */ + serial_bm_flush(intf); + serial_bm_send_msg(intf, msg, msg_len); + + /* wait for response */ + rv = serial_bm_wait_response(intf, &req_ctx[0], + &read_ctx, msg, sizeof (msg)); + + /* check for IO error */ + if (rv < 0) { + return NULL; + } + + /* check for timeout */ + if (rv == 0) { + continue; + } + + /* check for bridging */ + if (bridging_level && msg[0] == 0) { + /* in the case of payload interface we check receive message queue */ + if (is_system) { + /* check message flags */ + rv = serial_bm_get_message(intf, &req_ctx[1], + &read_ctx, msg, sizeof (msg)); + + /* check for IO error */ + if (rv < 0) { + return NULL; + } + + /* check for timeout */ + if (rv == 0) { + continue; + } + /* check if response for inner request is not encapsulated */ + } else if (rv == 1) { + /* wait for response for inner request */ + rv = serial_bm_wait_response(intf, &req_ctx[0], + &read_ctx, msg, sizeof (msg)); + + /* check for IO error */ + if (rv < 0) { + return NULL; + } + + /* check for timeout */ + if (rv == 0) { + continue; + } + } else { + /* skip outer level header */ + resp = msg + 7; + /* decrement response size */ + rv -= 8; + } + + /* check response size */ + if (resp[0] == 0 && bridging_level == 2 && rv < 8) { + lprintf(LOG_ERR, "ipmitool: Message response is too short"); + /* invalid message length */ + return NULL; + } + } + + /* check for double bridging */ + if (bridging_level == 2 && resp[0] == 0) { + /* get completion code */ + rsp.ccode = resp[7]; + rsp.data_len = rv - 9; + memcpy(rsp.data, resp + 8, rsp.data_len); + } else { + rsp.ccode = resp[0]; + rsp.data_len = rv - 1; + memcpy(rsp.data, resp + 1, rsp.data_len); + } + + /* return response */ + return &rsp; + } + + /* no valid response */ + return NULL; +} + +int +serial_bm_set_my_addr(struct ipmi_intf * intf, uint8_t addr) +{ + intf->my_addr = addr; + return 0; +} + +/* + * Serial BM interface + */ +struct ipmi_intf ipmi_serial_bm_intf = { + name: "serial-basic", + desc: "Serial Interface, Basic Mode", + setup: serial_bm_setup, + open: serial_bm_open, + close: serial_bm_close, + sendrecv: serial_bm_send_request, + set_my_addr:serial_bm_set_my_addr +}; diff --git a/src/plugins/serial/serial_terminal.c b/src/plugins/serial/serial_terminal.c new file mode 100644 index 0000000..41d3753 --- /dev/null +++ b/src/plugins/serial/serial_terminal.c @@ -0,0 +1,919 @@ +/* + * Copyright (c) 2007-2012 Pigeon Point Systems. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Pigeon Point Systems nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * PIGEON POINT SYSTEMS ("PPS") AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * PPS OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF PPS HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +/* Serial Interface, Terminal Mode plugin. */ + +#include <stdio.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/poll.h> +#include <termios.h> + +#include <ipmitool/ipmi.h> +#include <ipmitool/ipmi_intf.h> +#include <ipmitool/helper.h> +#include <ipmitool/log.h> + +#if defined(HAVE_CONFIG_H) +# include <config.h> +#endif + +#define IPMI_SERIAL_TIMEOUT 5 +#define IPMI_SERIAL_RETRY 5 +#define IPMI_SERIAL_MAX_RESPONSE 256 + +/* + * Terminal Mode interface is required to support 40 byte transactions. + */ +#define IPMI_SERIAL_MAX_RQ_SIZE 37 /* 40 - 3 */ +#define IPMI_SERIAL_MAX_RS_SIZE 36 /* 40 - 4 */ + +/* + * IPMB message header + */ +struct ipmb_msg_hdr { + unsigned char rsSA; + unsigned char netFn; /* NET FN | RS LUN */ + unsigned char csum1; + unsigned char rqSA; + unsigned char rqSeq; /* RQ SEQ | RQ LUN */ + unsigned char cmd; + unsigned char data[0]; +}; + +/* + * Send Message command request for IPMB-format + */ +struct ipmi_send_message_rq { + unsigned char channel; + struct ipmb_msg_hdr msg; +}; + +/* + * Get Message command response for IPMB-format + */ +struct ipmi_get_message_rp { + unsigned char completion; + unsigned char channel; + unsigned char netFn; + unsigned char csum1; + unsigned char rsSA; + unsigned char rqSeq; + unsigned char cmd; + unsigned char data[0]; +}; + +/* + * Terminal mode message header + */ +struct serial_term_hdr { + unsigned char netFn; + unsigned char seq; + unsigned char cmd; +}; + +/* + * Sending context + */ +struct serial_term_request_ctx { + uint8_t netFn; + uint8_t sa; + uint8_t seq; + uint8_t cmd; +}; + +/* + * Table of supported baud rates + */ +static const struct { + int baudinit; + int baudrate; +} rates[] = { + { B2400, 2400 }, + { B9600, 9600 }, + { B19200, 19200 }, + { B38400, 38400 }, + { B57600, 57600 }, + { B115200, 115200 }, + { B230400, 230400 }, +#ifdef B460800 + { B460800, 460800 }, +#endif +}; + +static int is_system; + +static int +ipmi_serial_term_open(struct ipmi_intf * intf) +{ + struct termios ti; + unsigned int rate = 9600; + char *p; + int i; + + if (!intf->devfile) { + lprintf(LOG_ERR, "Serial device is not specified"); + return -1; + } + + is_system = 0; + + /* check if baud rate is specified */ + if ((p = strchr(intf->devfile, ':'))) { + char * pp; + + /* separate device name from baud rate */ + *p++ = '\0'; + + /* check for second colon */ + if ((pp = strchr(p, ':'))) { + /* this is needed to normally acquire baud rate */ + *pp++ = '\0'; + + /* check if it is a system interface */ + if (pp[0] == 'S' || pp[0] == 's') { + is_system = 1; + } + } + + if (str2uint(p, &rate)) { + lprintf(LOG_ERR, "Invalid baud rate specified\n"); + return -1; + } + } + + intf->fd = open(intf->devfile, O_RDWR | O_NONBLOCK, 0); + if (intf->fd < 0) { + lperror(LOG_ERR, "Could not open device at %s", intf->devfile); + return -1; + } + + for (i = 0; i < sizeof(rates) / sizeof(rates[0]); i++) { + if (rates[i].baudrate == rate) { + break; + } + } + if (i >= sizeof(rates) / sizeof(rates[0])) { + lprintf(LOG_ERR, "Unsupported baud rate %i specified", rate); + return -1; + } + + tcgetattr(intf->fd, &ti); + + cfsetispeed(&ti, rates[i].baudinit); + cfsetospeed(&ti, rates[i].baudinit); + + /* 8N1 */ + ti.c_cflag &= ~PARENB; + ti.c_cflag &= ~CSTOPB; + ti.c_cflag &= ~CSIZE; + ti.c_cflag |= CS8; + + /* enable the receiver and set local mode */ + ti.c_cflag |= (CLOCAL | CREAD); + + /* no flow control */ + ti.c_cflag &= ~CRTSCTS; + ti.c_iflag &= ~(IGNBRK | IGNCR | INLCR | ICRNL | INPCK | ISTRIP + | IXON | IXOFF | IXANY); +#ifdef IUCLC + /* Only disable uppercase-to-lowercase mapping on input for + platforms supporting the flag. */ + ti.c_iflag &= ~(IUCLC); +#endif + + ti.c_oflag &= ~(OPOST); + ti.c_lflag &= ~(ICANON | ISIG | ECHO | ECHONL | NOFLSH); + + /* set the new options for the port with flushing */ + tcsetattr(intf->fd, TCSAFLUSH, &ti); + + if (intf->session->timeout == 0) + intf->session->timeout = IPMI_SERIAL_TIMEOUT; + if (intf->session->retry == 0) + intf->session->retry = IPMI_SERIAL_RETRY; + + intf->opened = 1; + + return 0; +} + +static void +ipmi_serial_term_close(struct ipmi_intf * intf) +{ + if (intf->opened) { + close(intf->fd); + intf->fd = -1; + } + + if (intf->session) { + free(intf->session); + intf->session = NULL; + } + + intf->opened = 0; +} + +/* + * This function waits for incoming byte during timeout (ms). + */ +static int +serial_wait_for_data(struct ipmi_intf * intf) +{ + int n; + struct pollfd pfd; + + pfd.fd = intf->fd; + pfd.events = POLLIN; + pfd.revents = 0; + + n = poll(&pfd, 1, intf->session->timeout*1000); + if (n < 0) { + lperror(LOG_ERR, "Poll for serial data failed"); + return -1; + } else if (!n) { + return -1; + } + return 0; +} + +/* + * Read a line from serial port + * Returns > 0 if there is a line, < 0 on error or timeout + */ +static int +serial_read_line(struct ipmi_intf * intf, char *str, int len) +{ + int rv, i; + + *str = 0; + i = 0; + while (i < len) { + if (serial_wait_for_data(intf)) { + return -1; + } + rv = read(intf->fd, str + i, 1); + if (rv < 0) { + return -1; + } else if (!rv) { + lperror(LOG_ERR, "Serial read failed: %s", strerror(errno)); + return -1; + } + if (str[i] == '\n' || str[i] == '\r') { + if (verbose > 4) { + char c = str[i]; + str[i] = '\0'; + fprintf(stderr, "Received data: %s\n", str); + str[i] = c; + } + return i + 1; + } else { + i++; + } + } + + lprintf(LOG_ERR, "Serial data is too long"); + return -1; +} + +/* + * Send zero-terminated string to serial port + * Returns the string length or negative error code + */ +static int +serial_write_line(struct ipmi_intf * intf, const char *str) +{ + int rv, cnt = 0; + int cb = strlen(str); + + while (cnt < cb) { + rv = write(intf->fd, str + cnt, cb - cnt); + if (rv < 0) { + return -1; + } else if (rv == 0) { + return -1; + } + cnt += rv; + } + + return cnt; +} + +/* + * Flush the buffers + */ +static int +serial_flush(struct ipmi_intf * intf) +{ +#if defined(TCFLSH) + return ioctl(intf->fd, TCFLSH, TCIOFLUSH); +#elif defined(TIOCFLUSH) + return ioctl(intf->fd, TIOCFLUSH); +#else +# error "unsupported platform, missing flush support (TCFLSH/TIOCFLUSH)" +#endif + +} + +/* + * Receive IPMI response from the device + * Len: buffer size + * Returns: -1 or response lenth on success + */ +static int +recv_response(struct ipmi_intf * intf, unsigned char *data, int len) +{ + char hex_rs[IPMI_SERIAL_MAX_RESPONSE * 3]; + int i, j, resp_len = 0; + unsigned long rv; + char *p, *pp; + char ch, str_hex[3]; + + p = hex_rs; + while (1) { + if ((rv = serial_read_line(intf, p, sizeof(hex_rs) - resp_len)) < 0) { + /* error */ + return -1; + } + p += rv; + resp_len += rv; + if (*(p - 2) == ']' && (*(p - 1) == '\n' || *(p - 1) == '\r')) { + *p = 0; + break; + } + } + + p = strrchr(hex_rs, '['); + if (!p) { + lprintf(LOG_ERR, "Serial response is invalid"); + return -1; + } + + p++; + pp = strchr(p, ']'); + if (!pp) { + lprintf(LOG_ERR, "Serial response is invalid"); + return -1; + } + *pp = 0; + + /* was it an error? */ + if (strncmp(p, "ERR ", 4) == 0) { + serial_write_line(intf, "\r\r\r\r"); + sleep(1); + serial_flush(intf); + errno = 0; + rv = strtoul(p + 4, &p, 16); + if ((rv && rv < 0x100 && *p == '\0') + || (rv == 0 && !errno)) { + /* The message didn't get it through. The upper + level will have to re-send */ + return 0; + } else { + lprintf(LOG_ERR, "Serial response is invalid"); + return -1; + } + } + + /* this is needed for correct string to long conversion */ + str_hex[2] = 0; + + /* parse the response */ + i = 0; + j = 0; + while (*p) { + if (i >= len) { + lprintf(LOG_ERR, "Serial response is too long(%d, %d)", i, len); + return -1; + } + ch = *(p++); + if (isxdigit(ch)) { + str_hex[j++] = ch; + } else { + if (j == 1 || !isspace(ch)) { + lprintf(LOG_ERR, "Serial response is invalid"); + return -1; + } + } + if (j == 2) { + unsigned long tmp; + errno = 0; + /* parse the hex number */ + tmp = strtoul(str_hex, NULL, 16); + if ( tmp > 0xFF || ( !tmp && errno ) ) { + lprintf(LOG_ERR, "Serial response is invalid"); + return -1; + } + data[i++] = tmp; + j = 0; + } + } + + return i; +} + +/* + * Allocate sequence number for tracking + */ +static uint8_t +serial_term_alloc_seq(void) +{ + static uint8_t seq = 0; + if (++seq == 64) { + seq = 0; + } + return seq; +} + +/* + * Build IPMB message to be transmitted + */ +static int +serial_term_build_msg(const struct ipmi_intf * intf, + const struct ipmi_rq * req, uint8_t * msg, size_t max_len, + struct serial_term_request_ctx * ctx, int * msg_len) +{ + uint8_t * data = msg, seq; + struct serial_term_hdr * term_hdr = (struct serial_term_hdr *) msg; + struct ipmi_send_message_rq * outer_rq = NULL; + struct ipmi_send_message_rq * inner_rq = NULL; + int bridging_level; + + /* acquire bridging level */ + if (intf->target_addr && intf->target_addr != intf->my_addr) { + if (intf->transit_addr != 0) { + bridging_level = 2; + } else { + bridging_level = 1; + } + } else { + bridging_level = 0; + } + + /* check overall packet length */ + if(req->msg.data_len + 3 + bridging_level * 8 > max_len) { + lprintf(LOG_ERR, "ipmitool: Message data is too long"); + return -1; + } + + /* allocate new sequence number */ + seq = serial_term_alloc_seq() << 2; + + /* check for bridging */ + if (bridging_level) { + /* compose terminal message header */ + term_hdr->netFn = 0x18; + term_hdr->seq = seq; + term_hdr->cmd = 0x34; + + /* set pointer to send message request data */ + outer_rq = (struct ipmi_send_message_rq *) (term_hdr + 1); + + if (bridging_level == 2) { + /* compose the outer send message request */ + outer_rq->channel = intf->transit_channel | 0x40; + outer_rq->msg.rsSA = intf->transit_addr; + outer_rq->msg.netFn = 0x18; + outer_rq->msg.csum1 = -(outer_rq->msg.rsSA + outer_rq->msg.netFn); + outer_rq->msg.rqSA = intf->my_addr; + outer_rq->msg.rqSeq = seq; + outer_rq->msg.cmd = 0x34; + + /* inner request is further */ + inner_rq = (outer_rq + 1); + } else { + /* there is only one header */ + inner_rq = outer_rq; + } + + /* compose the inner send message request */ + inner_rq->channel = intf->target_channel | 0x40; + inner_rq->msg.rsSA = intf->target_addr; + inner_rq->msg.netFn = (req->msg.netfn << 2) | req->msg.lun; + inner_rq->msg.csum1 = -(inner_rq->msg.rsSA + inner_rq->msg.netFn); + inner_rq->msg.rqSA = intf->my_addr; + inner_rq->msg.rqSeq = seq; + inner_rq->msg.cmd = req->msg.cmd; + + /* check if interface is the system one */ + if (is_system) { + /* need response to LUN 2 */ + outer_rq->msg.rqSeq |= 2; + + /* do not track response */ + outer_rq->channel &= ~0x40; + + /* restore BMC SA if bridging not to primary IPMB channel */ + if (outer_rq->channel) { + outer_rq->msg.rqSA = IPMI_BMC_SLAVE_ADDR; + } + } + + /* fill the second context */ + ctx[1].netFn = outer_rq->msg.netFn; + ctx[1].sa = outer_rq->msg.rsSA; + ctx[1].seq = outer_rq->msg.rqSeq; + ctx[1].cmd = outer_rq->msg.cmd; + + /* move write pointer */ + msg = (uint8_t *)(inner_rq + 1); + } else { + /* compose terminal message header */ + term_hdr->netFn = (req->msg.netfn << 2) | req->msg.lun; + term_hdr->seq = seq; + term_hdr->cmd = req->msg.cmd; + + /* move write pointer */ + msg = (uint8_t *)(term_hdr + 1); + } + + /* fill the first context */ + ctx[0].netFn = term_hdr->netFn; + ctx[0].seq = term_hdr->seq; + ctx[0].cmd = term_hdr->cmd; + + /* write request data */ + memcpy(msg, req->msg.data, req->msg.data_len); + + /* move write pointer */ + msg += req->msg.data_len; + + if (bridging_level) { + /* write inner message checksum */ + *msg++ = ipmi_csum(&inner_rq->msg.rqSA, req->msg.data_len + 3); + + /* check for double bridging */ + if (bridging_level == 2) { + /* write outer message checksum */ + *msg++ = ipmi_csum(&outer_rq->msg.rqSA, 4); + } + } + + + /* save message length */ + *msg_len = msg - data; + + /* return bridging level */ + return bridging_level; +} + +/* + * Send message to serial port + */ +static int +serial_term_send_msg(struct ipmi_intf * intf, uint8_t * msg, int msg_len) +{ + int i, size, tmp = 0; + uint8_t * buf, * data; + + if (verbose > 3) { + fprintf(stderr, "Sending request:\n"); + fprintf(stderr, " NetFN/rsLUN = 0x%x\n", msg[0]); + fprintf(stderr, " rqSeq = 0x%x\n", msg[1]); + fprintf(stderr, " cmd = 0x%x\n", msg[2]); + if (msg_len > 7) { + fprintf(stderr, " data_len = %d\n", msg_len - 3); + fprintf(stderr, " data = %s\n", + buf2str(msg + 3, msg_len - 3)); + } + } + + if (verbose > 4) { + fprintf(stderr, "Message data:\n"); + fprintf(stderr, " %s\n", buf2str(msg, msg_len)); + } + + /* calculate required buffer size */ + size = msg_len * 2 + 4; + + /* allocate buffer for output data */ + buf = data = (uint8_t *) alloca(size); + + if (!buf) { + lperror(LOG_ERR, "ipmitool: alloca error"); + return -1; + } + + /* start character */ + *buf++ = '['; + + /* body */ + for (i = 0; i < msg_len; i++) { + buf += sprintf( buf, "%02x", msg[i]); + } + + /* stop character */ + *buf++ = ']'; + + /* carriage return */ + *buf++ = '\r'; + + /* line feed */ + *buf++ = '\n'; + + /* write data to serial port */ + tmp = write(intf->fd, data, size); + if (tmp <= 0) { + lperror(LOG_ERR, "ipmitool: write error"); + return -1; + } + + return 0; +} + +/* + * Wait for request response + */ +static int +serial_term_wait_response(struct ipmi_intf * intf, + struct serial_term_request_ctx * req_ctx, + uint8_t * msg, size_t max_len) +{ + struct serial_term_hdr * hdr = (struct serial_term_hdr *) msg; + int msg_len; + + /* wait for response(s) */ + do { + /* receive message */ + msg_len = recv_response(intf, msg, max_len); + + /* check if valid message received */ + if (msg_len > 0) { + /* validate message size */ + if (msg_len < 4) { + /* either bad response or non-related message */ + continue; + } + + /* check for the waited response */ + if (hdr->netFn == (req_ctx->netFn|4) + && (hdr->seq & ~3) == req_ctx->seq + && hdr->cmd == req_ctx->cmd) { + /* check if something new has been parsed */ + if (verbose > 3) { + fprintf(stderr, "Got response:\n"); + fprintf(stderr, " NetFN/rsLUN = 0x%x\n", msg[0]); + fprintf(stderr, " rqSeq/Bridge = 0x%x\n", msg[1]); + fprintf(stderr, " cmd = 0x%x\n", msg[2]); + fprintf(stderr, " completion code = 0x%x\n", msg[3]); + if (msg_len > 8) { + fprintf(stderr, " data_len = %d\n", + msg_len - 4); + fprintf(stderr, " data = %s\n", + buf2str(msg + 4, msg_len - 4)); + } + } + + /* move to start from completion code */ + memmove(msg, hdr + 1, msg_len - sizeof (*hdr)); + + /* the waited one */ + return msg_len - sizeof (*hdr); + } + } + } while (msg_len > 0); + + return 0; +} + +/* + * Get message from receive message queue + */ +static int +serial_term_get_message(struct ipmi_intf * intf, + struct serial_term_request_ctx * req_ctx, + uint8_t * msg, size_t max_len) +{ + uint8_t data[IPMI_SERIAL_MAX_RESPONSE]; + struct serial_term_request_ctx tmp_ctx; + struct ipmi_get_message_rp * rp = (struct ipmi_get_message_rp *) data; + struct serial_term_hdr hdr; + clock_t start, tm; + int rv, netFn, rqSeq; + + start = clock(); + + do { + /* fill-in request context */ + tmp_ctx.netFn = 0x18; + tmp_ctx.seq = serial_term_alloc_seq() << 2; + tmp_ctx.cmd = 0x33; + + /* fill-in request data */ + hdr.netFn = tmp_ctx.netFn; + hdr.seq = tmp_ctx.seq; + hdr.cmd = tmp_ctx.cmd; + + /* send request */ + serial_flush(intf); + serial_term_send_msg(intf, (uint8_t *) &hdr, 3); + + /* wait for response */ + rv = serial_term_wait_response(intf, &tmp_ctx, data, sizeof (data)); + + /* check for IO error or timeout */ + if (rv <= 0) { + return rv; + } + + netFn = (req_ctx->netFn & ~3)|(req_ctx->seq & 3)|4; + rqSeq = req_ctx->seq & ~3; + + /* check completion code */ + if (rp->completion == 0) { + /* check for the waited response */ + if (rp->netFn == netFn + && rp->rsSA == req_ctx->sa + && rp->rqSeq == rqSeq + && rp->cmd == req_ctx->cmd) { + /* copy the rest of message */ + memcpy(msg, rp + 1, rv - sizeof (*rp) - 1); + return rv - sizeof (*rp) - 1; + } + } else if (rp->completion != 0x80) { + return 0; + } + + tm = clock() - start; + + tm /= CLOCKS_PER_SEC; + } while (tm < intf->session->timeout); + + return 0; +} + +static struct ipmi_rs * +ipmi_serial_term_send_cmd(struct ipmi_intf * intf, struct ipmi_rq * req) +{ + static struct ipmi_rs rsp; + uint8_t msg[IPMI_SERIAL_MAX_RESPONSE], * resp = msg; + struct serial_term_request_ctx req_ctx[2]; + int retry, rv, msg_len, bridging_level; + + if (!intf->opened && intf->open && intf->open(intf) < 0) { + return NULL; + } + + /* Send the message and receive the answer */ + for (retry = 0; retry < intf->session->retry; retry++) { + /* build output message */ + bridging_level = serial_term_build_msg(intf, req, msg, + sizeof (msg), req_ctx, &msg_len); + if (msg_len < 0) { + return NULL; + } + + /* send request */ + serial_flush(intf); + serial_term_send_msg(intf, msg, msg_len); + + /* wait for response */ + rv = serial_term_wait_response(intf, &req_ctx[0], msg, sizeof (msg)); + + /* check for IO error */ + if (rv < 0) { + return NULL; + } + + /* check for timeout */ + if (rv == 0) { + continue; + } + + /* check for bridging */ + if (bridging_level && msg[0] == 0) { + /* in the case of payload interface we check receive message queue */ + if (is_system) { + /* check message flags */ + rv = serial_term_get_message(intf, &req_ctx[1], + msg, sizeof (msg)); + + /* check for IO error */ + if (rv < 0) { + return NULL; + } + + /* check for timeout */ + if (rv == 0) { + continue; + } + /* check if response for inner request is not encapsulated */ + } else if (rv == 1) { + /* wait for response for inner request */ + rv = serial_term_wait_response(intf, &req_ctx[1], + msg, sizeof (msg)); + + /* check for IO error */ + if (rv < 0) { + return NULL; + } + + /* check for timeout */ + if (rv == 0) { + continue; + } + } else { + /* skip outer level header */ + resp = msg + sizeof (struct ipmb_msg_hdr) + 1; + /* decrement response size */ + rv -= + sizeof (struct ipmb_msg_hdr) + 2; + } + + /* check response size */ + if (resp[0] == 0 && bridging_level == 2 && rv < 8) { + lprintf(LOG_ERR, "ipmitool: Message response is too short"); + /* invalid message length */ + return NULL; + } + } + + /* check for double bridging */ + if (bridging_level == 2 && resp[0] == 0) { + /* get completion code */ + rsp.ccode = resp[7]; + rsp.data_len = rv - 9; + memcpy(rsp.data, resp + 8, rsp.data_len); + } else { + rsp.ccode = resp[0]; + rsp.data_len = rv - 1; + memcpy(rsp.data, resp + 1, rsp.data_len); + } + + /* return response */ + return &rsp; + } + + /* no valid response */ + return NULL; +} + +static int +ipmi_serial_term_setup(struct ipmi_intf * intf) +{ + intf->session = malloc(sizeof(struct ipmi_session)); + if (intf->session == NULL) { + lprintf(LOG_ERR, "ipmitool: malloc failure"); + return -1; + } + + memset(intf->session, 0, sizeof(struct ipmi_session)); + + /* setup default LAN maximum request and response sizes */ + intf->max_request_data_size = IPMI_SERIAL_MAX_RQ_SIZE; + intf->max_response_data_size = IPMI_SERIAL_MAX_RS_SIZE; + return 0; +} + +int +ipmi_serial_term_set_my_addr(struct ipmi_intf * intf, uint8_t addr) +{ + intf->my_addr = addr; + return 0; +} + +struct ipmi_intf ipmi_serial_term_intf = { + name: "serial-terminal", + desc: "Serial Interface, Terminal Mode", + setup: ipmi_serial_term_setup, + open: ipmi_serial_term_open, + close: ipmi_serial_term_close, + sendrecv: ipmi_serial_term_send_cmd, + set_my_addr:ipmi_serial_term_set_my_addr +}; |