diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 00000000..aae6c796 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,5 @@ +ebus Daemon (ebusd) - AUTHORS +============================= + +Current maintainer: + Roland Jax diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 00000000..04f972db --- /dev/null +++ b/ChangeLog @@ -0,0 +1,178 @@ +ebus Daemon (ebusd) - ChangeLog +=============================== + +2013-11-06: +* Specific vaillant data type "ulg" (unsigned long) added. + +2013-09-20: +* Removed unnecessary spaces located behind last return value. (thx JuMi2006 / KNX Forum) +* Removed more unneeded space characters. (thx JuMi2006 / KNX Forum) + +2013-09-04: +* Fixed eb_recv_data_get function. (Replaced strncpy with memcpy.) + +2013-08-26: +* Modified hex decode routine. (To set start/end point of data bytes.) +* Some cosmetic changes. + +2013-05-28: +* Modified timeout statement, too force serial device reconnect. (thx aloz77 / KNX Forum) + +2013-05-27: +* Added timeout to select at main_loop. (thx aloz77 / KNX Forum) + +2013-05-25: +* Modified hex Dump (EBH) for active sent messages. (thx Fry / KNX Forum) +* Changed telnet connecting log level info from L_NET to L_DBG. +* Added ability to reopen temporary removed serial device. (thx aloz77 / KNX Forum) + Attention: If the serial device is removed, network socket will be closed too. +* Removed data output format for executed commands. (thx JuMi2006 / KNX Forum) + +2013-05-24: +* Added hex type to eb_cmd_encode_value function. (thx Fry / KNX Forum) + +2013-05-17: +* Modified eb_serial_valid function and usage. (thx aloz77 / KNX Forum) + +2013-05-16: +* Added ability to restrict tcp connection to localhost. (thx aloz77 / KNX Forum) +* Added skip of negative sleep time while waiting for QQ byte. (thx aloz77 / KNX Forum) + +2013-05-05: +* Added function checks whether the FD is still a valid serial device. + +2013-04-27: +* Improved csv files read in a way that ebus parts are not case sensitive. +* Added eb_cmd_uppercase function. + +2013-04-17: +* Changed error messages from daemon startup vom E_ERR to E_ALL. + +2013-04-09: +* Extended signal handler to fit with logrotate / logging facility. +* Added ebusd.logrotate.d file too contrib. +* Added gentoo ebusd.init.d and ebusd.conf.d scripts. +* Reverted logging functions too old behavior. + +2013-04-08: +* Logging functions changed so it fits together with the behavior of logrotate. +* Fixed function eb_cmd_search_com_id so it compares class length too. + +2013-03-28: +* Fixed daemon crash if some spaces was found behind command (without subcommands.) + +2013-03-16: +* Fixed slave message CRC check for cyc messages. + +2013-03-15: +* Improved cyc commands data handling so all parts from message could be decoded. +* Added new parameter d_part to element configuration part. +* Updated documantation. +* ebusd.init script (debian) slightly modified. + +2013-03-06: +* Fixed extension for command files in configuration. A point is not necessary. + +2013-03-03: +* Added debian init script ebusd.init to contrib. +* Fixed autogen.sh that it worked properly with bourne shell. + +2013-03-01: +* Added new directory doc with some usefull documentation. +* Added an example command csv file into contrib directory. +* Changed width of class string from 5 to 20. + +2013-02-28: +* Added loglevel as telnet command to change logging level at runtime. +* Moved eb_cmd* functions from ebus-bus file to new ebus-cmd file. +* Added many explicit casts to remove compiler warnings. + +2013-02-27: +* Fixed memory leak in eb_raw_print_hex twice. +* Added new config parameter print_size to adjust number of bytes at output stream. + +2013-02-25: +* Fixed possible memory leaks in eb_raw_print_hex and eb_search_cyc + +2013-02-22: +* Added too each client answer (no need of special config for putty) +* Minor print cosmetic with cyc commands on Master Slave types + +2013-02-21: +* Improved decoding of cyc commands to all ebus message types. +* Added function eb_cyc_data_process to decode all cyc ebus message types. +* Removed hex bitmask of logging output. +* Fixed uninitialized buffer in eb_cyc_data_recv. + +2013-02-19: +* Added some usefull description of ebusd to README + +2013-02-18: +* Changed name of functions for message handling from eb_send_cmd to eb_msg_execute. +* Changed name of eb_msg_search_hex function to eb_msg_search_cyc. + +2013-02-17: +* Added csv tool to print command configuration. +* Added decoding of cyc commands. +* Fixed daemon crash when telnet command window closed without quit signal. +* Split dat (date) decode format into bda (bcd) and hda (hex). +* Split tim (time) decode format into bti (bcd) and hti (hex). +* Added internal buffer for cyc data, so cyc commands can be used like get/set + to fetch last data. + +2013-02-16: +* Structure for commands improved so only one structure for GET/SET/CYC is needed +* Possible formats for GET / SET (asc, bcd, d1b, d1c, d2b, d2c, dat, tim) +* Commands with format hex will return undecoded data. +* Added d_valid to structure. It will be used to check answers with given items. +* Added functions to decode/encode date and time to library. +* Changed CSV file struture to fit new data structure. + +2013-02-10: +* Changed decode datatype from int to bcd +* Added bcd, d2b and d2c too available SET commands +* Changed CSV file structur - move comment on position 4 + +2013-02-09: +* Upgrade the send ability for ebus SET commands. (only d1b and d1c) + +2013-02-06: +* Fixed increase length of ebus command from 20 to 30 +* Fixed set initiale value of crclen before escape in eb_send_data_prepare() +* Fixed eb_msg_decode_result() do strtok on local copy from r_pos. Maybe + we don't wont do modife the original value. +* Fixed compare strlen of given command with command from csv file too. +* Added send_retry parameter (2=default) to daemon configuration. + +2013-02-04: +* Add the ability to send commands via telnet to daemon + (This ist just implemented for GET and a few ebus commands for response) +* The telnet commands will collected into a message buffer. + If the cycle buffer is free, only one command is sent. + +2013-02-01: +* Added functions to read ebus command files and load them into nested + structure. This will be used to send and decode ebus messages. +* Try to read daemon configuration file at daemon directory too. +* ebus files splitted into ebus-decode and ebus-bus part. +* Moved log.* from src to lib directory and build liblog.a + +2013-01-25: +* Added autogen.sh script in main dir. Just type ./autogen.sh for build all. +* Logging functions changed from level to class system. The advantage of this + system is that different classes can be easily mixed together. + There are now 8 classes: INF, NOT, WAR, ERR, DBG, EBH, EBS, NET + and ALL for just all messages. + +2013-01-24: +* Added new directory contrib with daemon configuration file +* Added functions with cfg_ prefix (utils). These parse + the daemon configuration file and set parameters. + Priority of given configuration by: + 1. commandline argurments + 2. configuration file + 3. default configuration (hard coded) + +2013-01-17: +* Initial release +* Prepare Source to compile with GNU Autotools diff --git a/INSTALL b/INSTALL new file mode 100644 index 00000000..a1e89e18 --- /dev/null +++ b/INSTALL @@ -0,0 +1,370 @@ +Installation Instructions +************************* + +Copyright (C) 1994-1996, 1999-2002, 2004-2011 Free Software Foundation, +Inc. + + Copying and distribution of this file, with or without modification, +are permitted in any medium without royalty provided the copyright +notice and this notice are preserved. This file is offered as-is, +without warranty of any kind. + +Basic Installation +================== + + Briefly, the shell commands `./configure; make; make install' should +configure, build, and install this package. The following +more-detailed instructions are generic; see the `README' file for +instructions specific to this package. Some packages provide this +`INSTALL' file but do not implement all of the features documented +below. The lack of an optional feature in a given package is not +necessarily a bug. More recommendations for GNU packages can be found +in *note Makefile Conventions: (standards)Makefile Conventions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, and a +file `config.log' containing compiler output (useful mainly for +debugging `configure'). + + It can also use an optional file (typically called `config.cache' +and enabled with `--cache-file=config.cache' or simply `-C') that saves +the results of its tests to speed up reconfiguring. Caching is +disabled by default to prevent problems with accidental use of stale +cache files. + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If you are using the cache, and at +some point `config.cache' contains results you don't want to keep, you +may remove or edit it. + + The file `configure.ac' (or `configure.in') is used to create +`configure' by a program called `autoconf'. You need `configure.ac' if +you want to change it or regenerate `configure' using a newer version +of `autoconf'. + + The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. + + Running `configure' might take a while. While running, it prints + some messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package, generally using the just-built uninstalled binaries. + + 4. Type `make install' to install the programs and any data files and + documentation. When installing into a prefix owned by root, it is + recommended that the package be configured and built as a regular + user, and only the `make install' phase executed with root + privileges. + + 5. Optionally, type `make installcheck' to repeat any self-tests, but + this time using the binaries in their final installed location. + This target does not install anything. Running this target as a + regular user, particularly if the prior `make install' required + root privileges, verifies that the installation completed + correctly. + + 6. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + + 7. Often, you can also type `make uninstall' to remove the installed + files again. In practice, not all packages have tested that + uninstallation works correctly, even though it is required by the + GNU Coding Standards. + + 8. Some packages, particularly those that use Automake, provide `make + distcheck', which can by used by developers to test that all other + targets like `make install' and `make uninstall' work correctly. + This target is generally not run by end users. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. Run `./configure --help' +for details on some of the pertinent environment variables. + + You can give `configure' initial values for configuration parameters +by setting variables in the command line or in the environment. Here +is an example: + + ./configure CC=c99 CFLAGS=-g LIBS=-lposix + + *Note Defining Variables::, for more details. + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you can use GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. This +is known as a "VPATH" build. + + With a non-GNU `make', it is safer to compile the package for one +architecture at a time in the source code directory. After you have +installed the package for one architecture, use `make distclean' before +reconfiguring for another architecture. + + On MacOS X 10.5 and later systems, you can create libraries and +executables that work on multiple system types--known as "fat" or +"universal" binaries--by specifying multiple `-arch' options to the +compiler but only a single `-arch' option to the preprocessor. Like +this: + + ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ + CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ + CPP="gcc -E" CXXCPP="g++ -E" + + This is not guaranteed to produce working output in all cases, you +may have to build one architecture at a time and combine the results +using the `lipo' tool if you have problems. + +Installation Names +================== + + By default, `make install' installs the package's commands under +`/usr/local/bin', include files under `/usr/local/include', etc. You +can specify an installation prefix other than `/usr/local' by giving +`configure' the option `--prefix=PREFIX', where PREFIX must be an +absolute file name. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +pass the option `--exec-prefix=PREFIX' to `configure', the package uses +PREFIX as the prefix for installing programs and libraries. +Documentation and other data files still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=DIR' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. In general, the +default for these options is expressed in terms of `${prefix}', so that +specifying just `--prefix' will affect all of the other directory +specifications that were not explicitly provided. + + The most portable way to affect installation locations is to pass the +correct locations to `configure'; however, many packages provide one or +both of the following shortcuts of passing variable assignments to the +`make install' command line to change installation locations without +having to reconfigure or recompile. + + The first method involves providing an override variable for each +affected directory. For example, `make install +prefix=/alternate/directory' will choose an alternate location for all +directory configuration variables that were expressed in terms of +`${prefix}'. Any directories that were specified during `configure', +but not in terms of `${prefix}', must each be overridden at install +time for the entire installation to be relocated. The approach of +makefile variable overrides for each directory variable is required by +the GNU Coding Standards, and ideally causes no recompilation. +However, some platforms have known limitations with the semantics of +shared libraries that end up requiring recompilation when using this +method, particularly noticeable in packages that use GNU Libtool. + + The second method involves providing the `DESTDIR' variable. For +example, `make install DESTDIR=/alternate/directory' will prepend +`/alternate/directory' before all installation names. The approach of +`DESTDIR' overrides is not required by the GNU Coding Standards, and +does not work on platforms that have drive letters. On the other hand, +it does better at avoiding recompilation issues, and works well even +when some directory options were not specified in terms of `${prefix}' +at `configure' time. + +Optional Features +================= + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + + Some packages offer the ability to configure how verbose the +execution of `make' will be. For these packages, running `./configure +--enable-silent-rules' sets the default to minimal output, which can be +overridden with `make V=1'; while running `./configure +--disable-silent-rules' sets the default to verbose, which can be +overridden with `make V=0'. + +Particular systems +================== + + On HP-UX, the default C compiler is not ANSI C compatible. If GNU +CC is not installed, it is recommended to use the following options in +order to use an ANSI C compiler: + + ./configure CC="cc -Ae -D_XOPEN_SOURCE=500" + +and if that doesn't work, install pre-built binaries of GCC for HP-UX. + + HP-UX `make' updates targets which have the same time stamps as +their prerequisites, which makes it generally unusable when shipped +generated files such as `configure' are involved. Use GNU `make' +instead. + + On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot +parse its `' header file. The option `-nodtk' can be used as +a workaround. If GNU CC is not installed, it is therefore recommended +to try + + ./configure CC="cc" + +and if that doesn't work, try + + ./configure CC="cc -nodtk" + + On Solaris, don't put `/usr/ucb' early in your `PATH'. This +directory contains several dysfunctional programs; working variants of +these programs are available in `/usr/bin'. So, if you need `/usr/ucb' +in your `PATH', put it _after_ `/usr/bin'. + + On Haiku, software installed for all users goes in `/boot/common', +not `/usr/local'. It is recommended to use the following options: + + ./configure --prefix=/boot/common + +Specifying the System Type +========================== + + There may be some features `configure' cannot figure out +automatically, but needs to determine by the type of machine the package +will run on. Usually, assuming the package is built to be run on the +_same_ architectures, `configure' can figure that out, but if it prints +a message saying it cannot guess the machine type, give it the +`--build=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name which has the form: + + CPU-COMPANY-SYSTEM + +where SYSTEM can have one of these forms: + + OS + KERNEL-OS + + See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the machine type. + + If you are _building_ compiler tools for cross-compiling, you should +use the option `--target=TYPE' to select the type of system they will +produce code for. + + If you want to _use_ a cross compiler, that generates code for a +platform different from the build platform, you should specify the +"host" platform (i.e., that on which the generated programs will +eventually be run) with `--host=TYPE'. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Defining Variables +================== + + Variables not defined in a site shell script can be set in the +environment passed to `configure'. However, some packages may run +configure again during the build, and the customized values of these +variables may be lost. In order to avoid this problem, you should set +them in the `configure' command line, using `VAR=value'. For example: + + ./configure CC=/usr/local2/bin/gcc + +causes the specified `gcc' to be used as the C compiler (unless it is +overridden in the site shell script). + +Unfortunately, this technique does not work for `CONFIG_SHELL' due to +an Autoconf bug. Until the bug is fixed you can use this workaround: + + CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash + +`configure' Invocation +====================== + + `configure' recognizes the following options to control how it +operates. + +`--help' +`-h' + Print a summary of all of the options to `configure', and exit. + +`--help=short' +`--help=recursive' + Print a summary of the options unique to this package's + `configure', and exit. The `short' variant lists options used + only in the top level, while the `recursive' variant lists options + also present in any nested packages. + +`--version' +`-V' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`--cache-file=FILE' + Enable the cache: use and save the results of the tests in FILE, + traditionally `config.cache'. FILE defaults to `/dev/null' to + disable caching. + +`--config-cache' +`-C' + Alias for `--cache-file=config.cache'. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`--prefix=DIR' + Use DIR as the installation prefix. *note Installation Names:: + for more details, including other options available for fine-tuning + the installation locations. + +`--no-create' +`-n' + Run the configure checks, but stop before creating any output + files. + +`configure' also accepts some other, not widely useful, options. Run +`configure --help' for more details. + diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 00000000..60594b56 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,8 @@ +SUBDIRS = lib src tools + +distclean-local: + -rm -rf autom4te.cache + -rm -f aclocal.m4 + -rm -f config.h.in + -rm -f configure + -rm -f Makefile.in diff --git a/NEWS b/NEWS new file mode 100644 index 00000000..f371b8e6 --- /dev/null +++ b/NEWS @@ -0,0 +1,77 @@ +ebus Daemon (ebusd) - NEWS +========================== + +2013-11-14: +* Initial commit on github. + +2013-11-06: +* Specific vaillant data type "ulg" (unsigned long) added. + +2013-05-25: +* Added ability to reopen temporary removed serial device. (thx aloz77 / KNX Forum) + +2013-05-16: +* Added ability to restrict tcp connection to localhost. (thx aloz77 / KNX Forum) + +2013-04-27: +* Improved csv files read in a way that ebus parts are not case sensitive. + +2013-04-09: +* Extended signal handler to fit with logrotate / logging facility. + +2013-04-08: +* Logging functions changed so it fits together with the behavior of logrotate. + +2013-03-15: +* Improved cyc commands data handling so all parts from message could be decoded. + +2013-03-03: +* Added debian init script ebusd.init to contrib. + +2013-03-01: +* Added documentation and an example command csv file. + +2013-02-28: +* Added loglevel as telnet command to change logging level at runtime. + +2013-02-27: +* Added new config parameter print_size to adjust number of bytes at output stream. + +2013-02-22: +* Changed client answer code so normal telnet client can handle it + +2013-02-21: +* Improved decoding of cyc commands to all ebus message types. + +2013-02-19: +* Added some usefull description of ebusd to README + +2013-02-17: +* Added csv tool to print command configuration. +* Added deconding of cyc commands. + +2013-02-16: +* Structure for commands improved so only one structure for GET/SET/CYC is needed +* Possible formats for GET / SET (asc, bcd, d1b, d1c, d2b, d2c, dat, tim) + +2013-02-06: +* Fixed some malfunctions of command decode function +* Added parameter send_retry so the daemon can try x times to send command + +2013-02-04: +* Added the ability to send commands via telnet to daemon + (This ist just added for GET and a few ebus commands for response) + +2013-02-01: +* Added functions to read ebus command files + +2013-01-25: +* Added autogen.sh script in main dir +* Logging functions changed from level to class system + +2013-01-24: +* Added example daemon configuration file +* Added functions for parsing daemon configuration file + +2013-01-17: +* Prepare Source to compile with GNU Autotools diff --git a/README.md b/README.md index 7180209a..0045bee8 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,111 @@ -ebusd -===== +ebusd - README +============== Daemon to communicate with the heating system via ebus + + +Gerneral Information +==================== + +ebus (energy bus) is a serial communication protocoll used in heating systems. + +ebusd acts like a proxy. On the one hand it is listing on a TCP port 8888 for +incoming telnet connections. On the other hand it sends and receive ebus message +via ebus-usb adapter to or from the ebus. + + +Features: +========= + + * ebus - send and receive ebus commands + - send Broadcast (BC), Master-Master (MM) and Master-Slave (MS) messages + - calculate CRC for sending data and check CRC of received data + - not allowed byte sequences inside data will be escaped and unescaped + - it saved data from specified cycle messages + + * telnet - multi telnet session (listen on TCP port 8888) + The available commands are different of each heating system and provider. + This commands are defined in csv files which will be parsed on daemon startup. + + Commands: + - get send message (BC, MM or MS) to slave and decoded received answer + - set send data to the slave with the intention to adjust this value + - cyc return data from collected cycle messages + - loglevel change loglevel at runtime + - quit disconnect from daemon + - shutdown stop daemon + + +Build: +====== + +$ ./autogen.sh + +This script execute the following commands in this order. + +$ aclocal +$ autoconf +$ autoheader +$ automake +$ ./configure +$ make + + +Daemon Configuration: +===================== + +The daemon will search ist configuration file ebusd.conf primary at /etc/ebusd. +This can be override by given commandline option -C. For detailed help of daemon +configuration try ./ebusd -h or take a look at ebusd.conf file. + + + +Command Configuration: +====================== + +In short some description to the neccassarry structure of the csv files. + + * [ 3] type - type of command (get, set, cyc) + * [ 5] class - comand class (ci, ...) + * [ 30] cmd - command (password, ...) + * [ 256] com - description of the command + * [ int] s_type - ebus message type (BC = 1, MM = 2 or MS = 3) + * [ 2] s_zz - hex address from slave (15) + * [ 4] s_cmd - primary and secendory ebus command (e.g. B509) + * [ int] s_len - length of data bytes + * [ 32] s_msg - data bytes of message + * [ int] d_elem - number of elements from different data types + +For each different data type must defined the follow struture + + * [ 20] d_sub - subcommand (pin1, ...) + * [ 2] d_part - part of message for data bytes (MD, SA, SD, MA) + * [ 10] d_pos - position at data bytes (1, 2, 3, ...) + * [ 3] d_type - data type (asc, bcd, ...)) + * [float] d_fac - facter to scale data (1.0, 0.02, ...) + * [ 6] d_unit - unit of data (°C, ...) + * [ 30] d_valid - possible entrys (01, 02, 03, ...) + * [ 256] d_com - description of sub command + + +Different data types [decode|encode]: + + * [de] x - asc - ascii + * [de] 1 - bcd - bcd + * [de] 1 - d1b - data1b + * [de] 1 - d1c - data1c + * [de] 2 - d2b - data2b + * [de] 2 - d2c - data2c + * [d ] 3 - bda - date (bcd format) + * [de] 3 - hda - date (hex format) + * [d ] 3 - bti - time (bcd format) + * [de] 3 - hti - time (hex format) + * [d ] 1 - bdy - day (bcd format) + * [de] 1 - hdy - day (hex format) + * [d ] x - hex - print data only with 2-nibble hex format + * [d ] 4 - ulg - unsigned long + + + +The author can be contacted at roland.jax@liwest.at. + diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 00000000..31cb28e0 --- /dev/null +++ b/autogen.sh @@ -0,0 +1,64 @@ +#!/bin/sh +# +# Copyright (C) Roland Jax 2012-2013 +# +# This file is part of ebusd. +# +# ebusd is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# ebusd is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with ebusd. If not, see http://www.gnu.org/licenses/. +# + +exists() +{ + if command $1 + then + return 1 + else + printf " command not found\n"; + exit + fi +} + +run() +{ + $1 + if [ $? -eq 0 ] + then + printf " done\n"; + else + printf " command failed\n"; + exit + fi +} + +printf ">>> aclocal"; +exists aclocal +run aclocal + +printf ">>> autoconf"; +exists autoconf; +run autoconf + +printf ">>> autoheader"; +exists autoheader +run autoheader + +printf ">>> automake"; +exists automake +run automake + +printf ">>> configure\n"; +./configure + +printf ">>> make\n"; +run make diff --git a/compile b/compile new file mode 100755 index 00000000..862a14e8 --- /dev/null +++ b/compile @@ -0,0 +1,343 @@ +#! /bin/sh +# Wrapper for compilers which do not understand '-c -o'. + +scriptversion=2012-03-05.13; # UTC + +# Copyright (C) 1999, 2000, 2003, 2004, 2005, 2009, 2010, 2012 Free +# Software Foundation, Inc. +# Written by Tom Tromey . +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# This file is maintained in Automake, please report +# bugs to or send patches to +# . + +nl=' +' + +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent tools from complaining about whitespace usage. +IFS=" "" $nl" + +file_conv= + +# func_file_conv build_file lazy +# Convert a $build file to $host form and store it in $file +# Currently only supports Windows hosts. If the determined conversion +# type is listed in (the comma separated) LAZY, no conversion will +# take place. +func_file_conv () +{ + file=$1 + case $file in + / | /[!/]*) # absolute file, and not a UNC file + if test -z "$file_conv"; then + # lazily determine how to convert abs files + case `uname -s` in + MINGW*) + file_conv=mingw + ;; + CYGWIN*) + file_conv=cygwin + ;; + *) + file_conv=wine + ;; + esac + fi + case $file_conv/,$2, in + *,$file_conv,*) + ;; + mingw/*) + file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'` + ;; + cygwin/*) + file=`cygpath -m "$file" || echo "$file"` + ;; + wine/*) + file=`winepath -w "$file" || echo "$file"` + ;; + esac + ;; + esac +} + +# func_cl_dashL linkdir +# Make cl look for libraries in LINKDIR +func_cl_dashL () +{ + func_file_conv "$1" + if test -z "$lib_path"; then + lib_path=$file + else + lib_path="$lib_path;$file" + fi + linker_opts="$linker_opts -LIBPATH:$file" +} + +# func_cl_dashl library +# Do a library search-path lookup for cl +func_cl_dashl () +{ + lib=$1 + found=no + save_IFS=$IFS + IFS=';' + for dir in $lib_path $LIB + do + IFS=$save_IFS + if $shared && test -f "$dir/$lib.dll.lib"; then + found=yes + lib=$dir/$lib.dll.lib + break + fi + if test -f "$dir/$lib.lib"; then + found=yes + lib=$dir/$lib.lib + break + fi + done + IFS=$save_IFS + + if test "$found" != yes; then + lib=$lib.lib + fi +} + +# func_cl_wrapper cl arg... +# Adjust compile command to suit cl +func_cl_wrapper () +{ + # Assume a capable shell + lib_path= + shared=: + linker_opts= + for arg + do + if test -n "$eat"; then + eat= + else + case $1 in + -o) + # configure might choose to run compile as 'compile cc -o foo foo.c'. + eat=1 + case $2 in + *.o | *.[oO][bB][jJ]) + func_file_conv "$2" + set x "$@" -Fo"$file" + shift + ;; + *) + func_file_conv "$2" + set x "$@" -Fe"$file" + shift + ;; + esac + ;; + -I) + eat=1 + func_file_conv "$2" mingw + set x "$@" -I"$file" + shift + ;; + -I*) + func_file_conv "${1#-I}" mingw + set x "$@" -I"$file" + shift + ;; + -l) + eat=1 + func_cl_dashl "$2" + set x "$@" "$lib" + shift + ;; + -l*) + func_cl_dashl "${1#-l}" + set x "$@" "$lib" + shift + ;; + -L) + eat=1 + func_cl_dashL "$2" + ;; + -L*) + func_cl_dashL "${1#-L}" + ;; + -static) + shared=false + ;; + -Wl,*) + arg=${1#-Wl,} + save_ifs="$IFS"; IFS=',' + for flag in $arg; do + IFS="$save_ifs" + linker_opts="$linker_opts $flag" + done + IFS="$save_ifs" + ;; + -Xlinker) + eat=1 + linker_opts="$linker_opts $2" + ;; + -*) + set x "$@" "$1" + shift + ;; + *.cc | *.CC | *.cxx | *.CXX | *.[cC]++) + func_file_conv "$1" + set x "$@" -Tp"$file" + shift + ;; + *.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO]) + func_file_conv "$1" mingw + set x "$@" "$file" + shift + ;; + *) + set x "$@" "$1" + shift + ;; + esac + fi + shift + done + if test -n "$linker_opts"; then + linker_opts="-link$linker_opts" + fi + exec "$@" $linker_opts + exit 1 +} + +eat= + +case $1 in + '') + echo "$0: No command. Try '$0 --help' for more information." 1>&2 + exit 1; + ;; + -h | --h*) + cat <<\EOF +Usage: compile [--help] [--version] PROGRAM [ARGS] + +Wrapper for compilers which do not understand '-c -o'. +Remove '-o dest.o' from ARGS, run PROGRAM with the remaining +arguments, and rename the output as expected. + +If you are trying to build a whole package this is not the +right script to run: please start by reading the file 'INSTALL'. + +Report bugs to . +EOF + exit $? + ;; + -v | --v*) + echo "compile $scriptversion" + exit $? + ;; + cl | *[/\\]cl | cl.exe | *[/\\]cl.exe ) + func_cl_wrapper "$@" # Doesn't return... + ;; +esac + +ofile= +cfile= + +for arg +do + if test -n "$eat"; then + eat= + else + case $1 in + -o) + # configure might choose to run compile as 'compile cc -o foo foo.c'. + # So we strip '-o arg' only if arg is an object. + eat=1 + case $2 in + *.o | *.obj) + ofile=$2 + ;; + *) + set x "$@" -o "$2" + shift + ;; + esac + ;; + *.c) + cfile=$1 + set x "$@" "$1" + shift + ;; + *) + set x "$@" "$1" + shift + ;; + esac + fi + shift +done + +if test -z "$ofile" || test -z "$cfile"; then + # If no '-o' option was seen then we might have been invoked from a + # pattern rule where we don't need one. That is ok -- this is a + # normal compilation that the losing compiler can handle. If no + # '.c' file was seen then we are probably linking. That is also + # ok. + exec "$@" +fi + +# Name of file we expect compiler to create. +cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'` + +# Create the lock directory. +# Note: use '[/\\:.-]' here to ensure that we don't use the same name +# that we are using for the .o file. Also, base the name on the expected +# object file name, since that is what matters with a parallel build. +lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d +while true; do + if mkdir "$lockdir" >/dev/null 2>&1; then + break + fi + sleep 1 +done +# FIXME: race condition here if user kills between mkdir and trap. +trap "rmdir '$lockdir'; exit 1" 1 2 15 + +# Run the compile. +"$@" +ret=$? + +if test -f "$cofile"; then + test "$cofile" = "$ofile" || mv "$cofile" "$ofile" +elif test -f "${cofile}bj"; then + test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile" +fi + +rmdir "$lockdir" +exit $ret + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/configure.ac b/configure.ac new file mode 100644 index 00000000..5bcbe121 --- /dev/null +++ b/configure.ac @@ -0,0 +1,27 @@ +AC_PREREQ([2.61]) +AC_INIT([ebusd], [0.1], [roland.jax@liwest.at]) +AC_CONFIG_SRCDIR([src/ebusd.c]) +AC_CONFIG_HEADERS([config.h]) + +AM_INIT_AUTOMAKE + +# Checks for programs. +AC_PROG_CC +AM_PROG_CC_C_O + +# Checks for libraries. +AC_PROG_RANLIB + +# Checks for header files. +AC_CHECK_HEADERS([fcntl.h netdb.h stdlib.h string.h sys/socket.h sys/time.h syslog.h termios.h unistd.h]) + +# Checks for typedefs, structures, and compiler characteristics. +AC_TYPE_PID_T + +# Checks for library functions. +AC_FUNC_FORK +AC_CHECK_FUNCS([gettimeofday memset select socket strerror strrchr]) + +AC_CONFIG_FILES([Makefile lib/Makefile src/Makefile tools/Makefile]) + +AC_OUTPUT diff --git a/contrib/debian/init.d b/contrib/debian/init.d new file mode 100755 index 00000000..a578d10c --- /dev/null +++ b/contrib/debian/init.d @@ -0,0 +1,44 @@ +#!/bin/sh + +### BEGIN INIT INFO +# Provides: ebusd +# Required-Start: $network $remote_fs $syslog +# Required-Stop: $network $remote_fs $syslog +# Default-Start: 2 3 4 5 +# Default-Stop: +# Short-Description: Start EBUS daemon +### END INIT INFO + +PATH=/sbin:/bin:/usr/sbin:/usr/bin + +. /lib/lsb/init-functions + +DAEMON=/root/ebusd/src/ebusd +PIDFILE=/var/run/ebusd.pid + +test -x $DAEMON || exit 5 + +case $1 in + start) + log_daemon_msg "Starting EBUS server" "ebusd" + start-stop-daemon --start --quiet --oknodo --pidfile $PIDFILE --startas $DAEMON -- -P $PIDFILE + status=$? + log_end_msg $status + ;; + stop) + log_daemon_msg "Stopping EBUS server" "ebusd" + start-stop-daemon --stop --quiet --oknodo --pidfile $PIDFILE + log_end_msg $? + rm -f $PIDFILE + ;; + restart|force-reload) + $0 stop && sleep 2 && $0 start + ;; + status) + status_of_proc $DAEMON "EBUS server" + ;; + *) + echo "Usage: $0 {start|stop|restart|force-reload|status}" + exit 2 + ;; +esac diff --git a/contrib/etc/ebusd/commands.csv b/contrib/etc/ebusd/commands.csv new file mode 100644 index 00000000..e72b426a --- /dev/null +++ b/contrib/etc/ebusd/commands.csv @@ -0,0 +1,3 @@ +# type;class;cmd;comment;msg type: 1=BROAD, 2=MM, 3=MS;ZZ;PBSB;NN;D1D2D3;elements;sub;part;position;datatype;factor;unit;valid;comment;sub;part;position;datatype;factor;unit;valid;comment;sub;part;position;datatype;factor;unit;valid;comment;sub;part;position;datatype;factor;unit;valid;comment +cyc;broad;date_time_temp;Datum, Uhrzeit und Aussentempertur;1;FE;0700;9;-;4;temp;md;1,2;d2b;1.0;°C;-;Temperatur;day;md;8;bdy;-;-;-;Wochentag;date;md;6,7,9;bda;-;-;-;Datum;time;md;5,4,3;bti;-;-;-;Uhrzeit + diff --git a/contrib/etc/ebusd/ebusd.conf b/contrib/etc/ebusd/ebusd.conf new file mode 100644 index 00000000..0b0f5145 --- /dev/null +++ b/contrib/etc/ebusd/ebusd.conf @@ -0,0 +1,58 @@ +# configuration file for ebusd + +# bus address (FF) +#address=FF + +# configuration directory of command files (/etc/ebusd) +#cfgdir=/etc/ebusd + +# serial device (/dev/ttyUSB0) +#device=/dev/ttyUSB0 + +# extension of command files (csv) +#extension=csv + +# run in foreground (NO | YES/NO) +#foreground=YES + +# log level (INF | INF, NOT, WAR, ERR, DBG, EBH, EBS, NET, ALL) +#loglevel=ALL + +# log file (/var/log/ebusd.log) +#logfile=/var/log/ebusd.log + +# pid file (/var/run/ebusd.pid) +#pidfile=/var/run/ebusd.pid + +# port (8888) +#port=8888 + +# dump raw ebus data to file (NO | YES/NO) +#rawdump=YES + +# raw file (/tmp/ebusd.bin) +#rawfile=/tmp/ebusd.bin + +# print raw data (NO | YES/NO) +#showraw=YES + +# print daemon settings (NO | YES/NO) +#settings=YES + +# allow only connection from localhost +#localhost=YES + +# retry getting bus (3 | max: 10) +#get_retry=3 + +# skipped ACK bytes after get-bus error (1) +#skip_ack=1 + +# wait time for QQ compare (4000 uesc) +#max_wait=4000 + +# retry sending msg (2 | max: 10) +#send_retry=2 + +# number of printed hex bytes (30) +#print_size=30 diff --git a/contrib/etc/logrotate.d/ebusd b/contrib/etc/logrotate.d/ebusd new file mode 100644 index 00000000..9a8ed047 --- /dev/null +++ b/contrib/etc/logrotate.d/ebusd @@ -0,0 +1,10 @@ +# ebusd.log + +/var/log/ebusd.log { + rotate 5 + size=100k + sharedscripts + postrotate + [ -f /var/run/ebusd.pid ] && /bin/kill -INT `cat /var/run/ebusd.pid` + endscript +} diff --git a/contrib/gentoo/etc/conf.d/ebusd b/contrib/gentoo/etc/conf.d/ebusd new file mode 100644 index 00000000..820e84d0 --- /dev/null +++ b/contrib/gentoo/etc/conf.d/ebusd @@ -0,0 +1,6 @@ +# /etc/conf.d/ebusd: config file for /etc/init.d/ebusd + +# Options to pass to the ebusd daemon. +# See the ebusd -h for more info. + +EBUSD_ARGS="" diff --git a/contrib/gentoo/etc/init.d/ebusd b/contrib/gentoo/etc/init.d/ebusd new file mode 100755 index 00000000..767e62ee --- /dev/null +++ b/contrib/gentoo/etc/init.d/ebusd @@ -0,0 +1,21 @@ +#!/sbin/runscript +# Copyright 1999-2013 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 +# $Header: $ + +#extra_started_commands="reload" +command="/usr/sbin/ebusd" +command_args="${EBUSD_ARGS}" +start_stop_daemon_args="--quiet" +description="Daemon for ebus Communication" + +depend() { + need localmount + use logger +} + +#reload() { +# ebegin "Reloading ebusd configuration" +# start-stop-daemon --exec $command --signal HUP +# eend $? +#} diff --git a/depcomp b/depcomp new file mode 100755 index 00000000..25a39e6c --- /dev/null +++ b/depcomp @@ -0,0 +1,708 @@ +#! /bin/sh +# depcomp - compile a program generating dependencies as side-effects + +scriptversion=2012-03-27.16; # UTC + +# Copyright (C) 1999, 2000, 2003, 2004, 2005, 2006, 2007, 2009, 2010, +# 2011, 2012 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Originally written by Alexandre Oliva . + +case $1 in + '') + echo "$0: No command. Try '$0 --help' for more information." 1>&2 + exit 1; + ;; + -h | --h*) + cat <<\EOF +Usage: depcomp [--help] [--version] PROGRAM [ARGS] + +Run PROGRAMS ARGS to compile a file, generating dependencies +as side-effects. + +Environment variables: + depmode Dependency tracking mode. + source Source file read by 'PROGRAMS ARGS'. + object Object file output by 'PROGRAMS ARGS'. + DEPDIR directory where to store dependencies. + depfile Dependency file to output. + tmpdepfile Temporary file to use when outputting dependencies. + libtool Whether libtool is used (yes/no). + +Report bugs to . +EOF + exit $? + ;; + -v | --v*) + echo "depcomp $scriptversion" + exit $? + ;; +esac + +# A tabulation character. +tab=' ' +# A newline character. +nl=' +' + +if test -z "$depmode" || test -z "$source" || test -z "$object"; then + echo "depcomp: Variables source, object and depmode must be set" 1>&2 + exit 1 +fi + +# Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po. +depfile=${depfile-`echo "$object" | + sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`} +tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`} + +rm -f "$tmpdepfile" + +# Some modes work just like other modes, but use different flags. We +# parameterize here, but still list the modes in the big case below, +# to make depend.m4 easier to write. Note that we *cannot* use a case +# here, because this file can only contain one case statement. +if test "$depmode" = hp; then + # HP compiler uses -M and no extra arg. + gccflag=-M + depmode=gcc +fi + +if test "$depmode" = dashXmstdout; then + # This is just like dashmstdout with a different argument. + dashmflag=-xM + depmode=dashmstdout +fi + +cygpath_u="cygpath -u -f -" +if test "$depmode" = msvcmsys; then + # This is just like msvisualcpp but w/o cygpath translation. + # Just convert the backslash-escaped backslashes to single forward + # slashes to satisfy depend.m4 + cygpath_u='sed s,\\\\,/,g' + depmode=msvisualcpp +fi + +if test "$depmode" = msvc7msys; then + # This is just like msvc7 but w/o cygpath translation. + # Just convert the backslash-escaped backslashes to single forward + # slashes to satisfy depend.m4 + cygpath_u='sed s,\\\\,/,g' + depmode=msvc7 +fi + +if test "$depmode" = xlc; then + # IBM C/C++ Compilers xlc/xlC can output gcc-like dependency informations. + gccflag=-qmakedep=gcc,-MF + depmode=gcc +fi + +case "$depmode" in +gcc3) +## gcc 3 implements dependency tracking that does exactly what +## we want. Yay! Note: for some reason libtool 1.4 doesn't like +## it if -MD -MP comes after the -MF stuff. Hmm. +## Unfortunately, FreeBSD c89 acceptance of flags depends upon +## the command line argument order; so add the flags where they +## appear in depend2.am. Note that the slowdown incurred here +## affects only configure: in makefiles, %FASTDEP% shortcuts this. + for arg + do + case $arg in + -c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;; + *) set fnord "$@" "$arg" ;; + esac + shift # fnord + shift # $arg + done + "$@" + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + mv "$tmpdepfile" "$depfile" + ;; + +gcc) +## There are various ways to get dependency output from gcc. Here's +## why we pick this rather obscure method: +## - Don't want to use -MD because we'd like the dependencies to end +## up in a subdir. Having to rename by hand is ugly. +## (We might end up doing this anyway to support other compilers.) +## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like +## -MM, not -M (despite what the docs say). +## - Using -M directly means running the compiler twice (even worse +## than renaming). + if test -z "$gccflag"; then + gccflag=-MD, + fi + "$@" -Wp,"$gccflag$tmpdepfile" + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + echo "$object : \\" > "$depfile" + alpha=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz +## The second -e expression handles DOS-style file names with drive letters. + sed -e 's/^[^:]*: / /' \ + -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile" +## This next piece of magic avoids the "deleted header file" problem. +## The problem is that when a header file which appears in a .P file +## is deleted, the dependency causes make to die (because there is +## typically no way to rebuild the header). We avoid this by adding +## dummy dependencies for each header file. Too bad gcc doesn't do +## this for us directly. + tr ' ' "$nl" < "$tmpdepfile" | +## Some versions of gcc put a space before the ':'. On the theory +## that the space means something, we add a space to the output as +## well. hp depmode also adds that space, but also prefixes the VPATH +## to the object. Take care to not repeat it in the output. +## Some versions of the HPUX 10.20 sed can't process this invocation +## correctly. Breaking it into two sed invocations is a workaround. + sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \ + | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +hp) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +sgi) + if test "$libtool" = yes; then + "$@" "-Wp,-MDupdate,$tmpdepfile" + else + "$@" -MDupdate "$tmpdepfile" + fi + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + + if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files + echo "$object : \\" > "$depfile" + + # Clip off the initial element (the dependent). Don't try to be + # clever and replace this with sed code, as IRIX sed won't handle + # lines with more than a fixed number of characters (4096 in + # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines; + # the IRIX cc adds comments like '#:fec' to the end of the + # dependency line. + tr ' ' "$nl" < "$tmpdepfile" \ + | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' | \ + tr "$nl" ' ' >> "$depfile" + echo >> "$depfile" + + # The second pass generates a dummy entry for each header file. + tr ' ' "$nl" < "$tmpdepfile" \ + | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \ + >> "$depfile" + else + # The sourcefile does not contain any dependencies, so just + # store a dummy comment line, to avoid errors with the Makefile + # "include basename.Plo" scheme. + echo "#dummy" > "$depfile" + fi + rm -f "$tmpdepfile" + ;; + +xlc) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +aix) + # The C for AIX Compiler uses -M and outputs the dependencies + # in a .u file. In older versions, this file always lives in the + # current directory. Also, the AIX compiler puts '$object:' at the + # start of each line; $object doesn't have directory information. + # Version 6 uses the directory in both cases. + dir=`echo "$object" | sed -e 's|/[^/]*$|/|'` + test "x$dir" = "x$object" && dir= + base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'` + if test "$libtool" = yes; then + tmpdepfile1=$dir$base.u + tmpdepfile2=$base.u + tmpdepfile3=$dir.libs/$base.u + "$@" -Wc,-M + else + tmpdepfile1=$dir$base.u + tmpdepfile2=$dir$base.u + tmpdepfile3=$dir$base.u + "$@" -M + fi + stat=$? + + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" + exit $stat + fi + + for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" + do + test -f "$tmpdepfile" && break + done + if test -f "$tmpdepfile"; then + # Each line is of the form 'foo.o: dependent.h'. + # Do two passes, one to just change these to + # '$object: dependent.h' and one to simply 'dependent.h:'. + sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile" + sed -e 's,^.*\.[a-z]*:['"$tab"' ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile" + else + # The sourcefile does not contain any dependencies, so just + # store a dummy comment line, to avoid errors with the Makefile + # "include basename.Plo" scheme. + echo "#dummy" > "$depfile" + fi + rm -f "$tmpdepfile" + ;; + +icc) + # Intel's C compiler anf tcc (Tiny C Compiler) understand '-MD -MF file'. + # However on + # $CC -MD -MF foo.d -c -o sub/foo.o sub/foo.c + # ICC 7.0 will fill foo.d with something like + # foo.o: sub/foo.c + # foo.o: sub/foo.h + # which is wrong. We want + # sub/foo.o: sub/foo.c + # sub/foo.o: sub/foo.h + # sub/foo.c: + # sub/foo.h: + # ICC 7.1 will output + # foo.o: sub/foo.c sub/foo.h + # and will wrap long lines using '\': + # foo.o: sub/foo.c ... \ + # sub/foo.h ... \ + # ... + # tcc 0.9.26 (FIXME still under development at the moment of writing) + # will emit a similar output, but also prepend the continuation lines + # with horizontal tabulation characters. + "$@" -MD -MF "$tmpdepfile" + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + # Each line is of the form 'foo.o: dependent.h', + # or 'foo.o: dep1.h dep2.h \', or ' dep3.h dep4.h \'. + # Do two passes, one to just change these to + # '$object: dependent.h' and one to simply 'dependent.h:'. + sed -e "s/^[ $tab][ $tab]*/ /" -e "s,^[^:]*:,$object :," \ + < "$tmpdepfile" > "$depfile" + sed ' + s/[ '"$tab"'][ '"$tab"']*/ /g + s/^ *// + s/ *\\*$// + s/^[^:]*: *// + /^$/d + /:$/d + s/$/ :/ + ' < "$tmpdepfile" >> "$depfile" + rm -f "$tmpdepfile" + ;; + +hp2) + # The "hp" stanza above does not work with aCC (C++) and HP's ia64 + # compilers, which have integrated preprocessors. The correct option + # to use with these is +Maked; it writes dependencies to a file named + # 'foo.d', which lands next to the object file, wherever that + # happens to be. + # Much of this is similar to the tru64 case; see comments there. + dir=`echo "$object" | sed -e 's|/[^/]*$|/|'` + test "x$dir" = "x$object" && dir= + base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'` + if test "$libtool" = yes; then + tmpdepfile1=$dir$base.d + tmpdepfile2=$dir.libs/$base.d + "$@" -Wc,+Maked + else + tmpdepfile1=$dir$base.d + tmpdepfile2=$dir$base.d + "$@" +Maked + fi + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile1" "$tmpdepfile2" + exit $stat + fi + + for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" + do + test -f "$tmpdepfile" && break + done + if test -f "$tmpdepfile"; then + sed -e "s,^.*\.[a-z]*:,$object:," "$tmpdepfile" > "$depfile" + # Add 'dependent.h:' lines. + sed -ne '2,${ + s/^ *// + s/ \\*$// + s/$/:/ + p + }' "$tmpdepfile" >> "$depfile" + else + echo "#dummy" > "$depfile" + fi + rm -f "$tmpdepfile" "$tmpdepfile2" + ;; + +tru64) + # The Tru64 compiler uses -MD to generate dependencies as a side + # effect. 'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'. + # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put + # dependencies in 'foo.d' instead, so we check for that too. + # Subdirectories are respected. + dir=`echo "$object" | sed -e 's|/[^/]*$|/|'` + test "x$dir" = "x$object" && dir= + base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'` + + if test "$libtool" = yes; then + # With Tru64 cc, shared objects can also be used to make a + # static library. This mechanism is used in libtool 1.4 series to + # handle both shared and static libraries in a single compilation. + # With libtool 1.4, dependencies were output in $dir.libs/$base.lo.d. + # + # With libtool 1.5 this exception was removed, and libtool now + # generates 2 separate objects for the 2 libraries. These two + # compilations output dependencies in $dir.libs/$base.o.d and + # in $dir$base.o.d. We have to check for both files, because + # one of the two compilations can be disabled. We should prefer + # $dir$base.o.d over $dir.libs/$base.o.d because the latter is + # automatically cleaned when .libs/ is deleted, while ignoring + # the former would cause a distcleancheck panic. + tmpdepfile1=$dir.libs/$base.lo.d # libtool 1.4 + tmpdepfile2=$dir$base.o.d # libtool 1.5 + tmpdepfile3=$dir.libs/$base.o.d # libtool 1.5 + tmpdepfile4=$dir.libs/$base.d # Compaq CCC V6.2-504 + "$@" -Wc,-MD + else + tmpdepfile1=$dir$base.o.d + tmpdepfile2=$dir$base.d + tmpdepfile3=$dir$base.d + tmpdepfile4=$dir$base.d + "$@" -MD + fi + + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4" + exit $stat + fi + + for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4" + do + test -f "$tmpdepfile" && break + done + if test -f "$tmpdepfile"; then + sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile" + sed -e 's,^.*\.[a-z]*:['"$tab"' ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile" + else + echo "#dummy" > "$depfile" + fi + rm -f "$tmpdepfile" + ;; + +msvc7) + if test "$libtool" = yes; then + showIncludes=-Wc,-showIncludes + else + showIncludes=-showIncludes + fi + "$@" $showIncludes > "$tmpdepfile" + stat=$? + grep -v '^Note: including file: ' "$tmpdepfile" + if test "$stat" = 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + echo "$object : \\" > "$depfile" + # The first sed program below extracts the file names and escapes + # backslashes for cygpath. The second sed program outputs the file + # name when reading, but also accumulates all include files in the + # hold buffer in order to output them again at the end. This only + # works with sed implementations that can handle large buffers. + sed < "$tmpdepfile" -n ' +/^Note: including file: *\(.*\)/ { + s//\1/ + s/\\/\\\\/g + p +}' | $cygpath_u | sort -u | sed -n ' +s/ /\\ /g +s/\(.*\)/'"$tab"'\1 \\/p +s/.\(.*\) \\/\1:/ +H +$ { + s/.*/'"$tab"'/ + G + p +}' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +msvc7msys) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +#nosideeffect) + # This comment above is used by automake to tell side-effect + # dependency tracking mechanisms from slower ones. + +dashmstdout) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout, regardless of -o. + "$@" || exit $? + + # Remove the call to Libtool. + if test "$libtool" = yes; then + while test "X$1" != 'X--mode=compile'; do + shift + done + shift + fi + + # Remove '-o $object'. + IFS=" " + for arg + do + case $arg in + -o) + shift + ;; + $object) + shift + ;; + *) + set fnord "$@" "$arg" + shift # fnord + shift # $arg + ;; + esac + done + + test -z "$dashmflag" && dashmflag=-M + # Require at least two characters before searching for ':' + # in the target name. This is to cope with DOS-style filenames: + # a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise. + "$@" $dashmflag | + sed 's:^['"$tab"' ]*[^:'"$tab"' ][^:][^:]*\:['"$tab"' ]*:'"$object"'\: :' > "$tmpdepfile" + rm -f "$depfile" + cat < "$tmpdepfile" > "$depfile" + tr ' ' "$nl" < "$tmpdepfile" | \ +## Some versions of the HPUX 10.20 sed can't process this invocation +## correctly. Breaking it into two sed invocations is a workaround. + sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +dashXmstdout) + # This case only exists to satisfy depend.m4. It is never actually + # run, as this mode is specially recognized in the preamble. + exit 1 + ;; + +makedepend) + "$@" || exit $? + # Remove any Libtool call + if test "$libtool" = yes; then + while test "X$1" != 'X--mode=compile'; do + shift + done + shift + fi + # X makedepend + shift + cleared=no eat=no + for arg + do + case $cleared in + no) + set ""; shift + cleared=yes ;; + esac + if test $eat = yes; then + eat=no + continue + fi + case "$arg" in + -D*|-I*) + set fnord "$@" "$arg"; shift ;; + # Strip any option that makedepend may not understand. Remove + # the object too, otherwise makedepend will parse it as a source file. + -arch) + eat=yes ;; + -*|$object) + ;; + *) + set fnord "$@" "$arg"; shift ;; + esac + done + obj_suffix=`echo "$object" | sed 's/^.*\././'` + touch "$tmpdepfile" + ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@" + rm -f "$depfile" + # makedepend may prepend the VPATH from the source file name to the object. + # No need to regex-escape $object, excess matching of '.' is harmless. + sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile" + sed '1,2d' "$tmpdepfile" | tr ' ' "$nl" | \ +## Some versions of the HPUX 10.20 sed can't process this invocation +## correctly. Breaking it into two sed invocations is a workaround. + sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" "$tmpdepfile".bak + ;; + +cpp) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout. + "$@" || exit $? + + # Remove the call to Libtool. + if test "$libtool" = yes; then + while test "X$1" != 'X--mode=compile'; do + shift + done + shift + fi + + # Remove '-o $object'. + IFS=" " + for arg + do + case $arg in + -o) + shift + ;; + $object) + shift + ;; + *) + set fnord "$@" "$arg" + shift # fnord + shift # $arg + ;; + esac + done + + "$@" -E | + sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ + -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' | + sed '$ s: \\$::' > "$tmpdepfile" + rm -f "$depfile" + echo "$object : \\" > "$depfile" + cat < "$tmpdepfile" >> "$depfile" + sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +msvisualcpp) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout. + "$@" || exit $? + + # Remove the call to Libtool. + if test "$libtool" = yes; then + while test "X$1" != 'X--mode=compile'; do + shift + done + shift + fi + + IFS=" " + for arg + do + case "$arg" in + -o) + shift + ;; + $object) + shift + ;; + "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI") + set fnord "$@" + shift + shift + ;; + *) + set fnord "$@" "$arg" + shift + shift + ;; + esac + done + "$@" -E 2>/dev/null | + sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile" + rm -f "$depfile" + echo "$object : \\" > "$depfile" + sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile" + echo "$tab" >> "$depfile" + sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +msvcmsys) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +none) + exec "$@" + ;; + +*) + echo "Unknown depmode $depmode" 1>&2 + exit 1 + ;; +esac + +exit 0 + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/install-sh b/install-sh new file mode 100755 index 00000000..a9244eb0 --- /dev/null +++ b/install-sh @@ -0,0 +1,527 @@ +#!/bin/sh +# install - install a program, script, or datafile + +scriptversion=2011-01-19.21; # UTC + +# This originates from X11R5 (mit/util/scripts/install.sh), which was +# later released in X11R6 (xc/config/util/install.sh) with the +# following copyright and license. +# +# Copyright (C) 1994 X Consortium +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- +# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +# Except as contained in this notice, the name of the X Consortium shall not +# be used in advertising or otherwise to promote the sale, use or other deal- +# ings in this Software without prior written authorization from the X Consor- +# tium. +# +# +# FSF changes to this file are in the public domain. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# `make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. + +nl=' +' +IFS=" "" $nl" + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit=${DOITPROG-} +if test -z "$doit"; then + doit_exec=exec +else + doit_exec=$doit +fi + +# Put in absolute file names if you don't have them in your path; +# or use environment vars. + +chgrpprog=${CHGRPPROG-chgrp} +chmodprog=${CHMODPROG-chmod} +chownprog=${CHOWNPROG-chown} +cmpprog=${CMPPROG-cmp} +cpprog=${CPPROG-cp} +mkdirprog=${MKDIRPROG-mkdir} +mvprog=${MVPROG-mv} +rmprog=${RMPROG-rm} +stripprog=${STRIPPROG-strip} + +posix_glob='?' +initialize_posix_glob=' + test "$posix_glob" != "?" || { + if (set -f) 2>/dev/null; then + posix_glob= + else + posix_glob=: + fi + } +' + +posix_mkdir= + +# Desired mode of installed file. +mode=0755 + +chgrpcmd= +chmodcmd=$chmodprog +chowncmd= +mvcmd=$mvprog +rmcmd="$rmprog -f" +stripcmd= + +src= +dst= +dir_arg= +dst_arg= + +copy_on_change=false +no_target_directory= + +usage="\ +Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE + or: $0 [OPTION]... SRCFILES... DIRECTORY + or: $0 [OPTION]... -t DIRECTORY SRCFILES... + or: $0 [OPTION]... -d DIRECTORIES... + +In the 1st form, copy SRCFILE to DSTFILE. +In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. +In the 4th, create DIRECTORIES. + +Options: + --help display this help and exit. + --version display version info and exit. + + -c (ignored) + -C install only if different (preserve the last data modification time) + -d create directories instead of installing files. + -g GROUP $chgrpprog installed files to GROUP. + -m MODE $chmodprog installed files to MODE. + -o USER $chownprog installed files to USER. + -s $stripprog installed files. + -t DIRECTORY install into DIRECTORY. + -T report an error if DSTFILE is a directory. + +Environment variables override the default commands: + CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG + RMPROG STRIPPROG +" + +while test $# -ne 0; do + case $1 in + -c) ;; + + -C) copy_on_change=true;; + + -d) dir_arg=true;; + + -g) chgrpcmd="$chgrpprog $2" + shift;; + + --help) echo "$usage"; exit $?;; + + -m) mode=$2 + case $mode in + *' '* | *' '* | *' +'* | *'*'* | *'?'* | *'['*) + echo "$0: invalid mode: $mode" >&2 + exit 1;; + esac + shift;; + + -o) chowncmd="$chownprog $2" + shift;; + + -s) stripcmd=$stripprog;; + + -t) dst_arg=$2 + # Protect names problematic for `test' and other utilities. + case $dst_arg in + -* | [=\(\)!]) dst_arg=./$dst_arg;; + esac + shift;; + + -T) no_target_directory=true;; + + --version) echo "$0 $scriptversion"; exit $?;; + + --) shift + break;; + + -*) echo "$0: invalid option: $1" >&2 + exit 1;; + + *) break;; + esac + shift +done + +if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then + # When -d is used, all remaining arguments are directories to create. + # When -t is used, the destination is already specified. + # Otherwise, the last argument is the destination. Remove it from $@. + for arg + do + if test -n "$dst_arg"; then + # $@ is not empty: it contains at least $arg. + set fnord "$@" "$dst_arg" + shift # fnord + fi + shift # arg + dst_arg=$arg + # Protect names problematic for `test' and other utilities. + case $dst_arg in + -* | [=\(\)!]) dst_arg=./$dst_arg;; + esac + done +fi + +if test $# -eq 0; then + if test -z "$dir_arg"; then + echo "$0: no input file specified." >&2 + exit 1 + fi + # It's OK to call `install-sh -d' without argument. + # This can happen when creating conditional directories. + exit 0 +fi + +if test -z "$dir_arg"; then + do_exit='(exit $ret); exit $ret' + trap "ret=129; $do_exit" 1 + trap "ret=130; $do_exit" 2 + trap "ret=141; $do_exit" 13 + trap "ret=143; $do_exit" 15 + + # Set umask so as not to create temps with too-generous modes. + # However, 'strip' requires both read and write access to temps. + case $mode in + # Optimize common cases. + *644) cp_umask=133;; + *755) cp_umask=22;; + + *[0-7]) + if test -z "$stripcmd"; then + u_plus_rw= + else + u_plus_rw='% 200' + fi + cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; + *) + if test -z "$stripcmd"; then + u_plus_rw= + else + u_plus_rw=,u+rw + fi + cp_umask=$mode$u_plus_rw;; + esac +fi + +for src +do + # Protect names problematic for `test' and other utilities. + case $src in + -* | [=\(\)!]) src=./$src;; + esac + + if test -n "$dir_arg"; then + dst=$src + dstdir=$dst + test -d "$dstdir" + dstdir_status=$? + else + + # Waiting for this to be detected by the "$cpprog $src $dsttmp" command + # might cause directories to be created, which would be especially bad + # if $src (and thus $dsttmp) contains '*'. + if test ! -f "$src" && test ! -d "$src"; then + echo "$0: $src does not exist." >&2 + exit 1 + fi + + if test -z "$dst_arg"; then + echo "$0: no destination specified." >&2 + exit 1 + fi + dst=$dst_arg + + # If destination is a directory, append the input filename; won't work + # if double slashes aren't ignored. + if test -d "$dst"; then + if test -n "$no_target_directory"; then + echo "$0: $dst_arg: Is a directory" >&2 + exit 1 + fi + dstdir=$dst + dst=$dstdir/`basename "$src"` + dstdir_status=0 + else + # Prefer dirname, but fall back on a substitute if dirname fails. + dstdir=` + (dirname "$dst") 2>/dev/null || + expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$dst" : 'X\(//\)[^/]' \| \ + X"$dst" : 'X\(//\)$' \| \ + X"$dst" : 'X\(/\)' \| . 2>/dev/null || + echo X"$dst" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q' + ` + + test -d "$dstdir" + dstdir_status=$? + fi + fi + + obsolete_mkdir_used=false + + if test $dstdir_status != 0; then + case $posix_mkdir in + '') + # Create intermediate dirs using mode 755 as modified by the umask. + # This is like FreeBSD 'install' as of 1997-10-28. + umask=`umask` + case $stripcmd.$umask in + # Optimize common cases. + *[2367][2367]) mkdir_umask=$umask;; + .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; + + *[0-7]) + mkdir_umask=`expr $umask + 22 \ + - $umask % 100 % 40 + $umask % 20 \ + - $umask % 10 % 4 + $umask % 2 + `;; + *) mkdir_umask=$umask,go-w;; + esac + + # With -d, create the new directory with the user-specified mode. + # Otherwise, rely on $mkdir_umask. + if test -n "$dir_arg"; then + mkdir_mode=-m$mode + else + mkdir_mode= + fi + + posix_mkdir=false + case $umask in + *[123567][0-7][0-7]) + # POSIX mkdir -p sets u+wx bits regardless of umask, which + # is incompatible with FreeBSD 'install' when (umask & 300) != 0. + ;; + *) + tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ + trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0 + + if (umask $mkdir_umask && + exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1 + then + if test -z "$dir_arg" || { + # Check for POSIX incompatibilities with -m. + # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or + # other-writeable bit of parent directory when it shouldn't. + # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. + ls_ld_tmpdir=`ls -ld "$tmpdir"` + case $ls_ld_tmpdir in + d????-?r-*) different_mode=700;; + d????-?--*) different_mode=755;; + *) false;; + esac && + $mkdirprog -m$different_mode -p -- "$tmpdir" && { + ls_ld_tmpdir_1=`ls -ld "$tmpdir"` + test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" + } + } + then posix_mkdir=: + fi + rmdir "$tmpdir/d" "$tmpdir" + else + # Remove any dirs left behind by ancient mkdir implementations. + rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null + fi + trap '' 0;; + esac;; + esac + + if + $posix_mkdir && ( + umask $mkdir_umask && + $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" + ) + then : + else + + # The umask is ridiculous, or mkdir does not conform to POSIX, + # or it failed possibly due to a race condition. Create the + # directory the slow way, step by step, checking for races as we go. + + case $dstdir in + /*) prefix='/';; + [-=\(\)!]*) prefix='./';; + *) prefix='';; + esac + + eval "$initialize_posix_glob" + + oIFS=$IFS + IFS=/ + $posix_glob set -f + set fnord $dstdir + shift + $posix_glob set +f + IFS=$oIFS + + prefixes= + + for d + do + test X"$d" = X && continue + + prefix=$prefix$d + if test -d "$prefix"; then + prefixes= + else + if $posix_mkdir; then + (umask=$mkdir_umask && + $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break + # Don't fail if two instances are running concurrently. + test -d "$prefix" || exit 1 + else + case $prefix in + *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; + *) qprefix=$prefix;; + esac + prefixes="$prefixes '$qprefix'" + fi + fi + prefix=$prefix/ + done + + if test -n "$prefixes"; then + # Don't fail if two instances are running concurrently. + (umask $mkdir_umask && + eval "\$doit_exec \$mkdirprog $prefixes") || + test -d "$dstdir" || exit 1 + obsolete_mkdir_used=true + fi + fi + fi + + if test -n "$dir_arg"; then + { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && + { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && + { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || + test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 + else + + # Make a couple of temp file names in the proper directory. + dsttmp=$dstdir/_inst.$$_ + rmtmp=$dstdir/_rm.$$_ + + # Trap to clean up those temp files at exit. + trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 + + # Copy the file name to the temp name. + (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") && + + # and set any options; do chmod last to preserve setuid bits. + # + # If any of these fail, we abort the whole thing. If we want to + # ignore errors from any of these, just make sure not to ignore + # errors from the above "$doit $cpprog $src $dsttmp" command. + # + { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && + { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && + { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && + { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && + + # If -C, don't bother to copy if it wouldn't change the file. + if $copy_on_change && + old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && + new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && + + eval "$initialize_posix_glob" && + $posix_glob set -f && + set X $old && old=:$2:$4:$5:$6 && + set X $new && new=:$2:$4:$5:$6 && + $posix_glob set +f && + + test "$old" = "$new" && + $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 + then + rm -f "$dsttmp" + else + # Rename the file to the real destination. + $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || + + # The rename failed, perhaps because mv can't rename something else + # to itself, or perhaps because mv is so ancient that it does not + # support -f. + { + # Now remove or move aside any old file at destination location. + # We try this two ways since rm can't unlink itself on some + # systems and the destination file might be busy for other + # reasons. In this case, the final cleanup might fail but the new + # file should still install successfully. + { + test ! -f "$dst" || + $doit $rmcmd -f "$dst" 2>/dev/null || + { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && + { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } + } || + { echo "$0: cannot unlink or rename $dst" >&2 + (exit 1); exit 1 + } + } && + + # Now rename the file to the real destination. + $doit $mvcmd "$dsttmp" "$dst" + } + fi || exit 1 + + trap '' 0 + fi +done + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/lib/Makefile.am b/lib/Makefile.am new file mode 100644 index 00000000..aaa87abf --- /dev/null +++ b/lib/Makefile.am @@ -0,0 +1,11 @@ +noinst_LIBRARIES = libebus.a liblog.a + +libebus_a_SOURCES = ebus-decode.c ebus-decode.h \ + ebus-cmd.c ebus-cmd.h \ + ebus-bus.c ebus-bus.h + +liblog_a_SOURCES = log.c log.h + +distclean-local: + -rm -f Makefile.in + diff --git a/lib/ebus-bus.c b/lib/ebus-bus.c new file mode 100644 index 00000000..5b326514 --- /dev/null +++ b/lib/ebus-bus.c @@ -0,0 +1,1195 @@ +/* + * Copyright (C) Roland Jax 2012-2013 + * + * This file is part of ebusd. + * + * ebusd is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ebusd is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ebusd. If not, see http://www.gnu.org/licenses/. + */ + +/** + * @file ebus-bus.c + * @brief ebus communication functions + * @author roland.jax@liwest.at + * @version 0.1 + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "ebus-decode.h" +#include "ebus-cmd.h" +#include "ebus-bus.h" + + + +static struct send_data send_data; + +static struct recv_data recv_data; + +static int rawdump = NO; +static int showraw = NO; + +static int get_retry = EBUS_GET_RETRY; +static int skip_ack = EBUS_SKIP_ACK; +static long max_wait = EBUS_MAX_WAIT; +static int send_retry = EBUS_SEND_RETRY; +static int print_size = EBUS_PRINT_SIZE; + +static unsigned char qq = EBUS_QQ; + +static unsigned char ack = EBUS_ACK; +static unsigned char nak = EBUS_NAK; +static unsigned char syn = EBUS_SYN; + +static int sfd; +static struct termios oldtio; + +static FILE *rawfp = NULL; + + + +void +eb_set_rawdump(int dump) +{ + rawdump = dump; +} + +void +eb_set_showraw(int show) +{ + showraw = show; +} + +void +eb_set_qq(unsigned char src) +{ + qq = src; +} + +void +eb_set_get_retry(int retry) +{ + get_retry = retry; +} + +void +eb_set_skip_ack(int skip) +{ + skip_ack = skip; +} + +void +eb_set_max_wait(long usec) +{ + max_wait = usec; +} + +void +eb_set_send_retry(int retry) +{ + send_retry = retry; +} + +void +eb_set_print_size(int size) +{ + print_size = size; +} + + +int +eb_diff_time(const struct timeval *tact, const struct timeval *tlast, struct timeval *tdiff) +{ + long diff; + + diff = (tact->tv_usec + 1000000 * tact->tv_sec) - + (tlast->tv_usec + 1000000 * tlast->tv_sec); + + tdiff->tv_sec = diff / 1000000; + tdiff->tv_usec = diff % 1000000; + + return (diff < 0); +} + + + +int +eb_raw_file_open(const char *file) +{ + rawfp = fopen(file, "w"); + err_ret_if(rawfp == NULL, -1); + + return 0; +} + +int +eb_raw_file_close(void) +{ + int ret; + + ret = fflush(rawfp); + err_ret_if(ret == EOF, -1); + + ret = fclose(rawfp); + err_ret_if(ret == EOF, -1); + + return 0; +} + +int +eb_raw_file_write(const unsigned char *buf, int buflen) +{ + int ret, i; + + for (i = 0; i < buflen; i++) { + ret = fputc(buf[i], rawfp); + err_ret_if(ret == EOF, -1); + } + + ret = fflush(rawfp); + err_ret_if(ret == EOF, -1); + + return 0; +} + + + +int +eb_serial_valid() +{ + int serial; + + if (ioctl(sfd, TIOCMGET, &serial) < 0) + return -1; + else + return 0; +} + +int +eb_serial_open(const char *dev, int *fd) +{ + int ret; + struct termios newtio; + + sfd = open(dev, O_RDWR | O_NOCTTY | O_NDELAY); + //~ err_ret_if(sfd < 0, -1); + if (sfd < 0) + return -1; + + ret = fcntl(sfd, F_SETFL, 0); + err_ret_if(ret < 0, -1); + + /* check if the usb device is working */ + ret = eb_serial_valid(); + err_ret_if(ret < 0, -2); + + /* save current settings of serial port */ + ret = tcgetattr(sfd, &oldtio); + err_ret_if(ret < 0, -1); + + memset(&newtio, '\0', sizeof(newtio)); + + newtio.c_cflag = SERIAL_BAUDRATE | CS8 | CLOCAL | CREAD; + newtio.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); + newtio.c_iflag = IGNPAR; + newtio.c_oflag &= ~OPOST; + + newtio.c_cc[VMIN] = 1; + newtio.c_cc[VTIME] = 0; + + ret = tcflush(sfd, TCIFLUSH); + err_ret_if(ret < 0, -1); + + /* activate new settings of serial port */ + ret = tcsetattr(sfd, TCSANOW, &newtio); + err_ret_if(ret < 0, -1); + + *fd = sfd; + + return 0; +} + +int +eb_serial_close(void) +{ + int ret; + + /* activate old settings of serial port */ + ret = tcsetattr(sfd, TCSANOW, &oldtio); + /* Ignore this error otherwise USB port may stay unclosed */ + /* err_ret_if(ret < 0, -1); */ + + /* Close file descriptor from serial device */ + ret = close(sfd); + err_ret_if(ret < 0, -1); + + return 0; +} + +int +eb_serial_send(const unsigned char *buf, int buflen) +{ + int ret, val; + + /* write msg to ebus device */ + val = write(sfd, buf, buflen); + err_ret_if(val < 0, -1); + + ret = tcflush(sfd, TCIOFLUSH); + err_ret_if(ret < 0, -1); + + return val; +} + +int +eb_serial_recv(unsigned char *buf, int *buflen) +{ + int ret; + + /* flush deactivated - brings us an error */ + /* tcflush(sfd, TCIOFLUSH); */ + + /* read msg from ebus device */ + *buflen = read(sfd, buf, *buflen); + err_if(*buflen < 0); + + if (*buflen < 0) { + log_print(L_WAR, "error read serial device"); + return -1; + } + + if (*buflen > SERIAL_BUFSIZE) { + log_print(L_WAR, "read data len > %d", SERIAL_BUFSIZE); + return -3; + } + + /* print bus */ + if (showraw == YES) + eb_print_hex(buf, *buflen); + + /* dump raw data*/ + if (rawdump == YES) { + ret = eb_raw_file_write(buf, *buflen); + if (ret < 0) + log_print(L_WAR, "can't write rawdata"); + } + + return 0; +} + + + +void +eb_print_result(void) +{ + int i; + //fprintf(stdout, ">>> "); + for (i = 0; i < recv_data.len; i++) + fprintf(stdout, " %02x", recv_data.msg[i]); + fprintf(stdout, "\n"); +} + +void +eb_print_hex(const unsigned char *buf, int buflen) +{ + int i, j, k; + char msg[3 * print_size]; + + memset(msg, '\0', sizeof(msg)); + k = 1; + + for (i = 0, j = 0; i < buflen; i++, j++) { + sprintf(&msg[3 * j], " %02x", buf[i]); + + if (j + 1 == print_size) { + log_print(L_EBH, "%d%s", k, msg); + memset(msg, '\0', sizeof(msg)); + j = -1; + k++; + } + } + + if (j > 0) { + if (k > 1) + log_print(L_EBH, "%d%s", k, msg); + else + log_print(L_EBH, " %s", msg); + } +} + + + +void +eb_recv_data_get(unsigned char *buf, int *buflen) +{ + memcpy(buf, recv_data.msg, recv_data.len); + *buflen = recv_data.len; +} + +void +eb_recv_data_prepare(const unsigned char *buf, int buflen) +{ + unsigned char tmp[SERIAL_BUFSIZE]; + int tmplen, crc; + + memset(tmp, '\0', sizeof(tmp)); + + /* reset struct */ + memset(&recv_data, '\0', sizeof(recv_data)); + + /* set recv_data.msg_esc */ + memcpy(&recv_data.msg_esc[0], buf, buflen); + recv_data.len_esc = buflen; + + /* set recv_data.crc_calc and .crc_recv */ + if (buf[buflen - 2] == EBUS_SYN_ESC_A9) { + recv_data.crc_calc = eb_calc_crc(buf, buflen - 2); + if (buf[buflen - 1] == EBUS_SYN_ESC_01) + recv_data.crc_recv = EBUS_SYN; + else + recv_data.crc_recv = EBUS_SYN_ESC_A9; + + crc = 2; + + } else { + recv_data.crc_calc = eb_calc_crc(buf, buflen - 1); + recv_data.crc_recv = buf[buflen - 1]; + + crc = 1; + } + + /* set recv_data.msg */ + memcpy(tmp, buf, buflen - crc); + tmplen = buflen - crc; + + eb_unesc(tmp, &tmplen); + + memcpy(&recv_data.msg[0], tmp, tmplen); + recv_data.len = tmplen; + +} + +int +eb_recv_data(unsigned char *buf, int *buflen) +{ + unsigned char tmp[SERIAL_BUFSIZE], msg[SERIAL_BUFSIZE]; + int tmplen, msglen, ret, i, esc, found; + + memset(msg, '\0', sizeof(msg)); + msglen = 0; + + esc = 0; + found = 99; + + /* do until found necessary string*/ + do { + memset(tmp, '\0', sizeof(tmp)); + tmplen = sizeof(tmp); + + ret = eb_serial_recv(tmp, &tmplen); + if (ret < 0) + return -1; + + if (tmplen > 0) { + + /* preset tmp buffer with not read bytes from get_bus */ + if (*buflen > 0) { + + /* save temporarily tmp in msg buffer */ + memcpy(msg, tmp, tmplen); + msglen = tmplen; + + /* copy input data into tmp buffer */ + memset(tmp, '\0', sizeof(tmp)); + memcpy(tmp, buf, *buflen); + tmplen = *buflen; + + /* set input data buffer len to 0 */ + *buflen = 0; + + /* copy saved bus data back to tmp buffer */ + memcpy(&tmp[tmplen], msg, msglen); + tmplen += msglen; + + /* reset msg buffer */ + memset(msg, '\0', sizeof(msg)); + msglen = 0; + } + + + i = 0; + while (i < tmplen) { + + msg[msglen] = tmp[i]; + msglen++; + + /* SYN */ + if (msg[0] == EBUS_SYN) { + found = -2; + break; + } + + /* get end of message */ + if (msglen > 1) { + + if (msg[msglen - 1] == EBUS_SYN_ESC_A9) + esc++; + + if (msglen == (2 + msg[0] + esc)) { + found = 0; + break; + } + } + + /* + * something went wrong - plz tell me + * we got some SYN during answer or + * msglen is out of spec. 1 + */ + if (msg[msglen] == EBUS_SYN || msg[0] > 16) { + + found = -3; + break; + } + + i++; + } + } + } while (found == 99); + + *buflen = msglen; + + memset(buf, '\0', sizeof(buf)); + for (i = 0; i < msglen; i++) + buf[i] = msg[i]; + + return found; +} + + + +int +eb_bus_wait_syn(int *skip) +{ + unsigned char buf[SERIAL_BUFSIZE]; + int buflen, ret, i, found; + + found = 99; + + /* do until SYN read*/ + do { + memset(buf, '\0', sizeof(buf)); + buflen = sizeof(buf); + + ret = eb_serial_recv(buf, &buflen); + if (ret < 0) + return -1; + + if (buflen > 0) { + + i = 0; + while (i < buflen) { + /* break if byte = SYN and it is last byte */ + if (buf[i] == EBUS_SYN && (i + 1) == buflen) { + found = 1; + break; + } + i++; + } + + if (*skip > 0) + *skip -= 1; + } + } while (found == 99); + + return 0; +} + +int +eb_bus_wait(void) +{ + unsigned char buf[SERIAL_BUFSIZE]; + int buflen, ret, skip, retry; + struct timeval tact, tlast, tdiff; + + skip = 0; + retry = 0; + + do { + ret = eb_bus_wait_syn(&skip); + if (ret < 0) + return -1; + + /* remember start time */ + gettimeofday(&tlast, NULL); + + /* send QQ */ + ret = eb_serial_send(&qq, 1); + if (ret < 0) + return -1; + + gettimeofday(&tact, NULL); + eb_diff_time(&tact, &tlast, &tdiff); + + /* wait ~4200 usec */ + if (max_wait - tdiff.tv_usec > 0.0 && + max_wait - tdiff.tv_usec <= max_wait) + usleep(max_wait - tdiff.tv_usec); + else + log_print(L_WAR, "usleep out of range - skipped"); + + /* receive 1 byte - must be QQ */ + memset(buf, '\0', sizeof(buf)); + buflen = sizeof(buf); + ret = eb_serial_recv(buf, &buflen); + if (ret < 0) + return -1; + + /* is sent and read qq byte is equal */ + if (buf[0] == qq && buflen == 1) + return 0; + + retry++; + skip = skip_ack + retry; + + } while (retry < get_retry); + + /* reached max retry */ + return 1; +} + +int +eb_bus_free(void) +{ + int ret, skip; + ret = 0; + skip = 0; + + ret = eb_serial_send(&syn, 1); + if (ret < 0) + return -1; + + ret = eb_bus_wait_syn(&skip); + if (ret < 0) + return -1; + + return 0; +} + + + +int +eb_send_data_get_ack(unsigned char *buf, int *buflen) +{ + unsigned char tmp[SERIAL_BUFSIZE]; + int tmplen, ret, i, j, found; + + j = 0; + found = 99; + + /* do until found necessary string */ + do { + memset(tmp, '\0', sizeof(tmp)); + tmplen = sizeof(tmp); + + ret = eb_serial_recv(tmp, &tmplen); + if (ret < 0) + return -1; + + if (tmplen > 0) { + i = 0; + while (i < tmplen) { + + /* compare recv with sent - is this possible */ + if (tmp[i] != buf[j] && j < *buflen) + return -2; + + /* compare only slaves answer */ + if (j > (*buflen - 1)) { + + /* ACK */ + if (tmp[i] == EBUS_ACK) + found = 0; + + /* NAK */ + else if (tmp[i] == EBUS_NAK) + found = 0; + + /* SYN */ + else if (tmp[i] == EBUS_SYN) + found = -3; + + /* ??? */ + else + found = -4; + i++; + break; + + } + i++; + j++; + } + } + } while (found == 99); + + *buflen = tmplen - i; + + memset(buf, '\0', sizeof(buf)); + for (j = 0; i < tmplen; i++, j++) + buf[j] = tmp[i]; + + return found; +} + +void +eb_send_data_prepare(const unsigned char *buf, int buflen) +{ + unsigned char crc[2], tmp[SERIAL_BUFSIZE]; + int tmplen, crclen; + + /* reset struct */ + memset(&send_data, '\0', sizeof(send_data)); + + /* set send_data.msg */ + memcpy(&send_data.msg[0], buf, buflen); + send_data.len = buflen; + + + /* set send_data.msg_esc + send_data.crc */ + memset(tmp, '\0', sizeof(tmp)); + memcpy(tmp, buf, buflen); + tmplen = buflen; + + eb_esc(tmp, &tmplen); + + memcpy(&send_data.msg_esc[0], &qq, 1); + memcpy(&send_data.msg_esc[1], tmp, tmplen); + tmplen++; + + memset(crc, '\0', sizeof(crc)); + send_data.crc = eb_calc_crc(&send_data.msg_esc[0], tmplen); + crc[0] = send_data.crc; + crclen = 1; + + if (crc[0] == EBUS_SYN || crc[0] == EBUS_SYN_ESC_A9) { + /* esc crc */ + eb_esc(crc, &crclen); + send_data.msg_esc[tmplen] = crc[0]; + tmplen++; + send_data.msg_esc[tmplen] = crc[1]; + tmplen++; + } else { + send_data.msg_esc[tmplen] = crc[0]; + tmplen++; + } + + send_data.len_esc = tmplen; + +} + +int +eb_send_data(const unsigned char *buf, int buflen, int type, unsigned char *bus, int *buslen) +{ + unsigned char tmp[SERIAL_BUFSIZE]; + int tmplen, ret, val; + + ret = 0; + + eb_send_data_prepare(buf, buflen); + + /* fetch AA and send QQ */ + ret = eb_bus_wait(); + if (ret != 0) + return -1; + + memcpy(bus, send_data.msg_esc, send_data.len_esc); + *buslen = send_data.len_esc; + + /* send message to slave */ + ret = eb_serial_send(&send_data.msg_esc[1], send_data.len_esc - 1); + if (ret < 0) + return -1; + + if (type == EBUS_MSG_BROADCAST) { + /* free bus */ + ret = eb_bus_free(); + if (ret < 0) + return -1; + + return 0; + } + + /* get data from bus (we got our sent message too) */ + memset(tmp, '\0', sizeof(tmp)); + memcpy(tmp, &send_data.msg_esc[1], send_data.len_esc - 1); + tmplen = send_data.len_esc - 1; + + ret = eb_send_data_get_ack(tmp, &tmplen); + + if (ret < 0 || ret > 1) { + /* free bus */ + ret = eb_bus_free(); + if (ret < 0) + return -1; + + return -1; + } + + /* first answer from slave is NAK - send message again (inkl. QQ) */ + if (ret == 1) { + + memcpy(&bus[*buslen], &nak, 1); + *buslen += 1; + + memcpy(&bus[*buslen], send_data.msg_esc, send_data.len_esc); + *buslen += send_data.len_esc; + + /* send message to slave */ + ret = eb_serial_send(&send_data.msg_esc[0], send_data.len_esc); + if (ret < 0) + return -1; + + /* get ack from bus (we got our sent message too) */ + memset(tmp, '\0', sizeof(tmp)); + memcpy(tmp, &send_data.msg_esc[0], send_data.len_esc); + tmplen = send_data.len_esc; + + ret = eb_send_data_get_ack(tmp, &tmplen); + + if (ret == 1) { + + memcpy(&bus[*buslen], &nak, 1); + *buslen += 1; + + /* free bus */ + ret = eb_bus_free(); + if (ret < 0) + return -1; + + return -1; + } + + } + + memcpy(&bus[*buslen], &ack, 1); + *buslen += 1; + + if (type == EBUS_MSG_MASTER_MASTER) { + val = ret; + + /* free bus */ + ret = eb_bus_free(); + if (ret < 0) + return -1; + + return val; + } + + /* get data - dont reset buffer */ + ret = eb_recv_data(tmp, &tmplen); + if (ret < 0) + return -1; + + eb_recv_data_prepare(tmp, tmplen); + + memcpy(&bus[*buslen], recv_data.msg_esc, recv_data.len_esc); + *buslen += recv_data.len_esc; + + /* check crc's from recv_data */ + if (recv_data.crc_calc != recv_data.crc_recv) { + + memcpy(&bus[*buslen], &nak, 1); + *buslen += 1; + + /* send message to slave */ + ret = eb_serial_send(&nak, 1); + if (ret < 0) + return -1; + + /* get data from bus (we got our sent message too) */ + memset(tmp, '\0', sizeof(tmp)); + tmplen = 0; + + ret = eb_send_data_get_ack(tmp, &tmplen); + + /* we compare against nak ! */ + if (ret != 1) { + /* free bus */ + ret = eb_bus_free(); + if (ret < 0) + return -1; + + return -1; + } + + /* get data - don't reset buffer */ + ret = eb_recv_data(tmp, &tmplen); + if (ret < 0) + return -1; + + eb_recv_data_prepare(tmp, tmplen); + + memcpy(&bus[*buslen], recv_data.msg_esc, recv_data.len_esc); + *buslen += recv_data.len_esc; + + } + + if (recv_data.crc_calc != recv_data.crc_recv) { + + memcpy(&bus[*buslen], &nak, 1); + *buslen += 1; + + ret = eb_serial_send(&nak, 1); + if (ret < 0) + return -1; + + val = 1; + } else { + + memcpy(&bus[*buslen], &ack, 1); + *buslen += 1; + + ret = eb_serial_send(&ack, 1); + if (ret < 0) + return -1; + + val = 0; + } + + /* free bus */ + ret = eb_bus_free(); + if (ret < 0) + return -1; + + return val; +} + + + +void +eb_execute(int id, char *data, char *buf, int *buflen) +{ + unsigned char msg[SERIAL_BUFSIZE], hlp[CMD_DATA_SIZE], bus[TMP_BUFSIZE]; + char tmp[TMP_BUFSIZE]; + int ret, msglen, buslen, msgtype, retry, cycdata, pos, len; + + memset(msg, '\0', sizeof(msg)); + msglen = sizeof(msg); + msgtype = eb_cmd_get_s_type(id); + + cycdata = NO; + ret = -1; + retry = 0; + + if (eb_cmd_check_type(id, "cyc") == YES) { + cycdata = YES; + } else { + /* prepare command - if prepare failed buflen > 0 */ + eb_cmd_prepare(id, data, msg, &msglen, buf); + /* eb_print_hex(msg, msglen); */ + } + + if (cycdata == NO && strlen(buf) == 0) { + /* send data to bus */ + while (ret < 0 && retry < send_retry) { + if (retry > 0) + log_print(L_NOT, "send retry: %d", retry); + + memset(bus, '\0', sizeof(bus)); + buslen = 0; + + ret = eb_send_data(msg, msglen, msgtype, bus, &buslen); + + if (buslen > 0) + eb_print_hex(bus, buslen); + + retry++; + } + } + + /* handle answer for sent messages */ + if (cycdata == NO && ret >= 0) { + + if (msgtype == EBUS_MSG_BROADCAST) + strcpy(buf, "broadcast done\n"); + + if (msgtype == EBUS_MSG_MASTER_MASTER) { + if (ret == 0) + strcpy(buf, "ACK\n"); + else + strcpy(buf, "NAK\n"); + } + + if (msgtype == EBUS_MSG_MASTER_SLAVE) { + if (ret == 0) { + memset(msg, '\0', sizeof(msg)); + msglen = sizeof(msg); + eb_recv_data_get(msg, &msglen); + /* eb_print_hex(msg, msglen); */ + + /* decode */ + if (eb_cmd_check_type(id, "set") == YES) { + strcpy(buf, "ACK\n"); + } else { + eb_cmd_decode(id, CMD_PART_SD, data, msg, buf); + if (strlen(buf) > 0) + strncat(buf, "\n", 1); + } + + } else { + strcpy(buf, "NAK\n"); + } + } + + } else if (cycdata == YES) { + memset(tmp, '\0', sizeof(tmp)); + memset(hlp, '\0', sizeof(hlp)); + + eb_cmd_get_cyc_buf(id, msg, &msglen); + + /* CMD_PART_MD */ + pos = 4; + len = 1 + (int) msg[4]; + memcpy(hlp, &msg[pos], len); + + + eb_cmd_decode(id, CMD_PART_MD, data, hlp, tmp); + strncpy(buf, tmp, strlen(tmp)); + + if (msgtype != EBUS_MSG_BROADCAST) { + /* CMD_PART_SA */ + memset(tmp, '\0', sizeof(tmp)); + memset(hlp, '\0', sizeof(hlp)); + + pos = 5 + (int) msg[4] + 1; + len = 2; + hlp[0] = 0x01; + memcpy(&hlp[1], &msg[pos], 1); + + eb_cmd_decode(id, CMD_PART_SA, data, hlp, tmp); + strncat(buf, tmp, strlen(tmp)); + } + + if (msgtype == EBUS_MSG_MASTER_SLAVE) { + /* CMD_PART_SD */ + memset(tmp, '\0', sizeof(tmp)); + memset(hlp, '\0', sizeof(hlp)); + + pos = 5 + (int) msg[4] + 2; + len = 1 + (int) msg[pos]; + memcpy(hlp, &msg[pos], len); + + eb_cmd_decode(id, CMD_PART_SD, data, hlp, tmp); + strncat(buf, tmp, strlen(tmp)); + memset(tmp, '\0', sizeof(tmp)); + memset(hlp, '\0', sizeof(hlp)); + + /* CMD_PART_MA */ + pos = 5 + (int) msg[4] + 2 +(int) msg[pos] + 2; + len = 2; + hlp[0] = 0x01; + memcpy(&hlp[1], &msg[pos], 1); + + eb_cmd_decode(id, CMD_PART_MA, data, hlp, tmp); + strncat(buf, tmp, strlen(tmp)); + } + + if (strlen(buf) > 0) + strncat(buf, "\n", 1); + else + strcpy(buf, "error get cyc data\n"); + + } else { + strcpy(buf, "error send ebus msg\n"); + } + + *buflen = strlen(buf); + +} + + + +int +eb_cyc_data_process(const unsigned char *buf, int buflen) +{ + unsigned char msg[CMD_DATA_SIZE], hlp[CMD_DATA_SIZE]; + unsigned char crcm_recv, crcm_calc, acks, crcs_recv, crcs_calc, ackm; + int id, msgtype, msglen, hlplen, mlen, slen, len; + + memset(msg, '\0', sizeof(msg)); + + /* search command definition */ + id = eb_cmd_search_com_cyc(&buf[1], buflen - 1); + + if (id >= 0) { + + msgtype = eb_cmd_get_s_type(id); + + /* unescape */ + memcpy(msg, buf, buflen); + msglen = buflen; + + eb_unesc(msg, &msglen); + + /* calc crc vom master */ + mlen = 5 + (int) msg[4]; + + memset(hlp, '\0', sizeof(hlp)); + memcpy(hlp, msg, mlen); + hlplen = mlen; + + eb_esc(hlp, &hlplen); + + crcm_calc = eb_calc_crc(hlp, hlplen); + crcm_recv = msg[mlen]; + + if (crcm_calc != crcm_recv) { + log_print(L_WAR, "%s", "Master CRC Error"); + return -2; + } + + /* set len */ + len = mlen + 1; + + + if (msgtype != EBUS_MSG_BROADCAST) { + + /* get ack from slave */ + acks = msg[mlen + 1]; + + if (acks == EBUS_NAK) { + log_print(L_WAR, "%s", "Slave ACK Error"); + //~ return -2; + } + + /* set len */ + len = mlen + 2; + } + + + if (msgtype == EBUS_MSG_MASTER_SLAVE) { + + /* calc crc vom slave */ + slen = 1 + (int) msg[mlen + 2]; + + memset(hlp, '\0', sizeof(hlp)); + memcpy(hlp, &msg[mlen + 2], slen); + hlplen = slen; + + eb_esc(hlp, &hlplen); + + crcs_calc = eb_calc_crc(hlp, hlplen); + crcs_recv = msg[mlen + 2 + slen]; + + if (crcs_calc != crcs_recv) { + log_print(L_WAR, "%s", "Slave CRC Error"); + return -2; + } + + /* get ack from master */ + ackm = msg[mlen + 2 + slen + 1]; + + if (ackm == EBUS_NAK) { + log_print(L_WAR, "%s", "Master ACK Error"); + //~ return -2; + } + + /* set len */ + len = mlen + 2 + slen + 2; + } + + /* check len */ + if (msglen > len) { + log_print(L_WAR, "%s", "LEN Error"); + return -2; + + } + + /* save data */ + eb_cmd_set_cyc_buf(id, msg, msglen); + + } + + return id; +} + +int +eb_cyc_data_recv() +{ + static unsigned char msg[SERIAL_BUFSIZE]; + static int msglen = 0; + + unsigned char buf[SERIAL_BUFSIZE]; + int ret, i, buflen; + + char tmp[TMP_BUFSIZE]; + int tmplen; + + memset(buf, '\0', sizeof(buf)); + buflen = sizeof(buf); + + if (msglen == 0) + memset(msg, '\0', sizeof(msg)); + + /* get new data */ + ret = eb_serial_recv(buf, &buflen); + if (ret < 0) + return -1; + + i = 0; + while (i < buflen) { + + if (buf[i] != EBUS_SYN) { + msg[msglen] = buf[i]; + msglen++; + } + + /* ebus syn sign is reached - decode ebus message */ + if (buf[i] == EBUS_SYN && msglen > 0) { + eb_print_hex(msg, msglen); + + ret = eb_cyc_data_process(msg, msglen); + + if (ret > 0) { + memset(tmp, '\0', sizeof(tmp)); + eb_execute(ret, "-", tmp, &tmplen); + tmp[strcspn(tmp,"\n")] = '\0'; + log_print(L_EBS, "%s", tmp); + } + + memset(msg, '\0', sizeof(msg)); + msglen = 0; + } + + i++; + } + + return msglen; +} + diff --git a/lib/ebus-bus.h b/lib/ebus-bus.h new file mode 100644 index 00000000..feb9c982 --- /dev/null +++ b/lib/ebus-bus.h @@ -0,0 +1,325 @@ +/* + * Copyright (C) Roland Jax 2012-2013 + * + * This file is part of ebusd. + * + * ebusd is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ebusd is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ebusd. If not, see http://www.gnu.org/licenses/. + */ + +/** + * @file ebus-bus.h + * @brief ebus communication functions + * @author roland.jax@liwest.at + * @version 0.1 + */ + +#ifndef EBUS_BUS_H_ +#define EBUS_BUS_H_ + + + +#include + +#include "ebus-common.h" + + + +#define TMP_BUFSIZE 1024 + + + +/** + * @brief sending data structure + */ +struct send_data { + unsigned char crc; /**< crc of escaped message */ + unsigned char msg[SERIAL_BUFSIZE + 1]; /**< original message */ + int len; /**< length of original message */ + unsigned char msg_esc[SERIAL_BUFSIZE + 1]; /**< esacaped message */ + int len_esc; /**< length of esacaped message */ +}; + +/** + * @brief receiving data structure + */ +struct recv_data { + unsigned char crc_recv; /**< received crc */ + unsigned char crc_calc; /**< calculated crc */ + unsigned char msg[SERIAL_BUFSIZE + 1]; /**< unescaped message */ + int len; /**< length of unescaped message */ + unsigned char msg_esc[SERIAL_BUFSIZE + 1]; /**< received message */ + int len_esc; /**< length of received message */ +}; + + + +/** + * @brief set rawdump + * @param [in] YES | NO + * @return none + */ +void eb_set_rawdump(int dump); + +/** + * @brief set showraw + * @param [in] YES | NO + * @return none + */ +void eb_set_showraw(int show); + +/** + * @brief set own ebus address + * @param [in] src ebus address + * @return none + */ +void eb_set_qq(unsigned char src); + +/** + * @brief set number of retry to get bus + * @param [in] retry + * @return none + */ +void eb_set_get_retry(int retry); + +/** + * @brief set number of skipped SYN if an error occurred while getting the bus + * @param [in] skip is only the start value (skip = skip_ack + retry;) + * @return none + */ +void eb_set_skip_ack(int skip); + +/** + * @brief set max wait time between send and receive of own address (QQ) + * @param [in] usec wait time in usec + * @return none + */ +void eb_set_max_wait(long usec); + +/** + * @brief set number of retry to send message + * @param [in] retry + * @return none + */ +void eb_set_send_retry(int retry); + +/** + * @brief set number of printed hex bytes + * @param [in] size + * @return none + */ +void eb_set_print_size(int size); + + + +/** + * @brief calculate delte time of given time stamps + * @param [in] *tact newer time stamp + * @param [in] *tlast older time stamp + * @param [out] *tdiff calculated time difference + * @return 0 positive | -1 negative ??? lol + */ +int eb_diff_time(const struct timeval *tact, const struct timeval *tlast, struct timeval *tdiff); + + + +/** + * @brief Open a file in write mode. + * @param [in] *file device + * @return 0 ok | -1 error + */ +int eb_raw_file_open(const char *file); + +/** + * @brief close a file. + * @return 0 ok | -1 error + */ +int eb_raw_file_close(void); + +/** + * @brief write bytes into file + * @param [in] *buf pointer to a byte array + * @param [in] buflen length of byte array + * @return 0 ok | -1 error + */ +int eb_raw_file_write(const unsigned char *buf, int buflen); + + + +/** + * @brief Check if serial FD is vaild. + * @return 0 ok | -1 error + */ +int eb_serial_valid(); + +/** + * @brief Open serial device in raw mode. 1 Byte is then minimum input length. + * @param [in] *dev serial device + * @param [out] *fd file descriptor from opened serial device + * @return 0 ok | -1 error + */ +int eb_serial_open(const char *dev, int *fd); + +/** + * @brief close serial device and set settings to default. + * @return 0 ok | -1 error | -2 bad file descriptor + */ +int eb_serial_close(void); + +/** + * @brief send bytes to serial device + * @param [in] *buf pointer to a byte array + * @param [in] buflen length of byte array + * @return 0 ok | -1 error | -2 bad file descriptor + */ +int eb_serial_send(const unsigned char *buf, int buflen); + +/** + * @brief receive bytes from serial device + * @param [out] *buf pointer to a byte array received bytes + * @param [out] *buflen length of received bytes + * @return 0 ok | -1 error | -2 bad file descriptor | -3 received data > BUFFER + */ +int eb_serial_recv(unsigned char *buf, int *buflen); + + + +/** + * @brief print received results in a specific format + * @return none + */ +void eb_print_result(void); + +/** + * @brief print received data with hex format + * @param [in] *buf pointer to a byte array of received bytes + * @param [in] buflen length of received bytes + * @return none + */ +void eb_print_hex(const unsigned char *buf, int buflen); + + + +/** + * @brief get received data + * @param [out] *buf pointer to a byte array of received bytes + * @param [out] *buflen length of received bytes + * @return none + */ +void eb_recv_data_get(unsigned char *buf, int *buflen); + +/** + * @brief prepare received data + * @param [in] *buf pointer to a byte array of received bytes + * @param [in] buflen length of received bytes + * @return none + */ +void eb_recv_data_prepare(const unsigned char *buf, int buflen); + +/** + * @brief receive bytes from serial device + * @param [out] *buf pointer to a byte array of received bytes + * @param [out] buflen length of received bytes + * @return 0 ok | -1 error | -2 syn no answer from slave + * | -3 SYN received after answer from slave + */ +int eb_recv_data(unsigned char *buf, int *buflen); + + + +/** + * @brief receive bytes from serial device until SYN byte was received + * @param [in] *skip number skipped SYN bytes + * @return 0 ok | -1 error + */ +int eb_bus_wait_syn(int *skip); + +/** + * @brief try to get bus for sending + * \li wait SYN byte, send our ebus address and wait for minimun of ~4200 usec + * \li read at least one byte and compare it with sent byte. + * @return 0 ok | -1 error | 1 max retry was reached + */ +int eb_bus_wait(void); + +/** + * @brief free bus after syn was sent + * @return 0 ok | -1 error + */ +int eb_bus_free(void); + + + + /** + * @brief receive ACK byte from serial device + * @param [out] *buf pointer to a byte array of received bytes + * @param [out] buflen length of received bytes + * @return 0 ok | 1 negative ACK | -1 error | -2 send and recv msg are different + * | -3 syn received (no answer from slave) | -4 we should never reach this + */ +int eb_send_data_get_ack(unsigned char *buf, int *buflen); + +/** + * @brief prepare send data + * @param [in] *buf pointer to a byte array of send bytes + * @param [in] buflen length of send bytes + * @return none + */ +void eb_send_data_prepare(const unsigned char *buf, int buflen); + +/** + * @brief handle sending ebus data + * + * \li Given input data will be prepared (escape, crc) and send to ebus. + * \li Answer will be received, prepared (unescape, crc) and return as result. + * + * @param [in] *buf pointer to a byte array + * @param [in] buflen length of byte array + * @param [in] type is the type of message to send + * @param [out] *bus pointer to answer array + * @param [out] *buslen point to answer length + * @return 0 ok | 1 neg. ACK from Slave | -1 error + */ +int eb_send_data(const unsigned char *buf, int buflen, int type, unsigned char *bus, int *buslen); + + + +/** + * @brief handle send ebus cmd + * @param [in] id is index in command array + * @param [in] *data pointer to data bytes for decode/encode + * @param [out] *buf pointer to answer array + * @param [out] *buflen point to answer length + * @return none + */ +void eb_execute(int id, char *data, char *buf, int *buflen); + + + +/** + * @brief handle one collected message + * @param [in] *buf pointer to a byte array + * @param [in] buflen length of byte array + * @return 0-x msg number of collected msg | -1 msg not found | -2 error + */ +int eb_cyc_data_process(const unsigned char *buf, int buflen); + +/** + * @brief handle reading cycle ebus data + * @return 0-x length of collected msg | -1 error + */ +int eb_cyc_data_recv(); + + + +#endif /* EBUS_BUS_H_ */ diff --git a/lib/ebus-cmd.c b/lib/ebus-cmd.c new file mode 100644 index 00000000..3e7ded24 --- /dev/null +++ b/lib/ebus-cmd.c @@ -0,0 +1,953 @@ +/* + * Copyright (C) Roland Jax 2012-2013 + * + * This file is part of ebusd. + * + * ebusd is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ebusd is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ebusd. If not, see http://www.gnu.org/licenses/. + */ + +/** + * @file ebus-cmd.c + * @brief ebus command file functions + * @author roland.jax@liwest.at + * @version 0.1 + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "ebus-decode.h" +#include "ebus-cmd.h" + +static struct cycbuf *cyc = NULL; +static int cyclen = 0; + +static struct commands *com = NULL; +static int comlen = 0; + +void +eb_cmd_uppercase(char *buf) +{ + while (*buf++ != '\0') + *buf = toupper((unsigned char) *buf); +} + + +int +eb_cmd_check_type(int id, const char *type) +{ + if (strncasecmp(com[id].type, type, 3) == 0) + return YES; + else + return NO; +} + + + +int +eb_cmd_get_s_type(int id) +{ + return com[id].s_type; +} + + + +void +eb_cmd_set_cyc_buf(int id, const unsigned char *msg, int msglen) +{ + int i; + + for (i = 0; i < cyclen; i++) + if (cyc[i].id == com[id].id) + break; + + memcpy(cyc[i].buf, msg, msglen); + cyc[i].buflen = msglen; +} + +void +eb_cmd_get_cyc_buf(int id, unsigned char *msg, int *msglen) +{ + int i; + + for (i = 0; i < cyclen; i++) + if (cyc[i].id == com[id].id) + break; + + *msglen = cyc[i].buflen; + memcpy(msg, cyc[i].buf, *msglen); +} + + + +int +eb_cmd_search_com_cyc(const unsigned char *hex, int hexlen) +{ + unsigned char hlp[(CMD_SIZE_S_MSG * 2) + 1]; + int i; + + if (hexlen > (CMD_SIZE_S_MSG * 2)) { + log_print(L_ERR, "hexlen: %d > hlp: %d ", hexlen, (CMD_SIZE_S_MSG * 2)); + return -2; + } + + memset(hlp, '\0', sizeof(hlp)); + for (i = 0; i < hexlen; i++) + sprintf((char *) &hlp[2 * i], "%02X", hex[i]); + + for (i = 0; i < cyclen; i++) { + if (memcmp(hlp, cyc[i].msg, strlen((const char *) cyc[i].msg)) == 0) { + log_print(L_NOT, " found: %s type: %d ==> id: %d", + cyc[i].msg, com[cyc[i].id].s_type, cyc[i].id); + + return cyc[i].id; + } + } + + return -1; +} + +int +eb_cmd_search_com_id(const char *type, const char *class, const char *cmd) +{ + int i; + + for (i = 0; i < comlen; i++) { + if ((strncasecmp(type, com[i].type, strlen(com[i].type)) == 0) + && (strncasecmp(class, com[i].class, strlen(com[i].class)) == 0) + && strlen(class) == strlen(com[i].class) + && (strncasecmp(cmd, com[i].cmd, strlen(com[i].cmd)) == 0) + && strlen(cmd) == strlen(com[i].cmd)) { + + log_print(L_NOT, " found: %s%s%02X%s type: %d ==> id: %d", + com[i].s_zz, com[i].s_cmd, com[i].s_len, + com[i].s_msg, com[i].s_type, i); + return i; + } + + } + + return -1; +} + +int +eb_cmd_search_com(char *buf, char *data) +{ + char *type, *class, *cmd, *tok; + int id; + + type = strtok(buf, " "); + class = strtok(NULL, " ."); + cmd = strtok(NULL, " .\n\r\t"); + + if (class != NULL && cmd != NULL) { + + if (strncasecmp(type, "get", 3) == 0 || + strncasecmp(type, "set", 3) == 0 || + strncasecmp(type, "cyc", 3) == 0) { + log_print(L_NOT, "search: %s %s.%s", type, class, cmd); + + id = eb_cmd_search_com_id(type, class, cmd); + if (id < 0) + return -1; + + tok = strtok(NULL, "\n\r"); + + if (tok == NULL) + strncpy(data, "-", 1); + else + strncpy(data, tok, strlen(tok)); + + log_print(L_NOT, " data: %s", data); + return id; + + } + } + + return -1; +} + + + +int +eb_cmd_decode_value(int id, int elem, unsigned char *msg, char *buf) +{ + char *c1, *c2, *c3, *c4; + char d_pos[CMD_SIZE_D_POS + 1]; + int ret, i, j, p1, p2, p3, p4; + float f; + unsigned long l; + + memset(d_pos, '\0', sizeof(d_pos)); + strncpy(d_pos, com[id].elem[elem].d_pos, strlen(com[id].elem[elem].d_pos)); + + c1 = strtok(d_pos, " ,\n"); + c2 = strtok(NULL, " ,\n"); + c3 = strtok(NULL, " ,\n"); + c4 = strtok(NULL, " ,\n"); + + p1 = c1 ? atoi(c1) : 0; + p2 = c2 ? atoi(c2) : 0; + p3 = c3 ? atoi(c3) : 0; + p4 = c4 ? atoi(c4) : 0; + + log_print(L_DBG, "id: %d elem: %d p1: %d p2: %d p3: %d p4: %d", id, elem, p1, p2, p3, p4); + + if (strncasecmp(com[id].elem[elem].d_type, "asc", 3) == 0) { + sprintf(buf, "%s", &msg[1]); + + } else if (strncasecmp(com[id].elem[elem].d_type, "bcd", 3) == 0) { + if (p1 > 0) { + ret = eb_bcd_to_int(msg[p1], &i); + + i *= com[id].elem[elem].d_fac; + sprintf(buf, "%d", i); + } else { + goto on_error; + } + + } else if (strncasecmp(com[id].elem[elem].d_type, "d1b", 3) == 0) { + if (p1 > 0) { + ret = eb_d1b_to_int(msg[p1], &i); + + f = i * com[id].elem[elem].d_fac; + sprintf(buf, "%f", f); + } else { + goto on_error; + } + + } else if (strncasecmp(com[id].elem[elem].d_type, "d1c", 3) == 0) { + if (p1 > 0) { + ret = eb_d1c_to_float(msg[p1], &f); + + f *= com[id].elem[elem].d_fac; + sprintf(buf, "%f", f); + } else { + goto on_error; + } + + } else if (strncasecmp(com[id].elem[elem].d_type, "d2b", 3) == 0) { + if (p1 > 0 && p2 > 0) { + if (p1 > p2) + ret = eb_d2b_to_float(msg[p2], msg[p1], &f); + else + ret = eb_d2b_to_float(msg[p1], msg[p2], &f); + + f *= com[id].elem[elem].d_fac; + sprintf(buf, "%f", f); + } else { + goto on_error; + } + + } else if (strncasecmp(com[id].elem[elem].d_type, "d2c", 3) == 0) { + if (p1 > 0 && p2 > 0) { + if (p1 > p2) + ret = eb_d2c_to_float(msg[p2], msg[p1], &f); + else + ret = eb_d2c_to_float(msg[p1], msg[p2], &f); + + f *= com[id].elem[elem].d_fac; + sprintf(buf, "%f", f); + } else { + goto on_error; + } + + } else if (strncasecmp(com[id].elem[elem].d_type, "bda", 3) == 0) { + if (p1 > 0 && p2 > 0 && p3 > 0) { + int dd, mm, yy; + ret = eb_bcd_to_int(msg[p1], &dd); + ret = eb_bcd_to_int(msg[p2], &mm); + ret = eb_bcd_to_int(msg[p3], &yy); + + ret = eb_dat_to_str(dd, mm, yy, buf); + if (ret < 0) + sprintf(buf, "error %s ==> %02x %02x %02x", + com[id].elem[elem].d_sub, msg[p1], msg[p2], msg[p3]); + } else { + goto on_error; + } + + } else if (strncasecmp(com[id].elem[elem].d_type, "hda", 3) == 0) { + if (p1 > 0 && p2 > 0 && p3 > 0) { + ret = eb_dat_to_str(msg[p1], msg[p2], msg[p3], buf); + if (ret < 0) + sprintf(buf, "error %s ==> %02x %02x %02x", + com[id].elem[elem].d_sub, msg[p1], msg[p2], msg[p3]); + } else { + goto on_error; + } + + } else if (strncasecmp(com[id].elem[elem].d_type, "bti", 3) == 0) { + if (p1 > 0 && p2 > 0 && p3 > 0) { + int hh, mm, ss; + + ret = eb_bcd_to_int(msg[p1], &hh); + ret = eb_bcd_to_int(msg[p2], &mm); + ret = eb_bcd_to_int(msg[p3], &ss); + + ret = eb_tim_to_str(hh, mm, ss, buf); + if (ret < 0) + sprintf(buf, "error %s ==> %02x %02x %02x", + com[id].elem[elem].d_sub, msg[p1], msg[p2], msg[p3]); + } else { + goto on_error; + } + + } else if (strncasecmp(com[id].elem[elem].d_type, "hti", 3) == 0) { + if (p1 > 0 && p2 > 0 && p3 > 0) { + ret = eb_tim_to_str(msg[p1], msg[p2], msg[p3], buf); + if (ret < 0) + sprintf(buf, "error %s ==> %02x %02x %02x", + com[id].elem[elem].d_sub, msg[p1], msg[p2], msg[p3]); + } else { + goto on_error; + } + + } else if (strncasecmp(com[id].elem[elem].d_type, "bdy", 3) == 0) { + if (p1 > 0) + ret = eb_day_to_str(msg[p1], buf); + else + goto on_error; + + } else if (strncasecmp(com[id].elem[elem].d_type, "hdy", 3) == 0) { + if (p1 > 0) { + msg[p1] = msg[p1] - 0x01; + ret = eb_day_to_str(msg[p1], buf); + } else { + goto on_error; + } + + } else if (strncasecmp(com[id].elem[elem].d_type, "hex", 3) == 0) { + if (p1 > 0) { + if (p2 > msg[0]) + p2 = msg[0]; + + if (p2 == 0 || p2 < p1) + p2 = p1; + + for (i = 0, j = p1 - 1; i <= p2 - p1; i++, j++) + sprintf(&buf[3 * i], "%02x ", msg[j + 1]); + + buf[i * 3 - 1] = '\0'; + } else { + goto on_error; + } + + } else if (strncasecmp(com[id].elem[elem].d_type, "ulg", 3) == 0) { + if (p1 > 0 && p2 > 0 && p3 > 0 && p4 > 0) { + l = msg[p1] + (msg[p2] << 8) + (msg[p3] << 16) + (msg[p4] << 24); + sprintf(buf, "%lu", l); + } else { + goto on_error; + } + + } + + log_print(L_DBG, "buf: %s", buf); + + return 0; + +on_error: + strcpy(buf, "error decode"); + return -1; + +} + +int +eb_cmd_decode(int id, char *part, char *data, unsigned char *msg, char *buf) +{ + char *tok; + char tmp[CMD_DATA_SIZE], hlp[CMD_DATA_SIZE]; + int ret, i, found; + + /* walk through all elements */ + for (i = 0; i < com[id].d_elem; i++) { + memset(tmp, '\0', sizeof(tmp)); + strncpy(tmp, data, strlen(data)); + + /* "-" indicate no sub command, so we print all */ + tok = strtok(tmp, " \n\r\t"); + if (tok == NULL || strncasecmp(tok, "-", 1) == 0) + found = YES; + else + found = NO; + + /* search sub command */ + while (tok != NULL && found == NO) { + if (strncasecmp(com[id].elem[i].d_sub, tok, + strlen(com[id].elem[i].d_sub)) == 0 && + strncasecmp(com[id].elem[i].d_part, part, 2) == 0) { + + found = YES; + break; + } + tok = strtok(NULL, " \n\r\t"); + } + + if (strncasecmp(com[id].elem[i].d_part, part, 2) == 0 && + found == YES) { + memset(hlp, '\0', sizeof(hlp)); + + ret = eb_cmd_decode_value(id, i, msg, hlp); + if (ret < 0) { + strncat(buf, "\n", 1); + return -1; + } + + if (i > 0) + strncat(buf, " ", 1); + + strncat(buf, hlp, strlen(hlp)); + } + } + + return 0; +} + +int +eb_cmd_encode_value(int id, int elem, char *data, unsigned char *msg, char *buf) +{ + char *c1, *c2, *c3; + char d_pos[CMD_SIZE_D_POS + 1]; + unsigned char bcd, d1b, d1c, d2b[2], d2c[2]; + int ret, i, j, p1, p2, p3; + float f; + + memset(d_pos, '\0', sizeof(d_pos)); + strncpy(d_pos, com[id].elem[elem].d_pos, strlen(com[id].elem[elem].d_pos)); + + c1 = strtok(d_pos, " ,\n"); + c2 = strtok(NULL, " ,\n"); + c3 = strtok(NULL, " ,\n"); + + p1 = c1 ? atoi(c1) : 0; + p2 = c2 ? atoi(c2) : 0; + p3 = c3 ? atoi(c3) : 0; + + log_print(L_DBG, "id: %d elem: %d p1: %d p2: %d p3: %d data: %s", + id, elem, p1, p2, p3, data); + + if (strncasecmp(com[id].elem[elem].d_type, "asc", 3) == 0) { + for (i = 0; i < strlen(data); i++) + sprintf((char *) &msg[i * 2], "%02x", data[i]); + + } else if (strncasecmp(com[id].elem[elem].d_type, "bcd", 3) == 0) { + if (p1 > 0) { + i = (int) (atof(data) / com[id].elem[elem].d_fac); + + ret = eb_int_to_bcd(i, &bcd); + sprintf((char *) msg, "%02x", bcd); + + } else { + goto on_error; + } + + } else if (strncasecmp(com[id].elem[elem].d_type, "d1b", 3) == 0) { + if (p1 > 0) { + i = (int) (atof(data) / com[id].elem[elem].d_fac); + + ret = eb_int_to_d1b(i, &d1b); + sprintf((char *) msg, "%02x", d1b); + } else { + goto on_error; + } + + } else if (strncasecmp(com[id].elem[elem].d_type, "d1c", 3) == 0) { + if (p1 > 0) { + f = (atof(data) / com[id].elem[elem].d_fac); + + ret = eb_float_to_d1c(f, &d1c); + sprintf((char *) msg, "%02x", d1c); + } else { + goto on_error; + } + + } else if (strncasecmp(com[id].elem[elem].d_type, "d2b", 3) == 0) { + if (p1 > 0 && p2 > 0) { + f = (atof(data) / com[id].elem[elem].d_fac); + + if (p1 > p2) + ret = eb_float_to_d2b(f, &d2b[1], &d2b[0]); + else + ret = eb_float_to_d2b(f, &d2b[0], &d2b[1]); + + sprintf((char *) msg, "%02x%02x", d2b[0], d2b[1]); + } else { + goto on_error; + } + + } else if (strncasecmp(com[id].elem[elem].d_type, "d2c", 3) == 0) { + if (p1 > 0 && p2 > 0) { + f = (atof(data) / com[id].elem[elem].d_fac); + + if (p1 > p2) + ret = eb_float_to_d2c(f, &d2c[1], &d2c[0]); + else + ret = eb_float_to_d2c(f, &d2c[0], &d2c[1]); + + sprintf((char *) msg, "%02x%02x", d2c[0], d2c[1]); + } else { + goto on_error; + } + + } else if (strncasecmp(com[id].elem[elem].d_type, "hda", 3) == 0) { + if (p1 > 0 && p2 > 0 && p3 > 0) { + int dd, mm, yy; + + dd = atoi(strtok(data, " .\n")); + mm = atoi(strtok(NULL, " .\n")); + yy = atoi(strtok(NULL, " .\n")); + + ret = eb_str_to_dat(dd, mm, yy, msg); + if (ret < 0) + sprintf(buf, "error ==> %d.%d.%d", dd, mm, yy); + + } else { + goto on_error; + } + + } else if (strncasecmp(com[id].elem[elem].d_type, "hti", 3) == 0) { + if (p1 > 0 && p2 > 0 && p3 > 0) { + int hh, mm, ss; + + hh = atoi(strtok(data, " :\n")); + mm = atoi(strtok(NULL, " :\n")); + ss = atoi(strtok(NULL, " :\n")); + + ret = eb_str_to_tim(hh, mm, ss, msg); + if (ret < 0) + sprintf(buf, "error ==> %d:%d:%d", hh, mm, ss); + + } else { + goto on_error; + } + + } else if (strncasecmp(com[id].elem[elem].d_type, "hdy", 3) == 0) { + if (p1 > 0) { + int day; + + day = atoi(data); + sprintf((char *) msg, "%02x", day); + + } else { + goto on_error; + } + + } else if (strncasecmp(com[id].elem[elem].d_type, "hex", 3) == 0) { + for (i = 0, j = 0; data[i]; i++) { + + if (isxdigit(data[i])) { + msg[j] = tolower(data[i]); + j++; + } else if (data[i] != ' ') { + goto on_error; + } + } + + } + + return 0; + +on_error: + strcpy(buf, "error encode"); + return -1; + +} + +int +eb_cmd_encode(int id, char *data, unsigned char *msg, char *buf) +{ + char *tok, *toksave; + unsigned char hlp[CMD_SIZE_S_MSG + 1]; + int ret, i; + + tok = strtok_r(data, " \n\r\t", &toksave); + + /* walk through all elements */ + for (i = 0; i < com[id].d_elem; i++) { + if (tok != NULL) { + memset(hlp, '\0', sizeof(hlp)); + ret = eb_cmd_encode_value(id, i, tok, hlp, buf); + if (ret < 0) { + strncat(buf, "\n", 1); + return -1; + } + + strncat((char *) msg, (const char *) hlp, strlen((const char *) hlp)); + + } else { + return -1; + } + + tok = strtok_r(NULL, " \n\r\t", &toksave); + } + + if (strlen(buf) > 0) + strncat(buf, "\n", 1); + + return 0; +} + +void +eb_cmd_prepare(int id, char *data, unsigned char *msg, int *msglen, char *buf) +{ + unsigned char tmp[CMD_SIZE_S_MSG + 1]; + char str[CMD_SIZE_S_ZZ + CMD_SIZE_S_CMD + 2 + CMD_SIZE_S_MSG + 1]; + char byte; + int ret, i, j, k; + int in[SERIAL_BUFSIZE]; + + /* encode msg */ + memset(tmp, '\0', sizeof(tmp)); + if (strncasecmp(com[id].type, "set", 3) == 0) + eb_cmd_encode(id, data, tmp, buf); + + memset(str, '\0', sizeof(str)); + sprintf(str, "%s%s%02X%s%s", + com[id].s_zz, com[id].s_cmd, com[id].s_len, com[id].s_msg, tmp); + + memset(in, '\0', sizeof(in)); + i = 0; + j = 0; + + while (str[j] != '\0') { + byte = str[j]; + if (i < sizeof(in)) { + + ret = eb_htoi(&byte); + if (ret != -1) { + in[i] = ret; + i++; + } + } + j++; + } + + memset(msg, '\0', sizeof(msg)); + for (j = 0, k = 0; j < i; j += 2, k++) + msg[k] = (unsigned char) (in[j]*16 + in[j+1]); + + *msglen = k; + +} + + + +void +eb_cmd_print(const char *type, int all, int detail) +{ + int i, j; + + for (i = 0; i < comlen; i++) { + + if (strncasecmp(com[i].type, type, 1) == 0 || all) { + + log_print(L_INF, "[%03d] %s : %5s.%-32s\t(type: %d)" \ + " %s%s%-10s (len: %d) [%d] ==> %s", + com[i].id, + com[i].type, + com[i].class, + com[i].cmd, + com[i].s_type, + com[i].s_zz, + com[i].s_cmd, + com[i].s_msg, + com[i].s_len, + com[i].d_elem, + com[i].com + ); + + if (detail) { + for (j = 0; j < com[i].d_elem; j++) { + log_print(L_INF, "\t\t %-20s %-2s " \ + "pos: %-10s\t%s [%5.2f] [%s] \t%s\t%s", + com[i].elem[j].d_sub, + com[i].elem[j].d_part, + com[i].elem[j].d_pos, + com[i].elem[j].d_type, + com[i].elem[j].d_fac, + com[i].elem[j].d_unit, + com[i].elem[j].d_valid, + com[i].elem[j].d_com + + ); + } + log_print(L_INF, ""); + } + + + } + } +} + + + +int +eb_cmd_fill(const char *tok) +{ + int i; + + com = (struct commands *) realloc(com, (comlen + 1) * sizeof(struct commands)); + err_ret_if(com == NULL, -1); + + memset(com + comlen, '\0', sizeof(struct commands)); + + /* id */ + com[comlen].id = comlen; + + /* type */ + strncpy(com[comlen].type, tok, strlen(tok)); + + /* class */ + tok = strtok(NULL, ";"); + strncpy(com[comlen].class, tok, strlen(tok)); + + + /* cmd */ + tok = strtok(NULL, ";"); + strncpy(com[comlen].cmd, tok, strlen(tok)); + + /* com */ + tok = strtok(NULL, ";"); + strncpy(com[comlen].com, tok, strlen(tok)); + + /* s_type */ + tok = strtok(NULL, ";"); + com[comlen].s_type = atoi(tok); + + /* s_zz */ + tok = strtok(NULL, ";"); + + strncpy(com[comlen].s_zz, tok, strlen(tok)); + eb_cmd_uppercase(com[comlen].s_zz); + + /* s_cmd */ + tok = strtok(NULL, ";"); + strncpy(com[comlen].s_cmd, tok, strlen(tok)); + eb_cmd_uppercase(com[comlen].s_cmd); + + /* s_len */ + tok = strtok(NULL, ";"); + com[comlen].s_len = atoi(tok); + + /* s_msg */ + tok = strtok(NULL, ";"); + if (strncasecmp(tok, "-", 1) != 0) { + strncpy(com[comlen].s_msg, tok, strlen(tok)); + eb_cmd_uppercase(com[comlen].s_msg); + } + + /* d_elem */ + tok = strtok(NULL, ";"); + com[comlen].d_elem = atoi(tok); + + com[comlen].elem = (struct element *) malloc(com[comlen].d_elem * sizeof(struct element)); + err_ret_if(com[comlen].elem == NULL, -1); + + memset(com[comlen].elem, '\0', com[comlen].d_elem * sizeof(struct element)); + + for (i = 0; i < com[comlen].d_elem; i++) { + + /* d_sub */ + tok = strtok(NULL, ";"); + if (strncasecmp(tok, "-", 1) != 0) + strncpy(com[comlen].elem[i].d_sub, tok, strlen(tok)); + + /* d_part */ + tok = strtok(NULL, ";"); + strncpy(com[comlen].elem[i].d_part, tok, strlen(tok)); + + /* d_pos */ + tok = strtok(NULL, ";"); + strncpy(com[comlen].elem[i].d_pos, tok, strlen(tok)); + + /* d_type */ + tok = strtok(NULL, ";"); + strncpy(com[comlen].elem[i].d_type, tok, strlen(tok)); + + /* d_fac */ + tok = strtok(NULL, ";"); + com[comlen].elem[i].d_fac = atof(tok); + + /* d_unit */ + tok = strtok(NULL, ";"); + strncpy(com[comlen].elem[i].d_unit, tok, strlen(tok)); + + /* d_valid */ + tok = strtok(NULL, ";"); + strncpy(com[comlen].elem[i].d_valid, tok, strlen(tok)); + + /* d_com */ + tok = strtok(NULL, ";"); + strncpy(com[comlen].elem[i].d_com, tok, strlen(tok)); + com[comlen].elem[i].d_com[strcspn(com[comlen].elem[i].d_com, "\n")] = '\0'; + + } + + if (strncasecmp(com[comlen].type, "cyc", 3) == 0) { + + cyc = (struct cycbuf *) realloc(cyc, (cyclen + 1) * sizeof(struct cycbuf)); + err_ret_if(cyc == NULL, -1); + + memset(cyc + cyclen, '\0', sizeof(struct cycbuf)); + + /* id */ + cyc[cyclen].id = com[comlen].id; + + /* msg */ + sprintf((char *) cyc[cyclen].msg, "%s%s%02X%s", + com[comlen].s_zz, com[comlen].s_cmd, + com[comlen].s_len, com[comlen].s_msg); + + cyclen++; + + } + + comlen++; + + return 0; +} + +int +eb_cmd_num_c(const char *str, const char c) +{ + const char *p = str; + int i = 0; + + do { + if (*p == c) + i++; + } while (*(p++)); + + return i; +} + +int +eb_cmd_file_read(const char *file) +{ + int ret; + char line[CMD_LINELEN]; + char *tok; + FILE *fp = NULL; + + log_print(L_NOT, "%s", file); + + /* open config file */ + fp = fopen(file, "r"); + err_ret_if(fp == NULL, -1); + + /* read each line and fill cmd array */ + while (fgets(line, CMD_LINELEN, fp) != NULL) { + tok = strtok(line, ";\n"); + + if (tok != NULL && tok[0] != '#' ) { + + ret = eb_cmd_fill(tok); + if (ret < 0) + return -2; + + } + + } + + /* close config file */ + ret = fclose(fp); + err_ret_if(ret == EOF, -1); + + log_print(L_NOT, "%s success", file); + + return 0; +} + +int +eb_cmd_dir_read(const char *cfgdir, const char *extension) +{ + struct dirent **dir; + int ret, i, j, files, extlen; + char file[CMD_FILELEN], extprep[11]; + char *ext; + + extlen = strlen(extension) + 1; + + memset(extprep, '\0', sizeof(extprep)); + extprep[0] = '.'; + strncpy(&extprep[1], extension, strlen(extension)); + + files = scandir(cfgdir, &dir, 0, alphasort); + if (files < 0) { + log_print(L_WAR, "configuration directory %s not found.", cfgdir); + return 1; + } + + i = 0; + j = 0; + while (i < files) { + ext = strrchr(dir[i]->d_name, '.'); + if (ext != NULL) { + if (strlen(ext) == extlen + && dir[i]->d_type == DT_REG + && strncasecmp(ext, extprep, extlen) == 0 ) { + memset(file, '\0', sizeof(file)); + sprintf(file, "%s/%s", cfgdir, dir[i]->d_name); + + ret = eb_cmd_file_read(file); + if (ret < 0) + return -1; + + j++; + } + } + + free(dir[i]); + i++; + } + + free(dir); + + if (j == 0) { + log_print(L_WAR, "no command files found ==> decode disabled."); + return 2; + } + + return 0; +} + +void +eb_cmd_dir_free(void) +{ + int i; + + if (comlen > 0) { + for (i = 0; i < comlen; i++) + free(&com[i].elem[0]); + + free(com); + } + + if (cyclen > 0) + free(cyc); + +} + diff --git a/lib/ebus-cmd.h b/lib/ebus-cmd.h new file mode 100644 index 00000000..23e76172 --- /dev/null +++ b/lib/ebus-cmd.h @@ -0,0 +1,290 @@ +/* + * Copyright (C) Roland Jax 2012-2013 + * + * This file is part of ebusd. + * + * ebusd is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ebusd is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ebusd. If not, see http://www.gnu.org/licenses/. + */ + +/** + * @file ebus-cmd.h + * @brief ebus command file functions + * @author roland.jax@liwest.at + * @version 0.1 + */ + +#ifndef EBUS_CMD_H_ +#define EBUS_CMD_H_ + + + +#include + +#include "ebus-common.h" + + + +#define CMD_LINELEN 512 +#define CMD_FILELEN 1024 + +#define CMD_SIZE_TYPE 3 +#define CMD_SIZE_CLASS 20 +#define CMD_SIZE_CMD 30 +#define CMD_SIZE_COM 256 +#define CMD_SIZE_S_ZZ 2 +#define CMD_SIZE_S_CMD 4 +#define CMD_SIZE_S_MSG 32 + +#define CMD_SIZE_D_SUB 20 +#define CMD_SIZE_D_PART 2 +#define CMD_SIZE_D_POS 10 +#define CMD_SIZE_D_TYPE 3 +#define CMD_SIZE_D_UNIT 6 +#define CMD_SIZE_D_VALID 30 +#define CMD_SIZE_D_COM 256 + +#define CMD_PART_MD "MD" +#define CMD_PART_SA "SA" +#define CMD_PART_SD "SD" +#define CMD_PART_MA "MA" + + + +/** + * @brief cycbuf structure + */ +struct cycbuf { + int id; /**< command id from command buffer */ + unsigned char msg[CMD_SIZE_S_MSG + 1]; /**< zz + cmd + len + msg */ + unsigned char buf[CMD_DATA_SIZE + 1]; /**< whole message - unescaped */ + int buflen; /**< len of saved message */ +}; + +/** + * @brief commands structure + */ +struct commands { + int id; /**< command id */ + char type[CMD_SIZE_TYPE + 1]; /**< type of message */ + char class[CMD_SIZE_CLASS + 1]; /**< ci */ + char cmd[CMD_SIZE_CMD + 1]; /**< hydraulic */ + char com[CMD_SIZE_COM + 1]; /**< just a comment */ + int s_type; /**< message type */ + char s_zz[CMD_SIZE_S_ZZ + 1]; /**< zz */ + char s_cmd[CMD_SIZE_S_CMD + 1]; /**< pb sb */ + int s_len; /**< number of send bytes */ + char s_msg[CMD_SIZE_S_MSG + 1]; /**< max 15 data bytes */ + int d_elem; /**< number of elements */ + struct element *elem; /**< pointer of array with elements */ +}; + +/** + * @brief element structure + */ +struct element { + char d_sub[CMD_SIZE_D_SUB + 1]; /**< pin1 */ + char d_part[CMD_SIZE_D_PART + 1]; /**< part of message */ + char d_pos[CMD_SIZE_D_POS + 1]; /**< data position at bytes */ + char d_type[CMD_SIZE_D_TYPE + 1]; /**< data type */ + float d_fac; /**< facter */ + char d_unit[CMD_SIZE_D_UNIT + 1]; /**< unit of data like °C,...) */ + char d_valid[CMD_SIZE_D_VALID + 1]; /**< valid data */ + char d_com[CMD_SIZE_D_COM + 1]; /**< just a comment */ +}; + + + +/** + * @brief convert string to uppercase + * @param [out] *buf pointer to buffer array + */ +void eb_cmd_uppercase(char *buf); + + + +/** + * @brief compare given type with type of given command id + * @param [in] id is index in command array + * @param [in] type for lookup + * @return YES or NO + */ +int eb_cmd_check_type(int id, const char *type); + + + +/** + * @brief returns s_type of given id + * @param [in] id is index in command array + * @return s_type of ebus command + */ +int eb_cmd_get_s_type(int id); + + + +/** + * @brief set buf from cyc array + * @param [in] id is index in command array + * @param [in] *msg pointer to message array + * @param [in] msglen pointer to message length + * @return none + */ +void eb_cmd_set_cyc_buf(int id, const unsigned char *msg, int msglen); + + +/** + * @brief get buf from cyc array + * @param [in] id is index in command array + * @param [out] *msg pointer to message array + * @param [out] *msglen pointer to message length + * @return none + */ +void eb_cmd_get_cyc_buf(int id, unsigned char *msg, int *msglen); + + + +/** + * @brief search given strings in cycbuf array + * @param [in] *hex pointer to hex string + * @return 0-x id of found ebus command in array + * -1 command not found | -2 hexlen too long + */ +int eb_cmd_search_com_cyc(const unsigned char *hex, int hexlen); + +/** + * @brief search given strings in command array + * @param [in] *type pointer to message type + * @param [in] *class pointer to a ebus class + * @param [in] *cmd pointer to a ebus command + * @return 0-x id of found ebus command in array | -1 command not found + */ +int eb_cmd_search_com_id(const char *type, const char *class, const char *cmd); + +/** + * @brief search given strings in command array + * @param [in] *buf pointer to a byte array + * @param [out] *data pointer to data array for passing to decode/encode + * @return 0-x id of found ebus command in array | -1 command not found + */ +int eb_cmd_search_com(char *buf, char *data); + + + +/** + * @brief decode given element + * @param [in] id is index in command array + * @param [in] elem is index of data position + * @param [out] *msg pointer to message array + * @param [out] *buf pointer to decoded answer + * @return 0 ok | -1 error at decode + */ +int eb_cmd_decode_value(int id, int elem, unsigned char *msg, char *buf); + +/** + * @brief decode msg + * @param [in] id is index in command array + * @param [in] *part point to part type for decode + * @param [in] *data pointer to data bytes for decode + * @param [out] *msg pointer to message array + * @param [out] *buf pointer to decoded answer + * @return 0 ok | -1 error at decode + */ +int eb_cmd_decode(int id, char *part, char *data, unsigned char *msg, char *buf); + +/** + * @brief decode given element + * @param [in] id is index in command array + * @param [in] elem is index of data position + * @param [in] *data pointer to data bytes for encode + * @param [out] *msg pointer to message array + * @param [out] *buf pointer to decoded answer + * @return 0 ok | -1 error at decode + */ +int eb_cmd_encode_value(int id, int elem, char *data, unsigned char *msg, char *buf); + +/** + * @brief encode msg + * @param [in] id is index in command array + * @param [in] *data pointer to data bytes for encode + * @param [out] *msg pointer to message array + * @param [out] *buf pointer to decoded answer + * @return 0 ok | -1 error at encode + */ +int eb_cmd_encode(int id, char *data, unsigned char *msg, char *buf); + +/** + * @brief prepare message string for given ebus cmd from array + * @param [in] id is index in command array + * @param [in] *data pointer to data bytes for decode/encode + * @param [out] *msg pointer to message array + * @param [out] *msglen pointer to message length + * @param [out] *buf pointer to decoded answer + * @return none + */ +void eb_cmd_prepare(int id, char *data, unsigned char *msg, int *msglen, char *buf); + + + +/** + * @brief print readed ebus commands + * @param [in] *type (get/set/cyc) + * @param [in] all print all + * @param [in] detail show details of command + * @return none + */ +void eb_cmd_print(const char *type, int all, int detail); + + + +/** + * @brief fill command structure + * @param [in] *tok pointer to given token + * @return 0 ok | -1 error + */ +int eb_cmd_fill(const char *tok); + +/** + * @brief prevent buffer overflow + * + * number of tokens must be >= number of columns - 1 of file + * + * @param [in] *buf pointer to a byte array + * @param [in] delimeter + * @return number of found delimeters + */ +int eb_cmd_num_c(const char *buf, const char c); + +/** + * @brief set cfgdir address + * @param [in] *file pointer to configuration file with *.csv + * @return 0 ok | -1 error | -2 read token error + */ +int eb_cmd_file_read(const char *file); + +/** + * @brief get all files with given extension from given configuration directory + * @param [in] *cfgdir pointer to configuration directory + * @param [in] *suffix pointer to given extension + * @return 0 ok | -1 error | 1 read file error | 2 no command files found + */ +int eb_cmd_dir_read(const char *cfgdir, const char *extension); + +/** + * @brief free mem for ebus commands + * @return none + */ +void eb_cmd_dir_free(void); + + + +#endif /* EBUS_CMD_H_ */ diff --git a/lib/ebus-common.h b/lib/ebus-common.h new file mode 100644 index 00000000..1fa6f4e6 --- /dev/null +++ b/lib/ebus-common.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) Roland Jax 2012-2013 + * crc calculations from http://www.mikrocontroller.net/topic/75698 + * + * This file is part of ebusd. + * + * ebusd is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ebusd is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ebusd. If not, see http://www.gnu.org/licenses/. + */ + +/** + * @file ebus-common.h + * @brief ebus common defines + * @author roland.jax@liwest.at + * @version 0.1 + */ + +#ifndef EBUS_COMMON_H_ +#define EBUS_COMMON_H_ + + + +#define EBUS_SYN 0xAA +#define EBUS_SYN_ESC_A9 0xA9 +#define EBUS_SYN_ESC_01 0x01 +#define EBUS_SYN_ESC_00 0x00 + +#define EBUS_ACK 0x00 +#define EBUS_NAK 0xFF + +#define EBUS_QQ 0xFF + +#define EBUS_GET_RETRY 3 +#define EBUS_GET_RETRY_MAX 10 +#define EBUS_SKIP_ACK 1 +#define EBUS_MAX_WAIT 4000 +#define EBUS_SEND_RETRY 2 +#define EBUS_SEND_RETRY_MAX 10 +#define EBUS_PRINT_SIZE 30 + +#define EBUS_MSG_BROADCAST 1 +#define EBUS_MSG_MASTER_MASTER 2 +#define EBUS_MSG_MASTER_SLAVE 3 + +#define SERIAL_DEVICE "/dev/ttyUSB0" +#define SERIAL_BAUDRATE B2400 +#define SERIAL_BUFSIZE 100 + +#define CMD_DATA_SIZE 50 /* 5+16+3+16+2 = 42 || 256 */ + +enum enum_bool {UNSET = -1, NO, YES}; + + + +#endif /* EBUS_COMMON_H_ */ diff --git a/lib/ebus-decode.c b/lib/ebus-decode.c new file mode 100644 index 00000000..e5d2410f --- /dev/null +++ b/lib/ebus-decode.c @@ -0,0 +1,439 @@ +/* + * Copyright (C) Roland Jax 2012-2013 + * crc calculations from http://www.mikrocontroller.net/topic/75698 + * + * This file is part of ebusd. + * + * ebusd is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ebusd is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ebusd. If not, see http://www.gnu.org/licenses/. + */ + +/** + * @file ebus-decode.c + * @brief ebus decode functions + * @author roland.jax@liwest.at + * @version 0.1 + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include +#include +#include + +#include "ebus-decode.h" + + + +const char *days[] = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"}; + + + +int +eb_htoi(const char *buf) +{ + int ret; + ret = -1; + + if (isxdigit(*buf)) { + if (isalpha(*buf)) + ret = 55; + else + ret = 48; + + ret = toupper(*buf) - ret; + } + + return ret; +} + + +void +eb_esc(unsigned char *buf, int *buflen) +{ + unsigned char tmp[SERIAL_BUFSIZE]; + int tmplen, i; + + memset(tmp, '\0', sizeof(tmp)); + i = 0; + tmplen = 0; + + while (i < *buflen) { + + if (buf[i] == EBUS_SYN) { + tmp[tmplen] = EBUS_SYN_ESC_A9; + tmplen++; + tmp[tmplen] = EBUS_SYN_ESC_01; + tmplen++; + } else if (buf[i] == EBUS_SYN_ESC_A9) { + tmp[tmplen] = EBUS_SYN_ESC_A9; + tmplen++; + tmp[tmplen] = EBUS_SYN_ESC_00; + tmplen++; + } else { + tmp[tmplen] = buf[i]; + tmplen++; + } + + i++; + } + + memset(buf, '\0', sizeof(buf)); + for (i = 0; i < tmplen; i++) + buf[i] = tmp[i]; + + *buflen = tmplen; +} + +void +eb_unesc(unsigned char *buf, int *buflen) +{ + unsigned char tmp[SERIAL_BUFSIZE]; + int tmplen, i, found; + + memset(tmp, '\0', sizeof(tmp)); + i = 0; + tmplen = 0; + found = 0; + + while (i < *buflen) { + + if (buf[i] == EBUS_SYN_ESC_A9) { + found = 1; + } else if (found == 1) { + if (buf[i] == EBUS_SYN_ESC_01) { + tmp[tmplen] = EBUS_SYN; + tmplen++; + } else { + tmp[tmplen] = EBUS_SYN_ESC_A9; + tmplen++; + } + + found = 0; + } else { + tmp[tmplen] = buf[i]; + tmplen++; + } + + i++; + } + + memset(buf, '\0', sizeof(buf)); + for (i = 0; i < tmplen; i++) + buf[i] = tmp[i]; + + *buflen = tmplen; +} + + + +int +eb_day_to_str(unsigned char day, char *tgt) +{ + int dd; + + if (dd >= 0x00 && dd <= 0x06) { + eb_bcd_to_int(day, &dd); + sprintf(tgt, "%s", days[day]); + } else { + return -1; + } + + return 0; +} + + + +int +eb_dat_to_str(unsigned char dd, unsigned char mm, unsigned char yy, char *tgt) +{ + if (dd >= 0x00 && dd <= 0x1F && + mm >= 0x00 && mm <= 0x0C && + yy >= 0x00 && yy <= 0x63 ) + sprintf(tgt, "%02d.%02d.%04d", dd, mm, yy + 2000); + else + return -1; + + return 0; +} + +int +eb_str_to_dat(int dd, int mm, int yy, unsigned char *tgt) +{ + if (yy > 2000) + yy -= 2000; + + if (dd >= 0 && dd <= 31 && + mm >= 0 && mm <= 12 && + yy >= 0 && yy <= 99 ) + sprintf((char *) tgt, "%02x%02x%02x", dd, mm, yy); + + else + return -1; + + return 0; +} + + + +int +eb_tim_to_str(unsigned char hh, unsigned char mm, unsigned char ss, char *tgt) +{ + if (hh >= 0x00 && hh <= 0x17 && + mm >= 0x00 && mm <= 0x3B && + ss >= 0x00 && ss <= 0x3B ) + sprintf(tgt, "%02d:%02d:%02d", hh, mm, ss); + else + return -1; + + return 0; +} + +int +eb_str_to_tim(int hh, int mm, int ss, unsigned char *tgt) +{ + + if (hh >= 0 && hh <= 23 && + mm >= 0 && mm <= 59 && + ss >= 0 && ss <= 59 ) + sprintf((char *) tgt, "%02x%02x%02x", hh, mm, ss); + + else + return -1; + + return 0; +} + + + +int +eb_bcd_to_int(unsigned char src, int *tgt) +{ + if ((src & 0x0F) > 0x09 || ((src >> 4) & 0x0F) > 0x09) { + *tgt = (int) (0xFF); + return 0; + } else { + *tgt = (int) ( ( ((src & 0xF0) >> 4) * 10) + (src & 0x0F) ); + return 1; + } +} + +int +eb_int_to_bcd(int src, unsigned char *tgt) +{ + if (src > 99) { + *tgt = (unsigned char) (0xFF); + return 0; + } else { + *tgt = (unsigned char) ( ((src / 10) << 4) | (src % 10) ); + return 1; + } +} + + + +int +eb_d1b_to_int(unsigned char src, int *tgt) +{ + if ((src & 0x80) == 0x80) { + *tgt = (int) (- ( ((unsigned char) (~ src)) + 1) ); + + if (*tgt == -0x80) + return 0; + else + return -1; + + } else { + *tgt = (int) (src); + return 1; + } +} + +int +eb_int_to_d1b(int src, unsigned char *tgt) +{ + if (src < -127 || src > 127) { + *tgt = (unsigned char) (0x80); + return 0; + } else { + if (src >= 0) { + *tgt = (unsigned char) (src); + return 1; + } else { + *tgt = (unsigned char) (- (~ (src - 1) ) ); + return -1; + } + } +} + + + +int +eb_d1c_to_float(unsigned char src, float *tgt) +{ + if (src > 0xC8) { + *tgt = (float) (0xFF); + return 0; + } else { + *tgt = (float) (src / 2.0); + return 1; + } +} + +int +eb_float_to_d1c(float src, unsigned char *tgt) +{ + if (src < 0.0 || src > 100.0) { + *tgt = (unsigned char) (0xFF); + return 0; + } else { + *tgt = (unsigned char) (src * 2.0); + return 1; + } +} + + + +int +eb_d2b_to_float(unsigned char src_lsb, unsigned char src_msb, float *tgt) +{ + if ((src_msb & 0x80) == 0x80) { + *tgt = (float) + (- ( ((unsigned char) (~ src_msb)) + + ( ( ((unsigned char) (~ src_lsb)) + 1) / 256.0) ) ); + + if (src_msb == 0x80 && src_lsb == 0x00) + return 0; + else + return -1; + + } else { + *tgt = (float) (src_msb + (src_lsb / 256.0)); + return 1; + } +} + +int +eb_float_to_d2b(float src, unsigned char *tgt_lsb, unsigned char *tgt_msb) +{ + if (src < -127.999 || src > 127.999) { + *tgt_msb = (unsigned char) (0x80); + *tgt_lsb = (unsigned char) (0x00); + return 0; + } else { + *tgt_lsb = (unsigned char) ((src - ((unsigned char) src)) * 256.0); + + if (src < 0.0 && *tgt_lsb != 0x00) + *tgt_msb = (unsigned char) (src - 1); + else + *tgt_msb = (unsigned char) (src); + + if (src >= 0.0) + return 1; + else + return -1; + + } +} + + + +int +eb_d2c_to_float(unsigned char src_lsb, unsigned char src_msb, float *tgt) +{ + if ((src_msb & 0x80) == 0x80) { + *tgt = (float) + (- ( ( ( ((unsigned char) (~ src_msb)) * 16.0) ) + + ( ( ((unsigned char) (~ src_lsb)) & 0xF0) >> 4) + + ( ( ( ((unsigned char) (~ src_lsb)) & 0x0F) +1 ) / 16.0) ) ); + + if (src_msb == 0x80 && src_lsb == 0x00) + return 0; + else + return -1; + + } else { + *tgt = (float) ( (src_msb * 16.0) + ((src_lsb & 0xF0) >> 4) + + ((src_lsb & 0x0F) / 16.0) ); + return 1; + } +} + +int +eb_float_to_d2c(float src, unsigned char *tgt_lsb, unsigned char *tgt_msb) +{ + if (src < -2047.999 || src > 2047.999) { + *tgt_msb = (unsigned char) (0x80); + *tgt_lsb = (unsigned char) (0x00); + return 0; + } else { + *tgt_lsb = + ( ((unsigned char) ( ((unsigned char) src) % 16) << 4) + + ((unsigned char) ( (src - ((unsigned char) src)) * 16.0)) ); + + if (src < 0.0 && *tgt_lsb != 0x00) + *tgt_msb = (unsigned char) ((src / 16.0) - 1); + else + *tgt_msb = (unsigned char) (src / 16.0); + + if (src >= 0.0) + return 1; + else + return -1; + } +} + + + +unsigned char +eb_calc_crc_byte(unsigned char byte, unsigned char init_crc) +{ + unsigned char crc, polynom; + int i; + + crc = init_crc; + + for (i = 0; i < 8; i++) { + + if (crc & 0x80) + polynom = (unsigned char) 0x9B; + else + polynom = (unsigned char) 0; + + crc = (unsigned char) ((crc & ~0x80) << 1); + + if (byte & 0x80) + crc = (unsigned char) (crc | 1); + + crc = (unsigned char) (crc ^ polynom); + byte = (unsigned char) (byte << 1); + } + + return crc; +} + +unsigned char +eb_calc_crc(const unsigned char *buf, int buflen) +{ + int i; + unsigned char crc = 0; + + for (i = 0 ; i < buflen ; i++, buf++) + crc = eb_calc_crc_byte(*buf, crc); + + return crc; +} + diff --git a/lib/ebus-decode.h b/lib/ebus-decode.h new file mode 100644 index 00000000..664c5456 --- /dev/null +++ b/lib/ebus-decode.h @@ -0,0 +1,242 @@ +/* + * Copyright (C) Roland Jax 2012-2013 + * crc calculations from http://www.mikrocontroller.net/topic/75698 + * + * This file is part of ebusd. + * + * ebusd is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ebusd is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ebusd. If not, see http://www.gnu.org/licenses/. + */ + +/** + * @file ebus-decode.h + * @brief ebus decode functions + * @author roland.jax@liwest.at + * @version 0.1 + */ + +/* + * name type description resolution substitue + * BCD CHAR 0 ... + 99 1 FFh + * DATA1b SIGNED CHAR - 127 ... + 127 1 80h + * DATA1c CHAR 0 ... + 100 0,5 FFh + * DATA2b SIGNED INTEGER - 127,99 ... + 127,99 1/256 8000h + * DATA2c SIGNED INTEGER -2047,9 ... + 2047,9 1/16 8000h + */ + +#ifndef EBUS_DECODE_H_ +#define EBUS_DECODE_H_ + + +#include "ebus-common.h" + + + +/** + * @brief calculate integer value of given hex byte + * @param [in] *buf hex byte + * @return integer value | -1 if given byte is no hex value + */ +int eb_htoi(const char *buf); + + + +/** + * @brief print received results in a specific format + * @param [out] *buf pointer to a byte array + * @param [out] *buflen length of byte array + * @return none + */ +void eb_esc(unsigned char *buf, int *buflen); + +/** + * @brief print received results in a specific format + * @param [out] *buf pointer to a byte array + * @param [out] *buflen length of byte array + * @return none + */ +void eb_unesc(unsigned char *buf, int *buflen); + + + +/** + * @brief convert hex bytes to day + * @param [in] day hex byte + * @param [out] *tgt pointer to day string xxx + * @return 0 OK | -1 values out of range + */ +int eb_day_to_str(unsigned char day, char *tgt); + + + +/** + * @brief convert hex bytes to date + * @param [in] dd day hex byte + * @param [in] mm month hex byte + * @param [in] yy year hex byte + * @param [out] *tgt pointer to date string dd.mm.yyyy + * @return 0 OK | -1 values out of range + */ +int eb_dat_to_str(unsigned char dd, unsigned char mm, unsigned char yy, char *tgt); + +/** + * @brief convert char date string to hex + * @param [in] dd day int value + * @param [in] mm month int value + * @param [in] yy year int value + * @param [out] *tgt pointer to hex + * @return 0 OK | -1 values out of range + */ +int eb_str_to_dat(int dd, int mm, int yy, unsigned char *tgt); + + + +/** + * @brief convert hex bytes to date + * @param [in] hh hour hex byte + * @param [in] mm minute hex byte + * @param [in] ss second hex byte + * @param [out] *tgt pointer to date string hh:mm:ss + * @return 0 OK | -1 values out of range + */ +int eb_tim_to_str(unsigned char hh, unsigned char mm, unsigned char ss, char *tgt); + +/** + * @brief convert char time string to hex + * @param [in] hh hour int value + * @param [in] mm minute int value + * @param [in] ss second int value + * @param [out] *tgt pointer to hex + * @return 0 OK | -1 values out of range + */ +int eb_str_to_tim(int hh, int mm, int ss, unsigned char *tgt); + + + +/** + * @brief convert bcd hex byte to int + * @param [in] src bcd hex byte + * @param [out] *tgt pointer to int value + * @return 0 substitute value | 1 positive value + */ +int eb_bcd_to_int(unsigned char src, int *tgt); + +/** + * @brief convert int to bcd hex byte + * @param [in] src int value + * @param [out] *tgt pointer to hex byte + * @return 0 substitute value | 1 positive value + */ +int eb_int_to_bcd(int src, unsigned char *tgt); + + + +/** + * @brief convert data1b hex byte to int + * @param [in] src data1b hex byte + * @param [out] *tgt pointer to int value + * @return 0 substitute value | 1 positive value | -1 negative value + */ +int eb_d1b_to_int(unsigned char src, int *tgt); + +/** + * @brief convert int to data1b hex byte + * @param [in] src int value + * @param [out] *tgt pointer to data1b hex byte + * @return 0 substitute value | 1 positive value | -1 negative value + */ +int eb_int_to_d1b(int src, unsigned char *tgt); + + + +/** + * @brief convert data1c hex byte to float + * @param [in] src data1c hex byte + * @param [out] *tgt pointer to float value + * @return 0 substitute value | 1 positive value | -1 negative value + */ +int eb_d1c_to_float(unsigned char src, float *tgt); + +/** + * @brief convert float to data1c hex byte + * @param [in] src float value + * @param [out] *tgt pointer to data1c hex byte + * @return 0 substitute value | 1 positive value | -1 negative value + */ +int eb_float_to_d1c(float src, unsigned char *tgt); + + + +/** + * @brief convert data2b hex bytes to float + * @param [in] src_lsb least significant data2b hex byte + * @param [in] src_msb most significant data2b hex byte + * @param [out] *tgt pointer to float value + * @return 0 substitute value | 1 positive value | -1 negative value + */ +int eb_d2b_to_float(unsigned char src_lsb, unsigned char src_msb, float *tgt); + +/** + * @brief convert float to data2b hex bytes + * @param [in] src float value + * @param [out] *tgt_lsb pointer to least significant data2b hex byte + * @param [out] *tgt_msb pointer to most significant data2b hex byte + * @return 0 substitute value | 1 positive value | -1 negative value + */ +int eb_float_to_d2b(float src, unsigned char *tgt_lsb, unsigned char *tgt_msb); + + + +/** + * @brief convert data2c hex bytes to float + * @param [in] src_lsb least significant data2c hex byte + * @param [in] src_msb most significant data2c hex byte + * @param [out] *tgt pointer to float value + * @return 0 substitute value | 1 positive value | -1 negative value + */ +int eb_d2c_to_float(unsigned char src_lsb, unsigned char src_msb, float *tgt); + +/** + * @brief convert float to data2c hex bytes + * @param [in] src float value + * @param [out] *tgt_lsb pointer to least significant data2c hex byte + * @param [out] *tgt_msb pointer to most significant data2c hex byte + * @return 0 substitute value | 1 positive value | -1 negative value + */ +int eb_float_to_d2c(float src, unsigned char *tgt_lsb, unsigned char *tgt_msb); + + + +/** + * @brief calculate crc of hex byte + * @param [in] byte byte to calculate + * @param [in] init_crc start value for calculation + * @return new calculated crc byte from byte and init crc byte + */ +unsigned char eb_calc_crc_byte(unsigned char byte, unsigned char init_crc); + +/** + * @brief calculate crc of given hex array + * + * \li crc calculation "CRC-8-WCDMA" with Polynom "x^8+x^7+x^4+x^3+x+1" + * \li crc calculations by http://www.mikrocontroller.net/topic/75698 + * + * @param [in] *bytes pointer to a byte array + * @param [in] size length of given bytes + * @return calculated crc byte + */ +unsigned char eb_calc_crc(const unsigned char *bytes, int size); + + + +#endif /* EBUS_DECODE_H_ */ diff --git a/lib/log.c b/lib/log.c new file mode 100644 index 00000000..608a4fdc --- /dev/null +++ b/lib/log.c @@ -0,0 +1,181 @@ +/* + * Copyright (C) Roland Jax 2012-2013 + * + * This file is part of ebusd. + * + * ebusd is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ebusd is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ebusd. If not, see http://www.gnu.org/licenses/. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" + +static unsigned char loglvl = L_NUL; +static int logtxtlen = sizeof(logtxt) / sizeof(char*); + +static FILE *logfp = NULL; + +char *log_time(char *time); +char *log_txt(unsigned char lvl); + +void +log_file(FILE *fp) +{ + if(logfp) + fclose(logfp); + + logfp = fp; +} + +void +log_level(char *lvl) +{ + unsigned char tmp; + int i; + char *par; + + loglvl = L_NUL; + + par = strtok(lvl, ", "); + while (par) { + if (strncasecmp(par, "ALL", 3) == 0) { + loglvl = L_ALL; + break; + } + + tmp = 0x01; + for (i = 0; i < logtxtlen; i++) { + if (strncasecmp(par, logtxt[i], 3) == 0) { + loglvl |= (tmp << i); + break; + } + } + par = strtok(NULL, ", "); + } +} + +int +log_open(const char *file, int foreground) +{ + FILE *fp = NULL; + + if (foreground) { + log_file(stdout); + } else { + if (file) { + if ((fp = fopen(file, "a+")) == 0) { + fprintf(stderr, "can't open logfile: %s\n", file); + + return -1; + } + + log_file(fp); + } + } + + openlog(NULL, LOG_CONS|LOG_NDELAY|LOG_PID, LOG_DAEMON); + + return 0; +} + +void +log_close() +{ + if (logfp) { + fflush(logfp); + fclose(logfp); + } + + closelog(); +} + +char * +log_time(char *time) +{ + struct timeval tv; + struct tm *tm; + + gettimeofday(&tv, NULL); + tm = localtime(&tv.tv_sec); + + sprintf(time, "%04d-%02d-%02d %02d:%02d:%02d.%03ld", + tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec, tv.tv_usec/1000); + + return time; +} + +char * +log_txt(unsigned char lvl) +{ + char *type = NULL; + if (logtxtlen > 0) { + unsigned char tmp; + int i; + + tmp = 0x01; + for (i = 0; i < logtxtlen; i++) { + if (lvl == L_ALL) { + type = "ALL"; + break; + } + + if ((lvl & (tmp << i)) != 0x00 && + (loglvl & (tmp << i)) != 0x00) { + type = (char *)logtxt[i]; + break; + } + } + } + return type; +} + +void +log_print(unsigned char lvl, const char *txt, ...) +{ + char time[24]; + char buf[512]; + va_list ap; + + va_start(ap, txt); + vsprintf(buf, txt, ap); + + if ((loglvl & lvl) != 0x00) { + if (logfp) { + //~ fprintf(logfp, "%s [0x%02x %s] %s\n", + //~ log_time(time), lvl , log_txt(lvl), buf); + fprintf(logfp, "%s [%s] %s\n", + log_time(time), log_txt(lvl), buf); + fflush(logfp); + + } else { + //~ syslog(LOG_INFO, "[0x%02x %s] %s\n", + //~ lvl, log_txt(lvl), txt); + syslog(LOG_INFO, "[%s] %s\n", log_txt(lvl), txt); + } + } + + va_end(ap); +} + diff --git a/lib/log.h b/lib/log.h new file mode 100644 index 00000000..397882d7 --- /dev/null +++ b/lib/log.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) Roland Jax 2012-2013 + * + * This file is part of ebusd. + * + * ebusd is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ebusd is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ebusd. If not, see http://www.gnu.org/licenses/. + */ + +#ifndef LOG_H_ +#define LOG_H_ + +#define L_NUL 0x00 +#define L_ALL 0xFF + +#define L_INF 0x01 +#define L_NOT 0x02 +#define L_WAR 0x04 +#define L_ERR 0x08 +#define L_DBG 0x10 +#define L_EBH 0x20 +#define L_EBS 0x40 +#define L_NET 0x80 + +#define LOGTXT "INF, NOT, WAR, ERR, DBG, EBH, EBS, NET, ALL" +static const char *logtxt[] = {"INF","NOT","WAR","ERR","DBG","EBH","EBS","NET"}; + +#define err_if(exp) \ + if (exp) { log_print(L_ERR, "%s: %d: %s: Error %s", \ + __FILE__, __LINE__, __PRETTY_FUNCTION__, strerror(errno));\ + } + +#define err_ret_if(exp, ret) \ + if (exp) { log_print(L_ERR, "%s: %d: %s: Error %s", \ + __FILE__, __LINE__, __PRETTY_FUNCTION__, strerror(errno));\ + return (ret); \ + } + +void log_file(FILE *fp); +void log_level(char *lvl); + +int log_open(const char *logfile, int foreground); +void log_close(); + +void log_print(unsigned char lvl, const char *txt, ...); + +#endif /* LOG_H_ */ diff --git a/missing b/missing new file mode 100755 index 00000000..86a8fc31 --- /dev/null +++ b/missing @@ -0,0 +1,331 @@ +#! /bin/sh +# Common stub for a few missing GNU programs while installing. + +scriptversion=2012-01-06.13; # UTC + +# Copyright (C) 1996, 1997, 1999, 2000, 2002, 2003, 2004, 2005, 2006, +# 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc. +# Originally by Fran,cois Pinard , 1996. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +if test $# -eq 0; then + echo 1>&2 "Try \`$0 --help' for more information" + exit 1 +fi + +run=: +sed_output='s/.* --output[ =]\([^ ]*\).*/\1/p' +sed_minuso='s/.* -o \([^ ]*\).*/\1/p' + +# In the cases where this matters, `missing' is being run in the +# srcdir already. +if test -f configure.ac; then + configure_ac=configure.ac +else + configure_ac=configure.in +fi + +msg="missing on your system" + +case $1 in +--run) + # Try to run requested program, and just exit if it succeeds. + run= + shift + "$@" && exit 0 + # Exit code 63 means version mismatch. This often happens + # when the user try to use an ancient version of a tool on + # a file that requires a minimum version. In this case we + # we should proceed has if the program had been absent, or + # if --run hadn't been passed. + if test $? = 63; then + run=: + msg="probably too old" + fi + ;; + + -h|--h|--he|--hel|--help) + echo "\ +$0 [OPTION]... PROGRAM [ARGUMENT]... + +Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an +error status if there is no known handling for PROGRAM. + +Options: + -h, --help display this help and exit + -v, --version output version information and exit + --run try to run the given command, and emulate it if it fails + +Supported PROGRAM values: + aclocal touch file \`aclocal.m4' + autoconf touch file \`configure' + autoheader touch file \`config.h.in' + autom4te touch the output file, or create a stub one + automake touch all \`Makefile.in' files + bison create \`y.tab.[ch]', if possible, from existing .[ch] + flex create \`lex.yy.c', if possible, from existing .c + help2man touch the output file + lex create \`lex.yy.c', if possible, from existing .c + makeinfo touch the output file + yacc create \`y.tab.[ch]', if possible, from existing .[ch] + +Version suffixes to PROGRAM as well as the prefixes \`gnu-', \`gnu', and +\`g' are ignored when checking the name. + +Send bug reports to ." + exit $? + ;; + + -v|--v|--ve|--ver|--vers|--versi|--versio|--version) + echo "missing $scriptversion (GNU Automake)" + exit $? + ;; + + -*) + echo 1>&2 "$0: Unknown \`$1' option" + echo 1>&2 "Try \`$0 --help' for more information" + exit 1 + ;; + +esac + +# normalize program name to check for. +program=`echo "$1" | sed ' + s/^gnu-//; t + s/^gnu//; t + s/^g//; t'` + +# Now exit if we have it, but it failed. Also exit now if we +# don't have it and --version was passed (most likely to detect +# the program). This is about non-GNU programs, so use $1 not +# $program. +case $1 in + lex*|yacc*) + # Not GNU programs, they don't have --version. + ;; + + *) + if test -z "$run" && ($1 --version) > /dev/null 2>&1; then + # We have it, but it failed. + exit 1 + elif test "x$2" = "x--version" || test "x$2" = "x--help"; then + # Could not run --version or --help. This is probably someone + # running `$TOOL --version' or `$TOOL --help' to check whether + # $TOOL exists and not knowing $TOOL uses missing. + exit 1 + fi + ;; +esac + +# If it does not exist, or fails to run (possibly an outdated version), +# try to emulate it. +case $program in + aclocal*) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified \`acinclude.m4' or \`${configure_ac}'. You might want + to install the \`Automake' and \`Perl' packages. Grab them from + any GNU archive site." + touch aclocal.m4 + ;; + + autoconf*) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified \`${configure_ac}'. You might want to install the + \`Autoconf' and \`GNU m4' packages. Grab them from any GNU + archive site." + touch configure + ;; + + autoheader*) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified \`acconfig.h' or \`${configure_ac}'. You might want + to install the \`Autoconf' and \`GNU m4' packages. Grab them + from any GNU archive site." + files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}` + test -z "$files" && files="config.h" + touch_files= + for f in $files; do + case $f in + *:*) touch_files="$touch_files "`echo "$f" | + sed -e 's/^[^:]*://' -e 's/:.*//'`;; + *) touch_files="$touch_files $f.in";; + esac + done + touch $touch_files + ;; + + automake*) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'. + You might want to install the \`Automake' and \`Perl' packages. + Grab them from any GNU archive site." + find . -type f -name Makefile.am -print | + sed 's/\.am$/.in/' | + while read f; do touch "$f"; done + ;; + + autom4te*) + echo 1>&2 "\ +WARNING: \`$1' is needed, but is $msg. + You might have modified some files without having the + proper tools for further handling them. + You can get \`$1' as part of \`Autoconf' from any GNU + archive site." + + file=`echo "$*" | sed -n "$sed_output"` + test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"` + if test -f "$file"; then + touch $file + else + test -z "$file" || exec >$file + echo "#! /bin/sh" + echo "# Created by GNU Automake missing as a replacement of" + echo "# $ $@" + echo "exit 0" + chmod +x $file + exit 1 + fi + ;; + + bison*|yacc*) + echo 1>&2 "\ +WARNING: \`$1' $msg. You should only need it if + you modified a \`.y' file. You may need the \`Bison' package + in order for those modifications to take effect. You can get + \`Bison' from any GNU archive site." + rm -f y.tab.c y.tab.h + if test $# -ne 1; then + eval LASTARG=\${$#} + case $LASTARG in + *.y) + SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'` + if test -f "$SRCFILE"; then + cp "$SRCFILE" y.tab.c + fi + SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'` + if test -f "$SRCFILE"; then + cp "$SRCFILE" y.tab.h + fi + ;; + esac + fi + if test ! -f y.tab.h; then + echo >y.tab.h + fi + if test ! -f y.tab.c; then + echo 'main() { return 0; }' >y.tab.c + fi + ;; + + lex*|flex*) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified a \`.l' file. You may need the \`Flex' package + in order for those modifications to take effect. You can get + \`Flex' from any GNU archive site." + rm -f lex.yy.c + if test $# -ne 1; then + eval LASTARG=\${$#} + case $LASTARG in + *.l) + SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'` + if test -f "$SRCFILE"; then + cp "$SRCFILE" lex.yy.c + fi + ;; + esac + fi + if test ! -f lex.yy.c; then + echo 'main() { return 0; }' >lex.yy.c + fi + ;; + + help2man*) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified a dependency of a manual page. You may need the + \`Help2man' package in order for those modifications to take + effect. You can get \`Help2man' from any GNU archive site." + + file=`echo "$*" | sed -n "$sed_output"` + test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"` + if test -f "$file"; then + touch $file + else + test -z "$file" || exec >$file + echo ".ab help2man is required to generate this page" + exit $? + fi + ;; + + makeinfo*) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified a \`.texi' or \`.texinfo' file, or any other file + indirectly affecting the aspect of the manual. The spurious + call might also be the consequence of using a buggy \`make' (AIX, + DU, IRIX). You might want to install the \`Texinfo' package or + the \`GNU make' package. Grab either from any GNU archive site." + # The file to touch is that specified with -o ... + file=`echo "$*" | sed -n "$sed_output"` + test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"` + if test -z "$file"; then + # ... or it is the one specified with @setfilename ... + infile=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'` + file=`sed -n ' + /^@setfilename/{ + s/.* \([^ ]*\) *$/\1/ + p + q + }' $infile` + # ... or it is derived from the source name (dir/f.texi becomes f.info) + test -z "$file" && file=`echo "$infile" | sed 's,.*/,,;s,.[^.]*$,,'`.info + fi + # If the file does not exist, the user really needs makeinfo; + # let's fail without touching anything. + test -f $file || exit 1 + touch $file + ;; + + *) + echo 1>&2 "\ +WARNING: \`$1' is needed, and is $msg. + You might have modified some files without having the + proper tools for further handling them. Check the \`README' file, + it often tells you about the needed prerequisites for installing + this package. You may also peek at any GNU archive site, in case + some other package would contain this missing \`$1' program." + exit 1 + ;; +esac + +exit 0 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 00000000..a43c71c1 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,9 @@ +bin_PROGRAMS = ebusd + +ebusd_SOURCES = ebusd.c ebusd.h utils.c utils.h +ebusd_LDADD = $(top_srcdir)/lib/libebus.a $(top_srcdir)/lib/liblog.a +ebusd_CFLAGS = -I$(top_srcdir)/lib + +distclean-local: + -rm -f Makefile.in + diff --git a/src/ebusd.c b/src/ebusd.c new file mode 100644 index 00000000..f6c85388 --- /dev/null +++ b/src/ebusd.c @@ -0,0 +1,742 @@ +/* + * Copyright (C) Roland Jax 2012-2013 + * + * This file is part of ebusd. + * + * ebusd is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ebusd is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ebusd. If not, see http://www.gnu.org/licenses/. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "utils.h" +#include "ebus-decode.h" +#include "ebus-cmd.h" +#include "ebus-bus.h" +#include "ebusd.h" + + +/* global variables */ +const char *progname; + +static int pidfile_locked = NO; +static int msg_queue_on = NO; + +static int pidfd = UNSET; /* pidfile file descriptor */ +static int serialfd = UNSET; /* serial file descriptor */ +static int socketfd = UNSET; /* socket file descriptor */ + + +static char address[3]; +static char cfgdir[CFG_LINELEN]; +static char cfgfile[CFG_LINELEN]; +static char device[CFG_LINELEN]; +static char extension[10]; +static int foreground = UNSET; +static char loglevel[CFG_LINELEN]; +static char logfile[CFG_LINELEN]; +static char pidfile[CFG_LINELEN]; +static int port = UNSET; +static int rawdump = UNSET; +static char rawfile[CFG_LINELEN]; +static int showraw = UNSET; +static int settings = UNSET; +static int localhost = UNSET; +static int get_retry = UNSET; +static int skip_ack = UNSET; +static int max_wait = UNSET; +static int send_retry = UNSET; +static int print_size = UNSET; + + + +static char options[] = "a:c:C:d:e:fl:L:P:p:rR:sStvh"; + +static struct option opts[] = { + {"address", required_argument, NULL, 'a'}, + {"cfgfdir", required_argument, NULL, 'c'}, + {"cfgfile", required_argument, NULL, 'C'}, + {"device", required_argument, NULL, 'd'}, + {"extension", required_argument, NULL, 'e'}, + {"foreground", no_argument, NULL, 'f'}, + {"loglevel", required_argument, NULL, 'l'}, + {"logfile", required_argument, NULL, 'L'}, + {"pidfile", required_argument, NULL, 'P'}, + {"port", required_argument, NULL, 'p'}, + {"rawdump", no_argument, NULL, 'r'}, + {"rawfile", required_argument, NULL, 'R'}, + {"showraw", no_argument, NULL, 's'}, + {"settings", no_argument, NULL, 'S'}, + {"localhost", no_argument, NULL, 't'}, + {"version", no_argument, NULL, 'v'}, + {"help", no_argument, NULL, 'h'}, + {NULL, no_argument, NULL, 0 }, +}; + +static struct config cfg[] = { + +{"address", STR, &address, "\tbus address (" NUMSTR(EBUS_QQ) ")"}, +{"cfgdir", STR, &cfgdir, "\tconfiguration directory of command files (" DAEMON_CFGDIR ")"}, +{"cfgfile", STR, &cfgfile, "\tdaemon configuration file (" DAEMON_CFGFILE ")"}, +{"device", STR, &device, "\tserial device (" SERIAL_DEVICE ")"}, +{"extension", STR, &extension, "extension of command files (" DAEMON_EXTENSION ")"}, +{"foreground", BOL, &foreground, "run in foreground"}, +{"loglevel", STR, &loglevel, "\tlog level (INF | " LOGTXT ")"}, +{"logfile", STR, &logfile, "\tlog file (" DAEMON_LOGFILE ")"}, +{"pidfile", STR, &pidfile, "\tpid file (" DAEMON_PIDFILE ")"}, +{"port", NUM, &port, "\tport (" NUMSTR(SOCKET_PORT) ")"}, +{"rawdump", BOL, &rawdump, "\tdump raw ebus data to file"}, +{"rawfile", STR, &rawfile, "\traw file (" DAEMON_RAWFILE ")"}, +{"showraw", BOL, &showraw, "\tprint raw data"}, +{"settings", BOL, &settings, "\tprint daemon settings"}, +{"localhost", BOL, &localhost, "allow only connection from localhost"}, +{"get_retry", NUM, &get_retry, NULL}, +{"skip_ack", NUM, &skip_ack, NULL}, +{"max_wait", NUM, &max_wait, NULL}, +{"send_retry", NUM, &send_retry, NULL}, +{"print_size", NUM, &print_size, NULL}, +{"version", STR, NULL, "\tprint version information"}, +{"help", STR, NULL, "\tprint this message"} +}; + +const int cfglen = sizeof(cfg) / sizeof(cfg[0]); + +void +usage(void) +{ + fprintf(stdout, "\nUsage: %s [OPTIONS]\n", progname); + + int i, skip; + + skip = 0; + + for (i = 0; i < cfglen; i++) { + if (cfg[i].info != NULL) { + fprintf(stdout, " -%c --%s\t%s\n", + opts[i - skip].val, + opts[i - skip].name, + cfg[i].info); + } else { + skip++; + } + } + + fprintf(stdout, "\n"); +} + +void +cmdline(int *argc, char ***argv) +{ + for (;;) { + int i; + + i = getopt_long(*argc, *argv, options, opts, NULL); + + if (i == -1) + break; + + switch (i) { + case 'a': + if (strlen(optarg) > 2) + strncpy(address, &optarg[strlen(optarg) - 2 ], 2); + else + strncpy(address, optarg, strlen(optarg)); + + break; + case 'c': + strncpy(cfgdir, optarg, strlen(optarg)); + break; + case 'C': + strncpy(cfgfile, optarg, strlen(optarg)); + break; + case 'd': + strncpy(device, optarg, strlen(optarg)); + break; + case 'e': + strncpy(extension, optarg, strlen(optarg)); + break; + case 'f': + foreground = YES; + break; + case 'l': + strncpy(loglevel, optarg, strlen(optarg)); + break; + case 'L': + strncpy(logfile, optarg, strlen(optarg)); + break; + case 'P': + strncpy(pidfile, optarg, strlen(optarg)); + break; + case 'p': + if (isdigit(*optarg)) + port = atoi(optarg); + break; + case 'r': + rawdump = YES; + break; + case 'R': + strncpy(rawfile, optarg, strlen(optarg)); + rawdump = YES; + break; + case 's': + showraw = YES; + break; + case 'S': + settings = YES; + break; + case 't': + localhost = YES; + break; + case 'v': + fprintf(stdout, DAEMON_NAME " " DAEMON_VERSION "\n"); + exit(EXIT_SUCCESS); + case 'h': + default: + usage(); + exit(EXIT_FAILURE); + break; + } + } +} + +void +set_unset(void) +{ + + if (*address == '\0') + strncpy(address , &NUMSTR(EBUS_QQ)[2], 2); + + if (*cfgdir == '\0') + strncpy(cfgdir , DAEMON_CFGDIR, strlen(DAEMON_CFGDIR)); + + if (*device == '\0') + strncpy(device , SERIAL_DEVICE, strlen(SERIAL_DEVICE)); + + if (*extension == '\0') { + strncpy(extension , DAEMON_EXTENSION, strlen(DAEMON_EXTENSION)); + } + + if (foreground == UNSET) + foreground = NO; + + if (*loglevel == '\0') + strncpy(loglevel , DAEMON_LOGLEVEL, strlen(DAEMON_LOGLEVEL)); + + if (*logfile == '\0') + strncpy(logfile , DAEMON_LOGFILE, strlen(DAEMON_LOGFILE)); + + if (*pidfile == '\0') + strncpy(pidfile , DAEMON_PIDFILE, strlen(DAEMON_PIDFILE)); + + if (port == UNSET) + port = SOCKET_PORT; + + if (rawdump == UNSET) + rawdump = NO; + + if (*rawfile == '\0') + strncpy(rawfile , DAEMON_RAWFILE, strlen(DAEMON_RAWFILE)); + + if (showraw == UNSET) + showraw = NO; + + if (settings == UNSET) + settings = NO; + + if (localhost == UNSET) + localhost = NO; + + if (get_retry == UNSET) + get_retry = EBUS_GET_RETRY; + /* set max */ + if (get_retry > EBUS_GET_RETRY_MAX) + get_retry = EBUS_GET_RETRY_MAX; + + if (skip_ack == UNSET) + skip_ack = EBUS_SKIP_ACK; + + if (max_wait == UNSET) + max_wait = EBUS_MAX_WAIT; + + if (send_retry == UNSET) + send_retry = EBUS_SEND_RETRY; + /* set max */ + if (send_retry > EBUS_SEND_RETRY_MAX) + send_retry = EBUS_SEND_RETRY_MAX; + + if (print_size == UNSET) + print_size = EBUS_PRINT_SIZE; + +} + +void +signal_handler(int sig) { + switch(sig) { + case SIGHUP: + log_print(L_ALL, "SIGHUP received"); + syslog(LOG_INFO, "SIGHUP received"); + break; + case SIGINT: + log_print(L_ALL, "SIGINT received - logfile reopen"); + syslog(LOG_INFO, "SIGINT received - logfile reopen"); + log_open(logfile, foreground); + break; + case SIGTERM: + log_print(L_ALL, "daemon exiting"); + syslog(LOG_INFO, "daemon exiting"); + cleanup(EXIT_SUCCESS); + break; + default: + log_print(L_ALL, "unknown signal %s", strsignal(sig)); + syslog(LOG_INFO, "unknown signal %s", strsignal(sig)); + break; + } +} + +void +daemonize(void) +{ + pid_t pid; + + /* fork off the parent process */ + pid = fork(); + if (pid < 0) { + err_if(1); + cleanup(EXIT_FAILURE); + } + /* If we got a good PID, then we can exit the parent process */ + if (pid > 0) { + /* printf("Child process created: %d\n", pid); */ + exit(EXIT_SUCCESS); + } + + /* At this point we are executing as the child process */ + + /* Set file permissions 750 */ + umask(027); + + /* Create a new SID for the child process and */ + /* detach the process from the parent (normally a shell) */ + if (setsid() < 0) { + err_if(1); + cleanup(EXIT_FAILURE); + } + + /* Change the current working directory. This prevents the current + directory from being locked; hence not being able to remove it. */ + if (chdir(DAEMON_WORKDIR) < 0) { + /* Log any failure here */ + err_if(1); + cleanup(EXIT_FAILURE); + } + + /* Route I/O connections */ + close(STDIN_FILENO); + close(STDOUT_FILENO); + close(STDERR_FILENO); + + /* write pidfile and try to lock it */ + if (pid_file_open(pidfile, &pidfd) == -1) { + log_print(L_ERR, "can't open pidfile: %s\n", pidfile); + cleanup(EXIT_FAILURE); + } else { + pidfile_locked = YES; + log_print(L_INF, "%s created.", pidfile); + } + + /* Cancel certain signals */ + signal(SIGCHLD, SIG_DFL); /* A child process dies */ + signal(SIGTSTP, SIG_IGN); /* Various TTY signals */ + signal(SIGTTOU, SIG_IGN); /* Ignore TTY background writes */ + signal(SIGTTIN, SIG_IGN); /* Ignore TTY background reads */ + + /* Trap signals that we expect to receive */ + signal(SIGHUP, signal_handler); + signal(SIGINT, signal_handler); + signal(SIGTERM, signal_handler); +} + +void +cleanup(int state) +{ + + /* free msg queue */ + if (msg_queue_on == YES) { + msg_queue_free(); + log_print(L_INF, "msg queue freeed"); + } + + /* close listing tcp socket */ + if (socketfd > 0) { + if (sock_close(socketfd) == -1) + log_print(L_ERR, "can't close port: %d", port); + else + log_print(L_INF, "port %d closed", port); + } + + /* close serial device */ + if (serialfd > 0) { + if (eb_serial_close() == -1) + log_print(L_ERR, "can't close device: %s", device); + else + log_print(L_INF, "%s closed", device); + } + + /* close rawfile */ + if (rawdump == YES) { + if (eb_raw_file_close() == -1) + log_print(L_ERR, "can't close rawfile: %s\n", rawfile); + else + log_print(L_INF, "%s closed", rawfile); + } + + + /* free mem for ebus commands */ + eb_cmd_dir_free(); + + if (foreground == NO) { + + /* delete PID file */ + if (pidfile_locked) + if (pid_file_close(pidfile, pidfd) == -1) + log_print(L_INF, "%s deleted", pidfile); + + /* Reset all signal handlers to default */ + signal(SIGCHLD, SIG_DFL); + signal(SIGTSTP, SIG_DFL); + signal(SIGTTOU, SIG_DFL); + signal(SIGTTIN, SIG_DFL); + signal(SIGHUP, SIG_DFL); + signal(SIGINT, SIG_DFL); + signal(SIGTERM, SIG_DFL); + + /* print end message */ + log_print(L_ALL, DAEMON_NAME " " DAEMON_VERSION " stopped"); + syslog(LOG_INFO, DAEMON_NAME " " DAEMON_VERSION " stopped"); + } + + /* close logging system */ + log_close(); + + exit(state); +} + + +void +main_loop(void) +{ + int maxfd, sfd_closed, timeout_reached; + fd_set listenfds; + struct timeval timeout; + + sfd_closed = NO; + timeout_reached = NO; + + FD_ZERO(&listenfds); + FD_SET(serialfd, &listenfds); + FD_SET(socketfd, &listenfds); + + maxfd = socketfd; + + /* serialfd should be always lower then socketfd */ + if (serialfd > socketfd) { + log_print(L_ERR, "serialfd %d > %d socketfd", serialfd, socketfd); + cleanup(EXIT_FAILURE); + } + + for (;;) { + fd_set readfds; + int readfd; + int ret; + + /* set select timeout 10 secs */ + timeout.tv_sec = 10; + timeout.tv_usec = 0; + + /* set readfds to inital listenfds */ + readfds = listenfds; + + /* check if the usb device is working */ + if (eb_serial_valid() < 0 || timeout_reached == YES) { + timeout_reached = NO; + + if (serialfd > 0 && sfd_closed == NO) { + log_print(L_ERR, "serial device is invalid"); + sfd_closed = YES; + + /* close listing tcp socket */ + if (socketfd > 0) { + if (sock_close(socketfd) == -1) + log_print(L_ERR, "can't close port: %d", port); + else + log_print(L_INF, "port %d closed", port); + } + + /* close serial device */ + if (eb_serial_close() == -1) + log_print(L_ERR, "can't close device: %s", device); + else + log_print(L_INF, "%s closed", device); + + } + + /* need sleep to prevent high cpu consumption */ + sleep(1); + + /* open serial device */ + if (eb_serial_open(device, &serialfd) == 0) { + log_print(L_INF, "%s opened", device); + sfd_closed = NO; + } + + /* open listing tcp socket */ + if (sfd_closed == NO && sock_open(&socketfd, port, localhost) == 0) + log_print(L_INF, "port %d opened", port); + + continue; + } + + ret = select(maxfd + 1, &readfds, NULL, NULL, &timeout); + + /* timeout after 10 secs means that ebus is probably + disconnected or USB device is dead */ + if (ret == 0) { + log_print(L_WAR, "select timeout (%d) reached", timeout.tv_sec); + timeout_reached = YES; + continue; + + /* ignore signals */ + } else if ((ret < 0) && (errno == EINTR)) { + /* log_print(L_NOT, "get signal at select: %s", strerror(errno)); */ + continue; + + /* on other errors */ + } else if (ret < 0) { + err_if(1); + cleanup(EXIT_FAILURE); + } + + /* new data from serial port? */ + if (FD_ISSET(serialfd, &readfds)) { + + /* get cycle message from bus */ + ret = eb_cyc_data_recv(); + + /* send msg to bus - only when cyc buf is empty */ + if (ret == 0 && msg_queue_entries() > 0) { + char tcpbuf[SOCKET_BUFSIZE]; + char data[MSG_QUEUE_MSG_SIZE]; + int tcpbuflen, id, clientfd; + + memset(tcpbuf, '\0', sizeof(tcpbuf)); + tcpbuflen = sizeof(tcpbuf); + + memset(data, '\0', sizeof(data)); + + /* get next entry from msg queue */ + msg_queue_msg_del(&id, data, &clientfd); + + /* just do it */ + eb_execute(id, data, tcpbuf, &tcpbuflen); + + /* send answer */ + sock_client_write(clientfd, tcpbuf, tcpbuflen); + } + + } + + /* new incoming connection at TCP port arrived? */ + if (FD_ISSET(socketfd, &readfds)) { + + /* get new TCP client fd*/ + ret = sock_client_accept(socketfd, &readfd); + if (readfd >= 0) { + /* add new TCP client fd to listenfds */ + FD_SET(readfd, &listenfds); + (readfd > maxfd) ? (maxfd = readfd) : (1); + } + } + + /* run through connected sockets for new data */ + for (readfd = socketfd + 1; readfd <= maxfd; ++readfd) { + + /* check all connected clients */ + if (FD_ISSET(readfd, &readfds)) { + char tcpbuf[SOCKET_BUFSIZE]; + char data[MSG_QUEUE_MSG_SIZE]; + int tcpbuflen; + + memset(tcpbuf, '\0', sizeof(tcpbuf)); + tcpbuflen = sizeof(tcpbuf); + + memset(data, '\0', sizeof(data)); + + /* get message from client */ + ret = sock_client_read(readfd, tcpbuf, &tcpbuflen); + + /* remove dead TCP client */ + if (ret < 0) { + FD_CLR(readfd, &listenfds); + continue; + } + + /* handle different commands */ + if (strncasecmp("shutdown", tcpbuf, 8) == 0) + cleanup(EXIT_SUCCESS); + + if (strncasecmp("loglevel", tcpbuf, 8) == 0) { + strncpy(loglevel, tcpbuf, strlen(tcpbuf)); + log_level(loglevel); + continue; + } + + /* search ebus command */ + if (tcpbuflen > 0) + ret = eb_cmd_search_com(tcpbuf, data); + else + ret = -1; + + /* command not found */ + if (ret < 0) { + memset(tcpbuf, '\0', sizeof(tcpbuf)); + strcpy(tcpbuf, "command not found\n"); + tcpbuflen = strlen(tcpbuf); + + /* send answer */ + sock_client_write(readfd, tcpbuf, tcpbuflen); + + } else { + msg_queue_msg_add(ret, data, readfd); + } + + } + } + } +} + +int +main(int argc, char *argv[]) +{ + int tmp; + + /* set progname */ + progname = (const char *)strrchr(argv[0], '/'); + progname = progname ? (progname + 1) : argv[0]; + + /* read command line */ + cmdline(&argc, &argv); + + /* set default cfgfile */ + if (*cfgfile == '\0') + strncpy(cfgfile , DAEMON_CFGFILE, strlen(DAEMON_CFGFILE)); + + /* read config file */ + if (cfg_file_read(cfgfile, cfg, cfglen) == -1) + fprintf(stderr, "can't open cfgfile: %s ==> " \ + "build in settings will be used\n", cfgfile); + + /* set unset configuration */ + set_unset(); + + /* print configuration */ + if (settings == YES) + cfg_print(cfg, cfglen); + + + /* set ebus configuration */ + eb_set_rawdump(rawdump); + eb_set_showraw(showraw); + + tmp = (eb_htoi(&address[0])) * 16 + (eb_htoi(&address[1])); + eb_set_qq((unsigned char) tmp); + + eb_set_get_retry(get_retry); + eb_set_skip_ack(skip_ack); + eb_set_max_wait(max_wait); + eb_set_send_retry(send_retry); + eb_set_print_size(print_size); + + /* open log */ + log_level(loglevel); + log_open(logfile, foreground); + + /* to be daemon */ + if (foreground == NO) { + log_print(L_ALL, DAEMON_NAME " " DAEMON_VERSION " started"); + syslog(LOG_INFO, DAEMON_NAME " " DAEMON_VERSION " started"); + daemonize(); + } + + /* read ebus command configuration files */ + if (eb_cmd_dir_read(cfgdir, extension) == -1) + log_print(L_WAR, "error during read command file"); + + /* open raw file */ + if (rawdump == YES) { + if (eb_raw_file_open(rawfile) == -1) { + log_print(L_ALL, "can't open rawfile: %s", rawfile); + cleanup(EXIT_FAILURE); + } else { + log_print(L_INF, "%s opened", rawfile); + } + + } + + /* open serial device */ + if (eb_serial_open(device, &serialfd) == -1) { + log_print(L_ALL, "can't open device: %s", device); + cleanup(EXIT_FAILURE); + } else { + log_print(L_INF, "%s opened", device); + } + + + /* open listing tcp socket */ + if (sock_open(&socketfd, port, localhost) == -1) { + log_print(L_ALL, "can't open port: %d", port); + cleanup(EXIT_FAILURE); + } else { + log_print(L_INF, "port %d opened", port); + } + + /* init msg queue */ + if (msg_queue_init() == -1) { + log_print(L_ALL, "can't initialize msg queue"); + cleanup(EXIT_FAILURE); + } else { + msg_queue_on = YES; + log_print(L_INF, "msg queue initialized"); + } + + /* enter main loop */ + main_loop(); + + cleanup(EXIT_SUCCESS); + + return 0; +} diff --git a/src/ebusd.h b/src/ebusd.h new file mode 100644 index 00000000..04a844b4 --- /dev/null +++ b/src/ebusd.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) Roland Jax 2012-2013 + * + * This file is part of ebusd. + * + * ebusd is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ebusd is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ebusd. If not, see http://www.gnu.org/licenses/. + */ + +#ifndef MAIN_H_ +#define MAIN_H_ + +#define DAEMON_NAME "ebusd" +#define DAEMON_VERSION "0.1" + +#define DAEMON_WORKDIR "/tmp/" + +#define DAEMON_CFGDIR "/etc/ebusd" +#define DAEMON_CFGFILE DAEMON_CFGDIR"/ebusd.conf" +#define DAEMON_EXTENSION "csv" +#define DAEMON_FOREGROUND NO +#define DAEMON_LOGLEVEL "INF" +#define DAEMON_LOGFILE "/var/log/ebusd.log" +#define DAEMON_NOSYN NO +#define DAEMON_PIDFILE "/var/run/ebusd.pid" +#define DAEMON_RAWFILE "/tmp/ebusd.bin" +#define DAEMON_RAWDUMP NO + + +void usage(void); + +void cmdline(int *argc, char ***argv); + +void set_unset(void); + +void signal_handler(int sig); + +void daemonize(void); + +void cleanup(int state); + +void main_loop(void); + +#endif /* MAIN_H_ */ diff --git a/src/utils.c b/src/utils.c new file mode 100644 index 00000000..07cf9979 --- /dev/null +++ b/src/utils.c @@ -0,0 +1,414 @@ +/* + * Copyright (C) Roland Jax 2012-2013 + * + * This file is part of ebusd. + * + * ebusd is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ebusd is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ebusd. If not, see http://www.gnu.org/licenses/. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "ebus-bus.h" +#include "utils.h" + + + +struct msg_queue *dummy; +static int msg_entries; + +int +msg_queue_entries(void) +{ + return msg_entries; +} + +int +msg_queue_init(void) +{ + dummy = (struct msg_queue *) malloc(sizeof(struct msg_queue)); + + if (dummy != NULL) { + dummy->id = -1; + memset(dummy->data, '\0', sizeof(dummy->data)); + dummy->clientfd = -1; + dummy->prev = NULL; + + msg_entries = 0; + return 0; + } + + return -1; +} + +void +msg_queue_free(void) +{ + while (dummy->prev != NULL) + msg_queue_get(); + + free(dummy); + msg_entries = 0; +} + +void +msg_queue_put(struct msg_queue *new) +{ + struct msg_queue *tmp; + + /* first element */ + if (dummy->prev == NULL) { + dummy->prev = new; + new->prev = NULL; + } else { + tmp = dummy; + /* get last element */ + while (tmp->prev != NULL) + tmp = tmp->prev; + + tmp->prev = new; + new->prev = NULL; + } +} + +void +msg_queue_get(void) +{ + struct msg_queue *tmp; + + /* delete element */ + if (dummy->prev != NULL) { + tmp = dummy->prev; + dummy->prev = tmp->prev; + free(tmp); + } else { + log_print(L_ERR, "msg queue empty - should never seen"); + } +} + +void +msg_queue_msg_add(int id, char *data, int clientfd) +{ + struct msg_queue *new; + + new = (struct msg_queue *) malloc(sizeof(struct msg_queue)); + + if (new != NULL) { + new->id = id; + memset(new->data, '\0', sizeof(new->data)); + strncpy(new->data, data, strlen(data)); + new->clientfd = clientfd; + new->prev = NULL; + + msg_queue_put(new); + msg_entries++; + + log_print(L_DBG, "add: id: %d clientfd: %d ==> entries: %d", + new->id, new->clientfd, msg_entries); + } +} + +void +msg_queue_msg_del(int *id, char *data, int *clientfd) +{ + if (dummy->prev != NULL) { + *id = dummy->prev->id; + memset(data, '\0', sizeof(data)); + strncpy(data, dummy->prev->data, strlen(dummy->prev->data)); + *clientfd = dummy->prev->clientfd; + + msg_queue_get(); + msg_entries--; + + log_print(L_DBG, "del: id: %d clientfd: %d ==> entries: %d", + *id, *clientfd, msg_entries); + + } else { + log_print(L_NOT, "msg queue empty"); + } +} + + + +void +cfg_print(struct config *cfg, int len) +{ + int i; + + fprintf(stdout, "\n"); + + for (i = 0; i < len; i++) { + + if (cfg[i].key != NULL && cfg[i].tgt != NULL) { + fprintf(stdout, "%s = ", cfg[i].key); + + switch (cfg[i].type) { + case STR: + fprintf(stdout, "%s\n", (char *) cfg[i].tgt); + break; + case BOL: + if (*(int *) cfg[i].tgt == NO) + fprintf(stdout, "NO\n"); + else if (*(int *) cfg[i].tgt == YES) + fprintf(stdout, "YES\n"); + else + fprintf(stdout, "UNSET\n"); + break; + case NUM: + fprintf(stdout, "%d\n", *(int *) cfg[i].tgt); + break; + default: + break; + } + } + } + fprintf(stdout, "\n"); +} + +int +cfg_file_set_param(char *par, struct config *cfg, int len) +{ + int i; + + for (i = 0; i < len; i++) { + + if (strncasecmp(par, cfg[i].key, strlen(cfg[i].key)) == 0 && + strlen(par) == strlen(cfg[i].key)) { + + par = strtok(NULL, "\t =\n\r"); + + switch (cfg[i].type) { + case STR: + if (strlen(cfg[i].tgt) == 0) + strncpy(cfg[i].tgt , par, strlen(par)); + break; + case BOL: + if (*(int *) cfg[i].tgt == UNSET) { + if (strncasecmp(par, "NO", 2) == 0) + *(int *) cfg[i].tgt = NO; + else if (strncasecmp(par, "YES", 3) == 0) + *(int *) cfg[i].tgt = YES; + else + *(int *) cfg[i].tgt = UNSET; + } + break; + case NUM: + if (*(int *) cfg[i].tgt == UNSET) + *(int *) cfg[i].tgt = atoi(par); + break; + default: + break; + } + + return 1; + } + } + + return 0; +} + +int +cfg_file_read(const char *file, struct config *cfg, int len) +{ + int ret; + char line[CFG_LINELEN]; + char *par, *tmp; + FILE *fp = NULL; + + + + /* open config file */ + fp = fopen(file, "r"); + + /* try local configuration file */ + if (fp == NULL) { + fprintf(stdout, "configuration file %s not found.\n", file); + + tmp = strrchr(file, '/'); + tmp++; + + /* open config file */ + fp = fopen(tmp, "r"); + err_ret_if(fp == NULL, -1); + + fprintf(stdout, "local configuration file %s used.\n", tmp); + } + + /* read each line and set parameter */ + while (fgets(line, CFG_LINELEN, fp) != NULL ) { + par = strtok(line, "\t =\n\r") ; + + if (par != NULL && par[0] != '#') + ret = cfg_file_set_param(par, cfg, len); + } + + /* close config file */ + ret = fclose(fp); + err_ret_if(ret == EOF, -1); + + return 0; +} + + + +int +pid_file_open(const char *file, int *fd) +{ + int ret; + char pid[10]; + + *fd = open(file, O_RDWR|O_CREAT, 0600); + err_ret_if(*fd < 0, -1); + + ret = lockf(*fd, F_TLOCK, 0); + err_ret_if(ret < 0, -1); + + sprintf(pid, "%d\n", getpid()); + ret = write(*fd, pid, strlen(pid)); + err_ret_if(ret < 0, -1); + + return 0; +} + +int +pid_file_close(const char *file, int fd) +{ + int ret; + + ret = close(fd); + err_ret_if(ret < 0, -1); + + ret = unlink(file); + err_ret_if(ret < 0, -1); + + return 0; +} + + + +int +sock_open(int *fd, int port, int localhost) +{ + int ret, opt; + struct sockaddr_in sock; + + *fd = socket(PF_INET, SOCK_STREAM, 0); + err_ret_if(fd < 0, -1); + + /* todo: verify if this realy work */ + /* prevent "Error Address already in use" error message */ + opt = 1; + ret = setsockopt(*fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int)); + err_ret_if(ret < 0, -1); + + memset((char *) &sock, 0, sizeof(sock)); + sock.sin_family = AF_INET; + + if (localhost == YES) + sock.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + else + sock.sin_addr.s_addr = htonl(INADDR_ANY); + + sock.sin_port = htons(port); + + ret = bind(*fd, (struct sockaddr *) &sock, sizeof(sock)); + err_ret_if(ret < 0, -1); + + ret = listen(*fd, 5); + err_ret_if(ret < 0, -1); + + return 0; +} + +int +sock_close(int fd) +{ + int ret; + + ret = close(fd); + err_ret_if(ret < 0, -1); + + return 0; +} + +int +sock_client_accept(int listenfd, int *datafd) +{ + struct sockaddr_in sock; + socklen_t socklen; + + socklen = sizeof(sock); + + *datafd = accept(listenfd, (struct sockaddr *) &sock, &socklen); + err_ret_if(*datafd < 0, -1); + + log_print(L_DBG, "client [%d] from %s connected.", + *datafd, inet_ntoa(sock.sin_addr)); + + return 0; +} + +int +sock_client_read(int fd, char *buf, int *buflen) +{ + *buflen = read(fd, buf, *buflen); + err_ret_if(*buflen < 0, -1); + + if (strncasecmp("quit", buf , 4) == 0 || *buflen <= 0) { + /* close tcp connection */ + log_print(L_DBG, "client [%d] disconnected.", fd); + sock_close(fd); + return -1; + } + + if (strchr(buf, '\n') != NULL) { + buf[strcspn(buf, "\n")] = '\0'; + *buflen -= 1; + } + + log_print(L_NET, ">>> client [%d] %s", fd, buf); + + return 0; +} + +int +sock_client_write(int fd, char *buf, int buflen) +{ + int ret; + + /* add to each line */ + buf[buflen] = '\r'; + buflen++; + + ret = write(fd, buf, buflen); + err_ret_if(ret < 0, -1); + + buf[strcspn(buf,"\n")] = '\0'; + log_print(L_NET, "<<< client [%d] %s", fd, buf); + + return 0; +} diff --git a/src/utils.h b/src/utils.h new file mode 100644 index 00000000..2827e8f0 --- /dev/null +++ b/src/utils.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) Roland Jax 2012-2013 + * + * This file is part of ebusd. + * + * ebusd is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ebusd is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ebusd. If not, see http://www.gnu.org/licenses/. + */ + +#ifndef UTILS_H_ +#define UTILS_H_ + +#define NUMSTR2(s) #s +#define NUMSTR(s) NUMSTR2(s) + +#define CFG_LINELEN 256 + +#define SOCKET_PORT 8888 +#define SOCKET_BUFSIZE 1024 + +#define MSG_QUEUE_MSG_SIZE 256 + +enum enum_config {STR, BOL, NUM}; + +struct config { + char *key; + int type; + void *tgt; + char *info; +}; + +struct msg_queue { + int id; + char data[MSG_QUEUE_MSG_SIZE + 1]; + int clientfd; + struct msg_queue *prev; +}; + +int msg_queue_entries(void); +int msg_queue_init(void); +void msg_queue_free(void); +void msg_queue_put(struct msg_queue *msg); +void msg_queue_get(void); +void msg_queue_msg_add(int id, char *data, int clientfd); +void msg_queue_msg_del(int *id, char *data, int *clientfd); + +void cfg_print(struct config *cfg, int len); +int cfg_file_set_param(char *param, struct config *cfg, int len); +int cfg_file_read(const char *file, struct config *cfg, int len); + +int pid_file_open(const char *file, int *fd); +int pid_file_close(const char *file, int fd); + +int sock_open(int *fd, int port, int localhost); +int sock_close(int fd); +int sock_client_accept(int listenfd, int *datafd); +int sock_client_read(int fd, char *buf, int *buflen); +int sock_client_write(int fd, char *buf, int buflen); + +#endif /* UTILS_H_ */ diff --git a/tools/Makefile.am b/tools/Makefile.am new file mode 100644 index 00000000..c71d45f7 --- /dev/null +++ b/tools/Makefile.am @@ -0,0 +1,17 @@ +bin_PROGRAMS = ebus_send check csv + +ebus_send_SOURCES = ebus_send.c +ebus_send_LDADD = $(top_srcdir)/lib/libebus.a $(top_srcdir)/lib/liblog.a +ebus_send_CFLAGS = -I$(top_srcdir)/lib + +check_SOURCES = check.c +check_LDADD = $(top_srcdir)/lib/libebus.a $(top_srcdir)/lib/liblog.a +check_CFLAGS = -I$(top_srcdir)/lib + +csv_SOURCES = csv.c +csv_LDADD = $(top_srcdir)/lib/libebus.a $(top_srcdir)/lib/liblog.a +csv_CFLAGS = -I$(top_srcdir)/lib + +distclean-local: + -rm -f Makefile.in + diff --git a/tools/check.c b/tools/check.c new file mode 100644 index 00000000..9187cb39 --- /dev/null +++ b/tools/check.c @@ -0,0 +1,107 @@ +/* + * Copyright (C) Roland Jax 2012-2013 + * + * This file is part of ebusd. + * + * ebusd is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ebusd is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ebusd. If not, see http://www.gnu.org/licenses/. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include +#include + +#include "ebus-decode.h" + +int main() { + + int i, j, end, ret; + + int bcd, d1b, data[SERIAL_BUFSIZE + 1]; + float d1c, d2b, d2c; + + char byte; + + unsigned char hex, tmp, crc, crc_calc[2] ; + + tmp = 0; + end = 0; + do { + i = 0; + printf("Input: "); + while ((byte = fgetc(stdin)) != EOF) { + + if (byte == '\n') { + break; + } + + if (byte == 'q') { + end = 1; + break; + } + + if (i < sizeof(data)) { + ret = eb_htoi(&byte); + if (ret != -1) { + data[i] = ret; + i++; + } + } else { + break; + } + } + + if (!end) { + + for (j = 0; j < i; j += 2) { + + bcd = 0; + d1b = 0; + ret = 0; + d1c = 0.0; + d2b = 0.0; + d2c = 0.0; + + hex = (unsigned char) (data[j]*16 + data[j+1]); + + ret = eb_bcd_to_int(hex, &bcd); + ret = eb_d1b_to_int(hex, &d1b); + + ret = eb_d1c_to_float(hex, &d1c); + printf("hex %02x ->\tbcd: %3d\td1b: %4d" + "\td1c: %5.1f", hex, bcd, d1b, d1c); + + + if (j == 2) { + ret = eb_d2b_to_float(tmp, hex, &d2b); + ret = eb_d2c_to_float(tmp, hex, &d2c); + crc_calc[0] = tmp; + crc_calc[1] = hex; + crc = eb_calc_crc(crc_calc,2); + printf("\td2b: %10.5f\td2c: %12.6f" + "\tcrc: %02x\n", d2b, d2c, crc); + } + else { + tmp = hex; + printf("\n"); + } + } + } + + } while (end == 0); + + return 0; +} diff --git a/tools/csv.c b/tools/csv.c new file mode 100644 index 00000000..6c738ce7 --- /dev/null +++ b/tools/csv.c @@ -0,0 +1,223 @@ +/* + * Copyright (C) Roland Jax 2012-2013 + * + * This file is part of ebusd. + * + * ebusd is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ebusd is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ebusd. If not, see http://www.gnu.org/licenses/. + */ + + +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "ebus-cmd.h" +#include "ebus-bus.h" + +/* global variables */ +const char *progname; + +static const char *cfgdir = NULL; +static int all = NO; +static int cyc = NO; +static int detail = NO; +static int get = NO; +static int set = NO; +static char loglevel[] = {"ALL"}; + + +void +usage() +{ + fprintf(stdout, "\nUsage: %s [OPTION] cfgdir\n\n" + " -a --all print ALL\n" + " -c --cyc print CYC\n" + " -d --detail print DETAIL\n" + " -g --get print GET\n" + " -s --set print SET\n" + " -h --help print this message.\n" + "\n", + progname); +} + +void +cmdline(int *argc, char ***argv) +{ + static struct option opts[] = { + {"all", no_argument, NULL, 'a'}, + {"cyc", no_argument, NULL, 'c'}, + {"detail", no_argument, NULL, 'd'}, + {"get", no_argument, NULL, 'g'}, + {"set", no_argument, NULL, 's'}, + {"help", no_argument, NULL, 'h'}, + {NULL, no_argument, NULL, 0 }, + }; + + for (;;) { + int i; + + i = getopt_long(*argc, *argv, "acdgsh", opts, NULL); + + if (i == -1) { + *argc = optind; + break; + } + + switch (i) { + case 'a': + cyc = YES; + get = YES; + set = YES; + break; + case 'c': + cyc = YES; + break; + case 'd': + detail = YES; + break; + case 'g': + get = YES; + break; + case 's': + set = YES; + break; + case 'h': + default: + usage(); + exit(EXIT_FAILURE); + break; + } + } +} + + + +int +main(int argc, char *argv[]) +{ + + /* set progname */ + progname = (const char *)strrchr(argv[0], '/'); + progname = progname ? (progname + 1) : argv[0]; + + cmdline(&argc, &argv); + + cfgdir = argv[argc]; + + /* open log */ + log_level(loglevel); + log_open("", 1); + + if (cfgdir) + eb_cmd_dir_read(cfgdir, "csv"); + else + usage(); + + if (cyc) + eb_cmd_print("cyc", all, detail); + + if (get) + eb_cmd_print("get", all, detail); + + if (set) + eb_cmd_print("set", all, detail); + + + + //~ Master Slave + //~ const unsigned char tmp[] = {'\x10','\x08','\xb5','\x10','\x09','\x00','\x02','\x40','\x00', + //~ '\x00','\x00','\x00','\x00','\x02','\x15','\x00','\x00','\x00','\x00'}; + //~ '\x00','\x00','\x00','\x00','\x02','\x15','\xff','\x00','\x00','\x00'}; + //~ '\x00','\x00','\x00','\x00','\x02','\x15','\x00','\x01','\x02','\x99','\x00'}; + //~ '\x00','\x00','\x00','\x00','\x02','\x15','\x00','\x02','\xe2','\x67','\x41','\x00'}; + //~ char cmd[] = "cyc ms test ma"; + //~ char cmd[] = "cyc ms test sd"; + //~ char cmd[] = "cyc ms test sa"; + //~ char cmd[] = "cyc ms test md"; + //~ char cmd[] = "cyc ms test"; + + //~ const unsigned char tmp[] = {'\x10','\x08','\xb5','\x09','\x03','\x29','\x0f','\x00','\x56','\x00', + //~ '\x05','\x0f','\x00','\xa9','\x01','\x00','\x00','\x86','\x00'}; + //~ char cmd[] = "cyc mv brine_in"; + + //~ const unsigned char tmp[] = {'\xff','\x08','\xb5','\x11','\x01','\x01','\x2d','\x00','\x09','\x62', + //~ '\xff','\x00','\x80','\x00','\x54','\x00','\x00','\xff','\x23','\x00'}; + + //~ char cmd[] = "cyc burner temps"; + //~ char cmd[] = "cyc burner temps_raw"; + + // 31835 + //~ const unsigned char tmp[] = {'\xff','\x08','\xb5','\x09','\x03','\x0d','\xbc','\x00','\x12', + //~ '\x00','\x04','\x5b','\x7c','\x00','\x00','\x75','\x00'}; +//~ +//~ + //~ char cmd[] = "cyc ms test"; + + + //~ Master Master + //~ 10 03 05 07 09 bb 06 2e 02 00 80 ff 6e ff 8f 00 + //~ const unsigned char tmp[] = {'\x10','\x03','\x05','\x07','\x09','\xbb','\x06','\x1b','\x02','\x00', + //~ '\x80','\xff','\x6e','\xff','\xf2','\x00'}; + + //~ const unsigned char tmp[] = {'\x10','\x23','\xb5','\x04','\x01','\x31','\xf6','\x00'}; + //~ const unsigned char tmp[] = {'\x10','\x23','\xb5','\x04','\x01','\x31','\xf6','\xff'}; + //~ char cmd[] = "cyc mm test test"; + //~ char cmd[] = "cyc mm test stat"; + //~ char cmd[] = "cyc mm test"; + + //~ Broadcast + //~ const unsigned char tmp[] = {'\x10','\xfe','\xb5','\x16','\x08','\x00','\x02','\x23','\x16','\x28','\x02','\x04','\x13','\x92'}; + //~ char cmd[] = "cyc broad date_time day"; + //~ char cmd[] = "cyc broad date_time"; + + //~ 10 46 11 2e 03 00 02 11 02 + //~ const unsigned char tmp[] = {'\x10','\x46','\x11','\x2e','\x03','\x00','\x02','\x11','\x02'}; + //~ char cmd[] = "cyc broad 1"; + +//############################################################### + + //~ const int tmplen = sizeof(tmp) / sizeof(unsigned char); + //~ char data[CMD_DATA_SIZE], tcpbuf[CMD_DATA_SIZE]; + //~ memset(data, '\0', sizeof(data)); + //~ memset(tcpbuf, '\0', sizeof(tcpbuf)); + //~ int id, tcpbuflen; +//~ + //~ fprintf(stdout, "Test command >%s<\n", cmd); + //~ eb_print_hex(tmp, tmplen); +//~ + //~ eb_cyc_data_process(tmp, tmplen); +//~ + //~ id = eb_cmd_search_com_cyc(&tmp[1], tmplen - 1); +//~ + //~ eb_execute(id, data, tcpbuf, &tcpbuflen); +//~ + //~ fprintf(stdout, "tcpbuf: %s tcpbuflen: %d\n", tcpbuf, tcpbuflen); + +//############################################################### + +//~ unsigned long val; +//~ eb_print_hex(&tmp[11], 4); +//~ fprintf(stdout, "%d %d %d %d\n", tmp[11], (tmp[12] << 8), (tmp[13] << 16), (tmp[14] << 24)); +//~ fprintf(stdout, "%d\n", tmp[11] + (tmp[12] << 8) + (tmp[13] << 16) + (tmp[14] << 24)); + +//############################################################### + + eb_cmd_dir_free(); + + return 0; +} diff --git a/tools/ebus_send.c b/tools/ebus_send.c new file mode 100644 index 00000000..1bcab4b2 --- /dev/null +++ b/tools/ebus_send.c @@ -0,0 +1,278 @@ +/* + * Copyright (C) Roland Jax 2012-2013 + * + * This file is part of ebusd. + * + * ebusd is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ebusd is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ebusd. If not, see http://www.gnu.org/licenses/. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ebus-decode.h" +#include "ebus-bus.h" + + +static int serialfd = -1; +static const char *device = SERIAL_DEVICE; +static const char *progname; +static int type = EBUS_MSG_MASTER_SLAVE; +static int prompt = NO; + +void +print_msg(const char *pre, const unsigned char *buf, int buflen, + const char *post) +{ + int i; + + fprintf(stdout, "%s", pre); + for (i = 0; i < buflen; i++) + fprintf(stdout, " %02x", buf[i]); + fprintf(stdout, "%s\n", post); +} + +void +usage() +{ + fprintf(stdout, "\nUsage: %s [OPTION] \n\n" + " spaces within message be removed.\n\n" + " -a --address set bus address. (%02x)\n" + " -d --device use a specified serial device. (%s)\n" + " -p --prompt stay on input prompt.\n" + " -r --retry max retry getting bus. (%d)\n" + " -s --skip skipped ACK bytes after get-bus error. (%d)\n" + " -t --type message type. (%d)\n" + " 1 = Broadcast, 2 = Master-Master, 3 = Master-Slave\n" + " -w --wait wait time for QQ compare. (~%d usec)\n" + " -h --help print this message.\n" + "\n", + progname, + EBUS_QQ, + device, + EBUS_GET_RETRY, + EBUS_SKIP_ACK, + type, + EBUS_MAX_WAIT); +} + +void +cmdline(int *argc, char ***argv) +{ + static struct option opts[] = { + {"address", required_argument, NULL, 'a'}, + {"device", required_argument, NULL, 'd'}, + {"prompt", no_argument, NULL, 'p'}, + {"retry", required_argument, NULL, 'r'}, + {"skip", required_argument, NULL, 's'}, + {"type", required_argument, NULL, 't'}, + {"wait", required_argument, NULL, 'w'}, + {"help", no_argument, NULL, 'h'}, + {NULL, no_argument, NULL, 0 }, + }; + + for (;;) { + int i; + + i = getopt_long(*argc, *argv, "a:d:pr:s:t:w:h", opts, NULL); + + if (i == -1) { + *argc = optind; + break; + } + + switch (i) { + case 'a': + if (isxdigit(*optarg) && strlen(optarg) == 2) { + int tmp; + tmp = (eb_htoi(&optarg[0])) * 16 + + (eb_htoi(&optarg[1])); + + eb_set_qq((unsigned char) tmp); + } + break; + case 'd': + device = optarg; + break; + case 'p': + prompt = YES; + break; + case 'r': + if (isdigit(*optarg)) { + eb_set_get_retry(atoi(optarg)); + } + break; + case 's': + if (isdigit(*optarg)) { + eb_set_skip_ack(atoi(optarg)); + } + break; + case 't': + if (isdigit(*optarg)) + type = atoi(optarg); + break; + case 'w': + if (isdigit(*optarg)) { + eb_set_max_wait(atol(optarg)); + } + break; + case 'h': + default: + usage(); + exit(EXIT_FAILURE); + break; + } + } +} + +int +main(int argc, char *argv[]) +{ + int i, j, k, end, ret, val, max_argc, buslen; + int in[SERIAL_BUFSIZE + 1]; + char byte; + unsigned char msg[SERIAL_BUFSIZE + 1], bus[TMP_BUFSIZE]; + + progname = (const char *)strrchr(argv[0], '/'); + progname = progname ? (progname + 1) : argv[0]; + + max_argc = argc; + cmdline(&argc, &argv); + + val = 0; + + if (prompt) { + + end = 0; + do { + byte = '\0'; + i = 0; + printf("msg: "); + while ((byte = fgetc(stdin)) != EOF) { + + if (byte == '\n') + break; + + if (byte == 'q') { + end = 1; + break; + } + + if (i < sizeof(in)) { + + ret = eb_htoi(&byte); + if (ret != -1) { + in[i] = ret; + i++; + } + + } else { + break; + } + } + + if (!end && i > 0) { + + memset(msg, '\0', sizeof(msg)); + memset(bus, '\0', sizeof(bus)); + + for (j = 0, k = 0; j < i; j += 2, k++) + msg[k] = (unsigned char) + (in[j]*16 + in[j+1]); + + ret = eb_serial_open(device, &serialfd); + if (ret < 0) + fprintf(stdout, "Error open %s.\n", device); + + if (ret == 0) { + ret = eb_send_data(msg, k, type, bus, &buslen); + if (ret == 0) { + fprintf(stdout, "res:"); + eb_print_result(); + } + + } + + ret = eb_serial_close(); + if (ret < 0) + fprintf(stdout, "Error close %s.\n", device); + + } + + } while (end == 0); + } else { + + memset(in, '\0', sizeof(in)); + k = 0; + i = 0; + for (k = argc; k < max_argc; k++) { + j = 0; + while (argv[k][j] != '\0') { + byte = argv[k][j]; + if (i < sizeof(in)) { + + ret = eb_htoi(&byte); + if (ret != -1) { + in[i] = ret; + i++; + } + } + j++; + } + } + + memset(bus, '\0', sizeof(bus)); + memset(msg, '\0', sizeof(msg)); + for (j = 0, k = 0; j < i; j += 2, k++) + msg[k] = (unsigned char) (in[j]*16 + in[j+1]); + + if (k > 0) { + + ret = eb_serial_open(device, &serialfd); + if (ret < 0) + fprintf(stdout, "Error open %s.\n", device); + + if (ret == 0) { + ret = eb_send_data(msg, k, type, bus, &buslen); + val = ret; + if (ret == 0) { + if (type == EBUS_MSG_MASTER_SLAVE) + eb_print_result(); + + if (type == EBUS_MSG_MASTER_MASTER) + fprintf(stdout, " %d\n", ret); + } + + } + + ret = eb_serial_close(); + if (ret < 0) + fprintf(stdout, "Error close %s.\n", device); + } + + } + + return val; +}